Merge branch 'feature/bt_support_external_codec' into 'master'

feat(bt/bluedroid): Support external codec in Bluedroid HFP and A2DP

Closes BT-3937

See merge request espressif/esp-idf!36607
This commit is contained in:
Jiang Jiang Jian
2025-04-01 10:34:23 +08:00
58 changed files with 3708 additions and 518 deletions

View File

@@ -288,7 +288,9 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source_ext_codec.c"
"host/bluedroid/btc/profile/std/a2dp/btc_av.c"
"host/bluedroid/btc/profile/std/avrc/btc_avrc.c"
"host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c"

View File

@@ -89,6 +89,15 @@ config BT_A2DP_ENABLE
help
Advanced Audio Distribution Profile
config BT_A2DP_USE_EXTERNAL_CODEC
bool "Use External Codec for A2DP"
depends on BT_A2DP_ENABLE
default n
help
If enable, user shall register audio codec capability to A2DP and encode/decode
audio data in application layer. The internal codec in A2DP will be remove in
the future, it is recommend to use external codec for new design.
config BT_AVRCP_ENABLED
bool
depends on BT_A2DP_ENABLE
@@ -184,6 +193,15 @@ choice BT_HFP_AUDIO_DATA_PATH
bool "HCI"
endchoice
config BT_HFP_USE_EXTERNAL_CODEC
bool "Use External Codec for HFP"
depends on BT_HFP_ENABLE && BT_HFP_AUDIO_DATA_PATH_HCI
default n
help
If enable, user shall encode/decode audio data in application layer. The internal
codec in HFP will be remove in the future, it is recommend to use external codec
for new design.
config BT_HFP_WBS_ENABLE
bool "Wide Band Speech"
depends on BT_HFP_ENABLE && BT_HFP_AUDIO_DATA_PATH_HCI

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,6 +14,37 @@
#if BTC_AV_INCLUDED
esp_a2d_audio_buff_t *esp_a2d_audio_buff_alloc(uint16_t size)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return NULL;
}
if (size == 0) {
return NULL;
}
uint8_t *p_buf = NULL, *p_data;
btc_av_audio_buff_alloc(size, &p_buf, &p_data);
if (p_buf == NULL) {
return NULL;
}
esp_a2d_audio_buff_t *audio_buf = (esp_a2d_audio_buff_t *)p_buf;
audio_buf->buff_size = size;
audio_buf->data_len = 0;
audio_buf->data = p_data;
return audio_buf;
}
void esp_a2d_audio_buff_free(esp_a2d_audio_buff_t *audio_buf)
{
if (audio_buf == NULL) {
return;
}
btc_av_audio_buff_free((uint8_t *)audio_buf);
}
#if BTC_AV_SINK_INCLUDED
esp_err_t esp_a2d_sink_init(void)
{
@@ -36,6 +67,35 @@ esp_err_t esp_a2d_sink_init(void)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
if (seid >= ESP_A2D_MAX_SEPS) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SINK_API_REG_SEP_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.reg_sep.seid = seid;
memcpy(&arg.reg_sep.mcc, mcc, sizeof(esp_a2d_mcc_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_deinit(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -81,6 +141,30 @@ esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_register_audio_data_callback(esp_a2d_sink_audio_data_cb_t callback)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_sink_ongoing_deinit) {
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_REG_AUDIO_DATA_CB_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.audio_data_cb = callback;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -251,6 +335,35 @@ esp_err_t esp_a2d_source_init(void)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
if (seid >= ESP_A2D_MAX_SEPS) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SRC_API_REG_SEP_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.reg_sep.seid = seid;
memcpy(&arg.reg_sep.mcc, mcc, sizeof(esp_a2d_mcc_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, 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) {
@@ -348,6 +461,26 @@ esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callbac
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED || !btc_av_is_started()) {
return ESP_ERR_INVALID_STATE;
}
if (audio_buf == NULL || audio_buf->data_len == 0 || conn_hdl == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!btc_a2d_src_audio_mtu_check(audio_buf->data_len)) {
return ESP_ERR_INVALID_SIZE;
}
if (btc_a2d_src_audio_data_send(conn_hdl, audio_buf) != BT_STATUS_SUCCESS) {
return ESP_FAIL;
}
return ESP_OK;
}
#endif /* BTC_AV_SRC_INCLUDED */
#endif /* #if BTC_AV_INCLUDED */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -528,6 +528,8 @@ esp_err_t esp_hf_ag_out_call(esp_bd_addr_t remote_addr, int num_active, int num_
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
#if (BTM_SCO_HCI_INCLUDED == TRUE)
esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -548,7 +550,73 @@ esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_h
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
#if (BTM_SCO_HCI_INCLUDED == TRUE)
esp_err_t esp_hf_ag_register_audio_data_callback(esp_hf_ag_audio_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_HF;
msg.act = BTC_HF_REGISTER_AUDIO_DATA_CALLBACK_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.reg_audio_data_cb.callback = callback;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL, NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_hf_audio_buff_t *esp_hf_ag_audio_buff_alloc(uint16_t size)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return NULL;
}
if (size == 0) {
return NULL;
}
uint8_t *p_buf = NULL, *p_data;
BTA_AgAudioBuffAlloc(size, &p_buf, &p_data);
if (p_buf == NULL) {
return NULL;
}
esp_hf_audio_buff_t *audio_buf = (esp_hf_audio_buff_t *)p_buf;
audio_buf->buff_size = size;
audio_buf->data_len = 0;
audio_buf->data = p_data;
return audio_buf;
}
void esp_hf_ag_audio_buff_free(esp_hf_audio_buff_t *audio_buf)
{
if (audio_buf == NULL) {
return;
}
BTA_AgAudioBuffFree((UINT8 *)audio_buf);
}
esp_err_t esp_hf_ag_audio_data_send(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (audio_buf == NULL || audio_buf->data_len == 0) {
return ESP_ERR_INVALID_ARG;
}
if (btc_hf_ag_audio_data_send(sync_conn_hdl, (uint8_t *)audio_buf, audio_buf->data, audio_buf->data_len)) {
return ESP_OK;
}
return ESP_FAIL;
}
esp_err_t esp_hf_ag_pkt_stat_nums_get(uint16_t sync_conn_handle)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -541,6 +541,71 @@ void esp_hf_client_outgoing_data_ready(void)
BTA_HfClientCiData();
}
esp_err_t esp_hf_client_register_audio_data_callback(esp_hf_client_audio_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_HF_CLIENT;
msg.act = BTC_HF_CLIENT_REGISTER_AUDIO_DATA_CALLBACK_EVT;
btc_hf_client_args_t arg;
memset(&arg, 0, sizeof(btc_hf_client_args_t));
arg.reg_audio_data_cb.callback = callback;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_client_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_hf_audio_buff_t *esp_hf_client_audio_buff_alloc(uint16_t size)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return NULL;
}
if (size == 0) {
return NULL;
}
uint8_t *p_buf = NULL, *p_data;
BTA_HfClientAudioBuffAlloc(size, &p_buf, &p_data);
if (p_buf == NULL) {
return NULL;
}
esp_hf_audio_buff_t *audio_buf = (esp_hf_audio_buff_t *)p_buf;
audio_buf->buff_size = size;
audio_buf->data_len = 0;
audio_buf->data = p_data;
return audio_buf;
}
void esp_hf_client_audio_buff_free(esp_hf_audio_buff_t *audio_buf)
{
if (audio_buf == NULL) {
return;
}
BTA_HfClientAudioBuffFree((UINT8 *)audio_buf);
}
esp_err_t esp_hf_client_audio_data_send(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (audio_buf == NULL || audio_buf->data_len == 0) {
return ESP_ERR_INVALID_ARG;
}
BTA_HfClientAudioDataSend(sync_conn_hdl, (uint8_t *)audio_buf, audio_buf->data, audio_buf->data_len);
return ESP_OK;
}
void esp_hf_client_pcm_resample_init(uint32_t src_sps, uint32_t bits, uint32_t channels)
{
BTA_DmPcmInitSamples(src_sps, bits, channels);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,11 +9,16 @@
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_a2dp_legacy_api.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_A2D_MAX_SEPS 1 /*!< Maximum number of Stream Endpoint that supported */
typedef uint16_t esp_a2d_conn_hdl_t; /*!< Connection handle, associate with specific device that connected */
/**
* @brief Media codec types supported by A2DP.
*/
@@ -30,6 +35,102 @@ typedef uint8_t esp_a2d_mct_t;
#define ESP_A2D_PSC_DELAY_RPT (1<<0) /*!< Delay Report */
typedef uint16_t esp_a2d_psc_t;
/**
* @brief A2DP SBC sampling frequency bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_SF_16K (0x8) /*!< SBC sampling frequency 16kHz */
#define ESP_A2D_SBC_CIE_SF_32K (0x4) /*!< SBC sampling frequency 32kHz */
#define ESP_A2D_SBC_CIE_SF_44K (0x2) /*!< SBC sampling frequency 44.1kHz */
#define ESP_A2D_SBC_CIE_SF_48K (0x1) /*!< SBC sampling frequency 48kHz */
/**
* @brief A2DP SBC channel mode bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_CH_MODE_MONO (0x8) /*!< SBC channel mode Mono */
#define ESP_A2D_SBC_CIE_CH_MODE_DUAL_CHANNEL (0x4) /*!< SBC channel mode Dual Channel */
#define ESP_A2D_SBC_CIE_CH_MODE_STEREO (0x2) /*!< SBC channel mode Stereo */
#define ESP_A2D_SBC_CIE_CH_MODE_JOINT_STEREO (0x1) /*!< SBC channel mode Stereo */
/**
* @brief A2DP SBC block length bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_BLOCK_LEN_4 (0x8) /*!< SBC block length 4 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_8 (0x4) /*!< SBC block length 8 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_12 (0x2) /*!< SBC block length 12 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_16 (0x1) /*!< SBC block length 16 */
/**
* @brief A2DP SBC number of subbands bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 (0x2) /*!< SBC number of subbands 4 */
#define ESP_A2D_SBC_CIE_NUM_SUBBANDS_8 (0x1) /*!< SBC number of subbands 8 */
/**
* @brief A2DP SBC allocation method bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_ALLOC_MTHD_SRN (0x2) /*!< SBC allocation method SNR */
#define ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS (0x1) /*!< SBC allocation method Loudness */
/**
* @brief A2DP SBC media codec capabilities information struct
*/
typedef struct {
uint8_t ch_mode : 4; /*!< Channel mode */
uint8_t samp_freq : 4; /*!< Sampling frequency */
uint8_t alloc_mthd : 2; /*!< Allocation method */
uint8_t num_subbands : 2; /*!< Number of subbands */
uint8_t block_len : 4; /*!< Block length */
uint8_t min_bitpool; /*!< Minimum bitpool */
uint8_t max_bitpool; /*!< Maximum bitpool */
} __attribute__((packed)) esp_a2d_cie_sbc_t;
/**
* @brief A2DP MPEG-1, 2 media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t ch_mode : 4; /*!< Channel mode */
uint8_t crc : 1; /*!< CRC protection */
uint8_t layer : 3; /*!< Layers of MPEG-1,2 Audio */
uint8_t samp_freq : 6; /*!< Sampling frequency */
uint8_t mpf : 1; /*!< Media payload format */
uint8_t rfu : 1; /*!< Reserved */
uint8_t bri1 : 7; /*!< Bit rate index part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t bri2; /*!< Bit rate index part 2 */
} __attribute__((packed)) esp_a2d_cie_m12_t;
/**
* @brief A2DP MPEG-2, 4 media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t drc : 1; /*!< Support of MPEG-D DRC */
uint8_t obj_type : 7; /*!< Object type */
uint8_t samp_freq1 : 8; /*!< Sampling frequency part 1 */
uint8_t ch : 4; /*!< Channels */
uint8_t samp_freq2 : 4; /*!< Sampling frequency part 2 */
uint8_t br1 : 7; /*!< Bit rate part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t br2; /*!< Bit rate part 2 */
uint8_t br3; /*!< Bit rate part 3 */
} __attribute__((packed)) esp_a2d_cie_m24_t;
/**
* @brief A2DP ATRAC media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t rfu1 : 2; /*!< Reserved */
uint8_t ch_mode : 3; /*!< Channel mode */
uint8_t version : 3; /*!< Version */
uint8_t bri1 : 3; /*!< Bit rate index part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t samp_freq : 2; /*!< Sampling frequency */
uint8_t rfu2 : 2; /*!< Reserved */
uint8_t bri2; /*!< Bit rate index part 2 */
uint8_t bri3; /*!< Bit rate index part 3 */
uint16_t max_sul; /*!< Maximum SUL */
uint8_t rfu3; /*!< Reserved */
} __attribute__((packed)) esp_a2d_cie_atrac_t;
/**
* @brief A2DP media codec capabilities union
*/
@@ -40,10 +141,15 @@ typedef struct {
#define ESP_A2D_CIE_LEN_M24 (6)
#define ESP_A2D_CIE_LEN_ATRAC (7)
union {
uint8_t sbc[ESP_A2D_CIE_LEN_SBC]; /*!< SBC codec capabilities */
uint8_t m12[ESP_A2D_CIE_LEN_M12]; /*!< MPEG-1,2 audio codec capabilities */
uint8_t m24[ESP_A2D_CIE_LEN_M24]; /*!< MPEG-2, 4 AAC audio codec capabilities */
uint8_t atrac[ESP_A2D_CIE_LEN_ATRAC]; /*!< ATRAC family codec capabilities */
uint8_t sbc[ESP_A2D_CIE_LEN_SBC] __attribute__((deprecated)); /*!< SBC codec capabilities, deprecated, use sbc_info instead */
uint8_t m12[ESP_A2D_CIE_LEN_M12] __attribute__((deprecated)); /*!< MPEG-1,2 audio codec capabilities, deprecated, use m12_info instead */
uint8_t m24[ESP_A2D_CIE_LEN_M24] __attribute__((deprecated)); /*!< MPEG-2, 4 AAC audio codec capabilities, deprecated, use m24_info instead */
uint8_t atrac[ESP_A2D_CIE_LEN_ATRAC] __attribute__((deprecated)); /*!< ATRAC family codec capabilities, deprecated, use atrac_info instead */
esp_a2d_cie_sbc_t sbc_info; /*!< SBC codec capabilities */
esp_a2d_cie_m12_t m12_info; /*!< MPEG-1,2 audio codec capabilities */
esp_a2d_cie_m24_t m24_info; /*!< MPEG-2, 4 AAC audio codec capabilities */
esp_a2d_cie_atrac_t atrac_info; /*!< ATRAC family codec capabilities */
} cie; /*!< A2DP codec information element */
} __attribute__((packed)) esp_a2d_mcc_t;
@@ -103,6 +209,16 @@ typedef enum {
ESP_A2D_INIT_SUCCESS /*!< A2DP profile deinit successful event */
} esp_a2d_init_state_t;
/**
* @brief Bluetooth A2DP SEP register states
*/
typedef enum {
ESP_A2D_SEP_REG_SUCCESS = 0, /*!< A2DP stream endpoint register success */
ESP_A2D_SEP_REG_FAIL, /*!< A2DP stream endpoint register generic fail */
ESP_A2D_SEP_REG_UNSUPPORTED, /*!< A2DP stream endpoint register fail, unsupported codec type or param */
ESP_A2D_SEP_REG_INVALID_STATE, /*!< A2DP stream endpoint register fail, invalid state */
} esp_a2d_sep_reg_state_t;
/**
* @brief Bluetooth A2DP set delay report value states
*/
@@ -117,15 +233,27 @@ typedef enum {
typedef enum {
ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
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_AUDIO_CFG_EVT, /*!< audio codec is configured */
ESP_A2D_MEDIA_CTRL_ACK_EVT, /*!< acknowledge event in response to media control commands */
ESP_A2D_PROF_STATE_EVT, /*!< indicate a2dp init&deinit complete */
ESP_A2D_SEP_REG_STATE_EVT, /*!< indicate a2dp steam endpoint register status */
ESP_A2D_SNK_PSC_CFG_EVT, /*!< protocol service capabilities configuredonly used for A2DP SINK */
ESP_A2D_SNK_SET_DELAY_VALUE_EVT, /*!< indicate a2dp sink set delay report value complete, only used for A2DP SINK */
ESP_A2D_SNK_GET_DELAY_VALUE_EVT, /*!< indicate a2dp sink get delay report value complete, only used for A2DP SINK */
ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, /*!< report delay value, only used for A2DP SRC */
} esp_a2d_cb_event_t;
/**
* @brief A2DP audio buffer
*/
typedef struct {
uint16_t buff_size; /*!< audio buff size */
uint16_t number_frame; /*!< number of encoded frame in this buff */
uint32_t timestamp; /*!< timestamp of the first frame */
uint16_t data_len; /*!< audio data len */
uint8_t *data; /*!< pointer to audio data start */
} esp_a2d_audio_buff_t; /*!< struct to store audio data */
/**
* @brief A2DP state callback parameters
*/
@@ -136,6 +264,8 @@ typedef union {
struct a2d_conn_stat_param {
esp_a2d_connection_state_t state; /*!< one of values from esp_a2d_connection_state_t */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
uint16_t audio_mtu; /*!< MTU of audio connection */
esp_a2d_disc_rsn_t disc_rsn; /*!< reason of disconnection for "DISCONNECTED" */
} conn_stat; /*!< A2DP connection status */
@@ -144,7 +274,8 @@ typedef union {
*/
struct a2d_audio_stat_param {
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 */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address*/
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
} audio_stat; /*!< audio stream playing state */
/**
@@ -152,6 +283,7 @@ typedef union {
*/
struct a2d_audio_cfg_param {
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */
} audio_cfg; /*!< media codec configuration information */
@@ -170,6 +302,14 @@ typedef union {
esp_a2d_init_state_t init_state; /*!< a2dp profile state param */
} a2d_prof_stat; /*!< status to indicate a2d prof init or deinit */
/**
* @brief ESP_A2D_SEP_REG_STATE_EVT
*/
struct a2d_sep_reg_stat_param {
uint8_t seid; /*!< the stream endpoint to register */
esp_a2d_sep_reg_state_t reg_state; /*!< stream endpoint register state */
} a2d_sep_reg_stat; /*!< status to indicate a2d sep register success or not */
/**
* @brief ESP_A2D_SNK_PSC_CFG_EVT
*/
@@ -213,25 +353,31 @@ typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *para
/**
* @brief A2DP sink data callback function
*
* @param[in] buf : pointer to the data received from A2DP source device and is PCM format decoded from SBC decoder;
* buf references to a static memory block and can be overwritten by upcoming data
* @param[in] conn_hdl: connection handle
*
* @param[in] len : size(in bytes) in buf
* @param[in] audio_buf: pointer to the data received from A2DP source device, should be freed by
* calling esp_a2d_audio_buff_free
*/
typedef void (* esp_a2d_sink_data_cb_t)(const uint8_t *buf, uint32_t len);
typedef void (* esp_a2d_sink_audio_data_cb_t)(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
* @brief A2DP source data read callback function
* @brief Allocate a audio buffer to store and send audio data, can be used in both sink and source.
*
* @param[in] buf : buffer to be filled with PCM data stream from higher layer
* @param[in] size: buffer size to allocate
*
* @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 argument len is -1, this value is ignored.
* @return allocated audio buffer, if Bluedroid is not enabled, no memory, or size is
* zeros, will return NULL
*
*/
typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
esp_a2d_audio_buff_t *esp_a2d_audio_buff_alloc(uint16_t size);
/**
* @brief Free a audio buffer allocated by esp_a2d_audio_buff_alloc.
*
* @param[in] audio_buf: audio buffer to free
*
*/
void esp_a2d_audio_buff_free(esp_a2d_audio_buff_t *audio_buf);
/**
* @brief Register application callback function to A2DP module. This function should be called
@@ -248,23 +394,20 @@ typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
*/
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, used only by A2DP sink. The callback is invoked in the context
* of A2DP sink task whose stack size is configurable through menuconfig.
* @brief Register A2DP sink audio data output function, the output format is undecoded audio data
* frame in esp_a2d_audio_buff_t, user shall call esp_a2d_audio_buff_free to free the buff
* when the data is consumed.
*
* @param[in] callback: A2DP sink data callback function
* @param[in] callback: A2DP sink audio data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
esp_err_t esp_a2d_sink_register_audio_data_callback(esp_a2d_sink_audio_data_cb_t callback);
/**
*
@@ -282,6 +425,23 @@ esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
*/
esp_err_t esp_a2d_sink_init(void);
/**
* @brief Register a a2dp sink Stream Endpoint (SEP) with specific codec capability, shall register
* SEP after a2dp sink initializing and before a2dp connection establishing. Register the same
* SEP index repeatedly will overwrite the old one.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently only supports SBC
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_sink_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc);
/**
*
@@ -297,7 +457,6 @@ esp_err_t esp_a2d_sink_init(void);
*/
esp_err_t esp_a2d_sink_deinit(void);
/**
*
* @brief Connect to remote bluetooth A2DP source device. This API must be called after
@@ -313,7 +472,6 @@ esp_err_t esp_a2d_sink_deinit(void);
*/
esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda);
/**
*
* @brief Disconnect from the remote A2DP source device. This API must be called after
@@ -359,7 +517,6 @@ esp_err_t esp_a2d_sink_set_delay_value(uint16_t delay_value);
*/
esp_err_t esp_a2d_sink_get_delay_value(void);
/**
*
* @brief Media control commands. This API can be used for both A2DP sink and source
@@ -375,7 +532,6 @@ esp_err_t esp_a2d_sink_get_delay_value(void);
*/
esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl);
/**
*
* @brief Initialize the bluetooth A2DP source module. A2DP can work independently.
@@ -392,6 +548,23 @@ esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl);
*/
esp_err_t esp_a2d_source_init(void);
/**
* @brief Register a a2dp source Stream Endpoint (SEP) with specific codec capability, shall register
* SEP after a2dp source initializing and before a2dp connection establishing. Register the same
* SEP index repeatedly will overwrite the old one.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently, only SBC supported
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc);
/**
*
@@ -407,23 +580,24 @@ esp_err_t esp_a2d_source_init(void);
*/
esp_err_t esp_a2d_source_deinit(void);
/**
* @brief Register A2DP source data input function. For now, the input shoule be PCM data stream.
* This function should be called only after esp_bluedroid_enable() completes
* successfully. The callback is invoked in the context of A2DP source task whose
* stack size is configurable through menuconfig.
* @brief Send a audio buff with encoded audio data to sink, the audio data len shall not bigger than
* audio connection mtu (retrieved from ESP_A2D_CONNECTION_STATE_EVT). if the return value is
* ESP_OK, then the audio buff is consumed, otherwise, audio buff can be reused by user.
*
* @param[in] callback: A2DP source data callback function
* @param[in] conn_hdl: connection handle
*
* @param[in] audio_buf: encoded audio data
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_ERR_INVALID_SIZE: data len bigger than mtu
* - ESP_FAIL: buffer queue is full, try again later
*
*/
esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback);
esp_err_t esp_a2d_source_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
*
@@ -440,7 +614,6 @@ esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callbac
*/
esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda);
/**
*
* @brief Disconnect from the remote A2DP sink device. This API must be called

View File

@@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Some legacy APIs of A2DP, will be removed in the future
*/
#pragma once
#include "esp_err.h"
#include "esp_bt_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief [Deprecated] A2DP sink data callback function
*
* @param[in] buf : pointer to the data received from A2DP source device and is PCM format decoded 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 [Deprecated] A2DP source data read callback function
*
* @param[in] buf : buffer to be filled with PCM data stream from higher 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 argument len is -1, this value is ignored.
*
*/
typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
/**
* @brief [Deprecated] 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, used only by A2DP sink. The callback is invoked in the context
* of A2DP sink task whose stack size is configurable through menuconfig.
*
* @param[in] callback: A2DP sink data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
/**
* @brief [Deprecated] Register A2DP source data input function. For now, the input should be PCM data stream.
* This function should be called only after esp_bluedroid_enable() completes
* successfully. The callback is invoked in the context of A2DP source task whose
* stack size is configurable through menuconfig.
*
* @param[in] callback: A2DP source data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_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);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,7 @@
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
#include "esp_hf_ag_legacy_api.h"
#ifdef __cplusplus
extern "C" {
@@ -93,7 +94,8 @@ typedef union
struct hf_audio_stat_param {
esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */
esp_hf_audio_state_t state; /*!< Audio connection state */
uint16_t sync_conn_handle; /*!< (e)SCO connection handle */
esp_hf_sync_conn_hdl_t sync_conn_handle; /*!< (e)SCO connection handle */
uint16_t preferred_frame_size; /*!< Valid only when Voice Over HCI is enabled, recommended frame size to send */
} audio_stat; /*!< AG callback param of ESP_HF_AUDIO_STATE_EVT */
/**
@@ -233,35 +235,6 @@ typedef union
} esp_hf_cb_param_t; /*!< HFP AG callback param compound*/
/**
* @brief AG incoming data callback function, the callback is useful in case of
* Voice Over HCI.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
*
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_hf_incoming_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief AG outgoing data callback function, the callback is useful in case of
* Voice Over HCI. Once audio connection is set up and the application layer has
* prepared data to send, the lower layer will call this function to read data
* and then send. This callback is supposed to be implemented as non-blocking,
* and if data is not enough, return value 0 is supposed.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
*
* @param[in] len : size(in bytes) in buf
*
* @return length of data successfully read
*/
typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len);
/**
* @brief HF AG callback function type
*
@@ -271,6 +244,20 @@ typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len);
*/
typedef void (* esp_hf_cb_t) (esp_hf_cb_event_t event, esp_hf_cb_param_t *param);
/**
* @brief HFP AG incoming audio data callback function, user should copy audio_buf struct
* to other place before return. This callback is used in case of Voice Over HCI.
*
* @param[in] sync_conn_hdl: (e)SCO connection handle
*
* @param[in] audio_buf: pointer to incoming data(payload of HCI synchronous data packet), user
* should free audio buffer by calling esp_hf_ag_audio_buff_free
*
* @param[in] is_bad_frame: whether this packet is marked as bad frame by baseband
*
*/
typedef void (* esp_hf_ag_audio_data_cb_t)(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf, bool is_bad_frame);
/************************************************************************************
** ESP HF API
************************************************************************************/
@@ -678,19 +665,58 @@ esp_err_t esp_hf_ag_end_call(esp_bd_addr_t remote_addr, int num_active, int num_
char *number, esp_hf_call_addr_type_t call_addr_type);
/**
* @brief Register AG data output function.
* The callback is only used in the case that Voice Over HCI is enabled.
* @brief Register HFP AG audio data output function; the callback is only used in
* the case that Voice Over HCI is enabled.
*
* @param[in] recv: HFP client incoming data callback function
* @param[in] send: HFP client outgoing data callback function
* @param[in] callback: HFP AG incoming audio data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
* - ESP_FAIL: others
*
*/
esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send);
esp_err_t esp_hf_ag_register_audio_data_callback(esp_hf_ag_audio_data_cb_t callback);
/**
* @brief Allocate a audio buffer to store and send audio data. This function is only
* used in the case that Voice Over HCI is enabled.
*
* @param[in] size: buffer size to allocate
*
* @return allocated audio buffer, if Bluedroid is not enabled, no memory, or size is
* zeros, will return NULL
*
*/
esp_hf_audio_buff_t *esp_hf_ag_audio_buff_alloc(uint16_t size);
/**
* @brief Free a audio buffer allocated by esp_hf_ag_audio_buff_alloc. This function
* is only used in the case that Voice Over HCI is enabled.
*
* @param[in] audio_buf: audio buffer to free
*
*/
void esp_hf_ag_audio_buff_free(esp_hf_audio_buff_t *audio_buf);
/**
* @brief Send audio data, the audio buffer should by allocated by esp_hf_ag_audio_buff_alloc.
* If the length of the audio data is equal to preferred_frame_size indicated by
* ESP_HF_AUDIO_STATE_EVT, then we can reduce one memory copy inside the Bluedroid stack.
* This function is only used in the case that Voice Over HCI is enabled.
*
* @param[in] sync_conn_hdl: (e)SCO connection handle
*
* @param[in] audio_buf: audio buffer that audio data stored
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_hf_ag_audio_data_send(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf);
/**
*
@@ -709,16 +735,6 @@ esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_h
*/
esp_err_t esp_hf_ag_pkt_stat_nums_get(uint16_t sync_conn_handle);
/**
* @brief Trigger the lower-layer to fetch and send audio data.
*
* This function is only used in the case that Voice Over HCI is enabled.
* As a precondition to use this API, Service Level Connection shall exist with HFP client.
* After this function is called, lower layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data
*
*/
void esp_hf_ag_outgoing_data_ready(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Some legacy APIs of HFP AG, will be removed in the future
*/
#pragma once
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief AG incoming data callback function, the callback is useful in case of
* Voice Over HCI.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
*
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_hf_incoming_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief AG outgoing data callback function, the callback is useful in case of
* Voice Over HCI. Once audio connection is set up and the application layer has
* prepared data to send, the lower layer will call this function to read data
* and then send. This callback is supposed to be implemented as non-blocking,
* and if data is not enough, return value 0 is supposed.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
*
* @param[in] len : size(in bytes) in buf
*
* @return length of data successfully read
*/
typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len);
/**
* @brief Register AG data output function.
* The callback is only used in the case that Voice Over HCI is enabled.
*
* @param[in] recv: HFP client incoming data callback function
* @param[in] send: HFP client outgoing data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_hf_ag_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send);
/**
* @brief Trigger the lower-layer to fetch and send audio data.
*
* This function is only used in the case that Voice Over HCI is enabled.
* As a precondition to use this API, Service Level Connection shall exist with HFP client.
* After this function is called, lower layer will invoke esp_hf_ag_outgoing_data_cb_t to fetch data
*
*/
void esp_hf_ag_outgoing_data_ready(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,7 @@
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
#include "esp_hf_client_legacy_api.h"
#ifdef __cplusplus
extern "C" {
@@ -118,7 +119,8 @@ typedef union {
struct hf_client_audio_stat_param {
esp_hf_client_audio_state_t state; /*!< audio connection state */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
uint16_t sync_conn_handle; /*!< (e)SCO connection handle */
esp_hf_sync_conn_hdl_t sync_conn_handle; /*!< (e)SCO connection handle */
uint16_t preferred_frame_size; /*!< valid only when Voice Over HCI is enabled, recommended frame size to send */
} audio_stat; /*!< HF callback param of ESP_HF_CLIENT_AUDIO_STATE_EVT */
/**
@@ -277,32 +279,18 @@ typedef union {
} esp_hf_client_cb_param_t; /*!< HFP client callback parameters */
/**
* @brief HFP client incoming data callback function, the callback is useful in case of
* Voice Over HCI.
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_hf_client_incoming_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief HFP client outgoing data callback function, the callback is useful in case of
* Voice Over HCI. Once audio connection is set up and the application layer has
* prepared data to send, the lower layer will call this function to read data
* and then send. This callback is supposed to be implemented as non-blocking,
* and if data is not enough, return value 0 is supposed.
* @brief HFP client incoming audio data callback function, user should copy audio_buf struct
* to other place before return. This callback is used in case of Voice Over HCI.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
* @param[in] sync_conn_hdl: (e)SCO connection handle
*
* @param[in] len : size(in bytes) in buf
* @param[in] audio_buf: pointer to incoming data(payload of HCI synchronous data packet), user
* should free audio buffer by calling esp_hf_client_audio_buff_free
*
* @return length of data successfully read
* @param[in] is_bad_frame: whether this packet is marked as bad frame by baseband
*
*/
typedef uint32_t (* esp_hf_client_outgoing_data_cb_t)(uint8_t *buf, uint32_t len);
typedef void (* esp_hf_client_audio_data_cb_t)(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf, bool is_bad_frame);
/**
* @brief HFP client callback function type
@@ -662,24 +650,6 @@ esp_err_t esp_hf_client_request_last_voice_tag_number(void);
*/
esp_err_t esp_hf_client_send_nrec(void);
/**
* @brief Register HFP client data output function; the callback is only used in
* the case that Voice Over HCI is enabled.
*
* @param[in] recv: HFP client incoming data callback function
*
* @param[in] send: HFP client outgoing data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_hf_client_register_data_callback(esp_hf_client_incoming_data_cb_t recv,
esp_hf_client_outgoing_data_cb_t send);
/**
*
* @brief Get the number of packets received and sent
@@ -697,15 +667,57 @@ esp_err_t esp_hf_client_register_data_callback(esp_hf_client_incoming_data_cb_t
esp_err_t esp_hf_client_pkt_stat_nums_get(uint16_t sync_conn_handle);
/**
* @brief Trigger the lower-layer to fetch and send audio data.
* This function is only only used in the case that Voice Over HCI is enabled. After this
* function is called, lower layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data.
* @brief Register HFP client audio data output function; the callback is only used in
* the case that Voice Over HCI is enabled.
*
* As a precondition to use this API, Service Level Connection shall exist with AG.
* @param[in] callback: HFP client incoming audio data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
void esp_hf_client_outgoing_data_ready(void);
esp_err_t esp_hf_client_register_audio_data_callback(esp_hf_client_audio_data_cb_t callback);
/**
* @brief Allocate a audio buffer to store and send audio data. This function is only
* used in the case that Voice Over HCI is enabled.
*
* @param[in] size: buffer size to allocate
*
* @return allocated audio buffer, if Bluedroid is not enabled, no memory, or size is
* zeros, will return NULL
*
*/
esp_hf_audio_buff_t *esp_hf_client_audio_buff_alloc(uint16_t size);
/**
* @brief Free a audio buffer allocated by esp_hf_client_audio_buff_alloc. This function
* is only used in the case that Voice Over HCI is enabled.
*
* @param[in] audio_buf: audio buffer to free
*
*/
void esp_hf_client_audio_buff_free(esp_hf_audio_buff_t *audio_buf);
/**
* @brief Send audio data, the audio buffer should by allocated by esp_hf_client_audio_buff_alloc.
* If the length of the audio data is equal to preferred_frame_size indicated by
* ESP_HF_CLIENT_AUDIO_STATE_EVT, then we can reduce one memory copy inside the Bluedroid stack.
* This function is only used in the case that Voice Over HCI is enabled.
*
* @param[in] sync_conn_hdl: (e)SCO connection handle
*
* @param[in] audio_buf: audio buffer that audio data stored
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
*
*/
esp_err_t esp_hf_client_audio_data_send(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf);
/**
* @brief Initialize the down sampling converter. This is a utility function that can

View File

@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Some legacy APIs of HFP HF, will be removed in the future
*/
#pragma once
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief HFP client incoming data callback function, the callback is useful in case of
* Voice Over HCI.
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_hf_client_incoming_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief HFP client outgoing data callback function, the callback is useful in case of
* Voice Over HCI. Once audio connection is set up and the application layer has
* prepared data to send, the lower layer will call this function to read data
* and then send. This callback is supposed to be implemented as non-blocking,
* and if data is not enough, return value 0 is supposed.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
*
* @param[in] len : size(in bytes) in buf
*
* @return length of data successfully read
*
*/
typedef uint32_t (* esp_hf_client_outgoing_data_cb_t)(uint8_t *buf, uint32_t len);
/**
* @brief Register HFP client data output function; the callback is only used in
* the case that Voice Over HCI is enabled.
*
* @param[in] recv: HFP client incoming data callback function
*
* @param[in] send: HFP client outgoing data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_hf_client_register_data_callback(esp_hf_client_incoming_data_cb_t recv,
esp_hf_client_outgoing_data_cb_t send);
/**
* @brief Trigger the lower-layer to fetch and send audio data.
* This function is only only used in the case that Voice Over HCI is enabled. After this
* function is called, lower layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data.
*
* As a precondition to use this API, Service Level Connection shall exist with AG.
*
*/
void esp_hf_client_outgoing_data_ready(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -13,6 +13,8 @@
extern "C" {
#endif
typedef uint16_t esp_hf_sync_conn_hdl_t;
/// profile states
typedef enum {
ESP_HF_INIT_SUCCESS = 0, /*!< Indicate init successful */
@@ -249,6 +251,25 @@ typedef enum {
ESP_HF_CME_NETWORK_NOT_ALLOWED = 32, /*!< network not allowed --emergency calls only */
} esp_hf_cme_err_t;
/* Since HFP uses a fixed set of mSBC codec parameters, define it here */
#define ESP_HF_MSBC_CHANNEL_MODE "Mono" /*!< mSBC channel mode */
#define ESP_HF_MSBC_SAMPLING_RATE "16 kHz" /*!< mSBC sampling rate */
#define ESP_HF_MSBC_ALLOCATION_METHOD "Loudness" /*!< mSBC allocation method */
#define ESP_HF_MSBC_SUBBANDS 8 /*!< mSBC subbands */
#define ESP_HF_MSBC_BLOCK_LENGTH 15 /*!< mSBC block length */
#define ESP_HF_MSBC_BITPOOL 26 /*!< mSBC bitpool */
/* frame size after mSBC encoded */
#define ESP_HF_MSBC_ENCODED_FRAME_SIZE 57 /*!< mSBC frame size */
/**
* @brief HFP audio buffer
*/
typedef struct {
uint16_t buff_size; /*!< buffer size */
uint16_t data_len; /*!< audio data length, data length should not greater than buffer size */
uint8_t *data; /*!< pointer to audio data start */
} esp_hf_audio_buff_t; /*!< struct to store audio data */
#ifdef __cplusplus
}
#endif

View File

@@ -57,7 +57,7 @@
#define BTA_AV_CLOSE_REQ_TIME_VAL 4000
#endif
/* number to retry on reconfigure failure - some headsets requirs this number to be more than 1 */
/* number to retry on reconfigure failure - some headsets requires this number to be more than 1 */
#ifndef BTA_AV_RECONFIG_RETRY
#define BTA_AV_RECONFIG_RETRY 6
#endif
@@ -239,7 +239,7 @@ static UINT8 bta_av_get_scb_handle(tBTA_AV_SCB *p_scb, UINT8 local_sep)
return (p_scb->seps[xx].av_handle);
}
}
APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropiate sep_type not found")
APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropriate sep_type not found")
return 0; /* return invalid handle */
}
@@ -260,7 +260,7 @@ static UINT8 bta_av_get_scb_sep_type(tBTA_AV_SCB *p_scb, UINT8 tavdt_handle)
return (p_scb->seps[xx].tsep);
}
}
APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropiate handle not found")
APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropriate handle not found")
return 3; /* return invalid sep type */
}
@@ -586,9 +586,18 @@ void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UI
osi_free(p_pkt);
return;
}
if (p_pkt->offset >= 4) {
/* The offset of avdt packet will always greater than 4, it is safe to add timestamp here */
*((UINT32 *) (p_pkt + 1)) = time_stamp;
}
else {
APPL_TRACE_WARNING("bta_av_stream_data_cback avdt packet offset small than 4");
}
p_pkt->event = BTA_AV_MEDIA_DATA_EVT;
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_DATA_EVT, (tBTA_AV_MEDIA *)p_pkt);
osi_free(p_pkt); /* a copy of packet had been delivered, we free this buffer */
/* packet will be free by upper */
}
/*******************************************************************************
@@ -843,7 +852,7 @@ void bta_av_role_res (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
bdcpy(av_open.bd_addr, p_scb->peer_addr);
av_open.chnl = p_scb->chnl;
av_open.hndl = p_scb->hndl;
start.status = BTA_AV_FAIL_ROLE;
av_open.status = BTA_AV_FAIL_ROLE;
if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) {
av_open.sep = AVDT_TSEP_SNK;
} else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) {
@@ -1271,7 +1280,7 @@ void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
if (AVDT_TSEP_SNK == local_sep) {
if ((p_data->ci_setconfig.err_code == AVDT_SUCCESS) &&
(p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL)) {
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) {
@@ -1384,7 +1393,7 @@ void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
/* TODO check if other audio channel is open.
* If yes, check if reconfig is needed
* Rigt now we do not do this kind of checking.
* Right now we do not do this kind of checking.
* BTA-AV is INT for 2nd audio connection.
* The application needs to make sure the current codec_info is proper.
* If one audio connection is open and another SNK attempts to connect to AV,
@@ -1397,6 +1406,7 @@ void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
open.status = BTA_AV_SUCCESS;
open.starting = bta_av_chk_start(p_scb);
open.edr = 0;
open.mtu = mtu;
if ( NULL != (p = BTM_ReadRemoteFeatures(p_scb->peer_addr))) {
if (HCI_EDR_ACL_2MPS_SUPPORTED(p)) {
open.edr |= BTA_AV_EDR_2MBPS;
@@ -1533,7 +1543,7 @@ void bta_av_do_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
AVDT_CloseReq(p_scb->avdt_handle);
/* just in case that the link is congested, link is flow controled by peer or
/* just in case that the link is congested, link is flow controlled by peer or
* for whatever reason the the close request can not be sent in time.
* when this timer expires, AVDT_DisconnectReq will be called to disconnect the link
*/
@@ -1814,7 +1824,7 @@ void bta_av_open_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
}
/* if there is already an active AV connnection with the same bd_addr,
/* if there is already an active AV connection with the same bd_addr,
don't send disconnect req, just report the open event with BTA_AV_FAIL_GET_CAP status */
if (is_av_opened == TRUE) {
bdcpy(open.bd_addr, p_scb->peer_addr);
@@ -1856,7 +1866,7 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
tAVDT_CFG cfg;
UINT8 media_type;
tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx];
UINT16 uuid_int; /* UUID for which connection was initiatied */
UINT16 uuid_int; /* UUID for which connection was initiated */
tBTA_AV_SNK_PSC_CFG psc_cfg = {0};
memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
@@ -1899,9 +1909,9 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
p_scb->cur_psc_mask = cfg.psc_mask;
if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) {
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
APPL_TRACE_DEBUG(" Configure Deoder for Sink Connection ");
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) {
@@ -1909,6 +1919,13 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
}
(*bta_av_cb.p_cback)(BTA_AV_SNK_PSC_CFG_EVT, (tBTA_AV *)&psc_cfg);
}
else {
/* UUID_SERVCLASS_AUDIO_SOURCE */
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
}
/* open the stream */
AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,
@@ -2387,7 +2404,7 @@ void bta_av_start_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
/* If sink starts stream, disable sniff mode here */
if (!initiator) {
/* If souce is the master role, disable role switch during streaming.
/* If source is the master role, disable role switch during streaming.
* Otherwise allow role switch, if source is slave.
* Because it would not hurt source, if the peer device wants source to be master */
if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) &&
@@ -2502,7 +2519,7 @@ void bta_av_str_closed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
bta_av_str_stopped(p_scb, NULL);
}
/* Update common mtu shared by remaining connectons */
/* Update common mtu shared by remaining connections */
mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU);
{

View File

@@ -129,6 +129,23 @@ void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id,
}
}
void BTA_AvRegSEP(tBTA_AV_CHNL chnl, UINT8 seid, UINT8 tsep, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, tBTA_AV_DATA_CBACK *p_data_cback)
{
tBTA_AV_API_REG_SEP *p_buf;
if ((p_buf = (tBTA_AV_API_REG_SEP *) osi_malloc(sizeof(tBTA_AV_API_REG_SEP))) != NULL) {
p_buf->hdr.layer_specific = chnl;
p_buf->hdr.event = BTA_AV_API_REG_SEP_EVT;
p_buf->seid = seid;
p_buf->tsep = tsep;
p_buf->codec_type = codec_type;
memcpy(p_buf->codec_info, p_codec_info, AVDT_CODEC_SIZE);
p_buf->p_data_cback = p_data_cback;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AvDeregister

View File

@@ -151,6 +151,7 @@ static const tBTA_AV_ST_TBL bta_av_st_tbl[] = {
typedef void (*tBTA_AV_NSM_ACT)(tBTA_AV_DATA *p_data);
static void bta_av_api_enable(tBTA_AV_DATA *p_data);
static void bta_av_api_register(tBTA_AV_DATA *p_data);
static void bta_av_api_reg_sep(tBTA_AV_DATA *p_data);
#if (BTA_AV_SINK_INCLUDED == TRUE)
static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data);
static void bta_av_api_get_delay_value(tBTA_AV_DATA *p_data);
@@ -170,6 +171,7 @@ static void bta_av_sys_rs_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 ap
const tBTA_AV_NSM_ACT bta_av_nsm_act[] = {
bta_av_api_enable, /* BTA_AV_API_ENABLE_EVT */
bta_av_api_register, /* BTA_AV_API_REGISTER_EVT */
bta_av_api_reg_sep, /* BTA_AV_API_REG_SEP_EVT */
bta_av_api_deregister, /* BTA_AV_API_DEREGISTER_EVT */
bta_av_api_disconnect, /* BTA_AV_API_DISCONNECT_EVT */
bta_av_ci_data, /* BTA_AV_CI_SRC_DATA_READY_EVT */
@@ -533,9 +535,11 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
tAVDT_REG reg;
tAVDT_CS cs;
char *p_service_name;
tBTA_AV_CODEC codec_type;
tBTA_UTL_COD cod;
#if (BTA_AV_EXT_CODEC == FALSE)
tBTA_AV_CODEC codec_type;
UINT8 index = 0;
#endif
char p_avk_service_name[BTA_SERVICE_NAME_LEN + 1];
BCM_STRNCPY_S(p_avk_service_name, BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN);
p_avk_service_name[BTA_SERVICE_NAME_LEN] = '\0';
@@ -658,6 +662,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
/* keep the configuration in the stream control block */
memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
#if (BTA_AV_EXT_CODEC == FALSE)
while (index < BTA_AV_MAX_SEPS &&
(p_scb->p_cos->init)(&codec_type, cs.cfg.codec_info,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg.tsep) == TRUE) {
@@ -673,12 +678,13 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
#if (BTA_AV_SINK_INCLUDED == TRUE)
p_scb->seps[index].tsep = cs.tsep;
if (cs.tsep == AVDT_TSEP_SNK) {
p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
} else {
p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */
}
// if (cs.tsep == AVDT_TSEP_SNK) {
// p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
// } else {
// p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */
// }
#endif
p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
APPL_TRACE_DEBUG("audio[%d] av_handle: %d codec_type: %d\n",
index, p_scb->seps[index].av_handle, p_scb->seps[index].codec_type);
@@ -687,6 +693,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
break;
}
}
#endif
if (!bta_av_cb.reg_audio) {
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
@@ -744,6 +751,86 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
(*bta_av_cb.p_cback)(BTA_AV_REGISTER_EVT, (tBTA_AV *)&registr);
}
static void bta_av_api_reg_sep(tBTA_AV_DATA *p_data)
{
UNUSED(p_data);
#if (BTA_AV_EXT_CODEC == TRUE)
tAVDT_CS cs;
tBTA_AV_CODEC codec_type;
UINT8 index = p_data->api_reg_sep.seid;
tBTA_AV_SEP_REG sep_reg;
sep_reg.seid = p_data->api_reg_sep.seid;
sep_reg.reg_state = BTA_AV_FAIL;
if (index > BTA_AV_MAX_SEPS || p_data->hdr.layer_specific != BTA_AV_CHNL_AUDIO) {
(*bta_av_cb.p_cback)(BTA_AV_SEP_REG_EVT, (tBTA_AV *)&sep_reg);
APPL_TRACE_WARNING("%s invalid parameter: seid %d, ch %d", __FUNCTION__, index, p_data->hdr.layer_specific);
return;
}
memset(&cs, 0, sizeof(tAVDT_CS));
cs.cfg.num_codec = 1;
cs.tsep = p_data->api_reg_sep.tsep;
cs.nsc_mask = AVDT_NSC_RECONFIG | ((bta_av_cb.features & BTA_AV_FEAT_PROTECT) ? 0 : AVDT_NSC_SECURITY);
for (int xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
if (bta_av_cb.p_scb[xx] != NULL && bta_av_cb.p_scb[xx]->chnl == BTA_AV_CHNL_AUDIO) {
tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[xx];
cs.p_ctrl_cback = bta_av_dt_cback[p_scb->hdi];
cs.cfg.psc_mask = AVDT_PSC_TRANS;
cs.media_type = AVDT_MEDIA_AUDIO;
cs.mtu = p_bta_av_cfg->audio_mtu;
cs.flush_to = L2CAP_DEFAULT_FLUSH_TO;
#if AVDT_REPORTING == TRUE
if (bta_av_cb.features & BTA_AV_FEAT_REPORT) {
cs.cfg.psc_mask |= AVDT_PSC_REPORT;
cs.p_report_cback = bta_av_a2dp_report_cback;
#if AVDT_MULTIPLEXING == TRUE
cs.cfg.mux_tsid_report = 2;
#endif
}
#endif
if (bta_av_cb.features & BTA_AV_FEAT_DELAY_RPT) {
cs.cfg.psc_mask |= AVDT_PSC_DELAY_RPT;
}
/* todo: check whether this memcpy is necessary */
memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
codec_type = p_data->api_reg_sep.codec_type;
memcpy(cs.cfg.codec_info, p_data->api_reg_sep.codec_info, AVDT_CODEC_SIZE);
if ((p_scb->p_cos->init)(&codec_type, cs.cfg.codec_info,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg_sep.tsep) == TRUE) {
#if (BTA_AV_SINK_INCLUDED == TRUE)
if (p_data->api_reg_sep.tsep == AVDT_TSEP_SNK) {
cs.p_data_cback = bta_av_stream_data_cback;
}
#endif
if ((p_scb->seps[index].av_handle != 0) && (AVDT_RemoveStream(p_scb->seps[index].av_handle) != AVDT_SUCCESS)) {
APPL_TRACE_WARNING("%s fail to remove exist sep", __FUNCTION__);
}
if (AVDT_CreateStream(&p_scb->seps[index].av_handle, &cs) == AVDT_SUCCESS) {
p_scb->seps[index].codec_type = codec_type;
p_scb->seps[index].tsep = cs.tsep;
p_scb->seps[index].p_app_data_cback = p_data->api_reg_sep.p_data_cback;
} else {
APPL_TRACE_WARNING("%s fail to create sep", __FUNCTION__);
break;
}
}
else {
APPL_TRACE_WARNING("%s invalid codec capability", __FUNCTION__);
}
}
}
sep_reg.reg_state = BTA_AV_SUCCESS;
(*bta_av_cb.p_cback)(BTA_AV_SEP_REG_EVT, (tBTA_AV *)&sep_reg);
#endif
}
/*******************************************************************************
**
** Function bta_av_api_deregister
@@ -1415,7 +1502,8 @@ char *bta_av_action_code(UINT16 action_code)
case 46: return "BTA_AV_DELAY_CO";
case 47: return "BTA_AV_OPEN_AT_INC";
case 48: return "BTA_AV_OPEN_FAIL_SDP";
case 49: return "NULL";
case 49: return "BTA_AV_SET_DELAY_VALUE";
case 50: return "NULL";
default: return "unknown";
}
}

View File

@@ -581,6 +581,64 @@ UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap)
return status;
}
/*******************************************************************************
**
** Function bta_av_sbc_cfg_in_external_codec_cap
**
** Description This function checks whether an SBC codec configuration
** is allowable for the given external codec capabilities.
**
** Returns 0 if ok, nonzero if error.
**
*******************************************************************************/
UINT8 bta_av_sbc_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap)
{
UINT8 status = 0;
tA2D_SBC_CIE cfg_cie;
tA2D_SBC_CIE cap_cie;
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, FALSE)) != 0) {
return status;
}
/* parse capability */
if ((status = A2D_ParsSbcInfo(&cap_cie, p_cap, TRUE)) != 0) {
return status;
}
/* verify that each parameter is in range */
/* sampling frequency */
if ((cfg_cie.samp_freq & cap_cie.samp_freq) == 0) {
status = A2D_NS_SAMP_FREQ;
}
/* channel mode */
else if ((cfg_cie.ch_mode & cap_cie.ch_mode) == 0) {
status = A2D_NS_CH_MODE;
}
/* block length */
else if ((cfg_cie.block_len & cap_cie.block_len) == 0) {
status = A2D_BAD_BLOCK_LEN;
}
/* subbands */
else if ((cfg_cie.num_subbands & cap_cie.num_subbands) == 0) {
status = A2D_NS_SUBBANDS;
}
/* allocation method */
else if ((cfg_cie.alloc_mthd & cap_cie.alloc_mthd) == 0) {
status = A2D_NS_ALLOC_MTHD;
}
/* max bitpool */
else if (cfg_cie.max_bitpool > cap_cie.max_bitpool) {
status = A2D_NS_MAX_BITPOOL;
}
/* min bitpool */
else if (cfg_cie.min_bitpool < cap_cie.min_bitpool) {
status = A2D_NS_MIN_BITPOOL;
}
return status;
}
/*******************************************************************************
**
** Function bta_av_sbc_bld_hdr

View File

@@ -98,6 +98,7 @@ enum {
/* these events are handled outside of the state machine */
BTA_AV_API_ENABLE_EVT,
BTA_AV_API_REGISTER_EVT,
BTA_AV_API_REG_SEP_EVT,
BTA_AV_API_DEREGISTER_EVT,
BTA_AV_API_DISCONNECT_EVT,
BTA_AV_CI_SRC_DATA_READY_EVT,
@@ -195,6 +196,15 @@ typedef struct {
tBTA_AVRC_CO_FUNCTS *bta_avrc_cos;
} tBTA_AV_API_REG;
/* data type for BTA_AV_API_REG_SEP_EVT */
typedef struct {
BT_HDR hdr;
UINT8 seid;
UINT8 tsep;
tBTA_AV_CODEC codec_type;
UINT8 codec_info[AVDT_CODEC_SIZE];
tBTA_AV_DATA_CBACK *p_data_cback;
} tBTA_AV_API_REG_SEP;
enum {
BTA_AV_RS_NONE, /* straight API call */
@@ -425,6 +435,7 @@ typedef union {
BT_HDR hdr;
tBTA_AV_API_ENABLE api_enable;
tBTA_AV_API_REG api_reg;
tBTA_AV_API_REG_SEP api_reg_sep;
tBTA_AV_API_OPEN api_open;
tBTA_AV_API_STOP api_stop;
tBTA_AV_API_DISCNT api_discnt;

View File

@@ -339,6 +339,73 @@ void BTA_AgCiData(UINT16 handle)
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgAudioBuffAlloc
**
** Description Allocate an audio buffer with specific size, reserve enough
** space and offset for lower layer to send the buffer directly.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioBuffAlloc(UINT16 size, UINT8 **pp_buff, UINT8 **pp_data)
{
/* reserve 1 byte at last, when the size is mSBC frame size (57), then we got a buffer that can hold 60 bytes data */
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + BTA_AG_H2_HEADER_LEN + size + 1);
if (p_buf != NULL) {
/* mSBC offset is large than CVSD, so this is also work in CVSD air mode */
p_buf->offset = BTA_AG_BUFF_OFFSET_MIN + BTA_AG_H2_HEADER_LEN;
*pp_buff = (UINT8 *)p_buf;
*pp_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
}
}
/*******************************************************************************
**
** Function BTA_AgAudioBuffFree
**
** Description Free an audio buffer.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioBuffFree(UINT8 *p_buf)
{
osi_free(p_buf);
}
/*******************************************************************************
**
** Function BTA_AgAudioDataSend
**
** Description Send audio data to lower level, whether success or not, buffer
** is consumed.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioDataSend(UINT16 handle, UINT8 *p_buff_start, UINT8 *p_data, UINT16 data_len)
{
BT_HDR *p_buf = (BT_HDR *)p_buff_start;
tBTA_AG_SCB *p_scb;
assert(p_data - (UINT8 *)(p_buf + 1) >= 0);
if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) {
p_buf->event = BTA_AG_API_SCO_DATA_SEND_EVT;
p_buf->layer_specific = handle;
p_buf->offset = p_data - (UINT8 *)(p_buf + 1);
p_buf->len = data_len;
bta_sys_sendmsg(p_buf);
}
else {
osi_free(p_buf);
}
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */
#endif /* #if (BTA_AG_INCLUDED == TRUE)*/

View File

@@ -89,6 +89,8 @@ enum
BTA_AG_SETCODEC,
BTA_AG_SEND_RING,
BTA_AG_CI_SCO_DATA,
BTA_AG_SCO_DATA_SEND,
BTA_AG_SCO_DATA_FREE,
BTA_AG_CI_RX_DATA,
BTA_AG_RCVD_SLC_READY,
BTA_AG_PKT_STAT_NUMS,
@@ -133,6 +135,8 @@ const tBTA_AG_ACTION bta_ag_action[] =
bta_ag_setcodec,
bta_ag_send_ring,
bta_ag_ci_sco_data,
bta_ag_sco_data_send,
bta_ag_sco_data_free,
bta_ag_ci_rx_data,
bta_ag_rcvd_slc_ready,
bta_ag_pkt_stat_nums
@@ -155,6 +159,7 @@ const UINT8 bta_ag_st_init[][BTA_AG_NUM_COLS] =
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_SCO_DATA_SEND_EVT */ {BTA_AG_SCO_DATA_FREE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_OPEN_EVT */ {BTA_AG_RFC_ACP_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
@@ -185,6 +190,7 @@ const UINT8 bta_ag_st_opening[][BTA_AG_NUM_COLS] =
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_SCO_DATA_SEND_EVT */ {BTA_AG_SCO_DATA_FREE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* RFC_OPEN_EVT */ {BTA_AG_RFC_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
@@ -215,6 +221,7 @@ const UINT8 bta_ag_st_open[][BTA_AG_NUM_COLS] =
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_SCO_CLOSE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_RESULT_EVT */ {BTA_AG_RESULT, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_SETCODEC_EVT */ {BTA_AG_SETCODEC, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_SCO_DATA_SEND_EVT */ {BTA_AG_SCO_DATA_SEND, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
@@ -245,6 +252,7 @@ const UINT8 bta_ag_st_closing[][BTA_AG_NUM_COLS] =
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_SCO_DATA_SEND_EVT */ {BTA_AG_SCO_DATA_FREE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
@@ -360,9 +368,9 @@ static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result)
case BTA_AG_API_DISABLE_EVT:
return "Disable AG";
case BTA_AG_CI_SCO_DATA_EVT:
return "SCO data Callin";
return "SCO data Call In";
case BTA_AG_CI_SLC_READY_EVT:
return "SLC Ready Callin";
return "SLC Ready Call In";
case BTA_AG_PKT_STAT_NUMS_GET_EVT:
return "Get Packet Nums";
default:
@@ -433,6 +441,7 @@ static tBTA_AG_SCB *bta_ag_scb_alloc(void)
#if (BTM_WBS_INCLUDED == TRUE)
p_scb->codec_updated = FALSE;
#endif
p_scb->p_sco_data = NULL;
/* set up timers */
p_scb->act_timer.param = (UINT32) p_scb;
p_scb->act_timer.p_cback = bta_ag_timer_cback;
@@ -473,6 +482,10 @@ void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb)
#if (BTM_WBS_INCLUDED == TRUE)
bta_sys_free_timer(&p_scb->cn_timer);
#endif
if (p_scb->p_sco_data != NULL) {
osi_free(p_scb->p_sco_data);
p_scb->p_sco_data = NULL;
}
bta_sys_free_timer(&p_scb->colli_timer);
/* initialize control block */
@@ -774,7 +787,7 @@ static void bta_ag_api_enable(tBTA_AG_DATA *p_data)
bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_CVSD;
}
/* set deault setting for eSCO/SCO */
/* set default setting for eSCO/SCO */
BTM_WriteVoiceSettings(AG_VOICE_SETTINGS);
bta_sys_collision_register (BTA_ID_AG, bta_ag_collision_cback);
/* call callback with enable event */
@@ -943,6 +956,7 @@ void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data)
BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg)
{
tBTA_AG_SCB *p_scb;
BOOLEAN free_msg = TRUE;
APPL_TRACE_DEBUG("bta_ag_hdl_event: Event 0x%04x ", p_msg->event);
switch (p_msg->event) {
@@ -966,15 +980,22 @@ BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg)
bta_ag_api_result((tBTA_AG_DATA *) p_msg);
break;
/* all others reference scb by handle */
case BTA_AG_API_SCO_DATA_SEND_EVT:
free_msg = FALSE;
/* fall through */
default:
/* all others reference scb by handle */
if ((p_scb = bta_ag_scb_by_idx(p_msg->layer_specific)) != NULL) {
APPL_TRACE_DEBUG("bta_ag_hdl_event: p_scb 0x%08x ", (unsigned int)p_scb);
bta_ag_sm_execute(p_scb, p_msg->event, (tBTA_AG_DATA *) p_msg);
}
else {
/* free message if p_scb not found */
free_msg = TRUE;
}
break;
}
return TRUE;
return free_msg;
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View File

@@ -345,9 +345,10 @@ static void bta_ag_sco_read_cback(UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_
APPL_TRACE_DEBUG("bta_ag_sco_read_cback: status(%d)", status);
}
#if (BTA_HFP_EXT_CODEC == FALSE)
/* Callout function must free the data. */
bta_ag_sco_co_in_data(p_data, status);
osi_free(p_data);
#endif
}
#endif
/*******************************************************************************
@@ -459,6 +460,37 @@ static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p
}
}
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
/*******************************************************************************
**
** Function bta_ag_sco_get_frame_size
**
** Description Get SCO frame size.
**
**
** Returns SCO frame size
**
*******************************************************************************/
static UINT16 bta_ag_sco_get_frame_size(tBTA_AG_SCB *p_scb)
{
UINT16 frame_size = 0;
switch (p_scb->air_mode)
{
case BTM_SCO_AIR_MODE_CVSD:
frame_size = p_scb->out_pkt_len;
break;
case BTM_SCO_AIR_MODE_TRANSPNT:
frame_size = BTA_AG_MSBC_FRAME_SIZE;
break;
default:
break;
}
return frame_size;
}
#endif
/*******************************************************************************
**
** Function bta_ag_cback_sco
@@ -471,14 +503,19 @@ static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p
*******************************************************************************/
static void bta_ag_cback_sco(tBTA_AG_SCB *p_scb, UINT8 event)
{
tBTA_AG_HDR sco;
tBTA_AG_AUDIO_STAT audio_stat = {0};
sco.handle = bta_ag_scb_to_idx(p_scb);
sco.app_id = p_scb->app_id;
sco.sync_conn_handle = BTM_ReadScoHandle(p_scb->sco_idx);
audio_stat.hdr.handle = bta_ag_scb_to_idx(p_scb);
audio_stat.hdr.app_id = p_scb->app_id;
audio_stat.hdr.sync_conn_handle = BTM_ReadScoHandle(p_scb->sco_idx);
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
if (event != BTA_AG_AUDIO_CLOSE_EVT) {
audio_stat.preferred_frame_size = bta_ag_sco_get_frame_size(p_scb);
}
#endif
/* call close cback */
(*bta_ag_cb.p_cback)(event, (tBTA_AG *) &sco);
(*bta_ag_cb.p_cback)(event, (tBTA_AG *) &audio_stat);
}
/*******************************************************************************
@@ -784,7 +821,7 @@ static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event)
p_scb->sco_idx, p_sco->state,
bta_ag_sco_state_str(p_sco->state), event, bta_ag_sco_evt_str(event));
#if (BTM_SCO_HCI_INCLUDED == TRUE)
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == FALSE)
BT_HDR *p_buf;
if (event == BTA_AG_SCO_CI_DATA_E)
{
@@ -1633,6 +1670,11 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
}
if (p_scb->p_sco_data != NULL) {
osi_free(p_scb->p_sco_data);
p_scb->p_sco_data = NULL;
}
/* call app callback */
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT);
#if (BTM_WBS_INCLUDED == TRUE)
@@ -1665,7 +1707,7 @@ void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data
bta_ag_cb.sco.state == BTA_AG_SCO_CLOSE_XFER_ST ||
bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_XFER_ST)
{
/* If script overrided sco parameter by BTA_CMD_SET_ESCO_PARAM */
/* If script override sco parameter by BTA_CMD_SET_ESCO_PARAM */
if (bta_ag_cb.sco.param_updated)
{
resp = bta_ag_cb.sco.params;
@@ -1736,7 +1778,7 @@ void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data
**
** Function bta_ag_ci_sco_data
**
** Description Process the SCO data ready callin event
** Description Process the SCO data ready call in event
**
**
** Returns void
@@ -1752,6 +1794,288 @@ void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
#endif
}
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
/*******************************************************************************
**
** Function bta_ag_write_sco_data
**
** Description Write two SCO data buffers to specified instance
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_write_sco_data(tBTA_AG_SCB *p_scb, BT_HDR *p_buf1, BT_HDR *p_buf2)
{
BTM_WriteScoData(p_scb->sco_idx, p_buf1);
if (p_buf2 != NULL) {
BTM_WriteScoData(p_scb->sco_idx, p_buf2);
}
}
/*******************************************************************************
**
** Function bta_ag_sco_data_send_cvsd
**
** Description Process SCO data of CVSD air mode
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_sco_data_send_cvsd(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
{
UINT16 out_pkt_len = p_scb->out_pkt_len;
if (p_scb->p_sco_data != NULL) {
/* the remaining data of last sending operation */
BT_HDR *p_buf_last = p_scb->p_sco_data;
/* remaining data len should small than out_pkt_len */
assert(p_buf_last->len < out_pkt_len);
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + out_pkt_len);
if (p_buf2 == NULL) {
osi_free(p_buf);
osi_free(p_buf_last);
p_scb->p_sco_data = NULL;
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_AG_BUFF_OFFSET_MIN;
UINT8 *p_data = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data, (UINT8 *)(p_buf_last + 1) + p_buf_last->offset, p_buf_last->len);
if (p_buf->len + p_buf_last->len < out_pkt_len) {
memcpy(p_data + p_buf_last->len, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len);
p_buf2->len = p_buf->len + p_buf_last->len;
osi_free(p_buf);
osi_free(p_buf_last);
p_scb->p_sco_data = p_buf2;
}
else {
UINT16 copy_len = out_pkt_len - p_buf_last->len;
memcpy(p_data + p_buf_last->len, (UINT8 *)(p_buf + 1) + p_buf->offset, copy_len);
p_buf2->len = out_pkt_len;
p_buf->offset += copy_len;
p_buf->len -= copy_len;
osi_free(p_buf_last);
p_scb->p_sco_data = NULL;
bta_ag_write_sco_data(p_scb, p_buf2, NULL);
if (p_buf->len == 0) {
osi_free(p_buf);
}
else {
/* recursive call, this will only called once */
bta_ag_sco_data_send_cvsd(p_scb, p_buf);
}
}
}
else if (p_buf->len < out_pkt_len) {
p_scb->p_sco_data = p_buf;
}
else {
/* p_scb->p_sco_data != NULL && p_buf->len >= out_pkt_len */
while (1) {
if (p_buf->len == out_pkt_len) {
bta_ag_write_sco_data(p_scb, p_buf, NULL);
break;
}
else {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + out_pkt_len);
if (p_buf2 == NULL) {
osi_free(p_buf);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_AG_BUFF_OFFSET_MIN;
UINT8 *p_data = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, out_pkt_len);
p_buf2->len = out_pkt_len;
p_buf->offset += out_pkt_len;
p_buf->len -= out_pkt_len;
bta_ag_write_sco_data(p_scb, p_buf2, NULL);
}
if (p_buf->len < out_pkt_len) {
p_scb->p_sco_data = p_buf;
break;
}
}
}
}
/*******************************************************************************
**
** Function bta_ag_sco_data_send_msbc
**
** Description Process SCO data of mSBC air mode
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
{
UINT16 out_pkt_len = p_scb->out_pkt_len;
if (p_buf->len == BTA_AG_MSBC_FRAME_SIZE && p_buf->offset >= BTA_AG_BUFF_OFFSET_MIN + BTA_AG_H2_HEADER_LEN) {
/* add H2 header */
p_buf->offset -= BTA_AG_H2_HEADER_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
bta_ag_h2_header((UINT16 *)p_data);
/* add header len, add addition one bytes, the len is BTA_AG_SCO_OUT_PKT_LEN_2EV3 now */
p_buf->len += BTA_AG_H2_HEADER_LEN + 1;
if (out_pkt_len == BTA_AG_SCO_OUT_PKT_LEN_2EV3) {
/* mSBC frame can be send directly */
bta_ag_write_sco_data(p_scb, p_buf, NULL);
}
else if (out_pkt_len == BTA_AG_SCO_OUT_PKT_LEN_EV3) {
/* need to split into 2 sco packages for sending */
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + BTA_AG_SCO_OUT_PKT_LEN_EV3);
if (p_buf2 == NULL) {
/* free the first buff too */
osi_free(p_buf);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_AG_BUFF_OFFSET_MIN;
p_buf2->len = BTA_AG_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data2, p_data + BTA_AG_SCO_OUT_PKT_LEN_EV3, BTA_AG_SCO_OUT_PKT_LEN_EV3);
/* update the first packet len */
p_buf->len = BTA_AG_SCO_OUT_PKT_LEN_EV3;
bta_ag_write_sco_data(p_scb, p_buf, p_buf2);
}
else {
osi_free(p_buf);
APPL_TRACE_WARNING("%s, invalid out pkt len: %d", __FUNCTION__, out_pkt_len);
}
}
else if (p_buf->len != 0 && p_buf->len % BTA_AG_MSBC_FRAME_SIZE == 0) {
/* multiple mSBC frame in the buffer, or just one but offset is too small */
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
UINT16 total_len = p_buf->len;
if (out_pkt_len == BTA_AG_SCO_OUT_PKT_LEN_2EV3) {
while (total_len != 0) {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + BTA_AG_SCO_OUT_PKT_LEN_2EV3);
if (p_buf2 == NULL) {
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
p_buf2->offset = BTA_AG_BUFF_OFFSET_MIN;
p_buf2->len = BTA_AG_SCO_OUT_PKT_LEN_2EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
bta_ag_h2_header((UINT16 *)p_data2);
p_data2 += BTA_AG_H2_HEADER_LEN;
memcpy(p_data2, p_data, BTA_AG_MSBC_FRAME_SIZE);
p_data += BTA_AG_MSBC_FRAME_SIZE;
total_len -= BTA_AG_MSBC_FRAME_SIZE;
bta_ag_write_sco_data(p_scb, p_buf2, NULL);
}
}
else if (out_pkt_len == BTA_AG_SCO_OUT_PKT_LEN_EV3) {
while (total_len != 0) {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + BTA_AG_SCO_OUT_PKT_LEN_EV3);
if (p_buf2 == NULL) {
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
BT_HDR *p_buf3 = osi_calloc(sizeof(BT_HDR) + BTA_AG_BUFF_OFFSET_MIN + BTA_AG_SCO_OUT_PKT_LEN_EV3);
if (p_buf3 == NULL) {
/* free the first buff too */
osi_free(p_buf2);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
/* build first packet, include H2 header */
p_buf2->offset = BTA_AG_BUFF_OFFSET_MIN;
p_buf2->len = BTA_AG_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
bta_ag_h2_header((UINT16 *)p_data2);
p_data2 += BTA_AG_H2_HEADER_LEN;
memcpy(p_data2, p_data, BTA_AG_SCO_OUT_PKT_LEN_EV3 - BTA_AG_H2_HEADER_LEN);
p_data += BTA_AG_SCO_OUT_PKT_LEN_EV3 - BTA_AG_H2_HEADER_LEN;
total_len -= BTA_AG_SCO_OUT_PKT_LEN_EV3 - BTA_AG_H2_HEADER_LEN;
/* build second packet, not include header */
p_buf3->offset = BTA_AG_BUFF_OFFSET_MIN;
p_buf3->len = BTA_AG_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data3 = (UINT8 *)(p_buf3 + 1) + p_buf3->offset;
memcpy(p_data3, p_data, BTA_AG_MSBC_FRAME_SIZE - BTA_AG_H2_HEADER_LEN - BTA_AG_SCO_OUT_PKT_LEN_EV3);
p_data += BTA_AG_MSBC_FRAME_SIZE - BTA_AG_H2_HEADER_LEN - BTA_AG_SCO_OUT_PKT_LEN_EV3;
total_len -= BTA_AG_MSBC_FRAME_SIZE - BTA_AG_H2_HEADER_LEN - BTA_AG_SCO_OUT_PKT_LEN_EV3;
bta_ag_write_sco_data(p_scb, p_buf2, p_buf3);
}
}
else {
APPL_TRACE_WARNING("%s, invalid out pkt len: %d", __FUNCTION__, out_pkt_len);
}
osi_free(p_buf);
}
else {
APPL_TRACE_WARNING("%s, unaccepted data len: %d", __FUNCTION__, p_buf->len);
osi_free(p_buf);
}
}
#endif
/*******************************************************************************
**
** Function bta_ag_sco_data_send
**
** Description Route SCO data to specific processing function based on air mode
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_data_send(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
BT_HDR *p_buf = (BT_HDR *) p_data;
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
if (bta_ag_cb.sco.state != BTA_AG_SCO_OPEN_ST || bta_ag_cb.sco.cur_idx != p_scb->sco_idx) {
osi_free(p_data);
APPL_TRACE_WARNING("%s: SCO invalid state", __FUNCTION__);
return;
}
switch (p_scb->air_mode)
{
case BTM_SCO_AIR_MODE_CVSD:
bta_ag_sco_data_send_cvsd(p_scb, p_buf);
break;
case BTM_SCO_AIR_MODE_TRANSPNT:
bta_ag_sco_data_send_msbc(p_scb, p_buf);
break;
default:
osi_free(p_buf);
APPL_TRACE_WARNING("%s: unsupported air mode: %d", __FUNCTION__, p_scb->air_mode);
break;
}
#else
osi_free(p_buf);
#endif
}
/*******************************************************************************
**
** Function bta_ag_sco_data_free
**
** Description Free SCO data
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_data_free(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UNUSED(p_scb);
osi_free(p_data);
}
/*******************************************************************************
**
** Function bta_ag_set_esco_param

View File

@@ -96,6 +96,19 @@
BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \
BTA_AG_FEAT_VTAG)
#if (BTM_SCO_HCI_INCLUDED == TRUE)
/* 1 (offset can not be 0) + HCI_SCO_PREAMBLE */
#define BTA_AG_BUFF_OFFSET_MIN (1 + HCI_SCO_PREAMBLE_SIZE)
/* mSBC H2 header length */
#define BTA_AG_H2_HEADER_LEN 2
/* mSBC frame size not include H1/H2 header */
#define BTA_AG_MSBC_FRAME_SIZE 57
/* max user data len of sco packet type EV3 */
#define BTA_AG_SCO_OUT_PKT_LEN_EV3 30
/* max user data len of sco packet type 2-EV3 */
#define BTA_AG_SCO_OUT_PKT_LEN_2EV3 60
#endif
enum
{
/* these events are handled by the state machine */
@@ -107,6 +120,7 @@ enum
BTA_AG_API_AUDIO_CLOSE_EVT,
BTA_AG_API_RESULT_EVT,
BTA_AG_API_SETCODEC_EVT,
BTA_AG_API_SCO_DATA_SEND_EVT,
BTA_AG_RFC_OPEN_EVT,
BTA_AG_RFC_CLOSE_EVT,
BTA_AG_RFC_SRV_CLOSE_EVT,
@@ -295,6 +309,7 @@ typedef struct
TIMER_LIST_ENT cn_timer; /* codec negotiation timer */
#endif
UINT16 sco_idx; /* SCO connection index */
BT_HDR *p_sco_data; /* remaining SCO data of last sending operation */
BOOLEAN in_use; /* scb in use */
BOOLEAN dealloc; /* TRUE if service shutting down */
BOOLEAN clip_enabled; /* set to TRUE if HF enables CLIP reporting */
@@ -452,10 +467,13 @@ extern void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_data_send(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_data_free(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param);
extern void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_pkt_stat_nums(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_h2_header(UINT16 *p_buf);
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View File

@@ -44,9 +44,9 @@ static const uint8_t bta_hf_client_cb_data_size[] = {
sizeof(tBTA_HF_CLIENT_OPEN), // #define BTA_HF_CLIENT_OPEN_EVT 2
0, // #define BTA_HF_CLIENT_CLOSE_EVT 3
sizeof(tBTA_HF_CLIENT_CONN), // #define BTA_HF_CLIENT_CONN_EVT 4
sizeof(tBTA_HF_CLIENT_HDR), // #define BTA_HF_CLIENT_AUDIO_OPEN_EVT 5
sizeof(tBTA_HF_CLIENT_HDR), //#define BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT 6
sizeof(tBTA_HF_CLIENT_HDR), // #define BTA_HF_CLIENT_AUDIO_CLOSE_EVT 7
sizeof(tBTA_HF_CLIENT_AUDIO_STAT), // #define BTA_HF_CLIENT_AUDIO_OPEN_EVT 5
sizeof(tBTA_HF_CLIENT_AUDIO_STAT), // #define BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT 6
sizeof(tBTA_HF_CLIENT_AUDIO_STAT), // #define BTA_HF_CLIENT_AUDIO_CLOSE_EVT 7
sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_SPK_EVT 8
sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_MIC_EVT 9
sizeof(tBTA_HF_CLIENT_IND), //#define BTA_HF_CLIENT_IND_EVT 10
@@ -322,6 +322,16 @@ void BTA_HfClientPktStatsNumsGet(UINT16 sync_conn_handle)
}
}
/*******************************************************************************
**
** Function BTA_HfClientCiData
**
** Description Send SCO outgoing data ready event
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientCiData(void)
{
BT_HDR *p_buf;
@@ -330,8 +340,79 @@ void BTA_HfClientCiData(void)
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_HfClientAudioBuffAlloc
**
** Description Allocate an audio buffer with specific size, reserve enough
** space and offset for lower layer to send the buffer directly.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioBuffAlloc(UINT16 size, UINT8 **pp_buff, UINT8 **pp_data)
{
/* reserve 1 byte at last, when the size is mSBC frame size (57), then we got a buffer that can hold 60 bytes data */
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_H2_HEADER_LEN + size + 1);
if (p_buf != NULL) {
/* mSBC offset is large than CVSD, so this is work in CVSD air mode */
p_buf->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_H2_HEADER_LEN;
*pp_buff = (UINT8 *)p_buf;
*pp_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
}
}
/*******************************************************************************
**
** Function BTA_HfClientAudioBuffFree
**
** Description Free an audio buffer.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioBuffFree(UINT8 *p_buf)
{
osi_free(p_buf);
}
/*******************************************************************************
**
** Function BTA_HfClientAudioDataSend
**
** Description Send audio data to lower layer, whether success or not, buffer
** is consumed.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioDataSend(UINT16 sync_conn_hdl, UINT8 *p_buff_start, UINT8 *p_data, UINT16 data_len)
{
/* currently, sync_conn_hdl is not used */
BT_HDR *p_buf = (BT_HDR *)p_buff_start;
assert(p_data - (UINT8 *)(p_buf + 1) >= 0);
p_buf->event = BTA_HF_CLIENT_SCO_DATA_SEND_EVT;
p_buf->offset = p_data - (UINT8 *)(p_buf + 1);
p_buf->len = data_len;
bta_sys_sendmsg(p_buf);
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */
/*******************************************************************************
**
** Function BTA_HfClientGetCbDataSize
**
** Description Get callback data size of specific event
**
**
** Returns void
**
*******************************************************************************/
int BTA_HfClientGetCbDataSize(tBTA_HF_CLIENT_EVT event)
{
return bta_hf_client_cb_data_size[event];

View File

@@ -24,6 +24,7 @@
#include "bta/bta_sys.h"
#include "bta/bta_hf_client_api.h"
#include "bta_hf_client_int.h"
#include "osi/allocator.h"
#if BT_HF_CLIENT_BQB_INCLUDED
static BOOLEAN s_bta_hf_client_bqb_clip_flag = TRUE;
@@ -79,6 +80,8 @@ enum {
BTA_HF_CLIENT_SEND_AT_CMD,
#if (BTM_SCO_HCI_INCLUDED == TRUE)
BTA_HF_CLIENT_CI_SCO_DATA,
BTA_HF_CLIENT_SCO_DATA_SEND,
BTA_HF_CLIENT_SCO_DATA_FREE,
BTA_HF_CLIENT_PKT_STAT_NUMS,
#endif
BTA_HF_CLIENT_NUM_ACTIONS,
@@ -118,6 +121,8 @@ const tBTA_HF_CLIENT_ACTION bta_hf_client_action[] = {
/* BTA_HF_CLIENT_SEND_AT_CMD */ bta_hf_client_send_at_cmd,
#if (BTM_SCO_HCI_INCLUDED == TRUE)
/* BTA_HF_CLIENT_CI_SCO_DATA */ bta_hf_client_ci_sco_data,
/* BTA_HF_CLIENT_SCO_DATA_SEND */ bta_hf_client_sco_data_send,
/* BTA_HF_CLIENT_SCO_DATA_FREE */ bta_hf_client_sco_data_free,
/* BTA_HF_CLIENT_PKT_STAT_NUMS */ bta_hf_client_pkt_stat_nums,
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};
@@ -149,6 +154,7 @@ const UINT8 bta_hf_client_st_init[][BTA_HF_CLIENT_NUM_COLS] = {
/* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST},
/* SCO_DATA_SEND_EVT */ {BTA_HF_CLIENT_SCO_DATA_FREE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST},
/* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_INIT_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};
@@ -175,6 +181,7 @@ const UINT8 bta_hf_client_st_opening[][BTA_HF_CLIENT_NUM_COLS] = {
/* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE)
/* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST},
/* SCO_DATA_SEND_EVT */ {BTA_HF_CLIENT_SCO_DATA_FREE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST},
/* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};
@@ -201,6 +208,7 @@ const UINT8 bta_hf_client_st_open[][BTA_HF_CLIENT_NUM_COLS] = {
/* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_SEND_AT_CMD, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_CI_SCO_DATA, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST},
/* SCO_DATA_SEND_EVT */ {BTA_HF_CLIENT_SCO_DATA_SEND, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST},
/* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPEN_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};
@@ -227,6 +235,7 @@ const UINT8 bta_hf_client_st_closing[][BTA_HF_CLIENT_NUM_COLS] = {
/* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST},
/* SCO_DATA_SEND_EVT */ {BTA_HF_CLIENT_SCO_DATA_FREE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST},
/* PKT_STAT_NUMS_GET_EVT */ {BTA_HF_CLIENT_PKT_STAT_NUMS, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};
@@ -284,6 +293,7 @@ void bta_hf_client_scb_init(void)
memset(&bta_hf_client_cb.scb, 0, sizeof(tBTA_HF_CLIENT_SCB));
bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX;
bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
bta_hf_client_cb.scb.p_sco_data = NULL;
}
/*******************************************************************************
@@ -299,6 +309,10 @@ void bta_hf_client_scb_init(void)
void bta_hf_client_scb_disable(void)
{
APPL_TRACE_DEBUG("%s", __FUNCTION__);
if (bta_hf_client_cb.scb.p_sco_data != NULL) {
osi_free(bta_hf_client_cb.scb.p_sco_data);
bta_hf_client_cb.scb.p_sco_data = NULL;
}
bta_hf_client_scb_init();
@@ -470,6 +484,7 @@ static void bta_hf_client_api_disable(tBTA_HF_CLIENT_DATA *p_data)
*******************************************************************************/
BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg)
{
BOOLEAN free_msg = TRUE;
#if BTA_HF_CLIENT_DEBUG == TRUE
APPL_TRACE_DEBUG("bta_hf_client_hdl_event %s (0x%x)", bta_hf_client_evt_str(p_msg->event), p_msg->event);
#endif
@@ -484,12 +499,16 @@ BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg)
case BTA_HF_CLIENT_API_DISABLE_EVT:
bta_hf_client_api_disable((tBTA_HF_CLIENT_DATA *) p_msg);
break;
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
case BTA_HF_CLIENT_SCO_DATA_SEND_EVT:
free_msg = false;
/* fall through */
#endif
default:
bta_hf_client_sm_execute(p_msg->event, (tBTA_HF_CLIENT_DATA *) p_msg);
break;
}
return TRUE;
return free_msg;
}
/*******************************************************************************

View File

@@ -177,6 +177,36 @@ static BOOLEAN bta_hf_client_sco_remove(BOOLEAN only_active)
return removed_started;
}
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
/*******************************************************************************
**
** Function bta_hf_client_sco_get_frame_size
**
** Description Get SCO frame size
**
** Returns frame size
**
*******************************************************************************/
static UINT16 bta_hf_client_sco_get_frame_size(void)
{
UINT16 frame_size = 0;
switch (bta_hf_client_cb.scb.air_mode)
{
case BTM_SCO_AIR_MODE_CVSD:
frame_size = bta_hf_client_cb.scb.out_pkt_len;
break;
case BTM_SCO_AIR_MODE_TRANSPNT:
frame_size = BTA_HF_CLIENT_MSBC_FRAME_SIZE;
break;
default:
break;
}
return frame_size;
}
#endif
/*******************************************************************************
**
** Function bta_hf_client_cback_sco
@@ -189,13 +219,17 @@ static BOOLEAN bta_hf_client_sco_remove(BOOLEAN only_active)
*******************************************************************************/
void bta_hf_client_cback_sco(UINT8 event)
{
tBTA_HF_CLIENT_HDR evt;
memset(&evt, 0, sizeof(evt));
evt.sync_conn_handle = BTM_ReadScoHandle(bta_hf_client_cb.scb.sco_idx);
tBTA_HF_CLIENT_AUDIO_STAT audio_stat;
memset(&audio_stat, 0, sizeof(audio_stat));
audio_stat.hdr.sync_conn_handle = BTM_ReadScoHandle(bta_hf_client_cb.scb.sco_idx);
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
if (event != BTA_HF_CLIENT_AUDIO_CLOSE_EVT) {
audio_stat.preferred_frame_size = bta_hf_client_sco_get_frame_size();
}
#endif
/* call app cback */
(*bta_hf_client_cb.p_cback)(event, (tBTA_HF_CLIENT_HDR *) &evt);
(*bta_hf_client_cb.p_cback)(event, &audio_stat);
}
#if (BTM_SCO_HCI_INCLUDED == TRUE )
@@ -217,7 +251,6 @@ static void bta_hf_client_sco_read_cback (UINT16 sco_idx, BT_HDR *p_data, tBTM_S
}
bta_hf_client_sco_co_in_data (p_data, status);
osi_free(p_data);
}
#endif /* BTM_SCO_HCI_INCLUDED */
@@ -307,7 +340,7 @@ void bta_hf_client_pkt_stat_nums(tBTA_HF_CLIENT_DATA *p_data)
**
** Function bta_hf_client_ci_sco_data
**
** Description Process the SCO data ready callin event
** Description Process the SCO data ready call in event
**
**
** Returns void
@@ -318,6 +351,281 @@ void bta_hf_client_ci_sco_data(tBTA_HF_CLIENT_DATA *p_data)
UNUSED(p_data);
bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CI_DATA_E);
}
/*******************************************************************************
**
** Function bta_hf_client_write_sco_data
**
** Description Write two SCO data buffers to specified instance
**
**
** Returns void
**
*******************************************************************************/
static void bta_hf_client_write_sco_data(BT_HDR *p_buf1, BT_HDR *p_buf2)
{
BTM_WriteScoData(bta_hf_client_cb.scb.sco_idx, p_buf1);
if (p_buf2 != NULL) {
BTM_WriteScoData(bta_hf_client_cb.scb.sco_idx, p_buf2);
}
}
/*******************************************************************************
**
** Function bta_hf_client_sco_data_send_cvsd
**
** Description Process SCO data of CVSD air mode
**
**
** Returns void
**
*******************************************************************************/
static void bta_hf_client_sco_data_send_cvsd(BT_HDR *p_buf, UINT8 out_pkt_len)
{
if (bta_hf_client_cb.scb.p_sco_data != NULL) {
/* the remaining data of last sending operation */
BT_HDR *p_buf_last = bta_hf_client_cb.scb.p_sco_data;
/* remaining data len should small than out_pkt_len */
assert(p_buf_last->len < out_pkt_len);
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + out_pkt_len);
if (p_buf2 == NULL) {
osi_free(p_buf);
osi_free(p_buf_last);
bta_hf_client_cb.scb.p_sco_data = NULL;
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
UINT8 *p_data = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data, (UINT8 *)(p_buf_last + 1) + p_buf_last->offset, p_buf_last->len);
if (p_buf->len + p_buf_last->len < out_pkt_len) {
memcpy(p_data + p_buf_last->len, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len);
p_buf2->len = p_buf->len + p_buf_last->len;
osi_free(p_buf);
osi_free(p_buf_last);
bta_hf_client_cb.scb.p_sco_data = p_buf2;
}
else {
UINT16 copy_len = out_pkt_len - p_buf_last->len;
memcpy(p_data + p_buf_last->len, (UINT8 *)(p_buf + 1) + p_buf->offset, copy_len);
p_buf2->len = out_pkt_len;
p_buf->offset += copy_len;
p_buf->len -= copy_len;
osi_free(p_buf_last);
bta_hf_client_cb.scb.p_sco_data = NULL;
bta_hf_client_write_sco_data(p_buf2, NULL);
if (p_buf->len == 0) {
osi_free(p_buf);
}
else {
/* Recursive call, this will only called once */
bta_hf_client_sco_data_send_cvsd(p_buf, out_pkt_len);
}
}
}
else if (p_buf->len < out_pkt_len) {
bta_hf_client_cb.scb.p_sco_data = p_buf;
}
else {
/* bta_hf_client_cb.scb.p_sco_data == NULL && p_buf->len >= out_pkt_len */
while (1) {
if (p_buf->len == out_pkt_len) {
bta_hf_client_write_sco_data(p_buf, NULL);
break;
}
else {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + out_pkt_len);
if (p_buf2 == NULL) {
osi_free(p_buf);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
UINT8 *p_data = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, out_pkt_len);
p_buf2->len = out_pkt_len;
p_buf->offset += out_pkt_len;
p_buf->len -= out_pkt_len;
bta_hf_client_write_sco_data(p_buf2, NULL);
}
if (p_buf->len < out_pkt_len) {
bta_hf_client_cb.scb.p_sco_data = p_buf;
break;
}
}
}
}
/*******************************************************************************
**
** Function bta_hf_client_sco_data_send_msbc
**
** Description Process SCO data of mSBC air mode
**
**
** Returns void
**
*******************************************************************************/
static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT8 out_pkt_len)
{
if (p_buf->len == BTA_HF_CLIENT_MSBC_FRAME_SIZE && p_buf->offset >= BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_H2_HEADER_LEN) {
/* add H2 header */
p_buf->offset -= BTA_HF_CLIENT_H2_HEADER_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
bta_hf_client_h2_header((UINT16 *)p_data);
/* add header len, add addition one bytes, the len is BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3 now */
p_buf->len += BTA_HF_CLIENT_H2_HEADER_LEN + 1;
if (out_pkt_len == BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3) {
/* mSBC frame can be send directly */
bta_hf_client_write_sco_data(p_buf, NULL);
}
else if (out_pkt_len == BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3) {
/* need to split into 2 sco packages for sending */
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3);
if (p_buf2 == NULL) {
/* free the first buff too */
osi_free(p_buf);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
return;
}
p_buf2->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
p_buf2->len = BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
memcpy(p_data2, p_data + BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3, BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3);
/* update the first packet len */
p_buf->len = BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
bta_hf_client_write_sco_data(p_buf, p_buf2);
}
else {
osi_free(p_buf);
APPL_TRACE_WARNING("%s, invalid out pkt len: %d", __FUNCTION__, out_pkt_len);
}
}
else if (p_buf->len != 0 && p_buf->len % BTA_HF_CLIENT_MSBC_FRAME_SIZE == 0) {
/* multiple mSBC frame in the buffer, or just one but offset is too small */
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
UINT16 total_len = p_buf->len;
if (out_pkt_len == BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3) {
while (total_len != 0) {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3);
if (p_buf2 == NULL) {
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
p_buf2->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
p_buf2->len = BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
bta_hf_client_h2_header((UINT16 *)p_data2);
p_data2 += BTA_HF_CLIENT_H2_HEADER_LEN;
memcpy(p_data2, p_data, BTA_HF_CLIENT_MSBC_FRAME_SIZE);
p_data += BTA_HF_CLIENT_MSBC_FRAME_SIZE;
total_len -= BTA_HF_CLIENT_MSBC_FRAME_SIZE;
bta_hf_client_write_sco_data(p_buf2, NULL);
}
}
else if (out_pkt_len == BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3) {
while (total_len != 0) {
BT_HDR *p_buf2 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3);
if (p_buf2 == NULL) {
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
BT_HDR *p_buf3 = osi_calloc(sizeof(BT_HDR) + BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3);
if (p_buf3 == NULL) {
/* free the first buff too */
osi_free(p_buf2);
APPL_TRACE_WARNING("%s, no memory", __FUNCTION__);
break;
}
/* build first packet, include H2 header */
p_buf2->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
p_buf2->len = BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data2 = (UINT8 *)(p_buf2 + 1) + p_buf2->offset;
bta_hf_client_h2_header((UINT16 *)p_data2);
p_data2 += BTA_HF_CLIENT_H2_HEADER_LEN;
memcpy(p_data2, p_data, BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 - BTA_HF_CLIENT_H2_HEADER_LEN);
p_data += BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 - BTA_HF_CLIENT_H2_HEADER_LEN;
total_len -= BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 - BTA_HF_CLIENT_H2_HEADER_LEN;
/* build second packet, not include header */
p_buf3->offset = BTA_HF_CLIENT_BUFF_OFFSET_MIN;
p_buf3->len = BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
UINT8 *p_data3 = (UINT8 *)(p_buf3 + 1) + p_buf3->offset;
memcpy(p_data3, p_data, BTA_HF_CLIENT_MSBC_FRAME_SIZE - BTA_HF_CLIENT_H2_HEADER_LEN - BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3);
p_data += BTA_HF_CLIENT_MSBC_FRAME_SIZE - BTA_HF_CLIENT_H2_HEADER_LEN - BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
total_len -= BTA_HF_CLIENT_MSBC_FRAME_SIZE - BTA_HF_CLIENT_H2_HEADER_LEN - BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3;
bta_hf_client_write_sco_data(p_buf2, p_buf3);
}
}
else {
APPL_TRACE_WARNING("%s, invalid out pkt len: %d", __FUNCTION__, out_pkt_len);
}
osi_free(p_buf);
}
else {
APPL_TRACE_WARNING("%s, unaccepted data len: %d", __FUNCTION__, p_buf->len);
osi_free(p_buf);
}
}
/*******************************************************************************
**
** Function bta_hf_client_sco_data_send
**
** Description Route SCO data to specific processing function based on air mode
**
**
** Returns void
**
*******************************************************************************/
void bta_hf_client_sco_data_send(tBTA_HF_CLIENT_DATA *p_data)
{
BT_HDR *p_buf = (BT_HDR *) p_data;
if (bta_hf_client_cb.scb.sco_state != BTA_HF_CLIENT_SCO_OPEN_ST) {
osi_free(p_data);
APPL_TRACE_WARNING("%s: SCO not open", __FUNCTION__);
return;
}
UINT8 out_pkt_len = bta_hf_client_cb.scb.out_pkt_len;
UINT8 air_mode = bta_hf_client_cb.scb.air_mode;
switch (air_mode)
{
case BTM_SCO_AIR_MODE_CVSD:
bta_hf_client_sco_data_send_cvsd(p_buf, out_pkt_len);
break;
case BTM_SCO_AIR_MODE_TRANSPNT:
bta_hf_client_sco_data_send_msbc(p_buf, out_pkt_len);
break;
default:
osi_free(p_buf);
APPL_TRACE_WARNING("%s: unsupported air mode: %d", __FUNCTION__, air_mode);
break;
}
}
/*******************************************************************************
**
** Function bta_hf_client_sco_data_free
**
** Description Free SCO data buffer
**
**
** Returns void
**
*******************************************************************************/
void bta_hf_client_sco_data_free(tBTA_HF_CLIENT_DATA *p_data)
{
/* just free the sco data buffer */
osi_free(p_data);
}
#endif
/*******************************************************************************
@@ -539,12 +847,9 @@ static void bta_hf_client_sco_event(UINT8 event)
APPL_TRACE_DEBUG("%s state: %d event: %d", __FUNCTION__,
bta_hf_client_cb.scb.sco_state, event);
#if (BTM_SCO_HCI_INCLUDED == TRUE )
tBTA_HF_CLIENT_SCB *p_scb = &bta_hf_client_cb.scb;
#if (BTM_SCO_HCI_INCLUDED == TRUE ) && (BTA_HFP_EXT_CODEC == FALSE)
BT_HDR *p_buf;
#endif
#if (BTM_SCO_HCI_INCLUDED == TRUE )
tBTA_HF_CLIENT_SCB *p_scb = &bta_hf_client_cb.scb;
if (event == BTA_HF_CLIENT_SCO_CI_DATA_E) {
UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE;
UINT16 len_to_send = 0;
@@ -890,6 +1195,11 @@ void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA *p_data)
bta_sys_sco_unuse(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
if (bta_hf_client_cb.scb.p_sco_data != NULL) {
osi_free(bta_hf_client_cb.scb.p_sco_data);
bta_hf_client_cb.scb.p_sco_data = NULL;
}
/* call app callback */
bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_CLOSE_EVT);

View File

@@ -44,6 +44,19 @@
#define BTA_HF_CLIENT_COLLISION_TIMER 2411
#endif
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/* 1 (offset can not be 0) + HCI_SCO_PREAMBLE */
#define BTA_HF_CLIENT_BUFF_OFFSET_MIN (1 + HCI_SCO_PREAMBLE_SIZE)
/* mSBC H2 header length */
#define BTA_HF_CLIENT_H2_HEADER_LEN 2
/* mSBC frame size not include H1/H2 header */
#define BTA_HF_CLIENT_MSBC_FRAME_SIZE 57
/* max user data len of sco packet type EV3 */
#define BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 30
/* max user data len of sco packet type 2-EV3 */
#define BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3 60
#endif
enum {
/* these events are handled by the state machine */
BTA_HF_CLIENT_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_HS),
@@ -65,6 +78,7 @@ enum {
BTA_HF_CLIENT_SEND_AT_CMD_EVT,
#if (BTM_SCO_HCI_INCLUDED == TRUE )
BTA_HF_CLIENT_CI_SCO_DATA_EVT,
BTA_HF_CLIENT_SCO_DATA_SEND_EVT,
BTA_HF_CLIENT_PKT_NUMS_GET_EVT,
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
BTA_HF_CLIENT_MAX_EVT,
@@ -138,7 +152,6 @@ typedef union {
tBTA_HF_CLIENT_RFC rfc;
tBTA_HF_CLIENT_DATA_VAL val;
tBTA_HF_CLIENT_PKT_STAT_GET pkt_stat;
} tBTA_HF_CLIENT_DATA;
/* type for each service control block */
@@ -158,6 +171,7 @@ typedef struct {
UINT16 sco_idx; /* SCO handle */
UINT8 sco_state; /* SCO state variable */
BOOLEAN sco_close_rfc; /* TRUE if also close RFCOMM after SCO */
BT_HDR *p_sco_data; /* remaining SCO data of last sending operation */
BOOLEAN retry_with_sco_only;
BOOLEAN deregister; /* TRUE if service shutting down */
BOOLEAN svc_conn; /* set to TRUE when service level connection is up */
@@ -312,5 +326,8 @@ extern void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data);
#if (BTM_SCO_HCI_INCLUDED == TRUE )
extern void bta_hf_client_pkt_stat_nums(tBTA_HF_CLIENT_DATA *p_data);
extern void bta_hf_client_ci_sco_data(tBTA_HF_CLIENT_DATA *p_data);
extern void bta_hf_client_h2_header(UINT16 *p_buf);
extern void bta_hf_client_sco_data_send(tBTA_HF_CLIENT_DATA *p_data);
extern void bta_hf_client_sco_data_free(tBTA_HF_CLIENT_DATA *p_data);
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
#endif /* #if (BTA_HF_INCLUDED == TRUE) */

View File

@@ -112,7 +112,7 @@ typedef UINT8 tBTA_AG_STATUS;
/* It is safe to use the same value as BTA_AG_HANDLE_ALL
* HANDLE_ALL is used for delivering indication
* SCO_NO_CHANGE is used for changing sco behavior
* They donot interfere with each other
* They do not interfere with each other
*/
#define BTA_AG_HANDLE_SCO_NO_CHANGE 0xFFFF
@@ -363,6 +363,13 @@ typedef struct
tBTA_AG_CHLD_FEAT chld_feat;
} tBTA_AG_CONN;
/* data associated with BTA_AG_AUDIO_OPEN_EVT, BTA_AG_AUDIO_CLOSE_EVT or BTA_AG_AUDIO_MSBC_OPEN_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
UINT16 preferred_frame_size;
} tBTA_AG_AUDIO_STAT;
/* data associated with AT command event */
typedef struct
{
@@ -427,6 +434,7 @@ typedef union
tBTA_AG_OPEN open;
tBTA_AG_CLOSE close;
tBTA_AG_CONN conn;
tBTA_AG_AUDIO_STAT audio_stat;
tBTA_AG_IND ind;
tBTA_AG_VAL val;
//add
@@ -619,6 +627,45 @@ void BTA_AgPktStatsNumsGet(UINT16 handle, UINT16 sync_conn_handle);
**
*******************************************************************************/
void BTA_AgCiData(UINT16 handle);
/*******************************************************************************
**
** Function BTA_AgAudioBuffAlloc
**
** Description Allocate an audio buffer with specific size, reserve enough
** space and offset for lower layer to send the buffer directly.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioBuffAlloc(UINT16 size, UINT8 **pp_buff, UINT8 **pp_data);
/*******************************************************************************
**
** Function BTA_AgAudioBuffFree
**
** Description Free an audio buffer.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioBuffFree(UINT8 *p_buf);
/*******************************************************************************
**
** Function BTA_AgAudioDataSend
**
** Description Send audio data to lower layer, whether success or not, buffer
** is consumed.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioDataSend(UINT16 handle, UINT8 *p_buff_start, UINT8 *p_data, UINT16 data_len);
#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */
#ifdef __cplusplus

View File

@@ -256,18 +256,19 @@ typedef UINT8 tBTA_AV_GET_TYPE;
#define BTA_AV_META_MSG_EVT 17 /* metadata messages */
#define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */
#define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */
#define BTA_AV_MEDIA_SINK_CFG_EVT 20 /* command to configure codec */
#define BTA_AV_MEDIA_CFG_EVT 20 /* command to configure codec */
#define BTA_AV_MEDIA_DATA_EVT 21 /* sending data to Media Task */
#define BTA_AV_SET_DELAY_VALUE_EVT 22 /* set delay reporting value */
#define BTA_AV_GET_DELAY_VALUE_EVT 23 /* get delay reporting value */
#define BTA_AV_SNK_PSC_CFG_EVT 24 /* Protocol service capabilities. */
#define BTA_AV_SEP_REG_EVT 25 /* stream endpoint registered */
/* still keep Cover Art event here if Cover Art feature not enabled */
#define BTA_AV_CA_STATUS_EVT 25 /* Cover Art Client status event */
#define BTA_AV_CA_DATA_EVT 26 /* Cover Art response body data */
#define BTA_AV_CA_STATUS_EVT 26 /* Cover Art Client status event */
#define BTA_AV_CA_DATA_EVT 27 /* Cover Art response body data */
/* Max BTA event */
#define BTA_AV_MAX_EVT 27
#define BTA_AV_MAX_EVT 28
/* function types for call-out functions */
@@ -341,6 +342,12 @@ typedef struct {
tBTA_AVRC_CO_FUNCTS *p_bta_avrc_cos;
} tBTA_AV_REGISTER;
/* Event associated with BTA_AV_SEP_REG_EVT */
typedef struct {
UINT8 seid;
tBTA_AV_STATUS reg_state;
} tBTA_AV_SEP_REG;
/* data associated with BTA_AV_OPEN_EVT */
#define BTA_AV_EDR_2MBPS 0x01
#define BTA_AV_EDR_3MBPS 0x02
@@ -354,6 +361,7 @@ typedef struct {
BOOLEAN starting;
tBTA_AV_EDR edr; /* 0, if peer device does not support EDR */
UINT8 sep; /* sep type of peer device */
UINT16 mtu;
} tBTA_AV_OPEN;
/* data associated with BTA_AV_CLOSE_EVT */
@@ -519,6 +527,7 @@ typedef union {
tBTA_AV_CHNL chnl;
tBTA_AV_ENABLE enable;
tBTA_AV_REGISTER registr;
tBTA_AV_SEP_REG sep_reg;
tBTA_AV_OPEN open;
tBTA_AV_CLOSE close;
tBTA_AV_START start;
@@ -657,6 +666,8 @@ void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name,
tBTA_AV_CO_FUNCTS *bta_av_cos, tBTA_AVRC_CO_FUNCTS *bta_avrc_cos,
UINT8 tsep);
void BTA_AvRegSEP(tBTA_AV_CHNL chnl, UINT8 idx, UINT8 tsep, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, tBTA_AV_DATA_CBACK *p_data_cback);
/*******************************************************************************
**
** Function BTA_AvDeregister

View File

@@ -194,6 +194,18 @@ extern UINT8 bta_av_sbc_cfg_for_cap(UINT8 *p_peer, tA2D_SBC_CIE *p_cap, tA2D_SBC
*******************************************************************************/
extern UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap);
/*******************************************************************************
**
** Function bta_av_sbc_cfg_in_external_codec_cap
**
** Description This function checks whether an SBC codec configuration
** is allowable for the given external codec capabilities.
**
** Returns 0 if ok, nonzero if error.
**
*******************************************************************************/
extern UINT8 bta_av_sbc_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap);
/*******************************************************************************
**
** Function bta_av_sbc_cfg_matches_cap

View File

@@ -188,6 +188,12 @@ typedef struct {
tBTA_HF_CLIENT_CHLD_FEAT chld_feat;
} tBTA_HF_CLIENT_CONN;
/* data associated with BTA_HF_CLIENT_AUDIO_XXX_EVT */
typedef struct {
tBTA_HF_CLIENT_HDR hdr;
UINT16 preferred_frame_size;
} tBTA_HF_CLIENT_AUDIO_STAT;
/* data associated with BTA_HF_CLIENT_IND_EVT event */
typedef struct {
tBTA_HF_CLIENT_HDR hdr;
@@ -251,6 +257,7 @@ typedef union {
tBTA_HF_CLIENT_REGISTER reg;
tBTA_HF_CLIENT_OPEN open;
tBTA_HF_CLIENT_CONN conn;
tBTA_HF_CLIENT_AUDIO_STAT audio_stat;
tBTA_HF_CLIENT_IND ind;
tBTA_HF_CLIENT_VAL val;
tBTA_HF_CLIENT_OPERATOR_NAME operator;
@@ -406,9 +413,68 @@ void BTA_HfClientSendAT(UINT16 handle, tBTA_HF_CLIENT_AT_CMD_TYPE at, UINT32 val
*******************************************************************************/
void BTA_HfClientPktStatsNumsGet(UINT16 sync_conn_handle);
/*******************************************************************************
**
** Function BTA_HfClientCiData
**
** Description Send SCO outgoing data ready event
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientCiData(void);
/*******************************************************************************
**
** Function BTA_HfClientAudioBuffAlloc
**
** Description Allocate an audio buffer with specific size, reserve enough
** space and offset for lower layer to send the buffer directly.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioBuffAlloc(UINT16 size, UINT8 **pp_buff, UINT8 **pp_data);
/*******************************************************************************
**
** Function BTA_HfClientAudioBuffFree
**
** Description Free an audio buffer.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioBuffFree(UINT8 *p_buf);
/*******************************************************************************
**
** Function BTA_HfClientAudioDataSend
**
** Description Send audio data to lower layer, whether success or not, buffer
** is consumed.
**
**
** Returns void
**
*******************************************************************************/
void BTA_HfClientAudioDataSend(UINT16 sync_conn_hdl, UINT8 *p_buff_start, UINT8 *p_data, UINT16 data_len);
#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */
/*******************************************************************************
**
** Function BTA_HfClientGetCbDataSize
**
** Description Get callback data size of specific event
**
**
** Returns void
**
*******************************************************************************/
int BTA_HfClientGetCbDataSize(tBTA_HF_CLIENT_EVT event);
#ifdef __cplusplus

View File

@@ -122,7 +122,6 @@ static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8
static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index);
/*******************************************************************************
**
** Function bta_av_co_cp_is_active
@@ -245,18 +244,29 @@ BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_
UINT16_TO_STREAM(p, BTA_AV_CP_SCMS_T_ID);
} while (0);
#endif
#if (BTC_AV_EXT_CODEC == TRUE)
/* for external codec, we get codec capability from BTA_AV */
bta_av_co_cb.codec_caps.id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps.info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_audio_codec_reset();
#else
/* Set up for SBC codec for SRC*/
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_caps, p_codec_info);
#endif
return TRUE;
} else if (tsep == AVDT_TSEP_SNK) {
#if (BTC_AV_EXT_CODEC == TRUE)
/* for external codec, we get codec capability from BTA_AV */
bta_av_co_cb.codec_caps.id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps.info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_audio_codec_reset();
#else
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_sink_caps, p_codec_info);
#endif
/* Codec is valid */
return TRUE;
} else {
@@ -387,7 +397,7 @@ void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
/* now try to build a preferred one */
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0) {
APPL_TRACE_DEBUG(" Cant parse src cap ret = %d", status);
APPL_TRACE_DEBUG(" Can't parse src cap ret = %d", status);
return ;
}
@@ -1062,12 +1072,12 @@ static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UIN
memcpy(p_codec_cfg, bta_av_co_cb.codec_cfg.info, BTA_AV_CO_SBC_MAX_BITPOOL_OFF + 1);
/* Update the bit pool boundaries with the codec capabilities */
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF];
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF];
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF] = BTA_AV_CO_MAX(p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF]);
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] = BTA_AV_CO_MIN(p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF], p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config : bitpool min %d, max %d",
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF],
p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
break;
default:
APPL_TRACE_ERROR("bta_av_co_audio_codec_build_config: unsupported codec id %d", bta_av_co_cb.codec_cfg.id);
@@ -1301,10 +1311,20 @@ static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer,
if (p_src_index) {
*p_src_index = index;
}
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps,
(tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps)) {
#if (BTC_AV_EXT_CODEC == TRUE)
tA2D_SBC_CIE cap_cie;
if (A2D_ParsSbcInfo(&cap_cie, bta_av_co_cb.codec_caps.info, TRUE) != A2D_SUCCESS) {
return FALSE;
}
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps, &cap_cie)) {
return TRUE;
}
#else
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps,
(tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps)) {
return TRUE;
}
#endif
break;
default:
@@ -1334,7 +1354,11 @@ static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_EXT_CODEC == TRUE)
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps.info);
#else
status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps);
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_M24:
@@ -1365,7 +1389,11 @@ static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_EXT_CODEC == TRUE)
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps.info);
#else
status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_caps);
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_M24:
@@ -1478,12 +1506,16 @@ void bta_av_co_audio_codec_reset(void)
osi_mutex_global_lock();
FUNC_TRACE();
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.codec_cfg.id = bta_av_co_cb.codec_caps.id;
bta_av_build_src_cfg(bta_av_co_cb.codec_cfg.info, bta_av_co_cb.codec_caps.info);
#else
/* Reset the current configuration to SBC */
bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC;
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, bta_av_co_cb.codec_cfg.info) != A2D_SUCCESS) {
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset A2D_BldSbcInfo failed");
}
#endif
osi_mutex_global_unlock();
}
@@ -1564,7 +1596,6 @@ BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_
/* The new config was correctly built */
bta_av_co_cb.codec_cfg = new_cfg;
/* Check all devices support it */
*p_status = BTC_AV_SUCCESS;
return bta_av_co_audio_codec_supported(p_status);
@@ -1666,13 +1697,16 @@ void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl)
** Returns Nothing
**
*******************************************************************************/
void bta_av_co_init(void)
void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps)
{
FUNC_TRACE();
/* Reset the control block */
memset(&bta_av_co_cb, 0, sizeof(bta_av_co_cb));
if (codec_caps) {
memcpy(&bta_av_co_cb.codec_caps, codec_caps, sizeof(tBTC_AV_CODEC_INFO));
}
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -1685,7 +1719,6 @@ void bta_av_co_init(void)
bta_av_co_audio_codec_reset();
}
/*******************************************************************************
**
** Function bta_av_co_peer_cp_supported
@@ -1729,7 +1762,7 @@ BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl)
** of our exported bitpool range. If set we will set the
** remote preference.
**
** Returns TRUE if config set, FALSE otherwize
** Returns TRUE if config set, FALSE otherwise
**
*******************************************************************************/

View File

@@ -49,7 +49,12 @@ void btc_a2dp_on_idle(void)
}
#endif // BTC_AV_SRC_INCLUDED
bta_av_co_init();
#if (BTC_AV_EXT_CODEC == TRUE)
tBTC_AV_CODEC_INFO *codec_caps = btc_av_codec_cap_get();
#else
tBTC_AV_CODEC_INFO *codec_caps = NULL;
#endif
bta_av_co_init(codec_caps);
#if BTC_AV_SINK_INCLUDED
if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) {
@@ -88,9 +93,11 @@ BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start)
ack = TRUE;
}
} else {
#if (BTC_AV_EXT_CODEC == FALSE)
/* we were remotely started, make sure codec
is setup before datapath is started */
btc_a2dp_source_setup_codec();
#endif
}
/* media task is autostarted upon a2dp audiopath connection */

View File

@@ -71,8 +71,10 @@ static void btc_a2dp_datapath_open(void)
/* Start the media task to encode SBC */
btc_a2dp_source_start_audio_req();
#if (BTC_AV_EXT_CODEC == FALSE)
/* make sure we update any changed sbc encoder params */
btc_a2dp_source_encoder_update();
#endif
}
#endif
#if (BTC_AV_SINK_INCLUDED == TRUE)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -30,12 +30,13 @@
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "oi_codec_sbc.h"
#include "oi_status.h"
#include "osi/future.h"
#include <assert.h>
#if (BTC_AV_SINK_INCLUDED == TRUE)
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE)
#include "oi_codec_sbc.h"
#include "oi_status.h"
/*****************************************************************************
** Constants
@@ -85,13 +86,6 @@ typedef struct {
void *param;
} a2dp_sink_task_evt_t;
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;
@@ -121,7 +115,7 @@ 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_inc_media(BT_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);
@@ -346,7 +340,7 @@ void btc_a2dp_sink_reset_decoder(UINT8 *p_av)
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
{
tBT_SBC_HDR *p_msg;
BT_HDR *p_msg;
int nb_of_msgs_to_process = 0;
if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) {
@@ -362,13 +356,12 @@ static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
return;
}
p_msg = (tBT_SBC_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0);
p_msg = (BT_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0);
if ( p_msg == NULL ) {
APPL_TRACE_ERROR("Insufficient data in que ");
break;
}
btc_a2dp_sink_handle_inc_media(p_msg);
osi_free(p_msg);
nb_of_msgs_to_process--;
}
APPL_TRACE_DEBUG(" Process Frames - ");
@@ -526,25 +519,27 @@ static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg
** Returns void
**
*******************************************************************************/
static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
{
UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
int count;
UINT32 pcmBytes, availPcmBytes;
OI_INT16 *pcmDataPointer = a2dp_sink_local_param.pcmData; /*Will be overwritten on next packet receipt*/
OI_STATUS status;
int num_sbc_frames = p_msg->num_frames_to_be_processed;
int num_sbc_frames = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
UINT32 sbc_frame_len = p_msg->len - 1;
availPcmBytes = sizeof(a2dp_sink_local_param.pcmData);
/* XXX: Check if the below check is correct, we are checking for peer to be sink when we are sink */
if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush)) {
APPL_TRACE_DEBUG(" State Changed happened in this tick ");
osi_free(p_msg);
return;
}
// ignore data if no one is listening
if (!btc_a2dp_control_get_datachnl_stat()) {
osi_free(p_msg);
return;
}
@@ -565,9 +560,9 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) {
pcmBytes = availPcmBytes;
status = OI_CODEC_SBC_DecodeFrame(&a2dp_sink_local_param.context, (const OI_BYTE **)&sbc_start_frame,
(OI_UINT32 *)&sbc_frame_len,
(OI_INT16 *)pcmDataPointer,
(OI_UINT32 *)&pcmBytes);
(OI_UINT32 *)&sbc_frame_len,
(OI_INT16 *)pcmDataPointer,
(OI_UINT32 *)&pcmBytes);
if (!OI_SUCCESS(status)) {
APPL_TRACE_ERROR("Decoding failure: %d\n", status);
break;
@@ -577,6 +572,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
p_msg->offset += (p_msg->len - 1) - sbc_frame_len;
p_msg->len = sbc_frame_len + 1;
}
osi_free(p_msg);
btc_a2d_data_cb_to_app((uint8_t *)a2dp_sink_local_param.pcmData, (sizeof(a2dp_sink_local_param.pcmData) - availPcmBytes));
}
@@ -663,35 +659,28 @@ static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype)
*******************************************************************************/
UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt)
{
tBT_SBC_HDR *p_msg;
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
osi_free(p_pkt);
return 0;
}
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enqueue */
osi_free(p_pkt);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ);
}
if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) {
osi_free(p_pkt);
APPL_TRACE_WARNING("Pkt dropped\n");
return fixed_queue_length(a2dp_sink_local_param.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(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
} else {
/* let caller deal with a failed allocation */
APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - ");
}
// p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_pkt, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ);
}
@@ -749,4 +738,4 @@ static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context)
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = NULL;
}
#endif /* BTC_AV_SINK_INCLUDED */
#endif /* (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE) */

