Merge branch 'feature/btdm_a2dp_src' into 'master'

Feature/btdm a2dp src

See merge request !1190
This commit is contained in:
Jiang Jiang Jian 2018-01-15 18:55:01 +08:00
commit cd54a95a7f
53 changed files with 4836 additions and 1520 deletions

View file

@ -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)"

View file

@ -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 */

View file

@ -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

View file

@ -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)

View file

@ -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 */

View file

@ -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);
}

View file

@ -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 */

View file

@ -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 <string.h>
#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, &param);
}
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 */

View file

@ -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 <string.h>
#include <stdint.h>
#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 */

File diff suppressed because it is too large Load diff

View file

@ -23,21 +23,21 @@
#include <string.h>
#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(&param, 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(&param, 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 */

File diff suppressed because it is too large Load diff

View file

@ -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);
/*******************************************************************************
**

View file

@ -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 <stdbool.h>
#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__ */

View file

@ -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 <stdbool.h>
#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__ */

View file

@ -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 <stdbool.h>
#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__ */

View file

@ -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 <stdbool.h>
#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__ */

View file

@ -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__ */

View file

@ -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"

View file

@ -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

View file

@ -27,6 +27,7 @@
#include "sbc_encoder.h"
#include "sbc_enc_func_declare.h"
/*#include <math.h>*/
#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 */

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -25,7 +25,7 @@
#include <string.h>
#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"

View file

@ -25,7 +25,7 @@
#include <string.h>
#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"

View file

@ -26,7 +26,7 @@
#include <string.h>
#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"

View file

@ -25,7 +25,7 @@
#include <string.h>
#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"

View file

@ -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"

View file

@ -26,7 +26,7 @@
#include <string.h>
#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"

View file

@ -26,7 +26,7 @@
#include <string.h>
#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"

View file

@ -25,7 +25,7 @@
#include <string.h>
#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"

View file

@ -29,7 +29,7 @@
#include <string.h>
#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"

View file

@ -26,7 +26,7 @@
#include <string.h>
#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"

View file

@ -26,7 +26,7 @@
#include <string.h>
#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"

View file

@ -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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 \

View file

@ -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:

View file

@ -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 */

View file

@ -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=

View file

@ -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

View file

@ -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.

View file

@ -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 <stdint.h>
#include <string.h>
#include <stdbool.h>
#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;
}
}

View file

@ -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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#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__ */

View file

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#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;
}
}

View file

@ -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=

View file

@ -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=