Files
2025-12-03 11:12:34 +08:00

356 lines
11 KiB
C

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".a2dp_handshake.data.bss")
#pragma data_seg(".a2dp_handshake.data")
#pragma const_seg(".a2dp_handshake.text.const")
#pragma code_seg(".a2dp_handshake.text")
#endif
/*************************************************************************************************/
/*!
* \file a2dp_handshake.c
*
* \brief
*
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
*
*/
/*************************************************************************************************/
#if 1
struct a2dp_handshake_data {
u8 ack;
u8 state;
u16 seqn;
u8 addr[6];
};
struct a2dp_tws_handshake {
u8 state;
u8 handshake_step;
u8 rx_state;
u8 need_ack;
u32 rx_time;
u16 rx_seqn;
u16 run_seqn;
u8 rx_addr[6];
u8 run_addr[6];
};
/*
* handshake state
* init / run / success / timeout
* */
#define A2DP_HANDSHAKE_STATE_INIT 0
#define A2DP_HANDSHAKE_STATE_RUN 1
#define A2DP_HANDSHAKE_STATE_SUCCESS 2
#define A2DP_HANDSHAKE_STATE_TIMEOUT 3
#define A2DP_HANDSHAKE_STATE_WAIT_MONITOR 4
/*
* handshake step
* init / request / wait ack / timeout / ack
*
*/
#define A2DP_HANDSHAKE_STEP_INIT 0
#define A2DP_HANDSHAKE_STEP_REQUEST 1
#define A2DP_HANDSHAKE_STEP_WAIT_ACK 2
#define A2DP_HANDSHAKE_STEP_TIMEOUT 3
#define A2DP_HANDSHAKE_STEP_ACK 4
static struct a2dp_tws_handshake a2dp_tws = {0};
#define A2DP_TWS_HANDSHAKE \
((int)((u8 )('A' + '2' + 'D' + 'P') << (3 * 8)) | \
(int)(('T' + 'W' + 'S') << (2 * 8)) | \
(int)(('H' + 'A' + 'N' + 'D') << (1 * 8)) | \
(int)(('S' + 'H' + 'A' + 'K' + 'E') << (0 * 8)))
#define A2DP_TWS_FILE_EXIT 0
#define A2DP_TWS_FILE_START 1
#define A2DP_TWS_FILE_RUN 2
extern u32 bt_audio_conn_clock_time(void *addr);
static void a2dp_tws_handskake_handler(void *buf, u16 len, bool rx)
{
struct a2dp_handshake_data handshake_data;
if (!CONFIG_BTCTLER_TWS_ENABLE) {
return;
}
if (rx) {
memcpy(&handshake_data, buf, len);
/*putchar(handshake_data.ack ? '*' : '#');*/
if (handshake_data.ack) {
if (a2dp_tws.handshake_step != A2DP_HANDSHAKE_STEP_INIT && a2dp_tws.handshake_step != A2DP_HANDSHAKE_STEP_ACK) {
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STEP_ACK;
a2dp_tws.rx_time = jiffies;
a2dp_tws.rx_seqn = handshake_data.seqn;
a2dp_tws.rx_state = handshake_data.state;
memcpy(a2dp_tws.rx_addr, handshake_data.addr, sizeof(handshake_data.addr));
}
} else {
a2dp_tws.need_ack = 1;
}
}
}
REGISTER_TWS_FUNC_STUB(a2dp_tws_handshake) = {
.func_id = A2DP_TWS_HANDSHAKE,
.func = a2dp_tws_handskake_handler,
};
#define bt_clkn_after(a, b) (((a - b) & 0x7ffffff) >= 0)
static void a2dp_file_abandon_data_before_time(void *file, u32 time)
{
struct a2dp_media_frame frame;
while (1) {
int len = a2dp_media_try_get_packet(file, &frame);
if (len <= 0) {
break;
}
if (bt_clkn_after(frame.clkn, time)) {
a2dp_media_put_packet(file, frame.packet);
break;
}
printf("drop clkn : %d\n", frame.clkn);
a2dp_media_free_packet(file, frame.packet);
}
}
static void a2dp_file_abandon_data_before_seqn(void *file, u16 ref_seqn)
{
u16 seqn;
struct a2dp_media_frame frame;
while (1) {
int len = a2dp_media_try_get_packet(file, &frame);
if (len <= 0) {
break;
}
seqn = RB16((u8 *)frame.packet + 2);
if (seqn == ref_seqn || seqn_after(seqn, ref_seqn)) {
a2dp_media_put_packet(file, frame.packet);
break;
}
printf("drop seqn : %d\n", seqn);
a2dp_media_free_packet(file, frame.packet);
}
}
static int a2dp_tws_media_pop_seqn(struct a2dp_file_hdl *hdl, u16 *seqn)
{
struct a2dp_media_frame frame;
int error_num = 0;
int len = 0;
while (len <= 0) {
if (++error_num > 3) {
break;
}
len = a2dp_media_try_get_packet(hdl->file, &frame);
}
if (len <= 0) {
return -EINVAL;
}
*seqn = RB16(frame.packet + 2);
a2dp_media_put_packet(hdl->file, frame.packet);
return 0;
}
static void a2dp_tws_media_handshake_ack(u8 file_running)
{
if (!a2dp_tws.need_ack) {
return;
}
struct a2dp_handshake_data handshake_data = {0};
/*收到握手消息则ack当前信息*/
handshake_data.ack = 1;
handshake_data.state = file_running ? a2dp_tws.state : A2DP_TWS_FILE_START;
handshake_data.seqn = a2dp_tws.run_seqn;
memcpy(handshake_data.addr, a2dp_tws.run_addr, sizeof(a2dp_tws.run_addr));
int err = tws_api_send_data_to_sibling(&handshake_data, sizeof(handshake_data), A2DP_TWS_HANDSHAKE);
if (!err) {
a2dp_tws.need_ack = 0;
}
}
static void a2dp_tws_media_try_handshake_ack(u8 file_running, u16 seqn)
{
if (!CONFIG_BTCTLER_TWS_ENABLE) {
return;
}
if (!(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED) || /*TWS未连接*/
!(tws_api_get_lmp_state(a2dp_tws.run_addr) & TWS_STA_MONITOR_START)) { /*TWS播放另外一边还未开始监听*/
return;
}
a2dp_tws.run_seqn = seqn;
a2dp_tws_media_handshake_ack(file_running);
}
static int a2dp_tws_media_handshake_init(struct a2dp_file_hdl *hdl)
{
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
u16 seqn = 0;
a2dp_file_abandon_data_before_time(hdl->file, bt_audio_conn_clock_time(hdl->bt_addr) - 200);
int err = a2dp_tws_media_pop_seqn(hdl, &seqn);
if (err) {
return -EINVAL;
}
local_irq_disable();
memset(&a2dp_tws, 0x0, sizeof(a2dp_tws));
a2dp_tws.state = A2DP_TWS_FILE_START;
a2dp_tws.run_seqn = seqn;
memcpy(a2dp_tws.run_addr, hdl->bt_addr, sizeof(a2dp_tws.run_addr));
local_irq_enable();
hdl->handshake_state = A2DP_HANDSHAKE_STATE_RUN;
hdl->handshake_timeout = jiffies + msecs_to_jiffies(300);
}
return 0;
}
static int a2dp_tws_conn_and_monitor_detect(struct a2dp_file_hdl *hdl)
{
if (!(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED)) {
return TWS_STA_SIBLING_DISCONNECTED;
}
if (!(tws_api_get_lmp_state(hdl->bt_addr) & TWS_STA_MONITOR_START)) { /*TWS播放另外一边还未开始监听*/
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
hdl->handshake_state = A2DP_HANDSHAKE_STATE_WAIT_MONITOR;
hdl->handshake_timeout = jiffies + msecs_to_jiffies(200);
return TWS_STA_MONITOR_ING;
}
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_WAIT_MONITOR) {
if (time_before(jiffies, hdl->handshake_timeout)) {
return TWS_STA_MONITOR_ING;
}
}
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
return TWS_STA_SIBLING_DISCONNECTED;
}
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_WAIT_MONITOR) {
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
}
return 0;
}
static int a2dp_tws_media_handshake(struct a2dp_file_hdl *hdl)
{
int err = 0;
if (!CONFIG_BTCTLER_TWS_ENABLE) {
return 0;
}
err = a2dp_tws_conn_and_monitor_detect(hdl);
if (err) {
if (err == TWS_STA_SIBLING_DISCONNECTED) {
memset(&a2dp_tws, 0x0, sizeof(a2dp_tws));
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
memcpy(a2dp_tws.run_addr, hdl->bt_addr, sizeof(a2dp_tws.run_addr));
}
hdl->tws_case = 1;
goto a2dp_file_run;
}
return -EINVAL;
}
err = a2dp_tws_media_handshake_init(hdl);
if (err) {
return err;
}
struct a2dp_handshake_data handshake_data = {0};
/*握手请求*/
printf("[handshake] : %d, %d\n", hdl->handshake_state, a2dp_tws.handshake_step);
switch (a2dp_tws.handshake_step) {
case A2DP_HANDSHAKE_STEP_INIT:
case A2DP_HANDSHAKE_STEP_REQUEST:
local_irq_disable();
hdl->request_timeout = jiffies + msecs_to_jiffies(60);
err = tws_api_send_data_to_sibling(&handshake_data, sizeof(handshake_data.ack), A2DP_TWS_HANDSHAKE);
a2dp_tws.handshake_step = err ? A2DP_HANDSHAKE_STEP_REQUEST : A2DP_HANDSHAKE_STEP_WAIT_ACK;
local_irq_enable();
break;
case A2DP_HANDSHAKE_STEP_WAIT_ACK:
local_irq_disable();
if (time_after(jiffies, hdl->request_timeout)) {
/*request后响应超时*/
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STEP_REQUEST;
}
local_irq_enable();
break;
case A2DP_HANDSHAKE_STEP_ACK:
hdl->handshake_state = A2DP_HANDSHAKE_STATE_SUCCESS;
break;
default:
break;
}
a2dp_tws_media_handshake_ack(0);
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_RUN) {
if (time_before(jiffies, hdl->handshake_timeout)) { //等待握手未超时,以失败返回
return -EINVAL;
}
hdl->handshake_state = A2DP_HANDSHAKE_STATE_TIMEOUT;
}
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_TIMEOUT) {
/*握手超时,这时候需要对超时数据进行处理*/
printf("a2dp handshake timeout : %d\n", bt_audio_conn_clock_time(hdl->bt_addr));
a2dp_file_abandon_data_before_time(hdl->file, bt_audio_conn_clock_time(hdl->bt_addr) - 200);
hdl->tws_case = 1;
goto a2dp_file_run;
}
if (memcmp(a2dp_tws.rx_addr, hdl->bt_addr, 6) != 0) {
printf("a2dp handshake bt addr error : \n");
put_buf(a2dp_tws.rx_addr, 6);
put_buf(hdl->bt_addr, 6);
hdl->tws_case = 1;
goto a2dp_file_run;
}
if (__builtin_abs((int)(a2dp_tws.rx_seqn - a2dp_tws.run_seqn)) > 50) {
printf("a2dp handshake seqn error : %d, %d, need check btstack.\n", a2dp_tws.rx_seqn, a2dp_tws.run_seqn);
hdl->tws_case = 1;
goto a2dp_file_run;
}
/*将两边数据处理为一致的seqn*/
a2dp_file_abandon_data_before_seqn(hdl->file, a2dp_tws.rx_seqn);
a2dp_file_run:
local_irq_disable();
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_SUCCESS && a2dp_tws.rx_state == A2DP_TWS_FILE_RUN) {
printf("------>>> A2DP player join tws <<<--------\n");
hdl->tws_case = 2;
}
a2dp_tws.state = A2DP_TWS_FILE_RUN;
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STATE_INIT;
local_irq_enable();
return 0;
}
void a2dp_tws_media_handshake_exit(struct a2dp_file_hdl *hdl)
{
if (!CONFIG_BTCTLER_TWS_ENABLE) {
return;
}
local_irq_disable();
a2dp_tws.state = A2DP_TWS_FILE_EXIT;
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
local_irq_enable();
}
#endif