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 "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "lll_conn_iso.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_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(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) {
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
len = sink_ctx->sdu_production.sdu_written + BT_HCI_ISO_TS_DATA_HDR_SIZE;
|
||||
|
||||
hdr->handle = sys_cpu_to_le16(handle_packed);
|
||||
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_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
|
||||
#if defined(CONFIG_BT_CTLR_ISO)
|
||||
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)
|
||||
struct ll_conn_iso_stream *cis =
|
||||
ll_conn_iso_stream_get(node_rx->hdr.handle);
|
||||
struct ll_iso_datapath *dp = cis->datapath_out;
|
||||
if (IS_CIS_HANDLE(handle)) {
|
||||
struct ll_conn_iso_stream *cis =
|
||||
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;
|
||||
|
||||
if (dp->path_id == BT_HCI_DATAPATH_ID_HCI) {
|
||||
/* If HCI datapath pass to ISO AL here */
|
||||
struct isoal_pdu_rx pckt_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 */
|
||||
|
|
|
@ -51,9 +51,6 @@ isoal_status_t isoal_init(void)
|
|||
isoal_status_t err = ISOAL_STATUS_OK;
|
||||
|
||||
err = isoal_init_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -64,9 +61,6 @@ isoal_status_t isoal_reset(void)
|
|||
isoal_status_t err = ISOAL_STATUS_OK;
|
||||
|
||||
err = isoal_init_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -106,24 +100,34 @@ static void isoal_sink_deallocate(isoal_sink_handle_t hdl)
|
|||
/**
|
||||
* @brief Create a new sink
|
||||
*
|
||||
* @param hdl[out] Handle to new sink
|
||||
* @param handle[in] Connection handle
|
||||
* @param burst_number[in] Burst Number
|
||||
* @param sdu_interval[in] SDU interval
|
||||
* @param iso_interval[in] ISO interval
|
||||
* @param sdu_alloc[in] Callback of SDU allocator
|
||||
* @param sdu_emit[in] Callback of SDU emitter
|
||||
* @param handle[in] Connection handle
|
||||
* @param role[in] Peripheral, Central or Broadcast
|
||||
* @param burst_number[in] Burst Number
|
||||
* @param flush_timeout[in] Flush timeout
|
||||
* @param sdu_interval[in] SDU interval
|
||||
* @param iso_interval[in] ISO interval
|
||||
* @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
|
||||
*/
|
||||
isoal_status_t isoal_sink_create(
|
||||
isoal_sink_handle_t *hdl,
|
||||
uint16_t handle,
|
||||
uint8_t role,
|
||||
uint8_t burst_number,
|
||||
uint8_t flush_timeout,
|
||||
uint32_t sdu_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_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;
|
||||
|
||||
|
@ -133,17 +137,77 @@ isoal_status_t isoal_sink_create(
|
|||
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 */
|
||||
isoal_global.sink_state[*hdl].session.pdus_per_sdu =
|
||||
burst_number * (sdu_interval / (iso_interval * 1250));
|
||||
session->pdus_per_sdu = 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 */
|
||||
isoal_global.sink_state[*hdl].session.sdu_alloc = sdu_alloc;
|
||||
isoal_global.sink_state[*hdl].session.sdu_emit = sdu_emit;
|
||||
isoal_global.sink_state[*hdl].session.sdu_write = sdu_write;
|
||||
session->sdu_alloc = sdu_alloc;
|
||||
session->sdu_emit = sdu_emit;
|
||||
session->sdu_write = sdu_write;
|
||||
|
||||
/* Initialize running seq number to zero */
|
||||
isoal_global.sink_state[*hdl].session.seqn = 0;
|
||||
session->seqn = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -198,42 +262,41 @@ void isoal_sink_destroy(isoal_sink_handle_t 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 */
|
||||
static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
|
||||
const struct isoal_pdu_rx *pdu_meta)
|
||||
{
|
||||
isoal_status_t err = ISOAL_STATUS_OK;
|
||||
struct isoal_sdu_produced *sdu = &sink->sdu_production.sdu;
|
||||
struct isoal_sink_session *session;
|
||||
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) */
|
||||
const bool sdu_complete = (sink->sdu_production.sdu_available == 0);
|
||||
const bool sdu_complete = (sp->sdu_available == 0);
|
||||
|
||||
if (sdu_complete) {
|
||||
/* Allocate new clean SDU buffer */
|
||||
err = sink->session.sdu_alloc(
|
||||
err = session->sdu_alloc(
|
||||
sink,
|
||||
pdu_meta, /* [in] PDU origin may determine buffer */
|
||||
&sdu->contents /* [out] Updated with pointer and size */
|
||||
);
|
||||
|
||||
/* Nothing has been written into buffer yet */
|
||||
sink->sdu_production.sdu_written = 0;
|
||||
sink->sdu_production.sdu_available = sdu->contents.size;
|
||||
sp->sdu_written = 0;
|
||||
sp->sdu_available = sdu->contents.size;
|
||||
LL_ASSERT(sdu->contents.size > 0);
|
||||
|
||||
/* Remember meta data */
|
||||
sdu->status = pdu_meta->meta->status;
|
||||
sdu->timestamp = pdu_meta->meta->timestamp;
|
||||
/* Get seq number from session counter, and increase SDU counter */
|
||||
sdu->seqn = sink->session.seqn++;
|
||||
/* Get seq number from session counter */
|
||||
sdu->seqn = session->seqn;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
isoal_status_t err = ISOAL_STATUS_OK;
|
||||
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;
|
||||
sp = &sink->sdu_production;
|
||||
sdu = &sp->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) {
|
||||
sink->sdu_production.sdu_available = 0;
|
||||
sp->sdu_available = 0;
|
||||
}
|
||||
|
||||
if (sdu_complete) {
|
||||
uint8_t next_state = BT_ISO_START;
|
||||
|
||||
switch (sink->sdu_production.sdu_state) {
|
||||
switch (sp->sdu_state) {
|
||||
case BT_ISO_START:
|
||||
if (end_of_sdu) {
|
||||
sink->sdu_production.sdu_state = BT_ISO_SINGLE;
|
||||
sp->sdu_state = BT_ISO_SINGLE;
|
||||
next_state = BT_ISO_START;
|
||||
} else {
|
||||
sink->sdu_production.sdu_state = BT_ISO_START;
|
||||
sp->sdu_state = BT_ISO_START;
|
||||
next_state = BT_ISO_CONT;
|
||||
}
|
||||
break;
|
||||
case BT_ISO_CONT:
|
||||
if (end_of_sdu) {
|
||||
sink->sdu_production.sdu_state = BT_ISO_END;
|
||||
sp->sdu_state = BT_ISO_END;
|
||||
next_state = BT_ISO_START;
|
||||
} else {
|
||||
sink->sdu_production.sdu_state = BT_ISO_CONT;
|
||||
sp->sdu_state = BT_ISO_CONT;
|
||||
next_state = BT_ISO_CONT;
|
||||
}
|
||||
break;
|
||||
case BT_ISO_END:
|
||||
case BT_ISO_SINGLE:
|
||||
default:
|
||||
LL_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
sdu->status = sink->sdu_production.sdu_status;
|
||||
err = sink->session.sdu_emit(sink, sdu);
|
||||
sdu->status = sp->sdu_status;
|
||||
struct isoal_sink_session *session = &sink->session;
|
||||
|
||||
err = session->sdu_emit(sink, sdu);
|
||||
|
||||
/* update 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,
|
||||
const struct isoal_pdu_rx *pdu_meta,
|
||||
uint8_t offset,
|
||||
uint8_t length,
|
||||
bool is_end_fragment)
|
||||
{
|
||||
const uint8_t *pdu_payload = pdu_meta->pdu->cis.payload;
|
||||
isoal_pdu_len_t packet_available = pdu_meta->pdu->cis.length;
|
||||
isoal_status_t err = ISOAL_STATUS_OK;
|
||||
isoal_pdu_len_t packet_available;
|
||||
const uint8_t *pdu_payload;
|
||||
bool handle_error_case;
|
||||
isoal_status_t err;
|
||||
|
||||
/* Might get an empty packed due to errors, we will need to terminate
|
||||
* and send something up anyhow
|
||||
*/
|
||||
packet_available = length;
|
||||
handle_error_case = (is_end_fragment && (packet_available == 0));
|
||||
|
||||
pdu_payload = pdu_meta->pdu->payload + offset;
|
||||
LL_ASSERT(pdu_payload);
|
||||
|
||||
/* While there is something left of the packet to consume */
|
||||
err = ISOAL_STATUS_OK;
|
||||
while ((packet_available > 0) || handle_error_case) {
|
||||
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;
|
||||
|
||||
|
@ -319,21 +390,21 @@ static isoal_status_t isoal_rx_append_to_sdu(struct isoal_sink *sink,
|
|||
*/
|
||||
const size_t consume_len = MIN(
|
||||
packet_available,
|
||||
sink->sdu_production.sdu_available
|
||||
sp->sdu_available
|
||||
);
|
||||
|
||||
LL_ASSERT(sdu->contents.dbuf);
|
||||
|
||||
if (consume_len > 0) {
|
||||
if (pdu_meta->meta->status == ISOAL_PDU_STATUS_VALID) {
|
||||
err |= sink->session.sdu_write(sdu->contents.dbuf,
|
||||
pdu_payload,
|
||||
consume_len);
|
||||
struct isoal_sink_session *session = &sink->session;
|
||||
|
||||
err |= session->sdu_write(sdu->contents.dbuf,
|
||||
pdu_payload,
|
||||
consume_len);
|
||||
}
|
||||
pdu_payload += consume_len;
|
||||
sink->sdu_production.sdu_written += consume_len;
|
||||
sink->sdu_production.sdu_available -= consume_len;
|
||||
packet_available -= consume_len;
|
||||
sp->sdu_written += consume_len;
|
||||
sp->sdu_available -= consume_len;
|
||||
packet_available -= consume_len;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
LL_ASSERT(packet_available == 0);
|
||||
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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
||||
const struct isoal_pdu_rx *pdu_meta)
|
||||
static isoal_status_t isoal_rx_unframed_consume(struct isoal_sink *sink,
|
||||
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;
|
||||
uint8_t next_state, llid;
|
||||
bool pdu_err, pdu_padding, last_pdu, end_of_packet, seq_err;
|
||||
bool pdu_padding;
|
||||
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;
|
||||
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);
|
||||
seq_err = (pdu_meta->meta->payload_number != sink->sdu_production.prev_pdu_id+1);
|
||||
pdu_padding = (pdu_meta->pdu->cis.length == 0) && (llid == PDU_BIS_LLID_START_CONTINUE);
|
||||
pdu_padding = (pdu_meta->pdu->length == 0) && (llid == PDU_BIS_LLID_START_CONTINUE);
|
||||
|
||||
if (sink->sdu_production.fsm == ISOAL_START) {
|
||||
sink->sdu_production.sdu_status = ISOAL_SDU_STATUS_VALID;
|
||||
sink->sdu_production.sdu_state = BT_ISO_START;
|
||||
sink->sdu_production.pdu_cnt = 1;
|
||||
} else {
|
||||
sink->sdu_production.pdu_cnt++;
|
||||
if (sp->fsm == ISOAL_START) {
|
||||
struct isoal_sdu_produced *sdu;
|
||||
uint32_t anchorpoint;
|
||||
uint32_t latency;
|
||||
|
||||
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;
|
||||
|
||||
switch (sink->sdu_production.fsm) {
|
||||
|
||||
switch (sp->fsm) {
|
||||
case ISOAL_START:
|
||||
case ISOAL_CONTINUE:
|
||||
if (pdu_err || seq_err) {
|
||||
/* 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) {
|
||||
/* PDU contains a continuation (neither start of end) fragment of SDU */
|
||||
if (last_pdu) {
|
||||
|
@ -409,7 +510,7 @@ static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
|||
}
|
||||
} else {
|
||||
/* Unsupported case */
|
||||
LL_ASSERT(0);
|
||||
err = ISOAL_STATUS_ERR_UNSPECIFIED;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -429,23 +530,221 @@ static isoal_status_t isoal_rx_packet_consume(struct isoal_sink *sink,
|
|||
|
||||
/* Update error state */
|
||||
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) &&
|
||||
(sink->sdu_production.fsm != ISOAL_ERR_SPOOL)) {
|
||||
(sp->fsm != ISOAL_ERR_SPOOL)) {
|
||||
/* END fragment never seen */
|
||||
sink->sdu_production.sdu_status |= ISOAL_SDU_STATUS_ERRORS;
|
||||
sp->sdu_status |= ISOAL_SDU_STATUS_ERRORS;
|
||||
} 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 */
|
||||
if (!pdu_padding && !pdu_err) {
|
||||
err |= isoal_rx_append_to_sdu(sink, pdu_meta, end_of_packet);
|
||||
if (!pdu_padding) {
|
||||
err |= isoal_rx_append_to_sdu(sink, pdu_meta, 0,
|
||||
pdu_meta->pdu->length,
|
||||
end_of_packet);
|
||||
}
|
||||
|
||||
/* Update next stat */
|
||||
sink->sdu_production.fsm = next_state;
|
||||
sink->sdu_production.prev_pdu_id = pdu_meta->meta->payload_number;
|
||||
/* Update next state */
|
||||
sp->fsm = next_state;
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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_SDU_ALLOC ((isoal_status_t) 0x04) /* SDU allocation */
|
||||
#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 */
|
||||
typedef uint8_t isoal_sink_handle_t;
|
||||
|
@ -108,19 +111,13 @@ struct isoal_sdu_produced {
|
|||
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 */
|
||||
struct isoal_pdu_rx {
|
||||
/** Meta */
|
||||
struct node_rx_iso_meta *meta;
|
||||
/** 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. */
|
||||
};
|
||||
|
||||
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 {
|
||||
/* Session-constant */
|
||||
struct {
|
||||
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;
|
||||
struct isoal_sink_session session;
|
||||
|
||||
/* State for SDU production */
|
||||
struct {
|
||||
/* 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;
|
||||
struct isoal_sdu_production sdu_production;
|
||||
};
|
||||
|
||||
|
||||
|
@ -218,14 +220,18 @@ isoal_status_t isoal_init(void);
|
|||
|
||||
isoal_status_t isoal_reset(void);
|
||||
|
||||
isoal_status_t isoal_sink_create(isoal_sink_handle_t *hdl,
|
||||
uint16_t handle,
|
||||
isoal_status_t isoal_sink_create(uint16_t handle,
|
||||
uint8_t role,
|
||||
uint8_t burst_number,
|
||||
uint8_t flush_timeout,
|
||||
uint32_t sdu_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_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);
|
||||
|
||||
|
|
|
@ -842,6 +842,52 @@ struct pdu_data {
|
|||
} __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 {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t ll_id:2;
|
||||
|
|
|
@ -61,10 +61,10 @@
|
|||
#include "ull_sync_internal.h"
|
||||
#include "ull_sync_iso_internal.h"
|
||||
#include "ull_central_internal.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "ull_conn_internal.h"
|
||||
#include "lll_conn_iso.h"
|
||||
#include "ull_conn_iso_types.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "ull_central_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_PERIODIC */
|
||||
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
#if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
case NODE_RX_TYPE_TERMINATE:
|
||||
{
|
||||
if (IS_ACL_HANDLE(rx_free->handle)) {
|
||||
|
@ -1509,7 +1509,7 @@ void ll_rx_mem_release(void **node_rx)
|
|||
}
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
#endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
case NODE_RX_TYPE_EVENT_DONE:
|
||||
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 ll_conn_iso_stream *cis =
|
||||
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;
|
||||
|
||||
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 = {
|
||||
.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 */
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "ull_tx_queue.h"
|
||||
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
|
||||
|
||||
#include "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "ull_conn_types.h"
|
||||
#include "ull_conn_iso_types.h"
|
||||
#include "ull_internal.h"
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "lll.h"
|
||||
#include "lll_conn.h"
|
||||
#include "ull_conn_types.h"
|
||||
#include "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
#include "lll_conn_iso.h"
|
||||
#include "ull_conn_iso_types.h"
|
||||
#include "ull_conn_internal.h"
|
||||
|
@ -30,7 +32,6 @@
|
|||
#include "common/log.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 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -8,19 +8,14 @@ struct ll_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_iso_stream_hdr hdr;
|
||||
struct ll_conn_iso_group *group;
|
||||
struct lll_conn_iso_stream lll;
|
||||
uint32_t sync_delay;
|
||||
uint8_t cis_id;
|
||||
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 */
|
||||
ll_iso_stream_released_cb_t released_cb; /* CIS release callback */
|
||||
uint8_t established : 1; /* 0 if CIS has not yet been established.
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "ull_sync_types.h"
|
||||
#include "ull_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_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);
|
||||
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path_dir > BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* shall return the error code Unknown Connection Identifier (0x02)
|
||||
*/
|
||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
struct ll_conn_iso_stream *cis;
|
||||
struct ll_conn_iso_group *cig;
|
||||
isoal_sink_handle_t sink_hdl;
|
||||
isoal_status_t err = 0;
|
||||
if (!cis) {
|
||||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
cis = ll_conn_iso_stream_get(handle);
|
||||
/* TODO: Check valid cis */
|
||||
|
||||
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)) {
|
||||
if ((path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR && dp_in) ||
|
||||
(path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST && dp_out)) {
|
||||
/* Data path has been set up, can only do setup once */
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (path_is_vendor_specific(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) {
|
||||
return BT_HCI_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
#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
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||
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;
|
||||
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) {
|
||||
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) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
/* Allocate and configure datapath */
|
||||
dp = mem_acquire(&datapath_free);
|
||||
struct ll_iso_datapath *dp = mem_acquire(&datapath_free);
|
||||
if (!dp) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
dp->path_dir = path_dir;
|
||||
dp->path_id = path_id;
|
||||
dp->path_dir = path_dir;
|
||||
dp->path_id = path_id;
|
||||
dp->coding_format = coding_format;
|
||||
dp->company_id = company_id;
|
||||
dp->company_id = company_id;
|
||||
|
||||
/* 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);
|
||||
lll_iso = &sync_iso->lll;
|
||||
|
||||
role = 1; /* FIXME: Set role from LLL struct */
|
||||
burst_number = lll_iso->bn;
|
||||
sdu_interval = lll_iso->sdu_interval;
|
||||
iso_interval = lll_iso->iso_interval;
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||
|
||||
err = isoal_sink_create(&sink_handle, handle, burst_number,
|
||||
sdu_interval, iso_interval, sink_sdu_alloc_hci,
|
||||
sink_sdu_emit_hci, sink_sdu_write_hci);
|
||||
if (err) {
|
||||
mem_release(dp, &datapath_free);
|
||||
if (path_id == BT_HCI_DATAPATH_ID_HCI) {
|
||||
/* Not vendor specific, thus alloc and emit functions known */
|
||||
err = isoal_sink_create(handle, role,
|
||||
burst_number, flush_timeout,
|
||||
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;
|
||||
stream->dp = dp;
|
||||
|
||||
isoal_sink_enable(sink_handle);
|
||||
#endif
|
||||
if (!err) {
|
||||
dp->sink_hdl = sink_handle;
|
||||
isoal_sink_enable(sink_handle);
|
||||
} else {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
* 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_conn_iso_stream *cis = ll_conn_iso_stream_get(handle);
|
||||
struct ll_iso_datapath *dp;
|
||||
|
||||
if (path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR) {
|
||||
dp = cis->datapath_in;
|
||||
dp = hdr->datapath_in;
|
||||
if (dp) {
|
||||
cis->datapath_in = NULL;
|
||||
hdr->datapath_in = NULL;
|
||||
mem_release(dp, &datapath_free);
|
||||
}
|
||||
} else if (path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
||||
dp = cis->datapath_out;
|
||||
dp = hdr->datapath_out;
|
||||
if (dp) {
|
||||
cis->datapath_out = NULL;
|
||||
hdr->datapath_out = NULL;
|
||||
mem_release(dp, &datapath_free);
|
||||
}
|
||||
} else {
|
||||
/* Reserved for future use */
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||
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) {
|
||||
/* Datapath was not previously set up */
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -4,6 +4,33 @@
|
|||
* 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 {
|
||||
uint8_t path_dir;
|
||||
uint8_t path_id;
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include "lll_conn.h"
|
||||
#include "lll_conn_iso.h"
|
||||
|
||||
#include "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
|
||||
#include "ull_conn_types.h"
|
||||
#include "ull_conn_iso_types.h"
|
||||
#include "ull_internal.h"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue