217 lines
5.6 KiB
C
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 */
|