View File

@@ -0,0 +1,286 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#include "common/bt_trace.h"
#include <string.h>
#include <stdint.h>
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#include "osi/thread.h"
#include "osi/fixed_queue.h"
#include "stack/a2d_api.h"
#include "bta/bta_av_api.h"
#include "bta/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/btc_manage.h"
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "osi/future.h"
#include <assert.h>
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE)
#define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (25)
#define BTC_A2DP_SNK_DATA_QUEUE_IDX (1)
enum {
BTC_A2DP_SINK_STATE_OFF = 0,
BTC_A2DP_SINK_STATE_ON = 1,
};
typedef struct {
BOOLEAN rx_flush; /* discards any incoming data when true */
struct osi_event *data_ready_event;
fixed_queue_t *audio_rx_q;
} tBTC_A2DP_SINK_CB;
typedef struct {
uint16_t expected_seq_num;
bool seq_num_recount;
} a2dp_sink_media_pkt_seq_num_t;
typedef struct {
tBTC_A2DP_SINK_CB btc_aa_snk_cb;
osi_thread_t *btc_aa_snk_task_hdl;
a2dp_sink_media_pkt_seq_num_t media_pkt_seq_num;
} a2dp_sink_local_param_t;
static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF;
static esp_a2d_sink_audio_data_cb_t bt_aa_snk_audio_data_cb = NULL;
#if A2D_DYNAMIC_MEMORY == FALSE
static a2dp_sink_local_param_t a2dp_sink_local_param;
#else
static a2dp_sink_local_param_t *a2dp_sink_local_param_ptr;
#define a2dp_sink_local_param (*a2dp_sink_local_param_ptr)
#endif ///A2D_DYNAMIC_MEMORY == FALSE
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context);
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg);
void btc_a2dp_sink_reg_audio_data_cb(esp_a2d_sink_audio_data_cb_t callback)
{
bt_aa_snk_audio_data_cb = callback;
}
static inline void btc_a2d_audio_data_cb_to_app(uint16_t conn_hdl, uint8_t *buf, uint8_t *data, uint16_t len, uint16_t number_frame, uint32_t timestamp)
{
if (bt_aa_snk_audio_data_cb) {
/* AVDT media packet offset is larger than sizeof(esp_a2d_audio_buff_t), it safe to do this */
esp_a2d_audio_buff_t *audio_buff = (esp_a2d_audio_buff_t *)buf;
audio_buff->buff_size = len;
audio_buff->number_frame = number_frame;
audio_buff->data_len = len;
audio_buff->data = data;
audio_buff->timestamp = timestamp;
bt_aa_snk_audio_data_cb(conn_hdl, audio_buff);
}
else {
osi_free(buf);
}
}
static void btc_a2dp_sink_rx_flush(void)
{
while (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
osi_free(fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, 0));
}
}
bool btc_a2dp_sink_startup(void)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_OFF) {
APPL_TRACE_ERROR("a2dp sink already start up");
return false;
}
#if A2D_DYNAMIC_MEMORY == TRUE
if ((a2dp_sink_local_param_ptr = (a2dp_sink_local_param_t *)osi_malloc(sizeof(a2dp_sink_local_param_t))) == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
return false;
}
memset((void *)a2dp_sink_local_param_ptr, 0, sizeof(a2dp_sink_local_param_t));
#endif
a2dp_sink_local_param.btc_aa_snk_task_hdl = btc_get_current_thread();
struct osi_event *data_event = osi_event_create(btc_a2dp_sink_data_ready, NULL);
assert (data_event != NULL);
osi_event_bind(data_event, a2dp_sink_local_param.btc_aa_snk_task_hdl, BTC_A2DP_SNK_DATA_QUEUE_IDX);
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = data_event;
a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q = fixed_queue_new(QUEUE_SIZE_MAX);
btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON;
btc_a2dp_control_init();
return true;
}
void btc_a2dp_sink_shutdown(void)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON) {
APPL_TRACE_ERROR("a2dp sink already shutdown");
return;
}
btc_a2dp_control_set_datachnl_stat(FALSE);
btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF;
btc_a2dp_control_cleanup();
fixed_queue_free(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, osi_free_func);
a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q = NULL;
osi_event_delete(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event);
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = NULL;
a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL;
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_sink_local_param_ptr);
a2dp_sink_local_param_ptr = NULL;
#endif
}
void btc_a2dp_sink_on_idle(void)
{
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av)
{
UNUSED(p_av);
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_control_set_datachnl_stat(FALSE);
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av)
{
UNUSED(p_av);
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_set_rx_flush(BOOLEAN enable)
{
if (enable == FALSE) {
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = 0x1;
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = true;
}
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = enable;
btc_a2dp_sink_rx_flush();
}
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
{
BT_HDR *p_msg;
int nb_of_msgs_to_process = 0;
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
return;
}
if (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) {
btc_a2dp_sink_rx_flush();
return;
}
nb_of_msgs_to_process = fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
APPL_TRACE_DEBUG("%s nb msgs:%d", __func__, nb_of_msgs_to_process);
while (nb_of_msgs_to_process > 0) {
p_msg = (BT_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, 0);
if ( p_msg == NULL ) {
APPL_TRACE_ERROR("%s insufficient data in queue", __func__);
break;
}
btc_a2dp_sink_handle_inc_media(p_msg);
nb_of_msgs_to_process--;
}
if (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
}
}
}
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
{
UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
int num_sbc_frames = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
UINT32 sbc_frame_len = p_msg->len - 1;
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush) {
osi_free(p_msg);
return;
}
/* ignore data if no one is listening */
if (!btc_a2dp_control_get_datachnl_stat()) {
osi_free(p_msg);
return;
}
if (p_msg->layer_specific != a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num) {
/* Because the sequence number of some devices is not recounted */
if (!a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount ||
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num != 0x1) {
APPL_TRACE_WARNING("Sequence numbers error, recv:0x%x, expect:0x%x, recount:0x%x",
p_msg->layer_specific, a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num,
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount);
}
}
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = p_msg->layer_specific + 1;
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = false;
APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
UINT32 timestamp = *((UINT32 *) (p_msg + 1));
UINT16 conn_hdl = btc_a2d_conn_handle_get();
btc_a2d_audio_data_cb_to_app(conn_hdl, (uint8_t *)p_msg, sbc_start_frame, sbc_frame_len, num_sbc_frames, timestamp);
/* dont free p_msg here */
}
UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
osi_free(p_pkt);
return 0;
}
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) {
/* Flush enabled, do not enqueue */
osi_free(p_pkt);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) {
osi_free(p_pkt);
APPL_TRACE_WARNING("Pkt dropped\n");
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + ");
// p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, p_pkt, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -36,11 +36,12 @@
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "sbc_encoder.h"
#include "osi/future.h"
#include <assert.h>
#if BTC_AV_SRC_INCLUDED
#if (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE)
#include "sbc_encoder.h"
/*****************************************************************************
** BQB global variables
@@ -83,12 +84,6 @@ enum {
#define BTC_MEDIA_AA_BUF_SIZE (4096+16)
#if (BTA_AV_CO_CP_SCMS_T == TRUE)
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
#else
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
#endif
#ifndef BTC_MEDIA_BITRATE_STEP
#define BTC_MEDIA_BITRATE_STEP 5
#endif
@@ -170,6 +165,10 @@ typedef struct {
UINT64 last_frame_us;
} a2dp_source_local_param_t;
static BOOLEAN btc_a2dp_source_stop_audio_req(void);
static BOOLEAN btc_a2dp_source_tx_flush_req(void);
static BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg);
static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context);
static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context);
static void btc_a2dp_source_flush_q(fixed_queue_t *p_q);
@@ -226,11 +225,6 @@ static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t
** BTC ADAPTATION
*****************************************************************************/
bool btc_a2dp_source_is_streaming(void)
{
return a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE;
}
bool btc_a2dp_source_is_task_shutting_down(void)
{
return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN;
@@ -505,7 +499,7 @@ BOOLEAN btc_a2dp_source_start_audio_req(void)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_stop_audio_req(void)
static BOOLEAN btc_a2dp_source_stop_audio_req(void)
{
/*
* Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to
@@ -536,7 +530,7 @@ BOOLEAN btc_a2dp_source_stop_audio_req(void)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
static BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
{
tBTC_MEDIA_INIT_AUDIO *p_buf;
if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO)))) {
@@ -559,7 +553,7 @@ BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg)
static BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg)
{
tBTC_MEDIA_UPDATE_AUDIO *p_buf;
if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_UPDATE_AUDIO)))) {
@@ -1451,6 +1445,7 @@ static void btc_a2dp_source_prep_2_send(UINT8 nb_frame)
*******************************************************************************/
static void btc_a2dp_source_send_aa_frame(void)
{
/* if external codec is used, skip generate audio frame */
UINT8 nb_frame_2_send;
/* get the number of frame to send */
@@ -1461,8 +1456,8 @@ static void btc_a2dp_source_send_aa_frame(void)
btc_a2dp_source_prep_2_send(nb_frame_2_send);
}
/* send it */
BTC_TRACE_VERBOSE("%s: send %d frames", __FUNCTION__, nb_frame_2_send);
BTC_TRACE_VERBOSE("%s: send %d new frames", __FUNCTION__, nb_frame_2_send);
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
}
@@ -1539,8 +1534,8 @@ static void btc_a2dp_source_aa_stop_tx(void)
when the DUT and the remote device issue SUSPEND simultaneously
and due to the processing of the SUSPEND request from the remote,
the media path is torn down. If the A2DP HAL happens to wait
for ACK for the initiated SUSPEND, it would never receive it casuing
a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed
for ACK for the initiated SUSPEND, it would never receive it causing
a block/wait. Due to this acknowledgement, the A2DP HAL is guaranteed
to get the ACK for any pending command in such cases. */
if (send_ack) {
@@ -1647,4 +1642,4 @@ static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context)
a2dp_source_local_param.btc_aa_src_cb.poll_data = NULL;
}
#endif /* BTC_AV_INCLUDED */
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE) */

