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-01-22 17:22:05 +08:00
parent 4fe1529fd4
commit eda7ea3f49
26 changed files with 1833 additions and 172 deletions

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

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

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

@ -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-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,13 @@
#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
#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 +126,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)
@ -593,6 +605,10 @@
#define SBC_ENC_INCLUDED FALSE
#endif
#ifndef BTC_HFP_EXT_CODEC
#define BTC_HFP_EXT_CODEC FALSE
#endif
/******************************************************************************
**
** BTA-layer components
@ -642,6 +658,10 @@
#define BTA_AV_CA_INCLUDED FALSE
#endif
#ifndef BTC_AV_EXT_CODEC
#define BTC_AV_EXT_CODEC FALSE
#endif
#ifndef BTA_AV_SINK_INCLUDED
#define BTA_AV_SINK_INCLUDED FALSE
#endif
@ -654,6 +674,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