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