1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*****************************************************************************
8  *
9  *  Filename:      btc_a2dp_control.c
10  *
11  *****************************************************************************/
12 #include "common/bt_target.h"
13 #include <string.h>
14 #include "common/bt_trace.h"
15 #include "bta/bta_api.h"
16 #include "bta/bta_av_api.h"
17 #include "btc/btc_manage.h"
18 #include "btc_av.h"
19 #include "btc_a2dp.h"
20 #include "btc_a2dp_control.h"
21 #include "btc_a2dp_sink.h"
22 #include "btc_a2dp_source.h"
23 #include "esp_a2dp_api.h"
24 
25 #if BTC_AV_INCLUDED
26 
27 typedef struct {
28     BOOLEAN data_channel_open; /* used only by A2DP sink */
29     UINT8 a2dp_cmd_pending; /* we can have max one command pending */
30 } tBTC_AA_CTRL_CB;
31 
32 static tBTC_AA_CTRL_CB btc_aa_ctrl_cb;
33 
btc_a2d_cb_to_app(esp_a2d_cb_event_t event,esp_a2d_cb_param_t * param)34 static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
35 {
36     esp_a2d_cb_t btc_a2d_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP);
37     if (btc_a2d_cb) {
38         btc_a2d_cb(event, param);
39     }
40 }
41 
a2dp_cmd_acknowledge(int cmd,int status)42 static inline void a2dp_cmd_acknowledge(int cmd, int status)
43 {
44     esp_a2d_cb_param_t param;
45 
46     param.media_ctrl_stat.cmd = cmd;
47     param.media_ctrl_stat.status = status;
48 
49     btc_a2d_cb_to_app(ESP_A2D_MEDIA_CTRL_ACK_EVT, &param);
50 }
51 
btc_a2dp_control_command_ack(int status)52 void btc_a2dp_control_command_ack(int status)
53 {
54     /* sanity check */
55     if (btc_aa_ctrl_cb.a2dp_cmd_pending == ESP_A2D_MEDIA_CTRL_NONE) {
56         APPL_TRACE_ERROR("warning : no command pending, ignore ack");
57         return;
58     }
59 
60     /* clear pending */
61     int cmd = btc_aa_ctrl_cb.a2dp_cmd_pending;
62     btc_aa_ctrl_cb.a2dp_cmd_pending = ESP_A2D_MEDIA_CTRL_NONE;
63 
64     a2dp_cmd_acknowledge(cmd, status);
65 }
66 
btc_a2dp_datapath_open(void)67 static void btc_a2dp_datapath_open(void)
68 {
69 #if BTC_AV_SRC_INCLUDED
70     if (btc_av_get_peer_sep() == AVDT_TSEP_SNK && btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) {
71         /* Start the media task to encode SBC */
72         btc_a2dp_source_start_audio_req();
73 
74         /* make sure we update any changed sbc encoder params */
75         btc_a2dp_source_encoder_update();
76     }
77 #endif
78 #if (BTC_AV_SINK_INCLUDED == TRUE)
79     btc_aa_ctrl_cb.data_channel_open = TRUE;
80 #endif
81 }
82 
btc_a2dp_control_get_datachnl_stat(void)83 BOOLEAN btc_a2dp_control_get_datachnl_stat(void)
84 {
85     return btc_aa_ctrl_cb.data_channel_open;
86 }
87 
btc_a2dp_control_set_datachnl_stat(BOOLEAN open)88 void btc_a2dp_control_set_datachnl_stat(BOOLEAN open)
89 {
90     btc_aa_ctrl_cb.data_channel_open = open;
91 }
92 
btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl)93 void btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl)
94 {
95     APPL_TRACE_DEBUG("BTC MEDIA (A2DP-DATA) EVENT %u", ctrl);
96 
97     if (btc_aa_ctrl_cb.a2dp_cmd_pending != ESP_A2D_MEDIA_CTRL_NONE) {
98         APPL_TRACE_WARNING("un-acked a2dp cmd: %u", btc_aa_ctrl_cb.a2dp_cmd_pending);
99         a2dp_cmd_acknowledge(ctrl, ESP_A2D_MEDIA_CTRL_ACK_BUSY);
100         return;
101     }
102 
103     btc_aa_ctrl_cb.a2dp_cmd_pending = ctrl;
104 
105     switch (ctrl) {
106     case ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY:
107 #if BTC_AV_SRC_INCLUDED
108         if (btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) {
109             if (btc_a2dp_source_is_task_shutting_down()) {
110                 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
111             } else if ((btc_av_stream_ready() == TRUE) ||
112                     (btc_av_stream_started_ready() == TRUE)) {
113                 /* check whether av is ready to setup a2dp datapath */
114                 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
115             } else {
116                 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
117             }
118         } else {
119             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
120         }
121 #else /* BTC_AV_SRC_INCLUDED */
122         btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
123 #endif /* #if BTC_AV_SRC_INCLUDED */
124         break;
125     case ESP_A2D_MEDIA_CTRL_START:
126         if (btc_av_stream_ready() == TRUE ) {
127             /* post start event */
128             btc_dispatch_sm_event(BTC_AV_START_STREAM_REQ_EVT, NULL, 0);
129 #if (BTC_AV_SINK_INCLUDED == TRUE)
130             if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) {
131                 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
132             }
133 #endif
134         } else if (btc_av_stream_started_ready()) {
135             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
136         } else {
137             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
138         }
139         break;
140     case ESP_A2D_MEDIA_CTRL_STOP:
141         if (btc_av_is_connected() == FALSE) {
142             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
143             break;
144         }
145 #if BTC_AV_SRC_INCLUDED
146         if (btc_av_get_peer_sep() == AVDT_TSEP_SNK && !btc_a2dp_source_is_streaming() &&
147                 btc_av_get_service_id() == BTA_A2DP_SOURCE_SERVICE_ID) {
148             /* we are already stopped, just ack back*/
149             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
150             break;
151         }
152 #endif /* BTC_AV_SRC_INCLUDED */
153         btc_dispatch_sm_event(BTC_AV_STOP_STREAM_REQ_EVT, NULL, 0);
154 #if (BTC_AV_SINK_INCLUDED == TRUE)
155         if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) {
156             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
157         }
158 #endif
159         break;
160     case ESP_A2D_MEDIA_CTRL_SUSPEND:
161         /* local suspend */
162         if (btc_av_stream_started_ready()) {
163             btc_dispatch_sm_event(BTC_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
164 #if (BTC_AV_SINK_INCLUDED == TRUE)
165             if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) {
166                 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
167             }
168 #endif
169         } else if (btc_av_is_connected() == TRUE) {
170             /* we are not in started state; just ack back ok. This can happen if we are
171                remotely suspended; clear REMOTE SUSPEND Flag */
172             btc_av_clear_remote_suspend_flag();
173             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
174         } else {
175             btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
176         }
177         break;
178     default :
179         APPL_TRACE_ERROR("### A2DP-MEDIA EVENT %u NOT HANDLED ###", ctrl);
180         btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
181         break;
182     }
183 }
184 
btc_a2dp_control_datapath_ctrl(uint32_t dp_evt)185 void btc_a2dp_control_datapath_ctrl(uint32_t dp_evt)
186 {
187     switch (dp_evt) {
188     case BTC_AV_DATAPATH_OPEN_EVT: {
189         btc_a2dp_datapath_open();
190         break;
191     }
192     default:
193         break;
194     }
195     return;
196 }
197 
btc_a2dp_control_init(void)198 bool btc_a2dp_control_init(void)
199 {
200     memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB));
201     return true;
202 }
203 
btc_a2dp_control_cleanup(void)204 void btc_a2dp_control_cleanup(void)
205 {
206     memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB));
207 }
208 
209 #endif /* #if BTC_AV_INCLUDED */
210