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 /********************************************************************************/
52 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
53 UINT16 param_len, UINT8 *p_req,
54 UINT8 *p_req_end);
55
56 static 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
60 static 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 *******************************************************************************/
sdp_server_handle_client_req(tCONN_CB * p_ccb,BT_HDR * p_msg)110 void 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 *******************************************************************************/
process_service_search(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)168 static 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];
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 *******************************************************************************/
process_service_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)302 static 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 >= 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 *******************************************************************************/
process_service_search_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)551 static 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 >= 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