1/****************************************************************************** 2 * 3 * Copyright (c) 2014 The Android Open Source Project 4 * Copyright (C) 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 32enum { 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 ******************************************************************************/ 50static 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 ******************************************************************************/ 83void 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, (tBTA_HF_CLIENT*)&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 ******************************************************************************/ 103static 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_T1); 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 ******************************************************************************/ 141static 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 ******************************************************************************/ 172static 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 ******************************************************************************/ 198static 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 ******************************************************************************/ 223static 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_MSBC_T1); 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 ******************************************************************************/ 269static 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 309 case BTA_HF_CLIENT_SCO_OPEN_E: 310 /* remove listening connection */ 311 bta_hf_client_sco_remove(client_cb); 312 313 /* create SCO connection to peer */ 314 bta_hf_client_sco_create(client_cb, true); 315 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 316 break; 317 318 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 319 case BTA_HF_CLIENT_SCO_CLOSE_E: 320 /* remove listening connection */ 321 bta_hf_client_sco_remove(client_cb); 322 323 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 324 break; 325 326 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 327 /* SCO failed; create SCO listen connection */ 328 bta_hf_client_sco_create(client_cb, false); 329 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 330 break; 331 332 default: 333 APPL_TRACE_WARNING( 334 "%s: BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", __func__, 335 event); 336 break; 337 } 338 break; 339 340 case BTA_HF_CLIENT_SCO_OPENING_ST: 341 switch (event) { 342 case BTA_HF_CLIENT_SCO_CLOSE_E: 343 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_CL_ST; 344 break; 345 346 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 347 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 348 break; 349 350 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 351 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPEN_ST; 352 break; 353 354 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 355 /* SCO failed; create SCO listen connection */ 356 bta_hf_client_sco_create(client_cb, false); 357 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 358 break; 359 360 default: 361 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPENING_ST: Ignoring event %d", 362 event); 363 break; 364 } 365 break; 366 367 case BTA_HF_CLIENT_SCO_OPEN_CL_ST: 368 switch (event) { 369 case BTA_HF_CLIENT_SCO_OPEN_E: 370 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 371 break; 372 373 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 374 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 375 break; 376 377 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 378 /* close SCO connection */ 379 bta_hf_client_sco_remove(client_cb); 380 381 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 382 break; 383 384 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 385 /* SCO failed; create SCO listen connection */ 386 387 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 388 break; 389 390 default: 391 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_CL_ST: Ignoring event %d", 392 event); 393 break; 394 } 395 break; 396 397 case BTA_HF_CLIENT_SCO_OPEN_ST: 398 switch (event) { 399 case BTA_HF_CLIENT_SCO_CLOSE_E: 400 if (bta_hf_client_sco_remove(client_cb)) { 401 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 402 } 403 break; 404 405 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 406 /* remove listening connection */ 407 bta_hf_client_sco_remove(client_cb); 408 409 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 410 break; 411 412 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 413 /* peer closed SCO */ 414 bta_hf_client_sco_create(client_cb, false); 415 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 416 break; 417 418 default: 419 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_OPEN_ST: Ignoring event %d", 420 event); 421 break; 422 } 423 break; 424 425 case BTA_HF_CLIENT_SCO_CLOSING_ST: 426 switch (event) { 427 case BTA_HF_CLIENT_SCO_OPEN_E: 428 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSE_OP_ST; 429 break; 430 431 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 432 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 433 break; 434 435 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 436 /* peer closed sco; create SCO listen connection */ 437 bta_hf_client_sco_create(client_cb, false); 438 client_cb->sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST; 439 break; 440 441 default: 442 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSING_ST: Ignoring event %d", 443 event); 444 break; 445 } 446 break; 447 448 case BTA_HF_CLIENT_SCO_CLOSE_OP_ST: 449 switch (event) { 450 case BTA_HF_CLIENT_SCO_CLOSE_E: 451 client_cb->sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST; 452 break; 453 454 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 455 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST; 456 break; 457 458 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 459 /* open SCO connection */ 460 bta_hf_client_sco_create(client_cb, true); 461 client_cb->sco_state = BTA_HF_CLIENT_SCO_OPENING_ST; 462 break; 463 464 default: 465 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_CLOSE_OP_ST: Ignoring event %d", 466 event); 467 break; 468 } 469 break; 470 471 case BTA_HF_CLIENT_SCO_SHUTTING_ST: 472 switch (event) { 473 case BTA_HF_CLIENT_SCO_CONN_OPEN_E: 474 /* close SCO connection; wait for conn close event */ 475 bta_hf_client_sco_remove(client_cb); 476 break; 477 478 case BTA_HF_CLIENT_SCO_CONN_CLOSE_E: 479 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 480 break; 481 482 case BTA_HF_CLIENT_SCO_SHUTDOWN_E: 483 client_cb->sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST; 484 break; 485 486 default: 487 APPL_TRACE_WARNING("BTA_HF_CLIENT_SCO_SHUTTING_ST: Ignoring event %d", 488 event); 489 break; 490 } 491 break; 492 493 default: 494 break; 495 } 496 497 APPL_TRACE_DEBUG("%s: after state: %d", __func__, client_cb->sco_state); 498} 499 500/******************************************************************************* 501 * 502 * Function bta_hf_client_sco_listen 503 * 504 * Description Initialize SCO listener 505 * 506 * 507 * Returns void 508 * 509 ******************************************************************************/ 510void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA* p_data) { 511 APPL_TRACE_DEBUG("%s", __func__); 512 513 tBTA_HF_CLIENT_CB* client_cb = 514 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 515 if (client_cb == NULL) { 516 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 517 p_data->hdr.layer_specific); 518 return; 519 } 520 521 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_LISTEN_E); 522} 523 524/******************************************************************************* 525 * 526 * Function bta_hf_client_sco_shutdown 527 * 528 * Description 529 * 530 * 531 * Returns void 532 * 533 ******************************************************************************/ 534void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_CB* client_cb) { 535 APPL_TRACE_DEBUG("%s", __func__); 536 537 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_SHUTDOWN_E); 538} 539 540/******************************************************************************* 541 * 542 * Function bta_hf_client_sco_conn_open 543 * 544 * Description 545 * 546 * 547 * Returns void 548 * 549 ******************************************************************************/ 550void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA* p_data) { 551 APPL_TRACE_DEBUG("%s", __func__); 552 553 tBTA_HF_CLIENT_CB* client_cb = 554 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 555 if (client_cb == NULL) { 556 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 557 p_data->hdr.layer_specific); 558 return; 559 } 560 561 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_OPEN_E); 562 563 bta_sys_sco_open(BTA_ID_HS, 1, client_cb->peer_addr); 564 565 if (client_cb->negotiated_codec == BTM_SCO_CODEC_MSBC) { 566 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT); 567 } else { 568 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_OPEN_EVT); 569 } 570} 571 572/******************************************************************************* 573 * 574 * Function bta_hf_client_sco_conn_close 575 * 576 * Description 577 * 578 * 579 * Returns void 580 * 581 ******************************************************************************/ 582void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA* p_data) { 583 APPL_TRACE_DEBUG("%s", __func__); 584 585 tBTA_HF_CLIENT_CB* client_cb = 586 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 587 if (client_cb == NULL) { 588 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 589 p_data->hdr.layer_specific); 590 return; 591 } 592 593 /* clear current scb */ 594 client_cb->sco_idx = BTM_INVALID_SCO_INDEX; 595 596 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CONN_CLOSE_E); 597 598 bta_sys_sco_close(BTA_ID_HS, 1, client_cb->peer_addr); 599 600 bta_sys_sco_unuse(BTA_ID_HS, 1, client_cb->peer_addr); 601 602 /* call app callback */ 603 bta_hf_client_cback_sco(client_cb, BTA_HF_CLIENT_AUDIO_CLOSE_EVT); 604 605 if (client_cb->sco_close_rfc == true) { 606 client_cb->sco_close_rfc = false; 607 bta_hf_client_rfc_do_close(p_data); 608 } 609} 610 611/******************************************************************************* 612 * 613 * Function bta_hf_client_sco_open 614 * 615 * Description 616 * 617 * 618 * Returns void 619 * 620 ******************************************************************************/ 621void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA* p_data) { 622 APPL_TRACE_DEBUG("%s", __func__); 623 624 tBTA_HF_CLIENT_CB* client_cb = 625 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 626 if (client_cb == NULL) { 627 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 628 p_data->hdr.layer_specific); 629 return; 630 } 631 632 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_OPEN_E); 633} 634 635/******************************************************************************* 636 * 637 * Function bta_hf_client_sco_close 638 * 639 * Description 640 * 641 * 642 * Returns void 643 * 644 ******************************************************************************/ 645void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA* p_data) { 646 tBTA_HF_CLIENT_CB* client_cb = 647 bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); 648 if (client_cb == NULL) { 649 APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__, 650 p_data->hdr.layer_specific); 651 return; 652 } 653 654 APPL_TRACE_DEBUG("%s: sco_idx 0x%x", __func__, client_cb->sco_idx); 655 656 if (client_cb->sco_idx != BTM_INVALID_SCO_INDEX) { 657 bta_hf_client_sco_event(client_cb, BTA_HF_CLIENT_SCO_CLOSE_E); 658 } 659} 660