Files
AC707N/SDK/audio/framework/nodes/esco_tx_node.c
T
2025-12-03 11:12:34 +08:00

217 lines
5.6 KiB
C

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".esco_tx_node.data.bss")
#pragma data_seg(".esco_tx_node.data")
#pragma const_seg(".esco_tx_node.text.const")
#pragma code_seg(".esco_tx_node.text")
#endif
#include "jlstream.h"
#include "classic/hci_lmp.h"
#include "media/audio_base.h"
#include "sync/audio_syncts.h"
#include "app_config.h"
#if TCFG_USER_BT_CLASSIC_ENABLE
struct esco_tx_sync_node {
u8 trigger;
void *syncts;
struct list_head entry;
};
struct esco_tx_hdl {
u8 start;
u8 reference_network;
u8 tx_frames;
u8 bt_addr[6];
struct list_head sync_list;
};
extern void bt_edr_conn_system_clock_init(void *addr, u8 factor);
extern u32 bt_edr_conn_master_to_local_time(void *addr, u32 usec);
static void esco_tx_handle_frame(struct stream_iport *iport, struct stream_note *note)
{
/* struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data; */
struct stream_frame *frame;
while (1) {
frame = jlstream_pull_frame(iport, note);
if (!frame) {
break;
}
/*printf("esco_tx: %d\n", frame->len);*/
lmp_private_send_esco_packet(NULL, frame->data, frame->len);
jlstream_free_frame(frame);
}
}
void esco_tx_tick_handler(void *priv, u32 clkn)
{
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)priv;
struct esco_tx_sync_node *node = NULL;
u32 time = bt_edr_conn_master_to_local_time(hdl->bt_addr, (clkn * 625));
list_for_each_entry(node, &hdl->sync_list, entry) {
if (!node->trigger) {
node->trigger = 1;
sound_pcm_syncts_latch_trigger(node->syncts);
}
sound_pcm_update_frame_num(node->syncts, hdl->tx_frames);
sound_pcm_update_frame_num_and_time(node->syncts, 0, time, 0);
}
}
static int esco_tx_bind(struct stream_node *node, u16 uuid)
{
return 0;
}
static void esco_tx_open_iport(struct stream_iport *iport)
{
struct esco_tx_hdl *hdl = zalloc(sizeof(*hdl));
iport->private_data = hdl;
INIT_LIST_HEAD(&hdl->sync_list);
hdl->reference_network = 0xff;
iport->handle_frame = esco_tx_handle_frame;
}
static void esco_tx_set_bt_addr(struct esco_tx_hdl *hdl, void *bt_addr)
{
memcpy(hdl->bt_addr, bt_addr, 6);
bt_edr_conn_system_clock_init(hdl->bt_addr, 1);
}
static int esco_tx_ioc_fmt_nego(struct stream_iport *iport)
{
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data;
struct stream_fmt *in_fmt = &iport->prev->fmt;
int type = lmp_private_get_esco_packet_type();
int frame_time = (lmp_private_get_esco_packet_type() >> 8) & 0xff;
int media_type = type & 0xff;
if (media_type == 0) {
in_fmt->sample_rate = 8000;
in_fmt->coding_type = AUDIO_CODING_CVSD;
hdl->tx_frames = frame_time == 12 ? 60 : 30;
} else {
in_fmt->sample_rate = 16000;
in_fmt->coding_type = AUDIO_CODING_MSBC;
hdl->tx_frames = frame_time == 12 ? 120 : 60;
}
in_fmt->channel_mode = AUDIO_CH_MIX;
return NEGO_STA_ACCPTED;
}
static int esco_tx_mount_syncts(struct esco_tx_hdl *hdl, void *syncts, u32 timestamp, u8 network)
{
struct esco_tx_sync_node *node = NULL;
list_for_each_entry(node, &hdl->sync_list, entry) {
if ((u32)node->syncts == (u32)syncts) {
return 0;
}
}
node = (struct esco_tx_sync_node *)zalloc(sizeof(struct esco_tx_sync_node));
node->syncts = syncts;
/*g_printf("esco tx mount syncts : 0x%x, %u\n", (u32)syncts, timestamp);*/
if (hdl->reference_network == 0xff) {
hdl->reference_network = network;
}
list_add(&node->entry, &hdl->sync_list);
return 0;
}
static void esco_tx_unmount_syncts(struct esco_tx_hdl *hdl, void *syncts)
{
struct esco_tx_sync_node *node;
list_for_each_entry(node, &hdl->sync_list, entry) {
if (node->syncts == syncts) {
goto unmount;
}
}
return;
unmount:
/*g_printf("esco tx unmount syncts : 0x%x\n", syncts);*/
list_del(&node->entry);
free(node);
}
static int esco_tx_syncts_handler(struct esco_tx_hdl *hdl, struct audio_syncts_ioc_params *params)
{
if (!params) {
return 0;
}
switch (params->cmd) {
case AUDIO_SYNCTS_MOUNT_ON_SNDPCM:
esco_tx_mount_syncts(hdl, (void *)params->data[0], params->data[1], params->data[2]);
break;
case AUDIO_SYNCTS_UMOUNT_ON_SNDPCM:
esco_tx_unmount_syncts(hdl, (void *)params->data[0]);
break;
}
return 0;
}
static int esco_tx_ioctl(struct stream_iport *iport, int cmd, int arg)
{
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data;
switch (cmd) {
case NODE_IOC_SET_BTADDR:
esco_tx_set_bt_addr(hdl, (void *)arg);
break;
case NODE_IOC_OPEN_IPORT:
esco_tx_open_iport(iport);
break;
case NODE_IOC_START:
lmp_esco_set_tx_notify(hdl->bt_addr, hdl, esco_tx_tick_handler);
break;
case NODE_IOC_STOP:
lmp_esco_set_tx_notify(hdl->bt_addr, NULL, NULL);
break;
case NODE_IOC_CLOSE_IPORT:
free(hdl);
break;
case NODE_IOC_NEGOTIATE:
*(int *)arg |= esco_tx_ioc_fmt_nego(iport);
break;
case NODE_IOC_SYNCTS:
esco_tx_syncts_handler(hdl, (struct audio_syncts_ioc_params *)arg);
break;
case NODE_IOC_GET_DELAY:
return lmp_private_get_esco_tx_packet_num() * 75;
}
return 0;
}
static void esco_tx_release(struct stream_node *node)
{
}
REGISTER_STREAM_NODE_ADAPTER(esco_tx_adapter) = {
.name = "esco_tx",
.uuid = NODE_UUID_ESCO_TX,
.bind = esco_tx_bind,
.ioctl = esco_tx_ioctl,
.release = esco_tx_release,
};
#endif /* #if TCFG_USER_BT_CLASSIC_ENABLE */