From f360fe5b3eec331769935166be24a72a229bef1d Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Mon, 25 Dec 2017 19:39:37 +0800 Subject: [PATCH] component/bt: implement A2DP source 1. Modified symbol names of A2DP sink APIs include: - esp_a2d_data_cb_t --> esp_a2d_sink_data_cb_t - esp_a2d_register_data_callback --> esp_a2d_sink_register_data_callback --- components/bt/Kconfig | 20 +- components/bt/bluedroid/api/esp_a2dp_api.c | 174 +- .../bt/bluedroid/api/include/esp_a2dp_api.h | 170 +- components/bt/bluedroid/bta/av/bta_av_sbc.c | 2 +- components/bt/bluedroid/btc/core/btc_task.c | 4 +- .../btc/profile/std/a2dp/bta_av_co.c | 17 +- .../bluedroid/btc/profile/std/a2dp/btc_a2dp.c | 153 ++ .../btc/profile/std/a2dp/btc_a2dp_control.c | 220 +++ .../btc/profile/std/a2dp/btc_a2dp_sink.c | 763 ++++++++ .../btc/profile/std/a2dp/btc_a2dp_source.c | 1617 +++++++++++++++++ .../profile/std/a2dp/{btc_avk.c => btc_av.c} | 448 +++-- .../btc/profile/std/a2dp/btc_media_task.c | 1015 ----------- .../btc/profile/std/a2dp/include/btc_av_co.h | 6 +- .../btc/profile/std/include/btc_a2dp.h | 108 ++ .../profile/std/include/btc_a2dp_control.h | 110 ++ .../btc/profile/std/include/btc_a2dp_sink.h | 139 ++ .../{btc_media.h => btc_a2dp_source.h} | 237 ++- .../btc/profile/std/include/btc_av.h | 64 +- .../btc/profile/std/include/btc_av_api.h | 2 - components/bt/bluedroid/btif/bta_dm_co.c | 2 +- .../external/sbc/encoder/srce/sbc_analysis.c | 1 + components/bt/bluedroid/include/bt_target.h | 178 +- components/bt/bluedroid/osi/alarm.c | 22 +- components/bt/bluedroid/osi/include/alarm.h | 4 + components/bt/bluedroid/osi/include/thread.h | 4 +- components/bt/bluedroid/stack/a2dp/a2d_sbc.c | 2 +- components/bt/bluedroid/stack/avct/avct_api.c | 2 +- components/bt/bluedroid/stack/avct/avct_l2c.c | 2 +- components/bt/bluedroid/stack/avct/avct_lcb.c | 2 +- .../bt/bluedroid/stack/avct/avct_lcb_act.c | 2 +- components/bt/bluedroid/stack/avdt/avdt_ad.c | 2 +- components/bt/bluedroid/stack/avdt/avdt_ccb.c | 2 +- .../bt/bluedroid/stack/avdt/avdt_ccb_act.c | 2 +- components/bt/bluedroid/stack/avdt/avdt_l2c.c | 2 +- components/bt/bluedroid/stack/avdt/avdt_msg.c | 2 +- components/bt/bluedroid/stack/avdt/avdt_scb.c | 2 +- .../bt/bluedroid/stack/avdt/avdt_scb_act.c | 2 +- .../bt/bluedroid/stack/avrc/avrc_bld_tg.c | 2 +- .../bt/bluedroid/stack/avrc/avrc_pars_ct.c | 2 +- components/bt/bluedroid/utils/bt_utils.c | 34 - .../bt/bluedroid/utils/include/bt_utils.h | 46 - components/bt/component.mk | 2 + examples/bluetooth/a2dp_sink/README.md | 7 +- examples/bluetooth/a2dp_sink/main/main.c | 2 +- .../bluetooth/a2dp_sink/sdkconfig.defaults | 10 +- examples/bluetooth/a2dp_source/Makefile | 9 + examples/bluetooth/a2dp_source/README.md | 18 + .../bluetooth/a2dp_source/main/bt_app_core.c | 119 ++ .../bluetooth/a2dp_source/main/bt_app_core.h | 53 + .../bluetooth/a2dp_source/main/component.mk | 5 + examples/bluetooth/a2dp_source/main/main.c | 526 ++++++ .../bluetooth/a2dp_source/sdkconfig.defaults | 12 + .../bluetooth/bt_discovery/sdkconfig.defaults | 5 +- 53 files changed, 4836 insertions(+), 1520 deletions(-) create mode 100644 components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp.c create mode 100644 components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c create mode 100644 components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c create mode 100644 components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c rename components/bt/bluedroid/btc/profile/std/a2dp/{btc_avk.c => btc_av.c} (83%) delete mode 100644 components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c create mode 100644 components/bt/bluedroid/btc/profile/std/include/btc_a2dp.h create mode 100644 components/bt/bluedroid/btc/profile/std/include/btc_a2dp_control.h create mode 100644 components/bt/bluedroid/btc/profile/std/include/btc_a2dp_sink.h rename components/bt/bluedroid/btc/profile/std/include/{btc_media.h => btc_a2dp_source.h} (56%) delete mode 100644 components/bt/bluedroid/utils/bt_utils.c delete mode 100644 components/bt/bluedroid/utils/include/bt_utils.h create mode 100755 examples/bluetooth/a2dp_source/Makefile create mode 100755 examples/bluetooth/a2dp_source/README.md create mode 100644 examples/bluetooth/a2dp_source/main/bt_app_core.c create mode 100644 examples/bluetooth/a2dp_source/main/bt_app_core.h create mode 100755 examples/bluetooth/a2dp_source/main/component.mk create mode 100644 examples/bluetooth/a2dp_source/main/main.c create mode 100644 examples/bluetooth/a2dp_source/sdkconfig.defaults diff --git a/components/bt/Kconfig b/components/bt/Kconfig index ebc677530..b311a1be7 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -113,19 +113,29 @@ config CLASSIC_BT_ENABLED help For now this option needs "SMP_ENABLE" to be set to yes -config A2DP_SNK_ENABLED - bool "A2DP(SINK)" +config A2DP_ENABLE + bool "A2DP" depends on CLASSIC_BT_ENABLED default n help - This option is used to enable/disable Advanced Audio Distribution Profile(Sink) + Advanced Audio Distrubution Profile +choice A2DP_ROLE + prompt "A2DP ROLE config" + depends on A2DP_ENABLE + +config A2DP_SINK_ENABLE + bool "SINK" +config A2DP_SRC_ENABLE + bool "SOURCE" +endchoice + config BT_SPP_ENABLED - bool "SPP Profile" + bool "SPP" depends on CLASSIC_BT_ENABLED default n help - This enables the SPP Profile + This enables the Serial Port Profile config GATTS_ENABLE bool "Include GATT server module(GATTS)" diff --git a/components/bt/bluedroid/api/esp_a2dp_api.c b/components/bt/bluedroid/api/esp_a2dp_api.c index cb635908c..2141ce0c2 100644 --- a/components/bt/bluedroid/api/esp_a2dp_api.c +++ b/components/bt/bluedroid/api/esp_a2dp_api.c @@ -22,20 +22,7 @@ #if BTC_AV_INCLUDED -esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback) -{ - if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { - return ESP_ERR_INVALID_STATE; - } - - if (callback == NULL) { - return ESP_FAIL; - } - - btc_profile_cb_set(BTC_PID_A2DP, callback); - return ESP_OK; -} - +#if BTC_AV_SINK_INCLUDED esp_err_t esp_a2d_sink_init(void) { if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { @@ -43,7 +30,7 @@ esp_err_t esp_a2d_sink_init(void) } btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; msg.act = BTC_AV_SINK_API_INIT_EVT; @@ -58,9 +45,9 @@ esp_err_t esp_a2d_sink_deinit(void) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; msg.act = BTC_AV_SINK_API_DEINIT_EVT; @@ -70,7 +57,7 @@ esp_err_t esp_a2d_sink_deinit(void) return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; } -esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t callback) +esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback) { if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; @@ -82,11 +69,11 @@ esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t callback) msg.act = BTC_AV_SINK_API_REG_DATA_CB_EVT; btc_av_args_t arg; - memset(&arg, 0, sizeof(btc_av_args_t)); + memset(&arg, 0, sizeof(btc_av_args_t)); arg.data_cb = callback; - + /* Switch to BTC context */ - bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_msg_t), NULL); + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; } @@ -95,17 +82,17 @@ esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + bt_status_t stat; btc_av_args_t arg; btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; msg.act = BTC_AV_SINK_API_CONNECT_EVT; memset(&arg, 0, sizeof(btc_av_args_t)); - + /* Switch to BTC context */ memcpy(&(arg.connect), remote_bda, sizeof(bt_bdaddr_t)); stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); @@ -117,10 +104,10 @@ esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + bt_status_t stat; btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; msg.act = BTC_AV_SINK_API_DISCONNECT_EVT; @@ -130,4 +117,139 @@ esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda) return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; } +#endif /* BTC_AV_SINK_INCLUDED */ + +esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_A2DP, callback); + return ESP_OK; +} + +esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_API_MEDIA_CTRL_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + arg.ctrl = ctrl; + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#if BTC_AV_SRC_INCLUDED +esp_err_t esp_a2d_source_init(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_deinit(void) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_DEINIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_av_args_t arg; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_CONNECT_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + + /* Switch to BTC context */ + memcpy(&(arg.src_connect), remote_bda, sizeof(bt_bdaddr_t)); + stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_disconnect(esp_bd_addr_t remote_bda) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + bt_status_t stat; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_DISCONNECT_EVT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SRC_API_REG_DATA_CB_EVT; + + btc_av_args_t arg; + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.src_data_cb = callback; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* BTC_AV_SRC_INCLUDED */ + #endif /* #if BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/api/include/esp_a2dp_api.h b/components/bt/bluedroid/api/include/esp_a2dp_api.h index 7f6868dde..ab1187146 100644 --- a/components/bt/bluedroid/api/include/esp_a2dp_api.h +++ b/components/bt/bluedroid/api/include/esp_a2dp_api.h @@ -67,11 +67,28 @@ typedef enum { ESP_A2D_AUDIO_STATE_STARTED, /*!< audio stream datapath started */ } esp_a2d_audio_state_t; +/// A2DP media control command acknowledgement code +typedef enum { + ESP_A2D_MEDIA_CTRL_ACK_SUCCESS = 0, /*!< media control command is acknowledged with success */ + ESP_A2D_MEDIA_CTRL_ACK_FAILURE, /*!< media control command is acknowledged with failure */ + ESP_A2D_MEDIA_CTRL_ACK_BUSY, /*!< media control command is rejected, as previous command is not yet acknowledged */ +} esp_a2d_media_ctrl_ack_t; + +/// A2DP media control commands +typedef enum { + ESP_A2D_MEDIA_CTRL_NONE = 0, /*!< dummy command */ + ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY, /*!< check whether AVDTP is connected, only used in A2DP source */ + ESP_A2D_MEDIA_CTRL_START, /*!< command to set up media transmission channel */ + ESP_A2D_MEDIA_CTRL_STOP, /*!< command to stop media transmission */ + ESP_A2D_MEDIA_CTRL_SUSPEND, /*!< command to suspend media transmission */ +} esp_a2d_media_ctrl_t; + /// A2DP callback events typedef enum { ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ - ESP_A2D_AUDIO_STATE_EVT = 1, /*!< audio stream transmission state changed event */ - ESP_A2D_AUDIO_CFG_EVT = 2 /*!< audio codec is configured */ + ESP_A2D_AUDIO_STATE_EVT, /*!< audio stream transmission state changed event */ + ESP_A2D_AUDIO_CFG_EVT, /*!< audio codec is configured, only used for A2DP SINK */ + ESP_A2D_MEDIA_CTRL_ACK_EVT, /*!< acknowledge event in response to media control commands */ } esp_a2d_cb_event_t; /// A2DP state callback parameters @@ -84,7 +101,7 @@ typedef union { esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ esp_a2d_disc_rsn_t disc_rsn; /*!< reason of disconnection for "DISCONNECTED" */ } conn_stat; /*!< A2DP connection status */ - + /** * @brief ESP_A2D_AUDIO_STATE_EVT */ @@ -92,7 +109,7 @@ typedef union { esp_a2d_audio_state_t state; /*!< one of the values from esp_a2d_audio_state_t */ esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ } audio_stat; /*!< audio stream playing state */ - + /** * @brief ESP_A2D_AUDIO_CFG_EVT */ @@ -100,32 +117,52 @@ typedef union { esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */ } audio_cfg; /*!< media codec configuration infomation */ + + /** + * @brief ESP_A2D_MEDIA_CTRL_ACK_EVT + */ + struct media_ctrl_stat_param { + esp_a2d_media_ctrl_t cmd; /*!< media control commands to acknowledge */ + esp_a2d_media_ctrl_ack_t status; /*!< acknowledgement to media control commands */ + } media_ctrl_stat; /*!< status in acknowledgement to media control commands */ } esp_a2d_cb_param_t; /** * @brief A2DP profile callback function type + * * @param event : Event type + * * @param param : Pointer to callback parameter */ typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); /** * @brief A2DP profile data callback function - * * @param[in] buf : data received from A2DP source device and is PCM format decoder from SBC decoder; * buf references to a static memory block and can be overwritten by upcoming data - * * @param[in] len : size(in bytes) in buf + */ +typedef void (* esp_a2d_sink_data_cb_t)(const uint8_t *buf, uint32_t len); + +/** + * @brief A2DP source data read callback function + * + * @param[in] buf : buffer to be filled with PCM data stream from higer layer + * + * @param[in] len : size(in bytes) of data block to be copied to buf. -1 is an indication to user + * that data buffer shall be flushed + * + * @return size of bytes read successfully, if the argumetn len is -1, this value is ignored. * */ -typedef void (* esp_a2d_data_cb_t)(const uint8_t *buf, uint32_t len); - +typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len); /** * @brief Register application callback function to A2DP module. This function should be called - * only after esp_bluedroid_enable() completes successfully - * - * @param[in] callback: A2DP sink event callback function + * only after esp_bluedroid_enable() completes successfully, used by both A2DP source + * and sink. + * + * @param[in] callback: A2DP event callback function * * @return * - ESP_OK: success @@ -138,10 +175,10 @@ esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback); /** * @brief Register A2DP sink data output function; For now the output is PCM data stream decoded - * from SBC format. This function should be called only after esp_bluedroid_enable() - * completes successfully - * - * @param[in] callback: A2DP data callback function + * from SBC format. This function should be called only after esp_bluedroid_enable() + * completes successfully, used only by A2DP sink. + * + * @param[in] callback: A2DP sink data callback function * * @return * - ESP_OK: success @@ -149,7 +186,7 @@ esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback); * - ESP_FAIL: if callback is a NULL function pointer * */ -esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t callback); +esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback); /** @@ -157,7 +194,7 @@ esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t callback); * @brief Initialize the bluetooth A2DP sink module. This function should be called * after esp_bluedroid_enable() completes successfully * - * @return + * @return * - ESP_OK: if the initialization request is sent successfully * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others @@ -168,10 +205,10 @@ esp_err_t esp_a2d_sink_init(void); /** * - * @brief De-initialize for A2DP sink module. This function + * @brief De-initialize for A2DP sink module. This function * should be called only after esp_bluedroid_enable() completes successfully * - * @return + * @return * - ESP_OK: success * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others @@ -182,11 +219,11 @@ esp_err_t esp_a2d_sink_deinit(void); /** * - * @brief Connect the remote bluetooth device bluetooth, must after esp_a2d_sink_init() + * @brief Connect to remote bluetooth A2DP source device, must after esp_a2d_sink_init() * * @param[in] remote_bda: remote bluetooth device address * - * @return + * @return * - ESP_OK: connect request is sent to lower layer * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others @@ -197,10 +234,10 @@ esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda); /** * - * @brief Disconnect the remote bluetooth device + * @brief Disconnect from the remote A2DP source device * * @param[in] remote_bda: remote bluetooth device address - * @return + * @return * - ESP_OK: disconnect request is sent to lower layer * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others @@ -208,6 +245,93 @@ esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda); */ esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief media control commands; this API can be used for both A2DP sink and source + * + * @param[in] ctrl: control commands for A2DP data channel + * @return + * - ESP_OK: control command is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl); + + +/** + * + * @brief Initialize the bluetooth A2DP source module. This function should be called + * after esp_bluedroid_enable() completes successfully + * + * @return + * - ESP_OK: if the initialization request is sent successfully + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_init(void); + + +/** + * + * @brief De-initialize for A2DP source module. This function + * should be called only after esp_bluedroid_enable() completes successfully + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_deinit(void); + + +/** + * @brief Register A2DP source data input function; For now the input is PCM data stream. + * This function should be called only after esp_bluedroid_enable() completes + * successfully + * + * @param[in] callback: A2DP source data callback function + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback); + + +/** + * + * @brief Connect to remote A2DP sink device, must after esp_a2d_source_init() + * + * @param[in] remote_bda: remote bluetooth device address + * + * @return + * - ESP_OK: connect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda); + + +/** + * + * @brief Disconnect from the remote A2DP sink device + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_a2d_source_disconnect(esp_bd_addr_t remote_bda); + #ifdef __cplusplus } #endif diff --git a/components/bt/bluedroid/bta/av/bta_av_sbc.c b/components/bt/bluedroid/bta/av/bta_av_sbc.c index 812876ff2..9ea59170a 100644 --- a/components/bt/bluedroid/bta/av/bta_av_sbc.c +++ b/components/bt/bluedroid/bta/av/bta_av_sbc.c @@ -28,7 +28,7 @@ #include "a2d_sbc.h" #include "bta_av_sbc.h" #include "utl.h" -#include "bt_utils.h" +#include "bt_defs.h" #if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 0d039c630..2a1147296 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -30,14 +30,14 @@ #include "btc_alarm.h" #include "bta_gatt_api.h" #if CONFIG_CLASSIC_BT_ENABLED +#include "btc_profile_queue.h" #if (BTC_GAP_BT_INCLUDED == TRUE) #include "btc_gap_bt.h" #endif /* BTC_GAP_BT_INCLUDED == TRUE */ -#include "btc_profile_queue.h" #if BTC_AV_INCLUDED #include "btc_av.h" #include "btc_avrc.h" -#endif /* #if BTC_AV_INCLUDED */ +#endif /* #if BTC_AV_INCLUDED */ #if CONFIG_BT_SPP_ENABLED #include "btc_spp.h" #endif /* #if CONFIG_BT_SPP_ENABLED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c b/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c index 59c06d589..d6c360544 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c @@ -31,7 +31,8 @@ #include "bta_av_co.h" #include "bta_av_ci.h" #include "bta_av_sbc.h" -#include "btc_media.h" +#include "btc_a2dp.h" +#include "btc_a2dp_source.h" #include "btc_av_co.h" #include "btc_util.h" #include "mutex.h" @@ -954,12 +955,13 @@ extern void bta_av_co_audio_stop(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type) void *bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, UINT32 *p_len, UINT32 *p_timestamp) { +#if BTC_AV_SRC_INCLUDED BT_HDR *p_buf; UNUSED(p_len); FUNC_TRACE(); - p_buf = btc_media_aa_readbuf(); + p_buf = btc_a2dp_source_audio_readbuf(); if (p_buf != NULL) { switch (codec_type) { case BTA_AV_CODEC_SBC: @@ -992,6 +994,9 @@ void *bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, UINT32 *p_len, #endif } return p_buf; +#else /* BTC_AV_SRC_INCLUDED */ + return NULL; +#endif /* BTC_AV_SRC_INCLUDED */ } /******************************************************************************* @@ -1379,7 +1384,7 @@ static BOOLEAN bta_av_co_audio_media_supports_config(UINT8 codec_type, const UIN ** Returns TRUE if all opened devices support this codec, FALSE otherwise ** *******************************************************************************/ -BOOLEAN bta_av_co_audio_codec_supported(tBTC_STATUS *p_status) +BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status) { UINT8 index; UINT8 snk_index; @@ -1449,7 +1454,7 @@ BOOLEAN bta_av_co_audio_codec_supported(tBTC_STATUS *p_status) } } - *p_status = BTC_SUCCESS; + *p_status = BTC_AV_SUCCESS; return TRUE; } @@ -1488,7 +1493,7 @@ void bta_av_co_audio_codec_reset(void) ** Returns TRUE if successful, FALSE otherwise ** *******************************************************************************/ -BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_STATUS *p_status) +BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_AV_STATUS *p_status) { tA2D_SBC_CIE sbc_config; tBTC_AV_CODEC_INFO new_cfg; @@ -1555,7 +1560,7 @@ BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_ /* Check all devices support it */ - *p_status = BTC_SUCCESS; + *p_status = BTC_AV_SUCCESS; return bta_av_co_audio_codec_supported(p_status); } diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp.c new file mode 100644 index 000000000..29004c2f2 --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp.c @@ -0,0 +1,153 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/***************************************************************************** + * + * Filename: btc_a2dp.c + * + *****************************************************************************/ +#include "bt_target.h" +#include "bt_trace.h" +#include "bta_api.h" +#include "bta_av_api.h" +#include "btc_av.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" + + +#if BTC_AV_INCLUDED + +/***************************************************************************** +** +** Function btc_a2dp_on_init +** +*******************************************************************************/ +void btc_a2dp_on_init(void) +{ + //tput_mon(1, 0, 1); +} + +/***************************************************************************** +** +** Function btc_a2dp_on_idle +** +*******************************************************************************/ + +void btc_a2dp_on_idle(void) +{ + APPL_TRACE_EVENT("## ON A2DP IDLE ## peer_sep = %d", btc_av_get_peer_sep()); +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK) { + btc_a2dp_source_on_idle(); + } +#endif // BTC_AV_SRC_INCLUDED + + bta_av_co_init(); + +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC) { + btc_a2dp_sink_on_idle(); + } +#endif // BTC_AV_SINK_INCLUDED +} + +/***************************************************************************** +** +** Function btc_a2dp_on_started +** +** Description +** +** Returns +** +*******************************************************************************/ + +BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start) +{ + BOOLEAN ack = FALSE; + + APPL_TRACE_EVENT("## ON A2DP STARTED ##"); +#if BTC_AV_SRC_INCLUDED + if (p_av == NULL) { + /* ack back a local start request */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + return TRUE; + } + + if (p_av->status == BTA_AV_SUCCESS) { + if (p_av->suspending == FALSE) { + if (p_av->initiator) { + if (pending_start) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + ack = TRUE; + } + } else { + /* we were remotely started, make sure codec + is setup before datapath is started */ + btc_a2dp_source_setup_codec(); + } + + /* media task is autostarted upon a2dp audiopath connection */ + } + } else if (pending_start) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + ack = TRUE; + } +#endif /* BTC_AV_SRC_INCLUDED */ + return ack; +} + +/***************************************************************************** +** +** Function btc_a2dp_on_stopped +** +*******************************************************************************/ + +void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + APPL_TRACE_EVENT("## ON A2DP STOPPED ##"); +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC) { + btc_a2dp_sink_on_stopped(p_av); + return; + } +#endif // BTC_AV_SINK_INCLUDED + +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_on_stopped(p_av); +#endif // BTC_AV_SRC_INCLUDED +} + +/***************************************************************************** +** +** Function btc_a2dp_on_suspended +** +*******************************************************************************/ +void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##"); +#if BTC_AV_SINK_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC) { + btc_a2dp_sink_on_suspended(p_av); + return; + } +#endif // BTC_AV_SINK_INCLUDED +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_on_suspended(p_av); +#endif // BTC_AV_SRC_INCLUDED +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c new file mode 100644 index 000000000..c3ae55572 --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c @@ -0,0 +1,220 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/***************************************************************************** + * + * Filename: btc_a2dp_control.c + * + *****************************************************************************/ +#include "bt_target.h" +#include +#include "bt_trace.h" +#include "bta_api.h" +#include "bta_av_api.h" +#include "btc_manage.h" +#include "btc_av.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED + +typedef struct { + BOOLEAN data_channel_open; + UINT8 a2dp_cmd_pending; /* we can have max one command pending */ +} tBTC_AA_CTRL_CB; + +static tBTC_AA_CTRL_CB btc_aa_ctrl_cb; + +static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_a2d_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_a2d_cb) { + btc_a2d_cb(event, param); + } +} + +static inline void a2dp_cmd_acknowledge(int cmd, int status) +{ + esp_a2d_cb_param_t param; + + param.media_ctrl_stat.cmd = cmd; + param.media_ctrl_stat.status = status; + + btc_a2d_cb_to_app(ESP_A2D_MEDIA_CTRL_ACK_EVT, ¶m); +} + +void btc_a2dp_control_command_ack(int status) +{ + /* sanity check */ + if (btc_aa_ctrl_cb.a2dp_cmd_pending == ESP_A2D_MEDIA_CTRL_NONE) { + APPL_TRACE_ERROR("warning : no command pending, ignore ack"); + return; + } + + /* clear pending */ + int cmd = btc_aa_ctrl_cb.a2dp_cmd_pending; + btc_aa_ctrl_cb.a2dp_cmd_pending = ESP_A2D_MEDIA_CTRL_NONE; + + a2dp_cmd_acknowledge(cmd, status); +} + +static void btc_a2dp_datapath_open(void) +{ +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK) { + /* Start the media task to encode SBC */ + btc_a2dp_source_start_audio_req(); + + /* make sure we update any changed sbc encoder params */ + btc_a2dp_source_encoder_update(); + } +#endif + btc_aa_ctrl_cb.data_channel_open = TRUE; +} + +BOOLEAN btc_a2dp_control_get_datachnl_stat(void) +{ + return btc_aa_ctrl_cb.data_channel_open; +} + +void btc_a2dp_control_set_datachnl_stat(BOOLEAN open) +{ + btc_aa_ctrl_cb.data_channel_open = open; +} + +static void btc_a2dp_dispatch_datapath_evt(uint32_t dp_evt) +{ + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_DATAPATH_CTRL_EVT; + + btc_av_args_t arg; + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.dp_evt = dp_evt; + + /* Switch to BTC context */ + APPL_TRACE_DEBUG("%s sig %u act %u, dp_evt %u\n", __func__, msg.sig, msg.act, arg.dp_evt); + btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); +} + +void btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl) +{ + APPL_TRACE_DEBUG("BTC MEDIA (A2DP-DATA) EVENT %u", ctrl); + + if (btc_aa_ctrl_cb.a2dp_cmd_pending != ESP_A2D_MEDIA_CTRL_NONE) { + APPL_TRACE_DEBUG("un-acked a2dp cmd: %u", btc_aa_ctrl_cb.a2dp_cmd_pending); + a2dp_cmd_acknowledge(ctrl, ESP_A2D_MEDIA_CTRL_ACK_BUSY); + return; + } + + btc_aa_ctrl_cb.a2dp_cmd_pending = ctrl; + + switch (ctrl) { + case ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY: +#if BTC_AV_SRC_INCLUDED + if (btc_a2dp_source_is_task_shutting_down()) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } else if ((btc_av_stream_ready() == TRUE) || + (btc_av_stream_started_ready() == TRUE)) { + /* check whether av is ready to setup a2dp datapath */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } +#else /* BTC_AV_SRC_INCLUDED */ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); +#endif /* #if BTC_AV_SRC_INCLUDED */ + break; + case ESP_A2D_MEDIA_CTRL_START: + if (btc_av_stream_ready() == TRUE ) { + /* post start event and wait for audio path to open */ + btc_dispatch_sm_event(BTC_AV_START_STREAM_REQ_EVT, NULL, 0); + + btc_a2dp_dispatch_datapath_evt(BTC_AV_DATAPATH_OPEN_EVT); +#if (BTC_AV_SINK_INCLUDED == TRUE) + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } +#endif + } else if (btc_av_stream_started_ready()) { + btc_a2dp_dispatch_datapath_evt(BTC_AV_DATAPATH_OPEN_EVT); + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } else { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + break; + case ESP_A2D_MEDIA_CTRL_STOP: +#if BTC_AV_SRC_INCLUDED + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK && !btc_a2dp_source_is_streaming()) { + /* we are already stopped, just ack back*/ + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + break; + } +#endif /* BTC_AV_SRC_INCLUDED */ + btc_dispatch_sm_event(BTC_AV_STOP_STREAM_REQ_EVT, NULL, 0); +#if (BTC_AV_SINK_INCLUDED == TRUE) + if (btc_av_get_peer_sep() == AVDT_TSEP_SRC) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } +#endif + break; + + case ESP_A2D_MEDIA_CTRL_SUSPEND: + /* local suspend */ + if (btc_av_stream_started_ready()) { + btc_dispatch_sm_event(BTC_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0); + } else { + /* we are not in started state; just ack back ok. This can happen if we are + remotely suspended; clear REMOTE SUSPEND Flag */ + btc_av_clear_remote_suspend_flag(); + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } + break; + + default : + APPL_TRACE_ERROR("### A2DP-MEDIA EVENT %u NOT HANDLED ###", ctrl); + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + break; + } +} + +void btc_a2dp_control_datapath_ctrl(uint32_t dp_evt) +{ + switch (dp_evt) { + case BTC_AV_DATAPATH_OPEN_EVT: { + btc_a2dp_datapath_open(); + break; + } + default: + break; + } + return; +} + +bool btc_a2dp_control_init(void) +{ + memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB)); + return true; +} + +void btc_a2dp_control_cleanup(void) +{ + memset(&btc_aa_ctrl_cb, 0, sizeof(tBTC_AA_CTRL_CB)); +} + +#endif /* #if BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c new file mode 100644 index 000000000..a246f2c0e --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -0,0 +1,763 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/****************************************************************************** + ** + ** Name: btc_a2dp_sink.c + ** + ******************************************************************************/ +#include "bt_target.h" +#include "bt_trace.h" +#include +#include +#include "bt_defs.h" +#include "allocator.h" +#include "mutex.h" +#include "thread.h" +#include "fixed_queue.h" +#include "a2d_api.h" +#include "a2d_sbc.h" +#include "bta_av_api.h" +#include "bta_av_ci.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_manage.h" +#include "btc_av.h" +#include "btc_util.h" +#include "esp_a2dp_api.h" +#include "oi_codec_sbc.h" +#include "oi_status.h" + +#if (BTC_AV_SINK_INCLUDED == TRUE) + +/***************************************************************************** + ** Constants + *****************************************************************************/ + +/* BTC media cmd event definition : BTC_MEDIA_TASK_CMD */ +enum { + BTC_MEDIA_TASK_SINK_INIT, + BTC_MEDIA_TASK_SINK_CLEAN_UP, + BTC_MEDIA_FLUSH_AA_RX, + BTC_MEDIA_AUDIO_SINK_CFG_UPDATE, + BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, +}; + +enum { + BTC_A2DP_SINK_STATE_OFF = 0, + BTC_A2DP_SINK_STATE_ON = 1, + BTC_A2DP_SINK_STATE_SHUTTING_DOWN = 2 +}; + +enum { + BTC_A2DP_SINK_DATA_EVT = 0, +}; + +/* + * CONGESTION COMPENSATION CTRL :: + * + * Thus setting controls how many buffers we will hold in media task + * during temp link congestion. Together with the stack buffer queues + * it controls much temporary a2dp link congestion we can + * compensate for. It however also depends on the default run level of sinks + * jitterbuffers. Depending on type of sink this would vary. + * Ideally the (SRC) max tx buffer capacity should equal the sinks + * jitterbuffer runlevel including any intermediate buffers on the way + * towards the sinks codec. + */ + +/* fixme -- define this in pcm time instead of buffer count */ + +/* The typical runlevel of the tx queue size is ~1 buffer + but due to link flow control or thread preemption in lower + layers we might need to temporarily buffer up data */ + +/* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */ +#define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (5) + +typedef struct { + UINT16 num_frames_to_be_processed; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} tBT_SBC_HDR; + +typedef struct { + BOOLEAN rx_flush; /* discards any incoming data when true */ + UINT8 channel_count; + fixed_queue_t *RxSbcQ; + UINT32 sample_rate; +} tBTC_A2DP_SINK_CB; + +static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context); +static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context); +static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q); +static void btc_a2dp_sink_rx_flush(void); +static int btc_a2dp_sink_get_track_frequency(UINT8 frequency); +static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype); +/* Handle incoming media packets A2DP SINK streaming*/ +static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg); +static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg); +static void btc_a2dp_sink_handle_clear_track(void); +static BOOLEAN btc_a2dp_sink_clear_track(void); +static void btc_a2dp_sink_task_handler(void *arg); + +static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context); + +static tBTC_A2DP_SINK_CB btc_aa_snk_cb; +static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; +static xTaskHandle btc_aa_snk_task_hdl = NULL; +static QueueHandle_t btc_aa_snk_data_queue = NULL; +static QueueHandle_t btc_aa_snk_ctrl_queue = NULL; +static QueueSetHandle_t btc_aa_snk_queue_set; + +static esp_a2d_sink_data_cb_t bt_aa_snk_data_cb = NULL; + +void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback) +{ + // todo: critical section protection + bt_aa_snk_data_cb = callback; +} + +static inline void btc_a2d_data_cb_to_app(const uint8_t *data, uint32_t len) +{ + // todo: critical section protection + if (bt_aa_snk_data_cb) { + bt_aa_snk_data_cb(data, len); + } +} + +OI_CODEC_SBC_DECODER_CONTEXT context; +OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; +OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; + +/***************************************************************************** + ** Misc helper functions + *****************************************************************************/ +static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_aa_cb) { + btc_aa_cb(event, param); + } +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ + +static void btc_a2dp_sink_ctrl_post(uint32_t sig, void *par) +{ + BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); + if (evt == NULL) { + return; + } + + evt->sig = sig; + evt->par = par; + + if (xQueueSend(btc_aa_snk_ctrl_queue, &evt, portMAX_DELAY) != pdTRUE) { + APPL_TRACE_WARNING("btc_aa_snk_ctrl_queue failed, sig 0x%x\n", sig); + } +} + +static void btc_a2dp_sink_ctrl_handler(BtTaskEvt_t *e) +{ + if (e == NULL) { + return; + } + switch (e->sig) { + case BTC_MEDIA_TASK_SINK_INIT: + btc_a2dp_sink_thread_init(NULL); + break; + case BTC_MEDIA_TASK_SINK_CLEAN_UP: + btc_a2dp_sink_thread_cleanup(NULL); + break; + case BTC_MEDIA_AUDIO_SINK_CFG_UPDATE: + btc_a2dp_sink_handle_decoder_reset(e->par); + break; + case BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK: + btc_a2dp_sink_handle_clear_track(); + break; + case BTC_MEDIA_FLUSH_AA_RX: + btc_a2dp_sink_rx_flush(); + break; + default: + APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", e->sig); + } + if (e->par != NULL) { + osi_free(e->par); + } +} + +static void btc_a2dp_sink_task_handler(void *arg) +{ + QueueSetMemberHandle_t xActivatedMember; + BtTaskEvt_t *e = NULL; + for (;;) { + xActivatedMember = xQueueSelectFromSet(btc_aa_snk_queue_set, portMAX_DELAY); + if (xActivatedMember == btc_aa_snk_data_queue) { + int32_t data_evt; + xQueueReceive(xActivatedMember, &data_evt, 0); + if (data_evt == BTC_A2DP_SINK_DATA_EVT) { + btc_a2dp_sink_data_ready(NULL); + } + } else if (xActivatedMember == btc_aa_snk_ctrl_queue) { + xQueueReceive(xActivatedMember, &e, 0); + btc_a2dp_sink_ctrl_handler(e); + osi_free(e); + } + } +} + +bool btc_a2dp_sink_startup(void) +{ + if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_OFF) { + APPL_TRACE_ERROR("warning : media task already running"); + return false; + } + + APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##"); + + btc_aa_snk_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN); + configASSERT(btc_aa_snk_queue_set); + btc_aa_snk_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(int32_t)); + configASSERT(btc_aa_snk_data_queue); + xQueueAddToSet(btc_aa_snk_data_queue, btc_aa_snk_queue_set); + + btc_aa_snk_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *)); + configASSERT(btc_aa_snk_ctrl_queue); + xQueueAddToSet(btc_aa_snk_ctrl_queue, btc_aa_snk_queue_set); + + if (!btc_aa_snk_data_queue || !btc_aa_snk_ctrl_queue || !btc_aa_snk_queue_set ) { + goto error_exit; + } + + xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE); + if (btc_aa_snk_task_hdl == NULL) { + goto error_exit; + } + + btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_INIT, NULL); + + APPL_TRACE_EVENT("## A2DP SINK MEDIA THREAD STARTED ##\n"); + + return true; + +error_exit:; + APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); + + if (btc_aa_snk_task_hdl != NULL) { + vTaskDelete(btc_aa_snk_task_hdl); + btc_aa_snk_task_hdl = NULL; + } + + if (btc_aa_snk_data_queue) { + vQueueDelete(btc_aa_snk_data_queue); + btc_aa_snk_data_queue = NULL; + } + if (btc_aa_snk_ctrl_queue) { + vQueueDelete(btc_aa_snk_ctrl_queue); + btc_aa_snk_ctrl_queue = NULL; + } + + return false; +} + +void btc_a2dp_sink_shutdown(void) +{ + APPL_TRACE_EVENT("## A2DP SINK STOP MEDIA THREAD ##\n"); + + // Exit thread + btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_CLEAN_UP, NULL); + + vTaskDelete(btc_aa_snk_task_hdl); + btc_aa_snk_task_hdl = NULL; + + vQueueDelete(btc_aa_snk_data_queue); + btc_aa_snk_data_queue = NULL; + + vQueueDelete(btc_aa_snk_ctrl_queue); + btc_aa_snk_ctrl_queue = NULL; +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_on_idle +** +*******************************************************************************/ + +void btc_a2dp_sink_on_idle(void) +{ + btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + btc_a2dp_sink_clear_track(); + + APPL_TRACE_DEBUG("Stopped BT track"); +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_on_stopped +** +*******************************************************************************/ + +void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + btc_a2dp_control_set_datachnl_stat(FALSE); +} + +/***************************************************************************** +** +** Function btc_a2dp_on_suspended +** +*******************************************************************************/ + +void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + btc_aa_snk_cb.rx_flush = TRUE; + btc_a2dp_sink_rx_flush_req(); + return; +} + +static void btc_a2dp_sink_data_post(int32_t data_type) +{ + if (xQueueSend(btc_aa_snk_data_queue, &data_type, 0) != pdTRUE) { + APPL_TRACE_DEBUG("Media data Q filled\n"); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_clear_track + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +static BOOLEAN btc_a2dp_sink_clear_track(void) +{ + btc_a2dp_sink_ctrl_post(BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, NULL); + return TRUE; +} + +/* when true media task discards any rx frames */ +void btc_a2dp_sink_set_rx_flush(BOOLEAN enable) +{ + APPL_TRACE_EVENT("## DROP RX %d ##\n", enable); + btc_aa_snk_cb.rx_flush = enable; +} + +/***************************************************************************** +** +** Function btc_a2dp_sink_reset_decoder +** +** Description +** +** Returns +** +*******************************************************************************/ + +void btc_a2dp_sink_reset_decoder(UINT8 *p_av) +{ + APPL_TRACE_EVENT("btc reset decoder"); + APPL_TRACE_DEBUG("btc reset decoder p_codec_info[%x:%x:%x:%x:%x:%x]\n", + p_av[1], p_av[2], p_av[3], + p_av[4], p_av[5], p_av[6]); + + tBTC_MEDIA_SINK_CFG_UPDATE *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_SINK_CFG_UPDATE)))) { + APPL_TRACE_ERROR("btc reset decoder No Buffer "); + return; + } + + memcpy(p_buf->codec_info, p_av, AVDT_CODEC_SIZE); + btc_a2dp_sink_ctrl_post(BTC_MEDIA_AUDIO_SINK_CFG_UPDATE, p_buf); +} + +static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context) +{ + tBT_SBC_HDR *p_msg; + + if (fixed_queue_is_empty(btc_aa_snk_cb.RxSbcQ)) { + APPL_TRACE_DEBUG(" QUE EMPTY "); + } else { + if (btc_aa_snk_cb.rx_flush == TRUE) { + btc_a2dp_sink_flush_q(btc_aa_snk_cb.RxSbcQ); + return; + } + + while ((p_msg = (tBT_SBC_HDR *)fixed_queue_try_peek_first(btc_aa_snk_cb.RxSbcQ)) != NULL ) { + btc_a2dp_sink_handle_inc_media(p_msg); + p_msg = (tBT_SBC_HDR *)fixed_queue_try_dequeue(btc_aa_snk_cb.RxSbcQ); + if ( p_msg == NULL ) { + APPL_TRACE_ERROR("Insufficient data in que "); + break; + } + osi_free(p_msg); + } + APPL_TRACE_DEBUG(" Process Frames - "); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_handle_decoder_reset + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg) +{ + tBTC_MEDIA_SINK_CFG_UPDATE *p_buf = p_msg; + tA2D_STATUS a2d_status; + tA2D_SBC_CIE sbc_cie; + OI_STATUS status; + UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/ + UINT32 num_blocks = 16; + UINT32 num_subbands = 8; + + APPL_TRACE_EVENT("%s p_codec_info[%x:%x:%x:%x:%x:%x]\n", __FUNCTION__, + p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3], + p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]); + + a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE); + if (a2d_status != A2D_SUCCESS) { + APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); + return; + } + + btc_aa_snk_cb.sample_rate = btc_a2dp_sink_get_track_frequency(sbc_cie.samp_freq); + btc_aa_snk_cb.channel_count = btc_a2dp_sink_get_track_channel_count(sbc_cie.ch_mode); + + btc_aa_snk_cb.rx_flush = FALSE; + APPL_TRACE_EVENT("Reset to sink role"); + status = OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } + + btc_a2dp_control_set_datachnl_stat(TRUE); + + switch (sbc_cie.samp_freq) { + case A2D_SBC_IE_SAMP_FREQ_16: + APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq); + freq_multiple = 16 * 20; + break; + case A2D_SBC_IE_SAMP_FREQ_32: + APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq); + freq_multiple = 32 * 20; + break; + case A2D_SBC_IE_SAMP_FREQ_44: + APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq); + freq_multiple = 441 * 2; + break; + case A2D_SBC_IE_SAMP_FREQ_48: + APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq); + freq_multiple = 48 * 20; + break; + default: + APPL_TRACE_DEBUG(" Unknown Frequency "); + break; + } + + switch (sbc_cie.ch_mode) { + case A2D_SBC_IE_CH_MD_MONO: + APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_DUAL: + APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_STEREO: + APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode); + break; + case A2D_SBC_IE_CH_MD_JOINT: + APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode); + break; + default: + APPL_TRACE_DEBUG(" Unknown Mode "); + break; + } + + switch (sbc_cie.block_len) { + case A2D_SBC_IE_BLOCKS_4: + APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len); + num_blocks = 4; + break; + case A2D_SBC_IE_BLOCKS_8: + APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len); + num_blocks = 8; + break; + case A2D_SBC_IE_BLOCKS_12: + APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len); + num_blocks = 12; + break; + case A2D_SBC_IE_BLOCKS_16: + APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len); + num_blocks = 16; + break; + default: + APPL_TRACE_DEBUG(" Unknown BlockLen "); + break; + } + + switch (sbc_cie.num_subbands) { + case A2D_SBC_IE_SUBBAND_4: + APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands); + num_subbands = 4; + break; + case A2D_SBC_IE_SUBBAND_8: + APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands); + num_subbands = 8; + break; + default: + APPL_TRACE_DEBUG(" Unknown SubBands "); + break; + } + + switch (sbc_cie.alloc_mthd) { + case A2D_SBC_IE_ALLOC_MD_S: + APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd); + break; + case A2D_SBC_IE_ALLOC_MD_L: + APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd); + break; + default: + APPL_TRACE_DEBUG(" Unknown Allocation Method"); + break; + } + + APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool); + + int frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1; + APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", frames_to_process); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_handle_inc_media + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) +{ + UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1); + int count; + UINT32 pcmBytes, availPcmBytes; + OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/ + OI_STATUS status; + int num_sbc_frames = p_msg->num_frames_to_be_processed; + UINT32 sbc_frame_len = p_msg->len - 1; + availPcmBytes = 2 * sizeof(pcmData); + + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (btc_aa_snk_cb.rx_flush)) { + APPL_TRACE_DEBUG(" State Changed happened in this tick "); + return; + } + + // ignore data if no one is listening + if (!btc_a2dp_control_get_datachnl_stat()) { + return; + } + + APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len); + + for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) { + pcmBytes = availPcmBytes; + status = OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE **)&sbc_start_frame, + (OI_UINT32 *)&sbc_frame_len, + (OI_INT16 *)pcmDataPointer, + (OI_UINT32 *)&pcmBytes); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("Decoding failure: %d\n", status); + break; + } + availPcmBytes -= pcmBytes; + pcmDataPointer += pcmBytes / 2; + p_msg->offset += (p_msg->len - 1) - sbc_frame_len; + p_msg->len = sbc_frame_len + 1; + } + + btc_a2d_data_cb_to_app((uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes)); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_sink_rx_flush_req(void) +{ + if (fixed_queue_is_empty(btc_aa_snk_cb.RxSbcQ) == TRUE) { /* Que is already empty */ + return TRUE; + } + + btc_a2dp_sink_ctrl_post(BTC_MEDIA_FLUSH_AA_RX, NULL); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_rx_flush(void) +{ + /* Flush all enqueued SBC buffers (encoded) */ + APPL_TRACE_DEBUG("btc_a2dp_sink_rx_flush"); + + btc_a2dp_sink_flush_q(btc_aa_snk_cb.RxSbcQ); +} + +static int btc_a2dp_sink_get_track_frequency(UINT8 frequency) +{ + int freq = 48000; + switch (frequency) { + case A2D_SBC_IE_SAMP_FREQ_16: + freq = 16000; + break; + case A2D_SBC_IE_SAMP_FREQ_32: + freq = 32000; + break; + case A2D_SBC_IE_SAMP_FREQ_44: + freq = 44100; + break; + case A2D_SBC_IE_SAMP_FREQ_48: + freq = 48000; + break; + } + return freq; +} + +static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype) +{ + int count = 1; + switch (channeltype) { + case A2D_SBC_IE_CH_MD_MONO: + count = 1; + break; + case A2D_SBC_IE_CH_MD_DUAL: + case A2D_SBC_IE_CH_MD_STEREO: + case A2D_SBC_IE_CH_MD_JOINT: + count = 2; + break; + } + return count; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_enque_buf + ** + ** Description This function is called by the av_co to fill A2DP Sink Queue + ** + ** + ** Returns size of the queue + *******************************************************************************/ +UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) +{ + tBT_SBC_HDR *p_msg; + + if (btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ + return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); + } + + if (fixed_queue_length(btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) { + APPL_TRACE_WARNING("Pkt dropped\n"); + return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); + } + + APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + "); + + /* allocate and Queue this buffer */ + if ((p_msg = (tBT_SBC_HDR *) osi_malloc(sizeof(tBT_SBC_HDR) + + p_pkt->offset + p_pkt->len)) != NULL) { + memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); + p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; + APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); + fixed_queue_enqueue(btc_aa_snk_cb.RxSbcQ, p_msg); + btc_a2dp_sink_data_post(BTC_A2DP_SINK_DATA_EVT); + } else { + /* let caller deal with a failed allocation */ + APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - "); + } + return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); +} + +static void btc_a2dp_sink_handle_clear_track (void) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_flush_q + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q) +{ + while (! fixed_queue_is_empty(p_q)) { + osi_free(fixed_queue_try_dequeue(p_q)); + } +} + +static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context) +{ + APPL_TRACE_EVENT("%s\n", __func__); + memset(&btc_aa_snk_cb, 0, sizeof(btc_aa_snk_cb)); + + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON; + + btc_aa_snk_cb.RxSbcQ = fixed_queue_new(SIZE_MAX); + + btc_a2dp_control_init(); +} + +static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context) +{ + /* make sure no channels are restarted while shutting down */ + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_SHUTTING_DOWN; + + btc_a2dp_control_set_datachnl_stat(FALSE); + /* Clear task flag */ + btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; + + btc_a2dp_control_cleanup(); + + fixed_queue_free(btc_aa_snk_cb.RxSbcQ, osi_free_func); +} + +#endif /* BTC_AV_SINK_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c new file mode 100644 index 000000000..1b2a01f7a --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -0,0 +1,1617 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/****************************************************************************** + ** + ** Name: btc_a2dp_source.c + ** + ******************************************************************************/ +#include "bt_target.h" +#include "bt_trace.h" +#include +#include +#include +#include +#include +#include +#include "allocator.h" +#include "alarm.h" +#include "thread.h" +#include "mutex.h" +#include "fixed_queue.h" +#include "a2d_api.h" +#include "a2d_sbc.h" +#include "bta_av_api.h" +#include "bta_av_sbc.h" +#include "bta_av_ci.h" +#include "btc_manage.h" +#include "btc_common.h" +#include "btc_av_co.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_source.h" +#include "btc_av.h" +#include "btc_util.h" +#include "esp_a2dp_api.h" +#include "sbc_encoder.h" + +#if BTC_AV_SRC_INCLUDED + +/***************************************************************************** + ** Constants + *****************************************************************************/ + +/* BTC source command event definition */ +enum { + BTC_MEDIA_TASK_INIT, + BTC_MEDIA_TASK_CLEAN_UP, + BTC_MEDIA_START_AA_TX, + BTC_MEDIA_STOP_AA_TX, + BTC_MEDIA_SBC_ENC_INIT, + BTC_MEDIA_SBC_ENC_UPDATE, + BTC_MEDIA_FLUSH_AA_TX, + BTC_MEDIA_AUDIO_FEEDING_INIT, +}; + +enum { + BTC_A2DP_SOURCE_STATE_OFF = 0, + BTC_A2DP_SOURCE_STATE_ON = 1, + BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN = 2 +}; + +enum { + BTC_A2DP_SOURCE_DATA_EVT = 1, +}; + +/* Media task tick in milliseconds, must be set to multiple of + (1000/TICKS_PER_SEC) */ +#define BTC_MEDIA_TIME_TICK_MS (30) +#define A2DP_DATA_READ_POLL_MS (BTC_MEDIA_TIME_TICK_MS / 2) + +#ifndef MAX_PCM_FRAME_NUM_PER_TICK +#define MAX_PCM_FRAME_NUM_PER_TICK 21 // 14 for 20ms +#endif + +#define BTC_MEDIA_AA_BUF_SIZE (4096+16) + +#if (BTA_AV_CO_CP_SCMS_T == TRUE) +#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1) +#else +#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE) +#endif + +#ifndef BTC_MEDIA_BITRATE_STEP +#define BTC_MEDIA_BITRATE_STEP 5 +#endif + +#ifndef BTC_A2DP_NON_EDR_MAX_RATE +#define BTC_A2DP_NON_EDR_MAX_RATE 229 +#endif + +/* Middle quality quality setting @ 44.1 khz */ +#define DEFAULT_SBC_BITRATE 328 + +/* + * CONGESTION COMPENSATION CTRL :: + * + * Thus setting controls how many buffers we will hold in media task + * during temp link congestion. Together with the stack buffer queues + * it controls much temporary a2dp link congestion we can + * compensate for. It however also depends on the default run level of sinks + * jitterbuffers. Depending on type of sink this would vary. + * Ideally the (SRC) max tx buffer capacity should equal the sinks + * jitterbuffer runlevel including any intermediate buffers on the way + * towards the sinks codec. + */ + +/* fixme -- define this in pcm time instead of buffer count */ + +/* The typical runlevel of the tx queue size is ~1 buffer + but due to link flow control or thread preemption in lower + layers we might need to temporarily buffer up data */ + +/* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */ +#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5) +#define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27) // 18 for 20ms tick + +typedef struct { + UINT16 num_frames_to_be_processed; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} tBT_SBC_HDR; + +typedef struct { + UINT32 aa_frame_counter; + INT32 aa_feed_counter; + INT32 aa_feed_residue; + UINT32 counter; + UINT32 bytes_per_tick; /* pcm bytes read each media task tick */ +} tBTC_AV_MEDIA_FEEDINGS_PCM_STATE; + +typedef union { + tBTC_AV_MEDIA_FEEDINGS_PCM_STATE pcm; +} tBTC_AV_MEDIA_FEEDINGS_STATE; + +typedef struct { + UINT8 TxTranscoding; + BOOLEAN tx_flush; /* discards any outgoing data when true */ + BOOLEAN is_tx_timer; + UINT16 TxAaMtuSize; + UINT32 timestamp; + fixed_queue_t *TxAaQ; + tBTC_AV_FEEDING_MODE feeding_mode; + tBTC_AV_MEDIA_FEEDINGS_STATE media_feeding_state; + tBTC_AV_MEDIA_FEEDINGS media_feeding; + SBC_ENC_PARAMS encoder; + osi_alarm_t *media_alarm; +} tBTC_A2DP_SOURCE_CB; + +static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context); +static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context); +static void btc_a2dp_source_flush_q(fixed_queue_t *p_q); + +static void btc_a2dp_source_feeding_state_reset(void); +static void btc_a2dp_source_send_aa_frame(void); +static void btc_a2dp_source_aa_start_tx(void); +static void btc_a2dp_source_aa_stop_tx(void); +static void btc_a2dp_source_enc_init(BT_HDR *p_msg); +static void btc_a2dp_source_enc_update(BT_HDR *p_msg); +static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg); +static void btc_a2dp_source_aa_tx_flush(void); +static void btc_a2dp_source_prep_2_send(UINT8 nb_frame); +static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context); +static void btc_a2dp_source_encoder_init(void); + +static tBTC_A2DP_SOURCE_CB btc_aa_src_cb; +static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; +static xTaskHandle btc_aa_src_task_hdl = NULL; +static QueueHandle_t btc_aa_src_data_queue = NULL; +static QueueHandle_t btc_aa_src_ctrl_queue = NULL; +static QueueSetHandle_t btc_aa_src_queue_set; + +static esp_a2d_source_data_cb_t btc_aa_src_data_cb = NULL; +static UINT64 last_frame_us = 0; + +void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback) +{ + // todo: critical section protection + btc_aa_src_data_cb = callback; +} + +static inline uint32_t btc_aa_src_data_read(uint8_t *data, int32_t len) +{ + // todo: critical section protection + if (btc_aa_src_data_cb) { + return btc_aa_src_data_cb(data, len); + } else { + return 0; + } +} + +/***************************************************************************** + ** Misc helper functions + *****************************************************************************/ +static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP); + if (btc_aa_cb) { + btc_aa_cb(event, param); + } +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ + +bool btc_a2dp_source_is_streaming(void) +{ + return btc_aa_src_cb.is_tx_timer == TRUE; +} + +bool btc_a2dp_source_is_task_shutting_down(void) +{ + return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; +} + +static void btc_a2dp_source_ctrl_post(uint32_t sig, void *par) +{ + BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); + if (evt == NULL) { + return; + } + + evt->sig = sig; + evt->par = par; + + if (xQueueSend(btc_aa_src_ctrl_queue, &evt, portMAX_DELAY) != pdTRUE) { + APPL_TRACE_WARNING("btc_aa_src_ctrl_queue failed, sig 0x%x\n", sig); + } +} + +static void btc_a2dp_source_ctrl_handler(BtTaskEvt_t *e) +{ + if (e == NULL) { + return; + } + switch (e->sig) { + case BTC_MEDIA_TASK_INIT: + btc_a2dp_source_thread_init(NULL); + break; + case BTC_MEDIA_TASK_CLEAN_UP: + btc_a2dp_source_thread_cleanup(NULL); + break; + case BTC_MEDIA_START_AA_TX: + btc_a2dp_source_aa_start_tx(); + break; + case BTC_MEDIA_STOP_AA_TX: + btc_a2dp_source_aa_stop_tx(); + break; + case BTC_MEDIA_SBC_ENC_INIT: + btc_a2dp_source_enc_init(e->par); + break; + case BTC_MEDIA_SBC_ENC_UPDATE: + btc_a2dp_source_enc_update(e->par); + break; + case BTC_MEDIA_AUDIO_FEEDING_INIT: + btc_a2dp_source_audio_feeding_init(e->par); + break; + case BTC_MEDIA_FLUSH_AA_TX: + btc_a2dp_source_aa_tx_flush(); + break; + default: + APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", e->sig); + } + if (e->par != NULL) { + osi_free(e->par); + } +} + +static void btc_a2dp_source_task_handler(void *arg) +{ + QueueSetMemberHandle_t xActivatedMember; + BtTaskEvt_t *e = NULL; + for (;;) { + xActivatedMember = xQueueSelectFromSet(btc_aa_src_queue_set, portMAX_DELAY); + if (xActivatedMember == btc_aa_src_data_queue) { + int32_t data_evt; + xQueueReceive(xActivatedMember, &data_evt, 0); + if (data_evt == BTC_A2DP_SOURCE_DATA_EVT) { + btc_a2dp_source_handle_timer(NULL); + } + } else if (xActivatedMember == btc_aa_src_ctrl_queue) { + xQueueReceive(xActivatedMember, &e, 0); + btc_a2dp_source_ctrl_handler(e); + osi_free(e); + } + } +} + +bool btc_a2dp_source_startup(void) +{ + if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_OFF) { + APPL_TRACE_ERROR("warning : media task already running"); + return false; + } + + APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##"); + + btc_aa_src_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN); + configASSERT(btc_aa_src_queue_set); + btc_aa_src_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *)); + configASSERT(btc_aa_src_data_queue); + xQueueAddToSet(btc_aa_src_data_queue, btc_aa_src_queue_set); + + btc_aa_src_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *)); + configASSERT(btc_aa_src_ctrl_queue); + xQueueAddToSet(btc_aa_src_ctrl_queue, btc_aa_src_queue_set); + + if (!btc_aa_src_data_queue || !btc_aa_src_ctrl_queue || !btc_aa_src_queue_set ) { + goto error_exit; + } + + xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_src_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE); + if (btc_aa_src_task_hdl == NULL) { + goto error_exit; + } + + btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_INIT, NULL); + + APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##\n"); + + return true; + +error_exit:; + APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); + + if (btc_aa_src_task_hdl != NULL) { + vTaskDelete(btc_aa_src_task_hdl); + btc_aa_src_task_hdl = NULL; + } + + if (btc_aa_src_data_queue) { + vQueueDelete(btc_aa_src_data_queue); + btc_aa_src_data_queue = NULL; + } + if (btc_aa_src_ctrl_queue) { + vQueueDelete(btc_aa_src_ctrl_queue); + btc_aa_src_ctrl_queue = NULL; + } + + return false; +} + +void btc_a2dp_source_shutdown(void) +{ + APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##\n"); + + // Exit thread + btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_CLEAN_UP, NULL); + + vTaskDelete(btc_aa_src_task_hdl); + btc_aa_src_task_hdl = NULL; + + vQueueDelete(btc_aa_src_data_queue); + btc_aa_src_data_queue = NULL; + + vQueueDelete(btc_aa_src_ctrl_queue); + btc_aa_src_ctrl_queue = NULL; +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_idle +** +*******************************************************************************/ +void btc_a2dp_source_on_idle(void) +{ + /* Make sure media task is stopped */ + btc_a2dp_source_stop_audio_req(); +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_stopped +** +*******************************************************************************/ +void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av) +{ + /* allow using this api for other than suspend */ + if (p_av != NULL) { + if (p_av->status != BTA_AV_SUCCESS) { + APPL_TRACE_EVENT("AV STOP FAILED (%d)", p_av->status); + if (p_av->initiator) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + return; + } + } + + /* ensure tx frames are immediately suspended */ + btc_aa_src_cb.tx_flush = 1; + + /* request to stop media task */ + btc_a2dp_source_tx_flush_req(); + btc_a2dp_source_stop_audio_req(); + + /* once stream is fully stopped we will ack back */ +} + +/***************************************************************************** +** +** Function btc_a2dp_source_on_suspended +** +** +*******************************************************************************/ + +void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) +{ + /* check for status failures */ + if (p_av->status != BTA_AV_SUCCESS) { + if (p_av->initiator == TRUE) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); + } + } + + /* once stream is fully stopped we will ack back */ + + /* ensure tx frames are immediately flushed */ + btc_aa_src_cb.tx_flush = 1; + + /* stop timer tick */ + btc_a2dp_source_stop_audio_req(); +} + +static void btc_a2dp_source_data_post(int32_t data_type) +{ + if (xQueueSend(btc_aa_src_data_queue, &data_type, 0) != pdTRUE) { + APPL_TRACE_DEBUG("Media data Q filled\n"); + } +} + +static UINT64 time_now_us() +{ +#if _POSIX_TIMERS + struct timespec ts_now; + clock_gettime(CLOCK_BOOTTIME, &ts_now); + return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_nsec / 1000); +#else + struct timeval ts_now; + gettimeofday(&ts_now, NULL); + return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_usec); +#endif +} + +static void log_tstamps_us(char *comment) +{ + static UINT64 prev_us = 0; + const UINT64 now_us = time_now_us(); + APPL_TRACE_DEBUG("[%s] ts %08llu, diff : %08llu, queue sz %d", comment, now_us, now_us - prev_us, + fixed_queue_length(btc_aa_src_cb.TxAaQ)); + prev_us = now_us; +} + +/* when true media task discards any tx frames */ +void btc_a2dp_source_set_tx_flush(BOOLEAN enable) +{ + APPL_TRACE_EVENT("## DROP TX %d ##", enable); + btc_aa_src_cb.tx_flush = enable; +} + +/***************************************************************************** +** +** Function btc_a2dp_source_setup_codec +** +** Description +** +** Returns +** +*******************************************************************************/ + +void btc_a2dp_source_setup_codec(void) +{ + tBTC_AV_MEDIA_FEEDINGS media_feeding; + tBTC_AV_STATUS status; + + APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n"); + + osi_mutex_global_lock(); + + /* for now hardcode 44.1 khz 16 bit stereo PCM format */ + media_feeding.cfg.pcm.sampling_freq = 44100; + media_feeding.cfg.pcm.bit_per_sample = 16; + media_feeding.cfg.pcm.num_channel = 2; + media_feeding.format = BTC_AV_CODEC_PCM; + + if (bta_av_co_audio_set_codec(&media_feeding, &status)) { + tBTC_MEDIA_INIT_AUDIO_FEEDING mfeed; + + /* Init the encoding task */ + btc_a2dp_source_encoder_init(); + + /* Build the media task configuration */ + mfeed.feeding = media_feeding; + mfeed.feeding_mode = BTC_AV_FEEDING_ASYNCHRONOUS; + /* Send message to Media task to configure transcoding */ + btc_a2dp_source_audio_feeding_init_req(&mfeed); + } + + osi_mutex_global_unlock(); +} + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_readbuf + ** + ** Description This function is called by the av_co to get the next buffer to send + ** + ** + ** Returns void + *******************************************************************************/ +BT_HDR *btc_a2dp_source_audio_readbuf(void) +{ + return fixed_queue_try_dequeue(btc_aa_src_cb.TxAaQ); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_start_audio_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_start_audio_req(void) +{ + btc_a2dp_source_ctrl_post(BTC_MEDIA_START_AA_TX, NULL); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_stop_audio_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_stop_audio_req(void) +{ + /* + * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to + * avoid a race condition during shutdown of the Bluetooth stack. + * This race condition is triggered when A2DP audio is streaming on + * shutdown: + * "btc_a2dp_on_stopped() -> btc_a2dp_source_stop_audio_req()" is called + * to stop the particular audio stream, and this happens right after + * the "cleanup() -> btc_a2dp_stop_media_task()" processing during + * the shutdown of the Bluetooth stack. + */ + if (btc_aa_src_ctrl_queue != NULL) { + btc_a2dp_source_ctrl_post(BTC_MEDIA_STOP_AA_TX, NULL); + } + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO)); + + btc_a2dp_source_ctrl_post(BTC_MEDIA_SBC_ENC_INIT, p_buf); + + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_update_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg) +{ + tBTC_MEDIA_UPDATE_AUDIO *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_UPDATE_AUDIO)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_UPDATE_AUDIO)); + btc_a2dp_source_ctrl_post(BTC_MEDIA_SBC_ENC_UPDATE, p_buf); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_feeding_init_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO_FEEDING *p_buf; + if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING)))) { + return FALSE; + } + + memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING)); + btc_a2dp_source_ctrl_post(BTC_MEDIA_AUDIO_FEEDING_INIT, p_buf); + return TRUE; +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_tx_flush_req + ** + ** Description + ** + ** Returns TRUE is success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_source_tx_flush_req(void) +{ + /* + * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to + * avoid a race condition during shutdown of the Bluetooth stack. + * This race condition is triggered when A2DP audio is streaming on + * shutdown: + * "btc_a2dp_on_stopped() -> btc_a2dp_source_tx_flush_req()" is called + * to stop the particular audio stream, and this happens right after + * the "cleanup() -> btc_a2dp_stop_media_task()" processing during + * the shutdown of the Bluetooth stack. + */ + if (btc_aa_src_ctrl_queue != NULL) { + btc_a2dp_source_ctrl_post(BTC_MEDIA_FLUSH_AA_TX, NULL); + } + + return TRUE; +} + +/***************************************************************************** + ** BTC ADAPTATION + *****************************************************************************/ +static UINT16 btc_a2dp_source_get_sbc_rate(void) +{ + UINT16 rate = DEFAULT_SBC_BITRATE; + + /* restrict bitrate if a2dp link is non-edr */ + if (!btc_av_is_peer_edr()) { + rate = BTC_A2DP_NON_EDR_MAX_RATE; + APPL_TRACE_DEBUG("non-edr a2dp sink detected, restrict rate to %d", rate); + } + return rate; +} + +static void btc_a2dp_source_encoder_init(void) +{ + UINT16 minmtu; + tBTC_MEDIA_INIT_AUDIO msg; + tA2D_SBC_CIE sbc_config; + + /* lookup table for converting channel mode */ + UINT16 codec_mode_tbl[5] = { SBC_JOINT_STEREO, SBC_STEREO, SBC_DUAL, 0, SBC_MONO }; + + /* lookup table for converting number of blocks */ + UINT16 codec_block_tbl[5] = { 16, 12, 8, 0, 4 }; + + /* lookup table to convert freq */ + UINT16 freq_block_tbl[5] = { SBC_sf48000, SBC_sf44100, SBC_sf32000, 0, SBC_sf16000 }; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + /* Retrieve the current SBC configuration (default if currently not used) */ + bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu); + msg.NumOfSubBands = (sbc_config.num_subbands == A2D_SBC_IE_SUBBAND_4) ? 4 : 8; + msg.NumOfBlocks = codec_block_tbl[sbc_config.block_len >> 5]; + msg.AllocationMethod = (sbc_config.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) ? SBC_LOUDNESS : SBC_SNR; + msg.ChannelMode = codec_mode_tbl[sbc_config.ch_mode >> 1]; + msg.SamplingFreq = freq_block_tbl[sbc_config.samp_freq >> 5]; + msg.MtuSize = minmtu; + + APPL_TRACE_EVENT("msg.ChannelMode %x", msg.ChannelMode); + + /* Init the media task to encode SBC properly */ + btc_a2dp_source_enc_init_req(&msg); +} + +void btc_a2dp_source_encoder_update(void) +{ + UINT16 minmtu; + tA2D_SBC_CIE sbc_config; + tBTC_MEDIA_UPDATE_AUDIO msg; + UINT8 pref_min; + UINT8 pref_max; + + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + /* Retrieve the current SBC configuration (default if currently not used) */ + bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu); + + APPL_TRACE_DEBUG("%s: Common min_bitpool:%d(0x%x) max_bitpool:%d(0x%x)", __FUNCTION__, + sbc_config.min_bitpool, sbc_config.min_bitpool, + sbc_config.max_bitpool, sbc_config.max_bitpool); + + if (sbc_config.min_bitpool > sbc_config.max_bitpool) { + APPL_TRACE_ERROR("%s: ERROR min_bitpool > max_bitpool", __FUNCTION__); + } + + /* check if remote sink has a preferred bitpool range */ + if (bta_av_co_get_remote_bitpool_pref(&pref_min, &pref_max) == TRUE) { + /* adjust our preferred bitpool with the remote preference if within + our capable range */ + + if (pref_min < sbc_config.min_bitpool) { + pref_min = sbc_config.min_bitpool; + } + + if (pref_max > sbc_config.max_bitpool) { + pref_max = sbc_config.max_bitpool; + } + + msg.MinBitPool = pref_min; + msg.MaxBitPool = pref_max; + + if ((pref_min != sbc_config.min_bitpool) || (pref_max != sbc_config.max_bitpool)) { + APPL_TRACE_EVENT("## adjusted our bitpool range to peer pref [%d:%d] ##", + pref_min, pref_max); + } + } else { + msg.MinBitPool = sbc_config.min_bitpool; + msg.MaxBitPool = sbc_config.max_bitpool; + } + + msg.MinMtuSize = minmtu; + + /* Update the media task to encode SBC properly */ + btc_a2dp_source_enc_update_req(&msg); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init + ** + ** Description Initialize encoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_enc_init(BT_HDR *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO *pInitAudio = (tBTC_MEDIA_INIT_AUDIO *) p_msg; + + APPL_TRACE_DEBUG("btc_a2dp_source_enc_init"); + + btc_aa_src_cb.timestamp = 0; + + /* SBC encoder config (enforced even if not used) */ + btc_aa_src_cb.encoder.s16ChannelMode = pInitAudio->ChannelMode; + btc_aa_src_cb.encoder.s16NumOfSubBands = pInitAudio->NumOfSubBands; + btc_aa_src_cb.encoder.s16NumOfBlocks = pInitAudio->NumOfBlocks; + btc_aa_src_cb.encoder.s16AllocationMethod = pInitAudio->AllocationMethod; + btc_aa_src_cb.encoder.s16SamplingFreq = pInitAudio->SamplingFreq; + + btc_aa_src_cb.encoder.u16BitRate = btc_a2dp_source_get_sbc_rate(); + + /* Default transcoding is PCM to SBC, modified by feeding configuration */ + btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) + < pInitAudio->MtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET + - sizeof(BT_HDR)) : pInitAudio->MtuSize; + + APPL_TRACE_EVENT("btc_a2dp_source_enc_init mtu %d, peer mtu %d", + btc_aa_src_cb.TxAaMtuSize, pInitAudio->MtuSize); + APPL_TRACE_EVENT(" ch mode %d, subnd %d, nb blk %d, alloc %d, rate %d, freq %d", + btc_aa_src_cb.encoder.s16ChannelMode, btc_aa_src_cb.encoder.s16NumOfSubBands, + btc_aa_src_cb.encoder.s16NumOfBlocks, + btc_aa_src_cb.encoder.s16AllocationMethod, btc_aa_src_cb.encoder.u16BitRate, + btc_aa_src_cb.encoder.s16SamplingFreq); + + /* Reset entirely the SBC encoder */ + SBC_Encoder_Init(&(btc_aa_src_cb.encoder)); + APPL_TRACE_DEBUG("btc_a2dp_source_enc_init bit pool %d", btc_aa_src_cb.encoder.s16BitPool); +} + + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_update + ** + ** Description Update encoding task + ** + ** Returns void + ** + *******************************************************************************/ + +static void btc_a2dp_source_enc_update(BT_HDR *p_msg) +{ + tBTC_MEDIA_UPDATE_AUDIO *pUpdateAudio = (tBTC_MEDIA_UPDATE_AUDIO *) p_msg; + SBC_ENC_PARAMS *pstrEncParams = &btc_aa_src_cb.encoder; + UINT16 s16SamplingFreq; + SINT16 s16BitPool = 0; + SINT16 s16BitRate; + SINT16 s16FrameLen; + UINT8 protect = 0; + + APPL_TRACE_DEBUG("%s : minmtu %d, maxbp %d minbp %d", __FUNCTION__, + pUpdateAudio->MinMtuSize, pUpdateAudio->MaxBitPool, pUpdateAudio->MinBitPool); + + /* Only update the bitrate and MTU size while timer is running to make sure it has been initialized */ + //if (btc_aa_src_cb.is_tx_timer) + { + btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - + BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) + < pUpdateAudio->MinMtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET + - sizeof(BT_HDR)) : pUpdateAudio->MinMtuSize; + + /* Set the initial target bit rate */ + pstrEncParams->u16BitRate = btc_a2dp_source_get_sbc_rate(); + + if (pstrEncParams->s16SamplingFreq == SBC_sf16000) { + s16SamplingFreq = 16000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf32000) { + s16SamplingFreq = 32000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf44100) { + s16SamplingFreq = 44100; + } else { + s16SamplingFreq = 48000; + } + + do { + if (pstrEncParams->s16NumOfBlocks == 0 || pstrEncParams->s16NumOfSubBands == 0 + || pstrEncParams->s16NumOfChannels == 0) { + APPL_TRACE_ERROR("%s - Avoiding division by zero...", __FUNCTION__); + APPL_TRACE_ERROR("%s - block=%d, subBands=%d, channels=%d", __FUNCTION__, + pstrEncParams->s16NumOfBlocks, pstrEncParams->s16NumOfSubBands, + pstrEncParams->s16NumOfChannels); + break; + } + + if ((pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) || + (pstrEncParams->s16ChannelMode == SBC_STEREO) ) { + s16BitPool = (SINT16)( (pstrEncParams->u16BitRate * + pstrEncParams->s16NumOfSubBands * 1000 / s16SamplingFreq) + - ( (32 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) + + ( (pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands ) ) + / pstrEncParams->s16NumOfBlocks) ); + + s16FrameLen = 4 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) / 8 + + ( ((pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands) + + (pstrEncParams->s16NumOfBlocks * s16BitPool) ) / 8; + + s16BitRate = (8 * s16FrameLen * s16SamplingFreq) + / (pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfBlocks * 1000); + + if (s16BitRate > pstrEncParams->u16BitRate) { + s16BitPool--; + } + + if (pstrEncParams->s16NumOfSubBands == 8) { + s16BitPool = (s16BitPool > 255) ? 255 : s16BitPool; + } else { + s16BitPool = (s16BitPool > 128) ? 128 : s16BitPool; + } + } else { + s16BitPool = (SINT16)( ((pstrEncParams->s16NumOfSubBands * + pstrEncParams->u16BitRate * 1000) + / (s16SamplingFreq * pstrEncParams->s16NumOfChannels)) + - ( ( (32 / pstrEncParams->s16NumOfChannels) + + (4 * pstrEncParams->s16NumOfSubBands) ) + / pstrEncParams->s16NumOfBlocks ) ); + + pstrEncParams->s16BitPool = (s16BitPool > + (16 * pstrEncParams->s16NumOfSubBands)) + ? (16 * pstrEncParams->s16NumOfSubBands) : s16BitPool; + } + + if (s16BitPool < 0) { + s16BitPool = 0; + } + + APPL_TRACE_EVENT("bitpool candidate : %d (%d kbps)", + s16BitPool, pstrEncParams->u16BitRate); + + if (s16BitPool > pUpdateAudio->MaxBitPool) { + APPL_TRACE_DEBUG("%s computed bitpool too large (%d)", __FUNCTION__, s16BitPool); + /* Decrease bitrate */ + btc_aa_src_cb.encoder.u16BitRate -= BTC_MEDIA_BITRATE_STEP; + /* Record that we have decreased the bitrate */ + protect |= 1; + } else if (s16BitPool < pUpdateAudio->MinBitPool) { + APPL_TRACE_WARNING("%s computed bitpool too small (%d)", __FUNCTION__, s16BitPool); + + /* Increase bitrate */ + UINT16 previous_u16BitRate = btc_aa_src_cb.encoder.u16BitRate; + btc_aa_src_cb.encoder.u16BitRate += BTC_MEDIA_BITRATE_STEP; + /* Record that we have increased the bitrate */ + protect |= 2; + /* Check over-flow */ + if (btc_aa_src_cb.encoder.u16BitRate < previous_u16BitRate) { + protect |= 3; + } + } else { + break; + } + /* In case we have already increased and decreased the bitrate, just stop */ + if (protect == 3) { + APPL_TRACE_ERROR("%s could not find bitpool in range", __FUNCTION__); + break; + } + } while (1); + + /* Finally update the bitpool in the encoder structure */ + pstrEncParams->s16BitPool = s16BitPool; + + APPL_TRACE_DEBUG("%s final bit rate %d, final bit pool %d", __FUNCTION__, + btc_aa_src_cb.encoder.u16BitRate, btc_aa_src_cb.encoder.s16BitPool); + + /* make sure we reinitialize encoder with new settings */ + SBC_Encoder_Init(&(btc_aa_src_cb.encoder)); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_pcm2sbc_init + ** + ** Description Init encoding task for PCM to SBC according to feeding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding) +{ + BOOLEAN reconfig_needed = FALSE; + + APPL_TRACE_DEBUG("PCM feeding:"); + APPL_TRACE_DEBUG("sampling_freq:%d", p_feeding->feeding.cfg.pcm.sampling_freq); + APPL_TRACE_DEBUG("num_channel:%d", p_feeding->feeding.cfg.pcm.num_channel); + APPL_TRACE_DEBUG("bit_per_sample:%d", p_feeding->feeding.cfg.pcm.bit_per_sample); + + /* Check the PCM feeding sampling_freq */ + switch (p_feeding->feeding.cfg.pcm.sampling_freq) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 48000: + /* For these sampling_freq the AV connection must be 48000 */ + if (btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf48000) { + /* Reconfiguration needed at 48000 */ + APPL_TRACE_DEBUG("SBC Reconfiguration needed at 48000"); + btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf48000; + reconfig_needed = TRUE; + } + break; + + case 11025: + case 22050: + case 44100: + /* For these sampling_freq the AV connection must be 44100 */ + if (btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf44100) { + /* Reconfiguration needed at 44100 */ + APPL_TRACE_DEBUG("SBC Reconfiguration needed at 44100"); + btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf44100; + reconfig_needed = TRUE; + } + break; + default: + APPL_TRACE_DEBUG("Feeding PCM sampling_freq unsupported"); + break; + } + + /* Some AV Headsets do not support Mono => always ask for Stereo */ + if (btc_aa_src_cb.encoder.s16ChannelMode == SBC_MONO) { + APPL_TRACE_DEBUG("SBC Reconfiguration needed in Stereo"); + btc_aa_src_cb.encoder.s16ChannelMode = SBC_JOINT_STEREO; + reconfig_needed = TRUE; + } + + if (reconfig_needed != FALSE) { + APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, btc_aa_src_cb.TxAaMtuSize); + APPL_TRACE_DEBUG("ch mode %d, nbsubd %d, nb %d, alloc %d, rate %d, freq %d", + btc_aa_src_cb.encoder.s16ChannelMode, + btc_aa_src_cb.encoder.s16NumOfSubBands, btc_aa_src_cb.encoder.s16NumOfBlocks, + btc_aa_src_cb.encoder.s16AllocationMethod, btc_aa_src_cb.encoder.u16BitRate, + btc_aa_src_cb.encoder.s16SamplingFreq); + + SBC_Encoder_Init(&(btc_aa_src_cb.encoder)); + } else { + APPL_TRACE_DEBUG("%s no SBC reconfig needed", __FUNCTION__); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_audio_feeding_init + ** + ** Description Initialize the audio path according to the feeding format + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg) +{ + tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding = (tBTC_MEDIA_INIT_AUDIO_FEEDING *) p_msg; + + APPL_TRACE_DEBUG("%s format:%d", __FUNCTION__, p_feeding->feeding.format); + + /* Save Media Feeding information */ + btc_aa_src_cb.feeding_mode = p_feeding->feeding_mode; + btc_aa_src_cb.media_feeding = p_feeding->feeding; + + /* Handle different feeding formats */ + switch (p_feeding->feeding.format) { + case BTC_AV_CODEC_PCM: + btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + btc_a2dp_source_pcm2sbc_init(p_feeding); + break; + + default : + APPL_TRACE_ERROR("unknown feeding format %d", p_feeding->feeding.format); + break; + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_tx_flush + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_aa_tx_flush(void) +{ + /* Flush all enqueued music buffers (encoded) */ + APPL_TRACE_DEBUG("%s", __FUNCTION__); + + btc_aa_src_cb.media_feeding_state.pcm.counter = 0; + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + + btc_a2dp_source_flush_q(btc_aa_src_cb.TxAaQ); + + btc_aa_src_data_read(NULL, -1); +} + +/******************************************************************************* + ** + ** Function btc_get_num_aa_frame + ** + ** Description + ** + ** Returns The number of media frames in this time slice + ** + *******************************************************************************/ +static UINT8 btc_get_num_aa_frame(void) +{ + UINT8 result = 0; + + switch (btc_aa_src_cb.TxTranscoding) { + case BTC_MEDIA_TRSCD_PCM_2_SBC: { + UINT32 pcm_bytes_per_frame = btc_aa_src_cb.encoder.s16NumOfSubBands * + btc_aa_src_cb.encoder.s16NumOfBlocks * + btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + + UINT32 us_this_tick = BTC_MEDIA_TIME_TICK_MS * 1000; + UINT64 now_us = time_now_us(); + if (last_frame_us != 0) { +#if _POSIX_TIMERS + us_this_tick = (now_us - last_frame_us); +#else + // consider the case that the number of day increases and timeofday wraps around + us_this_tick = (now_us > last_frame_us) ? (now_us - last_frame_us) : + (now_us + 86400000000ull - last_frame_us); +#endif + } + last_frame_us = now_us; + + btc_aa_src_cb.media_feeding_state.pcm.counter += + btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick * + us_this_tick / (BTC_MEDIA_TIME_TICK_MS * 1000); + + /* calculate nbr of frames pending for this media tick */ + result = btc_aa_src_cb.media_feeding_state.pcm.counter / pcm_bytes_per_frame; + if (result > MAX_PCM_FRAME_NUM_PER_TICK) { + APPL_TRACE_WARNING("%s() - Limiting frames to be sent from %d to %d" + , __FUNCTION__, result, MAX_PCM_FRAME_NUM_PER_TICK); + result = MAX_PCM_FRAME_NUM_PER_TICK; + } + btc_aa_src_cb.media_feeding_state.pcm.counter -= result * pcm_bytes_per_frame; + + LOG_VERBOSE("WRITE %d FRAMES", result); + } + break; + + default: + APPL_TRACE_ERROR("ERROR btc_get_num_aa_frame Unsupported transcoding format 0x%x", + btc_aa_src_cb.TxTranscoding); + result = 0; + break; + } + + return (UINT8)result; +} + +/******************************************************************************* + ** + ** Function btc_media_aa_read_feeding + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ + +BOOLEAN btc_media_aa_read_feeding(void) +{ + UINT16 blocm_x_subband = btc_aa_src_cb.encoder.s16NumOfSubBands * \ + btc_aa_src_cb.encoder.s16NumOfBlocks; + UINT32 read_size; + UINT16 sbc_sampling = 48000; + UINT32 src_samples; + UINT16 bytes_needed = blocm_x_subband * btc_aa_src_cb.encoder.s16NumOfChannels * \ + btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + static UINT16 up_sampled_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS + * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * 2]; + static UINT16 read_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS + * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + UINT32 src_size_used; + UINT32 dst_size_used; + BOOLEAN fract_needed; + INT32 fract_max; + INT32 fract_threshold; + UINT32 nb_byte_read = 0; + + /* Get the SBC sampling rate */ + switch (btc_aa_src_cb.encoder.s16SamplingFreq) { + case SBC_sf48000: + sbc_sampling = 48000; + break; + case SBC_sf44100: + sbc_sampling = 44100; + break; + case SBC_sf32000: + sbc_sampling = 32000; + break; + case SBC_sf16000: + sbc_sampling = 16000; + break; + } + + if (sbc_sampling == btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + read_size = bytes_needed - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue; + nb_byte_read = btc_aa_src_data_read( + ((uint8_t *)btc_aa_src_cb.encoder.as16PcmBuffer) + + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + read_size); + if (nb_byte_read == read_size) { + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + return TRUE; + } else { + APPL_TRACE_WARNING("### UNDERFLOW :: ONLY READ %d BYTES OUT OF %d ###", + nb_byte_read, read_size); + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read; + return FALSE; + } + } + + /* Some Feeding PCM frequencies require to split the number of sample */ + /* to read. */ + /* E.g 128/6=21.3333 => read 22 and 21 and 21 => max = 2; threshold = 0*/ + fract_needed = FALSE; /* Default */ + switch (btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + case 32000: + case 8000: + fract_needed = TRUE; + fract_max = 2; /* 0, 1 and 2 */ + fract_threshold = 0; /* Add one for the first */ + break; + case 16000: + fract_needed = TRUE; + fract_max = 2; /* 0, 1 and 2 */ + fract_threshold = 1; /* Add one for the first two frames*/ + break; + } + + /* Compute number of sample to read from source */ + src_samples = blocm_x_subband; + src_samples *= btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq; + src_samples /= sbc_sampling; + + /* The previous division may have a remainder not null */ + if (fract_needed) { + if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter <= fract_threshold) { + src_samples++; /* for every read before threshold add one sample */ + } + + /* do nothing if counter >= threshold */ + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter++; /* one more read */ + if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter > fract_max) { + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter = 0; + } + } + + /* Compute number of bytes to read from source */ + read_size = src_samples; + read_size *= btc_aa_src_cb.media_feeding.cfg.pcm.num_channel; + read_size *= (btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8); + + /* Read Data from data channel */ + nb_byte_read = btc_aa_src_data_read((uint8_t *)read_buffer, read_size); + + //tput_mon(TRUE, nb_byte_read, FALSE); + + if (nb_byte_read < read_size) { + APPL_TRACE_WARNING("### UNDERRUN :: ONLY READ %d BYTES OUT OF %d ###", + nb_byte_read, read_size); + + if (nb_byte_read == 0) { + return FALSE; + } + + if (btc_aa_src_cb.feeding_mode == BTC_AV_FEEDING_ASYNCHRONOUS) { + /* Fill the unfilled part of the read buffer with silence (0) */ + memset(((UINT8 *)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read); + nb_byte_read = read_size; + } + } + + /* Initialize PCM up-sampling engine */ + bta_av_sbc_init_up_sample(btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq, + sbc_sampling, btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample, + btc_aa_src_cb.media_feeding.cfg.pcm.num_channel); + + /* re-sample read buffer */ + /* The output PCM buffer will be stereo, 16 bit per sample */ + dst_size_used = bta_av_sbc_up_sample((UINT8 *)read_buffer, + (UINT8 *)up_sampled_buffer + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + nb_byte_read, + sizeof(up_sampled_buffer) - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + &src_size_used); + + /* update the residue */ + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += dst_size_used; + + /* only copy the pcm sample when we have up-sampled enough PCM */ + if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue >= bytes_needed) { + /* Copy the output pcm samples in SBC encoding buffer */ + memcpy((UINT8 *)btc_aa_src_cb.encoder.as16PcmBuffer, + (UINT8 *)up_sampled_buffer, + bytes_needed); + /* update the residue */ + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue -= bytes_needed; + + if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue != 0) { + memcpy((UINT8 *)up_sampled_buffer, + (UINT8 *)up_sampled_buffer + bytes_needed, + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + } + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* + ** + ** Function btc_media_aa_prep_sbc_2_send + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) +{ + BT_HDR *p_buf; + UINT16 blocm_x_subband = btc_aa_src_cb.encoder.s16NumOfSubBands * + btc_aa_src_cb.encoder.s16NumOfBlocks; + + while (nb_frame) { + if (NULL == (p_buf = osi_malloc(BTC_MEDIA_AA_BUF_SIZE))) { + APPL_TRACE_ERROR ("ERROR btc_media_aa_prep_sbc_2_send no buffer TxCnt %d ", + fixed_queue_length(btc_aa_src_cb.TxAaQ)); + return; + } + + /* Init buffer */ + p_buf->offset = BTC_MEDIA_AA_SBC_OFFSET; + p_buf->len = 0; + p_buf->layer_specific = 0; + + do { + /* Write @ of allocated buffer in encoder.pu8Packet */ + btc_aa_src_cb.encoder.pu8Packet = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len; + /* Fill allocated buffer with 0 */ + memset(btc_aa_src_cb.encoder.as16PcmBuffer, 0, blocm_x_subband + * btc_aa_src_cb.encoder.s16NumOfChannels); + + /* Read PCM data and upsample them if needed */ + if (btc_media_aa_read_feeding()) { + /* SBC encode and descramble frame */ + SBC_Encoder(&(btc_aa_src_cb.encoder)); + A2D_SbcChkFrInit(btc_aa_src_cb.encoder.pu8Packet); + A2D_SbcDescramble(btc_aa_src_cb.encoder.pu8Packet, btc_aa_src_cb.encoder.u16PacketLength); + /* Update SBC frame length */ + p_buf->len += btc_aa_src_cb.encoder.u16PacketLength; + nb_frame--; + p_buf->layer_specific++; + } else { + APPL_TRACE_WARNING("btc_media_aa_prep_sbc_2_send underflow %d, %d", + nb_frame, btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + btc_aa_src_cb.media_feeding_state.pcm.counter += nb_frame * + btc_aa_src_cb.encoder.s16NumOfSubBands * + btc_aa_src_cb.encoder.s16NumOfBlocks * + btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + /* no more pcm to read */ + nb_frame = 0; + + /* break read loop if timer was stopped (media task stopped) */ + if ( btc_aa_src_cb.is_tx_timer == FALSE ) { + osi_free(p_buf); + return; + } + } + + } while (((p_buf->len + btc_aa_src_cb.encoder.u16PacketLength) < btc_aa_src_cb.TxAaMtuSize) + && (p_buf->layer_specific < 0x0F) && nb_frame); + + if (p_buf->len) { + /* timestamp of the media packet header represent the TS of the first SBC frame + i.e the timestamp before including this frame */ + *((UINT32 *) (p_buf + 1)) = btc_aa_src_cb.timestamp; + + btc_aa_src_cb.timestamp += p_buf->layer_specific * blocm_x_subband; + + if (btc_aa_src_cb.tx_flush) { + APPL_TRACE_DEBUG("### tx suspended, discarded frame ###"); + + if (fixed_queue_length(btc_aa_src_cb.TxAaQ) > 0) { + btc_a2dp_source_flush_q(btc_aa_src_cb.TxAaQ); + } + + osi_free(p_buf); + return; + } + + /* Enqueue the encoded SBC frame in AA Tx Queue */ + fixed_queue_enqueue(btc_aa_src_cb.TxAaQ, p_buf); + } else { + osi_free(p_buf); + } + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_prep_2_send + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_prep_2_send(UINT8 nb_frame) +{ + // Check for TX queue overflow + if (nb_frame > MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ) { + nb_frame = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ; + } + + if (fixed_queue_length(btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + APPL_TRACE_WARNING("TX Q overflow: %d/%d", + fixed_queue_length(btc_aa_src_cb.TxAaQ), MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame); + } + + while (fixed_queue_length(btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + osi_free(fixed_queue_try_dequeue(btc_aa_src_cb.TxAaQ)); + } + + // Transcode frame + + switch (btc_aa_src_cb.TxTranscoding) { + case BTC_MEDIA_TRSCD_PCM_2_SBC: + btc_media_aa_prep_sbc_2_send(nb_frame); + break; + + default: + APPL_TRACE_ERROR("%s unsupported transcoding format 0x%x", __func__, btc_aa_src_cb.TxTranscoding); + break; + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_send_aa_frame + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_send_aa_frame(void) +{ + UINT8 nb_frame_2_send; + + /* get the number of frame to send */ + nb_frame_2_send = btc_get_num_aa_frame(); + + if (nb_frame_2_send != 0) { + /* format and Q buffer to send */ + btc_a2dp_source_prep_2_send(nb_frame_2_send); + } + + /* send it */ + LOG_VERBOSE("%s: send %d frames", __FUNCTION__, nb_frame_2_send); + bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO); +} + +static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context) +{ + log_tstamps_us("media task tx timer"); + +#if (BTA_AV_INCLUDED == TRUE) + if (btc_aa_src_cb.is_tx_timer == TRUE) { + btc_a2dp_source_send_aa_frame(); + } else { + APPL_TRACE_WARNING("Media task Scheduled after Suspend"); + } +#endif +} + +static void btc_a2dp_source_alarm_cb(UNUSED_ATTR void *context) +{ + btc_a2dp_source_data_post(BTC_A2DP_SOURCE_DATA_EVT); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_feeding_state_reset + ** + ** Description Reset the media feeding state + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_feeding_state_reset(void) +{ + /* By default, just clear the entire state */ + memset(&btc_aa_src_cb.media_feeding_state, 0, sizeof(btc_aa_src_cb.media_feeding_state)); + + if (btc_aa_src_cb.TxTranscoding == BTC_MEDIA_TRSCD_PCM_2_SBC) { + btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick = + (btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq * + btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8 * + btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + BTC_MEDIA_TIME_TICK_MS) / 1000; + + APPL_TRACE_WARNING("pcm bytes per tick %d", + (int)btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick); + } +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_start_tx + ** + ** Description Start media task encoding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_aa_start_tx(void) +{ + APPL_TRACE_DEBUG("btc_a2dp_source_aa_start_tx is timer %d, feeding mode %d", + btc_aa_src_cb.is_tx_timer, btc_aa_src_cb.feeding_mode); + + btc_aa_src_cb.is_tx_timer = TRUE; + last_frame_us = 0; + + /* Reset the media feeding state */ + btc_a2dp_source_feeding_state_reset(); + + APPL_TRACE_EVENT("starting timer %dms", BTC_MEDIA_TIME_TICK_MS); + + assert(btc_aa_src_cb.media_alarm == NULL); + + btc_aa_src_cb.media_alarm = osi_alarm_new("aaTx", btc_a2dp_source_alarm_cb, NULL, BTC_MEDIA_TIME_TICK_MS); + + if (!btc_aa_src_cb.media_alarm) { + LOG_ERROR("%s unable to allocate media alarm.", __func__); + return; + } + + osi_alarm_set_periodic(btc_aa_src_cb.media_alarm, BTC_MEDIA_TIME_TICK_MS); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_aa_stop_tx + ** + ** Description Stop media task encoding + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_aa_stop_tx(void) +{ + APPL_TRACE_DEBUG("%s is_tx_timer: %d", __func__, btc_aa_src_cb.is_tx_timer); + + const bool send_ack = (btc_aa_src_cb.is_tx_timer != FALSE); + + /* Stop the timer first */ + if (btc_aa_src_cb.media_alarm) { + osi_alarm_cancel(btc_aa_src_cb.media_alarm); + osi_alarm_free(btc_aa_src_cb.media_alarm); + } + btc_aa_src_cb.media_alarm = NULL; + btc_aa_src_cb.is_tx_timer = FALSE; + + /* Try to send acknowldegment once the media stream is + stopped. This will make sure that the A2DP HAL layer is + un-blocked on wait for acknowledgment for the sent command. + This resolves a corner cases AVDTP SUSPEND collision + when the DUT and the remote device issue SUSPEND simultaneously + and due to the processing of the SUSPEND request from the remote, + the media path is torn down. If the A2DP HAL happens to wait + for ACK for the initiated SUSPEND, it would never receive it casuing + a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed + to get the ACK for any pending command in such cases. */ + + if (send_ack) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS); + } + + /* audio engine stopped, reset tx suspended flag */ + btc_aa_src_cb.tx_flush = 0; + last_frame_us = 0; + + /* Reset the feeding state */ + btc_a2dp_source_feeding_state_reset(); +} + +/******************************************************************************* + ** + ** Function btc_a2dp_source_flush_q + ** + ** Description + ** + ** Returns void + ** + *******************************************************************************/ +static void btc_a2dp_source_flush_q(fixed_queue_t *p_q) +{ + while (! fixed_queue_is_empty(p_q)) { + osi_free(fixed_queue_try_dequeue(p_q)); + } +} + +static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context) +{ + APPL_TRACE_EVENT("%s\n", __func__); + memset(&btc_aa_src_cb, 0, sizeof(btc_aa_src_cb)); + + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON; + + btc_aa_src_cb.TxAaQ = fixed_queue_new(SIZE_MAX); + + btc_a2dp_control_init(); +} + +static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context) +{ + /* make sure no channels are restarted while shutting down */ + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; + + btc_a2dp_control_set_datachnl_stat(FALSE); + /* Clear media task flag */ + btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; + + btc_a2dp_control_cleanup(); + + fixed_queue_free(btc_aa_src_cb.TxAaQ, osi_free_func); +} + +#endif /* BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c similarity index 83% rename from components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c rename to components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c index 7c04cbfe0..1ac3b96e2 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -23,21 +23,21 @@ #include #include "bt_trace.h" #include "bt_defs.h" -#include "esp_bt_defs.h" -#include "esp_a2dp_api.h" #include "allocator.h" +#include "btu.h" +#include "bta_av_api.h" #include "btc_dm.h" +#include "btc_common.h" +#include "btc_manage.h" #include "btc_av.h" #include "btc_avrc.h" #include "btc_util.h" #include "btc_profile_queue.h" -#include "bta_api.h" -#include "btc_media.h" -#include "bta_av_api.h" -#include "btu.h" -#include "bt_utils.h" -#include "btc_common.h" -#include "btc_manage.h" +#include "btc_a2dp.h" +#include "btc_a2dp_control.h" +#include "btc_a2dp_sink.h" +#include "btc_a2dp_source.h" +#include "esp_a2dp_api.h" #if BTC_AV_INCLUDED @@ -116,6 +116,18 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *data); static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *data); static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *data); +#if BTC_AV_SRC_INCLUDED +static bt_status_t btc_a2d_src_init(void); +static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda); +static void btc_a2d_src_deinit(void); +#endif /* BTC_AV_SRC_INCLUDED */ + +#if BTC_AV_SINK_INCLUDED +static bt_status_t btc_a2d_sink_init(void); +static bt_status_t btc_a2d_sink_connect(bt_bdaddr_t *remote_bda); +static void btc_a2d_sink_deinit(void); +#endif /* BTC_AV_SINK_INCLUDED */ + static const btc_sm_handler_t btc_av_state_handlers[] = { btc_av_state_idle_handler, btc_av_state_opening_handler, @@ -226,6 +238,7 @@ static void btc_initiate_av_open_tmr_hdlr(TIMER_LIST_ENT *tle) ******************************************************************************/ static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, int disc_rsn) { + // todo: add callback for SRC esp_a2d_cb_param_t param; memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); @@ -242,6 +255,7 @@ static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bda static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_addr) { + // todo: add callback for SRC esp_a2d_cb_param_t param; memset(¶m, 0, sizeof(esp_a2d_cb_param_t)); @@ -389,7 +403,6 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data) btc_av_cb.edr = p_bta_data->open.edr; btc_av_cb.peer_sep = p_bta_data->open.sep; - btc_a2dp_set_peer_sep(p_bta_data->open.sep); } else { LOG_WARN("BTA_AV_OPEN_EVT::FAILED status: %d\n", p_bta_data->open.status ); @@ -401,7 +414,13 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data) btc_report_connection_state(state, &(btc_av_cb.peer_bda), 0); /* change state to open/idle based on the status */ btc_sm_change_state(btc_av_cb.sm_handle, av_state); - if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* if queued PLAY command, send it now */ + /* necessary to add this? + btc_rc_check_handle_pending_play(p_bta_data->open.bd_addr, + (p_bta_data->open.status == BTA_AV_SUCCESS)); + */ + } else if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { /* Bring up AVRCP connection too */ BTA_AvOpenRc(btc_av_cb.bta_handle); } @@ -473,17 +492,33 @@ static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *p_data) switch (event) { case BTC_SM_ENTER_EVT: - if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { - btc_a2dp_set_rx_flush(TRUE); +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately stop transmission of frames */ + btc_a2dp_source_set_tx_flush(TRUE); + /* wait for audioflinger to stop a2dp */ } +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(TRUE); + } +#endif /* BTC_AV_SINK_INCLUDED */ break; case BTA_AV_STOP_EVT: case BTC_AV_STOP_STREAM_REQ_EVT: - if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { - btc_a2dp_set_rx_flush(TRUE); +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately flush any pending tx frames while suspend is pending */ + btc_a2dp_source_set_tx_flush(TRUE); } - +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + btc_a2dp_sink_set_rx_flush(TRUE); + } +#endif /* BTC_AV_SINK_INCLUDED */ btc_a2dp_on_stopped(NULL); break; @@ -548,6 +583,11 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) break; case BTC_AV_START_STREAM_REQ_EVT: +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep != AVDT_TSEP_SRC) { + btc_a2dp_source_setup_codec(); + } +#endif /* BTC_AV_SRC_INCLUDED */ BTA_AvStart(); btc_av_cb.flags |= BTC_AV_FLAG_PENDING_START; break; @@ -559,21 +599,44 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE)) { return TRUE; } - +#if BTC_AV_SRC_INCLUDED + /* if remote tries to start a2dp when DUT is a2dp source + * then suspend. In case a2dp is sink and call is active + * then disconnect the AVDTP channel + */ + if (!(btc_av_cb.flags & BTC_AV_FLAG_PENDING_START)) { + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + LOG_INFO("%s: trigger suspend as remote initiated!!", __FUNCTION__); + btc_dispatch_sm_event(BTC_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0); + } + } + /* In case peer is A2DP SRC we do not want to ack commands on UIPC*/ + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + if (btc_a2dp_on_started(&p_av->start, + ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) != 0))) { + /* only clear pending flag after acknowledgement */ + btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + } + } +#endif /* BTC_AV_SRC_INCLUDED */ /* remain in open state if status failed */ if (p_av->start.status != BTA_AV_SUCCESS) { return FALSE; } - +#if BTC_AV_SINK_INCLUDED if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { - btc_a2dp_set_rx_flush(FALSE); /* remove flush state, ready for streaming*/ + btc_a2dp_sink_set_rx_flush(FALSE); /* remove flush state, ready for streaming*/ } - +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED /* change state to started, send acknowledgement if start is pending */ if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { - + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + btc_a2dp_on_started(NULL, TRUE); + } /* pending start flag will be cleared when exit current state */ } +#endif /* BTC_AV_SRC_INCLUDED */ btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_STARTED); } break; @@ -599,6 +662,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) /* change state to idle, send acknowledgement if start is pending */ if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); /* pending start flag will be cleared when exit current state */ } btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); @@ -612,6 +676,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) BTA_AvStart(); } else if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; + btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE); } break; @@ -665,17 +730,23 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data) /* increase the a2dp consumer task priority temporarily when start ** audio playing, to avoid overflow the audio packet queue. */ - adjust_priority_a2dp(TRUE); + // adjust_priority_a2dp(TRUE); break; case BTC_SM_EXIT_EVT: /* restore the a2dp consumer task priority when stop audio playing. */ - adjust_priority_a2dp(FALSE); + // adjust_priority_a2dp(FALSE); break; case BTC_AV_START_STREAM_REQ_EVT: +#if BTC_AV_SRC_INCLUDED + /* we were remotely started, just ack back the local request */ + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + btc_a2dp_on_started(NULL, TRUE); + } +#endif /* BTC_AV_SRC_INCLUDED */ break; /* fixme -- use suspend = true always to work around issue with BTA AV */ @@ -689,12 +760,18 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data) /* if we were remotely suspended but suspend locally, local suspend always overrides */ btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND; - +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* immediately stop transmission of frames while suspend is pending */ + btc_a2dp_source_set_tx_flush(TRUE); + } +#endif /* BTC_AV_SRC_INCLUDED */ +#if BTC_AV_SINK_INCLUDED if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { - btc_a2dp_set_rx_flush(TRUE); + btc_a2dp_sink_set_rx_flush(TRUE); btc_a2dp_on_stopped(NULL); } - +#endif /* BTC_AV_SINK_INCLUDED */ BTA_AvStop(TRUE); break; @@ -724,7 +801,12 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data) /* if not successful, remain in current state */ if (p_av->suspend.status != BTA_AV_SUCCESS) { btc_av_cb.flags &= ~BTC_AV_FLAG_LOCAL_SUSPEND_PENDING; - +#if BTC_AV_SRC_INCLUDED + if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { + /* suspend failed, reset back tx flush state */ + btc_a2dp_source_set_tx_flush(FALSE); + } +#endif /* BTC_AV_SRC_INCLUDED */ return FALSE; } @@ -855,62 +937,6 @@ static void btc_av_event_free_data(btc_sm_event_t event, void *p_data) } } -static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data) -{ - bt_status_t stat; - btc_msg_t msg; - - msg.sig = BTC_SIG_API_CB; - msg.pid = BTC_PID_A2DP; - msg.act = (uint8_t) event; - stat = btc_transfer_context(&msg, p_data, sizeof(tBTA_AV), btc_av_event_deep_copy); - - if (stat) { - LOG_ERROR("%s transfer failed\n", __func__); - } -} - -static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) -{ - btc_sm_state_t state; - UINT8 que_len; - tA2D_STATUS a2d_status; - tA2D_SBC_CIE sbc_cie; - - if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTC_MEDIA context */ - state = btc_sm_get_state(btc_av_cb.sm_handle); - if ( (state == BTC_AV_STATE_STARTED) || /* send SBC packets only in Started State */ - (state == BTC_AV_STATE_OPENED) ) { - que_len = btc_media_sink_enque_buf((BT_HDR *)p_data); - LOG_DEBUG(" Packets in Que %d\n", que_len); - } else { - return; - } - } - - if (event == BTA_AV_MEDIA_SINK_CFG_EVT) { - /* send a command to BT Media Task */ - btc_reset_decoder((UINT8 *)p_data); - - /* currently only supportes SBC */ - a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE); - if (a2d_status == A2D_SUCCESS) { - btc_msg_t msg; - btc_av_args_t arg; - - msg.sig = BTC_SIG_API_CB; - msg.pid = BTC_PID_A2DP; - msg.act = BTC_AV_SINK_CONFIG_REQ_EVT; - - memset(&arg, 0, sizeof(btc_av_args_t)); - arg.mcc.type = ESP_A2D_MCT_SBC; - memcpy(arg.mcc.cie.sbc, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC); - btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); - } else { - LOG_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); - } - } -} /******************************************************************************* ** ** Function btc_av_init @@ -921,10 +947,21 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) ** *******************************************************************************/ -bt_status_t btc_av_init(void) +static bt_status_t btc_av_init(int service_id) { if (btc_av_cb.sm_handle == NULL) { - if (!btc_a2dp_start_media_task()) { + bool stat = false; + if (service_id == BTA_A2DP_SOURCE_SERVICE_ID) { +#if BTC_AV_SRC_INCLUDED + stat = btc_a2dp_source_startup(); +#endif + } else if (service_id == BTA_A2DP_SINK_SERVICE_ID) { +#if BTC_AV_SINK_INCLUDED + stat = btc_a2dp_sink_startup(); +#endif + } + + if (!stat) { return BT_STATUS_FAIL; } @@ -933,30 +970,17 @@ bt_status_t btc_av_init(void) btc_sm_init((const btc_sm_handler_t *)btc_av_state_handlers, BTC_AV_STATE_IDLE); btc_dm_enable_service(BTA_A2DP_SOURCE_SERVICE_ID); -#if (BTA_AV_SINK_INCLUDED == TRUE) - btc_dm_enable_service(BTA_A2DP_SINK_SERVICE_ID); -#endif + + if (service_id == BTA_A2DP_SINK_SERVICE_ID) { + btc_dm_enable_service(BTA_A2DP_SINK_SERVICE_ID); + } btc_a2dp_on_init(); + + return BT_STATUS_SUCCESS; } - return BT_STATUS_SUCCESS; -} - -/******************************************************************************* -** -** Function init_sink -** -** Description Initializes the AV interface for sink mode -** -** Returns bt_status_t -** -*******************************************************************************/ -bt_status_t btc_a2d_sink_init(void) -{ - LOG_DEBUG("%s()\n", __func__); - - return btc_av_init(); + return BT_STATUS_FAIL; } /******************************************************************************* @@ -981,33 +1005,34 @@ static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid) return BT_STATUS_SUCCESS; } -bt_status_t btc_a2d_sink_connect(bt_bdaddr_t *remote_bda) -{ - LOG_DEBUG("%s\n", __FUNCTION__); - CHECK_BTAV_INIT(); - - return btc_queue_connect(UUID_SERVCLASS_AUDIO_SINK, remote_bda, connect_int); -} - /******************************************************************************* ** -** Function cleanup +** Function clean_up ** ** Description Shuts down the AV interface and does the cleanup ** ** Returns None ** *******************************************************************************/ -static void btc_a2d_sink_deinit(void) +static void clean_up(int service_id) { LOG_DEBUG("%s\n", __FUNCTION__); - btc_a2dp_stop_media_task(); + if (service_id == BTA_A2DP_SOURCE_SERVICE_ID) { +#if BTC_AV_SRC_INCLUDED + btc_a2dp_source_shutdown(); +#endif /* BTC_AV_SRC_INCLUDED */ + } else if (service_id == BTA_A2DP_SINK_SERVICE_ID) { +#if BTC_AV_SINK_INCLUDED + btc_a2dp_sink_shutdown(); +#endif /* BTC_AV_SINK_INCLUDED */ + } btc_dm_disable_service(BTA_A2DP_SOURCE_SERVICE_ID); -#if (BTA_AV_SINK_INCLUDED == TRUE) - btc_dm_disable_service(BTA_A2DP_SINK_SERVICE_ID); -#endif + + if (service_id == BTA_A2DP_SINK_SERVICE_ID) { + btc_dm_disable_service(BTA_A2DP_SINK_SERVICE_ID); + } /* Also shut down the AV state machine */ btc_sm_shutdown(btc_av_cb.sm_handle); @@ -1100,6 +1125,70 @@ void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len) btc_transfer_context(&msg, p_data, len, NULL); } +static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data) +{ + bt_status_t stat; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_A2DP; + msg.act = (uint8_t) event; + stat = btc_transfer_context(&msg, p_data, sizeof(tBTA_AV), btc_av_event_deep_copy); + + if (stat) { + LOG_ERROR("%s transfer failed\n", __func__); + } +} + +#if BTC_AV_SINK_INCLUDED +static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) +{ + btc_sm_state_t state; + UINT8 que_len; + tA2D_STATUS a2d_status; + tA2D_SBC_CIE sbc_cie; + + if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTC_MEDIA context */ + state = btc_sm_get_state(btc_av_cb.sm_handle); + if ( (state == BTC_AV_STATE_STARTED) || /* send SBC packets only in Started State */ + (state == BTC_AV_STATE_OPENED) ) { + que_len = btc_a2dp_sink_enque_buf((BT_HDR *)p_data); + LOG_DEBUG(" Packets in Que %d\n", que_len); + } else { + return; + } + } + + if (event == BTA_AV_MEDIA_SINK_CFG_EVT) { + /* send a command to BT Media Task */ + btc_a2dp_sink_reset_decoder((UINT8 *)p_data); + + /* currently only supportes SBC */ + a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE); + if (a2d_status == A2D_SUCCESS) { + btc_msg_t msg; + btc_av_args_t arg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_A2DP; + msg.act = BTC_AV_SINK_CONFIG_REQ_EVT; + + memset(&arg, 0, sizeof(btc_av_args_t)); + arg.mcc.type = ESP_A2D_MCT_SBC; + memcpy(arg.mcc.cie.sbc, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC); + btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); + } else { + LOG_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); + } + } +} +#else +static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) +{ + LOG_WARN("%s : event %u\n", __func__, event); +} +#endif + /******************************************************************************* ** ** Function btc_av_execute_service @@ -1163,6 +1252,21 @@ BOOLEAN btc_av_is_connected(void) return ((state == BTC_AV_STATE_OPENED) || (state == BTC_AV_STATE_STARTED)); } +/******************************************************************************* + * + * Function btc_av_get_peer_sep + * + * Description Get the stream endpoint type. + * + * Returns The stream endpoint type: either AVDT_TSEP_SRC or + * AVDT_TSEP_SNK. + * + ******************************************************************************/ + +uint8_t btc_av_get_peer_sep(void) +{ + return btc_av_cb.peer_sep; +} /******************************************************************************* ** ** Function btc_av_is_peer_edr @@ -1204,6 +1308,7 @@ void btc_a2dp_call_handler(btc_msg_t *msg) { btc_av_args_t *arg = (btc_av_args_t *)(msg->arg); switch (msg->act) { +#if BTC_AV_SINK_INCLUDED case BTC_AV_SINK_CONFIG_REQ_EVT: { btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, (void *)(msg->arg)); break; @@ -1232,6 +1337,45 @@ void btc_a2dp_call_handler(btc_msg_t *msg) btc_a2dp_sink_reg_data_cb(arg->data_cb); break; } +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + case BTC_AV_SRC_API_INIT_EVT: { + btc_a2d_src_init(); + break; + } + case BTC_AV_SRC_API_DEINIT_EVT: { + btc_a2d_src_deinit(); + break; + } + case BTC_AV_SRC_API_CONNECT_EVT: { + btc_a2d_src_connect(&arg->src_connect); + break; + } + case BTC_AV_SRC_API_DISCONNECT_EVT: { + CHECK_BTAV_INIT(); + btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, NULL); + break; + } + case BTC_AV_SRC_API_REG_DATA_CB_EVT: { + btc_a2dp_src_reg_data_cb(arg->src_data_cb); + break; + } +#endif /* BTC_AV_SRC_INCLUDED */ + case BTC_AV_API_MEDIA_CTRL_EVT: { + btc_a2dp_control_media_ctrl(arg->ctrl); + break; + } + case BTC_AV_DATAPATH_CTRL_EVT: { + btc_a2dp_control_datapath_ctrl(arg->dp_evt); + break; + } + // case BTC_AV_DISCONNECT_REQ_EVT: + case BTC_AV_START_STREAM_REQ_EVT: + case BTC_AV_STOP_STREAM_REQ_EVT: + case BTC_AV_SUSPEND_STREAM_REQ_EVT: { + btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, NULL); + break; + } default: LOG_WARN("%s : unhandled event: %d\n", __FUNCTION__, msg->act); } @@ -1243,4 +1387,70 @@ void btc_a2dp_cb_handler(btc_msg_t *msg) btc_av_event_free_data(msg->act, msg->arg); } +#if BTC_AV_SINK_INCLUDED + +/******************************************************************************* +** +** Function init_sink +** +** Description Initializes the AV interface for sink mode +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_a2d_sink_init(void) +{ + LOG_DEBUG("%s()\n", __func__); + + return btc_av_init(BTA_A2DP_SINK_SERVICE_ID); +} + +static bt_status_t btc_a2d_sink_connect(bt_bdaddr_t *remote_bda) +{ + LOG_DEBUG("%s\n", __FUNCTION__); + CHECK_BTAV_INIT(); + + return btc_queue_connect(UUID_SERVCLASS_AUDIO_SINK, remote_bda, connect_int); +} + +static void btc_a2d_sink_deinit(void) +{ + clean_up(BTA_A2DP_SINK_SERVICE_ID); +} + +#endif /* BTC_AV_SINK_INCLUDED */ + +#if BTC_AV_SRC_INCLUDED + +/******************************************************************************* +** +** Function btc_a2d_src_init +** +** Description Initializes the AV interface for source mode +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t btc_a2d_src_init(void) +{ + LOG_DEBUG("%s()\n", __func__); + + return btc_av_init(BTA_A2DP_SOURCE_SERVICE_ID); +} + +static void btc_a2d_src_deinit(void) +{ + clean_up(BTA_A2DP_SOURCE_SERVICE_ID); +} + +static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda) +{ + LOG_DEBUG("%s\n", __FUNCTION__); + CHECK_BTAV_INIT(); + + return btc_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, remote_bda, connect_int); +} + +#endif /* BTC_AV_SRC_INCLUDED */ + #endif /* #if BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c deleted file mode 100644 index 1b6db63c4..000000000 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_media_task.c +++ /dev/null @@ -1,1015 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/****************************************************************************** - ** - ** Name: btc_media_task.c - ** - ** Description: This is the multimedia module for the BTC system. It - ** contains task implementations AV, HS and HF profiles - ** audio & video processing - ** - ******************************************************************************/ - -#include "bt_target.h" -#include "bt_trace.h" -#include -#include -#include -#include "fixed_queue.h" -#include "bta_api.h" -#include "btu.h" -#include "bta_sys.h" -#include "bta_sys_int.h" -#include "bta_av_api.h" -#include "a2d_api.h" -#include "a2d_sbc.h" -#include "a2d_int.h" -#include "bta_av_sbc.h" -#include "bta_av_ci.h" -#include "l2c_api.h" -#include "btc_av_co.h" -#include "btc_media.h" -#include "alarm.h" -#include "bt_trace.h" -#include "thread.h" -#include "bt_defs.h" -#include "btc_av.h" -#include "btc_sm.h" -#include "btc_util.h" -#include "allocator.h" -#include "bt_utils.h" -#include "esp_a2dp_api.h" -#include "mutex.h" - -// #if (BTA_AV_SINK_INCLUDED == TRUE) -#include "oi_codec_sbc.h" -#include "oi_status.h" -// #endif - -#if BTC_AV_INCLUDED - -// #if (BTA_AV_SINK_INCLUDED == TRUE) -OI_CODEC_SBC_DECODER_CONTEXT context; -OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; -OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; -// #endif - -/***************************************************************************** - ** Constants - *****************************************************************************/ - -/* BTC media cmd event definition : BTC_MEDIA_TASK_CMD */ -enum { - BTC_MEDIA_FLUSH_AA_RX = 1, - BTC_MEDIA_AUDIO_SINK_CFG_UPDATE, - BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK -}; - -enum { - MEDIA_TASK_STATE_OFF = 0, - MEDIA_TASK_STATE_ON = 1, - MEDIA_TASK_STATE_SHUTTING_DOWN = 2 -}; - -enum { - SIG_MEDIA_TASK_INIT = 0xf0, - SIG_MEDIA_TASK_CLEAN_UP = 0xf1, - SIG_MEDIA_TASK_AVK_DATA_READY = 0xf2, - SIG_MEDIA_TASK_CMD_READY = 0xf3 -}; - -/* - * CONGESTION COMPENSATION CTRL :: - * - * Thus setting controls how many buffers we will hold in media task - * during temp link congestion. Together with the stack buffer queues - * it controls much temporary a2dp link congestion we can - * compensate for. It however also depends on the default run level of sinks - * jitterbuffers. Depending on type of sink this would vary. - * Ideally the (SRC) max tx buffer capacity should equal the sinks - * jitterbuffer runlevel including any intermediate buffers on the way - * towards the sinks codec. - */ - -/* fixme -- define this in pcm time instead of buffer count */ - -/* The typical runlevel of the tx queue size is ~1 buffer - but due to link flow control or thread preemption in lower - layers we might need to temporarily buffer up data */ - -/* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */ -#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5) - -typedef struct { - UINT16 num_frames_to_be_processed; - UINT16 len; - UINT16 offset; - UINT16 layer_specific; -} tBT_SBC_HDR; - -typedef struct { - fixed_queue_t *RxSbcQ; - void *av_sm_hdl; - UINT8 peer_sep; - UINT8 busy_level; - BOOLEAN rx_flush; /* discards any incoming data when true */ - BOOLEAN data_channel_open; - UINT8 channel_count; - UINT32 sample_rate; -} tBTC_MEDIA_CB; - -// #if (BTA_AV_SINK_INCLUDED == TRUE) -extern OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, - const OI_BYTE **frameData, - unsigned long *frameBytes, - OI_INT16 *pcmData, - unsigned long *pcmBytes); -extern OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, - unsigned long *decoderData, - unsigned long decoderDataBytes, - OI_UINT8 maxChannels, - OI_UINT8 pcmStride, - OI_BOOL enhanced); -// #endif - -static void btc_media_flush_q(fixed_queue_t *p_q); -static void btc_media_task_aa_rx_flush(void); -static const char *dump_media_event(UINT16 event); -static void btc_media_thread_handle_cmd(fixed_queue_t *queue); - -/* Handle incoming media packets A2DP SINK streaming*/ -static void btc_media_task_handle_inc_media(tBT_SBC_HDR *p_msg); -static void btc_media_task_aa_handle_decoder_reset(BT_HDR *p_msg); -static void btc_media_task_aa_handle_clear_track(void); -BOOLEAN btc_media_task_clear_track(void); -static void btc_media_task_handler(void *arg); - -static void btc_media_task_avk_data_ready(UNUSED_ATTR void *context); -static void btc_media_thread_init(UNUSED_ATTR void *context); -static void btc_media_thread_cleanup(UNUSED_ATTR void *context); - -static tBTC_MEDIA_CB btc_media_cb; -static int media_task_running = MEDIA_TASK_STATE_OFF; -static fixed_queue_t *btc_media_cmd_msg_queue = NULL; -static xTaskHandle xBtcMediaTaskHandle = NULL; -static QueueHandle_t xBtcMediaDataQueue = NULL; -static QueueHandle_t xBtcMediaCtrlQueue = NULL; -static QueueSetHandle_t xBtcMediaQueueSet; -static esp_a2d_data_cb_t bt_av_sink_data_callback = NULL; - -void btc_a2dp_sink_reg_data_cb(esp_a2d_data_cb_t callback) -{ - // todo: critical section protection - bt_av_sink_data_callback = callback; -} - -static inline void btc_a2d_data_cb_to_app(const uint8_t *data, uint32_t len) -{ - // todo: critical section protection - if (bt_av_sink_data_callback) { - bt_av_sink_data_callback(data, len); - } -} - -/***************************************************************************** - ** Misc helper functions - *****************************************************************************/ -UNUSED_ATTR static const char *dump_media_event(UINT16 event) -{ - switch (event) { - CASE_RETURN_STR(BTC_MEDIA_FLUSH_AA_RX) - CASE_RETURN_STR(BTC_MEDIA_AUDIO_SINK_CFG_UPDATE) - CASE_RETURN_STR(BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK) - - default: - return "UNKNOWN MEDIA EVENT"; - } -} - -/***************************************************************************** - ** BTC ADAPTATION - *****************************************************************************/ - -static void btc_media_ctrl_post(uint32_t sig) -{ - BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); - if (evt == NULL) { - return; - } - - evt->sig = sig; - evt->par = 0; - - if (xQueueSend(xBtcMediaCtrlQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { - APPL_TRACE_WARNING("xBtcMediaCtrlQueue failed, sig 0x%x\n", sig); - } -} - -static void btc_media_data_post(void) -{ - void *evt; - if (xQueueSend(xBtcMediaDataQueue, &evt, 0) != pdTRUE) { - APPL_TRACE_DEBUG("Media data Q filled\n"); - } -} - -static void btc_media_ctrl_handler(BtTaskEvt_t *e) -{ - if (e == NULL) { - return; - } - switch (e->sig) { - case SIG_MEDIA_TASK_CMD_READY: - fixed_queue_process(btc_media_cmd_msg_queue); - break; - case SIG_MEDIA_TASK_INIT: - btc_media_thread_init(NULL); - break; - case SIG_MEDIA_TASK_CLEAN_UP: - btc_media_thread_cleanup(NULL); - break; - default: - APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", e->sig); - } -} - - -static void btc_media_task_handler(void *arg) -{ - QueueSetMemberHandle_t xActivatedMember; - BtTaskEvt_t *e = NULL; - for (;;) { - xActivatedMember = xQueueSelectFromSet(xBtcMediaQueueSet, portMAX_DELAY); - if (xActivatedMember == xBtcMediaDataQueue) { - xQueueReceive(xActivatedMember, &e, 0); - btc_media_task_avk_data_ready(NULL); - } else if (xActivatedMember == xBtcMediaCtrlQueue) { - xQueueReceive(xActivatedMember, &e, 0); - btc_media_ctrl_handler(e); - osi_free(e); - } - } -} - -bool btc_a2dp_start_media_task(void) -{ - if (media_task_running != MEDIA_TASK_STATE_OFF) { - APPL_TRACE_ERROR("warning : media task already running"); - return false; - } - - APPL_TRACE_EVENT("## A2DP START MEDIA THREAD ##"); - - xBtcMediaQueueSet = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN); - configASSERT(xBtcMediaQueueSet); - xBtcMediaDataQueue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *)); - configASSERT(xBtcMediaDataQueue); - xQueueAddToSet(xBtcMediaDataQueue, xBtcMediaQueueSet); - - xBtcMediaCtrlQueue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *)); - configASSERT(xBtcMediaCtrlQueue); - xQueueAddToSet(xBtcMediaCtrlQueue, xBtcMediaQueueSet); - - if (!xBtcMediaDataQueue || !xBtcMediaCtrlQueue || !xBtcMediaQueueSet ) { - goto error_exit; - } - - xTaskCreatePinnedToCore(btc_media_task_handler, "BtcMediaT\n", 2048, NULL, configMAX_PRIORITIES - 3, &xBtcMediaTaskHandle, 0); - if (xBtcMediaTaskHandle == NULL) { - goto error_exit; - } - - btc_media_cmd_msg_queue = fixed_queue_new(SIZE_MAX); - if (btc_media_cmd_msg_queue == NULL) { - goto error_exit; - } - fixed_queue_register_dequeue(btc_media_cmd_msg_queue, btc_media_thread_handle_cmd); - btc_media_ctrl_post(SIG_MEDIA_TASK_INIT); - - APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##\n"); - - return true; - -error_exit:; - APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - - if (xBtcMediaTaskHandle != NULL) { - vTaskDelete(xBtcMediaTaskHandle); - xBtcMediaTaskHandle = NULL; - } - - if (xBtcMediaDataQueue) { - vQueueDelete(xBtcMediaDataQueue); - xBtcMediaDataQueue = NULL; - } - if (xBtcMediaCtrlQueue) { - vQueueDelete(xBtcMediaCtrlQueue); - xBtcMediaCtrlQueue = NULL; - } - - fixed_queue_free(btc_media_cmd_msg_queue, NULL); - btc_media_cmd_msg_queue = NULL; - return false; -} - -void btc_a2dp_stop_media_task(void) -{ - APPL_TRACE_EVENT("## A2DP STOP MEDIA THREAD ##\n"); - - // Exit thread - btc_media_ctrl_post(SIG_MEDIA_TASK_CLEAN_UP); - // TODO: wait until CLEAN up is done, then do task delete - vTaskDelete(xBtcMediaTaskHandle); - xBtcMediaTaskHandle = NULL; - - vQueueDelete(xBtcMediaDataQueue); - xBtcMediaDataQueue = NULL; - - vQueueDelete(xBtcMediaCtrlQueue); - xBtcMediaCtrlQueue = NULL; - - fixed_queue_free(btc_media_cmd_msg_queue, NULL); - btc_media_cmd_msg_queue = NULL; -} - -/***************************************************************************** -** -** Function btc_a2dp_on_init -** -** Description -** -** Returns -** -*******************************************************************************/ -void btc_a2dp_on_init(void) -{ - //tput_mon(1, 0, 1); -} - -/***************************************************************************** -** -** Function btc_a2dp_setup_codec -** -** Description -** -** Returns -** -*******************************************************************************/ - -void btc_a2dp_setup_codec(void) -{ - tBTC_AV_MEDIA_FEEDINGS media_feeding; - tBTC_STATUS status; - - APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n"); - - osi_mutex_global_lock(); - - /* for now hardcode 44.1 khz 16 bit stereo PCM format */ - media_feeding.cfg.pcm.sampling_freq = 44100; - media_feeding.cfg.pcm.bit_per_sample = 16; - media_feeding.cfg.pcm.num_channel = 2; - media_feeding.format = BTC_AV_CODEC_PCM; - - bta_av_co_audio_set_codec(&media_feeding, &status); - - - osi_mutex_global_unlock(); -} - -/***************************************************************************** -** -** Function btc_a2dp_on_idle -** -** Description -** -** Returns -** -*******************************************************************************/ - -void btc_a2dp_on_idle(void) -{ - APPL_TRACE_EVENT("## ON A2DP IDLE ##\n"); - - bta_av_co_init(); - if (btc_media_cb.peer_sep == AVDT_TSEP_SRC) { - btc_media_cb.rx_flush = TRUE; - btc_media_task_aa_rx_flush_req(); - btc_media_task_clear_track(); - APPL_TRACE_DEBUG("Stopped BT track"); - } -} - -/******************************************************************************* - ** - ** Function btc_media_task_clear_track - ** - ** Description - ** - ** Returns TRUE is success - ** - *******************************************************************************/ -BOOLEAN btc_media_task_clear_track(void) -{ - BT_HDR *p_buf; - - if (NULL == (p_buf = osi_malloc(sizeof(BT_HDR)))) { - return FALSE; - } - - p_buf->event = BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK; - - fixed_queue_enqueue(btc_media_cmd_msg_queue, p_buf); - btc_media_ctrl_post(SIG_MEDIA_TASK_CMD_READY); - return TRUE; -} - -/***************************************************************************** -** -** Function btc_reset_decoder -** -** Description -** -** Returns -** -*******************************************************************************/ - -void btc_reset_decoder(UINT8 *p_av) -{ - APPL_TRACE_EVENT("btc_reset_decoder"); - APPL_TRACE_DEBUG("btc_reset_decoder p_codec_info[%x:%x:%x:%x:%x:%x]\n", - p_av[1], p_av[2], p_av[3], - p_av[4], p_av[5], p_av[6]); - - tBTC_MEDIA_SINK_CFG_UPDATE *p_buf; - if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_SINK_CFG_UPDATE)))) { - APPL_TRACE_ERROR("btc_reset_decoder No Buffer "); - return; - } - - memcpy(p_buf->codec_info, p_av, AVDT_CODEC_SIZE); - p_buf->hdr.event = BTC_MEDIA_AUDIO_SINK_CFG_UPDATE; - - fixed_queue_enqueue(btc_media_cmd_msg_queue, p_buf); - btc_media_ctrl_post(SIG_MEDIA_TASK_CMD_READY); -} - -/***************************************************************************** -** -** Function btc_a2dp_on_stopped -** -** Description -** -** Returns -** -*******************************************************************************/ - -void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av) -{ - APPL_TRACE_EVENT("## ON A2DP STOPPED ##\n"); - if (btc_media_cb.peer_sep == AVDT_TSEP_SRC) { /* Handling for A2DP SINK cases*/ - btc_media_cb.rx_flush = TRUE; - btc_media_task_aa_rx_flush_req(); - btc_media_cb.data_channel_open = FALSE; - return; - } -} - -/***************************************************************************** -** -** Function btc_a2dp_on_suspended -** -** Description -** -** Returns -** -*******************************************************************************/ - -void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av) -{ - APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##\n"); - if (btc_media_cb.peer_sep == AVDT_TSEP_SRC) { - btc_media_cb.rx_flush = TRUE; - btc_media_task_aa_rx_flush_req(); - return; - } -} - -/* when true media task discards any rx frames */ -void btc_a2dp_set_rx_flush(BOOLEAN enable) -{ - APPL_TRACE_EVENT("## DROP RX %d ##\n", enable); - btc_media_cb.rx_flush = enable; -} - -static void btc_media_task_avk_data_ready(UNUSED_ATTR void *context) -{ - tBT_SBC_HDR *p_msg; - - if (fixed_queue_is_empty(btc_media_cb.RxSbcQ)) { - APPL_TRACE_DEBUG(" QUE EMPTY "); - } else { - if (btc_media_cb.rx_flush == TRUE) { - btc_media_flush_q(btc_media_cb.RxSbcQ); - return; - } - - while ((p_msg = (tBT_SBC_HDR *)fixed_queue_try_peek_first(btc_media_cb.RxSbcQ)) != NULL ) { - btc_media_task_handle_inc_media(p_msg); - p_msg = (tBT_SBC_HDR *)fixed_queue_try_dequeue(btc_media_cb.RxSbcQ); - if ( p_msg == NULL ) { - APPL_TRACE_ERROR("Insufficient data in que "); - break; - } - osi_free(p_msg); - } - APPL_TRACE_DEBUG(" Process Frames - "); - } -} - -static void btc_media_thread_init(UNUSED_ATTR void *context) -{ - APPL_TRACE_EVENT("%s\n", __func__); - memset(&btc_media_cb, 0, sizeof(btc_media_cb)); - btc_media_cb.av_sm_hdl = btc_av_get_sm_handle(); - raise_priority_a2dp(TASK_HIGH_MEDIA); - media_task_running = MEDIA_TASK_STATE_ON; - - btc_media_cb.RxSbcQ = fixed_queue_new(SIZE_MAX); -} - -static void btc_media_thread_cleanup(UNUSED_ATTR void *context) -{ - /* make sure no channels are restarted while shutting down */ - media_task_running = MEDIA_TASK_STATE_SHUTTING_DOWN; - - btc_media_cb.data_channel_open = FALSE; - /* Clear media task flag */ - media_task_running = MEDIA_TASK_STATE_OFF; - - fixed_queue_free(btc_media_cb.RxSbcQ, osi_free_func); -} - -/******************************************************************************* - ** - ** Function btc_media_flush_q - ** - ** Description - ** - ** Returns void - ** - *******************************************************************************/ -static void btc_media_flush_q(fixed_queue_t *p_q) -{ - while (! fixed_queue_is_empty(p_q)) { - osi_free(fixed_queue_try_dequeue(p_q)); - } -} - -static void btc_media_thread_handle_cmd(fixed_queue_t *queue) -{ - BT_HDR *p_msg; - while (!fixed_queue_is_empty(queue)) { - p_msg = (BT_HDR *)fixed_queue_dequeue(queue); - APPL_TRACE_VERBOSE("btc_media_thread_handle_cmd : %d %s\n", p_msg->event, - dump_media_event(p_msg->event)); - - switch (p_msg->event) { - case BTC_MEDIA_AUDIO_SINK_CFG_UPDATE: - btc_media_task_aa_handle_decoder_reset(p_msg); - break; - case BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK: - btc_media_task_aa_handle_clear_track(); - break; - case BTC_MEDIA_FLUSH_AA_RX: - btc_media_task_aa_rx_flush(); - break; - default: - APPL_TRACE_ERROR("ERROR in %s unknown event %d\n", __func__, p_msg->event); - } - osi_free(p_msg); - APPL_TRACE_VERBOSE("%s: %s DONE\n", __func__, dump_media_event(p_msg->event)); - } -} - -/******************************************************************************* - ** - ** Function btc_media_task_handle_inc_media - ** - ** Description - ** - ** Returns void - ** - *******************************************************************************/ -static void btc_media_task_handle_inc_media(tBT_SBC_HDR *p_msg) -{ - UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1); - int count; - UINT32 pcmBytes, availPcmBytes; - OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/ - OI_STATUS status; - int num_sbc_frames = p_msg->num_frames_to_be_processed; - UINT32 sbc_frame_len = p_msg->len - 1; - availPcmBytes = 2 * sizeof(pcmData); - - if ((btc_media_cb.peer_sep == AVDT_TSEP_SNK) || (btc_media_cb.rx_flush)) { - APPL_TRACE_DEBUG(" State Changed happened in this tick "); - return; - } - - // ignore data if no one is listening - if (!btc_media_cb.data_channel_open) { - return; - } - - APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len); - - for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) { - pcmBytes = availPcmBytes; - status = OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE **)&sbc_start_frame, - (OI_UINT32 *)&sbc_frame_len, - (OI_INT16 *)pcmDataPointer, - (OI_UINT32 *)&pcmBytes); - if (!OI_SUCCESS(status)) { - APPL_TRACE_ERROR("Decoding failure: %d\n", status); - break; - } - availPcmBytes -= pcmBytes; - pcmDataPointer += pcmBytes / 2; - p_msg->offset += (p_msg->len - 1) - sbc_frame_len; - p_msg->len = sbc_frame_len + 1; - } - - btc_a2d_data_cb_to_app((uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes)); -} - -/******************************************************************************* - ** - ** Function btc_media_task_aa_rx_flush_req - ** - ** Description - ** - ** Returns TRUE is success - ** - *******************************************************************************/ -BOOLEAN btc_media_task_aa_rx_flush_req(void) -{ - BT_HDR *p_buf; - - if (fixed_queue_is_empty(btc_media_cb.RxSbcQ) == TRUE) { /* Que is already empty */ - return TRUE; - } - - if (NULL == (p_buf = osi_malloc(sizeof(BT_HDR)))) { - return FALSE; - } - - p_buf->event = BTC_MEDIA_FLUSH_AA_RX; - - fixed_queue_enqueue(btc_media_cmd_msg_queue, p_buf); - btc_media_ctrl_post(SIG_MEDIA_TASK_CMD_READY); - return TRUE; -} - -/******************************************************************************* - ** - ** Function btc_media_task_aa_rx_flush - ** - ** Description - ** - ** Returns void - ** - *******************************************************************************/ -static void btc_media_task_aa_rx_flush(void) -{ - /* Flush all enqueued SBC buffers (encoded) */ - APPL_TRACE_DEBUG("btc_media_task_aa_rx_flush"); - - btc_media_flush_q(btc_media_cb.RxSbcQ); -} - -int btc_a2dp_get_track_frequency(UINT8 frequency) -{ - int freq = 48000; - switch (frequency) { - case A2D_SBC_IE_SAMP_FREQ_16: - freq = 16000; - break; - case A2D_SBC_IE_SAMP_FREQ_32: - freq = 32000; - break; - case A2D_SBC_IE_SAMP_FREQ_44: - freq = 44100; - break; - case A2D_SBC_IE_SAMP_FREQ_48: - freq = 48000; - break; - } - return freq; -} - -int btc_a2dp_get_track_channel_count(UINT8 channeltype) -{ - int count = 1; - switch (channeltype) { - case A2D_SBC_IE_CH_MD_MONO: - count = 1; - break; - case A2D_SBC_IE_CH_MD_DUAL: - case A2D_SBC_IE_CH_MD_STEREO: - case A2D_SBC_IE_CH_MD_JOINT: - count = 2; - break; - } - return count; -} - -void btc_a2dp_set_peer_sep(UINT8 sep) -{ - btc_media_cb.peer_sep = sep; -} - -static void btc_media_task_aa_handle_clear_track (void) -{ - APPL_TRACE_DEBUG("btc_media_task_aa_handle_clear_track"); -} - -/******************************************************************************* - ** - ** Function btc_media_task_aa_handle_decoder_reset - ** - ** Description - ** - ** Returns void - ** - *******************************************************************************/ -static void btc_media_task_aa_handle_decoder_reset(BT_HDR *p_msg) -{ - tBTC_MEDIA_SINK_CFG_UPDATE *p_buf = (tBTC_MEDIA_SINK_CFG_UPDATE *) p_msg; - tA2D_STATUS a2d_status; - tA2D_SBC_CIE sbc_cie; - OI_STATUS status; - UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/ - UINT32 num_blocks = 16; - UINT32 num_subbands = 8; - - APPL_TRACE_EVENT("btc_media_task_aa_handle_decoder_reset p_codec_info[%x:%x:%x:%x:%x:%x]\n", - p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3], - p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]); - - a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE); - if (a2d_status != A2D_SUCCESS) { - APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); - return; - } - - btc_media_cb.sample_rate = btc_a2dp_get_track_frequency(sbc_cie.samp_freq); - btc_media_cb.channel_count = btc_a2dp_get_track_channel_count(sbc_cie.ch_mode); - - btc_media_cb.rx_flush = FALSE; - APPL_TRACE_EVENT("Reset to sink role"); - status = OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE); - if (!OI_SUCCESS(status)) { - APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); - } - - btc_media_cb.data_channel_open = TRUE; - - switch (sbc_cie.samp_freq) { - case A2D_SBC_IE_SAMP_FREQ_16: - APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq); - freq_multiple = 16 * 20; - break; - case A2D_SBC_IE_SAMP_FREQ_32: - APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq); - freq_multiple = 32 * 20; - break; - case A2D_SBC_IE_SAMP_FREQ_44: - APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq); - freq_multiple = 441 * 2; - break; - case A2D_SBC_IE_SAMP_FREQ_48: - APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq); - freq_multiple = 48 * 20; - break; - default: - APPL_TRACE_DEBUG(" Unknown Frequency "); - break; - } - - switch (sbc_cie.ch_mode) { - case A2D_SBC_IE_CH_MD_MONO: - APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode); - break; - case A2D_SBC_IE_CH_MD_DUAL: - APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode); - break; - case A2D_SBC_IE_CH_MD_STEREO: - APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode); - break; - case A2D_SBC_IE_CH_MD_JOINT: - APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode); - break; - default: - APPL_TRACE_DEBUG(" Unknown Mode "); - break; - } - - switch (sbc_cie.block_len) { - case A2D_SBC_IE_BLOCKS_4: - APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len); - num_blocks = 4; - break; - case A2D_SBC_IE_BLOCKS_8: - APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len); - num_blocks = 8; - break; - case A2D_SBC_IE_BLOCKS_12: - APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len); - num_blocks = 12; - break; - case A2D_SBC_IE_BLOCKS_16: - APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len); - num_blocks = 16; - break; - default: - APPL_TRACE_DEBUG(" Unknown BlockLen "); - break; - } - - switch (sbc_cie.num_subbands) { - case A2D_SBC_IE_SUBBAND_4: - APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands); - num_subbands = 4; - break; - case A2D_SBC_IE_SUBBAND_8: - APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands); - num_subbands = 8; - break; - default: - APPL_TRACE_DEBUG(" Unknown SubBands "); - break; - } - - switch (sbc_cie.alloc_mthd) { - case A2D_SBC_IE_ALLOC_MD_S: - APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd); - break; - case A2D_SBC_IE_ALLOC_MD_L: - APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd); - break; - default: - APPL_TRACE_DEBUG(" Unknown Allocation Method"); - break; - } - - APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool); - - int frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1; - APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", frames_to_process); -} - -/******************************************************************************* - ** - ** Function btc_media_sink_enque_buf - ** - ** Description This function is called by the av_co to fill A2DP Sink Queue - ** - ** - ** Returns size of the queue - *******************************************************************************/ -UINT8 btc_media_sink_enque_buf(BT_HDR *p_pkt) -{ - tBT_SBC_HDR *p_msg; - - if (btc_media_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ - return fixed_queue_length(btc_media_cb.RxSbcQ); - } - - if (fixed_queue_length(btc_media_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ) { - APPL_TRACE_WARNING("Pkt dropped\n"); - return fixed_queue_length(btc_media_cb.RxSbcQ); - } - - APPL_TRACE_DEBUG("btc_media_sink_enque_buf + "); - - /* allocate and Queue this buffer */ - if ((p_msg = (tBT_SBC_HDR *) osi_malloc(sizeof(tBT_SBC_HDR) + - p_pkt->offset + p_pkt->len)) != NULL) { - memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); - p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; - APPL_TRACE_VERBOSE("btc_media_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); - fixed_queue_enqueue(btc_media_cb.RxSbcQ, p_msg); - btc_media_data_post(); - } else { - /* let caller deal with a failed allocation */ - APPL_TRACE_WARNING("btc_media_sink_enque_buf No Buffer left - "); - } - return fixed_queue_length(btc_media_cb.RxSbcQ); -} - -/******************************************************************************* - ** - ** Function btc_media_aa_readbuf - ** - ** Description This function is called by the av_co to get the next buffer to send - ** - ** - ** Returns void - *******************************************************************************/ -BT_HDR *btc_media_aa_readbuf(void) -{ - return NULL; -} - -/******************************************************************************* - ** - ** Function dump_codec_info - ** - ** Description Decode and display codec_info (for debug) - ** - ** Returns void - ** - *******************************************************************************/ -void dump_codec_info(unsigned char *p_codec) -{ - tA2D_STATUS a2d_status; - tA2D_SBC_CIE sbc_cie; - - a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_codec, FALSE); - if (a2d_status != A2D_SUCCESS) { - APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); - return; - } - - APPL_TRACE_DEBUG("dump_codec_info"); - - if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_16) { - APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq); - } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_32) { - APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq); - } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_44) { - APPL_TRACE_DEBUG("\tsamp_freq:%d (44.100)\n", sbc_cie.samp_freq); - } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_48) { - APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq); - } else { - APPL_TRACE_DEBUG("\tBAD samp_freq:%d\n", sbc_cie.samp_freq); - } - - if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_MONO) { - APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode); - } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_DUAL) { - APPL_TRACE_DEBUG("\tch_mode:%d (Dual)\n", sbc_cie.ch_mode); - } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_STEREO) { - APPL_TRACE_DEBUG("\tch_mode:%d (Stereo)\n", sbc_cie.ch_mode); - } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_JOINT) { - APPL_TRACE_DEBUG("\tch_mode:%d (Joint)\n", sbc_cie.ch_mode); - } else { - APPL_TRACE_DEBUG("\tBAD ch_mode:%d\n", sbc_cie.ch_mode); - } - - if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_4) { - APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len); - } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_8) { - APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len); - } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_12) { - APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len); - } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_16) { - APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len); - } else { - APPL_TRACE_DEBUG("\tBAD block_len:%d\n", sbc_cie.block_len); - } - - if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_4) { - APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands); - } else if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_8) { - APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands); - } else { - APPL_TRACE_DEBUG("\tBAD num_subbands:%d\n", sbc_cie.num_subbands); - } - - if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_S) { - APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd); - } else if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) { - APPL_TRACE_DEBUG("\talloc_mthd:%d (Loundess)\n", sbc_cie.alloc_mthd); - } else { - APPL_TRACE_DEBUG("\tBAD alloc_mthd:%d\n", sbc_cie.alloc_mthd); - } - - APPL_TRACE_DEBUG("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool); - -} - -#endif /* #if BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h b/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h index 9ec9b5d44..cacaa01d8 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h +++ b/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h @@ -15,7 +15,7 @@ #ifndef __BTC_AV_CO_H__ #define __BTC_AV_CO_H__ -#include "btc_media.h" +#include "btc_a2dp.h" #if (BTA_AV_INCLUDED == TRUE) /******************************************************************************* @@ -93,7 +93,7 @@ void bta_av_co_audio_codec_reset(void); ** Returns TRUE if all opened devices support this codec, FALSE otherwise ** *******************************************************************************/ -BOOLEAN bta_av_co_audio_codec_supported(tBTC_STATUS *p_status); +BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status); /******************************************************************************* ** @@ -106,7 +106,7 @@ BOOLEAN bta_av_co_audio_codec_supported(tBTC_STATUS *p_status); ** Returns TRUE if successful, FALSE otherwise ** *******************************************************************************/ -BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_STATUS *p_status); +BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_AV_STATUS *p_status); /******************************************************************************* ** diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_a2dp.h b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp.h new file mode 100644 index 000000000..6243eb43b --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp.h @@ -0,0 +1,108 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * + * Filename: btc_a2dp.h + * + * Description: Common definitions for A2DP + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_H__ +#define __BTC_A2DP_H__ + +#include +#include "bt_target.h" +#include "bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED + +/******************************************************************************* + ** Constants + *******************************************************************************/ +#define BTC_AV_SUCCESS (0) +/** + * AV (Audio Video source) Errors + */ +#define BTC_ERROR_SRV_AV_NOT_ENABLED 700 /* AV is not enabled */ +#define BTC_ERROR_SRV_AV_FEEDING_NOT_SUPPORTED 701 /* Requested Feeding not supported */ +#define BTC_ERROR_SRV_AV_BUSY 702 /* Another operation ongoing */ +#define BTC_ERROR_SRV_AV_NOT_OPENED 703 /* No AV link opened */ +#define BTC_ERROR_SRV_AV_NOT_STARTED 704 /* AV is not started */ +#define BTC_ERROR_SRV_AV_CP_NOT_SUPPORTED 705 /* Content protection is not supported by all headsets */ + +/* Transcoding definition for TxTranscoding and RxTranscoding */ +#define BTC_MEDIA_TRSCD_OFF 0 +#define BTC_MEDIA_TRSCD_PCM_2_SBC 1 /* Tx */ + + +/******************************************************************************* + ** Data types + *******************************************************************************/ +typedef int tBTC_AV_STATUS; + +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +void btc_a2dp_on_init(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_idle + ** + ** Description Process 'idle' request from BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_on_idle(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_started + ** + ** Description Process 'start' request from BTC AV state machine to prepare + ** for A2DP streaming + ** + ** Return TRUE if an ACK for the local command is sent + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_stopped + ** + ** Description Process 'stop' request from BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_on_suspended + ** + ** Description Process 'stop' request from BTC AV state machine to suspend + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av); + +#endif /* #if BTC_AV_INCLUDED */ + +#endif /* __BTC_A2DP_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_control.h b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_control.h new file mode 100644 index 000000000..e1d5e2ff6 --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_control.h @@ -0,0 +1,110 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * + * Filename: btc_a2dp_control.h + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_CONTROL_H__ +#define __BTC_A2DP_CONTROL_H__ + +#include +#include "bt_target.h" +#include "bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_INCLUDED +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +/******************************************************************************* + ** + ** Function btc_a2dp_control_media_ctrl + ** + ** Description Handle the media_ctrl command + ** + *******************************************************************************/ +void btc_a2dp_control_media_ctrl(esp_a2d_media_ctrl_t ctrl); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_datapath_ctrl + ** + ** Description Handle the media datapath event, which is adapted from UIPC + ** data channel from bluedroid + ** + *******************************************************************************/ +void btc_a2dp_control_datapath_ctrl(uint32_t dp_evt); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_command_ack + ** + ** Description Acknowledge the pending media_ctrl command + ** + *******************************************************************************/ +void btc_a2dp_control_command_ack(int status); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_get_datachnl_stat + ** + ** Description Check whether the data channel state is open + ** + ** Return TRUE if the data channel state is open + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_control_get_datachnl_stat(void); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_set_datachnl_stat + ** + ** Description Set the data channel state flag + ** + *******************************************************************************/ +void btc_a2dp_control_set_datachnl_stat(BOOLEAN open); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_init + ** + ** Description Initialize the A2DP control module. It should be called during + ** the startup stage of A2DP streaming. + ** + *******************************************************************************/ +bool btc_a2dp_control_init(void); + + +/******************************************************************************* + ** + ** Function btc_a2dp_control_cleanup + ** + ** Description Cleanup the A2DP control module + ** + *******************************************************************************/ +void btc_a2dp_control_cleanup(void); + +#endif /* #if BTC_AV_INCLUDED */ + +#endif /* __BTC_A2DP_CONTROL_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_sink.h b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_sink.h new file mode 100644 index 000000000..772ffea6d --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_sink.h @@ -0,0 +1,139 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * + * Filename: btc_a2dp_sink.h + * + *******************************************************************************/ + +#ifndef __BTC_A2DP_SINK_H__ +#define __BTC_A2DP_SINK_H__ + +#include +#include "bt_target.h" +#include "bta_api.h" +#include "btc_av_api.h" +#include "esp_a2dp_api.h" + +#if BTC_AV_SINK_INCLUDED +/******************************************************************************* + ** Data types + *******************************************************************************/ +typedef struct { + BT_HDR hdr; + UINT8 codec_info[AVDT_CODEC_SIZE]; +} tBTC_MEDIA_SINK_CFG_UPDATE; + +/******************************************************************************* + ** Public functions + *******************************************************************************/ + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_startup + ** + ** Description Initialize and startup the A2DP sink module. This function + ** should be called by the BTC AV state machine prior to using + ** the module. + ** + ** Returns true if success + ** + *******************************************************************************/ +bool btc_a2dp_sink_startup(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_shutdown + ** + ** Description Shutdown and cleanup the A2DP sink module + ** + *******************************************************************************/ +void btc_a2dp_sink_shutdown(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_rx_flush_req + ** + ** Description Request to flush audio decoding pipe + ** + ** Returns TRUE if success + ** + *******************************************************************************/ +BOOLEAN btc_a2dp_sink_rx_flush_req(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_enque_buf + ** + ** Description Enqueue a Advance Audio media buffer to be processed by btc media task. + ** + ** Returns size of the queue + ** + *******************************************************************************/ +UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_buf); + + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_idle + ** + ** Description Process 'idle' request from the BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_sink_on_idle(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_stopped + ** + ** Description Process 'stop' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_on_suspended + ** + ** Description Process 'suspend' request from the BTC AV state machine to + ** suspend A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_set_rx_flush + ** + ** Description enable/disabel discarding of received A2DP frames + ** + *******************************************************************************/ +void btc_a2dp_sink_set_rx_flush(BOOLEAN enable); + +/******************************************************************************* + ** + ** Function btc_a2dp_sink_reset_decoder + ** + ** Description Reset decoder parameters according to configuration from remote + ** device + ** + *******************************************************************************/ +void btc_a2dp_sink_reset_decoder(UINT8 *p_av); + +#endif /* #if BTC_AV_SINK_INCLUDED */ + +#endif /* __BTC_A2DP_SINK_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_media.h b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_source.h similarity index 56% rename from components/bt/bluedroid/btc/profile/std/include/btc_media.h rename to components/bt/bluedroid/btc/profile/std/include/btc_a2dp_source.h index 66ad366fb..214c9e723 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_media.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_a2dp_source.h @@ -14,46 +14,24 @@ /******************************************************************************* * - * Filename: btc_media.h - * - * Description: This is the audio module for the BTC system. + * Filename: btc_a2dp_source.h * *******************************************************************************/ -#ifndef __BTC_MEDIA_H__ -#define __BTC_MEDIA_H__ +#ifndef __BTC_A2DP_SOURCE_H__ +#define __BTC_A2DP_SOURCE_H__ #include +#include "bt_target.h" #include "bta_api.h" #include "btc_av_api.h" +#include "esp_a2dp_api.h" -#if (BTA_AV_INCLUDED == TRUE) - -/******************************************************************************* - ** Constants - *******************************************************************************/ -#define BTC_SUCCESS (0) -/** - * AV (Audio Video source) Errors - */ -#define BTC_ERROR_SRV_AV_NOT_ENABLED 700 /* AV is not enabled */ -#define BTC_ERROR_SRV_AV_FEEDING_NOT_SUPPORTED 701 /* Requested Feeding not supported */ -#define BTC_ERROR_SRV_AV_BUSY 702 /* Another operation ongoing */ -#define BTC_ERROR_SRV_AV_NOT_OPENED 703 /* No AV link opened */ -#define BTC_ERROR_SRV_AV_NOT_STARTED 704 /* AV is not started */ -#define BTC_ERROR_SRV_AV_CP_NOT_SUPPORTED 705 /* Content protection is not supported by all headsets */ - -/* Transcoding definition for TxTranscoding and RxTranscoding */ -#define BTC_MEDIA_TRSCD_OFF 0 -#define BTC_MEDIA_TRSCD_PCM_2_SBC 1 /* Tx */ - - +#if BTC_AV_SRC_INCLUDED /******************************************************************************* ** Data types *******************************************************************************/ -typedef int tBTC_STATUS; - /* tBTC_MEDIA_INIT_AUDIO msg structure */ typedef struct { BT_HDR hdr; @@ -65,7 +43,6 @@ typedef struct { UINT16 MtuSize; /* peer mtu size */ } tBTC_MEDIA_INIT_AUDIO; -#if (BTA_AV_INCLUDED == TRUE) /* tBTC_MEDIA_UPDATE_AUDIO msg structure */ typedef struct { BT_HDR hdr; @@ -81,187 +58,187 @@ typedef struct { tBTC_AV_MEDIA_FEEDINGS feeding; } tBTC_MEDIA_INIT_AUDIO_FEEDING; -typedef struct { - BT_HDR hdr; - UINT8 codec_info[AVDT_CODEC_SIZE]; -} tBTC_MEDIA_SINK_CFG_UPDATE; -#endif - /******************************************************************************* ** Public functions *******************************************************************************/ /******************************************************************************* ** - ** Function btc_av_task + ** Function btc_a2dp_source_startup ** - ** Description + ** Description Initialize and startup the A2DP source module. This function + ** should be called by the BTC AV state machine prior to using + ** the module ** - ** Returns void + ** Returns TRUE is success ** *******************************************************************************/ -extern void btc_media_task(void); +bool btc_a2dp_source_startup(void); /******************************************************************************* ** - ** Function btc_media_task_enc_init_req + ** Function btc_a2dp_source_shutdown + ** + ** Description Shutdown and cleanup the A2DP source module. + ** + *******************************************************************************/ +void btc_a2dp_source_shutdown(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_enc_init_req ** ** Description Request to initialize the media task encoder ** ** Returns TRUE is success ** *******************************************************************************/ -extern BOOLEAN btc_media_task_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg); +BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg); /******************************************************************************* ** - ** Function btc_media_task_enc_update_req + ** Function btc_a2dp_source_enc_udpate_req ** ** Description Request to update the media task encoder ** ** Returns TRUE is success ** *******************************************************************************/ -#if (BTA_AV_INCLUDED == TRUE) -extern BOOLEAN btc_media_task_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg); -#endif +BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg); + /******************************************************************************* ** - ** Function btc_media_task_start_aa_req + ** Function btc_a2dp_source_start_audio_req ** ** Description Request to start audio encoding task ** ** Returns TRUE is success ** *******************************************************************************/ -extern BOOLEAN btc_media_task_start_aa_req(void); +BOOLEAN btc_a2dp_source_start_audio_req(void); /******************************************************************************* ** - ** Function btc_media_task_stop_aa_req + ** Function btc_a2dp_source_stop_audio_req ** ** Description Request to stop audio encoding task ** ** Returns TRUE is success ** *******************************************************************************/ -extern BOOLEAN btc_media_task_stop_aa_req(void); +BOOLEAN btc_a2dp_source_stop_audio_req(void); /******************************************************************************* ** - ** Function btc_media_task_aa_rx_flush_req - ** - ** Description Request to flush audio decoding pipe - ** - ** Returns TRUE is success - ** - *******************************************************************************/ -extern BOOLEAN btc_media_task_aa_rx_flush_req(void); -/******************************************************************************* - ** - ** Function btc_media_task_aa_tx_flush_req + ** Function btc_a2dp_source_tx_flush_req ** ** Description Request to flush audio encoding pipe ** ** Returns TRUE is success ** *******************************************************************************/ -extern BOOLEAN btc_media_task_aa_tx_flush_req(void); +BOOLEAN btc_a2dp_source_tx_flush_req(void); /******************************************************************************* ** - ** Function btc_media_aa_readbuf + ** Function btc_a2dp_source_audio_readbuf ** ** Description Read an audio buffer from the BTC media TX queue ** ** Returns pointer on a aa buffer ready to send ** *******************************************************************************/ -extern BT_HDR *btc_media_aa_readbuf(void); +BT_HDR *btc_a2dp_source_audio_readbuf(void); /******************************************************************************* ** - ** Function btc_media_sink_enque_buf - ** - ** Description This function is called by the av_co to fill A2DP Sink Queue - ** - ** - ** Returns size of the queue - *******************************************************************************/ -UINT8 btc_media_sink_enque_buf(BT_HDR *p_buf); - - - -/******************************************************************************* - ** - ** Function btc_media_aa_writebuf - ** - ** Description Enqueue a Advance Audio media buffer to be processed by btc media task. - ** - ** Returns TRUE is success - ** - *******************************************************************************/ -extern void btc_media_aa_writebuf(BT_HDR *pBuf, UINT32 timestamp, UINT16 seq_num); - -/******************************************************************************* - ** - ** Function btc_media_av_writebuf - ** - ** Description Enqueue a video media buffer to be processed by btc media task. - ** - ** Returns TRUE is success - ** - *******************************************************************************/ -extern BOOLEAN btc_media_av_writebuf(UINT8 *p_media, UINT32 media_len, - UINT32 timestamp, UINT16 seq_num); - -#if (BTA_AV_INCLUDED == TRUE) -/******************************************************************************* - ** - ** Function btc_media_task_audio_feeding_init_req + ** Function btc_a2dp_source_audio_feeding_init_req ** ** Description Request to initialize audio feeding ** - ** Returns TRUE is success + ** Returns TRUE if success ** *******************************************************************************/ -extern BOOLEAN btc_media_task_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg); -#endif +BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg); /******************************************************************************* ** - ** Function dump_codec_info + ** Function btc_a2dp_source_is_streaming ** - ** Description Decode and display codec_info (for debug) - ** - ** Returns void + ** Description Check whether A2DP source is in streaming state ** *******************************************************************************/ -extern void dump_codec_info(unsigned char *p_codec); +bool btc_a2dp_source_is_streaming(void); -/** - * Local adaptation helper functions between btc and media task - */ +/******************************************************************************* + ** + ** Function btc_a2dp_source_is_task_shutting_down + ** + ** Description Check whether A2DP source media task is shutting down + ** + *******************************************************************************/ +bool btc_a2dp_source_is_task_shutting_down(void); -bool btc_a2dp_start_media_task(void); -void btc_a2dp_stop_media_task(void); -void btc_a2dp_on_init(void); -void btc_a2dp_setup_codec(void); -void btc_a2dp_on_idle(void); -BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start); -void btc_a2dp_on_stop_req(void); -void btc_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av); -void btc_a2dp_on_suspend(void); -void btc_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av); -void btc_a2dp_set_rx_flush(BOOLEAN enable); -void btc_media_check_iop_exceptions(UINT8 *peer_bda); -void btc_reset_decoder(UINT8 *p_av); +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_idle + ** + ** Description Request 'idle' request from BTC AV state machine during + ** initialization + ** + *******************************************************************************/ +void btc_a2dp_source_on_idle(void); -int btc_a2dp_get_track_frequency(UINT8 frequency); -int btc_a2dp_get_track_channel_count(UINT8 channeltype); -void btc_a2dp_set_peer_sep(UINT8 sep); -#endif ///BTA_AV_INCLUDED == TRUE -#endif +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_stopped + ** + ** Description Process 'stop' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_on_suspended + ** + ** Description Process 'suspend' request from the BTC AV state machine to stop + ** A2DP streaming + ** + *******************************************************************************/ +void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_setup_codec + ** + ** Description initialize the encoder parameters + ** + *******************************************************************************/ +void btc_a2dp_source_setup_codec(void); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_set_tx_flush + ** + ** Description enable/disable discarding of transmitted frames + ** + *******************************************************************************/ +void btc_a2dp_source_set_tx_flush(BOOLEAN enable); + +/******************************************************************************* + ** + ** Function btc_a2dp_source_encoder_update + ** + ** Description update changed SBC encoder parameters + ** + *******************************************************************************/ +void btc_a2dp_source_encoder_update(void); + +#endif /* #if BTC_AV_SRC_INCLUDED */ + +#endif /* __BTC_A2DP_SOURCE_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_av.h b/components/bt/bluedroid/btc/profile/std/include/btc_av.h index 8a8664cd2..312958b0c 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_av.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_av.h @@ -25,17 +25,23 @@ #ifndef __BTC_AV_H__ #define __BTC_AV_H__ +#include "bt_target.h" #include "esp_a2dp_api.h" #include "btc_task.h" #include "btc_common.h" #include "btc_sm.h" #include "bta_av_api.h" -#if (BTA_AV_INCLUDED == TRUE) +#if (BTC_AV_INCLUDED == TRUE) /******************************************************************************* ** Type definitions for callback functions ********************************************************************************/ +enum { + BTC_AV_DATAPATH_OPEN_EVT, // original UIPC_OPEN_EVT for data channel in bluedroid + BTC_AV_DATAPATH_MAX_EVT, +}; + typedef enum { BTC_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT, BTC_AV_DISCONNECT_REQ_EVT, @@ -46,21 +52,44 @@ typedef enum { } btc_av_sm_event_t; typedef enum { +#if BTC_AV_SINK_INCLUDED BTC_AV_SINK_API_INIT_EVT = 0, BTC_AV_SINK_API_DEINIT_EVT, BTC_AV_SINK_API_CONNECT_EVT, BTC_AV_SINK_API_DISCONNECT_EVT, BTC_AV_SINK_API_REG_DATA_CB_EVT, +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + BTC_AV_SRC_API_INIT_EVT, + BTC_AV_SRC_API_DEINIT_EVT, + BTC_AV_SRC_API_CONNECT_EVT, + BTC_AV_SRC_API_DISCONNECT_EVT, + BTC_AV_SRC_API_REG_DATA_CB_EVT, +#endif /* BTC_AV_SRC_INCLUDED */ + BTC_AV_API_MEDIA_CTRL_EVT, + BTC_AV_DATAPATH_CTRL_EVT, } btc_av_act_t; /* btc_av_args_t */ typedef union { +#if BTC_AV_SINK_INCLUDED // BTC_AV_SINK_CONFIG_REQ_EVT -- internal event esp_a2d_mcc_t mcc; // BTC_AV_SINK_API_CONNECT_EVT bt_bdaddr_t connect; // BTC_AV_SINK_API_REG_DATA_CB_EVT - esp_a2d_data_cb_t data_cb; + esp_a2d_sink_data_cb_t data_cb; +#endif /* BTC_AV_SINK_INCLUDED */ +#if BTC_AV_SRC_INCLUDED + // BTC_AV_SRC_API_REG_DATA_CB_EVT + esp_a2d_source_data_cb_t src_data_cb; + // BTC_AV_SRC_API_CONNECT + bt_bdaddr_t src_connect; +#endif /* BTC_AV_SRC_INCLUDED */ + // BTC_AV_API_MEDIA_CTRL_EVT + esp_a2d_media_ctrl_t ctrl; + // BTC_AV_DATAPATH_CTRL_EVT + uint32_t dp_evt; } btc_av_args_t; /******************************************************************************* @@ -71,7 +100,9 @@ void btc_a2dp_call_handler(btc_msg_t *msg); void btc_a2dp_cb_handler(btc_msg_t *msg); -void btc_a2dp_sink_reg_data_cb(esp_a2d_data_cb_t callback); +void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback); + +void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback); /******************************************************************************* ** ** Function btc_av_get_sm_handle @@ -121,18 +152,6 @@ BOOLEAN btc_av_stream_started_ready(void); /* used to pass events to AV statemachine from other tasks */ void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len); -/******************************************************************************* -** -** Function btc_av_init -** -** Description Initializes btc AV if not already done -** -** Returns bt_status_t -** -*******************************************************************************/ - -bt_status_t btc_av_init(void); - /******************************************************************************* ** ** Function btc_av_is_connected @@ -146,6 +165,19 @@ bt_status_t btc_av_init(void); BOOLEAN btc_av_is_connected(void); +/******************************************************************************* + * + * Function btc_av_get_peer_sep + * + * Description Get the stream endpoint type. + * + * Returns The stream endpoint type: either AVDT_TSEP_SRC or + * AVDT_TSEP_SNK. + * + ******************************************************************************/ + +uint8_t btc_av_get_peer_sep(void); + /******************************************************************************* ** ** Function btc_av_is_peer_edr @@ -171,6 +203,6 @@ BOOLEAN btc_av_is_peer_edr(void); ********************************************************************************/ void btc_av_clear_remote_suspend_flag(void); -#endif ///BTA_AV_INCLUDED == TRUE +#endif ///BTC_AV_INCLUDED == TRUE #endif /* __BTC_AV_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_av_api.h b/components/bt/bluedroid/btc/profile/std/include/btc_av_api.h index c7672a075..7d5f376d6 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_av_api.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_av_api.h @@ -27,8 +27,6 @@ #include "bt_target.h" #include "bta_av_api.h" - -#include "btc_media.h" #include "a2d_api.h" #include "a2d_sbc.h" diff --git a/components/bt/bluedroid/btif/bta_dm_co.c b/components/bt/bluedroid/btif/bta_dm_co.c index 83e6db1fe..f71ebfc44 100644 --- a/components/bt/bluedroid/btif/bta_dm_co.c +++ b/components/bt/bluedroid/btif/bta_dm_co.c @@ -24,7 +24,7 @@ #include "bta_dm_ci.h" #include "btc_dm.h" #if (defined(BTIF_INCLUDED) && BTIF_INCLUDED == TRUE) -#include "bt_utils.h" +#include "bt_defs.h" #if (BTM_OOB_INCLUDED == TRUE) #include "btif_dm.h" #endif diff --git a/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c b/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c index adc0eeda7..2de5a81aa 100644 --- a/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c +++ b/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c @@ -27,6 +27,7 @@ #include "sbc_encoder.h" #include "sbc_enc_func_declare.h" /*#include */ +#if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) #if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) #define WIND_4_SUBBANDS_0_1 (SINT32)0x01659F45 /* gas32CoeffFor4SBs[8] = -gas32CoeffFor4SBs[32] = 0x01659F45 */ diff --git a/components/bt/bluedroid/include/bt_target.h b/components/bt/bluedroid/include/bt_target.h index 2393f9244..5be76cc0a 100644 --- a/components/bt/bluedroid/include/bt_target.h +++ b/components/bt/bluedroid/include/bt_target.h @@ -39,81 +39,57 @@ #include "dyn_mem.h" /* defines static and/or dynamic memory for components */ +/****************************************************************************** +** +** Classic BT features +** +******************************************************************************/ #if CONFIG_CLASSIC_BT_ENABLED #define CLASSIC_BT_INCLUDED TRUE +#define BTC_SM_INCLUDED TRUE +#define BTC_PRF_QUEUE_INCLUDED TRUE +#define BTC_GAP_BT_INCLUDED TRUE #define BTA_SDP_INCLUDED TRUE -#define BTA_PAN_INCLUDED FALSE -#define BTA_HH_INCLUDED FALSE #define SDP_INCLUDED TRUE -#if CONFIG_A2DP_SNK_ENABLED +#if CONFIG_A2DP_ENABLE #define BTA_AR_INCLUDED TRUE #define BTA_AV_INCLUDED TRUE -#define BTA_AV_SINK_INCLUDED TRUE #define AVDT_INCLUDED TRUE #define A2D_INCLUDED TRUE #define AVCT_INCLUDED TRUE #define AVRC_INCLUDED TRUE #define BTC_AV_INCLUDED TRUE +#endif /* CONFIG_A2DP_ENABLE */ + +#if CONFIG_A2DP_SINK_ENABLE +#define BTA_AV_SINK_INCLUDED TRUE +#define BTC_AV_SINK_INCLUDED TRUE #define SBC_DEC_INCLUDED TRUE -#else -#define BTA_AR_INCLUDED FALSE -#define BTA_AV_INCLUDED FALSE -#define BTA_AV_SINK_INCLUDED FALSE -#define AVDT_INCLUDED FALSE -#define A2D_INCLUDED FALSE -#define AVCT_INCLUDED FALSE -#define AVRC_INCLUDED FALSE -#define BTC_AV_INCLUDED FALSE -#define SBC_DEC_INCLUDED FALSE -#endif /* CONFIG_A2DP_SNK_ENABLED */ +#endif /* CONFIG_A2DP_SINK_ENABLE */ + +#if CONFIG_A2DP_SRC_ENABLE +#define BTC_AV_SRC_INCLUDED TRUE +#define SBC_ENC_INCLUDED TRUE +#endif /* CONFIG_A2DP_SRC_ENABLE */ #if CONFIG_BT_SPP_ENABLED #define RFCOMM_INCLUDED TRUE #define BTA_JV_INCLUDED TRUE #define BTC_SPP_INCLUDED TRUE -#else /* #if CONFIG_BT_SPP_ENABLED */ -#define RFCOMM_INCLUDED FALSE -#define BTA_JV_INCLUDED FALSE -#define BTC_SPP_INCLUDED FALSE -#endif /* #if CONFIG_BT_SPP_ENABLED */ - -#define PAN_INCLUDED FALSE -#define HID_HOST_INCLUDED FALSE -#define SBC_ENC_INCLUDED FALSE -#define MCA_INCLUDED FALSE -#define BTC_SM_INCLUDED TRUE -#define BTC_PRF_QUEUE_INCLUDED TRUE -#define BTC_GAP_BT_INCLUDED TRUE - -#else /* #if CONFIG_CLASSIC_BT_ENABLED */ -#define CLASSIC_BT_INCLUDED FALSE -#define BTA_SDP_INCLUDED FALSE -#define BTA_PAN_INCLUDED FALSE -#define BTA_HH_INCLUDED FALSE -#define BTA_AR_INCLUDED FALSE -#define BTA_AV_INCLUDED FALSE -#define BTA_AV_SINK_INCLUDED FALSE -#define SDP_INCLUDED FALSE -#define RFCOMM_INCLUDED FALSE -#define BTA_JV_INCLUDED FALSE -#define BTC_SPP_INCLUDED FALSE -#define PAN_INCLUDED FALSE -#define HID_HOST_INCLUDED FALSE -#define AVDT_INCLUDED FALSE -#define A2D_INCLUDED FALSE -#define AVCT_INCLUDED FALSE -#define AVRC_INCLUDED FALSE -#define SBC_DEC_INCLUDED FALSE -#define SBC_ENC_INCLUDED FALSE -#define MCA_INCLUDED FALSE -#define BTC_SM_INCLUDED FALSE -#define BTC_PRF_QUEUE_INCLUDED FALSE -#define BTC_GAP_BT_INCLUDED FALSE -#define BTC_AV_INCLUDED FALSE +#endif /* CONFIG_BT_SPP_ENABLED */ #endif /* #if CONFIG_CLASSIC_BT_ENABLED */ +#ifndef CLASSIC_BT_INCLUDED +#define CLASSIC_BT_INCLUDED FALSE +#endif /* CLASSIC_BT_INCLUDED */ + +/****************************************************************************** +** +** BLE features +** +******************************************************************************/ #if (CONFIG_GATTS_ENABLE) #define GATTS_INCLUDED TRUE #else @@ -164,16 +140,62 @@ #define BTIF_INCLUDED FALSE #endif +/****************************************************************************** +** +** BTC-layer components +** +******************************************************************************/ +#ifndef BTC_GAP_BT_INCLUDED +#define BTC_GAP_BT_INCLUDED FALSE +#endif + +#ifndef BTC_PRF_QUEUE_INCLUDED +#define BTC_PRF_QUEUE_INCLUDED FALSE +#endif + +#ifndef BTC_SM_INCLUDED +#define BTC_SM_INCLUDED FALSE +#endif + +#ifndef BTC_AV_INCLUDED +#define BTC_AV_INCLUDED FALSE +#endif + +#ifndef BTC_AV_SINK_INCLUDED +#define BTC_AV_SINK_INCLUDED FALSE +#endif + +#ifndef BTC_AV_SRC_INCLUDED +#define BTC_AV_SRC_INCLUDED FALSE +#endif + +#ifndef BTC_SPP_INCLUDED +#define BTC_SPP_INCLUDED FALSE +#endif + +#ifndef SBC_DEC_INCLUDED +#define SBC_DEC_INCLUDED FALSE +#endif + +#ifndef SBC_ENC_INCLUDED +#define SBC_ENC_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** BTA-layer components +** +******************************************************************************/ #ifndef BTA_INCLUDED #define BTA_INCLUDED TRUE #endif #ifndef BTA_PAN_INCLUDED -#define BTA_PAN_INCLUDED FALSE//TRUE +#define BTA_PAN_INCLUDED FALSE #endif #ifndef BTA_HH_INCLUDED -#define BTA_HH_INCLUDED FALSE//TRUE +#define BTA_HH_INCLUDED FALSE #endif #ifndef BTA_HH_ROLE @@ -181,21 +203,47 @@ #endif #ifndef BTA_HH_LE_INCLUDED -#define BTA_HH_LE_INCLUDED FALSE//TRUE +#define BTA_HH_LE_INCLUDED FALSE #endif #ifndef BTA_AR_INCLUDED -#define BTA_AR_INCLUDED TRUE//TRUE +#define BTA_AR_INCLUDED FALSE #endif #ifndef BTA_AV_INCLUDED -#define BTA_AV_INCLUDED TRUE//TRUE +#define BTA_AV_INCLUDED FALSE #endif #ifndef BTA_AV_SINK_INCLUDED -#define BTA_AV_SINK_INCLUDED TRUE//FALSE +#define BTA_AV_SINK_INCLUDED FALSE #endif +#ifndef BTA_JV_INCLUDED +#define BTA_JV_INCLUDED FALSE +#endif + +#ifndef BTA_SDP_INCLUDED +#define BTA_SDP_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** Stack-layer components +** +******************************************************************************/ +#ifndef AVCT_INCLUDED +#define AVCT_INCLUDED FALSE +#endif + +#ifndef AVDT_INCLUDED +#define AVDT_INCLUDED FALSE +#endif + +/****************************************************************************** +** +** Parameter Configurations for components +** +******************************************************************************/ #ifndef BTA_DISABLE_DELAY #define BTA_DISABLE_DELAY 200 /* in milliseconds */ #endif @@ -1071,7 +1119,7 @@ ******************************************************************************/ #ifndef SDP_INCLUDED -#define SDP_INCLUDED FALSE //TRUE +#define SDP_INCLUDED FALSE #endif /* This is set to enable SDP server functionality. */ @@ -1458,7 +1506,7 @@ Range: 2 octets ******************************************************************************/ #ifndef PAN_INCLUDED -#define PAN_INCLUDED FALSE//TRUE +#define PAN_INCLUDED FALSE #endif /* This will enable the PANU role */ @@ -1603,7 +1651,7 @@ Range: 2 octets ** Definitions for HID-Host */ #ifndef HID_HOST_INCLUDED -#define HID_HOST_INCLUDED FALSE//TRUE +#define HID_HOST_INCLUDED FALSE #endif #ifndef HID_HOST_MAX_DEVICES @@ -1630,7 +1678,7 @@ Range: 2 octets * A2DP Definitions */ #ifndef A2D_INCLUDED -#define A2D_INCLUDED FALSE//TRUE +#define A2D_INCLUDED FALSE #endif /****************************************************************************** @@ -1655,7 +1703,7 @@ Range: 2 octets ** ******************************************************************************/ #ifndef AVRC_INCLUDED -#define AVRC_INCLUDED TRUE +#define AVRC_INCLUDED FALSE #endif #ifndef AVRC_METADATA_INCLUDED diff --git a/components/bt/bluedroid/osi/alarm.c b/components/bt/bluedroid/osi/alarm.c index 034ff902c..bdc58ec44 100644 --- a/components/bt/bluedroid/osi/alarm.c +++ b/components/bt/bluedroid/osi/alarm.c @@ -47,6 +47,7 @@ static int alarm_state; static struct alarm_t alarm_cbs[ALARM_CBS_NUM]; static osi_alarm_err_t alarm_free(osi_alarm_t *alarm); +static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic); int osi_alarm_create_mux(void) { @@ -207,7 +208,7 @@ end: return; } -osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout) +static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic) { assert(alarm_mutex != NULL); @@ -226,19 +227,34 @@ osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout) } int64_t timeout_us = 1000 * (int64_t)timeout; - esp_err_t stat = esp_timer_start_once(alarm->alarm_hdl, (uint64_t)timeout_us); + esp_err_t stat; + if (is_periodic) { + stat = esp_timer_start_periodic(alarm->alarm_hdl, (uint64_t)timeout_us); + } else { + stat = esp_timer_start_once(alarm->alarm_hdl, (uint64_t)timeout_us); + } if (stat != ESP_OK) { LOG_ERROR("%s failed to start timer, err 0x%x\n", __func__, stat); ret = OSI_ALARM_ERR_FAIL; goto end; } - alarm->deadline_us = timeout_us + esp_timer_get_time(); + alarm->deadline_us = is_periodic ? 0 : (timeout_us + esp_timer_get_time()); end: osi_mutex_unlock(&alarm_mutex); return ret; } +osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout) +{ + return alarm_set(alarm, timeout, false); +} + +osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period) +{ + return alarm_set(alarm, period, true); +} + osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm) { int ret = OSI_ALARM_ERR_PASS; diff --git a/components/bt/bluedroid/osi/include/alarm.h b/components/bt/bluedroid/osi/include/alarm.h index e71168ea0..3dc177c7a 100644 --- a/components/bt/bluedroid/osi/include/alarm.h +++ b/components/bt/bluedroid/osi/include/alarm.h @@ -57,6 +57,9 @@ void osi_alarm_free(osi_alarm_t *alarm); // |alarm| and |cb| may not be NULL. osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout); +// Sets an periodic alarm to fire |cb| each given |period|. +osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period); + // This function cancels the |alarm| if it was previously set. When this call // returns, the caller has a guarantee that the callback is not in progress and // will not be called if it hasn't already been called. This function is idempotent. @@ -65,6 +68,7 @@ osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm); // Figure out how much time until next expiration. // Returns 0 if not armed. |alarm| may not be NULL. +// only for oneshot alarm, not for periodic alarm // TODO: Remove this function once PM timers can be re-factored period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm); diff --git a/components/bt/bluedroid/osi/include/thread.h b/components/bt/bluedroid/osi/include/thread.h index 9ac5da6bd..afd3f08d0 100644 --- a/components/bt/bluedroid/osi/include/thread.h +++ b/components/bt/bluedroid/osi/include/thread.h @@ -84,10 +84,10 @@ typedef enum { #define BTC_TASK_QUEUE_LEN 60 #define BTC_MEDIA_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_MEDIA_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) +#define BTC_MEDIA_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) #define BTC_MEDIA_TASK_NAME "BtcMediaT" #define BTC_MEDIA_TASK_PRIO (configMAX_PRIORITIES - 3) -#define BTC_MEDIA_DATA_QUEUE_LEN (1) +#define BTC_MEDIA_DATA_QUEUE_LEN (3) #define BTC_MEDIA_CTRL_QUEUE_LEN (5) #define BTC_MEDIA_TASK_QUEUE_SET_LEN (BTC_MEDIA_DATA_QUEUE_LEN + BTC_MEDIA_CTRL_QUEUE_LEN) diff --git a/components/bt/bluedroid/stack/a2dp/a2d_sbc.c b/components/bt/bluedroid/stack/a2dp/a2d_sbc.c index 3a8f694fa..0c2b4adc8 100644 --- a/components/bt/bluedroid/stack/a2dp/a2d_sbc.c +++ b/components/bt/bluedroid/stack/a2dp/a2d_sbc.c @@ -29,7 +29,7 @@ #include "a2d_api.h" #include "a2d_int.h" #include "a2d_sbc.h" -#include "bt_utils.h" +#include "bt_defs.h" #if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/stack/avct/avct_api.c b/components/bt/bluedroid/stack/avct/avct_api.c index 675ecaf83..3ff678b93 100644 --- a/components/bt/bluedroid/stack/avct/avct_api.c +++ b/components/bt/bluedroid/stack/avct/avct_api.c @@ -25,7 +25,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "l2c_api.h" #include "l2cdefs.h" #include "btm_api.h" diff --git a/components/bt/bluedroid/stack/avct/avct_l2c.c b/components/bt/bluedroid/stack/avct/avct_l2c.c index a1656d7f5..35abf45b2 100644 --- a/components/bt/bluedroid/stack/avct/avct_l2c.c +++ b/components/bt/bluedroid/stack/avct/avct_l2c.c @@ -25,7 +25,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avct_api.h" #include "avct_int.h" #include "l2c_api.h" diff --git a/components/bt/bluedroid/stack/avct/avct_lcb.c b/components/bt/bluedroid/stack/avct/avct_lcb.c index faf07b0a7..71c70f797 100644 --- a/components/bt/bluedroid/stack/avct/avct_lcb.c +++ b/components/bt/bluedroid/stack/avct/avct_lcb.c @@ -26,7 +26,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avct_api.h" #include "avct_int.h" #include "allocator.h" diff --git a/components/bt/bluedroid/stack/avct/avct_lcb_act.c b/components/bt/bluedroid/stack/avct/avct_lcb_act.c index b61690616..c33aa8943 100644 --- a/components/bt/bluedroid/stack/avct/avct_lcb_act.c +++ b/components/bt/bluedroid/stack/avct/avct_lcb_act.c @@ -25,7 +25,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avct_api.h" #include "avct_int.h" #include "btm_api.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_ad.c b/components/bt/bluedroid/stack/avdt/avdt_ad.c index a47241204..9a85fcef4 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_ad.c +++ b/components/bt/bluedroid/stack/avdt/avdt_ad.c @@ -28,7 +28,7 @@ #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_ccb.c b/components/bt/bluedroid/stack/avdt/avdt_ccb.c index 55cd1f9dc..f047db6bd 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_ccb.c +++ b/components/bt/bluedroid/stack/avdt/avdt_ccb.c @@ -26,7 +26,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c b/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c index 0314438d5..2a35906eb 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c +++ b/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c @@ -26,7 +26,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_l2c.c b/components/bt/bluedroid/stack/avdt/avdt_l2c.c index b99e83159..8df482fcc 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_l2c.c +++ b/components/bt/bluedroid/stack/avdt/avdt_l2c.c @@ -25,7 +25,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_msg.c b/components/bt/bluedroid/stack/avdt/avdt_msg.c index 43245b87b..61378a6b1 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_msg.c +++ b/components/bt/bluedroid/stack/avdt/avdt_msg.c @@ -29,7 +29,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_scb.c b/components/bt/bluedroid/stack/avdt/avdt_scb.c index bbad58528..060884e1d 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_scb.c +++ b/components/bt/bluedroid/stack/avdt/avdt_scb.c @@ -26,7 +26,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avdt/avdt_scb_act.c b/components/bt/bluedroid/stack/avdt/avdt_scb_act.c index d7699c2e6..963e8c3f1 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_scb_act.c +++ b/components/bt/bluedroid/stack/avdt/avdt_scb_act.c @@ -26,7 +26,7 @@ #include #include "bt_types.h" #include "bt_target.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "avdt_api.h" #include "avdtc_api.h" #include "avdt_int.h" diff --git a/components/bt/bluedroid/stack/avrc/avrc_bld_tg.c b/components/bt/bluedroid/stack/avrc/avrc_bld_tg.c index 19ac85c58..2ab7c0534 100644 --- a/components/bt/bluedroid/stack/avrc/avrc_bld_tg.c +++ b/components/bt/bluedroid/stack/avrc/avrc_bld_tg.c @@ -20,7 +20,7 @@ #include "avrc_api.h" #include "avrc_defs.h" #include "avrc_int.h" -#include "bt_utils.h" +#include "bt_defs.h" #include "allocator.h" #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/stack/avrc/avrc_pars_ct.c b/components/bt/bluedroid/stack/avrc/avrc_pars_ct.c index 02b1eac88..a108d7077 100644 --- a/components/bt/bluedroid/stack/avrc/avrc_pars_ct.c +++ b/components/bt/bluedroid/stack/avrc/avrc_pars_ct.c @@ -20,7 +20,7 @@ #include "avrc_api.h" #include "avrc_defs.h" #include "avrc_int.h" -#include "bt_utils.h" +#include "bt_defs.h" #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/utils/bt_utils.c b/components/bt/bluedroid/utils/bt_utils.c deleted file mode 100644 index 410b83ea9..000000000 --- a/components/bt/bluedroid/utils/bt_utils.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "bt_utils.h" - - -/***************************************************************************** -** -** Function raise_priority_a2dp -** -** Description Raise task priority for A2DP streaming -** -** Returns void -** -*******************************************************************************/ -void raise_priority_a2dp(tHIGH_PRIORITY_TASK high_task) -{ - (void) high_task; - return; -} - -/***************************************************************************** -** -** Function adjust_priority_a2dp -** -** Description increase the a2dp consumer task priority temporarily when start -** audio playing, to avoid overflow the audio packet queue, restore -** the a2dp consumer task priority when stop audio playing. -** -** Returns void -** -*******************************************************************************/ -void adjust_priority_a2dp(int start) -{ - (void) start; - return; -} diff --git a/components/bt/bluedroid/utils/include/bt_utils.h b/components/bt/bluedroid/utils/include/bt_utils.h deleted file mode 100644 index 39e253551..000000000 --- a/components/bt/bluedroid/utils/include/bt_utils.h +++ /dev/null @@ -1,46 +0,0 @@ -/****************************************************************************** - * - * Copyright (C) 2009-2012 Broadcom Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ******************************************************************************/ - -#ifndef BT_UTILS_H -#define BT_UTILS_H - -// static const char BT_UTILS_MODULE[] = "bt_utils_module"; - -/******************************************************************************* -** Type definitions -********************************************************************************/ - -typedef enum { - TASK_HIGH_MEDIA = 0, - TASK_HIGH_GKI_TIMER, - TASK_HIGH_BTU, - TASK_HIGH_HCI_WORKER, - TASK_HIGH_USERIAL_READ, - TASK_UIPC_READ, - TASK_JAVA_ALARM, - TASK_HIGH_MAX -} tHIGH_PRIORITY_TASK; - -/******************************************************************************* -** Functions -********************************************************************************/ - -void raise_priority_a2dp(tHIGH_PRIORITY_TASK high_task); -void adjust_priority_a2dp(int start); -#define UNUSED(x) (void)(x) -#endif /* BT_UTILS_H */ diff --git a/components/bt/component.mk b/components/bt/component.mk index 4fa144ba0..a2682cdd1 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -31,6 +31,7 @@ COMPONENT_ADD_INCLUDEDIRS += bluedroid/bta/include \ bluedroid/osi/include \ bluedroid/utils/include \ bluedroid/external/sbc/decoder/include \ + bluedroid/external/sbc/encoder/include \ bluedroid/btc/core/include \ bluedroid/btc/profile/esp/blufi/include \ bluedroid/btc/profile/esp/include \ @@ -75,6 +76,7 @@ COMPONENT_SRCDIRS += bluedroid/bta/dm \ bluedroid/main \ bluedroid/osi \ bluedroid/external/sbc/decoder/srce \ + bluedroid/external/sbc/encoder/srce \ bluedroid/btc/core \ bluedroid/btc/profile/esp/blufi \ bluedroid/btc/profile/std/gap \ diff --git a/examples/bluetooth/a2dp_sink/README.md b/examples/bluetooth/a2dp_sink/README.md index b27393a73..20b50d928 100755 --- a/examples/bluetooth/a2dp_sink/README.md +++ b/examples/bluetooth/a2dp_sink/README.md @@ -3,13 +3,14 @@ ESP-IDF A2DP-SINK demo Demo of A2DP audio sink role -This is the demo for user to use ESP_APIs to create a GATT Server. +This is the demo for user to use ESP_APIs to use Advanced Audio Distribution Profile in receiving audio stream Options choose step: 1. make menuconfig. 2. enter menuconfig "Component config", choose "Bluetooth" - 3. enter menu Bluetooth, choose "Classic Bluetooth" and do not choose "Release DRAM from Classic BT controller" - 4. choose your options. + 3. enter menu Bluetooth, choose "Bluedroid Enable" + 4. enter menu Bluedroid Enable, choose "Classic Bluetooth" + 5. select "A2DP" and choose "SINK" For the I2S codec, pick whatever chip or board works for you; this code was written using a PCM5102 chip, but other I2S boards and chips will probably work as well. The default I2S connections are shown below, but these can be changed in menuconfig: diff --git a/examples/bluetooth/a2dp_sink/main/main.c b/examples/bluetooth/a2dp_sink/main/main.c index aa489f583..a60933862 100644 --- a/examples/bluetooth/a2dp_sink/main/main.c +++ b/examples/bluetooth/a2dp_sink/main/main.c @@ -125,7 +125,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) /* initialize A2DP sink */ esp_a2d_register_callback(&bt_app_a2d_cb); - esp_a2d_register_data_callback(bt_app_a2d_data_cb); + esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); esp_a2d_sink_init(); /* initialize AVRCP controller */ diff --git a/examples/bluetooth/a2dp_sink/sdkconfig.defaults b/examples/bluetooth/a2dp_sink/sdkconfig.defaults index 371169bd4..d99fa240c 100644 --- a/examples/bluetooth/a2dp_sink/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_sink/sdkconfig.defaults @@ -1,6 +1,12 @@ # Override some defaults so BT stack is enabled and # Classic BT is enabled and BT_DRAM_RELEASE is disabled CONFIG_BT_ENABLED=y +CONFIG_BLUEDROID_ENABLED=y CONFIG_CLASSIC_BT_ENABLED=y -CONFIG_A2DP_SNK_ENABLED=y - +CONFIG_A2DP_ENABLE=y +CONFIG_A2DP_SINK_ENABLE=y +CONFIG_A2DP_SRC_ENABLE= +CONFIG_BT_SPP_ENABLED= +CONFIG_GATTS_ENABLE= +CONFIG_GATTC_ENABLE= +CONFIG_BLE_SMP_ENABLE= diff --git a/examples/bluetooth/a2dp_source/Makefile b/examples/bluetooth/a2dp_source/Makefile new file mode 100755 index 000000000..62e30c037 --- /dev/null +++ b/examples/bluetooth/a2dp_source/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := a2dp_source + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/bluetooth/a2dp_source/README.md b/examples/bluetooth/a2dp_source/README.md new file mode 100755 index 000000000..457ee5c40 --- /dev/null +++ b/examples/bluetooth/a2dp_source/README.md @@ -0,0 +1,18 @@ +ESP-IDF A2DP-SOURCE demo +======================== + +Demo of A2DP audio source role + +This is the demo for user to use ESP_APIs to use Advanced Audio Distribution Profile in transmitting audio stream + +Options choose step: + 1. make menuconfig. + 2. enter menuconfig "Component config", choose "Bluetooth" + 3. enter menu Bluetooth, choose "Bluedroid Enable" + 4. enter menu Bluedroid Enable, choose "Classic Bluetooth" + 5. select "A2DP" and choose "SOURCE" + +In this example, the bluetooth device implements A2DP source. The A2DP sink device to be connected to can be set up with the example "A2DP sink" in another folder in ESP-IDF example directory. +For the first step, the device performs device discovery to find a target device(A2DP sink) named "ESP_SPEAKER". Then it initiate connection with the target device. +After connection is established, the device then start media transmission. The raw PCM media stream to be encoded and transmited in this example is random sequence therefore continuous noise can be heard if the stream is decoded and played on the sink side. +After a period of time, media stream suspend, disconnection and reconnection procedure will be performed. diff --git a/examples/bluetooth/a2dp_source/main/bt_app_core.c b/examples/bluetooth/a2dp_source/main/bt_app_core.c new file mode 100644 index 000000000..5be9f525a --- /dev/null +++ b/examples/bluetooth/a2dp_source/main/bt_app_core.c @@ -0,0 +1,119 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "bt_app_core.h" + +static void bt_app_task_handler(void *arg); +static bool bt_app_send_msg(bt_app_msg_t *msg); +static void bt_app_work_dispatched(bt_app_msg_t *msg); + +static xQueueHandle bt_app_task_queue = NULL; +static xTaskHandle bt_app_task_handle = NULL; + +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) +{ + ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len); + + bt_app_msg_t msg; + memset(&msg, 0, sizeof(bt_app_msg_t)); + + msg.sig = BT_APP_SIG_WORK_DISPATCH; + msg.event = event; + msg.cb = p_cback; + + if (param_len == 0) { + return bt_app_send_msg(&msg); + } else if (p_params && param_len > 0) { + if ((msg.param = malloc(param_len)) != NULL) { + memcpy(msg.param, p_params, param_len); + /* check if caller has provided a copy callback to do the deep copy */ + if (p_copy_cback) { + p_copy_cback(&msg, msg.param, p_params); + } + return bt_app_send_msg(&msg); + } + } + + return false; +} + +static bool bt_app_send_msg(bt_app_msg_t *msg) +{ + if (msg == NULL) { + return false; + } + + if (xQueueSend(bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { + ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); + return false; + } + return true; +} + +static void bt_app_work_dispatched(bt_app_msg_t *msg) +{ + if (msg->cb) { + msg->cb(msg->event, msg->param); + } +} + +static void bt_app_task_handler(void *arg) +{ + bt_app_msg_t msg; + for (;;) { + if (pdTRUE == xQueueReceive(bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) { + ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); + switch (msg.sig) { + case BT_APP_SIG_WORK_DISPATCH: + bt_app_work_dispatched(&msg); + break; + default: + ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig); + break; + } // switch (msg.sig) + + if (msg.param) { + free(msg.param); + } + } + } +} + +void bt_app_task_start_up(void) +{ + bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); + xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, bt_app_task_handle); + return; +} + +void bt_app_task_shut_down(void) +{ + if (bt_app_task_handle) { + vTaskDelete(bt_app_task_handle); + bt_app_task_handle = NULL; + } + if (bt_app_task_queue) { + vQueueDelete(bt_app_task_queue); + bt_app_task_queue = NULL; + } +} diff --git a/examples/bluetooth/a2dp_source/main/bt_app_core.h b/examples/bluetooth/a2dp_source/main/bt_app_core.h new file mode 100644 index 000000000..0238ba4e1 --- /dev/null +++ b/examples/bluetooth/a2dp_source/main/bt_app_core.h @@ -0,0 +1,53 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __BT_APP_CORE_H__ +#define __BT_APP_CORE_H__ + +#include +#include +#include + +#define BT_APP_CORE_TAG "BT_APP_CORE" + +#define BT_APP_SIG_WORK_DISPATCH (0x01) + +/** + * @brief handler for the dispatched work + */ +typedef void (* bt_app_cb_t) (uint16_t event, void *param); + +/* message to be sent */ +typedef struct { + uint16_t sig; /*!< signal to bt_app_task */ + uint16_t event; /*!< message event id */ + bt_app_cb_t cb; /*!< context switch callback */ + void *param; /*!< parameter area needs to be last */ +} bt_app_msg_t; + +/** + * @brief parameter deep-copy function to be customized + */ +typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src); + +/** + * @brief work dispatcher for the application task + */ +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback); + +void bt_app_task_start_up(void); + +void bt_app_task_shut_down(void); + +#endif /* __BT_APP_CORE_H__ */ diff --git a/examples/bluetooth/a2dp_source/main/component.mk b/examples/bluetooth/a2dp_source/main/component.mk new file mode 100755 index 000000000..0b9d7585e --- /dev/null +++ b/examples/bluetooth/a2dp_source/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/bluetooth/a2dp_source/main/main.c b/examples/bluetooth/a2dp_source/main/main.c new file mode 100644 index 000000000..2d8039187 --- /dev/null +++ b/examples/bluetooth/a2dp_source/main/main.c @@ -0,0 +1,526 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "esp_bt.h" +#include "bt_app_core.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gap_bt_api.h" +#include "esp_a2dp_api.h" +#include "esp_avrc_api.h" + +#define BT_AV_TAG "BT_AV" + +/* event for handler "bt_av_hdl_stack_up */ +enum { + BT_APP_EVT_STACK_UP = 0, +}; + +/* A2DP global state */ +enum { + APP_AV_STATE_IDLE, + APP_AV_STATE_DISCOVERING, + APP_AV_STATE_DISCOVERED, + APP_AV_STATE_UNCONNECTED, + APP_AV_STATE_CONNECTING, + APP_AV_STATE_CONNECTED, + APP_AV_STATE_DISCONNECTING, +}; + +/* sub states of APP_AV_STATE_CONNECTED */ +enum { + APP_AV_MEDIA_STATE_IDLE, + APP_AV_MEDIA_STATE_STARTING, + APP_AV_MEDIA_STATE_STARTED, + APP_AV_MEDIA_STATE_STOPPING, +}; + +#define BT_APP_HEART_BEAT_EVT (0xff00) + +/// handler for bluetooth stack enabled events +static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); + +/// callback function for A2DP source +static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); + +/// callback function for A2DP source audio data stream +static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len); + +static void a2d_app_heart_beat(void *arg); + +/// A2DP application state machine +static void bt_app_av_sm_hdlr(uint16_t event, void *param); + +/* A2DP application state machine handler for each state */ +static void bt_app_av_state_unconnected(uint16_t event, void *param); +static void bt_app_av_state_connecting(uint16_t event, void *param); +static void bt_app_av_state_connected(uint16_t event, void *param); +static void bt_app_av_state_disconnecting(uint16_t event, void *param); + +static esp_bd_addr_t peer_bda = {0}; +static uint8_t peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; +static int m_a2d_state = APP_AV_STATE_IDLE; +static int m_media_state = APP_AV_MEDIA_STATE_IDLE; +static int m_intv_cnt = 0; +static int m_connecting_intv = 0; +static uint32_t m_pkt_cnt = 0; + +TimerHandle_t tmr; + +static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) +{ + if (bda == NULL || str == NULL || size < 18) { + return NULL; + } + + uint8_t *p = bda; + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + return str; +} + +void app_main() +{ + // Initialize NVS. + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + if (esp_bt_controller_init(&bt_cfg) != ESP_OK) { + ESP_LOGE(BT_AV_TAG, "%s initialize controller failed\n", __func__); + return; + } + + if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) { + ESP_LOGE(BT_AV_TAG, "%s enable controller failed\n", __func__); + return; + } + + if (esp_bluedroid_init() != ESP_OK) { + ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed\n", __func__); + return; + } + + if (esp_bluedroid_enable() != ESP_OK) { + ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed\n", __func__); + return; + } + + /* create application task */ + bt_app_task_start_up(); + + /* Bluetooth device name, connection mode and profile set up */ + bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); +} + +static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) +{ + uint8_t *rmt_bdname = NULL; + uint8_t rmt_bdname_len = 0; + + if (!eir) { + return false; + } + + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); + if (!rmt_bdname) { + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); + } + + if (rmt_bdname) { + if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { + rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; + } + + if (bdname) { + memcpy(bdname, rmt_bdname, rmt_bdname_len); + bdname[rmt_bdname_len] = '\0'; + } + if (bdname_len) { + *bdname_len = rmt_bdname_len; + } + return true; + } + + return false; +} + +static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) +{ + char bda_str[18]; + uint32_t cod = 0; + int32_t rssi = -129; /* invalid value */ + uint8_t *eir = NULL; + esp_bt_gap_dev_prop_t *p; + + ESP_LOGI(BT_AV_TAG, "Scanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); + for (int i = 0; i < param->disc_res.num_prop; i++) { + p = param->disc_res.prop + i; + switch (p->type) { + case ESP_BT_GAP_DEV_PROP_COD: + cod = *(uint32_t *)(p->val); + ESP_LOGI(BT_AV_TAG, "--Class of Device: 0x%x", cod); + break; + case ESP_BT_GAP_DEV_PROP_RSSI: + rssi = *(int8_t *)(p->val); + ESP_LOGI(BT_AV_TAG, "--RSSI: %d", rssi); + break; + case ESP_BT_GAP_DEV_PROP_EIR: + eir = (uint8_t *)(p->val); + break; + case ESP_BT_GAP_DEV_PROP_BDNAME: + default: + break; + } + } + + /* search for device with MAJOR service class as "rendering" in COD */ + if (!esp_bt_gap_is_valid_cod(cod) || + !(esp_bt_gap_get_cod_srvc(cod) & ESP_BT_COD_SRVC_RENDERING)) { + return; + } + + /* search for device named "ESP_SPEAKER" in its extended inqury response */ + if (eir) { + get_name_from_eir(eir, peer_bdname, NULL); + if (strcmp((char *)peer_bdname, "ESP_SPEAKER") != 0) { + return; + } + + ESP_LOGI(BT_AV_TAG, "Found a target device, address %s, name %s", bda_str, peer_bdname); + m_a2d_state = APP_AV_STATE_DISCOVERED; + memcpy(peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN); + ESP_LOGI(BT_AV_TAG, "Cancel device discovery ..."); + esp_bt_gap_cancel_discovery(); + } +} + + +void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + switch (event) { + case ESP_BT_GAP_DISC_RES_EVT: { + filter_inquiry_scan_result(param); + break; + } + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { + if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { + if (m_a2d_state == APP_AV_STATE_DISCOVERED) { + m_a2d_state = APP_AV_STATE_CONNECTING; + ESP_LOGI(BT_AV_TAG, "Device discovery stopped."); + ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %s", peer_bdname); + esp_a2d_source_connect(peer_bda); + } else { + // not discovered, continue to discover + ESP_LOGI(BT_AV_TAG, "Device discovery failed, continue to discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + } else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { + ESP_LOGI(BT_AV_TAG, "Discovery started."); + } + break; + } + case ESP_BT_GAP_RMT_SRVCS_EVT: + case ESP_BT_GAP_RMT_SRVC_REC_EVT: + default: { + ESP_LOGI(BT_AV_TAG, "event: %d", event); + break; + } + } + return; +} + +static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) +{ + ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); + switch (event) { + case BT_APP_EVT_STACK_UP: { + /* set up device name */ + char *dev_name = "ESP_A2DP_SRC"; + esp_bt_dev_set_device_name(dev_name); + + /* register GAP callback function */ + esp_bt_gap_register_callback(bt_app_gap_cb); + + /* initialize A2DP source */ + esp_a2d_register_callback(&bt_app_a2d_cb); + esp_a2d_source_register_data_callback(bt_app_a2d_data_cb); + esp_a2d_source_init(); + + /* set discoverable and connectable mode */ + esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); + + /* start device discovery */ + ESP_LOGI(BT_AV_TAG, "Starting device discovery..."); + m_a2d_state = APP_AV_STATE_DISCOVERING; + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + + /* create and start heart beat timer */ + do { + int tmr_id = 0; + tmr = xTimerCreate("connTmr", (10000 / portTICK_RATE_MS), + pdTRUE, (void *)tmr_id, a2d_app_heart_beat); + xTimerStart(tmr, portMAX_DELAY); + } while (0); + break; + } + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL); +} + +static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) +{ + if (len < 0 || data == NULL) { + return 0; + } + + // generate random sequence + int val = rand() % (1 << 16); + for (int i = 0; i < (len >> 1); i++) { + data[(i << 1)] = val & 0xff; + data[(i << 1) + 1] = (val >> 8) & 0xff; + } + + return len; +} + +static void a2d_app_heart_beat(void *arg) +{ + bt_app_work_dispatch(bt_app_av_sm_hdlr, BT_APP_HEART_BEAT_EVT, NULL, 0, NULL); +} + +static void bt_app_av_sm_hdlr(uint16_t event, void *param) +{ + ESP_LOGE(BT_AV_TAG, "%s state %d, evt 0x%x", __func__, m_a2d_state, event); + switch (m_a2d_state) { + case APP_AV_STATE_DISCOVERING: + case APP_AV_STATE_DISCOVERED: + break; + case APP_AV_STATE_UNCONNECTED: + bt_app_av_state_unconnected(event, param); + break; + case APP_AV_STATE_CONNECTING: + bt_app_av_state_connecting(event, param); + break; + case APP_AV_STATE_CONNECTED: + bt_app_av_state_connected(event, param); + break; + case APP_AV_STATE_DISCONNECTING: + bt_app_av_state_disconnecting(event, param); + break; + default: + ESP_LOGE(BT_AV_TAG, "%s invalid state %d", __func__, m_a2d_state); + break; + } +} + +static void bt_app_av_state_unconnected(uint16_t event, void *param) +{ + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: + case ESP_A2D_AUDIO_STATE_EVT: + case ESP_A2D_AUDIO_CFG_EVT: + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + break; + case BT_APP_HEART_BEAT_EVT: { + uint8_t *p = peer_bda; + ESP_LOGI(BT_AV_TAG, "a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + esp_a2d_source_connect(peer_bda); + m_a2d_state = APP_AV_STATE_CONNECTING; + m_connecting_intv = 0; + break; + } + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_av_state_connecting(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) { + ESP_LOGI(BT_AV_TAG, "a2dp connected"); + m_a2d_state = APP_AV_STATE_CONNECTED; + m_media_state = APP_AV_MEDIA_STATE_IDLE; + + } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + m_a2d_state = APP_AV_STATE_UNCONNECTED; + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: + case ESP_A2D_AUDIO_CFG_EVT: + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + break; + case BT_APP_HEART_BEAT_EVT: + if (++m_connecting_intv >= 2) { + m_a2d_state = APP_AV_STATE_UNCONNECTED; + m_connecting_intv = 0; + } + break; + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_av_media_proc(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (m_media_state) { + case APP_AV_MEDIA_STATE_IDLE: { + if (event == BT_APP_HEART_BEAT_EVT) { + ESP_LOGI(BT_AV_TAG, "a2dp media ready checking ..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); + } else if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { + ESP_LOGI(BT_AV_TAG, "a2dp media ready, starting ..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); + m_media_state = APP_AV_MEDIA_STATE_STARTING; + } + } + break; + } + case APP_AV_MEDIA_STATE_STARTING: { + if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { + ESP_LOGI(BT_AV_TAG, "a2dp media start successfully."); + m_intv_cnt = 0; + m_media_state = APP_AV_MEDIA_STATE_STARTED; + } else { + // not started succesfully, transfer to idle state + ESP_LOGI(BT_AV_TAG, "a2dp media start failed."); + m_media_state = APP_AV_MEDIA_STATE_IDLE; + } + } + break; + } + case APP_AV_MEDIA_STATE_STARTED: { + if (event == BT_APP_HEART_BEAT_EVT) { + if (++m_intv_cnt >= 10) { + ESP_LOGI(BT_AV_TAG, "a2dp media stopping..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); + m_media_state = APP_AV_MEDIA_STATE_STOPPING; + m_intv_cnt = 0; + } + } + break; + } + case APP_AV_MEDIA_STATE_STOPPING: { + if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { + ESP_LOGI(BT_AV_TAG, "a2dp media stopped successfully, disconnecting..."); + m_media_state = APP_AV_MEDIA_STATE_IDLE; + esp_a2d_source_disconnect(peer_bda); + m_a2d_state = APP_AV_STATE_DISCONNECTING; + } else { + ESP_LOGI(BT_AV_TAG, "a2dp media stopping..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); + } + } + break; + } + } +} + +static void bt_app_av_state_connected(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + ESP_LOGI(BT_AV_TAG, "a2dp disconnected"); + m_a2d_state = APP_AV_STATE_UNCONNECTED; + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { + m_pkt_cnt = 0; + } + break; + } + case ESP_A2D_AUDIO_CFG_EVT: + // not suppposed to occur for A2DP source + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + case BT_APP_HEART_BEAT_EVT: { + bt_app_av_media_proc(event, param); + break; + } + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_av_state_disconnecting(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + ESP_LOGI(BT_AV_TAG, "a2dp disconnected"); + m_a2d_state = APP_AV_STATE_UNCONNECTED; + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: + case ESP_A2D_AUDIO_CFG_EVT: + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + case BT_APP_HEART_BEAT_EVT: + break; + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} diff --git a/examples/bluetooth/a2dp_source/sdkconfig.defaults b/examples/bluetooth/a2dp_source/sdkconfig.defaults new file mode 100644 index 000000000..1ec00447a --- /dev/null +++ b/examples/bluetooth/a2dp_source/sdkconfig.defaults @@ -0,0 +1,12 @@ +# Override some defaults so BT stack is enabled and +# Classic BT is enabled +CONFIG_BT_ENABLED=y +CONFIG_BLUEDROID_ENABLED=y +CONFIG_CLASSIC_BT_ENABLED=y +CONFIG_A2DP_ENABLE=y +CONFIG_A2DP_SINK_ENABLE= +CONFIG_A2DP_SRC_ENABLE=y +CONFIG_BT_SPP_ENABLED= +CONFIG_GATTS_ENABLE= +CONFIG_GATTC_ENABLE= +CONFIG_BLE_SMP_ENABLE= diff --git a/examples/bluetooth/bt_discovery/sdkconfig.defaults b/examples/bluetooth/bt_discovery/sdkconfig.defaults index dd570f866..ab028190f 100644 --- a/examples/bluetooth/bt_discovery/sdkconfig.defaults +++ b/examples/bluetooth/bt_discovery/sdkconfig.defaults @@ -2,4 +2,7 @@ # Classic BT is enabled and BT_DRAM_RELEASE is disabled CONFIG_BT_ENABLED=y CONFIG_CLASSIC_BT_ENABLED=y - +CONFIG_A2DP_ENABLE= +CONFIG_GATTS_ENABLE= +CONFIG_GATTC_ENABLE= +CONFIG_BLE_SMP_ENABLE=