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 *******************************************************************************/ 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 *******************************************************************************/ 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] = {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 *******************************************************************************/ 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 >= 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 *******************************************************************************/ 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 >= 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