OSDN Git Service

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