bluetooth: controller: Add ISO-AL TX unframed fragmentation
- Implemented ISO-AL TX interface functions for fragmentation of unframed PDUs - Implemented ISO-AL source construct and its creation for an input data path Signed-off-by: Nirosharn Amarasinghe <niag@demant.com>
This commit is contained in:
parent
c966a95718
commit
3cdaf72d9d
8 changed files with 914 additions and 36 deletions
|
@ -225,6 +225,24 @@ config BT_CTLR_ISO_TX_BUFFER_SIZE
|
|||
Size of the Isochronous Tx buffers and the value returned in HCI LE
|
||||
Read Buffer Size V2 command response.
|
||||
|
||||
config BT_CTLR_ISOAL_SOURCES
|
||||
int "Number of Isochronous Adaptation Layer sinks"
|
||||
depends on BT_CTLR_ADV_ISO || BT_CTLR_CONN_ISO
|
||||
range 1 64
|
||||
default 1
|
||||
help
|
||||
Set the number of concurrently active sources supported by the
|
||||
ISO AL.
|
||||
|
||||
config BT_CTLR_ISOAL_SINKS
|
||||
int "Number of Isochronous Adaptation Layer sinks"
|
||||
depends on BT_CTLR_SYNC_ISO || BT_CTLR_CONN_ISO
|
||||
range 1 64
|
||||
default 1
|
||||
help
|
||||
Set the number of concurrently active sinks supported by the
|
||||
ISO AL.
|
||||
|
||||
config BT_CTLR_ISO_VENDOR_DATA_PATH
|
||||
bool "Vendor-specific ISO data path"
|
||||
depends on BT_CTLR_SYNC_ISO || BT_CTLR_CONN_ISO
|
||||
|
|
|
@ -9,27 +9,36 @@
|
|||
#include <toolchain.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
#include <zephyr.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include "util/memq.h"
|
||||
#include "pdu.h"
|
||||
|
||||
|
||||
#include "ll.h"
|
||||
#include "lll.h"
|
||||
#include "lll_conn_iso.h"
|
||||
#include "lll_iso_tx.h"
|
||||
#include "isoal.h"
|
||||
#include "ull_iso_types.h"
|
||||
|
||||
#define LOG_MODULE_NAME bt_ctlr_isoal
|
||||
#include "common/log.h"
|
||||
#include "hal/debug.h"
|
||||
|
||||
/* TODO this must be taken from a Kconfig */
|
||||
#define ISOAL_SINKS_MAX (4)
|
||||
|
||||
/** Allocation state */
|
||||
typedef uint8_t isoal_alloc_state_t;
|
||||
#define ISOAL_ALLOC_STATE_FREE ((isoal_alloc_state_t) 0x00)
|
||||
#define ISOAL_ALLOC_STATE_TAKEN ((isoal_alloc_state_t) 0x01)
|
||||
|
||||
static struct
|
||||
struct
|
||||
{
|
||||
isoal_alloc_state_t sink_allocated[ISOAL_SINKS_MAX];
|
||||
struct isoal_sink sink_state[ISOAL_SINKS_MAX];
|
||||
isoal_alloc_state_t sink_allocated[CONFIG_BT_CTLR_ISOAL_SINKS];
|
||||
isoal_alloc_state_t source_allocated[CONFIG_BT_CTLR_ISOAL_SOURCES];
|
||||
struct isoal_sink sink_state[CONFIG_BT_CTLR_ISOAL_SINKS];
|
||||
struct isoal_source source_state[CONFIG_BT_CTLR_ISOAL_SOURCES];
|
||||
} isoal_global;
|
||||
|
||||
|
||||
|
@ -77,7 +86,7 @@ static isoal_status_t isoal_sink_allocate(isoal_sink_handle_t *hdl)
|
|||
isoal_sink_handle_t i;
|
||||
|
||||
/* Very small linear search to find first free */
|
||||
for (i = 0; i < ISOAL_SINKS_MAX; i++) {
|
||||
for (i = 0; i < CONFIG_BT_CTLR_ISOAL_SINKS; i++) {
|
||||
if (isoal_global.sink_allocated[i] == ISOAL_ALLOC_STATE_FREE) {
|
||||
isoal_global.sink_allocated[i] = ISOAL_ALLOC_STATE_TAKEN;
|
||||
*hdl = i;
|
||||
|
@ -147,7 +156,8 @@ isoal_status_t isoal_sink_create(
|
|||
*/
|
||||
|
||||
/* Note: sdu_interval unit is uS, iso_interval is a multiple of 1.25mS */
|
||||
session->pdus_per_sdu = burst_number * (sdu_interval / (iso_interval * 1250));
|
||||
session->pdus_per_sdu = (burst_number * sdu_interval) /
|
||||
((uint32_t)iso_interval * CONN_INT_UNIT_US);
|
||||
|
||||
/* Computation of transport latency (constant part)
|
||||
*
|
||||
|
@ -212,6 +222,111 @@ isoal_status_t isoal_sink_create(
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find free source from statically-sized pool and allocate it
|
||||
* @details Implemented as linear search since pool is very small
|
||||
*
|
||||
* @param hdl[out] Handle to source
|
||||
* @return ISOAL_STATUS_OK if we could allocate; otherwise ISOAL_STATUS_ERR_SOURCE_ALLOC
|
||||
*/
|
||||
static isoal_status_t isoal_source_allocate(isoal_source_handle_t *hdl)
|
||||
{
|
||||
isoal_source_handle_t i;
|
||||
|
||||
/* Very small linear search to find first free */
|
||||
for (i = 0; i < CONFIG_BT_CTLR_ISOAL_SOURCES; i++) {
|
||||
if (isoal_global.source_allocated[i] == ISOAL_ALLOC_STATE_FREE) {
|
||||
isoal_global.source_allocated[i] = ISOAL_ALLOC_STATE_TAKEN;
|
||||
*hdl = i;
|
||||
return ISOAL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ISOAL_STATUS_ERR_SOURCE_ALLOC; /* All entries were taken */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mark a source as being free to allocate again
|
||||
* @param hdl[in] Handle to source
|
||||
*/
|
||||
static void isoal_source_deallocate(isoal_source_handle_t hdl)
|
||||
{
|
||||
isoal_global.source_allocated[hdl] = ISOAL_ALLOC_STATE_FREE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new source
|
||||
*
|
||||
* @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 max_octets[in] Maximum PDU size (Max_PDU_C_To_P / Max_PDU_P_To_C)
|
||||
* @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 pdu_alloc[in] Callback of PDU allocator
|
||||
* @param pdu_write[in] Callback of PDU byte writer
|
||||
* @param pdu_emit[in] Callback of PDU emitter
|
||||
* @param pdu_release[in] Callback of PDU deallocator
|
||||
* @param hdl[out] Handle to new source
|
||||
*
|
||||
* @return ISOAL_STATUS_OK if we could create a new sink; otherwise ISOAL_STATUS_ERR_SOURCE_ALLOC
|
||||
*/
|
||||
isoal_status_t isoal_source_create(
|
||||
uint16_t handle,
|
||||
uint8_t role,
|
||||
uint8_t burst_number,
|
||||
uint8_t flush_timeout,
|
||||
uint8_t max_octets,
|
||||
uint32_t sdu_interval,
|
||||
uint16_t iso_interval,
|
||||
uint32_t stream_sync_delay,
|
||||
uint32_t group_sync_delay,
|
||||
isoal_source_pdu_alloc_cb pdu_alloc,
|
||||
isoal_source_pdu_write_cb pdu_write,
|
||||
isoal_source_pdu_emit_cb pdu_emit,
|
||||
isoal_source_pdu_release_cb pdu_release,
|
||||
isoal_source_handle_t *hdl)
|
||||
{
|
||||
isoal_status_t err;
|
||||
|
||||
/* Allocate a new source */
|
||||
err = isoal_source_allocate(hdl);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
struct isoal_source_session *session = &isoal_global.source_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 */
|
||||
session->pdus_per_sdu = (burst_number * sdu_interval) /
|
||||
((uint32_t)iso_interval * CONN_INT_UNIT_US);
|
||||
/* Set maximum PDU size */
|
||||
session->max_pdu_size = max_octets;
|
||||
|
||||
/* Remember the platform-specific callbacks */
|
||||
session->pdu_alloc = pdu_alloc;
|
||||
session->pdu_write = pdu_write;
|
||||
session->pdu_emit = pdu_emit;
|
||||
session->pdu_release = pdu_release;
|
||||
|
||||
/* TODO: Constant need to be updated */
|
||||
|
||||
/* Initialize running seq number to zero */
|
||||
session->seqn = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get reference to configuration struct
|
||||
*
|
||||
|
@ -262,6 +377,56 @@ void isoal_sink_destroy(isoal_sink_handle_t hdl)
|
|||
isoal_sink_deallocate(hdl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get reference to configuration struct
|
||||
*
|
||||
* @param hdl[in] Handle to new source
|
||||
* @return Reference to parameter struct, to be configured by caller
|
||||
*/
|
||||
struct isoal_source_config *isoal_get_source_param_ref(isoal_source_handle_t hdl)
|
||||
{
|
||||
LL_ASSERT(isoal_global.source_allocated[hdl] == ISOAL_ALLOC_STATE_TAKEN);
|
||||
|
||||
return &isoal_global.source_state[hdl].session.param;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically enable latch-in of packets and PDU production
|
||||
* @param hdl[in] Handle of existing instance
|
||||
*/
|
||||
void isoal_source_enable(isoal_source_handle_t hdl)
|
||||
{
|
||||
/* Reset bookkeeping state */
|
||||
memset(&isoal_global.source_state[hdl].pdu_production, 0,
|
||||
sizeof(isoal_global.source_state[hdl].pdu_production));
|
||||
|
||||
/* Atomically enable */
|
||||
isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically disable latch-in of packets and PDU production
|
||||
* @param hdl[in] Handle of existing instance
|
||||
*/
|
||||
void isoal_source_disable(isoal_source_handle_t hdl)
|
||||
{
|
||||
/* Atomically disable */
|
||||
isoal_global.source_state[hdl].pdu_production.mode = ISOAL_PRODUCTION_MODE_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable and deallocate existing source
|
||||
* @param hdl[in] Handle of existing instance
|
||||
*/
|
||||
void isoal_source_destroy(isoal_source_handle_t hdl)
|
||||
{
|
||||
/* Atomic disable */
|
||||
isoal_source_disable(hdl);
|
||||
|
||||
/* Permit allocation anew */
|
||||
isoal_source_deallocate(hdl);
|
||||
}
|
||||
|
||||
/* Obtain destination SDU */
|
||||
static isoal_status_t isoal_rx_allocate_sdu(struct isoal_sink *sink,
|
||||
const struct isoal_pdu_rx *pdu_meta)
|
||||
|
@ -812,3 +977,313 @@ isoal_status_t isoal_rx_pdu_recombine(isoal_sink_handle_t sink_hdl,
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue the PDU in production in the relevant LL transmit queue. If the
|
||||
* attmept to release the PDU fails, the buffer linked to the PDU will be released
|
||||
* and it will not be possible to retry the emit operation on the same PDU.
|
||||
* @param[in] source_ctx ISO-AL source reference for this CIS / BIS
|
||||
* @param[in] produced_pdu PDU in production
|
||||
* @param[in] pdu_ll_id LLID to be set indicating the type of fragment
|
||||
* @param[in] payload_size Length of the data written to the PDU
|
||||
* @return Error status of the operation
|
||||
*/
|
||||
static isoal_status_t isoal_tx_pdu_emit(const struct isoal_source *source_ctx,
|
||||
const struct isoal_pdu_produced *produced_pdu,
|
||||
const uint8_t pdu_ll_id,
|
||||
const isoal_pdu_len_t payload_size)
|
||||
{
|
||||
struct node_tx_iso *node_tx;
|
||||
isoal_status_t status;
|
||||
uint16_t handle;
|
||||
|
||||
/* Retrieve CIS / BIS handle */
|
||||
handle = bt_iso_handle(source_ctx->session.handle);
|
||||
|
||||
/* Retrieve Node handle */
|
||||
node_tx = produced_pdu->contents.handle;
|
||||
/* Set PDU LLID */
|
||||
produced_pdu->contents.pdu->ll_id = pdu_ll_id;
|
||||
/* Set PDU length */
|
||||
produced_pdu->contents.pdu->length = (uint8_t)payload_size;
|
||||
|
||||
/* Attempt to enqueue the node towards the LL */
|
||||
status = source_ctx->session.pdu_emit(node_tx, handle);
|
||||
|
||||
if (status != ISOAL_STATUS_OK) {
|
||||
/* If it fails, the node will be released and no further attempt
|
||||
* will be possible
|
||||
*/
|
||||
BT_ERR("Failed to enqueue node (%p)", node_tx);
|
||||
source_ctx->session.pdu_release(node_tx, handle, status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Allocates a new PDU only if the previous PDU was emitted */
|
||||
static isoal_status_t isoal_tx_allocate_pdu(struct isoal_source *source,
|
||||
const struct isoal_sdu_tx *tx_sdu)
|
||||
{
|
||||
struct isoal_source_session *session;
|
||||
struct isoal_pdu_production *pp;
|
||||
struct isoal_pdu_produced *pdu;
|
||||
isoal_status_t err;
|
||||
|
||||
err = ISOAL_STATUS_OK;
|
||||
session = &source->session;
|
||||
pp = &source->pdu_production;
|
||||
pdu = &pp->pdu;
|
||||
|
||||
/* Allocate a PDU if the previous was filled (thus sent) */
|
||||
const bool pdu_complete = (pp->pdu_available == 0);
|
||||
|
||||
if (pdu_complete) {
|
||||
/* Allocate new PDU buffer */
|
||||
err = session->pdu_alloc(
|
||||
&pdu->contents /* [out] Updated with pointer and size */
|
||||
);
|
||||
|
||||
if (err) {
|
||||
pdu->contents.handle = NULL;
|
||||
pdu->contents.pdu = NULL;
|
||||
pdu->contents.size = 0;
|
||||
}
|
||||
|
||||
/* Get maximum buffer available */
|
||||
const size_t available_len = MIN(
|
||||
session->max_pdu_size,
|
||||
pdu->contents.size
|
||||
);
|
||||
|
||||
/* Nothing has been written into buffer yet */
|
||||
pp->pdu_written = 0;
|
||||
pp->pdu_available = available_len;
|
||||
LL_ASSERT(available_len > 0);
|
||||
|
||||
pp->pdu_cnt++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to emit the PDU in production if it is complete.
|
||||
* @param[in] source ISO-AL source reference
|
||||
* @param[in] end_of_sdu SDU end has been reached
|
||||
* @param[in] pdu_ll_id LLID / PDU fragment type as Start, Cont, End, Single (Unframed) or Framed
|
||||
* @return Error status of operation
|
||||
*/
|
||||
static isoal_status_t isoal_tx_try_emit_pdu(struct isoal_source *source,
|
||||
bool end_of_sdu,
|
||||
uint8_t pdu_ll_id)
|
||||
{
|
||||
struct isoal_pdu_production *pp;
|
||||
struct isoal_pdu_produced *pdu;
|
||||
isoal_status_t err;
|
||||
|
||||
err = ISOAL_STATUS_OK;
|
||||
pp = &source->pdu_production;
|
||||
pdu = &pp->pdu;
|
||||
|
||||
/* Emit a PDU */
|
||||
const bool pdu_complete = (pp->pdu_available == 0) || end_of_sdu;
|
||||
|
||||
if (end_of_sdu) {
|
||||
pp->pdu_available = 0;
|
||||
}
|
||||
|
||||
if (pdu_complete) {
|
||||
err = isoal_tx_pdu_emit(source, pdu, pdu_ll_id, pp->pdu_written);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Fragment received SDU and produce unframed PDUs
|
||||
* @details Destination source may have an already partially built PDU
|
||||
*
|
||||
* @param source[in,out] Destination source with bookkeeping state
|
||||
* @param tx_sdu[in] SDU with packet boundary information
|
||||
*
|
||||
* @return Status
|
||||
*/
|
||||
static isoal_status_t isoal_tx_unframed_produce(struct isoal_source *source,
|
||||
const struct isoal_sdu_tx *tx_sdu)
|
||||
{
|
||||
struct isoal_source_session *session;
|
||||
isoal_sdu_len_t packet_available;
|
||||
struct isoal_pdu_production *pp;
|
||||
const uint8_t *sdu_payload;
|
||||
bool zero_length_sdu;
|
||||
isoal_status_t err;
|
||||
bool padding_pdu;
|
||||
uint8_t ll_id;
|
||||
|
||||
session = &source->session;
|
||||
pp = &source->pdu_production;
|
||||
padding_pdu = false;
|
||||
err = ISOAL_STATUS_OK;
|
||||
|
||||
packet_available = tx_sdu->size;
|
||||
sdu_payload = tx_sdu->dbuf;
|
||||
LL_ASSERT(sdu_payload);
|
||||
|
||||
zero_length_sdu = (packet_available == 0 &&
|
||||
tx_sdu->sdu_state == BT_ISO_SINGLE);
|
||||
|
||||
if (tx_sdu->sdu_state == BT_ISO_START ||
|
||||
tx_sdu->sdu_state == BT_ISO_SINGLE) {
|
||||
/* Start of a new SDU */
|
||||
|
||||
/* Update sequence number for received SDU
|
||||
*
|
||||
* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
|
||||
* 2 ISOAL Features :
|
||||
* SDUs received by the ISOAL from the upper layer shall be
|
||||
* given a sequence number which is initialized to 0 when the
|
||||
* CIS or BIS is created.
|
||||
*
|
||||
* NOTE: The upper layer may synchronize its sequence number
|
||||
* with the sequence number in the ISOAL once the Datapath is
|
||||
* configured and the link is established.
|
||||
*/
|
||||
session->seqn++;
|
||||
|
||||
/* Reset PDU fragmentation count for this SDU */
|
||||
pp->pdu_cnt = 0;
|
||||
}
|
||||
|
||||
/* PDUs should be created until the SDU fragment has been fragmented or
|
||||
* if this is the last fragment of the SDU, until the required padding
|
||||
* PDU(s) are sent.
|
||||
*/
|
||||
while ((err == ISOAL_STATUS_OK) &&
|
||||
((packet_available > 0) || padding_pdu || zero_length_sdu)) {
|
||||
const isoal_status_t err_alloc = isoal_tx_allocate_pdu(source, tx_sdu);
|
||||
struct isoal_pdu_produced *pdu = &pp->pdu;
|
||||
|
||||
err |= err_alloc;
|
||||
|
||||
/*
|
||||
* For this PDU we can only consume of packet, bounded by:
|
||||
* - What can fit in the destination PDU.
|
||||
* - What remains of the packet.
|
||||
*/
|
||||
const size_t consume_len = MIN(
|
||||
packet_available,
|
||||
pp->pdu_available
|
||||
);
|
||||
|
||||
if (consume_len > 0) {
|
||||
err |= session->pdu_write(&pdu->contents,
|
||||
pp->pdu_written,
|
||||
sdu_payload,
|
||||
consume_len);
|
||||
sdu_payload += consume_len;
|
||||
pp->pdu_written += consume_len;
|
||||
pp->pdu_available -= consume_len;
|
||||
packet_available -= consume_len;
|
||||
}
|
||||
|
||||
/* End of the SDU is reached at the end of the last SDU fragment
|
||||
* or if this is a single fragment SDU
|
||||
*/
|
||||
bool end_of_sdu = (packet_available == 0) &&
|
||||
((tx_sdu->sdu_state == BT_ISO_SINGLE) ||
|
||||
(tx_sdu->sdu_state == BT_ISO_END));
|
||||
|
||||
/* Decide PDU type
|
||||
*
|
||||
* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
|
||||
* 2.1 Unframed PDU :
|
||||
* LLID 0b00 PDU_BIS_LLID_COMPLETE_END:
|
||||
* (1) When the payload of the ISO Data PDU contains the end
|
||||
* fragment of an SDU.
|
||||
* (2) When the payload of the ISO Data PDU contains a complete
|
||||
* SDU.
|
||||
* (3) When an SDU contains zero length data, the corresponding
|
||||
* PDU shall be of zero length and the LLID field shall be
|
||||
* set to 0b00.
|
||||
*
|
||||
* LLID 0b01 PDU_BIS_LLID_COMPLETE_END:
|
||||
* (1) When the payload of the ISO Data PDU contains a start or
|
||||
* a continuation fragment of an SDU.
|
||||
* (2) When the ISO Data PDU is used as padding.
|
||||
*/
|
||||
ll_id = PDU_BIS_LLID_COMPLETE_END;
|
||||
if (!end_of_sdu || padding_pdu) {
|
||||
ll_id = PDU_BIS_LLID_START_CONTINUE;
|
||||
}
|
||||
|
||||
const isoal_status_t err_emit = isoal_tx_try_emit_pdu(source, end_of_sdu, ll_id);
|
||||
|
||||
err |= err_emit;
|
||||
|
||||
/* Send padding PDU(s) if required
|
||||
*
|
||||
* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
|
||||
* 2.1 Unframed PDU :
|
||||
* Each SDU shall generate BN ÷ (ISO_Interval ÷ SDU_Interval)
|
||||
* fragments. If an SDU generates less than this number of
|
||||
* fragments, empty payloads shall be used to make up the
|
||||
* number.
|
||||
*/
|
||||
padding_pdu = (end_of_sdu && (pp->pdu_cnt < session->pdus_per_sdu));
|
||||
zero_length_sdu = false;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deep copy a SDU, fragment into PDU(s)
|
||||
* @details Fragmentation will occur individually for every enabled source
|
||||
*
|
||||
* @param source_hdl[in] Handle of destination source
|
||||
* @param tx_sdu[in] SDU along with packet boudary state
|
||||
* @return Status
|
||||
*/
|
||||
isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl,
|
||||
const struct isoal_sdu_tx *tx_sdu)
|
||||
{
|
||||
struct isoal_source *source = &isoal_global.source_state[source_hdl];
|
||||
isoal_status_t err = ISOAL_STATUS_ERR_PDU_ALLOC;
|
||||
|
||||
if (source->pdu_production.mode != ISOAL_PRODUCTION_MODE_DISABLED) {
|
||||
/* TODO: consider how to separate framed and unframed production
|
||||
* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
|
||||
* 2 ISOAL Features :
|
||||
* (1) Unframed PDUs shall only be used when the ISO_Interval
|
||||
* is equal to or an integer multiple of the SDU_Interval
|
||||
* and a constant time offset alignment is maintained
|
||||
* between the SDU generation and the timing in the
|
||||
* isochronous transport.
|
||||
* (2) When the Host requests the use of framed PDUs, the
|
||||
* Controller shall use framed PDUs.
|
||||
*/
|
||||
bool pdu_framed = false;
|
||||
|
||||
if (pdu_framed) {
|
||||
/* TODO: add framed handling */
|
||||
} else {
|
||||
err = isoal_tx_unframed_produce(source, tx_sdu);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void isoal_tx_pdu_release(isoal_source_handle_t source_hdl,
|
||||
struct node_tx_iso *node_tx)
|
||||
{
|
||||
struct isoal_source *source = &isoal_global.source_state[source_hdl];
|
||||
|
||||
if (source && source->session.pdu_release) {
|
||||
source->session.pdu_release(node_tx, source->session.handle,
|
||||
ISOAL_STATUS_OK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,18 @@ 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 ISOAL_STATUS_ERR_PDU_ALLOC ((isoal_status_t) 0x10) /* PDU allocation */
|
||||
#define ISOAL_STATUS_ERR_PDU_EMIT ((isoal_status_t) 0x20) /* PDU emission */
|
||||
#define ISOAL_STATUS_ERR_UNSPECIFIED ((isoal_status_t) 0x80) /* 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;
|
||||
|
||||
/** Handle to a registered ISO Sub-System source */
|
||||
typedef uint8_t isoal_source_handle_t;
|
||||
|
||||
/** Byte length of an ISO SDU */
|
||||
typedef uint16_t isoal_sdu_len_t;
|
||||
|
||||
|
@ -74,6 +79,11 @@ struct isoal_rx_origin {
|
|||
} inst;
|
||||
};
|
||||
|
||||
enum isoal_mode {
|
||||
ISOAL_MODE_CIS,
|
||||
ISOAL_MODE_BIS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ISO frame SDU buffer - typically an Audio frame buffer
|
||||
*
|
||||
|
@ -93,6 +103,23 @@ struct isoal_sdu_buffer {
|
|||
isoal_sdu_len_t size;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief ISO frame PDU buffer
|
||||
*/
|
||||
struct isoal_pdu_buffer {
|
||||
/** Additional handle for the provided pdu */
|
||||
void *handle;
|
||||
/** PDU contents */
|
||||
struct pdu_iso *pdu;
|
||||
/** Maximum size of the data buffer allocated for the payload in the PDU.
|
||||
* Should be at least Max_PDU_C_To_P / Max_PDU_P_To_C depending on
|
||||
* the role.
|
||||
*/
|
||||
isoal_pdu_len_t size;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Produced ISO SDU frame with associated meta data */
|
||||
struct isoal_sdu_produced {
|
||||
/** Status of contents, if valid or SDU was lost.
|
||||
|
@ -117,6 +144,13 @@ struct isoal_sdu_produced {
|
|||
};
|
||||
|
||||
|
||||
/** @brief Produced ISO PDU encapsulation */
|
||||
struct isoal_pdu_produced {
|
||||
/** Contents information provided at PDU allocation */
|
||||
struct isoal_pdu_buffer contents;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Received ISO PDU with associated meta data */
|
||||
struct isoal_pdu_rx {
|
||||
/** Meta */
|
||||
|
@ -125,9 +159,25 @@ struct isoal_pdu_rx {
|
|||
struct pdu_iso *pdu;
|
||||
};
|
||||
|
||||
/** @brief Received ISO SDU with associated meta data */
|
||||
struct isoal_sdu_tx {
|
||||
/** Code word buffer
|
||||
* Type, location and alignment decided by ISO sub system
|
||||
*/
|
||||
void *dbuf;
|
||||
/** Number of bytes accessible behind the dbuf pointer */
|
||||
isoal_sdu_len_t size;
|
||||
/** SDU packet boundary flags from HCI indicating the fragment type
|
||||
* can be directly assigned to the SDU state
|
||||
*/
|
||||
uint8_t sdu_state;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
struct isoal_sink;
|
||||
struct node_tx_iso;
|
||||
|
||||
/**
|
||||
* @brief Callback: Request memory for a new ISO SDU buffer
|
||||
|
@ -174,10 +224,7 @@ typedef isoal_status_t (*isoal_sink_sdu_write_cb)(
|
|||
|
||||
|
||||
struct isoal_sink_config {
|
||||
enum {
|
||||
ISOAL_MODE_CIS,
|
||||
ISOAL_MODE_BIS
|
||||
} mode;
|
||||
enum isoal_mode mode;
|
||||
/* TODO add SDU and PDU max length etc. */
|
||||
};
|
||||
|
||||
|
@ -220,6 +267,104 @@ struct isoal_sink {
|
|||
struct isoal_sdu_production sdu_production;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct isoal_source;
|
||||
|
||||
/**
|
||||
* @brief Callback: Request memory for a new ISO PDU buffer
|
||||
*
|
||||
* Proprietary ISO sub systems may have
|
||||
* specific requirements or opinions on where to locate ISO PDUs; some
|
||||
* memories may be faster, may be dynamically mapped in, etc.
|
||||
*
|
||||
* @return ISOAL_STATUS_ERR_ALLOC if size_request could not be fulfilled, otherwise
|
||||
* ISOAL_STATUS_OK.
|
||||
*/
|
||||
typedef isoal_status_t (*isoal_source_pdu_alloc_cb)(
|
||||
/*!< [out] Struct is modified. Must not be NULL */
|
||||
struct isoal_pdu_buffer *pdu_buffer
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback: Release an ISO PDU
|
||||
*/
|
||||
typedef isoal_status_t (*isoal_source_pdu_release_cb)(
|
||||
/*!< [in] PDU to be released */
|
||||
struct node_tx_iso *node_tx,
|
||||
/*!< [in] CIS/BIS handle */
|
||||
const uint16_t handle,
|
||||
/*!< [in] Cause of release. ISOAL_STATUS_OK means PDU was enqueued towards
|
||||
* LLL and sent or flushed, and may need further handling before
|
||||
* memory is released, e.g. forwarded to HCI thread for complete-
|
||||
* counting. Any other status indicates cause of error during SDU
|
||||
* fragmentation/segmentation, in which case the PDU will not be
|
||||
* produced.
|
||||
*/
|
||||
const isoal_status_t status
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback: Write a number of bytes to PDU buffer
|
||||
*/
|
||||
typedef isoal_status_t (*isoal_source_pdu_write_cb)(
|
||||
/*!< [out] PDU under production */
|
||||
struct isoal_pdu_buffer *pdu_buffer,
|
||||
/*!< [in] Offset within PDU buffer */
|
||||
const size_t offset,
|
||||
/*!< [in] Source data */
|
||||
const uint8_t *sdu_payload,
|
||||
/*!< [in] Number of bytes to be copied */
|
||||
const size_t consume_len
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback: Enqueue an ISO PDU
|
||||
*/
|
||||
typedef isoal_status_t (*isoal_source_pdu_emit_cb)(
|
||||
/*!< [in] PDU to be enqueued */
|
||||
struct node_tx_iso *node_tx,
|
||||
/*!< [in] CIS/BIS handle */
|
||||
const uint16_t handle
|
||||
);
|
||||
|
||||
struct isoal_source_config {
|
||||
enum isoal_mode mode;
|
||||
/* TODO add SDU and PDU max length etc. */
|
||||
};
|
||||
|
||||
struct isoal_source_session {
|
||||
isoal_source_pdu_alloc_cb pdu_alloc;
|
||||
isoal_source_pdu_write_cb pdu_write;
|
||||
isoal_source_pdu_emit_cb pdu_emit;
|
||||
isoal_source_pdu_release_cb pdu_release;
|
||||
|
||||
struct isoal_source_config param;
|
||||
isoal_sdu_cnt_t seqn;
|
||||
uint16_t handle;
|
||||
uint8_t pdus_per_sdu;
|
||||
uint8_t max_pdu_size;
|
||||
uint32_t latency_unframed;
|
||||
uint32_t latency_framed;
|
||||
};
|
||||
|
||||
struct isoal_pdu_production {
|
||||
/* Permit atomic enable/disable of PDU production */
|
||||
volatile isoal_production_mode_t mode;
|
||||
/* We are constructing an PDU from {<1 or =1 or >1} SDUs */
|
||||
struct isoal_pdu_produced pdu;
|
||||
/* PDUs produced for current SDU */
|
||||
uint8_t pdu_cnt;
|
||||
isoal_pdu_len_t pdu_written;
|
||||
isoal_pdu_len_t pdu_available;
|
||||
};
|
||||
|
||||
struct isoal_source {
|
||||
/* Session-constant */
|
||||
struct isoal_source_session session;
|
||||
|
||||
/* State for PDU production */
|
||||
struct isoal_pdu_production pdu_production;
|
||||
};
|
||||
|
||||
isoal_status_t isoal_init(void);
|
||||
|
||||
|
@ -258,3 +403,32 @@ isoal_status_t sink_sdu_emit_hci(const struct isoal_sink *sink_ctx,
|
|||
isoal_status_t sink_sdu_write_hci(void *dbuf,
|
||||
const uint8_t *pdu_payload,
|
||||
const size_t consume_len);
|
||||
|
||||
isoal_status_t isoal_source_create(uint16_t handle,
|
||||
uint8_t role,
|
||||
uint8_t burst_number,
|
||||
uint8_t flush_timeout,
|
||||
uint8_t max_octets,
|
||||
uint32_t sdu_interval,
|
||||
uint16_t iso_interval,
|
||||
uint32_t stream_sync_delay,
|
||||
uint32_t group_sync_delay,
|
||||
isoal_source_pdu_alloc_cb pdu_alloc,
|
||||
isoal_source_pdu_write_cb pdu_write,
|
||||
isoal_source_pdu_emit_cb pdu_emit,
|
||||
isoal_source_pdu_release_cb pdu_release,
|
||||
isoal_source_handle_t *hdl);
|
||||
|
||||
struct isoal_source_config *isoal_get_source_param_ref(isoal_source_handle_t hdl);
|
||||
|
||||
void isoal_source_enable(isoal_source_handle_t hdl);
|
||||
|
||||
void isoal_source_disable(isoal_source_handle_t hdl);
|
||||
|
||||
void isoal_source_destroy(isoal_source_handle_t hdl);
|
||||
|
||||
isoal_status_t isoal_tx_sdu_fragment(isoal_source_handle_t source_hdl,
|
||||
const struct isoal_sdu_tx *tx_sdu);
|
||||
|
||||
void isoal_tx_pdu_release(isoal_source_handle_t source_hdl,
|
||||
struct node_tx_iso *node_tx);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "ull_conn_internal.h"
|
||||
#include "ull_sync_iso_internal.h"
|
||||
#include "ull_conn_iso_internal.h"
|
||||
#include "ull_conn_types.h"
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
|
||||
#define LOG_MODULE_NAME bt_ctlr_ull_iso
|
||||
|
@ -62,6 +63,19 @@
|
|||
|
||||
static int init_reset(void);
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
static isoal_status_t ll_iso_pdu_alloc(struct isoal_pdu_buffer *pdu_buffer);
|
||||
static isoal_status_t ll_iso_pdu_write(struct isoal_pdu_buffer *pdu_buffer,
|
||||
const size_t offset,
|
||||
const uint8_t *sdu_payload,
|
||||
const size_t consume_len);
|
||||
static isoal_status_t ll_iso_pdu_emit(struct node_tx_iso *node_tx,
|
||||
const uint16_t handle);
|
||||
static isoal_status_t ll_iso_pdu_release(struct node_tx_iso *node_tx,
|
||||
const uint16_t handle,
|
||||
const isoal_status_t status);
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
/* Allocate data path pools for RX/TX directions for each stream */
|
||||
#define BT_CTLR_ISO_STREAMS ((2 * (BT_CTLR_CONN_ISO_STREAMS)) + \
|
||||
BT_CTLR_SYNC_ISO_STREAMS)
|
||||
|
@ -162,6 +176,22 @@ __weak bool ll_data_path_sink_create(struct ll_iso_datapath *datapath,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Could be implemented by vendor */
|
||||
__weak bool ll_data_path_source_create(struct ll_iso_datapath *datapath,
|
||||
isoal_source_pdu_alloc_cb *pdu_alloc,
|
||||
isoal_source_pdu_write_cb *pdu_write,
|
||||
isoal_source_pdu_emit_cb *pdu_emit,
|
||||
isoal_source_pdu_release_cb *pdu_release)
|
||||
{
|
||||
ARG_UNUSED(datapath);
|
||||
ARG_UNUSED(pdu_alloc);
|
||||
ARG_UNUSED(pdu_write);
|
||||
ARG_UNUSED(pdu_emit);
|
||||
ARG_UNUSED(pdu_release);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool path_is_vendor_specific(uint8_t path_id)
|
||||
{
|
||||
return (path_id >= BT_HCI_DATAPATH_ID_VS &&
|
||||
|
@ -181,6 +211,7 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||
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
|
||||
|
@ -188,8 +219,13 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
*/
|
||||
return BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
isoal_source_handle_t source_handle;
|
||||
uint8_t max_octets;
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
isoal_sink_handle_t sink_handle;
|
||||
uint32_t stream_sync_delay;
|
||||
uint32_t group_sync_delay;
|
||||
|
@ -281,22 +317,11 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
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;
|
||||
if (path_dir == BT_HCI_DATAPATH_DIR_CTLR_TO_HOST) {
|
||||
/* Create sink for RX data path */
|
||||
burst_number = cis->lll.rx.burst_number;
|
||||
flush_timeout = cis->lll.rx.flush_timeout;
|
||||
max_octets = cis->lll.rx.max_octets;
|
||||
|
||||
if (role) {
|
||||
/* peripheral */
|
||||
|
@ -307,8 +332,92 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
}
|
||||
|
||||
cis->hdr.datapath_out = dp;
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
dp->sink_hdl = sink_handle;
|
||||
isoal_sink_enable(sink_handle);
|
||||
} else {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
} else {
|
||||
/* path_dir == BT_HCI_DATAPATH_DIR_HOST_TO_CTLR */
|
||||
burst_number = cis->lll.tx.burst_number;
|
||||
flush_timeout = cis->lll.tx.flush_timeout;
|
||||
max_octets = cis->lll.tx.max_octets;
|
||||
|
||||
if (role) {
|
||||
/* peripheral */
|
||||
sdu_interval = cig->p_sdu_interval;
|
||||
} else {
|
||||
/* central */
|
||||
sdu_interval = cig->c_sdu_interval;
|
||||
}
|
||||
|
||||
cis->hdr.datapath_in = dp;
|
||||
|
||||
/* Create source for TX data path */
|
||||
isoal_source_pdu_alloc_cb pdu_alloc;
|
||||
isoal_source_pdu_write_cb pdu_write;
|
||||
isoal_source_pdu_emit_cb pdu_emit;
|
||||
isoal_source_pdu_release_cb pdu_release;
|
||||
|
||||
/* Set default callbacks assuming not vendor specific
|
||||
* or that the vendor specific path is the same.
|
||||
*/
|
||||
pdu_alloc = ll_iso_pdu_alloc;
|
||||
pdu_write = ll_iso_pdu_write;
|
||||
pdu_emit = ll_iso_pdu_emit;
|
||||
pdu_release = ll_iso_pdu_release;
|
||||
|
||||
if (path_is_vendor_specific(path_id)) {
|
||||
if (!ll_data_path_source_create(dp, &pdu_alloc, &pdu_write,
|
||||
&pdu_emit, &pdu_release)) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
err = isoal_source_create(handle, role,
|
||||
burst_number, flush_timeout, max_octets,
|
||||
sdu_interval, iso_interval,
|
||||
stream_sync_delay, group_sync_delay,
|
||||
pdu_alloc, pdu_write, pdu_emit,
|
||||
pdu_release, &source_handle);
|
||||
|
||||
if (!err) {
|
||||
dp->source_hdl = source_handle;
|
||||
isoal_source_enable(source_handle);
|
||||
} else {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||
struct ll_sync_iso_set *sync_iso;
|
||||
|
@ -321,7 +430,6 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
burst_number = lll_iso->bn;
|
||||
sdu_interval = lll_iso->sdu_interval;
|
||||
iso_interval = lll_iso->iso_interval;
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||
|
||||
if (path_id == BT_HCI_DATAPATH_ID_HCI) {
|
||||
/* Not vendor specific, thus alloc and emit functions known */
|
||||
|
@ -353,9 +461,7 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
}
|
||||
|
||||
if (!err) {
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
|
||||
stream->dp = dp;
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||
|
||||
dp->sink_hdl = sink_handle;
|
||||
isoal_sink_enable(sink_handle);
|
||||
|
@ -364,6 +470,7 @@ uint8_t ll_setup_iso_path(uint16_t handle, uint8_t path_dir, uint8_t path_id,
|
|||
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
|
||||
#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
return 0;
|
||||
|
@ -804,6 +911,94 @@ void ull_iso_datapath_release(struct ll_iso_datapath *dp)
|
|||
mem_release(dp, &datapath_free);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
/**
|
||||
* Allocate a PDU from the LL and store the details in the given buffer. Allocation
|
||||
* is not expected to fail as there must always be sufficient PDU buffers. Any
|
||||
* failure will trigger the assert.
|
||||
* @param[in] pdu_buffer Buffer to store PDU details in
|
||||
* @return Error status of operation
|
||||
*/
|
||||
static isoal_status_t ll_iso_pdu_alloc(struct isoal_pdu_buffer *pdu_buffer)
|
||||
{
|
||||
ARG_UNUSED(pdu_buffer);
|
||||
|
||||
/* TODO: Function will be populated along with the data-path
|
||||
* implementation
|
||||
*/
|
||||
|
||||
return ISOAL_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given SDU payload to the target PDU buffer at the given offset.
|
||||
* @param[in,out] pdu_buffer Target PDU buffer
|
||||
* @param[in] pdu_offset Offset / current write position within PDU
|
||||
* @param[in] sdu_payload Location of source data
|
||||
* @param[in] consume_len Length of data to copy
|
||||
* @return Error status of write operation
|
||||
*/
|
||||
static isoal_status_t ll_iso_pdu_write(struct isoal_pdu_buffer *pdu_buffer,
|
||||
const size_t pdu_offset,
|
||||
const uint8_t *sdu_payload,
|
||||
const size_t consume_len)
|
||||
{
|
||||
ARG_UNUSED(pdu_offset);
|
||||
ARG_UNUSED(consume_len);
|
||||
|
||||
LL_ASSERT(pdu_buffer);
|
||||
LL_ASSERT(pdu_buffer->pdu);
|
||||
LL_ASSERT(sdu_payload);
|
||||
|
||||
/* TODO: Function will be populated along with the data-path
|
||||
* implementation
|
||||
*/
|
||||
|
||||
return ISOAL_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the encoded node to the transmission queue
|
||||
* @param node_tx TX node to enqueue
|
||||
* @param handle CIS/BIS handle
|
||||
* @return Error status of enqueue operation
|
||||
*/
|
||||
static isoal_status_t ll_iso_pdu_emit(struct node_tx_iso *node_tx,
|
||||
const uint16_t handle)
|
||||
{
|
||||
ARG_UNUSED(node_tx);
|
||||
ARG_UNUSED(handle);
|
||||
|
||||
/* TODO: Function will be populated along with the data-path
|
||||
* implementation
|
||||
*/
|
||||
|
||||
return ISOAL_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the given payload back to the memory pool.
|
||||
* @param node_tx TX node to release or forward
|
||||
* @param handle CIS/BIS handle
|
||||
* @param status Reason for release
|
||||
* @return Error status of release operation
|
||||
*/
|
||||
static isoal_status_t ll_iso_pdu_release(struct node_tx_iso *node_tx,
|
||||
const uint16_t handle,
|
||||
const isoal_status_t status)
|
||||
{
|
||||
ARG_UNUSED(node_tx);
|
||||
ARG_UNUSED(handle);
|
||||
ARG_UNUSED(status);
|
||||
|
||||
/* TODO: Function will be populated along with the data-path
|
||||
* implementation
|
||||
*/
|
||||
|
||||
return ISOAL_STATUS_OK;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_CONN_ISO */
|
||||
|
||||
static int init_reset(void)
|
||||
{
|
||||
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
|
||||
|
|
|
@ -37,4 +37,5 @@ struct ll_iso_datapath {
|
|||
uint8_t coding_format;
|
||||
uint16_t company_id;
|
||||
isoal_sink_handle_t sink_hdl;
|
||||
isoal_source_handle_t source_hdl;
|
||||
};
|
||||
|
|
12
tests/bluetooth/ctrl_isoal/Kconfig
Normal file
12
tests/bluetooth/ctrl_isoal/Kconfig
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Bluetooth Controller configuration options for ISO-AL Unit Tests
|
||||
|
||||
# Copyright (c) 2022 Oticon A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config BT_CTLR_ISOAL_SINKS
|
||||
int "Number of Isochronous Adaptation Layer sinks (for unittest)"
|
||||
|
||||
config BT_CTLR_ISOAL_SOURCES
|
||||
int "Number of Isochronous Adaptation Layer sinks (for unittest)"
|
||||
|
||||
source "Kconfig.zephyr"
|
|
@ -6,3 +6,6 @@ CONFIG_ZTEST_MOCKING=y
|
|||
CONFIG_ZTEST_PARAMETER_COUNT=32
|
||||
|
||||
CONFIG_ZTEST_ASSERT_HOOK=y
|
||||
|
||||
CONFIG_BT_CTLR_ISOAL_SINKS=4
|
||||
CONFIG_BT_CTLR_ISOAL_SOURCES=4
|
||||
|
|
|
@ -515,7 +515,7 @@ void test_unframed_long_pdu_short_sdu(void)
|
|||
*/
|
||||
void test_sink_create_destroy(void)
|
||||
{
|
||||
isoal_sink_handle_t hdl[ISOAL_SINKS_MAX];
|
||||
isoal_sink_handle_t hdl[CONFIG_BT_CTLR_ISOAL_SINKS];
|
||||
struct isoal_sink_config *config_ptr;
|
||||
|
||||
err = isoal_init();
|
||||
|
@ -526,7 +526,7 @@ void test_sink_create_destroy(void)
|
|||
|
||||
uint8_t dummy_role = BT_CONN_ROLE_CENTRAL;
|
||||
|
||||
for (int i = 0; i < ISOAL_SINKS_MAX; i++) {
|
||||
for (int i = 0; i < CONFIG_BT_CTLR_ISOAL_SINKS; i++) {
|
||||
/* Create a sink based on global parameters */
|
||||
err = isoal_sink_create(handle, dummy_role,
|
||||
burst_number, flush_timeout,
|
||||
|
@ -545,7 +545,7 @@ void test_sink_create_destroy(void)
|
|||
dummy_role = (dummy_role + 1) % (BT_ROLE_BROADCAST + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ISOAL_SINKS_MAX; i++) {
|
||||
for (int i = 0; i < CONFIG_BT_CTLR_ISOAL_SINKS; i++) {
|
||||
/* Destroy sink */
|
||||
isoal_sink_destroy(hdl[i]);
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ void test_sink_create_err(void)
|
|||
|
||||
isoal_sink_handle_t hdl;
|
||||
|
||||
for (int i = 0; i < ISOAL_SINKS_MAX; i++) {
|
||||
for (int i = 0; i < CONFIG_BT_CTLR_ISOAL_SINKS; i++) {
|
||||
/* Create a sink based on global parameters */
|
||||
err = isoal_sink_create(handle, role,
|
||||
burst_number, flush_timeout,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue