feat(bluedroid): Update LC3-SWB support in bluedroid

- Update LC3 in btc and bta, update AT
- Update LC3 in SCO frame transfer and examples
- improve lc3 transfer speed
- Fix some encoding and decoding bugs
- Rename the functions and macros to fit both msbc and lc3
- Add config dependency
- Add migration guides for bluetooth-classic
- change the statement of "Wide Band Speech" to "Wideband Speech" to fit HFPv1.10
This commit is contained in:
hejiaxin
2026-09-03 11:02:50 +08:00
parent fbb0518b1a
commit 9f6cf1ac3c
43 changed files with 1490 additions and 185 deletions
+10 -2
View File
@@ -238,13 +238,21 @@ config BT_HFP_USE_EXTERNAL_CODEC
for new design.
config BT_HFP_WBS_ENABLE
bool "Wide Band Speech"
bool "Wideband Speech"
depends on BT_HFP_ENABLE && BT_HFP_AUDIO_DATA_PATH_HCI
default y
help
This enables Wide Band Speech. Should disable it when SCO data path is PCM.
This enables Wideband Speech. Should disable it when SCO data path is PCM.
Otherwise there will be no data transmitted via GPIOs.
config BT_HFP_LC3_ENABLE
bool "Super Wideband Speech (LC3-SWB)"
depends on BT_HFP_ENABLE && BT_HFP_WBS_ENABLE && BT_HFP_USE_EXTERNAL_CODEC
default n
help
Enable HFP 1.9 LC3-SWB codec negotiation support.
Requires Wideband Speech (codec negotiation) to be enabled.
menuconfig BT_HID_ENABLED
bool "Classic BT HID"
depends on BT_CLASSIC_ENABLED
@@ -160,6 +160,32 @@ esp_err_t esp_hf_ag_audio_disconnect(esp_bd_addr_t remote_addr)
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_hf_ag_set_codec(esp_bd_addr_t remote_bda, esp_hf_codec_mode_t mode)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (remote_bda == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (mode > ESP_HF_CODEC_LC3) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_SET_CODEC_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&arg.set_codec.remote_addr, remote_bda, sizeof(esp_bd_addr_t));
arg.set_codec.mode = mode;
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_err_t esp_hf_ag_vra_control(esp_bd_addr_t remote_addr, esp_hf_vr_state_t value)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -61,10 +61,11 @@ typedef enum
ESP_HF_ATA_RESPONSE_EVT, /*!< Answer an Incoming Call */
ESP_HF_CHUP_RESPONSE_EVT, /*!< Reject an Incoming Call */
ESP_HF_DIAL_EVT, /*!< Originate an outgoing call with specific number or dial the last number */
ESP_HF_WBS_RESPONSE_EVT, /*!< Codec Status */
ESP_HF_WBS_RESPONSE_EVT, /*!< Result of esp_hf_ag_set_codec() */
ESP_HF_BCS_RESPONSE_EVT, /*!< Final Codec Choice */
ESP_HF_PKT_STAT_NUMS_GET_EVT, /*!< Request number of packet different status */
ESP_HF_PROF_STATE_EVT, /*!< Indicate HF init or deinit complete */
ESP_HF_BAC_RESPONSE_EVT, /*!< Peer codec capabilities from AT+BAC */
} esp_hf_cb_event_t;
/// Dial type of ESP_HF_DIAL_EVT
@@ -210,15 +211,24 @@ typedef union
*/
struct hf_wbs_rep_param {
esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */
esp_hf_wbs_config_t codec; /*!< codec mode CVSD or mSBC */
esp_hf_codec_mode_t codec; /*!< Requested codec CVSD, mSBC or LC3 */
esp_bt_status_t status; /*!< ESP_BT_STATUS_SUCCESS or FAIL */
} wbs_rep; /*!< AG callback param of ESP_HF_WBS_RESPONSE_EVT */
/**
* @brief ESP_HF_BAC_RESPONSE_EVT
*/
struct hf_bac_rep_param {
esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */
uint16_t peer_codecs; /*!< Bitmap: ESP_HF_CODEC_CAP_CVSD | MSBC | LC3 */
} bac_rep; /*!< AG callback param of ESP_HF_BAC_RESPONSE_EVT */
/**
* @brief ESP_HF_BCS_RESPONSE_EVT
*/
struct hf_bcs_rep_param {
esp_bd_addr_t remote_addr; /*!< Remote bluetooth device address */
esp_hf_wbs_config_t mode; /*!< codec mode CVSD or mSBC */
esp_hf_codec_mode_t mode; /*!< Final negotiated codec mode CVSD, mSBC or LC3 */
} bcs_rep; /*!< AG callback param of ESP_HF_BCS_RESPONSE_EVT */
/**
@@ -371,6 +381,26 @@ esp_err_t esp_hf_ag_audio_connect(esp_bd_addr_t remote_bda);
*/
esp_err_t esp_hf_ag_audio_disconnect(esp_bd_addr_t remote_bda);
/**
*
* @brief Set preferred codec for subsequent SCO connections with a remote HF.
* Should be called after receiving ESP_HF_BAC_RESPONSE_EVT to know peer capabilities.
* Result is reported via ESP_HF_WBS_RESPONSE_EVT.
* As a precondition to use this API, Service Level Connection shall exist with HFP client.
*
* @param[in] remote_bda: remote bluetooth HFP client device address
* @param[in] mode: ESP_HF_CODEC_CVSD, ESP_HF_CODEC_MSBC, ESP_HF_CODEC_LC3,
* or ESP_HF_CODEC_NONE to reset to stack default
*
* @return
* - ESP_OK: request sent to lower layer
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled or SLC not connected
* - ESP_ERR_INVALID_ARG: invalid codec mode or address
* - ESP_FAIL: others
*
*/
esp_err_t esp_hf_ag_set_codec(esp_bd_addr_t remote_bda, esp_hf_codec_mode_t mode);
/**
*
* @brief Response of Volume Recognition Command(AT+VRA) from HFP client.
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -35,6 +35,7 @@ typedef enum {
ESP_HF_CLIENT_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */
ESP_HF_CLIENT_AUDIO_STATE_CONNECTED, /*!< audio connection is established */
ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */
ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_LC3, /*!< LC3-SWB audio connection is established */
} esp_hf_client_audio_state_t;
/// in-band ring tone state
@@ -67,6 +68,9 @@ typedef struct {
#define ESP_HF_CLIENT_PEER_FEAT_HF_IND 0x400 /*!< HF Indicators */
#define ESP_HF_CLIENT_PEER_FEAT_ESCO_S4 0x800 /*!< eSCO S4 Setting Supported */
/* HFP 1.9 - local HF feature, maps to SDP SupportedFeatures bit 8 */
#define ESP_HF_CLIENT_FEAT_SWB 0x00010000 /*!< Super Wideband Speech */
/* CHLD feature masks of AG */
#define ESP_HF_CLIENT_CHLD_FEAT_REL 0x01 /*!< 0 Release waiting call or held calls */
#define ESP_HF_CLIENT_CHLD_FEAT_REL_ACC 0x02 /*!< 1 Release active calls and accept other waiting or held call */
@@ -47,8 +47,9 @@ typedef enum {
typedef enum {
ESP_HF_AUDIO_STATE_DISCONNECTED = 0, /*!< audio connection released */
ESP_HF_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */
ESP_HF_AUDIO_STATE_CONNECTED, /*!< audio connection is established */
ESP_HF_AUDIO_STATE_CONNECTED, /*!< CVSD audio connection is established */
ESP_HF_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */
ESP_HF_AUDIO_STATE_CONNECTED_LC3, /*!< LC3-SWB audio connection is established */
} esp_hf_audio_state_t;
/// Bluetooth HFP audio volume type
@@ -179,13 +180,32 @@ typedef enum {
ESP_HF_CALL_WAITING_ACTIVE, /*!< active call waiting */
} esp_hf_call_waiting_status_t;
/* WBS codec setting */
/* codec setting - values match HFP Codec ID in AT+BAC/+BCS (Appendix B) */
typedef enum
{
ESP_HF_WBS_NONE, /*!< No Wideband Speech (WBS) codec support */
ESP_HF_WBS_NO, /*!< Wideband Speech (WBS) codec is not enabled */
ESP_HF_WBS_YES /*!< Wideband Speech (WBS) codec is enabled */
}esp_hf_wbs_config_t;
ESP_HF_CODEC_NONE = 0, /*!< No codec negotiated */
ESP_HF_CODEC_CVSD = 1, /*!< Narrowband Speech (CVSD) */
ESP_HF_CODEC_MSBC = 2, /*!< Wideband Speech (mSBC) */
ESP_HF_CODEC_LC3 = 3 /*!< Super Wideband Speech (LC3-SWB) */
} esp_hf_codec_mode_t;
/*!< @deprecated Renamed to ESP_HF_CODEC_NONE */
#define ESP_HF_WBS_NONE (ESP_HF_CODEC_NONE) /*!< No Wideband Speech (WBS) codec support */
/*!< @deprecated Renamed to ESP_HF_CODEC_CVSD */
#define ESP_HF_WBS_NO (ESP_HF_CODEC_CVSD) /*!< Wideband Speech (WBS) codec is not enabled */
/*!< @deprecated Renamed to ESP_HF_CODEC_MSBC */
#define ESP_HF_WBS_YES (ESP_HF_CODEC_MSBC) /*!< Wideband Speech (WBS) codec is enabled */
/*!< @deprecated esp_hf_wbs_config_t is changed to esp_hf_codec_mode_t */
typedef esp_hf_codec_mode_t esp_hf_wbs_config_t;
/** @brief Codec capability bitmap for AT+BAC, matches BTA_AG_CODEC_* flags */
#define ESP_HF_CODEC_CAP_CVSD (1u << (ESP_HF_CODEC_CVSD - 1)) /*!< 0x01 */
#define ESP_HF_CODEC_CAP_MSBC (1u << (ESP_HF_CODEC_MSBC - 1)) /*!< 0x02 */
#define ESP_HF_CODEC_CAP_LC3 (1u << (ESP_HF_CODEC_LC3 - 1)) /*!< 0x04 */
/// Bluetooth HFP RFCOMM connection and service level connection status
typedef enum {
@@ -261,6 +281,17 @@ typedef enum {
#define ESP_HF_MSBC_BITPOOL 26 /*!< mSBC bitpool */
/* frame size after mSBC encoded */
#define ESP_HF_MSBC_ENCODED_FRAME_SIZE 57 /*!< mSBC frame size */
#define ESP_HF_MSBC_SAMPLING_RATE_HZ 16000 /*!< mSBC PCM sampling rate */
#define ESP_HF_MSBC_H2_HEADER_LEN 2 /*!< H2 sync header length */
#define ESP_HF_MSBC_ENCODED_FRAME_SIZE_WITH_H2 (ESP_HF_MSBC_ENCODED_FRAME_SIZE + ESP_HF_MSBC_H2_HEADER_LEN)
/* Since HFP uses a fixed set of LC3-SWB codec parameters (HFP 1.9 Table 6.15) */
#define ESP_HF_LC3_SAMPLING_RATE_HZ 32000 /*!< LC3-SWB PCM sampling rate */
#define ESP_HF_LC3_FRAME_DURATION_US 7500 /*!< LC3-SWB frame duration */
#define ESP_HF_LC3_ENCODED_FRAME_SIZE 58 /*!< LC3 payload size (excl. H2 header) */
#define ESP_HF_LC3_H2_HEADER_LEN 2 /*!< H2 sync header length */
#define ESP_HF_LC3_ENCODED_FRAME_SIZE_WITH_H2 (ESP_HF_LC3_ENCODED_FRAME_SIZE + ESP_HF_LC3_H2_HEADER_LEN)
#define ESP_HF_LC3_BITRATE_BPS 61867 /*!< Resulting bit rate (excl. H2 header) */
/**
* @brief HFP audio buffer
@@ -805,7 +805,8 @@ void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
/* Check if the requested codec type is valid */
if((codec_type != BTA_AG_CODEC_NONE) &&
(codec_type != BTA_AG_CODEC_CVSD) &&
(codec_type != BTA_AG_CODEC_MSBC)) {
(codec_type != BTA_AG_CODEC_MSBC) &&
(codec_type != BTA_AG_CODEC_LC3)) {
val.hdr.status = BTA_AG_FAIL_RESOURCES;
APPL_TRACE_ERROR("%s error: unsupported codec type %d", __func__, codec_type);
if (bta_ag_cb.p_cback) {
@@ -355,7 +355,7 @@ void BTA_AgCiData(UINT16 handle)
*******************************************************************************/
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 */
/* reserve trailing space for H2 header and optional eSCO padding (mSBC: 57+2+1=60, LC3: 58+2=60) */
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 */
@@ -694,6 +694,10 @@ static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB *p_scb, char *p_s)
retval |= BTA_AG_CODEC_MSBC;
break;
case UUID_CODEC_LC3:
retval |= BTA_AG_CODEC_LC3;
break;
default:
APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", uuid_codec);
break;
@@ -707,6 +711,32 @@ static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB *p_scb, char *p_s)
}
return (retval);
}
/*******************************************************************************
**
** Function bta_ag_select_sco_codec_from_peer
**
** Description Select preferred SCO codec from peer capabilities (LC3 > mSBC > CVSD).
**
** Returns void
**
*******************************************************************************/
static void bta_ag_select_sco_codec_from_peer(tBTA_AG_SCB *p_scb)
{
#if UC_BT_HFP_LC3_ENABLE
if (p_scb->peer_codecs & BTA_AG_CODEC_LC3) {
p_scb->sco_codec = BTA_AG_CODEC_LC3;
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to LC3");
} else
#endif
if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
p_scb->sco_codec = BTA_AG_CODEC_MSBC;
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to MSBC");
} else {
p_scb->sco_codec = BTA_AG_CODEC_CVSD;
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to CVSD");
}
}
#endif /* #if (BTM_WBS_INCLUDED == TRUE ) */
/*******************************************************************************
@@ -1167,13 +1197,7 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
p_scb->peer_codecs = bta_ag_parse_bac(p_scb, p_arg);
p_scb->codec_updated = TRUE;
if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
p_scb->sco_codec = UUID_CODEC_MSBC;
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to MSBC");
} else {
p_scb->sco_codec = UUID_CODEC_CVSD;
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to CVSD");
}
bta_ag_select_sco_codec_from_peer(p_scb);
/* The above logic sets the stack preferred codec based on local and peer codec
capabilities. This can be overridden by the application depending on its preference
using the bta_ag_setcodec API. We send the peer_codecs to the application. */
@@ -1204,6 +1228,10 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
codec_type = BTA_AG_CODEC_MSBC;
break;
case UUID_CODEC_LC3:
codec_type = BTA_AG_CODEC_LC3;
break;
default:
APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg);
codec_type = 0xFFFF;
@@ -1675,6 +1703,10 @@ void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
codec_uuid = UUID_CODEC_MSBC;
break;
case BTA_AG_CODEC_LC3:
codec_uuid = UUID_CODEC_LC3;
break;
default:
APPL_TRACE_ERROR("bta_ag_send_bcs: unknown codec %d, use CVSD", p_scb->sco_codec);
codec_uuid = UUID_CODEC_CVSD;
@@ -27,6 +27,7 @@
#include "bta_ag_int.h"
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "common/bt_target.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_ag_co.h"
#include "bta/utl.h"
@@ -286,7 +287,11 @@ const tBTA_AG_ST_TBL bta_ag_st_tbl[] =
/*****************************************************************************
** Global data
*****************************************************************************/
#if UC_BT_HFP_LC3_ENABLE
const uint16_t bta_ag_version = HFP_VERSION_1_9;
#else
const uint16_t bta_ag_version = HFP_VERSION_1_8;
#endif
/* AG control block */
#if BTA_DYNAMIC_MEMORY == FALSE
tBTA_AG_CB bta_ag_cb;
@@ -129,6 +129,22 @@ static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] =
BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */
}
};
static BOOLEAN bta_ag_is_transparent_codec(tBTM_SCO_CODEC_TYPE codec)
{
return (codec == BTM_SCO_CODEC_MSBC || codec == BTM_SCO_CODEC_LC3);
}
static UINT32 bta_ag_sco_get_pcm_rate(tBTM_SCO_CODEC_TYPE codec)
{
if (codec == BTM_SCO_CODEC_LC3) {
return BTA_HFP_SCO_SAMP_RATE_32K;
}
if (codec == BTM_SCO_CODEC_MSBC) {
return BTA_HFP_SCO_SAMP_RATE_16K;
}
return BTA_HFP_SCO_SAMP_RATE_8K;
}
#else
#define BTA_AG_NUM_CODECS 2
#define BTA_AG_ESCO_SETTING_IDX_CVSD 0 /* eSCO setting for CVSD S3 */
@@ -282,24 +298,33 @@ static void bta_ag_sco_disc_cback(UINT16 sco_idx)
#if (BTM_WBS_INCLUDED == TRUE )
/* Restore settings */
if(bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC)
if (bta_ag_is_transparent_codec(bta_ag_cb.sco.p_curr_scb->inuse_codec))
{
/* set_sco_codec(BTM_SCO_CODEC_NONE); we should get a close */
BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);
/* If SCO open was initiated by AG and failed for mSBC, then attempt
mSBC with T1 settings i.e. 'Safe Settings'. If this fails, then switch to CVSD */
/* If SCO open was initiated by AG and failed for mSBC/LC3, then attempt
transparent codec with T1 settings i.e. 'Safe Settings'. If this fails,
fall back to mSBC (from LC3) or CVSD. */
if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb))
{
if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2)
{
APPL_TRACE_DEBUG("Fallback to mSBC T1 settings");
APPL_TRACE_DEBUG("Fallback to transparent codec T1 settings");
bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T1;
}
else
{
APPL_TRACE_DEBUG("Fallback to CVSD settings");
bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE;
if (bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_LC3 &&
(bta_ag_cb.sco.p_curr_scb->peer_codecs & BTA_AG_CODEC_MSBC)) {
APPL_TRACE_DEBUG("Fallback from LC3 to mSBC");
bta_ag_cb.sco.p_curr_scb->sco_codec = BTA_AG_CODEC_MSBC;
bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
bta_ag_cb.sco.p_curr_scb->codec_updated = TRUE;
} else {
APPL_TRACE_DEBUG("Fallback to CVSD settings");
bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE;
}
}
}
}
@@ -482,7 +507,11 @@ static UINT16 bta_ag_sco_get_frame_size(tBTA_AG_SCB *p_scb)
frame_size = p_scb->out_pkt_len;
break;
case BTM_SCO_AIR_MODE_TRANSPNT:
frame_size = BTA_AG_MSBC_FRAME_SIZE;
if (p_scb->inuse_codec == BTA_AG_CODEC_LC3) {
frame_size = BTA_AG_LC3_FRAME_PAYLOAD_SIZE;
} else {
frame_size = BTA_AG_MSBC_FRAME_PAYLOAD_SIZE;
}
break;
default:
break;
@@ -555,9 +584,9 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
#if (BTM_WBS_INCLUDED == TRUE)
if ((p_scb->sco_codec == BTM_SCO_CODEC_MSBC) && !p_scb->codec_fallback && !p_scb->retry_with_sco_only)
if (bta_ag_is_transparent_codec(p_scb->sco_codec) && !p_scb->codec_fallback && !p_scb->retry_with_sco_only)
{
esco_codec = BTM_SCO_CODEC_MSBC;
esco_codec = p_scb->sco_codec;
}
if (p_scb->codec_fallback)
{
@@ -566,8 +595,8 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
p_scb->codec_updated = TRUE;
}
/* If WBS included, use CVSD by default, index is 0 for CVSD by initialization */
/* If eSCO codec is mSBC, index is T2 or T1 */
if (esco_codec == BTM_SCO_CODEC_MSBC)
/* If eSCO codec is mSBC or LC3, index is T2 or T1 */
if (bta_ag_is_transparent_codec(esco_codec))
{
if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2)
{
@@ -625,12 +654,12 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
||!((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_AG_NO_EDR_ESCO))
{
#if (BTM_WBS_INCLUDED == TRUE)
if (esco_codec != BTA_AG_CODEC_MSBC)
if (!bta_ag_is_transparent_codec(esco_codec))
{
p_scb->retry_with_sco_only = TRUE;
APPL_TRACE_API("Setting retry_with_sco_only to TRUE");
}
else /* Do not use SCO when using mSBC */
else /* Do not use SCO when using mSBC or LC3 */
{
p_scb->retry_with_sco_only = FALSE;
APPL_TRACE_API("Setting retry_with_sco_only to FALSE");
@@ -661,7 +690,7 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
/* This setting may not be necessary */
/* To be verified with stable 2049 boards */
if (esco_codec == BTA_AG_CODEC_MSBC)
if (bta_ag_is_transparent_codec(esco_codec))
BTM_WriteVoiceSettings(BTM_VOICE_SETTING_TRANS);
else
BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD);
@@ -675,13 +704,12 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
#if (BTM_SCO_HCI_INCLUDED == TRUE)
#if (BTM_WBS_INCLUDED == TRUE)
if (esco_codec == BTA_AG_CODEC_MSBC) {
pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_16K;
} else
#endif
pcm_sample_rate = bta_ag_sco_get_pcm_rate(esco_codec);
#else
{
pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K;
}
#endif
sco_route = bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id);
#endif
@@ -730,7 +758,7 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
*******************************************************************************/
BOOLEAN bta_ag_attempt_msbc_safe_settings(tBTA_AG_SCB *p_scb)
{
if (p_scb->svc_conn && p_scb->sco_codec == BTM_SCO_CODEC_MSBC &&
if (p_scb->svc_conn && bta_ag_is_transparent_codec(p_scb->sco_codec) &&
p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1)
return TRUE;
else
@@ -791,6 +819,7 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb)
bta_ag_send_bcs(p_scb, NULL);
/* Start timer to handle timeout */
bta_sys_stop_timer(&p_scb->cn_timer);
p_scb->cn_timer.p_cback = (TIMER_CBACK*)&bta_ag_cn_timer_cback;
p_scb->cn_timer.param = (UINT32)p_scb;
bta_sys_start_timer(&p_scb->cn_timer, 0, BTA_AG_CODEC_NEGO_TIMEOUT);
@@ -1588,7 +1617,9 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
#if (BTM_WBS_INCLUDED == TRUE)
/* call app callback */
if (p_scb->sco_codec == BTA_AG_CODEC_MSBC) {
if (p_scb->inuse_codec == BTA_AG_CODEC_LC3) {
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_LC3_OPEN_EVT);
} else if (p_scb->inuse_codec == BTA_AG_CODEC_MSBC) {
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_MSBC_OPEN_EVT);
} else {
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT);
@@ -1905,24 +1936,43 @@ static void bta_ag_sco_data_send_cvsd(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
/*******************************************************************************
**
** Function bta_ag_sco_data_send_msbc
** Function bta_ag_sco_transparent_air_pad
**
** Description Process SCO data of mSBC air mode
** Description Padding bytes after H2 + codec payload to fill eSCO packet.
**
** Returns Number of padding bytes
**
*******************************************************************************/
static UINT8 bta_ag_sco_transparent_air_pad(UINT16 frame_payload_size, UINT16 out_pkt_len)
{
UINT16 air_len = frame_payload_size + BTA_AG_H2_HEADER_LEN;
if (out_pkt_len > air_len) {
return (UINT8)(out_pkt_len - air_len);
}
return 0;
}
/*******************************************************************************
**
** Function bta_ag_sco_data_send_transparent
**
** Description Process SCO data of transparent air mode (mSBC or LC3-SWB)
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
static void bta_ag_sco_data_send_transparent(tBTA_AG_SCB *p_scb, BT_HDR *p_buf, UINT16 frame_payload_size)
{
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) {
UINT8 air_pad = bta_ag_sco_transparent_air_pad(frame_payload_size, out_pkt_len);
if (p_buf->len == frame_payload_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;
p_buf->len += BTA_AG_H2_HEADER_LEN + air_pad;
if (out_pkt_len == BTA_AG_SCO_OUT_PKT_LEN_2EV3) {
/* mSBC frame can be send directly */
@@ -1950,8 +2000,8 @@ static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *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 */
else if (p_buf->len != 0 && p_buf->len % frame_payload_size == 0) {
/* multiple mSBC or LC3-SWB 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) {
@@ -1966,9 +2016,9 @@ static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
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;
memcpy(p_data2, p_data, frame_payload_size);
p_data += frame_payload_size;
total_len -= frame_payload_size;
bta_ag_write_sco_data(p_scb, p_buf2, NULL);
}
}
@@ -2001,7 +2051,7 @@ static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
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;
UINT16 rem_payload = BTA_AG_MSBC_FRAME_SIZE - (BTA_AG_SCO_OUT_PKT_LEN_EV3 - BTA_AG_H2_HEADER_LEN);
UINT16 rem_payload = frame_payload_size - (BTA_AG_SCO_OUT_PKT_LEN_EV3 - BTA_AG_H2_HEADER_LEN);
memcpy(p_data3, p_data, rem_payload);
p_data += rem_payload;
total_len -= rem_payload;
@@ -2014,7 +2064,7 @@ static void bta_ag_sco_data_send_msbc(tBTA_AG_SCB *p_scb, BT_HDR *p_buf)
osi_free(p_buf);
}
else {
APPL_TRACE_WARNING("%s, unaccepted data len: %d", __FUNCTION__, p_buf->len);
APPL_TRACE_WARNING("%s, unaccepted data len: %d (frame %d)", __FUNCTION__, p_buf->len, frame_payload_size);
osi_free(p_buf);
}
}
@@ -2048,7 +2098,11 @@ void bta_ag_sco_data_send(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
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);
if (p_scb->inuse_codec == BTA_AG_CODEC_LC3) {
bta_ag_sco_data_send_transparent(p_scb, p_buf, BTA_AG_LC3_FRAME_PAYLOAD_SIZE);
} else {
bta_ag_sco_data_send_transparent(p_scb, p_buf, BTA_AG_MSBC_FRAME_PAYLOAD_SIZE);
}
break;
default:
osi_free(p_buf);
@@ -25,6 +25,7 @@
#include <string.h>
#include "bta_ag_int.h"
#include "common/bt_target.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
@@ -161,7 +162,11 @@ BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn,
/* add profile descriptor list */
if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) {
profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
#if UC_BT_HFP_LC3_ENABLE
version = HFP_VERSION_1_9;
#else
version = HFP_VERSION_1_8;
#endif
} else {
profile_uuid = UUID_SERVCLASS_HEADSET;
version = HSP_VERSION_1_2;
@@ -188,6 +193,11 @@ BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn,
if (codec_supported) {
features |= 0x0020;
}
#if UC_BT_HFP_LC3_ENABLE
if (codec_supported) {
features |= 0x0100; /* SWB supported in SDP */
}
#endif
UINT16_TO_BE_FIELD(buf, features);
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf);
}
@@ -50,6 +50,7 @@
#define HFP_VERSION_1_6 0x0106
#define HFP_VERSION_1_7 0x0107
#define HFP_VERSION_1_8 0x0108
#define HFP_VERSION_1_9 0x0109
#define HSP_VERSION_1_0 0x0100
#define HSP_VERSION_1_2 0x0102
@@ -102,7 +103,9 @@
/* 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
#define BTA_AG_MSBC_FRAME_PAYLOAD_SIZE 57
#define BTA_AG_LC3_FRAME_PAYLOAD_SIZE 58
#define BTA_AG_LC3_VOHCI_FRAME_SIZE 60
/* 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 */
@@ -63,6 +63,7 @@ static const uint8_t bta_hf_client_cb_data_size[] = {
sizeof(tBTA_HF_CLIENT_VAL), // #define BTA_HF_CLIENT_RING_INDICATION 21
0, // #define BTA_HF_CLIENT_DISABLE_EVT 22
sizeof(tBTA_SCO_PKT_STAT_NUMS), // #define BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT 23
sizeof(tBTA_HF_CLIENT_AUDIO_STAT), // #define BTA_HF_CLIENT_AUDIO_LC3_OPEN_EVT 24
};
/*****************************************************************************
** External Function Declarations
@@ -368,7 +369,7 @@ void BTA_HfClientAudioBuffAlloc(UINT16 size, UINT8 **pp_buff, UINT8 **pp_data)
return;
}
/* reserve 1 byte at last, when the size is mSBC frame size (57), then we got a buffer that can hold 60 bytes data */
/* reserve trailing space for H2 header and optional eSCO padding (mSBC: 57+2+1=60, LC3: 58+2=60) */
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 */
@@ -22,6 +22,8 @@
#include "bta/bta_hf_client_api.h"
#include "bta_hf_client_int.h"
#include "common/bt_target.h"
#include "stack/sdpdefs.h"
#include "stack/port_api.h"
#include "osi/allocator.h"
@@ -488,14 +490,49 @@ static void bta_hf_client_handle_ciev(UINT32 index, UINT32 value)
}
}
static tBTM_SCO_CODEC_TYPE bta_hf_client_hfp_codec_id_to_btm(UINT32 hfp_codec_id)
{
switch (hfp_codec_id) {
case UUID_CODEC_MSBC:
return BTM_SCO_CODEC_MSBC;
case UUID_CODEC_LC3:
return BTM_SCO_CODEC_LC3;
case UUID_CODEC_CVSD:
return BTM_SCO_CODEC_CVSD;
default:
/* Accept BTM bitmask values for backward compatibility */
if (hfp_codec_id == BTM_SCO_CODEC_MSBC || hfp_codec_id == BTM_SCO_CODEC_LC3 ||
hfp_codec_id == BTM_SCO_CODEC_CVSD) {
return (tBTM_SCO_CODEC_TYPE)hfp_codec_id;
}
return BTM_SCO_CODEC_CVSD;
}
}
static UINT32 bta_hf_client_btm_codec_to_hfp_id(tBTM_SCO_CODEC_TYPE btm_codec)
{
switch (btm_codec) {
case BTM_SCO_CODEC_MSBC:
return UUID_CODEC_MSBC;
case BTM_SCO_CODEC_LC3:
return UUID_CODEC_LC3;
case BTM_SCO_CODEC_CVSD:
default:
return UUID_CODEC_CVSD;
}
}
static void bta_hf_client_handle_bcs(UINT32 codec)
{
APPL_TRACE_DEBUG("%s %u", __FUNCTION__, codec);
tBTM_SCO_CODEC_TYPE btm_codec = bta_hf_client_hfp_codec_id_to_btm(codec);
if (codec == BTM_SCO_CODEC_CVSD ||
(codec == BTM_SCO_CODEC_MSBC && bta_hf_client_cb.msbc_enabled == TRUE)) {
bta_hf_client_cb.scb.negotiated_codec = codec;
bta_hf_client_send_at_bcs(codec);
APPL_TRACE_DEBUG("%s hfp_id %u btm_codec %u", __FUNCTION__, codec, btm_codec);
if (btm_codec == BTM_SCO_CODEC_CVSD ||
(btm_codec == BTM_SCO_CODEC_MSBC && bta_hf_client_cb.msbc_enabled == TRUE) ||
(btm_codec == BTM_SCO_CODEC_LC3 && bta_hf_client_cb.lc3_enabled == TRUE)) {
bta_hf_client_cb.scb.negotiated_codec = btm_codec;
bta_hf_client_send_at_bcs(btm_codec);
} else {
bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
bta_hf_client_send_at_bac();
@@ -1427,7 +1464,8 @@ void bta_hf_client_send_at_brsf(void)
APPL_TRACE_DEBUG("%s", __FUNCTION__);
at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BRSF=%u\r", bta_hf_client_cb.scb.features);
at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BRSF=%u\r",
bta_hf_client_cb.scb.features & BTA_HF_CLIENT_BRSF_FEAT_SPEC);
at_len = MIN(at_len, BTA_HF_CLIENT_AT_MAX_LEN - 1);
if (at_len < 0) {
@@ -1446,6 +1484,11 @@ void bta_hf_client_send_at_bac(void)
APPL_TRACE_DEBUG("%s", __FUNCTION__);
#if UC_BT_HFP_LC3_ENABLE
if (bta_hf_client_cb.lc3_enabled && bta_hf_client_cb.msbc_enabled) {
buf = "AT+BAC=1,2,3\r";
} else
#endif
if (bta_hf_client_cb.msbc_enabled) {
buf = "AT+BAC=1,2\r";
} else {
@@ -1465,7 +1508,8 @@ void bta_hf_client_send_at_bcs(UINT32 codec)
return;
}
at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BCS=%u\r", codec);
at_len = snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN, "AT+BCS=%u\r",
bta_hf_client_btm_codec_to_hfp_id((tBTM_SCO_CODEC_TYPE)codec));
at_len = MIN(at_len, BTA_HF_CLIENT_AT_MAX_LEN - 1);
if (at_len < 0) {
@@ -20,6 +20,7 @@
#include <string.h>
#include <stdlib.h>
#include "common/bt_defs.h"
#include "common/bt_target.h"
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_hf_client_api.h"
@@ -251,7 +252,11 @@ const tBTA_HF_CLIENT_ST_TBL bta_hf_client_st_tbl[] = {
bta_hf_client_st_closing
};
#if UC_BT_HFP_LC3_ENABLE
const int bta_hf_client_version = HFP_HF_VERSION_1_9;
#else
const int bta_hf_client_version = HFP_HF_VERSION_1_7;
#endif
/* HF Client control block */
#if BTA_DYNAMIC_MEMORY == FALSE
@@ -460,6 +465,16 @@ static void bta_hf_client_api_enable(tBTA_HF_CLIENT_DATA *p_data)
bta_hf_client_cb.msbc_enabled = FALSE;
}
#if UC_BT_HFP_LC3_ENABLE
if (bta_hf_client_version >= HFP_HF_VERSION_1_9) {
bta_hf_client_cb.lc3_enabled = TRUE;
} else {
bta_hf_client_cb.lc3_enabled = FALSE;
}
#else
bta_hf_client_cb.lc3_enabled = FALSE;
#endif
bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
/* set same setting as AG does */
@@ -43,6 +43,22 @@ static BOOLEAN s_bta_hf_client_bqb_esco_s1_flag = false;
#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S3 1 /* eSCO setting for CVSD S3 */
#define BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2 2 /* eSCO setting for mSBC T2 */
#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S4 3 /* eSCO setting for CVSD S4 */
static BOOLEAN bta_hf_client_is_transparent_codec(tBTM_SCO_CODEC_TYPE codec)
{
return (codec == BTM_SCO_CODEC_MSBC || codec == BTM_SCO_CODEC_LC3);
}
static UINT32 bta_hf_client_sco_get_pcm_rate(tBTM_SCO_CODEC_TYPE codec)
{
if (codec == BTM_SCO_CODEC_LC3) {
return BTA_HFP_SCO_SAMP_RATE_32K;
}
if (codec == BTM_SCO_CODEC_MSBC) {
return BTA_HFP_SCO_SAMP_RATE_16K;
}
return BTA_HFP_SCO_SAMP_RATE_8K;
}
#define BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S1 4 /* eSCO setting for CVSD S1 */
static const tBTM_ESCO_PARAMS bta_hf_client_esco_params[] = {
@@ -197,7 +213,11 @@ static UINT16 bta_hf_client_sco_get_frame_size(void)
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;
if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_LC3) {
frame_size = BTA_HF_CLIENT_LC3_FRAME_SIZE;
} else {
frame_size = BTA_HF_CLIENT_MSBC_FRAME_SIZE;
}
break;
default:
break;
@@ -291,7 +311,7 @@ static void bta_hf_client_sco_conn_rsp(tBTM_ESCO_CONN_REQ_EVT_DATA *p_data)
index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S1;
}
#endif /* BT_HF_CLIENT_BQB_INCLUDED */
} else if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) {
} else if (bta_hf_client_is_transparent_codec(bta_hf_client_cb.scb.negotiated_codec)) {
index = BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2;
}
}
@@ -302,7 +322,7 @@ static void bta_hf_client_sco_conn_rsp(tBTM_ESCO_CONN_REQ_EVT_DATA *p_data)
#if (BTM_SCO_HCI_INCLUDED == TRUE )
bta_hf_client_co_audio_state(bta_hf_client_cb.scb.sco_idx, SCO_STATE_SETUP, 0);
pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K;
pcm_sample_rate = bta_hf_client_sco_get_pcm_rate(bta_hf_client_cb.scb.negotiated_codec);
/* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */
BTM_ConfigScoPath(bta_hf_client_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, 0),
@@ -460,25 +480,34 @@ static void bta_hf_client_sco_data_send_cvsd(BT_HDR *p_buf, UINT16 out_pkt_len)
}
}
static UINT8 bta_hf_client_sco_transparent_air_pad(UINT16 frame_payload_size, UINT16 out_pkt_len)
{
UINT16 air_len = frame_payload_size + BTA_HF_CLIENT_H2_HEADER_LEN;
if (out_pkt_len > air_len) {
return (UINT8)(out_pkt_len - air_len);
}
return 0;
}
/*******************************************************************************
**
** Function bta_hf_client_sco_data_send_msbc
** Function bta_hf_client_sco_data_send_transparent
**
** Description Process SCO data of mSBC air mode
** Description Process SCO data of transparent air mode (mSBC or LC3-SWB)
**
**
** Returns void
**
*******************************************************************************/
static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT16 out_pkt_len)
static void bta_hf_client_sco_data_send_transparent(BT_HDR *p_buf, UINT16 out_pkt_len, UINT16 frame_payload_size)
{
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 */
UINT8 air_pad = bta_hf_client_sco_transparent_air_pad(frame_payload_size, out_pkt_len);
if (p_buf->len == frame_payload_size && p_buf->offset >= BTA_HF_CLIENT_BUFF_OFFSET_MIN + BTA_HF_CLIENT_H2_HEADER_LEN) {
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;
p_buf->len += BTA_HF_CLIENT_H2_HEADER_LEN + air_pad;
if (out_pkt_len == BTA_HF_CLIENT_SCO_OUT_PKT_LEN_2EV3) {
/* mSBC frame can be send directly */
@@ -506,8 +535,7 @@ static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT16 out_pkt_len)
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 */
else if (p_buf->len != 0 && p_buf->len % frame_payload_size == 0) {
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) {
@@ -522,9 +550,9 @@ static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT16 out_pkt_len)
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;
memcpy(p_data2, p_data, frame_payload_size);
p_data += frame_payload_size;
total_len -= frame_payload_size;
bta_hf_client_write_sco_data(p_buf2, NULL);
}
}
@@ -557,7 +585,7 @@ static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT16 out_pkt_len)
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;
UINT16 rem_payload = BTA_HF_CLIENT_MSBC_FRAME_SIZE - (BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 - BTA_HF_CLIENT_H2_HEADER_LEN);
UINT16 rem_payload = frame_payload_size - (BTA_HF_CLIENT_SCO_OUT_PKT_LEN_EV3 - BTA_HF_CLIENT_H2_HEADER_LEN);
memcpy(p_data3, p_data, rem_payload);
p_data += rem_payload;
total_len -= rem_payload;
@@ -570,7 +598,7 @@ static void bta_hf_client_sco_data_send_msbc(BT_HDR *p_buf, UINT16 out_pkt_len)
osi_free(p_buf);
}
else {
APPL_TRACE_WARNING("%s, unaccepted data len: %d", __FUNCTION__, p_buf->len);
APPL_TRACE_WARNING("%s, unaccepted data len: %d (frame %d)", __FUNCTION__, p_buf->len, frame_payload_size);
osi_free(p_buf);
}
}
@@ -604,7 +632,11 @@ void bta_hf_client_sco_data_send(tBTA_HF_CLIENT_DATA *p_data)
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);
if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_LC3) {
bta_hf_client_sco_data_send_transparent(p_buf, out_pkt_len, BTA_HF_CLIENT_LC3_FRAME_SIZE);
} else {
bta_hf_client_sco_data_send_transparent(p_buf, out_pkt_len, BTA_HF_CLIENT_MSBC_FRAME_SIZE);
}
break;
default:
osi_free(p_buf);
@@ -778,7 +810,7 @@ static void bta_hf_client_sco_create(BOOLEAN is_orig)
(bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_ESCO_S4)) {
index = BTA_HF_CLIENT_ESCO_PARAM_IDX_CVSD_S4;
}
} else if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) {
} else if (bta_hf_client_is_transparent_codec(bta_hf_client_cb.scb.negotiated_codec)) {
index = BTA_HF_CLIENT_ESCO_PARAM_IDX_MSBC_T2;
}
params = bta_hf_client_esco_params[index];
@@ -810,7 +842,7 @@ static void bta_hf_client_sco_create(BOOLEAN is_orig)
/* Allow any platform specific pre-SCO set up to take place */
bta_hf_client_co_audio_state(bta_hf_client_cb.scb.sco_idx, SCO_STATE_SETUP, 0);
pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K;
pcm_sample_rate = bta_hf_client_sco_get_pcm_rate(bta_hf_client_cb.scb.negotiated_codec);
sco_route = bta_hf_client_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, 0);
/* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */
@@ -1165,7 +1197,9 @@ void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA *p_data)
bta_hf_client_cb.scb.out_pkt_len, BTA_HF_CLIENT_CI_SCO_DATA_EVT);
#endif
if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) {
if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_LC3) {
bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_LC3_OPEN_EVT);
} else if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC) {
bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT);
} else {
bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_OPEN_EVT);
@@ -26,6 +26,7 @@
#include <string.h>
#include "bta/bta_api.h"
#include "common/bt_target.h"
#include "bta/bta_sys.h"
#include "common/bt_defs.h"
#include "bta/bta_hf_client_api.h"
@@ -114,7 +115,11 @@ BOOLEAN bta_hf_client_add_record(char *p_service_name, UINT8 scn,
/* add profile descriptor list */
profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
#if UC_BT_HFP_LC3_ENABLE
version = HFP_VERSION_1_9;
#else
version = HFP_VERSION_1_8;
#endif
result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
@@ -150,6 +155,12 @@ BOOLEAN bta_hf_client_add_record(char *p_service_name, UINT8 scn,
sdp_features |= 0x0020;
}
#if UC_BT_HFP_LC3_ENABLE
if (features & BTA_HF_CLIENT_FEAT_SWB) {
sdp_features |= 0x0100; /* SWB supported in SDP */
}
#endif
UINT16_TO_BE_FIELD(buf, sdp_features);
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf);
@@ -31,6 +31,7 @@
#define HFP_VERSION_1_6 0x0106
#define HFP_VERSION_1_7 0x0107
#define HFP_VERSION_1_8 0x0108
#define HFP_VERSION_1_9 0x0109
/* RFCOMM MTU SIZE */
#define BTA_HF_CLIENT_MTU 256
@@ -39,6 +40,13 @@
#define BTA_HF_CLIENT_ACP 0 /* accepted connection */
#define BTA_HF_CLIENT_INT 1 /* initiating connection */
/* feature mask that matches spec */
#define BTA_HF_CLIENT_BRSF_FEAT_SPEC (BTA_HF_CLIENT_FEAT_ECNR | BTA_HF_CLIENT_FEAT_3WAY | \
BTA_HF_CLIENT_FEAT_CLI | BTA_HF_CLIENT_FEAT_VREC | \
BTA_HF_CLIENT_FEAT_VOL | BTA_HF_CLIENT_FEAT_ECS | \
BTA_HF_CLIENT_FEAT_ECC | BTA_HF_CLIENT_FEAT_CODEC | \
BTA_HF_CLIENT_FEAT_HF_IND | BTA_HF_CLIENT_FEAT_ESCO_S4)
/* Timer to wait for retry in case of collision */
#ifndef BTA_HF_CLIENT_COLLISION_TIMER
#define BTA_HF_CLIENT_COLLISION_TIMER 2411
@@ -51,6 +59,8 @@
#define BTA_HF_CLIENT_H2_HEADER_LEN 2
/* mSBC frame size not include H1/H2 header */
#define BTA_HF_CLIENT_MSBC_FRAME_SIZE 57
#define BTA_HF_CLIENT_LC3_FRAME_SIZE 58
#define BTA_HF_CLIENT_LC3_VOHCI_FRAME_SIZE 60
/* 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 */
@@ -209,6 +219,7 @@ typedef struct {
UINT8 scn;
tBTA_HF_CLIENT_CBACK *p_cback; /* application callback */
BOOLEAN msbc_enabled;
BOOLEAN lc3_enabled;
} tBTA_HF_CLIENT_CB;
/*****************************************************************************
@@ -92,6 +92,7 @@ typedef UINT16 tBTA_AG_CHLD_FEAT;
#define BTA_AG_CODEC_NONE BTM_SCO_CODEC_NONE
#define BTA_AG_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */
#define BTA_AG_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */
#define BTA_AG_CODEC_LC3 BTM_SCO_CODEC_LC3 /* LC3-SWB */
typedef UINT16 tBTA_AG_PEER_CODEC;
/* AG parse mode */
@@ -159,6 +160,7 @@ typedef UINT8 tBTA_AG_RES;
#define BTA_AG_AUDIO_MSBC_OPEN_EVT 32 /* Audio connection with mSBC codec open */
#define BTA_AG_PKT_NUMS_GET_EVT 33 /* AG packet status nums */
#define BTA_AG_AUDIO_LC3_OPEN_EVT 34 /* Audio connection with LC3-SWB codec open */
/* Values below are for HFP only */
#define BTA_AG_AT_A_EVT 10 /* Answer a incoming call */
@@ -363,7 +365,8 @@ 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 */
/* data associated with BTA_AG_AUDIO_OPEN_EVT, BTA_AG_AUDIO_CLOSE_EVT, */
/* BTA_AG_AUDIO_MSBC_OPEN_EVT or BTA_AG_AUDIO_LC3_OPEN_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
@@ -36,6 +36,7 @@
/* Hands-Free unit(HF) version */
#define HFP_HF_VERSION_1_6 0x0106 /* v1.6 */
#define HFP_HF_VERSION_1_7 0x0107 /* v1.7 */
#define HFP_HF_VERSION_1_9 0x0109 /* v1.9 */
/* HFP peer (AG) features*/
#define BTA_HF_CLIENT_PEER_FEAT_3WAY 0x00000001 /* Three-way calling */
@@ -66,6 +67,9 @@ typedef UINT16 tBTA_HF_CLIENT_PEER_FEAT;
#define BTA_HF_CLIENT_FEAT_HF_IND 0x00000100 /* HF indicators */
#define BTA_HF_CLIENT_FEAT_ESCO_S4 0x00000200 /* eSCO S4 Setting Supported */
/* Proprietary features: using 31 ~ 16 bits, AT+BRSF bit 12 ~ 31 are reserved by spec */
#define BTA_HF_CLIENT_FEAT_SWB 0x00010000 /* Super Wideband Speech */
/* HFP HF extended call handling - masks not related to any spec */
#define BTA_HF_CLIENT_CHLD_REL 0x00000001 /* 0 Release waiting call or held calls */
#define BTA_HF_CLIENT_CHLD_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */
@@ -114,6 +118,7 @@ typedef UINT8 tBTA_HF_CLIENT_AT_RESULT_TYPE;
#define BTA_HF_CLIENT_RING_INDICATION 21 /* HF Client ring indication */
#define BTA_HF_CLIENT_DISABLE_EVT 22 /* HF Client disabled */
#define BTA_HF_CLIENT_PKT_STAT_NUMS_GET_EVT 23 /* HF Client packet status nums */
#define BTA_HF_CLIENT_AUDIO_LC3_OPEN_EVT 24 /* Audio connection with LC3-SWB codec open */
typedef UINT8 tBTA_HF_CLIENT_EVT;
@@ -188,7 +193,7 @@ typedef struct {
tBTA_HF_CLIENT_CHLD_FEAT chld_feat;
} tBTA_HF_CLIENT_CONN;
/* data associated with BTA_HF_CLIENT_AUDIO_XXX_EVT */
/* data associated with BTA_HF_CLIENT_AUDIO_XXX_EVT (OPEN/MSBC/LC3/CLOSE) */
typedef struct {
tBTA_HF_CLIENT_HDR hdr;
UINT16 preferred_frame_size;
@@ -12,6 +12,7 @@
#define BTA_HFP_CODEC_NONE BTM_SCO_CODEC_NONE
#define BTA_HFP_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */
#define BTA_HFP_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */
#define BTA_HFP_CODEC_LC3 BTM_SCO_CODEC_LC3 /* LC3-SWB */
typedef UINT16 tBTA_HFP_PEER_CODEC;
@@ -25,6 +26,7 @@ typedef UINT8 tBTA_HFP_SCO_CODEC_TYPE;
#define BTA_HFP_SCO_SAMP_RATE_8K 8000
#define BTA_HFP_SCO_SAMP_RATE_16K 16000
#define BTA_HFP_SCO_SAMP_RATE_32K 32000
/* SCO codec information */
typedef struct {
@@ -307,7 +307,7 @@ static void bta_ag_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad
*length = (UINT8)frame_len;
}
// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech
// PLC_INCLUDED will be set to TRUE when enabling Wideband Speech
#if (PLC_INCLUDED == TRUE)
switch(status) {
case OI_OK:
@@ -106,7 +106,7 @@ hf_local_param_t *hf_local_param_ptr = NULL;
#endif
#endif
/* wide band synchronous */
/* Wideband synchronous */
#ifndef BTC_HF_WBS_PREFERRED
#define BTC_HF_WBS_PREFERRED TRUE
#endif
@@ -203,6 +203,38 @@ static BOOLEAN is_connected(int idx, bt_bdaddr_t *bd_addr)
return FALSE;
}
static esp_hf_codec_mode_t btc_hf_bta_codec_to_esp(tBTA_AG_PEER_CODEC codec)
{
if (codec == BTA_AG_CODEC_NONE) {
return ESP_HF_CODEC_NONE;
}
if (codec & BTA_AG_CODEC_LC3) {
return ESP_HF_CODEC_LC3;
}
if (codec & BTA_AG_CODEC_MSBC) {
return ESP_HF_CODEC_MSBC;
}
if (codec & BTA_AG_CODEC_CVSD) {
return ESP_HF_CODEC_CVSD;
}
return ESP_HF_CODEC_NONE;
}
static tBTA_AG_PEER_CODEC btc_hf_esp_codec_to_bta(esp_hf_codec_mode_t mode)
{
switch (mode) {
case ESP_HF_CODEC_CVSD:
return BTA_AG_CODEC_CVSD;
case ESP_HF_CODEC_MSBC:
return BTA_AG_CODEC_MSBC;
case ESP_HF_CODEC_LC3:
return BTA_AG_CODEC_LC3;
case ESP_HF_CODEC_NONE:
default:
return BTA_AG_CODEC_NONE;
}
}
static int btc_hf_latest_connected_idx(void)
{
struct timespec now, conn_time_delta;
@@ -265,7 +297,8 @@ static void bte_hf_evt(tBTA_AG_EVT event, tBTA_AG *param)
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)) {
else if ((BTA_AG_AUDIO_OPEN_EVT == event) || (BTA_AG_AUDIO_CLOSE_EVT == event) ||
(BTA_AG_AUDIO_MSBC_OPEN_EVT == event) || (BTA_AG_AUDIO_LC3_OPEN_EVT == event)) {
param_len = sizeof(tBTA_AG_AUDIO_STAT);
}
else if (param) {
@@ -465,6 +498,22 @@ bt_status_t btc_hf_disconnect(bt_bdaddr_t *bd_addr)
return BT_STATUS_FAIL;
}
bt_status_t btc_hf_set_codec(bt_bdaddr_t *bd_addr, esp_hf_codec_mode_t mode)
{
int idx = btc_hf_idx_by_bdaddr(bd_addr);
if ((idx < 0) || (idx >= BTC_HF_NUM_CB) || !hf_local_param.initialized) {
return BT_STATUS_FAIL;
}
/* AT+BAC may arrive before SLC_CONNECTED; allow CONNECTED or SLC_CONNECTED */
if (!is_connected(idx, bd_addr)) {
BTIF_TRACE_WARNING("%s: HF AG not connected", __FUNCTION__);
return BT_STATUS_NOT_READY;
}
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle, btc_hf_esp_codec_to_bta(mode));
return BT_STATUS_SUCCESS;
}
bt_status_t btc_hf_connect_audio(bt_bdaddr_t *bd_addr)
{
int idx = btc_hf_idx_by_bdaddr(bd_addr);
@@ -1299,6 +1348,12 @@ void btc_hf_call_handler(btc_msg_t *msg)
break;
}
case BTC_HF_SET_CODEC_EVT:
{
btc_hf_set_codec(&arg->set_codec.remote_addr, arg->set_codec.mode);
break;
}
default:
BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act);
}
@@ -1454,6 +1509,20 @@ void btc_hf_cb_handler(btc_msg_t *msg)
} while (0);
break;
}
case BTA_AG_AUDIO_LC3_OPEN_EVT:
{
idx = p_data->hdr.handle - 1;
CHECK_HF_IDX(idx);
do {
param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED_LC3;
memcpy(param.audio_stat.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda,sizeof(esp_bd_addr_t));
hf_local_param.btc_hf_cb[idx].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;
}
case BTA_AG_AUDIO_CLOSE_EVT:
{
idx = p_data->hdr.handle - 1;
@@ -1641,18 +1710,24 @@ void btc_hf_cb_handler(btc_msg_t *msg)
CHECK_HF_IDX(idx);
BTC_TRACE_DEBUG("AG Bitmap of peer-codecs %d", p_data->val.num);
#if (BTM_WBS_INCLUDED == TRUE)
/* If the peer supports mSBC and the BTC preferred codec is also mSBC, then
** we should set the BTA AG Codec to mSBC. This would trigger a +BCS to mSBC at the time
** of SCO connection establishment */
/* Default stack preference; application may override in ESP_HF_BAC_RESPONSE_EVT */
#if UC_BT_HFP_LC3_ENABLE
if (p_data->val.num & BTA_AG_CODEC_LC3) {
BTC_TRACE_DEBUG("%s btc_hf override-Preferred Codec to LC3", __FUNCTION__);
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle, BTA_AG_CODEC_LC3);
} else
#endif
if ((btc_conf_hf_force_wbs == TRUE) && (p_data->val.num & BTA_AG_CODEC_MSBC)) {
BTC_TRACE_DEBUG("%s btc_hf override-Preferred Codec to MSBC", __FUNCTION__);
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle,BTA_AG_CODEC_MSBC);
}
else {
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle, BTA_AG_CODEC_MSBC);
} else {
BTC_TRACE_DEBUG("%s btc_hf override-Preferred Codec to CVSD", __FUNCTION__);
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle,BTA_AG_CODEC_CVSD);
BTA_AgSetCodec(hf_local_param.btc_hf_cb[idx].handle, BTA_AG_CODEC_CVSD);
}
#endif
memcpy(param.bac_rep.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda, sizeof(esp_bd_addr_t));
param.bac_rep.peer_codecs = p_data->val.num;
btc_hf_cb_to_app(ESP_HF_BAC_RESPONSE_EVT, &param);
break;
}
#if (BTM_WBS_INCLUDED == TRUE)
@@ -1661,9 +1736,11 @@ void btc_hf_cb_handler(btc_msg_t *msg)
idx = p_data->hdr.handle - 1;
CHECK_HF_IDX(idx);
do {
BTC_TRACE_DEBUG("Set codec status %d codec %d 1=CVSD 2=MSBC", p_data->val.hdr.status, p_data->val.num);
memcpy(param.wbs_rep.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda,sizeof(esp_bd_addr_t));
param.wbs_rep.codec = p_data->val.num;
BTC_TRACE_DEBUG("Set codec status %d codec %d", p_data->val.hdr.status, p_data->val.num);
memcpy(param.wbs_rep.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda, sizeof(esp_bd_addr_t));
param.wbs_rep.status = (p_data->val.hdr.status == BTA_AG_SUCCESS) ?
ESP_BT_STATUS_SUCCESS : ESP_BT_STATUS_FAIL;
param.wbs_rep.codec = btc_hf_bta_codec_to_esp(p_data->val.num);
btc_hf_cb_to_app(ESP_HF_WBS_RESPONSE_EVT, &param);
} while (0);
break;
@@ -1674,10 +1751,9 @@ void btc_hf_cb_handler(btc_msg_t *msg)
idx = p_data->hdr.handle - 1;
CHECK_HF_IDX(idx);
do {
BTC_TRACE_DEBUG("AG final seleded codec is %d 1=CVSD 2=MSBC", p_data->val.num);
memcpy(param.bcs_rep.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda,sizeof(esp_bd_addr_t));
param.bcs_rep.mode = p_data->val.num;
/* No ESP_HF_WBS_NONE case, because HFP 1.6 supported device can send BCS */
BTC_TRACE_DEBUG("AG final selected codec is %d", p_data->val.num);
memcpy(param.bcs_rep.remote_addr, &hf_local_param.btc_hf_cb[idx].connected_bda, sizeof(esp_bd_addr_t));
param.bcs_rep.mode = btc_hf_bta_codec_to_esp(p_data->val.num);
btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, &param);
} while (0);
break;
@@ -433,7 +433,7 @@ static void bta_hf_client_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN
*length = (UINT8)frame_len;
}
// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech
// PLC_INCLUDED will be set to TRUE when enabling Wideband Speech
#if (PLC_INCLUDED == TRUE)
switch(status){
case OI_OK:
@@ -534,18 +534,21 @@ void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
}
p_new_buf->offset = 0;
UINT8 *p_data = (UINT8 *)(p_new_buf + 1) + p_new_buf->offset;
UINT16 data_len = BTM_MSBC_FRAME_SIZE;
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;
data_len -= 2;
}
else if (!bta_hf_client_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p_data += 1;
data_len -= 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);
btc_hf_client_audio_data_cb_to_app((uint8_t *)p_new_buf, (uint8_t *)p_data, data_len, 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 / 2);
}
@@ -555,15 +558,18 @@ void bta_hf_client_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
if (pkt_size > BTM_MSBC_FRAME_SIZE) {
pkt_size = BTM_MSBC_FRAME_SIZE;
}
UINT16 data_len = pkt_size;
if (BTA_HF_H2_HEADER_SYNC_WORD_CHECK(p)) {
/* H2 header sync word found, skip */
p += 2;
data_len -= 2;
}
else if (!bta_hf_client_co_cb.is_bad_frame){
/* not a bad frame, assume as H1 header */
p += 1;
data_len -= 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);
btc_hf_client_audio_data_cb_to_app((uint8_t *)p_buf, (uint8_t *)p, data_len, bta_hf_client_co_cb.is_bad_frame);
bta_hf_client_co_cb.is_bad_frame = false;
}
else {
@@ -78,7 +78,12 @@ void bta_hf_client_bqb_esco_s4_ctrl(BOOLEAN enable)
/************************************************************************************
** Static variables
************************************************************************************/
const int btc_hf_client_version = HFP_HF_VERSION_1_7;
const int btc_hf_client_version =
#if UC_BT_HFP_LC3_ENABLE
HFP_HF_VERSION_1_9;
#else
HFP_HF_VERSION_1_7;
#endif
#if HFP_DYNAMIC_MEMORY == FALSE
static hf_client_local_param_t hf_client_local_param;
@@ -796,6 +801,9 @@ bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable)
/* Enable and register with BTA-HFClient */
BTA_HfClientEnable(bte_hf_client_evt);
hf_client_local_param.btc_hf_client_features = BTC_HF_CLIENT_FEATURES;
#if UC_BT_HFP_LC3_ENABLE
hf_client_local_param.btc_hf_client_features |= BTA_HF_CLIENT_FEAT_SWB;
#endif
if (btc_hf_client_version >= HFP_HF_VERSION_1_7)
{
hf_client_local_param.btc_hf_client_features |= BTA_HF_CLIENT_FEAT_ESCO_S4;
@@ -1114,6 +1122,17 @@ void btc_hf_client_cb_handler(btc_msg_t *msg)
btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, &param);
} while (0);
break;
case BTA_HF_CLIENT_AUDIO_LC3_OPEN_EVT:
do {
param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_LC3;
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;
case BTA_HF_CLIENT_AUDIO_CLOSE_EVT:
do {
param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -57,7 +57,8 @@ typedef enum
//REG
BTC_HF_REGISTER_DATA_CALLBACK_EVT,
BTC_HF_REGISTER_AUDIO_DATA_CALLBACK_EVT,
BTC_HF_REQUEST_PKT_STAT_EVT
BTC_HF_REQUEST_PKT_STAT_EVT,
BTC_HF_SET_CODEC_EVT
} btc_hf_act_t;
/* btc_hf_args_t */
@@ -190,6 +191,12 @@ typedef union
UINT16 sync_conn_handle;
} pkt_sync_hd;
// BTC_HF_SET_CODEC_EVT
struct ag_set_codec_param {
bt_bdaddr_t remote_addr;
esp_hf_codec_mode_t mode;
} set_codec;
} btc_hf_args_t;
/************************************************************************************
@@ -805,13 +805,20 @@
#define UC_BT_HFP_AUDIO_DATA_PATH_HCI FALSE
#endif
//Wide Band Speech
//Wideband Speech
#ifdef CONFIG_BT_HFP_WBS_ENABLE
#define UC_BT_HFP_WBS_ENABLE CONFIG_BT_HFP_WBS_ENABLE
#else
#define UC_BT_HFP_WBS_ENABLE FALSE
#endif
//Super Wideband Speech (LC3-SWB)
#ifdef CONFIG_BT_HFP_LC3_ENABLE
#define UC_BT_HFP_LC3_ENABLE CONFIG_BT_HFP_LC3_ENABLE
#else
#define UC_BT_HFP_LC3_ENABLE FALSE
#endif
/**********************************************************
* Memory reference
**********************************************************/
@@ -1221,6 +1221,20 @@
#define BTM_MSBC_FRAME_DATA_SIZE 57
#endif
/* LC3-SWB eSCO air frame (H2 + payload); same 60-byte eSCO packet as mSBC */
#ifndef BTM_LC3_FRAME_SIZE
#define BTM_LC3_FRAME_SIZE 60
#endif
#ifndef BTM_LC3_FRAME_DATA_SIZE
#define BTM_LC3_FRAME_DATA_SIZE 58
#endif
/* Transparent wideband air frame size (mSBC and LC3-SWB) */
#ifndef BTM_HFP_TRANSPARENT_FRAME_SIZE
#define BTM_HFP_TRANSPARENT_FRAME_SIZE BTM_MSBC_FRAME_SIZE
#endif
/* The size in bytes of the BTM inquiry database. 5 As Default */
#ifndef BTM_INQ_DB_SIZE
#define BTM_INQ_DB_SIZE 5
@@ -1116,6 +1116,7 @@ typedef UINT8 tBTM_SCO_ROUTE_TYPE;
#define BTM_SCO_CODEC_NONE 0x0000
#define BTM_SCO_CODEC_CVSD 0x0001
#define BTM_SCO_CODEC_MSBC 0x0002
#define BTM_SCO_CODEC_LC3 0x0004
typedef UINT16 tBTM_SCO_CODEC_TYPE;
@@ -279,6 +279,7 @@
#if (BTM_WBS_INCLUDED == TRUE)
#define UUID_CODEC_CVSD 0x0001 /* CVSD */
#define UUID_CODEC_MSBC 0x0002 /* mSBC */
#define UUID_CODEC_LC3 0x0003 /* LC3-SWB */
#endif
/* Define all the 'Descriptor Type' values.
@@ -308,7 +309,7 @@
#define LANG_ID_CODE_ENGLISH ((UINT16) 0x656e) /* "en" */
#define LANG_ID_CHAR_ENCODE_UTF8 ((UINT16) 0x006a) /* UTF-8 */
/* Constants used for display purposes only. These define ovelapping attribute values */
/* Constants used for display purposes only. These define overlapping attribute values */
#define ATTR_ID_VERS_OR_GRP_OR_DRELNUM_OR_IPSUB_OR_SPECID 0x0200
#define ATTR_ID_VEND_ID_OR_SERVICE_DB_STATE_OR_PARSE_VER 0x0201
#define ATTR_ID_PROD_ID_OR_HID_DEV_SUBCLASS 0x0202