refactor(bt/bluedroid): Refactor hfp audio data path

- Refactor audio APIs, optimize audio data path, reduce memory copy operations
- Support using external codec in application layer
This commit is contained in:
linruihao
2025-03-28 14:13:20 +08:00
parent 4fe1529fd4
commit eda7ea3f49
26 changed files with 1833 additions and 172 deletions
@@ -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) */
@@ -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);
@@ -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) */
@@ -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;
@@ -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__ */
@@ -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);