sdp_server.c revision ead3cde4bac0c3e32cd31f149093f004eef8ceeb
1/******************************************************************************
2 *
3 *  Copyright (C) 1999-2012 Broadcom Corporation
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19/******************************************************************************
20 *
21 *  This file contains functions that handle the SDP server functions.
22 *  This is mainly dealing with client requests
23 *
24 ******************************************************************************/
25
26#include <stdlib.h>
27#include <string.h>
28#include <stdio.h>
29
30#include "gki.h"
31#include "bt_types.h"
32#include "btu.h"
33
34#include "l2cdefs.h"
35#include "hcidefs.h"
36#include "hcimsgs.h"
37
38#include "sdp_api.h"
39#include "sdpint.h"
40
41
42#if SDP_SERVER_ENABLED == TRUE
43
44/* Maximum number of bytes to reserve out of SDP MTU for response data */
45#define SDP_MAX_SERVICE_RSPHDR_LEN      12
46#define SDP_MAX_SERVATTR_RSPHDR_LEN     10
47#define SDP_MAX_ATTR_RSPHDR_LEN         10
48
49/********************************************************************************/
50/*              L O C A L    F U N C T I O N     P R O T O T Y P E S            */
51/********************************************************************************/
52static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
53                                    UINT16 param_len, UINT8 *p_req,
54                                    UINT8 *p_req_end);
55
56static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
57                                      UINT16 param_len, UINT8 *p_req,
58                                      UINT8 *p_req_end);
59
60static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
61                                             UINT16 param_len, UINT8 *p_req,
62                                             UINT8 *p_req_end);
63
64
65/********************************************************************************/
66/*                  E R R O R   T E X T   S T R I N G S                         */
67/*                                                                              */
68/* The default is to have no text string, but we allow the strings to be        */
69/* configured in target.h if people want them.                                  */
70/********************************************************************************/
71#ifndef SDP_TEXT_BAD_HEADER
72#define SDP_TEXT_BAD_HEADER     NULL
73#endif
74
75#ifndef SDP_TEXT_BAD_PDU
76#define SDP_TEXT_BAD_PDU        NULL
77#endif
78
79#ifndef SDP_TEXT_BAD_UUID_LIST
80#define SDP_TEXT_BAD_UUID_LIST  NULL
81#endif
82
83#ifndef SDP_TEXT_BAD_HANDLE
84#define SDP_TEXT_BAD_HANDLE     NULL
85#endif
86
87#ifndef SDP_TEXT_BAD_ATTR_LIST
88#define SDP_TEXT_BAD_ATTR_LIST  NULL
89#endif
90
91#ifndef SDP_TEXT_BAD_CONT_LEN
92#define SDP_TEXT_BAD_CONT_LEN   NULL
93#endif
94
95#ifndef SDP_TEXT_BAD_CONT_INX
96#define SDP_TEXT_BAD_CONT_INX   NULL
97#endif
98
99/*******************************************************************************
100**
101** Function         sdp_server_handle_client_req
102**
103** Description      This is the main dispatcher of the SDP server. It is called
104**                  when any data is received from L2CAP, and dispatches the
105**                  request to the appropriate handler.
106**
107** Returns          void
108**
109*******************************************************************************/
110void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
111{
112    UINT8   *p_req     = (UINT8 *) (p_msg + 1) + p_msg->offset;
113    UINT8   *p_req_end = p_req + p_msg->len;
114    UINT8   pdu_id;
115    UINT16  trans_num, param_len;
116
117
118    /* Start inactivity timer */
119    btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
120
121    /* The first byte in the message is the pdu type */
122    pdu_id = *p_req++;
123
124    /* Extract the transaction number and parameter length */
125    BE_STREAM_TO_UINT16 (trans_num, p_req);
126    BE_STREAM_TO_UINT16 (param_len, p_req);
127
128    if ((p_req + param_len) > p_req_end)
129    {
130        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER);
131        return;
132    }
133
134    switch (pdu_id)
135    {
136    case SDP_PDU_SERVICE_SEARCH_REQ:
137        process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end);
138        break;
139
140    case SDP_PDU_SERVICE_ATTR_REQ:
141        process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
142        break;
143
144    case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
145        process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
146        break;
147
148    default:
149        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU);
150        SDP_TRACE_WARNING1 ("SDP - server got unknown PDU: 0x%x", pdu_id);
151        break;
152    }
153}
154
155
156
157/*******************************************************************************
158**
159** Function         process_service_search
160**
161** Description      This function handles a service search request from the
162**                  client. It builds a reply message with info from the database,
163**                  and sends the reply back to the client.
164**
165** Returns          void
166**
167*******************************************************************************/
168static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
169                                    UINT16 param_len, UINT8 *p_req,
170                                    UINT8 *p_req_end)
171{
172    UINT16          max_replies, cur_handles, rem_handles, cont_offset;
173    tSDP_UUID_SEQ   uid_seq;
174    UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
175    UINT16          rsp_param_len, num_rsp_handles, xx;
176    UINT32          rsp_handles[SDP_MAX_RECORDS] = {0};
177    tSDP_RECORD    *p_rec = NULL;
178    BT_HDR         *p_buf;
179    BOOLEAN         is_cont = FALSE;
180
181    p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
182
183    if ((!p_req) || (!uid_seq.num_uids))
184    {
185        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
186        return;
187    }
188
189    /* Get the max replies we can send. Cap it at our max anyways. */
190    BE_STREAM_TO_UINT16 (max_replies, p_req);
191
192    if (max_replies > SDP_MAX_RECORDS)
193        max_replies = SDP_MAX_RECORDS;
194
195    /* Get a list of handles that match the UUIDs given to us */
196    for (num_rsp_handles = 0; num_rsp_handles < max_replies; )
197    {
198        p_rec = sdp_db_service_search (p_rec, &uid_seq);
199
200        if (p_rec)
201            rsp_handles[num_rsp_handles++] = p_rec->record_handle;
202        else
203            break;
204    }
205
206    /* Check if this is a continuation request */
207    if (*p_req)
208    {
209        if (*p_req++ != SDP_CONTINUATION_LEN)
210        {
211            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
212                                     SDP_TEXT_BAD_CONT_LEN);
213            return;
214        }
215        BE_STREAM_TO_UINT16 (cont_offset, p_req);
216
217        if (cont_offset != p_ccb->cont_offset)
218        {
219            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
220                                     SDP_TEXT_BAD_CONT_INX);
221            return;
222        }
223
224        rem_handles = num_rsp_handles - cont_offset;    /* extract the remaining handles */
225    }
226    else
227    {
228        rem_handles = num_rsp_handles;
229        cont_offset = 0;
230    }
231
232    /* Calculate how many handles will fit in one PDU */
233    cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
234
235    if (rem_handles <= cur_handles)
236        cur_handles = rem_handles;
237    else /* Continuation is set */
238    {
239        p_ccb->cont_offset += cur_handles;
240        is_cont = TRUE;
241    }
242
243    /* Get a buffer to use to build the response */
244    if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
245    {
246        SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
247        return;
248    }
249    p_buf->offset = L2CAP_MIN_OFFSET;
250    p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
251
252    /* Start building a rsponse */
253    UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
254    UINT16_TO_BE_STREAM (p_rsp, trans_num);
255
256    /* Skip the length, we need to add it at the end */
257    p_rsp_param_len = p_rsp;
258    p_rsp += 2;
259
260    /* Put in total and current number of handles, and handles themselves */
261    UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles);
262    UINT16_TO_BE_STREAM (p_rsp, cur_handles);
263
264/*    SDP_TRACE_DEBUG5("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d",
265                     num_rsp_handles, cur_handles, cont_offset,
266                     cont_offset + cur_handles-1, is_cont); */
267    for (xx = cont_offset; xx < cont_offset + cur_handles; xx++)
268        UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]);
269
270    if (is_cont)
271    {
272        UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
273        UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
274    }
275    else
276        UINT8_TO_BE_STREAM (p_rsp, 0);
277
278    /* Go back and put the parameter length into the buffer */
279    rsp_param_len = p_rsp - p_rsp_param_len - 2;
280    UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
281
282    /* Set the length of the SDP data in the buffer */
283    p_buf->len = p_rsp - p_rsp_start;
284
285
286    /* Send the buffer through L2CAP */
287    L2CA_DataWrite (p_ccb->connection_id, p_buf);
288}
289
290
291/*******************************************************************************
292**
293** Function         process_service_attr_req
294**
295** Description      This function handles an attribute request from the client.
296**                  It builds a reply message with info from the database,
297**                  and sends the reply back to the client.
298**
299** Returns          void
300**
301*******************************************************************************/
302static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
303                                      UINT16 param_len, UINT8 *p_req,
304                                      UINT8 *p_req_end)
305{
306    UINT16          max_list_len, len_to_send, cont_offset;
307    INT16           rem_len;
308    tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
309    UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
310    UINT16          rsp_param_len, xx;
311    UINT32          rec_handle;
312    tSDP_RECORD     *p_rec;
313    tSDP_ATTRIBUTE  *p_attr;
314    BT_HDR          *p_buf;
315    BOOLEAN         is_cont = FALSE;
316    UINT16          attr_len;
317
318    /* Extract the record handle */
319    BE_STREAM_TO_UINT32 (rec_handle, p_req);
320
321    if (p_req > p_req_end)
322    {
323        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
324        return;
325    }
326
327    /* Get the max list length we can send. Cap it at MTU size minus overhead */
328    BE_STREAM_TO_UINT16 (max_list_len, p_req);
329
330    if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN))
331        max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
332
333    p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
334
335    if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end))
336    {
337        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
338        return;
339    }
340
341    memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
342
343    /* Find a record with the record handle */
344    p_rec = sdp_db_find_record (rec_handle);
345    if (!p_rec)
346    {
347        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
348        return;
349    }
350
351    /* Check if this is a continuation request */
352    if (*p_req)
353    {
354        if (*p_req++ != SDP_CONTINUATION_LEN)
355        {
356            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
357            return;
358        }
359        BE_STREAM_TO_UINT16 (cont_offset, p_req);
360
361        if (cont_offset != p_ccb->cont_offset)
362        {
363            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
364            return;
365        }
366
367        if (!p_ccb->rsp_list)
368        {
369            sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
370            return;
371        }
372        is_cont = TRUE;
373
374        /* Initialise for continuation response */
375        p_rsp = &p_ccb->rsp_list[0];
376        attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
377    }
378    else
379    {
380        /* Get a scratch buffer to store response */
381        if (!p_ccb->rsp_list)
382        {
383            p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
384            if (p_ccb->rsp_list == NULL)
385            {
386                SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
387                return;
388            }
389        }
390
391        p_ccb->cont_offset = 0;
392        p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
393
394        /* Reset continuation parameters in p_ccb */
395        p_ccb->cont_info.prev_sdp_rec = NULL;
396        p_ccb->cont_info.next_attr_index = 0;
397        p_ccb->cont_info.attr_offset = 0;
398    }
399
400    /* Search for attributes that match the list given to us */
401    for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
402    {
403        p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
404
405        if (p_attr)
406        {
407            /* Check if attribute fits. Assume 3-byte value type/length */
408            rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
409
410            /* just in case */
411            if (rem_len <= 0)
412            {
413                p_ccb->cont_info.next_attr_index = xx;
414                p_ccb->cont_info.next_attr_start_id = p_attr->id;
415                break;
416            }
417
418            attr_len = sdpu_get_attrib_entry_len(p_attr);
419            /* if there is a partial attribute pending to be sent */
420            if (p_ccb->cont_info.attr_offset)
421            {
422                p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
423                                                         &p_ccb->cont_info.attr_offset);
424
425                /* If the partial attrib could not been fully added yet */
426                if (p_ccb->cont_info.attr_offset != attr_len)
427                    break;
428                else /* If the partial attrib has been added in full by now */
429                    p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
430            }
431            else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
432            {
433                if (attr_len >= SDP_MAX_ATTR_LEN)
434                {
435                    SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
436                    sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
437                    return;
438                }
439
440                /* add the partial attribute if possible */
441                p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
442                                                         &p_ccb->cont_info.attr_offset);
443
444                p_ccb->cont_info.next_attr_index = xx;
445                p_ccb->cont_info.next_attr_start_id = p_attr->id;
446                break;
447            }
448            else /* build the whole attribute */
449                p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
450
451            /* If doing a range, stick with this one till no more attributes found */
452            if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
453            {
454                /* Update for next time through */
455                attr_seq.attr_entry[xx].start = p_attr->id + 1;
456
457                xx--;
458            }
459        }
460    }
461    /* If all the attributes have been accomodated in p_rsp,
462       reset next_attr_index */
463    if (xx == attr_seq.num_attr)
464        p_ccb->cont_info.next_attr_index = 0;
465
466    len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
467    cont_offset = 0;
468
469    if (!is_cont)
470    {
471        p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
472        /* Put in the sequence header (2 or 3 bytes) */
473        if (p_ccb->list_len > 255)
474        {
475            p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
476            p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
477            p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
478        }
479        else
480        {
481            cont_offset = 1;
482
483            p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
484            p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
485
486            p_ccb->list_len--;
487            len_to_send--;
488        }
489    }
490
491    /* Get a buffer to use to build the response */
492    if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
493    {
494        SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
495        return;
496    }
497    p_buf->offset = L2CAP_MIN_OFFSET;
498    p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
499
500    /* Start building a rsponse */
501    UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
502    UINT16_TO_BE_STREAM (p_rsp, trans_num);
503
504    /* Skip the parameter length, add it when we know the length */
505    p_rsp_param_len = p_rsp;
506    p_rsp += 2;
507
508    UINT16_TO_BE_STREAM (p_rsp, len_to_send);
509
510    memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
511    p_rsp += len_to_send;
512
513    p_ccb->cont_offset += len_to_send;
514
515    /* If anything left to send, continuation needed */
516    if (p_ccb->cont_offset < p_ccb->list_len)
517    {
518        is_cont = TRUE;
519
520        UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
521        UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
522    }
523    else
524        UINT8_TO_BE_STREAM (p_rsp, 0);
525
526    /* Go back and put the parameter length into the buffer */
527    rsp_param_len = p_rsp - p_rsp_param_len - 2;
528    UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
529
530    /* Set the length of the SDP data in the buffer */
531    p_buf->len = p_rsp - p_rsp_start;
532
533
534    /* Send the buffer through L2CAP */
535    L2CA_DataWrite (p_ccb->connection_id, p_buf);
536}
537
538
539
540/*******************************************************************************
541**
542** Function         process_service_search_attr_req
543**
544** Description      This function handles a combined service search and attribute
545**                  read request from the client. It builds a reply message with
546**                  info from the database, and sends the reply back to the client.
547**
548** Returns          void
549**
550*******************************************************************************/
551static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
552                                             UINT16 param_len, UINT8 *p_req,
553                                             UINT8 *p_req_end)
554{
555    UINT16         max_list_len;
556    INT16          rem_len;
557    UINT16         len_to_send, cont_offset;
558    tSDP_UUID_SEQ   uid_seq;
559    UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
560    UINT16          rsp_param_len, xx;
561    tSDP_RECORD    *p_rec;
562    tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
563    tSDP_ATTRIBUTE *p_attr;
564    BT_HDR         *p_buf;
565    BOOLEAN         maxxed_out = FALSE, is_cont = FALSE;
566    UINT8           *p_seq_start;
567    UINT16          seq_len, attr_len;
568
569    /* Extract the UUID sequence to search for */
570    p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
571
572    if ((!p_req) || (!uid_seq.num_uids))
573    {
574        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
575        return;
576    }
577
578    /* Get the max list length we can send. Cap it at our max list length. */
579    BE_STREAM_TO_UINT16 (max_list_len, p_req);
580
581    if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN))
582        max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
583
584    p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
585
586    if ((!p_req) || (!attr_seq.num_attr))
587    {
588        sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
589        return;
590    }
591
592    memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
593
594    /* Check if this is a continuation request */
595    if (*p_req)
596    {
597        if (*p_req++ != SDP_CONTINUATION_LEN)
598        {
599            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
600            return;
601        }
602        BE_STREAM_TO_UINT16 (cont_offset, p_req);
603
604        if (cont_offset != p_ccb->cont_offset)
605        {
606            sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
607            return;
608        }
609
610        if (!p_ccb->rsp_list)
611        {
612            sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
613            return;
614        }
615        is_cont = TRUE;
616
617        /* Initialise for continuation response */
618        p_rsp = &p_ccb->rsp_list[0];
619        attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
620    }
621    else
622    {
623        /* Get a scratch buffer to store response */
624        if (!p_ccb->rsp_list)
625        {
626            p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
627            if (p_ccb->rsp_list == NULL)
628            {
629                SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
630                return;
631            }
632        }
633
634        p_ccb->cont_offset = 0;
635        p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
636
637        /* Reset continuation parameters in p_ccb */
638        p_ccb->cont_info.prev_sdp_rec = NULL;
639        p_ccb->cont_info.next_attr_index = 0;
640        p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
641        p_ccb->cont_info.attr_offset = 0;
642    }
643
644    /* Get a list of handles that match the UUIDs given to us */
645    for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq))
646    {
647        /* Allow space for attribute sequence type and length */
648        p_seq_start = p_rsp;
649        if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
650        {
651            /* See if there is enough room to include a new service in the current response */
652            rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
653            if (rem_len < 3)
654            {
655                /* Not enough room. Update continuation info for next response */
656                p_ccb->cont_info.next_attr_index = 0;
657                p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
658                break;
659            }
660            p_rsp += 3;
661        }
662
663        /* Get a list of handles that match the UUIDs given to us */
664        for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
665        {
666            p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
667
668            if (p_attr)
669            {
670                /* Check if attribute fits. Assume 3-byte value type/length */
671                rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
672
673                /* just in case */
674                if (rem_len <= 0)
675                {
676                    p_ccb->cont_info.next_attr_index = xx;
677                    p_ccb->cont_info.next_attr_start_id = p_attr->id;
678                    maxxed_out = TRUE;
679                    break;
680                }
681
682                attr_len = sdpu_get_attrib_entry_len(p_attr);
683                /* if there is a partial attribute pending to be sent */
684                if (p_ccb->cont_info.attr_offset)
685                {
686                    p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
687                                                             &p_ccb->cont_info.attr_offset);
688
689                    /* If the partial attrib could not been fully added yet */
690                    if (p_ccb->cont_info.attr_offset != attr_len)
691                    {
692                        maxxed_out = TRUE;
693                        break;
694                    }
695                    else /* If the partial attrib has been added in full by now */
696                        p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
697                }
698                else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
699                {
700                    if (attr_len >= SDP_MAX_ATTR_LEN)
701                    {
702                        SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
703                        sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
704                        return;
705                    }
706
707                    /* add the partial attribute if possible */
708                    p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
709                                                             &p_ccb->cont_info.attr_offset);
710
711                    p_ccb->cont_info.next_attr_index = xx;
712                    p_ccb->cont_info.next_attr_start_id = p_attr->id;
713                    maxxed_out = TRUE;
714                    break;
715                }
716                else /* build the whole attribute */
717                    p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
718
719                /* If doing a range, stick with this one till no more attributes found */
720                if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
721                {
722                    /* Update for next time through */
723                    attr_seq.attr_entry[xx].start = p_attr->id + 1;
724
725                    xx--;
726                }
727            }
728        }
729
730        /* Go back and put the type and length into the buffer */
731        if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
732        {
733            seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
734            if (seq_len != 0)
735            {
736                UINT8_TO_BE_STREAM  (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
737                UINT16_TO_BE_STREAM (p_seq_start, seq_len);
738
739                if (maxxed_out)
740                    p_ccb->cont_info.last_attr_seq_desc_sent = TRUE;
741            }
742            else
743                p_rsp = p_seq_start;
744        }
745
746        if (maxxed_out)
747            break;
748
749        /* Restore the attr_seq to look for in the next sdp record */
750        memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ;
751
752        /* Reset the next attr index */
753        p_ccb->cont_info.next_attr_index = 0;
754        p_ccb->cont_info.prev_sdp_rec = p_rec;
755        p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
756    }
757
758    /* response length */
759    len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
760    cont_offset = 0;
761
762    /* If first response, insert sequence header */
763    if (!is_cont)
764    {
765        /* Get the total list length for requested uid and attribute sequence */
766        p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
767        /* Put in the sequence header (2 or 3 bytes) */
768        if (p_ccb->list_len > 255)
769        {
770            p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
771            p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
772            p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
773        }
774        else
775        {
776            cont_offset = 1;
777
778            p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
779            p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
780
781            p_ccb->list_len--;
782            len_to_send--;
783        }
784    }
785
786    /* Get a buffer to use to build the response */
787    if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
788    {
789        SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
790        return;
791    }
792    p_buf->offset = L2CAP_MIN_OFFSET;
793    p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
794
795    /* Start building a rsponse */
796    UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
797    UINT16_TO_BE_STREAM (p_rsp, trans_num);
798
799    /* Skip the parameter length, add it when we know the length */
800    p_rsp_param_len = p_rsp;
801    p_rsp += 2;
802
803    /* Stream the list length to send */
804    UINT16_TO_BE_STREAM (p_rsp, len_to_send);
805
806    /* copy from rsp_list to the actual buffer to be sent */
807    memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
808    p_rsp += len_to_send;
809
810    p_ccb->cont_offset += len_to_send;
811
812    /* If anything left to send, continuation needed */
813    if (p_ccb->cont_offset < p_ccb->list_len)
814    {
815        is_cont = TRUE;
816
817        UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
818        UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
819    }
820    else
821        UINT8_TO_BE_STREAM (p_rsp, 0);
822
823    /* Go back and put the parameter length into the buffer */
824    rsp_param_len = p_rsp - p_rsp_param_len - 2;
825    UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
826
827    /* Set the length of the SDP data in the buffer */
828    p_buf->len = p_rsp - p_rsp_start;
829
830
831    /* Send the buffer through L2CAP */
832    L2CA_DataWrite (p_ccb->connection_id, p_buf);
833}
834
835#endif  /* SDP_SERVER_ENABLED == TRUE */
836