View File

@@ -0,0 +1,235 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#include "common/bt_trace.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "osi/allocator.h"
#include "osi/fixed_queue.h"
#include "stack/a2d_api.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_ci.h"
#include "btc/btc_manage.h"
#include "btc/btc_common.h"
#include "btc_av_co.h"
#include "btc_a2dp.h"
#include "btc_a2dp_control.h"
#include "btc_a2dp_source.h"
#include "btc_av.h"
#include "esp_a2dp_api.h"
#include <assert.h>
#if (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE)
#define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27)
enum {
BTC_A2DP_SOURCE_STATE_OFF = 0,
BTC_A2DP_SOURCE_STATE_ON = 1,
};
typedef struct {
BOOLEAN stream_started;
BOOLEAN tx_flush;
fixed_queue_t *audio_tx_q;
} a2dp_source_local_param_t;
static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
#if A2D_DYNAMIC_MEMORY == FALSE
static a2dp_source_local_param_t a2dp_source_local_param;
#else
static a2dp_source_local_param_t *a2dp_source_local_param_ptr;
#define a2dp_source_local_param (*a2dp_source_local_param_ptr)
#endif ///A2D_DYNAMIC_MEMORY == FALSE
static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP);
if (btc_aa_cb) {
btc_aa_cb(event, param);
}
}
static void btc_a2dp_source_tx_flush(void)
{
while (!fixed_queue_is_empty(a2dp_source_local_param.audio_tx_q)) {
osi_free(fixed_queue_dequeue(a2dp_source_local_param.audio_tx_q, 0));
}
}
static void btc_a2dp_source_tx_stop(void)
{
if (a2dp_source_local_param.stream_started == TRUE) {
a2dp_source_local_param.stream_started = FALSE;
/* ack to command */
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
}
/* flush tx queue */
btc_a2dp_source_tx_flush();
a2dp_source_local_param.tx_flush = FALSE;
}
BOOLEAN btc_a2dp_source_enqueue_audio_frame(BT_HDR *p_buf)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON) {
APPL_TRACE_WARNING("%s source not start up", __func__);
return FALSE;
}
if (a2dp_source_local_param.tx_flush) {
APPL_TRACE_WARNING("%s try to send data when tx flush enable", __func__);
return FALSE;
}
if (fixed_queue_length(a2dp_source_local_param.audio_tx_q) > MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ) {
APPL_TRACE_WARNING("%s audio tx queue overflow: %d", __func__, fixed_queue_length(a2dp_source_local_param.audio_tx_q));
return FALSE;
}
fixed_queue_enqueue(a2dp_source_local_param.audio_tx_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
return TRUE;
}
void btc_source_report_delay_value(UINT16 delay_value)
{
esp_a2d_cb_param_t param;
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){
return;
}
param.a2d_report_delay_value_stat.delay_value = delay_value;
btc_aa_cb_to_app(ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, &param);
}
BOOLEAN btc_a2dp_source_start_audio_req(void)
{
a2dp_source_local_param.stream_started = TRUE;
return TRUE;
}
BT_HDR *btc_a2dp_source_audio_readbuf(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON || a2dp_source_local_param.tx_flush){
return NULL;
}
return fixed_queue_dequeue(a2dp_source_local_param.audio_tx_q, 0);
}
void btc_a2dp_source_set_tx_flush(BOOLEAN enable)
{
a2dp_source_local_param.tx_flush = enable;
if (enable) {
btc_a2dp_source_tx_flush();
}
}
void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av)
{
/* check for status failures */
if (p_av->status != BTA_AV_SUCCESS) {
if (p_av->initiator == TRUE) {
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
}
}
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av)
{
/* allow using this api for other than suspend */
if (p_av != NULL) {
if (p_av->status != BTA_AV_SUCCESS) {
if (p_av->initiator) {
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
}
return;
}
}
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
void btc_a2dp_source_on_idle(void)
{
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
bool btc_a2dp_source_is_task_shutting_down(void)
{
/* always return false, remove this api when internal codec is remove */
return false;
}
bool btc_a2dp_source_startup(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_OFF) {
APPL_TRACE_ERROR("%s A2DP source already start up", __func__);
return false;
}
#if A2D_DYNAMIC_MEMORY == TRUE
if ((a2dp_source_local_param_ptr = (a2dp_source_local_param_t *)osi_malloc(sizeof(a2dp_source_local_param_t))) == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
return false;
}
memset((void *)a2dp_source_local_param_ptr, 0, sizeof(a2dp_source_local_param_t));
#endif
btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON;
a2dp_source_local_param.audio_tx_q = fixed_queue_new(QUEUE_SIZE_MAX);
if(a2dp_source_local_param.audio_tx_q == NULL) {
goto error_exit;
}
btc_a2dp_control_init();
return true;
error_exit:;
APPL_TRACE_ERROR("%s A2DP source start up failed", __func__);
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_source_local_param_ptr);
a2dp_source_local_param_ptr = NULL;
#endif
return false;
}
void btc_a2dp_source_shutdown(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON) {
APPL_TRACE_ERROR("%s A2DP source already shutdown", __func__);
return;
}
btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
btc_a2dp_control_cleanup();
fixed_queue_free(a2dp_source_local_param.audio_tx_q, osi_free_func);
a2dp_source_local_param.audio_tx_q = NULL;
a2dp_source_local_param.tx_flush = FALSE;
a2dp_source_local_param.stream_started = FALSE;
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_source_local_param_ptr);
a2dp_source_local_param_ptr = NULL;
#endif
}
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,6 +22,7 @@
#include "btc/btc_common.h"
#include "btc/btc_manage.h"
#include "btc_av.h"
#include "btc_av_co.h"
#include "btc_avrc.h"
#include "btc/btc_util.h"
#include "btc/btc_profile_queue.h"
@@ -46,6 +47,9 @@ bool g_a2dp_source_ongoing_deinit;
bool g_a2dp_sink_ongoing_deinit;
/* reserve some bytes for media payload header (currently, only SBC, 1 byte) */
#define BTC_AV_AUDIO_MTU_RESERVE 1
/*****************************************************************************
** Constants & Macros
******************************************************************************/
@@ -74,6 +78,9 @@ typedef enum {
#define BTC_AV_FLAG_PENDING_START 0x4
#define BTC_AV_FLAG_PENDING_STOP 0x8
#define BTC_AV_SBC_CIE_OFFSET 3
#define BTC_AV_SBC_CIE_LEN 4
/*****************************************************************************
** Local type definitions
******************************************************************************/
@@ -81,11 +88,13 @@ typedef enum {
typedef struct {
int service_id;
tBTA_AV_HNDL bta_handle;
UINT16 mtu;
bt_bdaddr_t peer_bda;
btc_sm_handle_t sm_handle;
UINT8 flags;
tBTA_AV_EDR edr;
UINT8 peer_sep; /* sep type of peer device */
tBTC_AV_CODEC_INFO codec_caps;
#if BTC_AV_SRC_INCLUDED
osi_alarm_t *tle_av_open_on_rc;
#endif /* BTC_AV_SRC_INCLUDED */
@@ -170,6 +179,7 @@ static void btc_av_event_free_data(btc_msg_t *msg);
extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
extern tBTA_AVRC_CO_FUNCTS bta_avrc_cos;
extern tA2D_SBC_CIE btc_av_sbc_default_config;
/*****************************************************************************
** Local helper functions
******************************************************************************/
@@ -222,7 +232,7 @@ UNUSED_ATTR static const char *dump_av_sm_event_name(btc_av_sm_event_t event)
CASE_RETURN_STR(BTC_AV_DISCONNECT_REQ_EVT)
CASE_RETURN_STR(BTC_AV_START_STREAM_REQ_EVT)
CASE_RETURN_STR(BTC_AV_SUSPEND_STREAM_REQ_EVT)
CASE_RETURN_STR(BTC_AV_SINK_CONFIG_REQ_EVT)
CASE_RETURN_STR(BTC_AV_CONFIG_EVT)
default: return "UNKNOWN_EVENT";
}
}
@@ -263,7 +273,7 @@ static void btc_initiate_av_open_tmr_hdlr(void *arg)
/*****************************************************************************
** Static functions
******************************************************************************/
static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, int disc_rsn)
static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, uint16_t mtu, int disc_rsn)
{
// todo: add callback for SRC
esp_a2d_cb_param_t param;
@@ -272,8 +282,15 @@ static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bda
param.conn_stat.state = state;
if (bd_addr) {
memcpy(param.conn_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
if (memcmp(bd_addr, &(btc_av_cb.peer_bda), sizeof(bt_bdaddr_t)) == 0) {
param.conn_stat.conn_hdl = btc_av_cb.bta_handle;
}
}
if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
param.conn_stat.audio_mtu = mtu;
btc_av_cb.mtu = mtu;
}
else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
param.conn_stat.disc_rsn = (disc_rsn == 0) ? ESP_A2D_DISC_RSN_NORMAL :
ESP_A2D_DISC_RSN_ABNORMAL;
}
@@ -289,6 +306,9 @@ static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_
param.audio_stat.state = state;
if (bd_addr) {
memcpy(param.audio_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
if (memcmp(bd_addr, &(btc_av_cb.peer_bda), sizeof(bt_bdaddr_t)) == 0) {
param.audio_stat.conn_hdl = btc_av_cb.bta_handle;
}
}
btc_a2d_cb_to_app(ESP_A2D_AUDIO_STATE_EVT, &param);
}
@@ -328,6 +348,12 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_REGISTER_EVT:
btc_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl;
break;
case BTA_AV_SEP_REG_EVT:
param.a2d_sep_reg_stat.seid = ((tBTA_AV *)p_data)->sep_reg.seid;
param.a2d_sep_reg_stat.reg_state = (((tBTA_AV *)p_data)->sep_reg.reg_state == BTA_AV_SUCCESS) ? ESP_A2D_SEP_REG_SUCCESS :
ESP_A2D_SEP_REG_FAIL;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
break;
case BTA_AV_PENDING_EVT:
case BTC_AV_CONNECT_REQ_EVT: {
@@ -358,7 +384,7 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data)
case BTC_AV_DISCONNECT_REQ_EVT:
BTC_TRACE_WARNING("No Link At All.");
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &((btc_av_disconn_req_t *)p_data)->target_bda, 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &((btc_av_disconn_req_t *)p_data)->target_bda, 0, 0);
break;
case BTA_AV_RC_OPEN_EVT:
@@ -448,7 +474,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
switch (event) {
case BTC_SM_ENTER_EVT:
/* inform the application that we are entering connecting state */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0, 0);
break;
case BTC_SM_EXIT_EVT:
@@ -456,7 +482,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_REJECT_EVT:
BTC_TRACE_WARNING(" Received BTA_AV_REJECT_EVT \n");
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0, 0);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
break;
@@ -464,13 +490,14 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
tBTA_AV *p_bta_data = (tBTA_AV *)p_data;
esp_a2d_connection_state_t conn_stat;
btc_sm_state_t av_state;
uint16_t mtu = 0;
BTC_TRACE_DEBUG("status:%d, edr 0x%x, peer sep %d\n", p_bta_data->open.status,
p_bta_data->open.edr, p_bta_data->open.sep);
if (p_bta_data->open.status == BTA_AV_SUCCESS) {
btc_av_cb.edr = p_bta_data->open.edr;
btc_av_cb.peer_sep = p_bta_data->open.sep;
mtu = p_bta_data->open.mtu - BTC_AV_AUDIO_MTU_RESERVE;
conn_stat = ESP_A2D_CONNECTION_STATE_CONNECTED;
av_state = BTC_AV_STATE_OPENED;
} else {
@@ -480,7 +507,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
av_state = BTC_AV_STATE_IDLE;
}
/* inform the application of the event */
btc_report_connection_state(conn_stat, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(conn_stat, &(btc_av_cb.peer_bda), mtu, 0);
/* change state to open/idle based on the status */
btc_sm_change_state(btc_av_cb.sm_handle, av_state);
@@ -502,13 +529,12 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
btc_queue_advance();
} break;
case BTC_AV_SINK_CONFIG_REQ_EVT: {
if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
esp_a2d_cb_param_t param;
memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, &param);
}
case BTC_AV_CONFIG_EVT: {
esp_a2d_cb_param_t param;
param.audio_cfg.conn_hdl = btc_av_cb.bta_handle;
memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, &param);
} break;
case BTC_AV_CONNECT_REQ_EVT:
@@ -520,7 +546,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
break;
} else {
BTC_TRACE_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0, 0);
btc_queue_advance();
break;
}
@@ -623,7 +649,7 @@ static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_CLOSE_EVT: {
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), close->disc_rsn);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0, close->disc_rsn);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
break;
}
@@ -693,7 +719,9 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
case BTC_AV_START_STREAM_REQ_EVT:
#if BTC_AV_SRC_INCLUDED
if (btc_av_cb.peer_sep != AVDT_TSEP_SRC) {
#if (BTC_AV_EXT_CODEC == FALSE)
btc_a2dp_source_setup_codec();
#endif
}
#endif /* BTC_AV_SRC_INCLUDED */
BTA_AvStart();
@@ -759,7 +787,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
}
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0, 0);
break;
case BTA_AV_CLOSE_EVT: {
@@ -767,7 +795,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
btc_a2dp_on_stopped(NULL);
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnected */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0,
close->disc_rsn);
if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) {
@@ -804,7 +832,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
} else {
BTC_TRACE_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED,
(bt_bdaddr_t *)p_data, ESP_A2D_DISC_RSN_NORMAL);
(bt_bdaddr_t *)p_data, 0, ESP_A2D_DISC_RSN_NORMAL);
}
btc_queue_advance();
break;
@@ -917,7 +945,7 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data)
}
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0, 0);
/* wait in closing state until fully closed */
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_CLOSING);
@@ -982,7 +1010,7 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data)
btc_a2dp_on_stopped(NULL);
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnected */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0,
close->disc_rsn);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
@@ -1337,7 +1365,7 @@ static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data)
}
#if BTC_AV_SINK_INCLUDED
static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
btc_sm_state_t state;
UINT8 que_len;
@@ -1351,13 +1379,16 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
que_len = btc_a2dp_sink_enque_buf((BT_HDR *)p_data);
BTC_TRACE_DEBUG(" Packets in Que %d\n", que_len);
} else {
osi_free(p_data);
return;
}
}
if (event == BTA_AV_MEDIA_SINK_CFG_EVT) {
if (event == BTA_AV_MEDIA_CFG_EVT) {
#if (BTC_AV_EXT_CODEC == FALSE)
/* send a command to BT Media Task */
btc_a2dp_sink_reset_decoder((UINT8 *)p_data);
#endif
/* currently only supports SBC */
a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
@@ -1367,11 +1398,11 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SINK_CONFIG_REQ_EVT;
msg.act = BTC_AV_CONFIG_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);
memcpy(&arg.mcc.cie.sbc_info, (uint8_t *)p_data + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
@@ -1380,7 +1411,7 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
UNUSED(que_len);
}
#else
static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
UNUSED(event);
UNUSED(p_data);
@@ -1388,6 +1419,82 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
}
#endif
#if BTC_AV_SRC_INCLUDED
static void bte_av_media_source_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
tA2D_STATUS a2d_status;
tA2D_SBC_CIE sbc_cie;
if (event == BTA_AV_MEDIA_CFG_EVT) {
/* currently only supports 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_CONFIG_EVT;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_SBC;
memcpy(&arg.mcc.cie.sbc_info, (uint8_t *)p_data + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsSbcInfo fail:%d\n", a2d_status);
}
}
}
#else
static void bte_av_media_source_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
UNUSED(event);
UNUSED(p_data);
BTC_TRACE_WARNING("%s : event %u\n", __func__, event);
}
#endif
#if (BTC_AV_EXT_CODEC == TRUE)
tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void)
{
return &btc_av_cb.codec_caps;
}
static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc)
{
tBTA_AV_DATA_CBACK *p_data_cback = NULL;
esp_a2d_cb_param_t param;
param.a2d_sep_reg_stat.seid = seid;
if (btc_av_cb.sm_handle == NULL || btc_sm_get_state(btc_av_cb.sm_handle) != BTC_AV_STATE_IDLE) {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_INVALID_STATE;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: try to reg sep when a2dp not init or connected", __func__);
}
if (mcc->type == ESP_A2D_MCT_SBC) {
A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, btc_av_cb.codec_caps.info);
/* overwrite sbc cie */
memcpy(btc_av_cb.codec_caps.info + A2D_SBC_CIE_OFF, &mcc->cie, A2D_SBC_CIE_LEN);
if (tsep == AVDT_TSEP_SNK) {
p_data_cback = bte_av_media_sink_callback;
}
else {
p_data_cback = bte_av_media_source_callback;
}
BTA_AvRegSEP(BTA_AV_CHNL_AUDIO, seid, tsep, BTA_AV_CODEC_SBC, btc_av_cb.codec_caps.info, p_data_cback);
}
else {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_UNSUPPORTED;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: unsupported codec type %d", __func__, mcc->type);
}
}
#endif
/*******************************************************************************
**
** Function btc_av_execute_service
@@ -1399,7 +1506,15 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
*******************************************************************************/
bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep)
{
tBTA_AV_DATA_CBACK *p_data_cback = NULL;
if (b_enable) {
if (tsep == AVDT_TSEP_SNK) {
p_data_cback = bte_av_media_sink_callback;
}
else {
p_data_cback = bte_av_media_source_callback;
}
/* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
* handle this request in order to allow incoming connections to succeed.
* We need to put this back once support for this is added */
@@ -1413,11 +1528,11 @@ bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep)
BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_DELAY_RPT,
bte_av_callback);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, &bta_avrc_cos, tsep);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, p_data_cback, &bta_av_a2d_cos, &bta_avrc_cos, tsep);
} else {
BTC_TRACE_WARNING("A2DP Enable without AVRC")
BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_DELAY_RPT, bte_av_callback);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, NULL, tsep);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, p_data_cback, &bta_av_a2d_cos, NULL, tsep);
}
} else {
BTA_AvDeregister(btc_av_cb.bta_handle);
@@ -1476,6 +1591,11 @@ BOOLEAN btc_av_is_connected(void)
return ((state == BTC_AV_STATE_OPENED) || (state == BTC_AV_STATE_STARTED));
}
BOOLEAN btc_av_is_started(void)
{
return ((btc_sm_get_state(btc_av_cb.sm_handle) == BTC_AV_STATE_STARTED));
}
/*******************************************************************************
*
* Function btc_av_get_service_id
@@ -1548,15 +1668,17 @@ 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;
}
case BTC_AV_SINK_API_INIT_EVT: {
btc_a2d_sink_init();
// todo: callback to application
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SINK_API_REG_SEP_EVT: {
btc_av_reg_sep(AVDT_TSEP_SNK, arg->reg_sep.seid, &arg->reg_sep.mcc);
break;
}
#endif
case BTC_AV_SINK_API_DEINIT_EVT: {
btc_a2d_sink_deinit();
// todo: callback to application
@@ -1574,10 +1696,17 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req);
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT: {
btc_a2dp_sink_reg_audio_data_cb(arg->audio_data_cb);
break;
}
#else
case BTC_AV_SINK_API_REG_DATA_CB_EVT: {
btc_a2dp_sink_reg_data_cb(arg->data_cb);
break;
}
#endif
case BTC_AV_SINK_API_SET_DELAY_VALUE_EVT: {
btc_a2d_sink_set_delay_value(arg->delay_value);
break;
@@ -1592,6 +1721,12 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_a2d_src_init();
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SRC_API_REG_SEP_EVT: {
btc_av_reg_sep(AVDT_TSEP_SRC, arg->reg_sep.seid, &arg->reg_sep.mcc);
break;
}
#endif
case BTC_AV_SRC_API_DEINIT_EVT: {
btc_a2d_src_deinit();
break;
@@ -1607,10 +1742,12 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req);
break;
}
#if (BTC_AV_EXT_CODEC == FALSE)
case BTC_AV_SRC_API_REG_DATA_CB_EVT: {
btc_a2dp_src_reg_data_cb(arg->src_data_cb);
break;
}
#endif
#endif /* BTC_AV_SRC_INCLUDED */
case BTC_AV_API_MEDIA_CTRL_EVT: {
btc_a2dp_control_media_ctrl(arg->ctrl);
@@ -1734,6 +1871,56 @@ static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda)
return btc_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, remote_bda, connect_int);
}
BOOLEAN btc_a2d_src_audio_mtu_check(uint16_t data_len)
{
return (data_len <= btc_av_cb.mtu);
}
bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
#if (BTC_AV_EXT_CODEC == TRUE)
if (conn_hdl != btc_av_cb.bta_handle) {
return BT_STATUS_FAIL;
}
BT_HDR *p_buf = (BT_HDR *)audio_buf;
assert(audio_buf->data - (UINT8 *)(p_buf + 1) >= BTC_AUDIO_BUFF_OFFSET);
/* since p_buf and audio_buf point to the same memory, backup those value before modify p_buf */
uint16_t number_frame = audio_buf->number_frame;
uint16_t data_len = audio_buf->data_len;
uint32_t timestamp = audio_buf->timestamp;
p_buf->offset = audio_buf->data - (UINT8 *)(p_buf + 1);
p_buf->layer_specific = number_frame;
p_buf->len = data_len;
*((UINT32 *) (p_buf + 1)) = timestamp;
if (btc_a2dp_source_enqueue_audio_frame(p_buf)) {
return BT_STATUS_SUCCESS;
}
#endif
return BT_STATUS_FAIL;
}
#endif /* BTC_AV_SRC_INCLUDED */
uint16_t btc_a2d_conn_handle_get(void)
{
return btc_av_cb.bta_handle;
}
void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data)
{
/* todo */
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + BTC_AUDIO_BUFF_OFFSET + size);
if (p_buf != NULL) {
*pp_buff = (uint8_t *)p_buf;
*pp_data = (uint8_t *)(p_buf + 1) + BTC_AUDIO_BUFF_OFFSET;
}
}
void btc_av_audio_buff_free(uint8_t *p_buf)
{
osi_free(p_buf);
}
#endif /* #if BTC_AV_INCLUDED */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -64,6 +64,7 @@ typedef struct {
typedef struct {
/* Connected peer information */
tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS];
tBTC_AV_CODEC_INFO codec_caps;
/* Current codec configuration - access to this variable must be protected */
tBTC_AV_CODEC_INFO codec_cfg;
tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */
@@ -189,8 +190,7 @@ void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl);
** Returns Nothing
**
*******************************************************************************/
void bta_av_co_init(void);
void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps);
/*******************************************************************************
**
@@ -211,7 +211,7 @@ BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl);
** of our exported bitpool range. If set we will set the
** remote preference.
**
** Returns TRUE if config set, FALSE otherwize
** Returns TRUE if config set, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max);

View File

@@ -35,9 +35,11 @@
* CONST
********************************************************************************/
#if (BTM_SCO_HCI_INCLUDED == TRUE)
#if (BTC_HFP_EXT_CODEC == FALSE)
#include "oi_codec_sbc.h"
#include "oi_status.h"
#include "sbc_encoder.h"
#endif
#if (PLC_INCLUDED == TRUE)
#include "sbc_plc.h"
@@ -58,21 +60,39 @@ static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr;
#define HF_SBC_DEC_RAW_DATA_SIZE 240
#define HF_SBC_ENC_RAW_DATA_SIZE 240
// H2: Header with synchronization word and sequence number
#define BTA_HF_H2_HEADER_SYNC_WORD 0x0801
#define BTA_HF_H2_HEADER_SYNC_WORD_MASK 0x0FFF
#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0)
#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1)
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15
#define BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p) ((*((uint16_t *)p) & BTA_HF_H2_HEADER_SYNC_WORD_MASK) == BTA_HF_H2_HEADER_SYNC_WORD)
/* BTA-AG-CO control block to map bdaddr to BTA handle */
typedef struct
{
#if (BTC_HFP_EXT_CODEC == FALSE)
OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN];
OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE];
SBC_ENC_PARAMS encoder;
UINT8 sequence_number;
bool is_bad_frame;
bool decode_first_pkt;
OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE];
bool encode_first_pkt;
OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE];
OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE];
#else
UINT8 sequence_number;
BOOLEAN rx_first_pkt;
BOOLEAN is_bad_frame;
UINT8 rx_half_msbc_data[BTM_MSBC_FRAME_SIZE/2];
#endif
} bta_ag_co_cb_t;
#if HFP_DYNAMIC_MEMORY == FALSE
@@ -151,7 +171,7 @@ void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len)
** Function bta_ag_ci_slc_ready
**
** Description This function is called to notify AG that SLC is up at
** the application. This funcion is only used when the app
** the application. This function is only used when the app
** is running in pass-through mode.
**
** Returns void
@@ -179,18 +199,9 @@ void bta_ag_ci_slc_ready(UINT16 handle)
** Returns void
**
*******************************************************************************/
static void bta_ag_h2_header(UINT16 *p_buf)
void bta_ag_h2_header(UINT16 *p_buf)
{
// H2: Header with synchronization word and sequence number
#define BTA_HF_H2_HEADER 0x0801
#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0)
#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1)
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15
UINT16 h2_header = BTA_HF_H2_HEADER;
UINT16 h2_header = BTA_HF_H2_HEADER_SYNC_WORD;
UINT8 h2_header_sn0 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK;
UINT8 h2_header_sn1 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK;
h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1
@@ -202,6 +213,19 @@ static void bta_ag_h2_header(UINT16 *p_buf)
*p_buf = h2_header;
}
#if (BTC_HFP_EXT_CODEC == TRUE)
static void bta_ag_pkt_state_reset(void)
{
bta_ag_co_cb.sequence_number = 0;
bta_ag_co_cb.rx_first_pkt = TRUE;
bta_ag_co_cb.is_bad_frame = FALSE;
}
#endif
#if (BTC_HFP_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function bta_hf_dec_init
@@ -217,6 +241,7 @@ static void bta_hf_dec_init(void)
sbc_plc_init(&(bta_hf_ct_plc.plc_state));
#endif ///(PLC_INCLUDED == TRUE)
OI_STATUS status = OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data,
HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE);
if (!OI_SUCCESS(status)) {
@@ -326,15 +351,21 @@ static void bta_ag_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad
APPL_TRACE_ERROR("Frame decode error: %d", status);
break;
}
#endif ///(PLC_INCLUDED == TRUE)
if (OI_SUCCESS(status)) {
btc_hf_incoming_data_cb_to_app((const uint8_t *)(bta_hf_ct_plc.sbc_plc_out), sbc_raw_data_size);
}
#else
if (OI_SUCCESS(status)) {
btc_hf_incoming_data_cb_to_app((const uint8_t *)(bta_ag_co_cb.decode_raw_data), sbc_raw_data_size);
}
#endif
}
#endif
/*******************************************************************************
* BTA AG SCO CO FUNCITONS
* BTA AG SCO CO FUNCTIONS
********************************************************************************/
/*******************************************************************************
**
@@ -418,8 +449,12 @@ void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 in
}
#endif ///(PLC_INCLUDED == TRUE)
#endif /// (HFP_DYNAMIC_MEMORY == TRUE)
#if (BTC_HFP_EXT_CODEC == TRUE)
bta_ag_pkt_state_reset();
#else
bta_hf_dec_init();
bta_hf_enc_init();
#endif
return;
} else {
return; // Nothing to do
@@ -476,6 +511,8 @@ void bta_ag_sco_co_close(void)
hf_inout_pkt_size = 0;
}
#if (BTC_HFP_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function bta_ag_sco_co_out_data
@@ -525,7 +562,7 @@ uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf)
//Never run to here.
}
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
APPL_TRACE_ERROR("%s invalid air mode: %d", __FUNCTION__, hf_air_mode);
}
return 0;
}
@@ -546,6 +583,65 @@ void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
STREAM_SKIP_UINT16(p);
STREAM_TO_UINT8(pkt_size, p);
#if (BTC_HFP_EXT_CODEC == TRUE)
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
btc_hf_audio_data_cb_to_app((uint8_t *)p_buf, (uint8_t *)p, pkt_size, status != BTM_SCO_DATA_CORRECT);
}
else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
if (pkt_size != hf_inout_pkt_size) {
bta_ag_co_cb.is_bad_frame = true;
}
if (status != BTM_SCO_DATA_CORRECT) {
bta_ag_co_cb.is_bad_frame = true;
}
if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) {
if (pkt_size > BTM_MSBC_FRAME_SIZE / 2) {
pkt_size = BTM_MSBC_FRAME_SIZE / 2;
}
if (bta_ag_co_cb.rx_first_pkt){
memcpy(bta_ag_co_cb.rx_half_msbc_data, p, pkt_size);
osi_free(p_buf);
} else {
BT_HDR *p_new_buf = osi_calloc(sizeof(BT_HDR) + BTM_MSBC_FRAME_SIZE);
p_new_buf->offset = 0;
UINT8 *p_data = (UINT8 *)(p_new_buf + 1) + p_new_buf->offset;
memcpy(p_data, bta_ag_co_cb.rx_half_msbc_data, BTM_MSBC_FRAME_SIZE / 2);
memcpy(p_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size);
osi_free(p_buf);
if (BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p_data)) {
/* H2 header sync word found, skip */
p_data += 2;
}
else if (!bta_ag_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p_data += 1;
}
btc_hf_audio_data_cb_to_app((uint8_t *)p_new_buf, (uint8_t *)p_data, BTM_MSBC_FRAME_SIZE, bta_ag_co_cb.is_bad_frame);
bta_ag_co_cb.is_bad_frame = false;
memset(bta_ag_co_cb.decode_msbc_data, 0, BTM_MSBC_FRAME_SIZE);
}
bta_ag_co_cb.rx_first_pkt = !bta_ag_co_cb.rx_first_pkt;
}
else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) {
if (pkt_size > BTM_MSBC_FRAME_SIZE) {
pkt_size = BTM_MSBC_FRAME_SIZE;
}
if (BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p)) {
/* H2 header sync word found, skip */
p += 2;
}
else if (!bta_ag_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p += 1;
}
btc_hf_audio_data_cb_to_app((uint8_t *)p_buf, (uint8_t *)p, pkt_size, bta_ag_co_cb.is_bad_frame);
bta_ag_co_cb.is_bad_frame = false;
}
else {
osi_free(p_buf);
}
}
#else
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
// CVSD
if(status != BTM_SCO_DATA_CORRECT) {
@@ -583,8 +679,12 @@ void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
//Never run to here.
}
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
APPL_TRACE_ERROR("%s invalid air mode: %d", __FUNCTION__, hf_air_mode);
}
osi_free(p_buf);
#endif
}
#endif
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -236,12 +236,15 @@ static void bte_hf_evt(tBTA_AG_EVT event, tBTA_AG *param)
else if (BTA_AG_OPEN_EVT == event) {
param_len = sizeof(tBTA_AG_OPEN);
}
else if ((BTA_AG_CLOSE_EVT == event) || (BTA_AG_AUDIO_OPEN_EVT == event) || (BTA_AG_AUDIO_CLOSE_EVT == event)) {
else if ((BTA_AG_CLOSE_EVT == event)) {
param_len = sizeof(tBTA_AG_HDR);
}
else if (BTA_AG_CONN_EVT == event) {
param_len = sizeof(tBTA_AG_CONN);
}
else if ((BTA_AG_AUDIO_OPEN_EVT == event) || (BTA_AG_AUDIO_CLOSE_EVT == event) || (BTA_AG_AUDIO_MSBC_OPEN_EVT == event)) {
param_len = sizeof(tBTA_AG_AUDIO_STAT);
}
else if (param) {
param_len = sizeof(tBTA_AG_VAL);
}
@@ -265,6 +268,26 @@ void btc_hf_reg_data_cb(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_
hf_local_param[0].btc_hf_outgoing_data_cb = send;
}
void btc_hf_reg_audio_data_cb(esp_hf_ag_audio_data_cb_t callback)
{
hf_local_param[0].btc_hf_audio_data_cb = callback;
}
void btc_hf_audio_data_cb_to_app(uint8_t *buf, uint8_t *data, uint16_t len, bool is_bad_frame)
{
if (hf_local_param[0].btc_hf_audio_data_cb) {
/* we always have sizeof(BT_HDR) bytes free space before data, it is enough for esp_hf_audio_buff_t */
esp_hf_audio_buff_t *audio_buff = (esp_hf_audio_buff_t *)buf;
audio_buff->buff_size = len;
audio_buff->data_len = len;
audio_buff->data = data;
hf_local_param[0].btc_hf_audio_data_cb(hf_local_param[0].btc_hf_cb.sync_conn_hdl, audio_buff, is_bad_frame);
}
else {
osi_free(buf);
}
}
void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len)
{
int idx = 0;
@@ -949,6 +972,20 @@ bt_status_t btc_hf_ci_sco_data(void)
return status;
}
bool btc_hf_ag_audio_data_send(uint16_t sync_conn_hdl, uint8_t *p_buff_start, uint8_t *p_data, uint8_t data_len)
{
#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTA_HFP_EXT_CODEC == TRUE)
/* currently, sync_conn_hdl is not used */
int idx = btc_hf_latest_connected_idx();
CHECK_HF_SLC_CONNECTED(idx);
if (idx != BTC_HF_INVALID_IDX) {
BTA_AgAudioDataSend(hf_local_param[idx].btc_hf_cb.handle, p_buff_start, p_data, data_len);
return true;
}
#endif
return false;
}
/************************************************************************************
** Memory malloc and release
************************************************************************************/
@@ -1266,6 +1303,13 @@ void btc_hf_call_handler(btc_msg_t *msg)
btc_hf_reg_data_cb(arg->reg_data_cb.recv, arg->reg_data_cb.send);
break;
}
case BTC_HF_REGISTER_AUDIO_DATA_CALLBACK_EVT:
{
btc_hf_reg_audio_data_cb(arg->reg_audio_data_cb.callback);
break;
}
case BTC_HF_REQUEST_PKT_STAT_EVT:
{
btc_hf_pkt_stat_nums_get(arg->pkt_sync_hd.sync_conn_handle);
@@ -1411,7 +1455,9 @@ void btc_hf_cb_handler(btc_msg_t *msg)
do {
param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED;
memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t));
hf_local_param[idx].btc_hf_cb.sync_conn_hdl = p_data->hdr.sync_conn_handle;
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
param.audio_stat.preferred_frame_size = p_data->audio_stat.preferred_frame_size;
btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, &param);
} while(0);
break;
@@ -1424,7 +1470,9 @@ void btc_hf_cb_handler(btc_msg_t *msg)
do {
param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED_MSBC;
memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t));
hf_local_param[idx].btc_hf_cb.sync_conn_hdl = p_data->hdr.sync_conn_handle;
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
param.audio_stat.preferred_frame_size = p_data->audio_stat.preferred_frame_size;
btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, &param);
} while (0);
break;
@@ -1435,6 +1483,7 @@ void btc_hf_cb_handler(btc_msg_t *msg)
CHECK_HF_IDX(idx);
do {
param.audio_stat.state = ESP_HF_AUDIO_STATE_DISCONNECTED;
hf_local_param[idx].btc_hf_cb.sync_conn_hdl = 0;
memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t));
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, &param);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,9 +14,23 @@
#if (BTM_SCO_HCI_INCLUDED == TRUE)
#if (BTC_HFP_EXT_CODEC == FALSE)
#include "oi_codec_sbc.h"
#include "oi_status.h"
#include "sbc_encoder.h"
#endif
// H2: Header with synchronization word and sequence number
#define BTA_HF_H2_HEADER_SYNC_WORD 0x0801
#define BTA_HF_H2_HEADER_SYNC_WORD_MASK 0x0FFF
#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0)
#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1)
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15
#define BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p) ((*((uint16_t *)p) & BTA_HF_H2_HEADER_SYNC_WORD_MASK) == BTA_HF_H2_HEADER_SYNC_WORD)
#if (PLC_INCLUDED == TRUE)
#include "sbc_plc.h"
@@ -44,18 +58,23 @@ static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr;
/* BTA-HF-CO control block to map bdaddr to BTA handle */
typedef struct
{
#if (BTC_HFP_EXT_CODEC == FALSE)
OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN];
OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE];
SBC_ENC_PARAMS encoder;
UINT8 sequence_number;
bool is_bad_frame;
bool decode_first_pkt;
OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE];
bool encode_first_pkt;
OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE];
OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE];
#else
UINT8 sequence_number;
BOOLEAN rx_first_pkt;
BOOLEAN is_bad_frame;
UINT8 rx_half_msbc_data[BTM_MSBC_FRAME_SIZE/2];
#endif
} bta_hf_client_co_cb_t;
#if HFP_DYNAMIC_MEMORY == FALSE
@@ -118,6 +137,8 @@ tBTA_HFP_SCO_ROUTE_TYPE bta_hf_client_sco_co_init(UINT32 rx_bw, UINT32 tx_bw,
return BTA_HFP_SCO_ROUTE_HCI;
}
#if (BTC_HFP_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function bta_hf_dec_init
@@ -168,6 +189,19 @@ static void bta_hf_enc_init(void) {
SBC_Encoder_Init(&(bta_hf_client_co_cb.encoder));
}
#endif
#if (BTC_HFP_EXT_CODEC == TRUE)
void bta_hf_pkt_state_reset(void)
{
bta_hf_client_co_cb.sequence_number = 0;
bta_hf_client_co_cb.rx_first_pkt = TRUE;
bta_hf_client_co_cb.is_bad_frame = FALSE;
}
#endif
/*******************************************************************************
**
** Function bta_hf_client_sco_co_open
@@ -204,8 +238,12 @@ void bta_hf_client_sco_co_open(UINT16 handle, UINT8 air_mode, UINT8 inout_pkt_si
#endif /// (HFP_DYNAMIC_MEMORY == TRUE)
#if (BTC_HFP_EXT_CODEC == TRUE)
bta_hf_pkt_state_reset();
#else
bta_hf_dec_init();
bta_hf_enc_init();
#endif
return;
} else {
@@ -280,18 +318,9 @@ void bta_hf_client_sco_co_close(void)
** Returns void
**
*******************************************************************************/
static void bta_hf_client_h2_header(UINT16 *p_buf)
void bta_hf_client_h2_header(UINT16 *p_buf)
{
// H2: Header with synchronization word and sequence number
#define BTA_HF_H2_HEADER 0x0801
#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0)
#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1)
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15
UINT16 h2_header = BTA_HF_H2_HEADER;
UINT16 h2_header = BTA_HF_H2_HEADER_SYNC_WORD;
UINT8 h2_header_sn0 = bta_hf_client_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK;
UINT8 h2_header_sn1 = bta_hf_client_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK;
h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1
@@ -304,6 +333,8 @@ static void bta_hf_client_h2_header(UINT16 *p_buf)
*p_buf = h2_header;
}
#if (BTC_HFP_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function bta_hf_client_sco_co_out_data
@@ -364,7 +395,7 @@ uint32_t bta_hf_client_sco_co_out_data(UINT8 *p_buf)
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
APPL_TRACE_ERROR("%s invalid air mode: %d", __FUNCTION__, hf_air_mode);
}
return 0;
}
@@ -439,6 +470,8 @@ static void bta_hf_client_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN
}
}
#endif
/*******************************************************************************
**
** Function bta_hf_client_sco_co_in_data
@@ -450,12 +483,77 @@ static void bta_hf_client_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN
*******************************************************************************/
void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
{
if (hf_air_mode != BTM_SCO_AIR_MODE_CVSD && hf_air_mode != BTM_SCO_AIR_MODE_TRANSPNT) {
APPL_TRACE_ERROR("%s invalid air mode: %d", __FUNCTION__, hf_air_mode);
osi_free(p_buf);
return;
}
UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
UINT8 pkt_size = 0;
STREAM_SKIP_UINT16(p);
STREAM_TO_UINT8 (pkt_size, p);
#if (BTC_HFP_EXT_CODEC == TRUE)
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
btc_hf_client_audio_data_cb_to_app((uint8_t *)p_buf, (uint8_t *)p, pkt_size, status != BTM_SCO_DATA_CORRECT);
}
else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
if (pkt_size != hf_inout_pkt_size) {
bta_hf_client_co_cb.is_bad_frame = true;
}
if (status != BTM_SCO_DATA_CORRECT) {
bta_hf_client_co_cb.is_bad_frame = true;
}
if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) {
if (pkt_size > BTM_MSBC_FRAME_SIZE / 2) {
pkt_size = BTM_MSBC_FRAME_SIZE / 2;
}
if (bta_hf_client_co_cb.rx_first_pkt){
memcpy(bta_hf_client_co_cb.rx_half_msbc_data, p, pkt_size);
osi_free(p_buf);
} else {
BT_HDR *p_new_buf = osi_calloc(sizeof(BT_HDR) + BTM_MSBC_FRAME_SIZE);
p_new_buf->offset = 0;
UINT8 *p_data = (UINT8 *)(p_new_buf + 1) + p_new_buf->offset;
memcpy(p_data, bta_hf_client_co_cb.rx_half_msbc_data, BTM_MSBC_FRAME_SIZE / 2);
memcpy(p_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size);
osi_free(p_buf);
if (BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p_data)) {
/* H2 header sync word found, skip */
p_data += 2;
}
else if (!bta_hf_client_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p_data += 1;
}
btc_hf_client_audio_data_cb_to_app((uint8_t *)p_new_buf, (uint8_t *)p_data, BTM_MSBC_FRAME_SIZE, bta_hf_client_co_cb.is_bad_frame);
bta_hf_client_co_cb.is_bad_frame = false;
memset(bta_hf_client_co_cb.rx_half_msbc_data, 0, BTM_MSBC_FRAME_SIZE);
}
bta_hf_client_co_cb.rx_first_pkt = !bta_hf_client_co_cb.rx_first_pkt;
}
else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) {
if (pkt_size > BTM_MSBC_FRAME_SIZE) {
pkt_size = BTM_MSBC_FRAME_SIZE;
}
if (BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p)) {
/* H2 header sync word found, skip */
p += 2;
}
else if (!bta_hf_client_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p += 1;
}
btc_hf_client_audio_data_cb_to_app((uint8_t *)p_buf, (uint8_t *)p, pkt_size, bta_hf_client_co_cb.is_bad_frame);
bta_hf_client_co_cb.is_bad_frame = false;
}
else {
osi_free(p_buf);
}
}
#else
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
// CVSD
if(status != BTM_SCO_DATA_CORRECT){
@@ -496,8 +594,10 @@ void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
//Never run to here.
}
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
APPL_TRACE_ERROR("%s invalid air mode: %d", __FUNCTION__, hf_air_mode);
}
osi_free(p_buf);
#endif
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -132,6 +132,26 @@ void btc_hf_client_reg_data_cb(esp_hf_client_incoming_data_cb_t recv,
hf_client_local_param.btc_hf_client_outgoing_data_cb = send;
}
static void btc_hf_client_reg_audio_data_cb(esp_hf_client_audio_data_cb_t callback)
{
hf_client_local_param.btc_hf_client_audio_data_cb = callback;
}
void btc_hf_client_audio_data_cb_to_app(uint8_t *buf, uint8_t *data, uint16_t len, bool is_bad_frame)
{
if (hf_client_local_param.btc_hf_client_audio_data_cb) {
/* we always have sizeof(BT_HDR) bytes free space before data, it is enough for esp_hf_audio_buff_t */
esp_hf_audio_buff_t *audio_buff = (esp_hf_audio_buff_t *)buf;
audio_buff->buff_size = len;
audio_buff->data_len = len;
audio_buff->data = data;
hf_client_local_param.btc_hf_client_audio_data_cb(hf_client_local_param.btc_hf_client_cb.sync_conn_hdl, audio_buff, is_bad_frame);
}
else {
osi_free(buf);
}
}
void btc_hf_client_incoming_data_cb_to_app(const uint8_t *data, uint32_t len)
{
// todo: critical section protection
@@ -1076,7 +1096,9 @@ void btc_hf_client_cb_handler(btc_msg_t *msg)
param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED;
memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda,
sizeof(esp_bd_addr_t));
hf_client_local_param.btc_hf_client_cb.sync_conn_hdl = p_data->hdr.sync_conn_handle;
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
param.audio_stat.preferred_frame_size = p_data->audio_stat.preferred_frame_size;
btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, &param);
} while (0);
break;
@@ -1085,7 +1107,9 @@ void btc_hf_client_cb_handler(btc_msg_t *msg)
param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda,
sizeof(esp_bd_addr_t));
hf_client_local_param.btc_hf_client_cb.sync_conn_hdl = p_data->hdr.sync_conn_handle;
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
param.audio_stat.preferred_frame_size = p_data->audio_stat.preferred_frame_size;
btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, &param);
} while (0);
break;
@@ -1094,7 +1118,9 @@ void btc_hf_client_cb_handler(btc_msg_t *msg)
param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda,
sizeof(esp_bd_addr_t));
hf_client_local_param.btc_hf_client_cb.sync_conn_hdl = 0;
param.audio_stat.sync_conn_handle = p_data->hdr.sync_conn_handle;
param.audio_stat.preferred_frame_size = 0;
btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, &param);
} while (0);
break;
@@ -1183,6 +1209,9 @@ void btc_hf_client_call_handler(btc_msg_t *msg)
case BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT:
btc_hf_client_reg_data_cb(arg->reg_data_cb.recv, arg->reg_data_cb.send);
break;
case BTC_HF_CLIENT_REGISTER_AUDIO_DATA_CALLBACK_EVT:
btc_hf_client_reg_audio_data_cb(arg->reg_audio_data_cb.callback);
break;
case BTC_HF_CLIENT_SEND_NREC_EVT:
btc_hf_client_send_nrec();
break;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -23,6 +23,7 @@
/*******************************************************************************
** Data types
*******************************************************************************/
#if (BTC_AV_EXT_CODEC == FALSE)
/* tBTC_MEDIA_INIT_AUDIO msg structure */
typedef struct {
@@ -50,6 +51,8 @@ typedef struct {
tBTC_AV_MEDIA_FEEDINGS feeding;
} tBTC_MEDIA_INIT_AUDIO_FEEDING;
#endif
/*******************************************************************************
** Public functions
*******************************************************************************/
@@ -76,29 +79,6 @@ bool btc_a2dp_source_startup(void);
*******************************************************************************/
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
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_enc_udpate_req
**
** Description Request to update the media task encoder
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_start_audio_req
@@ -110,28 +90,6 @@ BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg);
*******************************************************************************/
BOOLEAN btc_a2dp_source_start_audio_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_stop_audio_req
**
** Description Request to stop audio encoding task
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_stop_audio_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_tx_flush_req
**
** Description Request to flush audio encoding pipe
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_tx_flush_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_audio_readbuf
@@ -143,27 +101,6 @@ BOOLEAN btc_a2dp_source_tx_flush_req(void);
*******************************************************************************/
BT_HDR *btc_a2dp_source_audio_readbuf(void);
/*******************************************************************************
**
** Function btc_a2dp_source_audio_feeding_init_req
**
** Description Request to initialize audio feeding
**
** Returns TRUE if success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_is_streaming
**
** Description Check whether A2DP source is in streaming state
**
*******************************************************************************/
bool btc_a2dp_source_is_streaming(void);
/*******************************************************************************
**
** Function btc_a2dp_source_is_task_shutting_down
@@ -204,15 +141,6 @@ void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av);
*******************************************************************************/
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
@@ -222,6 +150,17 @@ void btc_a2dp_source_setup_codec(void);
*******************************************************************************/
void btc_a2dp_source_set_tx_flush(BOOLEAN enable);
#if (BTC_AV_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function btc_a2dp_source_setup_codec
**
** Description initialize the encoder parameters
**
*******************************************************************************/
void btc_a2dp_source_setup_codec(void);
/*******************************************************************************
**
** Function btc_a2dp_source_encoder_update
@@ -231,6 +170,8 @@ void btc_a2dp_source_set_tx_flush(BOOLEAN enable);
*******************************************************************************/
void btc_a2dp_source_encoder_update(void);
#endif
/*****************************************************************************
**
** Function btc_source_report_delay_value
@@ -240,6 +181,19 @@ void btc_a2dp_source_encoder_update(void);
*******************************************************************************/
void btc_source_report_delay_value(UINT16 delay_value);
#if (BTC_AV_EXT_CODEC == TRUE)
/*****************************************************************************
**
** Function btc_a2dp_source_enqueue_audio_frame
**
** Description Enqueue source audio frame to tx queue
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enqueue_audio_frame(BT_HDR *p_buf);
#endif
#endif /* #if BTC_AV_SRC_INCLUDED */
#endif /* __BTC_A2DP_SOURCE_H__ */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,11 +22,21 @@
#include "btc/btc_task.h"
#include "btc/btc_common.h"
#include "btc/btc_sm.h"
#include "btc_av_api.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_sbc.h"
#if (BTC_AV_INCLUDED == TRUE)
// global variable to inidcate avrc is initialized with a2dp
#if (BTA_AV_CO_CP_SCMS_T == TRUE)
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
#else
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
#endif
#define BTC_AUDIO_BUFF_OFFSET BTC_MEDIA_AA_SBC_OFFSET
// global variable to indicate avrc is initialized with a2dp
extern bool g_av_with_rc;
// global variable to indicate a2dp is initialized
extern bool g_a2dp_on_init;
@@ -51,21 +61,24 @@ typedef enum {
BTC_AV_DISCONNECT_REQ_EVT,
BTC_AV_START_STREAM_REQ_EVT,
BTC_AV_SUSPEND_STREAM_REQ_EVT,
BTC_AV_SINK_CONFIG_REQ_EVT,
BTC_AV_CONFIG_EVT,
} btc_av_sm_event_t;
typedef enum {
#if BTC_AV_SINK_INCLUDED
BTC_AV_SINK_API_INIT_EVT = 0,
BTC_AV_SINK_API_REG_SEP_EVT,
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,
BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT,
BTC_AV_SINK_API_SET_DELAY_VALUE_EVT,
BTC_AV_SINK_API_GET_DELAY_VALUE_EVT,
#endif /* BTC_AV_SINK_INCLUDED */
#if BTC_AV_SRC_INCLUDED
BTC_AV_SRC_API_INIT_EVT,
BTC_AV_SRC_API_REG_SEP_EVT,
BTC_AV_SRC_API_DEINIT_EVT,
BTC_AV_SRC_API_CONNECT_EVT,
BTC_AV_SRC_API_DISCONNECT_EVT,
@@ -77,14 +90,14 @@ typedef enum {
/* 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_DISCONNECT_EVT
bt_bdaddr_t disconn;
// BTC_AV_SINK_API_REG_DATA_CB_EVT
esp_a2d_sink_data_cb_t data_cb;
// BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT
esp_a2d_sink_audio_data_cb_t audio_data_cb;
// BTC_AV_SINK_API_SET_DELAY_VALUE_EVT
uint16_t delay_value;
#endif /* BTC_AV_SINK_INCLUDED */
@@ -96,6 +109,13 @@ typedef union {
// BTC_AV_SRC_API_DISCONNECT_EVT
bt_bdaddr_t src_disconn;
#endif /* BTC_AV_SRC_INCLUDED */
// BTC_AV_CONFIG_EVT
esp_a2d_mcc_t mcc;
// BTC_AV_SINK_API_REG_SEP_EVT or BTC_AV_SRC_API_REG_SEP_EVT
struct {
uint8_t seid;
esp_a2d_mcc_t mcc;
} reg_sep;
// BTC_AV_API_MEDIA_CTRL_EVT
esp_a2d_media_ctrl_t ctrl;
} btc_av_args_t;
@@ -110,6 +130,32 @@ void btc_a2dp_cb_handler(btc_msg_t *msg);
void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback);
/*******************************************************************************
**
** Function btc_a2dp_sink_reg_audio_data_cb
**
** Description Register a2dp sink audio data callback
**
** Returns None
**
*******************************************************************************/
void btc_a2dp_sink_reg_audio_data_cb(esp_a2d_sink_audio_data_cb_t callback);
#if (BTC_AV_EXT_CODEC == TRUE)
/*******************************************************************************
**
** Function btc_av_codec_cap_get
**
** Description Get external codec capability
**
** Returns Pointer to codec capability
**
*******************************************************************************/
tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void);
#endif
void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback);
/*******************************************************************************
**
@@ -172,6 +218,16 @@ void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len);
BOOLEAN btc_av_is_connected(void);
/*******************************************************************************
**
** Function btc_av_is_started
**
** Description Checks if av is started
**
** Returns BOOLEAN
**
*******************************************************************************/
BOOLEAN btc_av_is_started(void);
/*******************************************************************************
*
@@ -225,4 +281,60 @@ uint8_t btc_av_get_service_id(void);
#endif ///BTC_AV_INCLUDED == TRUE
/*******************************************************************************
**
** Function btc_a2d_conn_handle_get
**
** Description Get connection handle from btc_av_cb
**
** Returns Void
**
*******************************************************************************/
uint16_t btc_a2d_conn_handle_get(void);
/*******************************************************************************
**
** Function btc_av_audio_buff_alloc
**
** Description Allocate audio buffer with specific size
**
** Returns Void
**
*******************************************************************************/
void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data);
/*******************************************************************************
**
** Function btc_av_audio_buff_free
**
** Description Free audio buffer
**
** Returns Void
**
*******************************************************************************/
void btc_av_audio_buff_free(uint8_t *p_buf);
/*******************************************************************************
**
** Function btc_a2d_src_audio_mtu_check
**
** Description Checks if data length is valid, not bigger than mtu
**
** Returns BOOLEAN
**
*******************************************************************************/
BOOLEAN btc_a2d_src_audio_mtu_check(uint16_t data_len);
/*******************************************************************************
**
** Function btc_a2d_src_audio_data_send
**
** Description Send audio data to lower layer, audio buffer is consumed
** only when operation is success
**
** Returns BT_STATUS_SUCCESS if success, otherwise, BT_STATUS_FAIL
**
*******************************************************************************/
bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
#endif /* __BTC_AV_H__ */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -57,6 +57,7 @@ typedef enum
BTC_HF_END_CALL_EVT,
//REG
BTC_HF_REGISTER_DATA_CALLBACK_EVT,
BTC_HF_REGISTER_AUDIO_DATA_CALLBACK_EVT,
BTC_HF_REQUEST_PKT_STAT_EVT
} btc_hf_act_t;
@@ -189,6 +190,11 @@ typedef union
esp_hf_outgoing_data_cb_t send;
} reg_data_cb;
// BTC_HF_REGISTER_AUDIO_DATA_CALLBACK_EVT
struct ag_reg_audio_data_callback {
esp_hf_ag_audio_data_cb_t callback;
} reg_audio_data_cb;
// BTC_HF_REQUEST_PKT_STAT_EVT
struct ag_req_pkt_stat_sync_handle {
UINT16 sync_conn_handle;
@@ -211,6 +217,7 @@ typedef struct
{
bool initialized;
UINT16 handle;
UINT16 sync_conn_hdl;
bt_bdaddr_t connected_bda;
tBTA_AG_PEER_FEAT peer_feat;
tBTA_AG_CHLD_FEAT chld_feat;
@@ -231,6 +238,7 @@ typedef struct
btc_hf_cb_t btc_hf_cb;
esp_hf_incoming_data_cb_t btc_hf_incoming_data_cb;
esp_hf_outgoing_data_cb_t btc_hf_outgoing_data_cb;
esp_hf_ag_audio_data_cb_t btc_hf_audio_data_cb;
} hf_local_param_t;
#if HFP_DYNAMIC_MEMORY == TRUE
@@ -247,6 +255,8 @@ void btc_hf_cb_handler(btc_msg_t *msg); //handle the event from bta
void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len);
void btc_hf_audio_data_cb_to_app(uint8_t *buf, uint8_t *data, uint16_t len, bool is_bad_frame);
uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len);
void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
@@ -255,6 +265,8 @@ void btc_hf_arg_deep_free(btc_msg_t *msg);
bt_status_t btc_hf_ci_sco_data(void);
bool btc_hf_ag_audio_data_send(uint16_t sync_conn_hdl, uint8_t *p_buff_start, uint8_t *p_data, uint8_t data_len);
#endif // BTC_HF_INCLUDED == TRUE
#endif /* __BTC_HF_AG_H__ */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -49,6 +49,7 @@ typedef enum {
BTC_HF_CLIENT_SEND_DTMF_EVT,
BTC_HF_CLIENT_REQUEST_LAST_VOICE_TAG_NUMBER_EVT,
BTC_HF_CLIENT_REGISTER_DATA_CALLBACK_EVT,
BTC_HF_CLIENT_REGISTER_AUDIO_DATA_CALLBACK_EVT,
BTC_HF_CLIENT_SEND_NREC_EVT,
BTC_HF_CLIENT_SEND_XAPL_EVT,
BTC_HF_CLIENT_SEND_IPHONEACCEV_EVT,
@@ -107,6 +108,11 @@ typedef union {
esp_hf_client_outgoing_data_cb_t send;
} reg_data_cb;
// BTC_HF_CLIENT_REGISTER_AUDIO_DATA_CALLBACK_EVT
struct hf_client_reg_audio_data_callback {
esp_hf_client_audio_data_cb_t callback;
} reg_audio_data_cb;
//BTC_HF_CLIENT_SEND_XAPL_EVT
struct send_xapl_args {
char information[ESP_BT_HF_AT_SEND_XAPL_LEN + 1];
@@ -134,6 +140,7 @@ typedef struct
{
bool initialized;
UINT16 handle;
UINT16 sync_conn_hdl;
bt_bdaddr_t connected_bda;
esp_hf_client_connection_state_t state;
esp_hf_vr_state_t vr_state;
@@ -147,6 +154,7 @@ typedef struct
btc_hf_client_cb_t btc_hf_client_cb;
esp_hf_client_incoming_data_cb_t btc_hf_client_incoming_data_cb;
esp_hf_client_outgoing_data_cb_t btc_hf_client_outgoing_data_cb;
esp_hf_client_audio_data_cb_t btc_hf_client_audio_data_cb;
}hf_client_local_param_t;
#if HFP_DYNAMIC_MEMORY == TRUE
@@ -162,6 +170,8 @@ void btc_hf_client_call_handler(btc_msg_t *msg);
void btc_hf_client_cb_handler(btc_msg_t *msg);
void btc_hf_client_audio_data_cb_to_app(uint8_t *buf, uint8_t *data, uint16_t len, bool is_bad_frame);
void btc_hf_client_incoming_data_cb_to_app(const uint8_t *data, uint32_t len);
uint32_t btc_hf_client_outgoing_data_cb_to_app(uint8_t *data, uint32_t len);

View File

@@ -39,6 +39,12 @@
#define UC_BT_A2DP_ENABLED FALSE
#endif
#ifdef CONFIG_BT_A2DP_USE_EXTERNAL_CODEC
#define UC_BT_A2DP_USE_EXTERNAL_CODEC CONFIG_BT_A2DP_USE_EXTERNAL_CODEC
#else
#define UC_BT_A2DP_USE_EXTERNAL_CODEC FALSE
#endif
//AVRCP
#ifdef CONFIG_BT_AVRCP_ENABLED
#define UC_BT_AVRCP_ENABLED TRUE
@@ -101,6 +107,12 @@
#define UC_BT_HFP_CLIENT_ENABLED FALSE
#endif
#ifdef CONFIG_BT_HFP_USE_EXTERNAL_CODEC
#define UC_BT_HFP_USE_EXTERNAL_CODEC CONFIG_BT_HFP_USE_EXTERNAL_CODEC
#else
#define UC_BT_HFP_USE_EXTERNAL_CODEC FALSE
#endif
//HID
#ifdef CONFIG_BT_HID_ENABLED
#define UC_BT_HID_ENABLED CONFIG_BT_HID_ENABLED

View File

@@ -88,9 +88,14 @@
#define BTC_AV_INCLUDED TRUE
#define BTA_AV_SINK_INCLUDED TRUE
#define BTC_AV_SINK_INCLUDED TRUE
#define SBC_DEC_INCLUDED TRUE
#define BTC_AV_SRC_INCLUDED TRUE
#if (UC_BT_A2DP_USE_EXTERNAL_CODEC == TRUE)
#define BTC_AV_EXT_CODEC TRUE
#define BTA_AV_EXT_CODEC TRUE
#else
#define SBC_DEC_INCLUDED TRUE
#define SBC_ENC_INCLUDED TRUE
#endif
#if UC_BT_AVRCP_CT_COVER_ART_ENABLED
#define BTA_AV_CA_INCLUDED TRUE
#define BTC_AV_CA_INCLUDED TRUE
@@ -122,14 +127,22 @@
#ifndef BTM_SCO_INCLUDED
#define BTM_SCO_INCLUDED TRUE
#endif
#if (UC_BT_HFP_AUDIO_DATA_PATH_HCI == TRUE)
#if (UC_BT_HFP_USE_EXTERNAL_CODEC == TRUE)
#define BTC_HFP_EXT_CODEC TRUE
#define BTA_HFP_EXT_CODEC TRUE
#define PLC_INCLUDED FALSE
#else
#ifndef SBC_DEC_INCLUDED
#define SBC_DEC_INCLUDED TRUE
#endif
#ifndef SBC_ENC_INCLUDED
#define SBC_ENC_INCLUDED TRUE
#endif
#ifndef PLC_INCLUDED
#define PLC_INCLUDED TRUE
#endif /* (UC_BT_HFP_USE_EXTERNAL_CODEC == TRUE) */
#else
#define PLC_INCLUDED FALSE
#endif
#if (UC_BT_HFP_AG_ENABLED == TRUE)
@@ -569,6 +582,10 @@
#define BTC_AV_SRC_INCLUDED FALSE
#endif
#ifndef BTC_AV_EXT_CODEC
#define BTC_AV_EXT_CODEC FALSE
#endif
#ifndef BTC_SPP_INCLUDED
#define BTC_SPP_INCLUDED FALSE
#endif
@@ -593,6 +610,10 @@
#define SBC_ENC_INCLUDED FALSE
#endif
#ifndef BTC_HFP_EXT_CODEC
#define BTC_HFP_EXT_CODEC FALSE
#endif
/******************************************************************************
**
** BTA-layer components
@@ -642,6 +663,10 @@
#define BTA_AV_CA_INCLUDED FALSE
#endif
#ifndef BTA_AV_EXT_CODEC
#define BTA_AV_EXT_CODEC FALSE
#endif
#ifndef BTA_AV_SINK_INCLUDED
#define BTA_AV_SINK_INCLUDED FALSE
#endif
@@ -654,6 +679,10 @@
#define BTA_SDP_INCLUDED FALSE
#endif
#ifndef BTA_HFP_EXT_CODEC
#define BTA_HFP_EXT_CODEC FALSE
#endif
/* This is set to enable use of GAP L2CAP connections. */
#ifndef VND_BT_JV_BTA_L2CAP
#define VND_BT_JV_BTA_L2CAP FALSE

View File

@@ -264,6 +264,37 @@ UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs)
return result;
}
/*******************************************************************************
**
** Function AVDT_UpdateCodecInfo
**
** Description Update codec capability for a stream endpoint.
**
**
** Returns AVDT_SUCCESS if successful, otherwise error.
**
*******************************************************************************/
UINT16 AVDT_UpdateCodecInfo(UINT8 handle, UINT8 num_codec, UINT8 *codec_info, UINT16 codec_info_len)
{
UINT16 result = AVDT_SUCCESS;
tAVDT_SCB *p_scb;
/* look up scb */
if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) {
result = AVDT_BAD_HANDLE;
}
else if (num_codec != 1 || codec_info == NULL || codec_info_len != AVDT_CODEC_SIZE) {
/* currently, only allow one codec info */
result = AVDT_BAD_PARAMS;
}
else {
/* update codec info */
p_scb->cs.cfg.num_codec = num_codec;
memcpy(p_scb->cs.cfg.codec_info, codec_info, codec_info_len);
}
return result;
}
/*******************************************************************************
**
** Function AVDT_RemoveStream

View File

@@ -30,7 +30,12 @@
/* the length of the SBC Media Payload header. */
#define A2D_SBC_MPL_HDR_LEN 1
/* the LOSC of SBC media codec capabilitiy */
/* CIE offset in the info byte sequence */
#define A2D_SBC_CIE_OFF 3
/* CIE length in the info byte sequence */
#define A2D_SBC_CIE_LEN 4
/* the LOSC of SBC media codec capability */
#define A2D_SBC_INFO_LEN 6
/* for Codec Specific Information Element */

