Bluetooth: controller: ISO-AL CIS RX framed and ISO bugfixes
Adds reassembly of framed PDUs to ISO adaptation layer. Reference times are also computed based on anchor points, transmission latency components and a PDU specific time offset. Some partial preparations for ISO broadcast can be found for convenience. This needs to be properly merged, and currently will not work with both CONFIG_BT_CTLR_CONN_ISO and CONFIG_BT_CTLR_SYNC_ISO enabled. Includes fixes and improvements for unframed mode. Signed-off-by: Asger Munk Nielsen <asmk@oticon.com> Signed-off-by: Morten Priess <mtpr@oticon.com>
This commit is contained in:
parent
2bf02f4854
commit
2d781ddfb2
11 changed files with 712 additions and 282 deletions
|
@ -48,9 +48,10 @@
|
||||||
#include "ll.h"
|
#include "ll.h"
|
||||||
|
|
||||||
#include "isoal.h"
|
#include "isoal.h"
|
||||||
|
#include "ull_iso_types.h"
|
||||||
#include "lll_conn_iso.h"
|
#include "lll_conn_iso.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
#include "ull_iso_types.h"
|
#include "ull_iso_internal.h"
|
||||||
#include "ull_conn_internal.h"
|
#include "ull_conn_internal.h"
|
||||||
#include "ull_conn_iso_internal.h"
|
#include "ull_conn_iso_internal.h"
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ isoal_status_t sink_sdu_alloc_hci(const struct isoal_sink *sink_ctx,
|
||||||
ARG_UNUSED(sink_ctx);
|
ARG_UNUSED(sink_ctx);
|
||||||
ARG_UNUSED(valid_pdu); /* TODO copy valid pdu into netbuf ? */
|
ARG_UNUSED(valid_pdu); /* TODO copy valid pdu into netbuf ? */
|
||||||
|
|
||||||
struct net_buf *buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT);
|
struct net_buf *buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_FOREVER);
|
||||||
|
|
||||||
if (buf) {
|
if (buf) {
|
||||||
/* Reserve space for headers */
|
/* Reserve space for headers */
|
||||||
|
@ -133,14 +134,30 @@ isoal_status_t sink_sdu_emit_hci(const struct isoal_sink *sink_ctx,
|
||||||
|
|
||||||
pb = sink_ctx->sdu_production.sdu_state;
|
pb = sink_ctx->sdu_production.sdu_state;
|
||||||
|
|
||||||
ts = 1; /*TODO: Always assume timestamp? */
|
/*
|
||||||
|
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||||
|
* 5.4.5 HCI ISO Data packets
|
||||||
|
*
|
||||||
|
* PB_Flag:
|
||||||
|
* Value Parameter Description
|
||||||
|
* 0b00 The ISO_Data_Load field contains a header and the first fragment
|
||||||
|
* of a fragmented SDU.
|
||||||
|
* 0b01 The ISO_Data_Load field contains a continuation fragment of an SDU.
|
||||||
|
* 0b10 The ISO_Data_Load field contains a header and a complete SDU.
|
||||||
|
* 0b11 The ISO_Data_Load field contains the last fragment of an SDU.
|
||||||
|
*
|
||||||
|
* The TS_Flag bit shall be set if the ISO_Data_Load field contains a
|
||||||
|
* Time_Stamp field. This bit shall only be set if the PB_Flag field equals 0b00 or
|
||||||
|
* 0b10.
|
||||||
|
*/
|
||||||
|
ts = !(pb & 1);
|
||||||
handle_packed = bt_iso_handle_pack(handle, pb, ts);
|
handle_packed = bt_iso_handle_pack(handle, pb, ts);
|
||||||
len = sink_ctx->sdu_production.sdu_written + BT_HCI_ISO_TS_DATA_HDR_SIZE;
|
len = sink_ctx->sdu_production.sdu_written + BT_HCI_ISO_TS_DATA_HDR_SIZE;
|
||||||
|
|
||||||
hdr->handle = sys_cpu_to_le16(handle_packed);
|
hdr->handle = sys_cpu_to_le16(handle_packed);
|
||||||
hdr->len = sys_cpu_to_le16(len);
|
hdr->len = sys_cpu_to_le16(len);
|
||||||
|
|
||||||
packet_status_flag = 0x0000; /* TODO: For now always assume "valid data" */
|
packet_status_flag = valid_sdu->status;
|
||||||
slen = sink_ctx->sdu_production.sdu_written;
|
slen = sink_ctx->sdu_production.sdu_written;
|
||||||
slen_packed = bt_iso_pkt_len_pack(slen, packet_status_flag);
|
slen_packed = bt_iso_pkt_len_pack(slen, packet_status_flag);
|
||||||
|
|
||||||
|
@ -308,17 +325,26 @@ static inline struct net_buf *encode_node(struct node_rx_pdu *node_rx,
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_BT_CTLR_ISO)
|
#if defined(CONFIG_BT_CTLR_ISO)
|
||||||
case HCI_CLASS_ISO_DATA: {
|
case HCI_CLASS_ISO_DATA: {
|
||||||
|
#if defined(CONFIG_BT_CTLR_CONN_ISO) || defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||||
|
uint8_t handle = node_rx->hdr.handle;
|
||||||
|
struct ll_iso_stream_hdr *hdr = NULL;
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
struct ll_conn_iso_stream *cis =
|
if (IS_CIS_HANDLE(handle)) {
|
||||||
ll_conn_iso_stream_get(node_rx->hdr.handle);
|
struct ll_conn_iso_stream *cis =
|
||||||
struct ll_iso_datapath *dp = cis->datapath_out;
|
ll_conn_iso_stream_get(handle);
|
||||||
|
hdr = &cis->hdr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ll_iso_datapath *dp = hdr->datapath_out;
|
||||||
isoal_sink_handle_t sink = dp->sink_hdl;
|
isoal_sink_handle_t sink = dp->sink_hdl;
|
||||||
|
|
||||||
if (dp->path_id == BT_HCI_DATAPATH_ID_HCI) {
|
if (dp->path_id == BT_HCI_DATAPATH_ID_HCI) {
|
||||||
/* If HCI datapath pass to ISO AL here */
|
/* If HCI datapath pass to ISO AL here */
|
||||||
struct isoal_pdu_rx pckt_meta = {
|
struct isoal_pdu_rx pckt_meta = {
|
||||||
.meta = &node_rx->hdr.rx_iso_meta,
|
.meta = &node_rx->hdr.rx_iso_meta,
|
||||||
.pdu = (union isoal_pdu *) &node_rx->pdu[0]
|
.pdu = (struct pdu_iso *) &node_rx->pdu[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Pass the ISO PDU through ISO-AL */
|
/* Pass the ISO PDU through ISO-AL */
|
||||||
|
|
|
@ -51,9 +51,6 @@ isoal_status_t isoal_init(void)
|
||||||
isoal_status_t err = ISOAL_STATUS_OK;
|
isoal_status_t err = ISOAL_STATUS_OK;
|
||||||
|
|
||||||
err = isoal_init_reset();
|
err = isoal_init_reset();
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -64,9 +61,6 @@ isoal_status_t isoal_reset(void)
|
||||||
isoal_status_t err = ISOAL_STATUS_OK;
|
isoal_status_t err = ISOAL_STATUS_OK;
|
||||||
|
|
||||||
err = isoal_init_reset();
|
err = isoal_init_reset();
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -106,24 +100,34 @@ static void isoal_sink_deallocate(isoal_sink_handle_t hdl)
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sink
|
* @brief Create a new sink
|
||||||
*
|
*
|
||||||
* @param hdl[out] Handle to new sink
|
* @param handle[in] Connection handle
|
||||||
* @param handle[in] Connection handle
|
* @param role[in] Peripheral, Central or Broadcast
|
||||||
* @param burst_number[in] Burst Number
|
* @param burst_number[in] Burst Number
|
||||||
* @param sdu_interval[in] SDU interval
|
* @param flush_timeout[in] Flush timeout
|
||||||
* @param iso_interval[in] ISO interval
|
* @param sdu_interval[in] SDU interval
|
||||||
* @param sdu_alloc[in] Callback of SDU allocator
|
* @param iso_interval[in] ISO interval
|
||||||
* @param sdu_emit[in] Callback of SDU emitter
|
* @param stream_sync_delay[in] CIS sync delay
|
||||||
|
* @param group_sync_delay[in] CIG sync delay
|
||||||
|
* @param sdu_alloc[in] Callback of SDU allocator
|
||||||
|
* @param sdu_emit[in] Callback of SDU emitter
|
||||||
|
* @param sdu_write[in] Callback of SDU byte writer
|
||||||
|
* @param hdl[out] Handle to new sink
|
||||||
|
*
|
||||||
* @return ISOAL_STATUS_OK if we could create a new sink; otherwise ISOAL_STATUS_ERR_SINK_ALLOC
|
* @return ISOAL_STATUS_OK if we could create a new sink; otherwise ISOAL_STATUS_ERR_SINK_ALLOC
|
||||||
*/
|
*/
|
||||||
isoal_status_t isoal_sink_create(
|
isoal_status_t isoal_sink_create(
|
||||||
isoal_sink_handle_t *hdl,
|
|
||||||
uint16_t handle,
|
uint16_t handle,
|
||||||
|
uint8_t role,
|
||||||
uint8_t burst_number,
|
uint8_t burst_number,
|
||||||
|
uint8_t flush_timeout,
|
||||||
uint32_t sdu_interval,
|
uint32_t sdu_interval,
|
||||||
uint16_t iso_interval,
|
uint16_t iso_interval,
|
||||||
|
uint32_t stream_sync_delay,
|
||||||
|
uint32_t group_sync_delay,
|
||||||
isoal_sink_sdu_alloc_cb sdu_alloc,
|
isoal_sink_sdu_alloc_cb sdu_alloc,
|
||||||
isoal_sink_sdu_emit_cb sdu_emit,
|
isoal_sink_sdu_emit_cb sdu_emit,
|
||||||
isoal_sink_sdu_write_cb sdu_write)
|
isoal_sink_sdu_write_cb sdu_write,
|
||||||
|
isoal_sink_handle_t *hdl)
|
||||||
{
|
{
|
||||||
isoal_status_t err;
|
isoal_status_t err;
|
||||||
|
|
||||||
|
@ -133,17 +137,77 @@ isoal_status_t isoal_sink_create(
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
isoal_global.sink_state[*hdl].session.handle = handle;
|
struct isoal_sink_session *session = &isoal_global.sink_state[*hdl].session;
|
||||||
|
|
||||||
|
session->handle = handle;
|
||||||
|
|
||||||
|
/* Todo: Next section computing various constants, should potentially be a
|
||||||
|
* function in itself as a number of the dependencies could be changed while
|
||||||
|
* a connection is active.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Note: sdu_interval unit is uS, iso_interval is a multiple of 1.25mS */
|
/* Note: sdu_interval unit is uS, iso_interval is a multiple of 1.25mS */
|
||||||
isoal_global.sink_state[*hdl].session.pdus_per_sdu =
|
session->pdus_per_sdu = burst_number * (sdu_interval / (iso_interval * 1250));
|
||||||
burst_number * (sdu_interval / (iso_interval * 1250));
|
|
||||||
|
/* Computation of transport latency (constant part)
|
||||||
|
*
|
||||||
|
* Unframed case:
|
||||||
|
*
|
||||||
|
* C->P: SDU_Synchronization_Reference =
|
||||||
|
* CIS reference anchor point + CIS_Sync_Delay + (FT_C_To_P - 1) * ISO_Interval
|
||||||
|
*
|
||||||
|
* P->C: SDU_Synchronization_Reference =
|
||||||
|
* CIS reference anchor point + CIS_Sync_Delay - CIG_Sync_Delay -
|
||||||
|
* ((ISO_Interval / SDU interval)-1) * SDU interval
|
||||||
|
*
|
||||||
|
* BIS: SDU_Synchronization_Reference =
|
||||||
|
* BIG reference anchor point + BIG_Sync_Delay
|
||||||
|
*
|
||||||
|
* Framed case:
|
||||||
|
*
|
||||||
|
* C->P: SDU_Synchronization_Reference =
|
||||||
|
* CIS Reference Anchor point +
|
||||||
|
* CIS_Sync_Delay + SDU_Interval_C_To_P + FT_C_To_P * ISO_Interval -
|
||||||
|
* Time_Offset
|
||||||
|
*
|
||||||
|
* P->C: synchronization reference SDU = CIS reference anchor point +
|
||||||
|
* CIS_Sync_Delay - CIG_Sync_Delay - Time_Offset
|
||||||
|
*
|
||||||
|
* BIS: SDU_Synchronization_Reference =
|
||||||
|
* BIG reference anchor point +
|
||||||
|
* BIG_Sync_Delay + SDU_interval + ISO_Interval - Time_Offset.
|
||||||
|
*/
|
||||||
|
if (role == BT_CONN_ROLE_PERIPHERAL) {
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_unframed =
|
||||||
|
stream_sync_delay + ((flush_timeout - 1) * iso_interval);
|
||||||
|
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_framed =
|
||||||
|
stream_sync_delay + sdu_interval + (flush_timeout * iso_interval);
|
||||||
|
} else if (role == BT_CONN_ROLE_CENTRAL) {
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_unframed =
|
||||||
|
stream_sync_delay - group_sync_delay -
|
||||||
|
(((iso_interval / sdu_interval) - 1) * iso_interval);
|
||||||
|
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_framed =
|
||||||
|
stream_sync_delay - group_sync_delay;
|
||||||
|
} else if (role == BT_ROLE_BROADCAST) {
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_unframed =
|
||||||
|
group_sync_delay;
|
||||||
|
|
||||||
|
isoal_global.sink_state[*hdl].session.latency_framed =
|
||||||
|
group_sync_delay + sdu_interval + iso_interval;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LL_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Remember the platform-specific callbacks */
|
/* Remember the platform-specific callbacks */
|
||||||
isoal_global.sink_state[*hdl].session.sdu_alloc = sdu_alloc;
|
session->sdu_alloc = sdu_alloc;
|
||||||
isoal_global.sink_state[*hdl].session.sdu_emit = sdu_emit;
|
session->sdu_emit = sdu_emit;
|
||||||
isoal_global.sink_state[*hdl].session.sdu_write = sdu_write;
|
session->sdu_write = sdu_write;
|
||||||
|
|
||||||
/* Initialize running seq number to zero */
|
/* Initialize running seq number to zero */
|
||||||
isoal_global.sink_state[*hdl].session.seqn = 0;
|
session->seqn = 0;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -198,42 +262,41 @@ void isoal_sink_destroy(isoal_sink_handle_t hdl)
|
||||||
isoal_sink_deallocate(hdl);
|
isoal_sink_deallocate(hdl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t isoal_rx_packet_classify(const struct isoal_pdu_rx *pdu_meta)
|
|
||||||
{
|
|
||||||
/* LLID location is the same for both CIS and BIS PDUs */
|
|
||||||
uint8_t llid = pdu_meta->pdu->cis.ll_id;
|
|
||||||
|
|
||||||
return llid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Obtain destination SDU */
|
/* Obtain destination SDU */
|
||||||
static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
|
static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
|
||||||
const struct isoal_pdu_rx *pdu_meta)
|
const struct isoal_pdu_rx *pdu_meta)
|
||||||
{
|
{
|
||||||
isoal_status_t err = ISOAL_STATUS_OK;
|
struct isoal_sink_session *session;
|
||||||
struct isoal_sdu_produced *sdu = &sink->sdu_production.sdu;
|
struct isoal_sdu_production *sp;
|
||||||
|
struct isoal_sdu_produced *sdu;
|
||||||
|
isoal_status_t err;
|
||||||
|
|
||||||
|
err = ISOAL_STATUS_OK;
|
||||||
|
session = &sink->session;
|
||||||
|
sp = &sink->sdu_production;
|
||||||
|
sdu = &sp->sdu;
|
||||||
|
|
||||||
/* Allocate a SDU if the previous was filled (thus sent) */
|
/* Allocate a SDU if the previous was filled (thus sent) */
|
||||||
const bool sdu_complete = (sink->sdu_production.sdu_available == 0);
|
const bool sdu_complete = (sp->sdu_available == 0);
|
||||||
|
|
||||||
if (sdu_complete) {
|
if (sdu_complete) {
|
||||||
/* Allocate new clean SDU buffer */
|
/* Allocate new clean SDU buffer */
|
||||||
err = sink->session.sdu_alloc(
|
err = session->sdu_alloc(
|
||||||
sink,
|
sink,
|
||||||
pdu_meta, /* [in] PDU origin may determine buffer */
|
pdu_meta, /* [in] PDU origin may determine buffer */
|
||||||
&sdu->contents /* [out] Updated with pointer and size */
|
&sdu->contents /* [out] Updated with pointer and size */
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Nothing has been written into buffer yet */
|
/* Nothing has been written into buffer yet */
|
||||||
sink->sdu_production.sdu_written = 0;
|
sp->sdu_written = 0;
|
||||||
sink->sdu_production.sdu_available = sdu->contents.size;
|
sp->sdu_available = sdu->contents.size;
|
||||||
LL_ASSERT(sdu->contents.size > 0);
|
LL_ASSERT(sdu->contents.size > 0);
|
||||||
|
|
||||||
/* Remember meta data */
|
/* Remember meta data */
|
||||||
sdu->status = pdu_meta->meta->status;
|
sdu->status = pdu_meta->meta->status;
|
||||||
sdu->timestamp = pdu_meta->meta->timestamp;
|
sdu->timestamp = pdu_meta->meta->timestamp;
|
||||||
/* Get seq number from session counter, and increase SDU counter */
|
/* Get seq number from session counter */
|
||||||
sdu->seqn = sink->session.seqn++;
|
sdu->seqn = session->seqn;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -241,46 +304,48 @@ static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
|
||||||
|
|
||||||
static isoal_status_t isoal_rx_try_emit_sdu(struct isoal_sink *sink, bool end_of_sdu)
|
static isoal_status_t isoal_rx_try_emit_sdu(struct isoal_sink *sink, bool end_of_sdu)
|
||||||
{
|
{
|
||||||
isoal_status_t err = ISOAL_STATUS_OK;
|
struct isoal_sdu_production *sp;
|
||||||
struct isoal_sdu_produced *sdu = &sink->sdu_production.sdu;
|
struct isoal_sdu_produced *sdu;
|
||||||
|
isoal_status_t err;
|
||||||
|
|
||||||
|
err = ISOAL_STATUS_OK;
|
||||||
|
sp = &sink->sdu_production;
|
||||||
|
sdu = &sp->sdu;
|
||||||
|
|
||||||
/* Emit a SDU */
|
/* Emit a SDU */
|
||||||
const bool sdu_complete = (sink->sdu_production.sdu_available == 0) || end_of_sdu;
|
const bool sdu_complete = (sp->sdu_available == 0) || end_of_sdu;
|
||||||
|
|
||||||
if (end_of_sdu) {
|
if (end_of_sdu) {
|
||||||
sink->sdu_production.sdu_available = 0;
|
sp->sdu_available = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdu_complete) {
|
if (sdu_complete) {
|
||||||
uint8_t next_state = BT_ISO_START;
|
uint8_t next_state = BT_ISO_START;
|
||||||
|
|
||||||
switch (sink->sdu_production.sdu_state) {
|
switch (sp->sdu_state) {
|
||||||
case BT_ISO_START:
|
case BT_ISO_START:
|
||||||
if (end_of_sdu) {
|
if (end_of_sdu) {
|
||||||
sink->sdu_production.sdu_state = BT_ISO_SINGLE;
|
sp->sdu_state = BT_ISO_SINGLE;
|
||||||
next_state = BT_ISO_START;
|
next_state = BT_ISO_START;
|
||||||
} else {
|
} else {
|
||||||
sink->sdu_production.sdu_state = BT_ISO_START;
|
sp->sdu_state = BT_ISO_START;
|
||||||
next_state = BT_ISO_CONT;
|
next_state = BT_ISO_CONT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BT_ISO_CONT:
|
case BT_ISO_CONT:
|
||||||
if (end_of_sdu) {
|
if (end_of_sdu) {
|
||||||
sink->sdu_production.sdu_state = BT_ISO_END;
|
sp->sdu_state = BT_ISO_END;
|
||||||
next_state = BT_ISO_START;
|
next_state = BT_ISO_START;
|
||||||
} else {
|
} else {
|
||||||
sink->sdu_production.sdu_state = BT_ISO_CONT;
|
sp->sdu_state = BT_ISO_CONT;
|
||||||
next_state = BT_ISO_CONT;
|
next_state = BT_ISO_CONT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BT_ISO_END:
|
|
||||||
case BT_ISO_SINGLE:
|
|
||||||
default:
|
|
||||||
LL_ASSERT(0);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
sdu->status = sink->sdu_production.sdu_status;
|
sdu->status = sp->sdu_status;
|
||||||
err = sink->session.sdu_emit(sink, sdu);
|
struct isoal_sink_session *session = &sink->session;
|
||||||
|
|
||||||
|
err = session->sdu_emit(sink, sdu);
|
||||||
|
|
||||||
/* update next state */
|
/* update next state */
|
||||||
sink->sdu_production.sdu_state = next_state;
|
sink->sdu_production.sdu_state = next_state;
|
||||||
|
@ -291,24 +356,30 @@ static isoal_status_t isoal_rx_try_emit_sdu(struct isoal_sink *sink, bool end_of
|
||||||
|
|
||||||
static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
||||||
const struct isoal_pdu_rx *pdu_meta,
|
const struct isoal_pdu_rx *pdu_meta,
|
||||||
|
uint8_t offset,
|
||||||
|
uint8_t length,
|
||||||
bool is_end_fragment)
|
bool is_end_fragment)
|
||||||
{
|
{
|
||||||
const uint8_t *pdu_payload = pdu_meta->pdu->cis.payload;
|
isoal_pdu_len_t packet_available;
|
||||||
isoal_pdu_len_t packet_available = pdu_meta->pdu->cis.length;
|
const uint8_t *pdu_payload;
|
||||||
isoal_status_t err = ISOAL_STATUS_OK;
|
|
||||||
bool handle_error_case;
|
bool handle_error_case;
|
||||||
|
isoal_status_t err;
|
||||||
|
|
||||||
/* Might get an empty packed due to errors, we will need to terminate
|
/* Might get an empty packed due to errors, we will need to terminate
|
||||||
* and send something up anyhow
|
* and send something up anyhow
|
||||||
*/
|
*/
|
||||||
|
packet_available = length;
|
||||||
handle_error_case = (is_end_fragment && (packet_available == 0));
|
handle_error_case = (is_end_fragment && (packet_available == 0));
|
||||||
|
|
||||||
|
pdu_payload = pdu_meta->pdu->payload + offset;
|
||||||
LL_ASSERT(pdu_payload);
|
LL_ASSERT(pdu_payload);
|
||||||
|
|
||||||
/* While there is something left of the packet to consume */
|
/* While there is something left of the packet to consume */
|
||||||
|
err = ISOAL_STATUS_OK;
|
||||||
while ((packet_available > 0) || handle_error_case) {
|
while ((packet_available > 0) || handle_error_case) {
|
||||||
const isoal_status_t err_alloc = isoal_rx_allocate_sdu(sink, pdu_meta);
|
const isoal_status_t err_alloc = isoal_rx_allocate_sdu(sink, pdu_meta);
|
||||||
struct isoal_sdu_produced *sdu = &sink->sdu_production.sdu;
|
struct isoal_sdu_production *sp = &sink->sdu_production;
|
||||||
|
struct isoal_sdu_produced *sdu = &sp->sdu;
|
||||||
|
|
||||||
err |= err_alloc;
|
err |= err_alloc;
|
||||||
|
|
||||||
|
@ -319,21 +390,21 @@ static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
||||||
*/
|
*/
|
||||||
const size_t consume_len = MIN(
|
const size_t consume_len = MIN(
|
||||||
packet_available,
|
packet_available,
|
||||||
sink->sdu_production.sdu_available
|
sp->sdu_available
|
||||||
);
|
);
|
||||||
|
|
||||||
LL_ASSERT(sdu->contents.dbuf);
|
|
||||||
|
|
||||||
if (consume_len > 0) {
|
if (consume_len > 0) {
|
||||||
if (pdu_meta->meta->status == ISOAL_PDU_STATUS_VALID) {
|
if (pdu_meta->meta->status == ISOAL_PDU_STATUS_VALID) {
|
||||||
err |= sink->session.sdu_write(sdu->contents.dbuf,
|
struct isoal_sink_session *session = &sink->session;
|
||||||
pdu_payload,
|
|
||||||
consume_len);
|
err |= session->sdu_write(sdu->contents.dbuf,
|
||||||
|
pdu_payload,
|
||||||
|
consume_len);
|
||||||
}
|
}
|
||||||
pdu_payload += consume_len;
|
pdu_payload += consume_len;
|
||||||
sink->sdu_production.sdu_written += consume_len;
|
sp->sdu_written += consume_len;
|
||||||
sink->sdu_production.sdu_available -= consume_len;
|
sp->sdu_available -= consume_len;
|
||||||
packet_available -= consume_len;
|
packet_available -= consume_len;
|
||||||
}
|
}
|
||||||
bool end_of_sdu = (packet_available == 0) && is_end_fragment;
|
bool end_of_sdu = (packet_available == 0) && is_end_fragment;
|
||||||
|
|
||||||
|
@ -343,13 +414,12 @@ static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
||||||
err |= err_emit;
|
err |= err_emit;
|
||||||
}
|
}
|
||||||
|
|
||||||
LL_ASSERT(packet_available == 0);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Consume a PDU: Copy contents into SDU(s) and emit to a sink
|
* @brief Consume an unframed PDU: Copy contents into SDU(s) and emit to a sink
|
||||||
* @details Destination sink may have an already partially built SDU
|
* @details Destination sink may have an already partially built SDU
|
||||||
*
|
*
|
||||||
* @param sink[in,out] Destination sink with bookkeeping state
|
* @param sink[in,out] Destination sink with bookkeeping state
|
||||||
|
@ -357,39 +427,70 @@ static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
||||||
*
|
*
|
||||||
* @return Status
|
* @return Status
|
||||||
*/
|
*/
|
||||||
static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
static isoal_status_t isoal_rx_unframed_consume(struct isoal_sink *sink,
|
||||||
const struct isoal_pdu_rx *pdu_meta)
|
const struct isoal_pdu_rx *pdu_meta)
|
||||||
{
|
{
|
||||||
|
struct isoal_sink_session *session;
|
||||||
|
struct isoal_sdu_production *sp;
|
||||||
|
struct node_rx_iso_meta *meta;
|
||||||
|
struct pdu_iso *pdu;
|
||||||
|
bool end_of_packet;
|
||||||
|
uint8_t next_state;
|
||||||
isoal_status_t err;
|
isoal_status_t err;
|
||||||
uint8_t next_state, llid;
|
bool pdu_padding;
|
||||||
bool pdu_err, pdu_padding, last_pdu, end_of_packet, seq_err;
|
bool last_pdu;
|
||||||
|
bool pdu_err;
|
||||||
|
bool seq_err;
|
||||||
|
uint8_t llid;
|
||||||
|
|
||||||
|
sp = &sink->sdu_production;
|
||||||
|
session = &sink->session;
|
||||||
|
meta = pdu_meta->meta;
|
||||||
|
pdu = pdu_meta->pdu;
|
||||||
|
|
||||||
err = ISOAL_STATUS_OK;
|
err = ISOAL_STATUS_OK;
|
||||||
next_state = ISOAL_START;
|
next_state = ISOAL_START;
|
||||||
|
|
||||||
llid = isoal_rx_packet_classify(pdu_meta);
|
llid = pdu_meta->pdu->ll_id;
|
||||||
pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID);
|
pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID);
|
||||||
seq_err = (pdu_meta->meta->payload_number != sink->sdu_production.prev_pdu_id+1);
|
pdu_padding = (pdu_meta->pdu->length == 0) && (llid == PDU_BIS_LLID_START_CONTINUE);
|
||||||
pdu_padding = (pdu_meta->pdu->cis.length == 0) && (llid == PDU_BIS_LLID_START_CONTINUE);
|
|
||||||
|
|
||||||
if (sink->sdu_production.fsm == ISOAL_START) {
|
if (sp->fsm == ISOAL_START) {
|
||||||
sink->sdu_production.sdu_status = ISOAL_SDU_STATUS_VALID;
|
struct isoal_sdu_produced *sdu;
|
||||||
sink->sdu_production.sdu_state = BT_ISO_START;
|
uint32_t anchorpoint;
|
||||||
sink->sdu_production.pdu_cnt = 1;
|
uint32_t latency;
|
||||||
} else {
|
|
||||||
sink->sdu_production.pdu_cnt++;
|
sp->sdu_status = ISOAL_SDU_STATUS_VALID;
|
||||||
|
sp->sdu_state = BT_ISO_START;
|
||||||
|
sp->pdu_cnt = 1;
|
||||||
|
session->seqn++;
|
||||||
|
seq_err = false;
|
||||||
|
|
||||||
|
/* Todo: anchorpoint must be reference anchor point, should be fixed in LL */
|
||||||
|
anchorpoint = meta->timestamp;
|
||||||
|
latency = session->latency_unframed;
|
||||||
|
sdu = &sp->sdu;
|
||||||
|
|
||||||
|
sdu->timestamp = anchorpoint + latency;
|
||||||
|
} else {
|
||||||
|
sp->pdu_cnt++;
|
||||||
|
seq_err = (meta->payload_number != (sp->prev_pdu_id+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
last_pdu = (sink->sdu_production.pdu_cnt == sink->session.pdus_per_sdu);
|
last_pdu = (sp->pdu_cnt == session->pdus_per_sdu);
|
||||||
end_of_packet = (llid == PDU_BIS_LLID_COMPLETE_END) || last_pdu;
|
end_of_packet = (llid == PDU_BIS_LLID_COMPLETE_END) || last_pdu;
|
||||||
|
|
||||||
switch (sink->sdu_production.fsm) {
|
switch (sp->fsm) {
|
||||||
|
|
||||||
case ISOAL_START:
|
case ISOAL_START:
|
||||||
case ISOAL_CONTINUE:
|
case ISOAL_CONTINUE:
|
||||||
if (pdu_err || seq_err) {
|
if (pdu_err || seq_err) {
|
||||||
/* PDU contains errors */
|
/* PDU contains errors */
|
||||||
next_state = ISOAL_ERR_SPOOL;
|
if (last_pdu) {
|
||||||
|
/* Last PDU all done */
|
||||||
|
next_state = ISOAL_START;
|
||||||
|
} else {
|
||||||
|
next_state = ISOAL_ERR_SPOOL;
|
||||||
|
}
|
||||||
} else if (llid == PDU_BIS_LLID_START_CONTINUE) {
|
} else if (llid == PDU_BIS_LLID_START_CONTINUE) {
|
||||||
/* PDU contains a continuation (neither start of end) fragment of SDU */
|
/* PDU contains a continuation (neither start of end) fragment of SDU */
|
||||||
if (last_pdu) {
|
if (last_pdu) {
|
||||||
|
@ -409,7 +510,7 @@ static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Unsupported case */
|
/* Unsupported case */
|
||||||
LL_ASSERT(0);
|
err = ISOAL_STATUS_ERR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -429,23 +530,221 @@ static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
||||||
|
|
||||||
/* Update error state */
|
/* Update error state */
|
||||||
if (pdu_err && !pdu_padding) {
|
if (pdu_err && !pdu_padding) {
|
||||||
sink->sdu_production.sdu_status |= pdu_meta->meta->status;
|
sp->sdu_status |= meta->status;
|
||||||
} else if (last_pdu && (llid != PDU_BIS_LLID_COMPLETE_END) &&
|
} else if (last_pdu && (llid != PDU_BIS_LLID_COMPLETE_END) &&
|
||||||
(sink->sdu_production.fsm != ISOAL_ERR_SPOOL)) {
|
(sp->fsm != ISOAL_ERR_SPOOL)) {
|
||||||
/* END fragment never seen */
|
/* END fragment never seen */
|
||||||
sink->sdu_production.sdu_status |= ISOAL_SDU_STATUS_ERRORS;
|
sp->sdu_status |= ISOAL_SDU_STATUS_ERRORS;
|
||||||
} else if (seq_err) {
|
} else if (seq_err) {
|
||||||
sink->sdu_production.sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
|
sp->sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append valid PDU to SDU */
|
/* Append valid PDU to SDU */
|
||||||
if (!pdu_padding && !pdu_err) {
|
if (!pdu_padding) {
|
||||||
err |= isoal_rx_append_to_sdu(sink, pdu_meta, end_of_packet);
|
err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0,
|
||||||
|
pdu_meta->pdu->length,
|
||||||
|
end_of_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update next stat */
|
/* Update next state */
|
||||||
sink->sdu_production.fsm = next_state;
|
sp->fsm = next_state;
|
||||||
sink->sdu_production.prev_pdu_id = pdu_meta->meta->payload_number;
|
sp->prev_pdu_id = meta->payload_number;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Consume a framed PDU: Copy contents into SDU(s) and emit to a sink
|
||||||
|
* @details Destination sink may have an already partially built SDU
|
||||||
|
*
|
||||||
|
* @param sink[in,out] Destination sink with bookkeeping state
|
||||||
|
* @param pdu_meta[out] PDU with meta information (origin, timing, status)
|
||||||
|
*
|
||||||
|
* @return Status
|
||||||
|
*/
|
||||||
|
static isoal_status_t isoal_rx_framed_consume(struct isoal_sink *sink,
|
||||||
|
const struct isoal_pdu_rx *pdu_meta)
|
||||||
|
{
|
||||||
|
struct isoal_sink_session *session;
|
||||||
|
struct isoal_sdu_production *sp;
|
||||||
|
struct isoal_sdu_produced *sdu;
|
||||||
|
struct pdu_iso_sdu_sh *seg_hdr;
|
||||||
|
struct node_rx_iso_meta *meta;
|
||||||
|
uint32_t anchorpoint;
|
||||||
|
uint8_t *end_of_pdu;
|
||||||
|
uint32_t timeoffset;
|
||||||
|
isoal_status_t err;
|
||||||
|
uint8_t next_state;
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint32_t latency;
|
||||||
|
bool pdu_padding;
|
||||||
|
bool pdu_err;
|
||||||
|
bool seq_err;
|
||||||
|
|
||||||
|
sp = &sink->sdu_production;
|
||||||
|
session = &sink->session;
|
||||||
|
meta = pdu_meta->meta;
|
||||||
|
sdu = &sp->sdu;
|
||||||
|
|
||||||
|
err = ISOAL_STATUS_OK;
|
||||||
|
next_state = ISOAL_START;
|
||||||
|
pdu_err = (pdu_meta->meta->status != ISOAL_PDU_STATUS_VALID);
|
||||||
|
pdu_padding = (pdu_meta->pdu->length == 0);
|
||||||
|
|
||||||
|
if (sp->fsm == ISOAL_START) {
|
||||||
|
seq_err = false;
|
||||||
|
} else {
|
||||||
|
seq_err = (meta->payload_number != (sp->prev_pdu_id + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
end_of_pdu = ((uint8_t *) pdu_meta->pdu->payload) + pdu_meta->pdu->length - 1;
|
||||||
|
seg_hdr = (struct pdu_iso_sdu_sh *) pdu_meta->pdu->payload;
|
||||||
|
|
||||||
|
if (pdu_err || seq_err) {
|
||||||
|
/* When one or more ISO Data PDUs are not received, the receiving device may
|
||||||
|
* discard all SDUs affected by the missing PDUs. Any partially received SDU
|
||||||
|
* may also be discarded.
|
||||||
|
*/
|
||||||
|
next_state = ISOAL_ERR_SPOOL;
|
||||||
|
/* Update next state */
|
||||||
|
sink->sdu_production.fsm = next_state;
|
||||||
|
|
||||||
|
if (pdu_err) {
|
||||||
|
sp->sdu_status |= meta->status;
|
||||||
|
} else if (seq_err) {
|
||||||
|
sp->sdu_status |= ISOAL_SDU_STATUS_LOST_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush current SDU with error if any */
|
||||||
|
err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0, 0, true);
|
||||||
|
|
||||||
|
/* Skip searching this PDU */
|
||||||
|
seg_hdr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdu_padding) {
|
||||||
|
/* Skip searching this PDU */
|
||||||
|
seg_hdr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (seg_hdr) {
|
||||||
|
bool append = true;
|
||||||
|
const uint8_t sc = seg_hdr->sc;
|
||||||
|
const uint8_t cmplt = seg_hdr->cmplt;
|
||||||
|
|
||||||
|
if (sp->fsm == ISOAL_START) {
|
||||||
|
sp->sdu_status = ISOAL_SDU_STATUS_VALID;
|
||||||
|
sp->sdu_state = BT_ISO_START;
|
||||||
|
session->seqn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sp->fsm) {
|
||||||
|
case ISOAL_START:
|
||||||
|
timeoffset = seg_hdr->timeoffset;
|
||||||
|
anchorpoint = meta->timestamp;
|
||||||
|
latency = session->latency_framed;
|
||||||
|
timestamp = anchorpoint + latency - timeoffset;
|
||||||
|
|
||||||
|
if (!sc && !cmplt) {
|
||||||
|
/* The start of a new SDU, where not all SDU data is included in
|
||||||
|
* the current PDU, and additional PDUs are required to complete
|
||||||
|
* the SDU.
|
||||||
|
*/
|
||||||
|
sdu->timestamp = timestamp;
|
||||||
|
next_state = ISOAL_CONTINUE;
|
||||||
|
} else if (!sc && cmplt) {
|
||||||
|
/* The start of a new SDU that contains the full SDU data in the
|
||||||
|
* current PDU.
|
||||||
|
*/
|
||||||
|
sdu->timestamp = timestamp;
|
||||||
|
next_state = ISOAL_START;
|
||||||
|
} else {
|
||||||
|
/* Unsupported case */
|
||||||
|
err = ISOAL_STATUS_ERR_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ISOAL_CONTINUE:
|
||||||
|
if (sc && !cmplt) {
|
||||||
|
/* The continuation of a previous SDU. The SDU payload is appended
|
||||||
|
* to the previous data and additional PDUs are required to
|
||||||
|
* complete the SDU.
|
||||||
|
*/
|
||||||
|
next_state = ISOAL_CONTINUE;
|
||||||
|
} else if (sc && cmplt) {
|
||||||
|
/* The continuation of a previous SDU.
|
||||||
|
* Frame data is appended to previously received SDU data and
|
||||||
|
* completes in the current PDU.
|
||||||
|
*/
|
||||||
|
next_state = ISOAL_START;
|
||||||
|
} else {
|
||||||
|
/* Unsupported case */
|
||||||
|
err = ISOAL_STATUS_ERR_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ISOAL_ERR_SPOOL:
|
||||||
|
/* In error state, search for valid next start of SDU */
|
||||||
|
timeoffset = seg_hdr->timeoffset;
|
||||||
|
anchorpoint = meta->timestamp;
|
||||||
|
latency = session->latency_framed;
|
||||||
|
timestamp = anchorpoint + latency - timeoffset;
|
||||||
|
|
||||||
|
if (!sc && !cmplt) {
|
||||||
|
/* The start of a new SDU, where not all SDU data is included in
|
||||||
|
* the current PDU, and additional PDUs are required to complete
|
||||||
|
* the SDU.
|
||||||
|
*/
|
||||||
|
sdu->timestamp = timestamp;
|
||||||
|
next_state = ISOAL_CONTINUE;
|
||||||
|
} else if (!sc && cmplt) {
|
||||||
|
/* The start of a new SDU that contains the full SDU data in the
|
||||||
|
* current PDU.
|
||||||
|
*/
|
||||||
|
sdu->timestamp = timestamp;
|
||||||
|
next_state = ISOAL_START;
|
||||||
|
} else {
|
||||||
|
/* Start not found yet, stay in Error state */
|
||||||
|
append = false;
|
||||||
|
next_state = ISOAL_ERR_SPOOL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (append) {
|
||||||
|
/* Calculate offset of first payload byte from SDU based on assumption
|
||||||
|
* of No time_offset in header
|
||||||
|
*/
|
||||||
|
uint8_t offset = ((uint8_t *) seg_hdr) + PDU_ISO_SEG_HDR_SIZE -
|
||||||
|
pdu_meta->pdu->payload;
|
||||||
|
uint8_t length = seg_hdr->length;
|
||||||
|
|
||||||
|
if (!sc) {
|
||||||
|
/* time_offset included in header, don't copy offset field to SDU */
|
||||||
|
offset = offset + PDU_ISO_SEG_TIMEOFFSET_SIZE;
|
||||||
|
length = length - PDU_ISO_SEG_TIMEOFFSET_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Todo: check if effective len=0 what happens then?
|
||||||
|
* We should possibly be able to send empty packets with only time stamp
|
||||||
|
*/
|
||||||
|
|
||||||
|
err |= isoal_rx_append_to_sdu(sink, pdu_meta, offset, length, cmplt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update next state */
|
||||||
|
sp->fsm = next_state;
|
||||||
|
|
||||||
|
/* Find next segment header, set to null if past end of PDU */
|
||||||
|
seg_hdr = (struct pdu_iso_sdu_sh *) (((uint8_t *) seg_hdr) +
|
||||||
|
seg_hdr->length + PDU_ISO_SEG_HDR_SIZE);
|
||||||
|
|
||||||
|
if (((uint8_t *) seg_hdr) > end_of_pdu) {
|
||||||
|
seg_hdr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sp->prev_pdu_id = meta->payload_number;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -465,7 +764,13 @@ isoal_status_t isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl,
|
||||||
isoal_status_t err = ISOAL_STATUS_ERR_SDU_ALLOC;
|
isoal_status_t err = ISOAL_STATUS_ERR_SDU_ALLOC;
|
||||||
|
|
||||||
if (sink->sdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) {
|
if (sink->sdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) {
|
||||||
err = isoal_rx_packet_consume(sink, pdu_meta);
|
bool pdu_framed = (pdu_meta->pdu->ll_id == PDU_BIS_LLID_FRAMED);
|
||||||
|
|
||||||
|
if (pdu_framed) {
|
||||||
|
err = isoal_rx_framed_consume(sink, pdu_meta);
|
||||||
|
} else {
|
||||||
|
err = isoal_rx_unframed_consume(sink, pdu_meta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -15,6 +15,9 @@ typedef uint8_t isoal_status_t;
|
||||||
#define ISOAL_STATUS_ERR_SOURCE_ALLOC ((isoal_status_t) 0x02) /* Source pool full */
|
#define ISOAL_STATUS_ERR_SOURCE_ALLOC ((isoal_status_t) 0x02) /* Source pool full */
|
||||||
#define ISOAL_STATUS_ERR_SDU_ALLOC ((isoal_status_t) 0x04) /* SDU allocation */
|
#define ISOAL_STATUS_ERR_SDU_ALLOC ((isoal_status_t) 0x04) /* SDU allocation */
|
||||||
#define ISOAL_STATUS_ERR_SDU_EMIT ((isoal_status_t) 0x08) /* SDU emission */
|
#define ISOAL_STATUS_ERR_SDU_EMIT ((isoal_status_t) 0x08) /* SDU emission */
|
||||||
|
#define ISOAL_STATUS_ERR_UNSPECIFIED ((isoal_status_t) 0x10) /* Unspecified error */
|
||||||
|
|
||||||
|
#define BT_ROLE_BROADCAST (BT_CONN_ROLE_PERIPHERAL + 1)
|
||||||
|
|
||||||
/** Handle to a registered ISO Sub-System sink */
|
/** Handle to a registered ISO Sub-System sink */
|
||||||
typedef uint8_t isoal_sink_handle_t;
|
typedef uint8_t isoal_sink_handle_t;
|
||||||
|
@ -108,19 +111,13 @@ struct isoal_sdu_produced {
|
||||||
void *ctx;
|
void *ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief ISO PDU. Covers both CIS and BIS */
|
|
||||||
union isoal_pdu {
|
|
||||||
struct pdu_cis cis;
|
|
||||||
struct pdu_bis bis;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** @brief Received ISO PDU with associated meta data */
|
/** @brief Received ISO PDU with associated meta data */
|
||||||
struct isoal_pdu_rx {
|
struct isoal_pdu_rx {
|
||||||
/** Meta */
|
/** Meta */
|
||||||
struct node_rx_iso_meta *meta;
|
struct node_rx_iso_meta *meta;
|
||||||
/** PDU contents and length can only be trusted if status is valid */
|
/** PDU contents and length can only be trusted if status is valid */
|
||||||
union isoal_pdu *pdu;
|
struct pdu_iso *pdu;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,38 +176,43 @@ struct isoal_sink_config {
|
||||||
/* TODO add SDU and PDU max length etc. */
|
/* TODO add SDU and PDU max length etc. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct isoal_sink_session {
|
||||||
|
isoal_sink_sdu_alloc_cb sdu_alloc;
|
||||||
|
isoal_sink_sdu_emit_cb sdu_emit;
|
||||||
|
isoal_sink_sdu_write_cb sdu_write;
|
||||||
|
struct isoal_sink_config param;
|
||||||
|
isoal_sdu_cnt_t seqn;
|
||||||
|
uint16_t handle;
|
||||||
|
uint8_t pdus_per_sdu;
|
||||||
|
uint32_t latency_unframed;
|
||||||
|
uint32_t latency_framed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct isoal_sdu_production {
|
||||||
|
/* Permit atomic enable/disable of SDU production */
|
||||||
|
volatile isoal_production_mode_t mode;
|
||||||
|
/* We are constructing an SDU from {<1 or =1 or >1} PDUs */
|
||||||
|
struct isoal_sdu_produced sdu;
|
||||||
|
/* Bookkeeping */
|
||||||
|
isoal_pdu_cnt_t prev_pdu_id : 39;
|
||||||
|
enum {
|
||||||
|
ISOAL_START,
|
||||||
|
ISOAL_CONTINUE,
|
||||||
|
ISOAL_ERR_SPOOL
|
||||||
|
} fsm;
|
||||||
|
uint8_t pdu_cnt;
|
||||||
|
uint8_t sdu_state;
|
||||||
|
isoal_sdu_len_t sdu_written;
|
||||||
|
isoal_sdu_len_t sdu_available;
|
||||||
|
isoal_sdu_status_t sdu_status;
|
||||||
|
};
|
||||||
|
|
||||||
struct isoal_sink {
|
struct isoal_sink {
|
||||||
/* Session-constant */
|
/* Session-constant */
|
||||||
struct {
|
struct isoal_sink_session session;
|
||||||
isoal_sink_sdu_alloc_cb sdu_alloc;
|
|
||||||
isoal_sink_sdu_emit_cb sdu_emit;
|
|
||||||
isoal_sink_sdu_write_cb sdu_write;
|
|
||||||
struct isoal_sink_config param;
|
|
||||||
isoal_sdu_cnt_t seqn;
|
|
||||||
uint16_t handle;
|
|
||||||
uint8_t pdus_per_sdu;
|
|
||||||
} session;
|
|
||||||
|
|
||||||
/* State for SDU production */
|
/* State for SDU production */
|
||||||
struct {
|
struct isoal_sdu_production sdu_production;
|
||||||
/* Permit atomic enable/disable of SDU production */
|
|
||||||
volatile isoal_production_mode_t mode;
|
|
||||||
/* We are constructing an SDU from {<1 or =1 or >1} PDUs */
|
|
||||||
struct isoal_sdu_produced sdu;
|
|
||||||
/* Bookkeeping */
|
|
||||||
isoal_pdu_cnt_t prev_pdu_id : 39;
|
|
||||||
enum {
|
|
||||||
ISOAL_START,
|
|
||||||
ISOAL_CONTINUE,
|
|
||||||
ISOAL_ERR_SPOOL
|
|
||||||
} fsm;
|
|
||||||
uint8_t pdu_cnt;
|
|
||||||
uint8_t sdu_state;
|
|
||||||
isoal_sdu_len_t sdu_written;
|
|
||||||
isoal_sdu_len_t sdu_available;
|
|
||||||
isoal_sdu_status_t sdu_status;
|
|
||||||
} sdu_production;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -218,14 +220,18 @@ isoal_status_t isoal_init(void);
|
||||||
|
|
||||||
isoal_status_t isoal_reset(void);
|
isoal_status_t isoal_reset(void);
|
||||||
|
|
||||||
isoal_status_t isoal_sink_create(isoal_sink_handle_t *hdl,
|
isoal_status_t isoal_sink_create(uint16_t handle,
|
||||||
uint16_t handle,
|
uint8_t role,
|
||||||
uint8_t burst_number,
|
uint8_t burst_number,
|
||||||
|
uint8_t flush_timeout,
|
||||||
uint32_t sdu_interval,
|
uint32_t sdu_interval,
|
||||||
uint16_t iso_interval,
|
uint16_t iso_interval,
|
||||||
|
uint32_t stream_sync_delay,
|
||||||
|
uint32_t group_sync_delay,
|
||||||
isoal_sink_sdu_alloc_cb sdu_alloc,
|
isoal_sink_sdu_alloc_cb sdu_alloc,
|
||||||
isoal_sink_sdu_emit_cb sdu_emit,
|
isoal_sink_sdu_emit_cb sdu_emit,
|
||||||
isoal_sink_sdu_write_cb sdu_write);
|
isoal_sink_sdu_write_cb sdu_write,
|
||||||
|
isoal_sink_handle_t *hdl);
|
||||||
|
|
||||||
struct isoal_sink_config *isoal_get_sink_param_ref(isoal_sink_handle_t hdl);
|
struct isoal_sink_config *isoal_get_sink_param_ref(isoal_sink_handle_t hdl);
|
||||||
|
|
||||||
|
|
|
@ -842,6 +842,52 @@ struct pdu_data {
|
||||||
} __packed;
|
} __packed;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/* Generic ISO pdu, could be CIS or BIS
|
||||||
|
* To be used when reffering to component withouth knowing CIS or BIS type
|
||||||
|
*/
|
||||||
|
struct pdu_iso {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
uint8_t ll_id:2;
|
||||||
|
uint8_t hdr_other:6;
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint8_t hdr_other:6;
|
||||||
|
uint8_t ll_id:2;
|
||||||
|
#else
|
||||||
|
#error "Unsupported endianness"
|
||||||
|
#endif /* __BYTE_ORDER__ */
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t payload[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* ISO SDU segmentation header */
|
||||||
|
#define PDU_ISO_SEG_HDR_SIZE 2
|
||||||
|
#define PDU_ISO_SEG_TIMEOFFSET_SIZE 3
|
||||||
|
|
||||||
|
struct pdu_iso_sdu_sh {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
uint8_t sc:1;
|
||||||
|
uint8_t cmplt:1;
|
||||||
|
uint8_t rfu:6;
|
||||||
|
|
||||||
|
uint8_t length;
|
||||||
|
/* Note, timeoffset only available in first segment of sdu */
|
||||||
|
uint32_t timeoffset:24;
|
||||||
|
uint32_t payload:8;
|
||||||
|
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint8_t rfu:6;
|
||||||
|
uint8_t cmplt:1;
|
||||||
|
uint8_t sc:1;
|
||||||
|
|
||||||
|
uint8_t length;
|
||||||
|
/* Note, timeoffset only available in first segment of sdu */
|
||||||
|
uint32_t payload:8;
|
||||||
|
uint32_t timeoffset:24;
|
||||||
|
#else
|
||||||
|
#error "Unsupported endianness"
|
||||||
|
#endif /* __BYTE_ORDER__ */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct pdu_cis {
|
struct pdu_cis {
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
uint8_t ll_id:2;
|
uint8_t ll_id:2;
|
||||||
|
|
|
@ -61,10 +61,10 @@
|
||||||
#include "ull_sync_internal.h"
|
#include "ull_sync_internal.h"
|
||||||
#include "ull_sync_iso_internal.h"
|
#include "ull_sync_iso_internal.h"
|
||||||
#include "ull_central_internal.h"
|
#include "ull_central_internal.h"
|
||||||
|
#include "ull_iso_types.h"
|
||||||
#include "ull_conn_internal.h"
|
#include "ull_conn_internal.h"
|
||||||
#include "lll_conn_iso.h"
|
#include "lll_conn_iso.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
#include "ull_iso_types.h"
|
|
||||||
#include "ull_central_iso_internal.h"
|
#include "ull_central_iso_internal.h"
|
||||||
|
|
||||||
#include "ull_conn_iso_internal.h"
|
#include "ull_conn_iso_internal.h"
|
||||||
|
@ -1490,7 +1490,7 @@ void ll_rx_mem_release(void **node_rx)
|
||||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||||
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
|
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CONN)
|
#if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
case NODE_RX_TYPE_TERMINATE:
|
case NODE_RX_TYPE_TERMINATE:
|
||||||
{
|
{
|
||||||
if (IS_ACL_HANDLE(rx_free->handle)) {
|
if (IS_ACL_HANDLE(rx_free->handle)) {
|
||||||
|
@ -1509,7 +1509,7 @@ void ll_rx_mem_release(void **node_rx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif /* CONFIG_BT_CONN */
|
#endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_CONN_ISO */
|
||||||
|
|
||||||
case NODE_RX_TYPE_EVENT_DONE:
|
case NODE_RX_TYPE_EVENT_DONE:
|
||||||
default:
|
default:
|
||||||
|
@ -2543,7 +2543,7 @@ static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx)
|
||||||
struct node_rx_pdu *rx_pdu = (struct node_rx_pdu *)rx;
|
struct node_rx_pdu *rx_pdu = (struct node_rx_pdu *)rx;
|
||||||
struct ll_conn_iso_stream *cis =
|
struct ll_conn_iso_stream *cis =
|
||||||
ll_conn_iso_stream_get(rx_pdu->hdr.handle);
|
ll_conn_iso_stream_get(rx_pdu->hdr.handle);
|
||||||
struct ll_iso_datapath *dp = cis->datapath_out;
|
struct ll_iso_datapath *dp = cis->hdr.datapath_out;
|
||||||
isoal_sink_handle_t sink = dp->sink_hdl;
|
isoal_sink_handle_t sink = dp->sink_hdl;
|
||||||
|
|
||||||
if (dp->path_id != BT_HCI_DATAPATH_ID_HCI) {
|
if (dp->path_id != BT_HCI_DATAPATH_ID_HCI) {
|
||||||
|
@ -2553,7 +2553,7 @@ static inline int rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx)
|
||||||
*/
|
*/
|
||||||
struct isoal_pdu_rx pckt_meta = {
|
struct isoal_pdu_rx pckt_meta = {
|
||||||
.meta = &rx_pdu->hdr.rx_iso_meta,
|
.meta = &rx_pdu->hdr.rx_iso_meta,
|
||||||
.pdu = (union isoal_pdu *) &rx_pdu->pdu[0]
|
.pdu = (struct pdu_iso *) &rx_pdu->pdu[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Pass the ISO PDU through ISO-AL */
|
/* Pass the ISO PDU through ISO-AL */
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
#include "ull_tx_queue.h"
|
#include "ull_tx_queue.h"
|
||||||
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
|
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
|
||||||
|
|
||||||
|
#include "isoal.h"
|
||||||
|
#include "ull_iso_types.h"
|
||||||
#include "ull_conn_types.h"
|
#include "ull_conn_types.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
#include "ull_internal.h"
|
#include "ull_internal.h"
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include "lll.h"
|
#include "lll.h"
|
||||||
#include "lll_conn.h"
|
#include "lll_conn.h"
|
||||||
#include "ull_conn_types.h"
|
#include "ull_conn_types.h"
|
||||||
|
#include "isoal.h"
|
||||||
|
#include "ull_iso_types.h"
|
||||||
#include "lll_conn_iso.h"
|
#include "lll_conn_iso.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
#include "ull_conn_internal.h"
|
#include "ull_conn_internal.h"
|
||||||
|
@ -30,7 +32,6 @@
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "hal/debug.h"
|
#include "hal/debug.h"
|
||||||
|
|
||||||
#define LAST_VALID_CIS_HANDLE (CONFIG_BT_CTLR_CONN_ISO_STREAMS + LL_CIS_HANDLE_BASE - 1)
|
|
||||||
|
|
||||||
static int init_reset(void);
|
static int init_reset(void);
|
||||||
static void ticker_update_cig_op_cb(uint32_t status, void *param);
|
static void ticker_update_cig_op_cb(uint32_t status, void *param);
|
||||||
|
@ -85,12 +86,19 @@ struct ll_conn_iso_group *ll_conn_iso_group_get_by_id(uint8_t id)
|
||||||
|
|
||||||
struct ll_conn_iso_stream *ll_conn_iso_stream_acquire(void)
|
struct ll_conn_iso_stream *ll_conn_iso_stream_acquire(void)
|
||||||
{
|
{
|
||||||
return mem_acquire(&cis_free);
|
struct ll_conn_iso_stream *cis = mem_acquire(&cis_free);
|
||||||
|
|
||||||
|
cis->hdr.datapath_in = NULL;
|
||||||
|
cis->hdr.datapath_out = NULL;
|
||||||
|
return cis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ll_conn_iso_stream_release(struct ll_conn_iso_stream *cis)
|
void ll_conn_iso_stream_release(struct ll_conn_iso_stream *cis)
|
||||||
{
|
{
|
||||||
mem_release(cis, &cig_free);
|
cis->cis_id = 0;
|
||||||
|
cis->group = NULL;
|
||||||
|
|
||||||
|
mem_release(cis, &cis_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ll_conn_iso_stream_handle_get(struct ll_conn_iso_stream *cis)
|
uint16_t ll_conn_iso_stream_handle_get(struct ll_conn_iso_stream *cis)
|
||||||
|
|
|
@ -8,19 +8,14 @@ struct ll_conn;
|
||||||
|
|
||||||
typedef void (*ll_iso_stream_released_cb_t)(struct ll_conn *conn);
|
typedef void (*ll_iso_stream_released_cb_t)(struct ll_conn *conn);
|
||||||
|
|
||||||
#define LL_CIS_HANDLE_BASE CONFIG_BT_MAX_CONN
|
|
||||||
|
|
||||||
#define LL_CIS_IDX_FROM_HANDLE(_handle) \
|
|
||||||
((_handle) - LL_CIS_HANDLE_BASE)
|
|
||||||
|
|
||||||
struct ll_conn_iso_stream {
|
struct ll_conn_iso_stream {
|
||||||
|
struct ll_iso_stream_hdr hdr;
|
||||||
struct ll_conn_iso_group *group;
|
struct ll_conn_iso_group *group;
|
||||||
struct lll_conn_iso_stream lll;
|
struct lll_conn_iso_stream lll;
|
||||||
uint32_t sync_delay;
|
uint32_t sync_delay;
|
||||||
uint8_t cis_id;
|
uint8_t cis_id;
|
||||||
uint8_t terminate_reason;
|
uint8_t terminate_reason;
|
||||||
struct ll_iso_datapath *datapath_in;
|
|
||||||
struct ll_iso_datapath *datapath_out;
|
|
||||||
uint32_t offset; /* Offset of CIS from ACL event in us */
|
uint32_t offset; /* Offset of CIS from ACL event in us */
|
||||||
ll_iso_stream_released_cb_t released_cb; /* CIS release callback */
|
ll_iso_stream_released_cb_t released_cb; /* CIS release callback */
|
||||||
uint8_t established : 1; /* 0 if CIS has not yet been established.
|
uint8_t established : 1; /* 0 if CIS has not yet been established.
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "ull_sync_types.h"
|
#include "ull_sync_types.h"
|
||||||
#include "ull_iso_types.h"
|
#include "ull_iso_types.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
|
#include "ull_internal.h"
|
||||||
|
#include "ull_iso_internal.h"
|
||||||
|
|
||||||
#include "ull_conn_internal.h"
|
#include "ull_conn_internal.h"
|
||||||
#include "ull_sync_iso_internal.h"
|
#include "ull_sync_iso_internal.h"
|
||||||
|
@ -136,37 +138,55 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
||||||
ARG_UNUSED(codec_config_len);
|
ARG_UNUSED(codec_config_len);
|
||||||
ARG_UNUSED(codec_config);
|
ARG_UNUSED(codec_config);
|
||||||
|
|
||||||
|
isoal_sink_handle_t sink_handle;
|
||||||
|
uint32_t stream_sync_delay;
|
||||||
|
uint32_t group_sync_delay;
|
||||||
|
uint8_t flush_timeout;
|
||||||
|
uint16_t iso_interval;
|
||||||
|
uint32_t sdu_interval;
|
||||||
|
uint8_t burst_number;
|
||||||
|
isoal_status_t err;
|
||||||
|
uint8_t role;
|
||||||
|
|
||||||
if (path_id == BT_HCI_DATAPATH_ID_DISABLED) {
|
if (path_id == BT_HCI_DATAPATH_ID_DISABLED) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_dir > BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
if (path_dir != BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
/* FIXME: workaround to succeed datapath setup for ISO
|
||||||
|
* broadcaster until Tx datapath is implemented, in the
|
||||||
|
* future.
|
||||||
|
*/
|
||||||
|
return BT_HCI_ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: If the Host attempts to set a data path with a Connection Handle
|
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
|
struct ll_iso_datapath *dp_in = NULL;
|
||||||
|
struct ll_iso_datapath *dp_out = NULL;
|
||||||
|
|
||||||
|
struct ll_conn_iso_stream *cis = NULL;
|
||||||
|
struct ll_conn_iso_group *cig = NULL;
|
||||||
|
|
||||||
|
if (IS_CIS_HANDLE(handle)) {
|
||||||
|
cis = ll_conn_iso_stream_get(handle);
|
||||||
|
cig = cis->group;
|
||||||
|
dp_in = cis->hdr.datapath_in;
|
||||||
|
dp_out = cis->hdr.datapath_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the Host attempts to set a data path with a Connection Handle
|
||||||
* that does not exist or that is not for a CIS or a BIS, the Controller
|
* that does not exist or that is not for a CIS or a BIS, the Controller
|
||||||
* shall return the error code Unknown Connection Identifier (0x02)
|
* shall return the error code Unknown Connection Identifier (0x02)
|
||||||
*/
|
*/
|
||||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
if (!cis) {
|
||||||
struct ll_conn_iso_stream *cis;
|
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||||
struct ll_conn_iso_group *cig;
|
}
|
||||||
isoal_sink_handle_t sink_hdl;
|
|
||||||
isoal_status_t err = 0;
|
|
||||||
|
|
||||||
cis = ll_conn_iso_stream_get(handle);
|
if ((path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR && dp_in) ||
|
||||||
/* TODO: Check valid cis */
|
(path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST && dp_out)) {
|
||||||
|
|
||||||
cig = cis->group;
|
|
||||||
|
|
||||||
if ((path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR &&
|
|
||||||
cis->datapath_in) ||
|
|
||||||
(path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST &&
|
|
||||||
cis->datapath_out)) {
|
|
||||||
/* Data path has been set up, can only do setup once */
|
/* Data path has been set up, can only do setup once */
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (path_is_vendor_specific(path_id) &&
|
if (path_is_vendor_specific(path_id) &&
|
||||||
!ll_data_path_configured(path_dir, path_id)) {
|
!ll_data_path_configured(path_dir, path_id)) {
|
||||||
|
@ -181,93 +201,11 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
||||||
if (codec_config_len && vs_codec_id == BT_HCI_CODING_FORMAT_TRANSPARENT) {
|
if (codec_config_len && vs_codec_id == BT_HCI_CODING_FORMAT_TRANSPARENT) {
|
||||||
return BT_HCI_ERR_INVALID_PARAM;
|
return BT_HCI_ERR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
|
||||||
/* Allocate and configure datapath */
|
|
||||||
struct ll_iso_datapath *dp = mem_acquire(&datapath_free);
|
|
||||||
|
|
||||||
/* TODO: check valid dp buffer */
|
|
||||||
|
|
||||||
dp->path_dir = path_dir;
|
|
||||||
dp->path_id = path_id;
|
|
||||||
dp->coding_format = coding_format;
|
|
||||||
dp->company_id = company_id;
|
|
||||||
|
|
||||||
/* TODO dp->sync_delay = controller_delay; ?*/
|
|
||||||
|
|
||||||
uint32_t sdu_interval;
|
|
||||||
uint8_t burst_number;
|
|
||||||
|
|
||||||
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
|
||||||
burst_number = cis->lll.tx.burst_number;
|
|
||||||
if (cig->lll.role) {
|
|
||||||
/* peripheral */
|
|
||||||
sdu_interval = cig->p_sdu_interval;
|
|
||||||
} else {
|
|
||||||
/* central */
|
|
||||||
sdu_interval = cig->c_sdu_interval;
|
|
||||||
}
|
|
||||||
cis->datapath_in = dp;
|
|
||||||
} else {
|
|
||||||
burst_number = cis->lll.rx.burst_number;
|
|
||||||
if (cig->lll.role) {
|
|
||||||
/* peripheral */
|
|
||||||
sdu_interval = cig->c_sdu_interval;
|
|
||||||
} else {
|
|
||||||
/* central */
|
|
||||||
sdu_interval = cig->p_sdu_interval;
|
|
||||||
}
|
|
||||||
cis->datapath_out = dp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path_id == BT_HCI_DATAPATH_ID_HCI) {
|
|
||||||
/* Set up HCI data path */
|
|
||||||
err = isoal_sink_create(&sink_hdl, handle, burst_number, sdu_interval,
|
|
||||||
cig->iso_interval, sink_sdu_alloc_hci,
|
|
||||||
sink_sdu_emit_hci, sink_sdu_write_hci);
|
|
||||||
} else {
|
|
||||||
/* Set up vendor specific data path */
|
|
||||||
isoal_sink_sdu_alloc_cb sdu_alloc;
|
|
||||||
isoal_sink_sdu_emit_cb sdu_emit;
|
|
||||||
isoal_sink_sdu_write_cb sdu_write;
|
|
||||||
|
|
||||||
/* Request vendor sink callbacks for path */
|
|
||||||
if (ll_data_path_sink_create(dp, &sdu_alloc, &sdu_emit, &sdu_write)) {
|
|
||||||
err = isoal_sink_create(&sink_hdl, handle, burst_number,
|
|
||||||
sdu_interval, cig->iso_interval,
|
|
||||||
sdu_alloc, sdu_emit, sdu_write);
|
|
||||||
} else {
|
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
dp->sink_hdl = sink_hdl;
|
|
||||||
isoal_sink_enable(sink_hdl);
|
|
||||||
} else {
|
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||||
struct lll_sync_iso_stream *stream;
|
struct lll_sync_iso_stream *stream;
|
||||||
struct ll_sync_iso_set *sync_iso;
|
|
||||||
isoal_sink_handle_t sink_handle;
|
|
||||||
struct lll_sync_iso *lll_iso;
|
|
||||||
struct ll_iso_datapath *dp;
|
|
||||||
uint16_t stream_handle;
|
uint16_t stream_handle;
|
||||||
uint32_t sdu_interval;
|
|
||||||
uint16_t iso_interval;
|
|
||||||
uint8_t burst_number;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (path_dir != BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
|
||||||
/* FIXME: workaround to succeed datapath setup for ISO
|
|
||||||
* broadcaster until Tx datapath is implemented, in the
|
|
||||||
* future.
|
|
||||||
*/
|
|
||||||
return BT_HCI_ERR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle < BT_CTLR_SYNC_ISO_STREAM_HANDLE_BASE) {
|
if (handle < BT_CTLR_SYNC_ISO_STREAM_HANDLE_BASE) {
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
|
@ -278,73 +216,148 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
||||||
if (stream->dp) {
|
if (stream->dp) {
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||||
|
|
||||||
/* Allocate and configure datapath */
|
/* Allocate and configure datapath */
|
||||||
dp = mem_acquire(&datapath_free);
|
struct ll_iso_datapath *dp = mem_acquire(&datapath_free);
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
dp->path_dir = path_dir;
|
dp->path_dir = path_dir;
|
||||||
dp->path_id = path_id;
|
dp->path_id = path_id;
|
||||||
dp->coding_format = coding_format;
|
dp->coding_format = coding_format;
|
||||||
dp->company_id = company_id;
|
dp->company_id = company_id;
|
||||||
|
|
||||||
/* TODO dp->sync_delay = controller_delay; ?*/
|
/* TODO dp->sync_delay = controller_delay; ?*/
|
||||||
|
|
||||||
|
flush_timeout = 0;
|
||||||
|
stream_sync_delay = 0;
|
||||||
|
group_sync_delay = 0;
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
|
role = cig->lll.role;
|
||||||
|
iso_interval = cig->iso_interval;
|
||||||
|
stream_sync_delay = cis->sync_delay;
|
||||||
|
group_sync_delay = cig->sync_delay;
|
||||||
|
|
||||||
|
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
||||||
|
burst_number = cis->lll.tx.burst_number;
|
||||||
|
flush_timeout = cis->lll.tx.flush_timeout;
|
||||||
|
|
||||||
|
if (role) {
|
||||||
|
/* peripheral */
|
||||||
|
sdu_interval = cig->p_sdu_interval;
|
||||||
|
} else {
|
||||||
|
/* central */
|
||||||
|
sdu_interval = cig->c_sdu_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
cis->hdr.datapath_in = dp;
|
||||||
|
} else {
|
||||||
|
burst_number = cis->lll.rx.burst_number;
|
||||||
|
flush_timeout = cis->lll.rx.flush_timeout;
|
||||||
|
|
||||||
|
if (role) {
|
||||||
|
/* peripheral */
|
||||||
|
sdu_interval = cig->c_sdu_interval;
|
||||||
|
} else {
|
||||||
|
/* central */
|
||||||
|
sdu_interval = cig->p_sdu_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
cis->hdr.datapath_out = dp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||||
|
struct ll_sync_iso_set *sync_iso;
|
||||||
|
struct lll_sync_iso *lll_iso;
|
||||||
|
|
||||||
sync_iso = ull_sync_iso_by_stream_get(stream_handle);
|
sync_iso = ull_sync_iso_by_stream_get(stream_handle);
|
||||||
lll_iso = &sync_iso->lll;
|
lll_iso = &sync_iso->lll;
|
||||||
|
|
||||||
|
role = 1; /* FIXME: Set role from LLL struct */
|
||||||
burst_number = lll_iso->bn;
|
burst_number = lll_iso->bn;
|
||||||
sdu_interval = lll_iso->sdu_interval;
|
sdu_interval = lll_iso->sdu_interval;
|
||||||
iso_interval = lll_iso->iso_interval;
|
iso_interval = lll_iso->iso_interval;
|
||||||
|
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||||
|
|
||||||
err = isoal_sink_create(&sink_handle, handle, burst_number,
|
if (path_id == BT_HCI_DATAPATH_ID_HCI) {
|
||||||
sdu_interval, iso_interval, sink_sdu_alloc_hci,
|
/* Not vendor specific, thus alloc and emit functions known */
|
||||||
sink_sdu_emit_hci, sink_sdu_write_hci);
|
err = isoal_sink_create(handle, role,
|
||||||
if (err) {
|
burst_number, flush_timeout,
|
||||||
mem_release(dp, &datapath_free);
|
sdu_interval, iso_interval,
|
||||||
|
stream_sync_delay, group_sync_delay,
|
||||||
|
sink_sdu_alloc_hci, sink_sdu_emit_hci,
|
||||||
|
sink_sdu_write_hci, &sink_handle);
|
||||||
|
} else {
|
||||||
|
/* Set up vendor specific data path */
|
||||||
|
isoal_sink_sdu_alloc_cb sdu_alloc;
|
||||||
|
isoal_sink_sdu_emit_cb sdu_emit;
|
||||||
|
isoal_sink_sdu_write_cb sdu_write;
|
||||||
|
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
/* Request vendor sink callbacks for path */
|
||||||
|
if (ll_data_path_sink_create(dp, &sdu_alloc, &sdu_emit, &sdu_write)) {
|
||||||
|
err = isoal_sink_create(handle, role,
|
||||||
|
burst_number, flush_timeout,
|
||||||
|
sdu_interval, iso_interval,
|
||||||
|
stream_sync_delay, group_sync_delay,
|
||||||
|
sdu_alloc, sdu_emit, sdu_write,
|
||||||
|
&sink_handle);
|
||||||
|
} else {
|
||||||
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dp->sink_hdl = sink_handle;
|
if (!err) {
|
||||||
stream->dp = dp;
|
dp->sink_hdl = sink_handle;
|
||||||
|
isoal_sink_enable(sink_handle);
|
||||||
isoal_sink_enable(sink_handle);
|
} else {
|
||||||
#endif
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ll_remove_iso_path(uint16_t handle, uint8_t path_dir)
|
uint8_t ll_remove_iso_path(uint16_t handle, uint8_t path_dir)
|
||||||
{
|
{
|
||||||
/* TBD: If the Host issues this command with a Connection_Handle that does not exist
|
struct ll_conn_iso_stream *cis = NULL;
|
||||||
|
struct ll_iso_stream_hdr *hdr = NULL;
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
|
if (IS_CIS_HANDLE(handle)) {
|
||||||
|
cis = ll_conn_iso_stream_get(handle);
|
||||||
|
hdr = &cis->hdr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If the Host issues this command with a Connection_Handle that does not exist
|
||||||
* or is not for a CIS or a BIS, the Controller shall return the error code Unknown
|
* or is not for a CIS or a BIS, the Controller shall return the error code Unknown
|
||||||
* Connection Identifier (0x02).
|
* Connection Identifier (0x02).
|
||||||
*/
|
*/
|
||||||
struct ll_iso_datapath *dp = NULL;
|
if (!cis) {
|
||||||
|
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
struct ll_iso_datapath *dp;
|
||||||
struct ll_conn_iso_stream *cis = ll_conn_iso_stream_get(handle);
|
|
||||||
|
|
||||||
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
||||||
dp = cis->datapath_in;
|
dp = hdr->datapath_in;
|
||||||
if (dp) {
|
if (dp) {
|
||||||
cis->datapath_in = NULL;
|
hdr->datapath_in = NULL;
|
||||||
mem_release(dp, &datapath_free);
|
mem_release(dp, &datapath_free);
|
||||||
}
|
}
|
||||||
} else if (path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
} else if (path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
||||||
dp = cis->datapath_out;
|
dp = hdr->datapath_out;
|
||||||
if (dp) {
|
if (dp) {
|
||||||
cis->datapath_out = NULL;
|
hdr->datapath_out = NULL;
|
||||||
mem_release(dp, &datapath_free);
|
mem_release(dp, &datapath_free);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Reserved for future use */
|
/* Reserved for future use */
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||||
struct lll_sync_iso_stream *stream;
|
struct lll_sync_iso_stream *stream;
|
||||||
|
@ -364,7 +377,6 @@ uint8_t ll_remove_iso_path(uint16_t handle, uint8_t path_dir)
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
/* Datapath was not previously set up */
|
/* Datapath was not previously set up */
|
||||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -4,6 +4,33 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* CIS */
|
||||||
|
#define LL_CIS_HANDLE_BASE CONFIG_BT_MAX_CONN
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_CONN_ISO_STREAMS)
|
||||||
|
#define LAST_VALID_CIS_HANDLE \
|
||||||
|
(CONFIG_BT_CTLR_CONN_ISO_STREAMS + LL_CIS_HANDLE_BASE - 1)
|
||||||
|
#else
|
||||||
|
#define LAST_VALID_CIS_HANDLE (LL_CIS_HANDLE_BASE - 1)
|
||||||
|
#endif /* CONFIG_BT_CTLR_CONN_ISO_STREAMS */
|
||||||
|
|
||||||
|
#define LL_CIS_IDX_FROM_HANDLE(_handle) \
|
||||||
|
((_handle) - LL_CIS_HANDLE_BASE)
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||||
|
#define IS_CIS_HANDLE(_handle) \
|
||||||
|
(((_handle) >= LL_CIS_HANDLE_BASE) && \
|
||||||
|
((_handle) <= LAST_VALID_CIS_HANDLE))
|
||||||
|
#else
|
||||||
|
#define IS_CIS_HANDLE(_handle) 0
|
||||||
|
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||||
|
|
||||||
|
/* Common memebers for ll_conn_iso_stream and ll_broadcast_iso_stream */
|
||||||
|
struct ll_iso_stream_hdr {
|
||||||
|
struct ll_iso_datapath *datapath_in;
|
||||||
|
struct ll_iso_datapath *datapath_out;
|
||||||
|
};
|
||||||
|
|
||||||
struct ll_iso_datapath {
|
struct ll_iso_datapath {
|
||||||
uint8_t path_dir;
|
uint8_t path_dir;
|
||||||
uint8_t path_id;
|
uint8_t path_id;
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
#include "lll_conn.h"
|
#include "lll_conn.h"
|
||||||
#include "lll_conn_iso.h"
|
#include "lll_conn_iso.h"
|
||||||
|
|
||||||
|
#include "isoal.h"
|
||||||
|
#include "ull_iso_types.h"
|
||||||
|
|
||||||
#include "ull_conn_types.h"
|
#include "ull_conn_types.h"
|
||||||
#include "ull_conn_iso_types.h"
|
#include "ull_conn_iso_types.h"
|
||||||
#include "ull_internal.h"
|
#include "ull_internal.h"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue