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, ¶m);
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