356 lines
11 KiB
C
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
|