1 /****************************************************************************** 2 * 3 * Copyright (c) 2014 The Android Open Source Project 4 * Copyright 2004-2012 Broadcom Corporation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 ******************************************************************************/ 19 #include <string.h> 20 21 #include "bt_trace.h" 22 #include "bt_utils.h" 23 #include "bta_ag_api.h" 24 #include "bta_hf_client_int.h" 25 #include "device/include/esco_parameters.h" 26 #include "osi/include/osi.h" 27 28 #define BTA_HF_CLIENT_NO_EDR_ESCO \ 29 (ESCO_PKT_TYPES_MASK_NO_2_EV3 | ESCO_PKT_TYPES_MASK_NO_3_EV3 | \ 30 ESCO_PKT_TYPES_MASK_NO_2_EV5 | ESCO_PKT_TYPES_MASK_NO_3_EV5) 31 32 enum { 33 BTA_HF_CLIENT_SCO_LISTEN_E, 34 BTA_HF_CLIENT_SCO_OPEN_E, /* open request */ 35 BTA_HF_CLIENT_SCO_CLOSE_E, /* close request */ 36 BTA_HF_CLIENT_SCO_SHUTDOWN_E, /* shutdown request */ 37 BTA_HF_CLIENT_SCO_CONN_OPEN_E, /* SCO opened */ 38 BTA_HF_CLIENT_SCO_CONN_CLOSE_E, /* SCO closed */ 39 }; 40 41 /******************************************************************************* 42 * 43 * Function bta_hf_client_remove_sco 44 * 45 * Description Removes the specified SCO from the system. 46 * 47 * Returns bool - true if SCO removal was started 48 * 49 ******************************************************************************/ 50 static bool bta_hf_client_sco_remove(tBTA_HF_CLIENT_CB* client_cb) { 51 bool removed_started = false; 52 tBTM_STATUS status; 53 54 APPL_TRACE_DEBUG("%s", __func__); 55 56 if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) { 57 status = BTM_RemoveSco(client_cb->sco_idx); 58 59 APPL_TRACE_DEBUG("%s: idx 0x%04x, status:0x%x", __func__, 60 client_cb->sco_idx, status); 61 62 if (status == BTM_CMD_STARTED) { 63 removed_started = true; 64 } 65 /* If no connection reset the SCO handle */ 66 else if ((status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR)) { 67 client_cb->sco_idx = BTM_INVALID_SCO_INDEX; 68 } 69 } 70 return removed_started; 71 } 72 73 /******************************************************************************* 74 * 75 * Function bta_hf_client_cback_sco 76 * 77 * Description Call application callback function with SCO event. 78 * 79 * 80 * Returns void 81 * 82 ******************************************************************************/ 83 void bta_hf_client_cback_sco(tBTA_HF_CLIENT_CB* client_cb, uint8_t event) { 84 tBTA_HF_CLIENT evt; 85 86 memset(&evt, 0, sizeof(evt)); 87 evt.bd_addr = client_cb->peer_addr; 88 89 /* call app cback */ 90 bta_hf_client_app_callback(event, &evt); 91 } 92 93 /******************************************************************************* 94 * 95 * Function bta_hf_client_sco_conn_rsp 96 * 97 * Description Process the SCO connection request 98 * 99 * 100 * Returns void 101 * 102 ******************************************************************************/ 103 static void bta_hf_client_sco_conn_rsp(tBTA_HF_CLIENT_CB* client_cb, 104 tBTM_ESCO_CONN_REQ_EVT_DATA* p_data) { 105 enh_esco_params_t resp; 106 uint8_t hci_status = HCI_SUCCESS; 107 108 APPL_TRACE_DEBUG("%s", __func__); 109 110 if (client_cb->sco_state == BTA_HF_CLIENT_SCO_LISTEN_ST) { 111 if (p_data->link_type == BTM_LINK_TYPE_SCO) { 112 resp = esco_parameters_for_codec(ESCO_CODEC_CVSD); 113 } else { 114 if (client_cb->negotiated_codec == BTA_AG_CODEC_MSBC) { 115 resp = esco_parameters_for_codec(ESCO_CODEC_MSBC_T2); 116 } else { 117 // default codec 118 resp = esco_parameters_for_codec(ESCO_CODEC_CVSD); 119 } 120 } 121 122 /* tell sys to stop av if any */ 123 bta_sys_sco_use(BTA_ID_HS, 1, client_cb->peer_addr); 124 } else { 125 hci_status = HCI_ERR_HOST_REJECT_DEVICE; 126 } 127 128 BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp); 129 } 130 131 /******************************************************************************* 132 * 133 * Function bta_hf_client_sco_connreq_cback 134 * 135 * Description BTM eSCO connection requests and eSCO change requests 136 * Only the connection requests are processed by BTA. 137 * 138 * Returns void 139 * 140 ******************************************************************************/ 141 static void bta_hf_client_esco_connreq_cback(tBTM_ESCO_EVT event, 142 tBTM_ESCO_EVT_DATA* p_data) { 143 APPL_TRACE_DEBUG("%s: %d", __func__, event); 144 145 tBTA_HF_CLIENT_CB* client_cb = 146 bta_hf_client_find_cb_by_sco_handle(p_data->conn_evt.sco_inx); 147 if (client_cb == NULL) { 148 APPL_TRACE_ERROR("%s: wrong SCO handle to control block %d", __func__, 149 p_data->conn_evt.sco_inx); 150 return; 151 } 152 153 if (event != BTM_ESCO_CONN_REQ_EVT) { 154 return; 155 } 156 157 bta_hf_client_sco_conn_rsp(client_cb, &p_data->conn_evt); 158 159 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 160 } 161 162 /******************************************************************************* 163 * 164 * Function bta_hf_client_sco_conn_cback 165 * 166 * Description BTM SCO connection callback. 167 * 168 * 169 * Returns void 170 * 171 ******************************************************************************/ 172 static void bta_hf_client_sco_conn_cback(uint16_t sco_idx) { 173 APPL_TRACE_DEBUG("%s: %d", __func__, sco_idx); 174 175 tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_sco_handle(sco_idx); 176 if (client_cb == NULL) { 177 APPL_TRACE_ERROR("%s: wrong SCO handle to control block %d", __func__, 178 sco_idx); 179 return; 180 } 181 182 BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR)); 183 p_buf->event = BTA_HF_CLIENT_SCO_OPEN_EVT; 184 p_buf->layer_specific = client_cb->handle; 185 bta_sys_sendmsg(p_buf); 186 } 187 188 /******************************************************************************* 189 * 190 * Function bta_hf_client_sco_disc_cback 191 * 192 * Description BTM SCO disconnection callback. 193 * 194 * 195 * Returns void 196 * 197 ******************************************************************************/ 198 static void bta_hf_client_sco_disc_cback(uint16_t sco_idx) { 199 APPL_TRACE_DEBUG("%s: sco_idx %d", __func__, sco_idx); 200 201 tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_sco_handle(sco_idx); 202 if (client_cb == NULL) { 203 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, sco_idx); 204 return; 205 } 206 207 BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR)); 208 p_buf->event = BTA_HF_CLIENT_SCO_CLOSE_EVT; 209 p_buf->layer_specific = client_cb->handle; 210 bta_sys_sendmsg(p_buf); 211 } 212 213 /******************************************************************************* 214 * 215 * Function bta_hf_client_create_sco 216 * 217 * Description 218 * 219 * 220 * Returns void 221 * 222 ******************************************************************************/ 223 static void bta_hf_client_sco_create(tBTA_HF_CLIENT_CB* client_cb, 224 bool is_orig) { 225 tBTM_STATUS status; 226 227 APPL_TRACE_DEBUG("%s: %d", __func__, is_orig); 228 229 /* Make sure this SCO handle is not already in use */ 230 if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) { 231 APPL_TRACE_WARNING("%s: Index 0x%04x already in use", __func__, 232 client_cb->sco_idx); 233 return; 234 } 235 236 enh_esco_params_t params = esco_parameters_for_codec(ESCO_CODEC_CVSD); 237 238 /* if initiating set current scb and peer bd addr */ 239 if (is_orig) { 240 BTM_SetEScoMode(¶ms); 241 /* tell sys to stop av if any */ 242 bta_sys_sco_use(BTA_ID_HS, 1, client_cb->peer_addr); 243 } 244 245 status = BTM_CreateSco(&client_cb->peer_addr, is_orig, params.packet_types, 246 &client_cb->sco_idx, bta_hf_client_sco_conn_cback, 247 bta_hf_client_sco_disc_cback); 248 if (status == BTM_CMD_STARTED && !is_orig) { 249 if (!BTM_RegForEScoEvts(client_cb->sco_idx, 250 bta_hf_client_esco_connreq_cback)) 251 APPL_TRACE_DEBUG("%s: SCO registration success", __func__); 252 } 253 254 APPL_TRACE_API("%s: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x", 255 __func__, is_orig, client_cb->sco_idx, status, 256 params.packet_types); 257 } 258 259 /******************************************************************************* 260 * 261 * Function bta_hf_client_sco_event 262 * 263 * Description Handle SCO events 264 * 265 * 266 * Returns void 267 * 268 ******************************************************************************/ 269 static void bta_hf_client_sco_event(tBTA_HF_CLIENT_CB* client_cb, 270 uint8_t event) { 271 APPL_TRACE_DEBUG("%s: before state: %d event: %d", __func__, 272 client_cb->sco_state, event); 273 274 switch (client_cb->sco_state) { 275 case BTA_HF_CLIENT_SCO_SHUTDOWN_ST: 276 switch (event) { 277 // For WBS we only listen to SCO requests. Even for outgoing SCO 278 // requests we first do a AT+BCC and wait for remote to initiate SCO 279 case BTA_HF_CLIENT_SCO_LISTEN_E: 280 /* create SCO listen connection */ 281 bta_hf_client_sco_create(client_cb, false); 282 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 283 break; 284 285 // For non WBS cases and enabling outgoing SCO requests we need to force 286 // open a SCO channel 287 case BTA_HF_CLIENT_SCO_OPEN_E: 288 /* remove listening connection */ 289 bta_hf_client_sco_remove(client_cb); 290 291 /* create SCO connection to peer */ 292 bta_hf_client_sco_create(client_cb, true); 293 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 294 break; 295 296 default: 297 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event %d", 298 event); 299 break; 300 } 301 break; 302 303 case BTA_HF_CLIENT_SCO_LISTEN_ST: 304 switch (event) { 305 case BTA_HF_CLIENT_SCO_LISTEN_E: 306 /* create SCO listen connection */ 307 bta_hf_client_sco_create(client_cb, false); 308 break; 309 310 case BTA_HF_CLIENT_SCO_OPEN_E: 311 /* remove listening connection */ 312 bta_hf_client_sco_remove(client_cb); 313 314 /* create SCO connection to peer */ 315 bta_hf_client_sco_create(client_cb, true); 316 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 317 break; 318 319 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 320 case BTA_HF_CLIENT_SCO_CLOSE_E: 321 /* remove listening connection */ 322 bta_hf_client_sco_remove(client_cb); 323 324 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 325 break; 326 327 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 328 /* SCO failed; create SCO listen connection */ 329 bta_hf_client_sco_create(client_cb, false); 330 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 331 break; 332 333 default: 334 APPL_TRACE_WARNING( 335 "%s: BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", __func__, 336 event); 337 break; 338 } 339 break; 340 341 case BTA_HF_CLIENT_SCO_OPENING_ST: 342 switch (event) { 343 case BTA_HF_CLIENT_SCO_CLOSE_E: 344 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_CL_ST; 345 break; 346 347 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 348 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 349 break; 350 351 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 352 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_ST; 353 break; 354 355 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 356 /* SCO failed; create SCO listen connection */ 357 bta_hf_client_sco_create(client_cb, false); 358 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 359 break; 360 361 default: 362 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPENING_ST: Ignoring event %d", 363 event); 364 break; 365 } 366 break; 367 368 case BTA_HF_CLIENT_SCO_OPEN_CL_ST: 369 switch (event) { 370 case BTA_HF_CLIENT_SCO_OPEN_E: 371 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 372 break; 373 374 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 375 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 376 break; 377 378 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 379 /* close SCO connection */ 380 bta_hf_client_sco_remove(client_cb); 381 382 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 383 break; 384 385 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 386 /* SCO failed; create SCO listen connection */ 387 388 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 389 break; 390 391 default: 392 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_CL_ST: Ignoring event %d", 393 event); 394 break; 395 } 396 break; 397 398 case BTA_HF_CLIENT_SCO_OPEN_ST: 399 switch (event) { 400 case BTA_HF_CLIENT_SCO_CLOSE_E: 401 if (bta_hf_client_sco_remove(client_cb)) { 402 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 403 } 404 break; 405 406 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 407 /* remove listening connection */ 408 bta_hf_client_sco_remove(client_cb); 409 410 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 411 break; 412 413 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 414 /* peer closed SCO */ 415 bta_hf_client_sco_create(client_cb, false); 416 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 417 break; 418 419 default: 420 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_ST: Ignoring event %d", 421 event); 422 break; 423 } 424 break; 425 426 case BTA_HF_CLIENT_SCO_CLOSING_ST: 427 switch (event) { 428 case BTA_HF_CLIENT_SCO_OPEN_E: 429 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSE_OP_ST; 430 break; 431 432 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 433 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 434 break; 435 436 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 437 /* peer closed sco; create SCO listen connection */ 438 bta_hf_client_sco_create(client_cb, false); 439 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 440 break; 441 442 default: 443 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSING_ST: Ignoring event %d", 444 event); 445 break; 446 } 447 break; 448 449 case BTA_HF_CLIENT_SCO_CLOSE_OP_ST: 450 switch (event) { 451 case BTA_HF_CLIENT_SCO_CLOSE_E: 452 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 453 break; 454 455 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 456 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 457 break; 458 459 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 460 /* open SCO connection */ 461 bta_hf_client_sco_create(client_cb, true); 462 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 463 break; 464 465 default: 466 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSE_OP_ST: Ignoring event %d", 467 event); 468 break; 469 } 470 break; 471 472 case BTA_HF_CLIENT_SCO_SHUTTING_ST: 473 switch (event) { 474 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 475 /* close SCO connection; wait for conn close event */ 476 bta_hf_client_sco_remove(client_cb); 477 break; 478 479 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 480 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 481 break; 482 483 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 484 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 485 break; 486 487 default: 488 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTTING_ST: Ignoring event %d", 489 event); 490 break; 491 } 492 break; 493 494 default: 495 break; 496 } 497 498 APPL_TRACE_DEBUG("%s: after state: %d", __func__, client_cb->sco_state); 499 } 500 501 /******************************************************************************* 502 * 503 * Function bta_hf_client_sco_listen 504 * 505 * Description Initialize SCO listener 506 * 507 * 508 * Returns void 509 * 510 ******************************************************************************/ 511 void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA* p_data) { 512 APPL_TRACE_DEBUG("%s", __func__); 513 514 tBTA_HF_CLIENT_CB* client_cb = 515 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 516 if (client_cb == NULL) { 517 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 518 p_data->hdr.layer_specific); 519 return; 520 } 521 522 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_LISTEN_E); 523 } 524 525 /******************************************************************************* 526 * 527 * Function bta_hf_client_sco_shutdown 528 * 529 * Description 530 * 531 * 532 * Returns void 533 * 534 ******************************************************************************/ 535 void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_CB* client_cb) { 536 APPL_TRACE_DEBUG("%s", __func__); 537 538 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_SHUTDOWN_E); 539 } 540 541 /******************************************************************************* 542 * 543 * Function bta_hf_client_sco_conn_open 544 * 545 * Description 546 * 547 * 548 * Returns void 549 * 550 ******************************************************************************/ 551 void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA* p_data) { 552 APPL_TRACE_DEBUG("%s", __func__); 553 554 tBTA_HF_CLIENT_CB* client_cb = 555 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 556 if (client_cb == NULL) { 557 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 558 p_data->hdr.layer_specific); 559 return; 560 } 561 562 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_OPEN_E); 563 564 bta_sys_sco_open(BTA_ID_HS, 1, client_cb->peer_addr); 565 566 if (client_cb->negotiated_codec == BTM_SCO_CODEC_MSBC) { 567 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT); 568 } else { 569 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_OPEN_EVT); 570 } 571 } 572 573 /******************************************************************************* 574 * 575 * Function bta_hf_client_sco_conn_close 576 * 577 * Description 578 * 579 * 580 * Returns void 581 * 582 ******************************************************************************/ 583 void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA* p_data) { 584 APPL_TRACE_DEBUG("%s", __func__); 585 586 tBTA_HF_CLIENT_CB* client_cb = 587 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 588 if (client_cb == NULL) { 589 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 590 p_data->hdr.layer_specific); 591 return; 592 } 593 594 /* clear current scb */ 595 client_cb->sco_idx = BTM_INVALID_SCO_INDEX; 596 597 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_CLOSE_E); 598 599 bta_sys_sco_close(BTA_ID_HS, 1, client_cb->peer_addr); 600 601 bta_sys_sco_unuse(BTA_ID_HS, 1, client_cb->peer_addr); 602 603 /* call app callback */ 604 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_CLOSE_EVT); 605 606 if (client_cb->sco_close_rfc) { 607 client_cb->sco_close_rfc = false; 608 bta_hf_client_rfc_do_close(p_data); 609 } 610 } 611 612 /******************************************************************************* 613 * 614 * Function bta_hf_client_sco_open 615 * 616 * Description 617 * 618 * 619 * Returns void 620 * 621 ******************************************************************************/ 622 void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA* p_data) { 623 APPL_TRACE_DEBUG("%s", __func__); 624 625 tBTA_HF_CLIENT_CB* client_cb = 626 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 627 if (client_cb == NULL) { 628 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 629 p_data->hdr.layer_specific); 630 return; 631 } 632 633 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_OPEN_E); 634 } 635 636 /******************************************************************************* 637 * 638 * Function bta_hf_client_sco_close 639 * 640 * Description 641 * 642 * 643 * Returns void 644 * 645 ******************************************************************************/ 646 void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA* p_data) { 647 tBTA_HF_CLIENT_CB* client_cb = 648 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 649 if (client_cb == NULL) { 650 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 651 p_data->hdr.layer_specific); 652 return; 653 } 654 655 APPL_TRACE_DEBUG("%s: sco_idx 0x%x", __func__, client_cb->sco_idx); 656 657 if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) { 658 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CLOSE_E); 659 } 660 } 661