View File

@@ -517,6 +517,18 @@ extern void AVDT_AbortReq(UINT8 handle);
*******************************************************************************/
extern UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs);
/*******************************************************************************
**
** Function AVDT_UpdateCodecInfo
**
** Description Update codec capability for a stream endpoint.
**
**
** Returns AVDT_SUCCESS if successful, otherwise error.
**
*******************************************************************************/
extern UINT16 AVDT_UpdateCodecInfo(UINT8 handle, UINT8 num_codec, UINT8 *codec_info, UINT16 codec_info_len);
/*******************************************************************************
**
** Function AVDT_RemoveStream

View File

@@ -61,4 +61,11 @@ menu "A2DP Example Configuration"
help
This enables the AVRCP Cover Art feature in example and try to get cover art image from peer device.
config EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC
bool "Use External Codec Instead of Internal"
default n
select BT_A2DP_USE_EXTERNAL_CODEC
help
If enable, Bluedroid stack will not decode A2DP audio data, user need to decode it in application layer.
endmenu

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -321,21 +321,21 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
/* when audio codec is configured, this event comes */
case ESP_A2D_AUDIO_CFG_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", a2d->audio_cfg.mcc.type);
esp_a2d_mcc_t *p_mcc = &a2d->audio_cfg.mcc;
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", p_mcc->type);
/* for now only SBC stream is supported */
if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
if (p_mcc->type == ESP_A2D_MCT_SBC) {
int sample_rate = 16000;
int ch_count = 2;
char oct0 = a2d->audio_cfg.mcc.cie.sbc[0];
if (oct0 & (0x01 << 6)) {
if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_32K) {
sample_rate = 32000;
} else if (oct0 & (0x01 << 5)) {
} else if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_44K) {
sample_rate = 44100;
} else if (oct0 & (0x01 << 4)) {
} else if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_48K) {
sample_rate = 48000;
}
if (oct0 & (0x01 << 3)) {
if (p_mcc->cie.sbc_info.ch_mode & ESP_A2D_SBC_CIE_CH_MODE_MONO) {
ch_count = 1;
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
@@ -362,11 +362,14 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
i2s_channel_reconfig_std_slot(tx_chan, &slot_cfg);
i2s_channel_enable(tx_chan);
#endif
ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x",
a2d->audio_cfg.mcc.cie.sbc[0],
a2d->audio_cfg.mcc.cie.sbc[1],
a2d->audio_cfg.mcc.cie.sbc[2],
a2d->audio_cfg.mcc.cie.sbc[3]);
ESP_LOGI(BT_AV_TAG, "Configure audio player: 0x%x-0x%x-0x%x-0x%x-0x%x-%d-%d",
p_mcc->cie.sbc_info.samp_freq,
p_mcc->cie.sbc_info.ch_mode,
p_mcc->cie.sbc_info.block_len,
p_mcc->cie.sbc_info.num_subbands,
p_mcc->cie.sbc_info.alloc_mthd,
p_mcc->cie.sbc_info.min_bitpool,
p_mcc->cie.sbc_info.max_bitpool);
ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate: %d", sample_rate);
}
break;
@@ -381,6 +384,17 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
break;
}
/* when using external codec, after sep registration done, this event comes */
case ESP_A2D_SEP_REG_STATE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
if (a2d->a2d_sep_reg_stat.reg_state == ESP_A2D_SEP_REG_SUCCESS) {
ESP_LOGI(BT_AV_TAG, "A2DP register SEP success, seid: %d", a2d->a2d_sep_reg_stat.seid);
}
else {
ESP_LOGI(BT_AV_TAG, "A2DP register SEP fail, seid: %d, state: %d", a2d->a2d_sep_reg_stat.seid, a2d->a2d_sep_reg_stat.reg_state);
}
break;
}
/* When protocol service capabilities configured, this event comes */
case ESP_A2D_SNK_PSC_CFG_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
@@ -591,6 +605,7 @@ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
case ESP_A2D_AUDIO_STATE_EVT:
case ESP_A2D_AUDIO_CFG_EVT:
case ESP_A2D_PROF_STATE_EVT:
case ESP_A2D_SEP_REG_STATE_EVT:
case ESP_A2D_SNK_PSC_CFG_EVT:
case ESP_A2D_SNK_SET_DELAY_VALUE_EVT:
case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: {
@@ -603,6 +618,8 @@ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
}
}
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
{
write_ringbuf(data, len);
@@ -613,6 +630,21 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
}
}
#else
void bt_app_a2d_audio_data_cb(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
ESP_LOGI(BT_AV_TAG, "data_len: %d, number_frame: %d, ts: %lu", audio_buf->data_len, audio_buf->number_frame, audio_buf->timestamp);
/*
* Normally, user should send the audio_buf to other task, decode and free audio buff,
* But the codec component is not merge into IDF now, so we just free audio data here
*/
esp_a2d_audio_buff_free(audio_buf);
}
#endif
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
{
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -32,6 +32,14 @@ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
*/
void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len);
/**
* @brief callback function for A2DP sink undecoded audio data
*
* @param [in] conn_hdl connection handle
* @param [in] audio_buf pointer to audio buff
*/
void bt_app_a2d_audio_data_cb(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
* @brief callback function for AVRCP controller
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -164,8 +164,23 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
assert(esp_a2d_sink_init() == ESP_OK);
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
#else
esp_a2d_mcc_t mcc = {0};
mcc.type = ESP_A2D_MCT_SBC;
mcc.cie.sbc_info.samp_freq = 0xf;
mcc.cie.sbc_info.ch_mode = 0xf;
mcc.cie.sbc_info.block_len = 0xf;
mcc.cie.sbc_info.num_subbands = 0x3;
mcc.cie.sbc_info.alloc_mthd = 0x3;
mcc.cie.sbc_info.max_bitpool = 250;
mcc.cie.sbc_info.min_bitpool = 2;
/* register stream end point, only support mSBC currently */
esp_a2d_sink_register_stream_endpoint(0, &mcc);
esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb);
#endif
/* Get the default value of the delay value */
esp_a2d_sink_get_delay_value();
/* Get local device name */

View File

@@ -92,7 +92,7 @@ static void bt_app_task_handler(void *arg)
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);
xTaskCreate(bt_app_task_handler, "BtAppT", 4096, NULL, configMAX_PRIORITIES - 3, &bt_app_task_handle);
return;
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -174,59 +174,55 @@ extern esp_bd_addr_t peer_addr;
// If you want to connect a specific device, add it's address here
// esp_bd_addr_t peer_addr = {0xac, 0x67, 0xb2, 0x53, 0x77, 0xbe};
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI && CONFIG_BT_HFP_USE_EXTERNAL_CODEC
#define ESP_HFP_RINGBUF_SIZE 3600
static RingbufHandle_t m_rb = NULL;
static esp_hf_sync_conn_hdl_t s_sync_conn_hdl;
static bool s_msbc_air_mode = false;
QueueHandle_t s_audio_buff_queue = NULL;
static int s_audio_buff_cnt = 0;
static void bt_app_hf_client_audio_open(void)
static void bt_app_hf_client_audio_data_cb(esp_hf_sync_conn_hdl_t sync_conn_hdl, esp_hf_audio_buff_t *audio_buf, bool is_bad_frame)
{
m_rb = xRingbufferCreate(ESP_HFP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
}
static void bt_app_hf_client_audio_close(void)
{
if (!m_rb) {
return ;
}
vRingbufferDelete(m_rb);
}
static uint32_t bt_app_hf_client_outgoing_cb(uint8_t *p_buf, uint32_t sz)
{
if (!m_rb) {
return 0;
}
size_t item_size = 0;
uint8_t *data = xRingbufferReceiveUpTo(m_rb, &item_size, 0, sz);
if (item_size == sz) {
memcpy(p_buf, data, item_size);
vRingbufferReturnItem(m_rb, data);
return sz;
} else if (0 < item_size) {
vRingbufferReturnItem(m_rb, data);
return 0;
} else {
// data not enough, do not read
return 0;
}
}
static void bt_app_hf_client_incoming_cb(const uint8_t *buf, uint32_t sz)
{
if (! m_rb) {
if (is_bad_frame) {
esp_hf_client_audio_buff_free(audio_buf);
return;
}
BaseType_t done = xRingbufferSend(m_rb, (uint8_t *)buf, sz, 0);
if (! done) {
ESP_LOGE(BT_HF_TAG, "rb send fail");
if (s_audio_buff_queue && xQueueSend(s_audio_buff_queue, &audio_buf, 0)) {
s_audio_buff_cnt++;
}
else {
esp_hf_client_audio_buff_free(audio_buf);
}
esp_hf_client_outgoing_data_ready();
/* cache some data to add latency */
if (s_audio_buff_cnt < 20) {
return;
}
esp_hf_audio_buff_t *audio_data_to_send;
if (!xQueueReceive(s_audio_buff_queue, &audio_data_to_send, 0)) {
return;
}
s_audio_buff_cnt--;
if (s_msbc_air_mode && audio_data_to_send->data_len > ESP_HF_MSBC_ENCODED_FRAME_SIZE) {
/*
* in mSBC air mode, we may receive a mSBC frame with some padding bytes at the end,
* but esp_hf_client_audio_data_send API do not allow adding padding bytes at the end,
* so we need to remove those padding bytes before send back to peer device.
*/
audio_data_to_send->data_len = ESP_HF_MSBC_ENCODED_FRAME_SIZE;
}
/* send audio data back to AG */
if (esp_hf_client_audio_data_send(s_sync_conn_hdl, audio_data_to_send) != ESP_OK) {
esp_hf_client_audio_buff_free(audio_data_to_send);
ESP_LOGW(BT_HF_TAG, "fail to send audio data");
}
}
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI && CONFIG_BT_HFP_USE_EXTERNAL_CODEC */
/* callback for HF_CLIENT */
void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param)
@@ -255,16 +251,32 @@ void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_
{
ESP_LOGI(BT_HF_TAG, "--audio state %s",
c_audio_state_str[param->audio_stat.state]);
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI && CONFIG_BT_HFP_USE_EXTERNAL_CODEC
if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC) {
s_msbc_air_mode = true;
ESP_LOGI(BT_HF_TAG, "--audio air mode: mSBC , preferred_frame_size: %d", param->audio_stat.preferred_frame_size);
}
else if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED) {
s_msbc_air_mode = false;
ESP_LOGI(BT_HF_TAG, "--audio air mode: CVSD , preferred_frame_size: %d", param->audio_stat.preferred_frame_size);
}
if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED ||
param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC) {
esp_hf_client_register_data_callback(bt_app_hf_client_incoming_cb,
bt_app_hf_client_outgoing_cb);
bt_app_hf_client_audio_open();
s_sync_conn_hdl = param->audio_stat.sync_conn_handle;
s_audio_buff_queue = xQueueCreate(50, sizeof(esp_hf_audio_buff_t*));
esp_hf_client_register_audio_data_callback(bt_app_hf_client_audio_data_cb);
} else if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
bt_app_hf_client_audio_close();
s_sync_conn_hdl = 0;
s_msbc_air_mode = false;
esp_hf_audio_buff_t *buff_to_free = NULL;
while (xQueueReceive(s_audio_buff_queue, &buff_to_free, 0)) {
esp_hf_client_audio_buff_free(buff_to_free);
}
vQueueDelete(s_audio_buff_queue);
s_audio_buff_cnt = 0;
}
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI && CONFIG_BT_HFP_USE_EXTERNAL_CODEC */
break;
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -297,21 +297,21 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
/* when audio codec is configured, this event comes */
case ESP_A2D_AUDIO_CFG_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", a2d->audio_cfg.mcc.type);
esp_a2d_mcc_t *p_mcc = &a2d->audio_cfg.mcc;
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", p_mcc->type);
/* for now only SBC stream is supported */
if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
if (p_mcc->type == ESP_A2D_MCT_SBC) {
int sample_rate = 16000;
int ch_count = 2;
char oct0 = a2d->audio_cfg.mcc.cie.sbc[0];
if (oct0 & (0x01 << 6)) {
if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_32K) {
sample_rate = 32000;
} else if (oct0 & (0x01 << 5)) {
} else if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_44K) {
sample_rate = 44100;
} else if (oct0 & (0x01 << 4)) {
} else if (p_mcc->cie.sbc_info.samp_freq & ESP_A2D_SBC_CIE_SF_48K) {
sample_rate = 48000;
}
if (oct0 & (0x01 << 3)) {
if (p_mcc->cie.sbc_info.ch_mode & ESP_A2D_SBC_CIE_CH_MODE_MONO) {
ch_count = 1;
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
@@ -338,11 +338,14 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
i2s_channel_reconfig_std_slot(tx_chan, &slot_cfg);
i2s_channel_enable(tx_chan);
#endif
ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x",
a2d->audio_cfg.mcc.cie.sbc[0],
a2d->audio_cfg.mcc.cie.sbc[1],
a2d->audio_cfg.mcc.cie.sbc[2],
a2d->audio_cfg.mcc.cie.sbc[3]);
ESP_LOGI(BT_AV_TAG, "Configure audio player: 0x%x-0x%x-0x%x-0x%x-0x%x-%d-%d",
p_mcc->cie.sbc_info.samp_freq,
p_mcc->cie.sbc_info.ch_mode,
p_mcc->cie.sbc_info.block_len,
p_mcc->cie.sbc_info.num_subbands,
p_mcc->cie.sbc_info.alloc_mthd,
p_mcc->cie.sbc_info.min_bitpool,
p_mcc->cie.sbc_info.max_bitpool);
ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate: %d", sample_rate);
}
break;
@@ -364,7 +367,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
if (a2d->a2d_psc_cfg_stat.psc_mask & ESP_A2D_PSC_DELAY_RPT) {
ESP_LOGI(BT_AV_TAG, "Peer device support delay reporting");
} else {
ESP_LOGI(BT_AV_TAG, "Peer device unsupport delay reporting");
ESP_LOGI(BT_AV_TAG, "Peer device unsupported delay reporting");
}
break;
}
@@ -415,13 +418,13 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
}
break;
}
/* when passthrough responsed, this event comes */
/* when passthrough responded, this event comes */
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d, rsp_code %d", rc->psth_rsp.key_code,
rc->psth_rsp.key_state, rc->psth_rsp.rsp_code);
break;
}
/* when metadata responsed, this event comes */
/* when metadata responded, this event comes */
case ESP_AVRC_CT_METADATA_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
free(rc->meta_rsp.attr_text);