zephyr/subsys/bluetooth/controller/ll_sw/ull_sync_iso.c
Vinayak Kariappa Chettimada b5d089fb48 Bluetooth: Controller: Receive starting at selected BIS and skip others
Updated implementation to save power consumption by
scheduling BIG event starting at the first BIS selected for
synchronization. Also, skip reception of any unselected
BISes in between multiple BISes that the Broadcaster is
transmitting. Added advance calculation of next subevent
channel index.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
2022-12-12 18:43:47 +01:00

815 lines
21 KiB
C

/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/sys/byteorder.h>
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "util/dbuf.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/ticker.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_vendor.h"
#include "lll_clock.h"
#include "lll_scan.h"
#include "lll/lll_df_types.h"
#include "lll_sync.h"
#include "lll_sync_iso.h"
#include "isoal.h"
#include "ull_scan_types.h"
#include "ull_sync_types.h"
#include "ull_iso_types.h"
#include "ull_internal.h"
#include "ull_scan_internal.h"
#include "ull_sync_internal.h"
#include "ull_iso_internal.h"
#include "ull_sync_iso_internal.h"
#include "ll.h"
#include <zephyr/bluetooth/hci.h>
#include "hal/debug.h"
static int init_reset(void);
static struct ll_sync_iso_set *sync_iso_get(uint8_t handle);
static uint8_t sync_iso_handle_get(struct ll_sync_iso_set *sync);
static struct stream *sync_iso_stream_acquire(void);
static uint16_t sync_iso_stream_handle_get(struct lll_sync_iso_stream *stream);
static void timeout_cleanup(struct ll_sync_iso_set *sync_iso);
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param);
static void ticker_start_op_cb(uint32_t status, void *param);
static void ticker_update_op_cb(uint32_t status, void *param);
static void ticker_stop_op_cb(uint32_t status, void *param);
static void sync_iso_disable(void *param);
static void disabled_cb(void *param);
static memq_link_t link_lll_prepare;
static struct mayfly mfy_lll_prepare = {0U, 0U, &link_lll_prepare, NULL, NULL};
static struct ll_sync_iso_set ll_sync_iso[CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET];
static struct lll_sync_iso_stream
stream_pool[CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT];
static void *stream_free;
uint8_t ll_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
uint8_t encryption, uint8_t *bcode, uint8_t mse,
uint16_t sync_timeout, uint8_t num_bis,
uint8_t *bis)
{
struct ll_sync_iso_set *sync_iso;
memq_link_t *link_sync_estab;
memq_link_t *link_sync_lost;
struct node_rx_hdr *node_rx;
struct ll_sync_set *sync;
struct lll_sync_iso *lll;
sync = ull_sync_is_enabled_get(sync_handle);
if (!sync || sync->iso.sync_iso) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
sync_iso = sync_iso_get(big_handle);
if (sync_iso->sync) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
/* TODO: Check parameters */
/* Check if free BISes available */
if (mem_free_count_get(stream_free) < num_bis) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
link_sync_estab = ll_rx_link_alloc();
if (!link_sync_estab) {
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
link_sync_lost = ll_rx_link_alloc();
if (!link_sync_lost) {
ll_rx_link_release(link_sync_estab);
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
node_rx = ll_rx_alloc();
if (!node_rx) {
ll_rx_link_release(link_sync_lost);
ll_rx_link_release(link_sync_estab);
return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
}
/* Initialize the ISO sync ULL context */
sync_iso->sync = sync;
sync_iso->timeout = sync_timeout;
sync_iso->timeout_reload = 0U;
sync_iso->timeout_expire = 0U;
/* Setup the periodic sync to establish ISO sync */
node_rx->link = link_sync_estab;
sync->iso.node_rx_estab = node_rx;
sync_iso->node_rx_lost.hdr.link = link_sync_lost;
/* Initialize sync LLL context */
lll = &sync_iso->lll;
lll->latency_prepare = 0U;
lll->latency_event = 0U;
lll->window_widening_prepare_us = 0U;
lll->window_widening_event_us = 0U;
lll->ctrl = 0U;
lll->cssn_curr = 0U;
lll->cssn_next = 0U;
lll->term_reason = 0U;
/* TODO: Implement usage of MSE to limit listening to subevents */
/* Allocate streams */
lll->stream_count = num_bis;
for (uint8_t i = 0U; i < num_bis; i++) {
struct lll_sync_iso_stream *stream;
stream = (void *)sync_iso_stream_acquire();
stream->big_handle = big_handle;
stream->bis_index = bis[i];
stream->dp = NULL;
lll->stream_handle[i] = sync_iso_stream_handle_get(stream);
}
/* Initialize ULL and LLL headers */
ull_hdr_init(&sync_iso->ull);
lll_hdr_init(lll, sync_iso);
/* Enable periodic advertising to establish ISO sync */
sync->iso.sync_iso = sync_iso;
return BT_HCI_ERR_SUCCESS;
}
uint8_t ll_big_sync_terminate(uint8_t big_handle, void **rx)
{
struct ll_sync_iso_set *sync_iso;
memq_link_t *link_sync_estab;
struct node_rx_pdu *node_rx;
memq_link_t *link_sync_lost;
struct ll_sync_set *sync;
int err;
sync_iso = sync_iso_get(big_handle);
if (!sync_iso) {
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
}
sync = sync_iso->sync;
if (sync && sync->iso.sync_iso) {
struct node_rx_sync_iso *se;
if (sync->iso.sync_iso != sync_iso) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
sync->iso.sync_iso = NULL;
node_rx = (void *)sync->iso.node_rx_estab;
link_sync_estab = node_rx->hdr.link;
link_sync_lost = sync_iso->node_rx_lost.hdr.link;
ll_rx_link_release(link_sync_lost);
ll_rx_link_release(link_sync_estab);
ll_rx_release(node_rx);
node_rx = (void *)&sync_iso->node_rx_lost;
node_rx->hdr.type = NODE_RX_TYPE_SYNC_ISO;
node_rx->hdr.handle = 0xffff;
/* NOTE: Since NODE_RX_TYPE_SYNC_ISO is only generated from ULL
* context, pass ULL context as parameter.
*/
node_rx->hdr.rx_ftr.param = sync_iso;
/* NOTE: struct node_rx_lost has uint8_t member following the
* struct node_rx_hdr to store the reason.
*/
se = (void *)node_rx->pdu;
se->status = BT_HCI_ERR_OP_CANCELLED_BY_HOST;
*rx = node_rx;
return BT_HCI_ERR_SUCCESS;
}
err = ull_ticker_stop_with_mark((TICKER_ID_SCAN_SYNC_ISO_BASE +
big_handle), sync_iso, &sync_iso->lll);
LL_ASSERT(err == 0 || err == -EALREADY);
if (err) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
ull_sync_iso_stream_release(sync_iso);
link_sync_lost = sync_iso->node_rx_lost.hdr.link;
ll_rx_link_release(link_sync_lost);
return BT_HCI_ERR_SUCCESS;
}
int ull_sync_iso_init(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
int ull_sync_iso_reset(void)
{
int err;
err = init_reset();
if (err) {
return err;
}
return 0;
}
uint8_t ull_sync_iso_lll_handle_get(struct lll_sync_iso *lll)
{
return sync_iso_handle_get(HDR_LLL2ULL(lll));
}
struct ll_sync_iso_set *ull_sync_iso_by_stream_get(uint16_t handle)
{
if (handle >= CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT) {
return NULL;
}
return sync_iso_get(stream_pool[handle].big_handle);
}
struct lll_sync_iso_stream *ull_sync_iso_stream_get(uint16_t handle)
{
struct ll_sync_iso_set *sync_iso;
/* Get the BIG Sync context and check for not being terminated */
sync_iso = ull_sync_iso_by_stream_get(handle);
if (!sync_iso || !sync_iso->sync) {
return NULL;
}
return &stream_pool[handle];
}
struct lll_sync_iso_stream *ull_sync_iso_lll_stream_get(uint16_t handle)
{
return ull_sync_iso_stream_get(handle);
}
void ull_sync_iso_stream_release(struct ll_sync_iso_set *sync_iso)
{
struct lll_sync_iso *lll;
lll = &sync_iso->lll;
while (lll->stream_count--) {
struct lll_sync_iso_stream *stream;
struct ll_iso_datapath *dp;
uint16_t handle;
handle = lll->stream_handle[lll->stream_count];
stream = ull_sync_iso_stream_get(handle);
LL_ASSERT(stream);
dp = stream->dp;
if (dp) {
stream->dp = NULL;
isoal_sink_destroy(dp->sink_hdl);
ull_iso_datapath_release(dp);
}
mem_release(stream, &stream_free);
}
sync_iso->sync = NULL;
}
void ull_sync_iso_setup(struct ll_sync_iso_set *sync_iso,
struct node_rx_hdr *node_rx,
uint8_t *acad, uint8_t acad_len)
{
struct lll_sync_iso_stream *stream;
uint32_t ticks_slot_overhead;
uint32_t sync_iso_offset_us;
uint32_t ticks_slot_offset;
struct lll_sync_iso *lll;
struct node_rx_ftr *ftr;
struct pdu_big_info *bi;
uint32_t ready_delay_us;
uint32_t interval_us;
struct pdu_adv *pdu;
uint8_t bi_size;
uint8_t handle;
uint32_t ret;
uint8_t sca;
if (acad_len < (PDU_BIG_INFO_CLEARTEXT_SIZE +
PDU_ADV_DATA_HEADER_SIZE)) {
return;
}
/* FIXME: Parse and find the BIGInfo */
bi_size = acad[PDU_ADV_DATA_HEADER_LEN_OFFSET];
bi = (void *)&acad[PDU_ADV_DATA_HEADER_DATA_OFFSET];
lll = &sync_iso->lll;
(void)memcpy(lll->seed_access_addr, &bi->seed_access_addr,
sizeof(lll->seed_access_addr));
(void)memcpy(lll->base_crc_init, &bi->base_crc_init,
sizeof(lll->base_crc_init));
(void)memcpy(lll->data_chan_map, bi->chm_phy,
sizeof(lll->data_chan_map));
lll->data_chan_map[4] &= 0x1F;
lll->data_chan_count = util_ones_count_get(lll->data_chan_map,
sizeof(lll->data_chan_map));
if (lll->data_chan_count < CHM_USED_COUNT_MIN) {
return;
}
/* Reset ISO create BIG flag in the periodic advertising context */
sync_iso->sync->iso.sync_iso = NULL;
lll->phy = BIT(bi->chm_phy[4] >> 5);
lll->num_bis = bi->num_bis;
lll->bn = bi->bn;
lll->nse = bi->nse;
lll->sub_interval = sys_le24_to_cpu(bi->sub_interval);
lll->max_pdu = bi->max_pdu;
lll->pto = bi->pto;
if (lll->pto) {
lll->ptc = lll->bn;
} else {
lll->ptc = 0U;
}
lll->bis_spacing = sys_le24_to_cpu(bi->spacing);
lll->irc = bi->irc;
lll->sdu_interval = sys_le24_to_cpu(bi->sdu_interval);
lll->payload_count = (uint64_t)bi->payload_count_framing[0];
lll->payload_count |= (uint64_t)bi->payload_count_framing[1] << 8;
lll->payload_count |= (uint64_t)bi->payload_count_framing[2] << 16;
lll->payload_count |= (uint64_t)bi->payload_count_framing[3] << 24;
lll->payload_count |= (uint64_t)bi->payload_count_framing[4] << 32;
/* Initialize payload pointers */
lll->payload_count_max = PDU_BIG_PAYLOAD_COUNT_MAX;
lll->payload_head = 0U;
lll->payload_tail = 0U;
for (int i = 0; i < CONFIG_BT_CTLR_SYNC_ISO_STREAM_MAX; i++) {
for (int j = 0; j < PDU_BIG_PAYLOAD_COUNT_MAX; j++) {
lll->payload[i][j] = NULL;
}
}
lll->iso_interval = sys_le16_to_cpu(bi->iso_interval);
interval_us = lll->iso_interval * PERIODIC_INT_UNIT_US;
sync_iso->timeout_reload =
RADIO_SYNC_EVENTS((sync_iso->timeout * 10U * USEC_PER_MSEC),
interval_us);
sca = sync_iso->sync->lll.sca;
lll->window_widening_periodic_us =
ceiling_fraction(((lll_clock_ppm_local_get() +
lll_clock_ppm_get(sca)) *
interval_us), USEC_PER_SEC);
lll->window_widening_max_us = (interval_us >> 1) - EVENT_IFS_US;
if (bi->offs_units) {
lll->window_size_event_us = OFFS_UNIT_300_US;
} else {
lll->window_size_event_us = OFFS_UNIT_30_US;
}
ftr = &node_rx->rx_ftr;
pdu = (void *)((struct node_rx_pdu *)node_rx)->pdu;
ready_delay_us = lll_radio_rx_ready_delay_get(lll->phy, PHY_FLAGS_S8);
/* Calculate the BIG Offset in microseconds */
sync_iso_offset_us = ftr->radio_end_us;
sync_iso_offset_us += (uint32_t)sys_le16_to_cpu(bi->offs) *
lll->window_size_event_us;
/* Skip to first selected BIS subevent */
/* FIXME: add support for interleaved packing */
stream = ull_sync_iso_stream_get(lll->stream_handle[0]);
sync_iso_offset_us += (stream->bis_index - 1U) * lll->sub_interval *
((lll->irc * lll->bn) + lll->ptc);
sync_iso_offset_us -= PDU_AC_US(pdu->len, sync_iso->sync->lll.phy,
ftr->phy_flags);
sync_iso_offset_us -= EVENT_TICKER_RES_MARGIN_US;
sync_iso_offset_us -= EVENT_JITTER_US;
sync_iso_offset_us -= ready_delay_us;
interval_us -= lll->window_widening_periodic_us;
/* TODO: active_to_start feature port */
sync_iso->ull.ticks_active_to_start = 0U;
sync_iso->ull.ticks_prepare_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
sync_iso->ull.ticks_preempt_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US);
sync_iso->ull.ticks_slot = HAL_TICKER_US_TO_TICKS(
EVENT_OVERHEAD_START_US + ready_delay_us +
PDU_BIS_MAX_US(PDU_AC_EXT_PAYLOAD_SIZE_MAX, lll->enc,
lll->phy) +
EVENT_OVERHEAD_END_US);
ticks_slot_offset = MAX(sync_iso->ull.ticks_active_to_start,
sync_iso->ull.ticks_prepare_to_start);
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = ticks_slot_offset;
} else {
ticks_slot_overhead = 0U;
}
ticks_slot_offset += HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US);
/* setup to use ISO create prepare function until sync established */
mfy_lll_prepare.fp = lll_sync_iso_create_prepare;
handle = sync_iso_handle_get(sync_iso);
ret = ticker_start(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_SYNC_ISO_BASE + handle),
ftr->ticks_anchor - ticks_slot_offset,
HAL_TICKER_US_TO_TICKS(sync_iso_offset_us),
HAL_TICKER_US_TO_TICKS(interval_us),
HAL_TICKER_REMAINDER(interval_us),
#if !defined(CONFIG_BT_TICKER_LOW_LAT) && \
!defined(CONFIG_BT_CTLR_LOW_LAT)
TICKER_LAZY_MUST_EXPIRE,
#else
TICKER_NULL_LAZY,
#endif /* !CONFIG_BT_TICKER_LOW_LAT && !CONFIG_BT_CTLR_LOW_LAT */
(sync_iso->ull.ticks_slot + ticks_slot_overhead),
ticker_cb, sync_iso,
ticker_start_op_cb, (void *)__LINE__);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
void ull_sync_iso_estab_done(struct node_rx_event_done *done)
{
struct ll_sync_iso_set *sync_iso;
struct node_rx_sync_iso *se;
struct lll_sync_iso *lll;
struct node_rx_pdu *rx;
/* switch to normal prepare */
mfy_lll_prepare.fp = lll_sync_iso_prepare;
/* Get reference to ULL context */
sync_iso = CONTAINER_OF(done->param, struct ll_sync_iso_set, ull);
lll = &sync_iso->lll;
/* Prepare BIG Sync Established */
rx = (void *)sync_iso->sync->iso.node_rx_estab;
rx->hdr.type = NODE_RX_TYPE_SYNC_ISO;
rx->hdr.handle = sync_iso_handle_get(sync_iso);
rx->hdr.rx_ftr.param = sync_iso;
se = (void *)rx->pdu;
se->status = BT_HCI_ERR_SUCCESS;
ll_rx_put_sched(rx->hdr.link, rx);
ull_sync_iso_done(done);
}
void ull_sync_iso_done(struct node_rx_event_done *done)
{
struct ll_sync_iso_set *sync_iso;
uint32_t ticks_drift_minus;
uint32_t ticks_drift_plus;
struct lll_sync_iso *lll;
uint16_t elapsed_event;
uint16_t latency_event;
uint16_t lazy;
uint8_t force;
/* Get reference to ULL context */
sync_iso = CONTAINER_OF(done->param, struct ll_sync_iso_set, ull);
lll = &sync_iso->lll;
/* Events elapsed used in timeout checks below */
latency_event = lll->latency_event;
if (lll->latency_prepare) {
elapsed_event = latency_event + lll->latency_prepare;
} else {
elapsed_event = latency_event + 1U;
}
/* Sync drift compensation and new skip calculation
*/
ticks_drift_plus = 0U;
ticks_drift_minus = 0U;
if (done->extra.trx_cnt) {
/* Calculate drift in ticks unit */
ull_drift_ticks_get(done, &ticks_drift_plus,
&ticks_drift_minus);
/* Reset latency */
lll->latency_event = 0U;
}
/* Reset supervision countdown */
if (done->extra.crc_valid) {
sync_iso->timeout_expire = 0U;
} else {
/* if anchor point not sync-ed, start timeout countdown */
if (!sync_iso->timeout_expire) {
sync_iso->timeout_expire = sync_iso->timeout_reload;
}
}
/* check timeout */
force = 0U;
if (sync_iso->timeout_expire) {
if (sync_iso->timeout_expire > elapsed_event) {
sync_iso->timeout_expire -= elapsed_event;
/* break skip */
lll->latency_event = 0U;
if (latency_event) {
force = 1U;
}
} else {
timeout_cleanup(sync_iso);
return;
}
}
/* check if skip needs update */
lazy = 0U;
if (force || (latency_event != lll->latency_event)) {
lazy = lll->latency_event + 1U;
}
/* Update Sync ticker instance */
if (ticks_drift_plus || ticks_drift_minus || lazy || force) {
uint8_t handle = sync_iso_handle_get(sync_iso);
uint32_t ticker_status;
/* Call to ticker_update can fail under the race
* condition where in the periodic sync role is being stopped
* but at the same time it is preempted by periodic sync event
* that gets into close state. Accept failure when periodic sync
* role is being stopped.
*/
ticker_status = ticker_update(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_SYNC_ISO_BASE +
handle),
ticks_drift_plus,
ticks_drift_minus, 0U, 0U,
lazy, force,
ticker_update_op_cb,
sync_iso);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY) ||
((void *)sync_iso == ull_disable_mark_get()));
}
}
void ull_sync_iso_done_terminate(struct node_rx_event_done *done)
{
struct ll_sync_iso_set *sync_iso;
struct lll_sync_iso *lll;
struct node_rx_pdu *rx;
uint8_t handle;
uint32_t ret;
/* Get reference to ULL context */
sync_iso = CONTAINER_OF(done->param, struct ll_sync_iso_set, ull);
lll = &sync_iso->lll;
/* Populate the Sync Lost which will be enqueued in disabled_cb */
rx = (void *)&sync_iso->node_rx_lost;
rx->hdr.handle = sync_iso_handle_get(sync_iso);
rx->hdr.type = NODE_RX_TYPE_SYNC_ISO_LOST;
rx->hdr.rx_ftr.param = sync_iso;
*((uint8_t *)rx->pdu) = lll->term_reason;
/* Stop Sync ISO Ticker */
handle = sync_iso_handle_get(sync_iso);
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_SYNC_ISO_BASE + handle),
ticker_stop_op_cb, (void *)sync_iso);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
static int init_reset(void)
{
/* Add initializations common to power up initialization and HCI reset
* initializations.
*/
mem_init((void *)stream_pool, sizeof(struct lll_sync_iso_stream),
CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT, &stream_free);
return 0;
}
static struct ll_sync_iso_set *sync_iso_get(uint8_t handle)
{
if (handle >= CONFIG_BT_CTLR_SCAN_SYNC_ISO_SET) {
return NULL;
}
return &ll_sync_iso[handle];
}
static uint8_t sync_iso_handle_get(struct ll_sync_iso_set *sync)
{
return mem_index_get(sync, ll_sync_iso, sizeof(*sync));
}
static struct stream *sync_iso_stream_acquire(void)
{
return mem_acquire(&stream_free);
}
static uint16_t sync_iso_stream_handle_get(struct lll_sync_iso_stream *stream)
{
return mem_index_get(stream, stream_pool, sizeof(*stream));
}
static void timeout_cleanup(struct ll_sync_iso_set *sync_iso)
{
struct node_rx_pdu *rx;
uint8_t handle;
uint32_t ret;
/* Populate the Sync Lost which will be enqueued in disabled_cb */
rx = (void *)&sync_iso->node_rx_lost;
rx->hdr.handle = sync_iso_handle_get(sync_iso);
rx->hdr.type = NODE_RX_TYPE_SYNC_ISO_LOST;
rx->hdr.rx_ftr.param = sync_iso;
*((uint8_t *)rx->pdu) = BT_HCI_ERR_CONN_TIMEOUT;
/* Stop Sync ISO Ticker */
handle = sync_iso_handle_get(sync_iso);
ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_SYNC_ISO_BASE + handle),
ticker_stop_op_cb, (void *)sync_iso);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param)
{
static struct lll_prepare_param p;
struct ll_sync_iso_set *sync_iso;
struct lll_sync_iso *lll;
uint32_t ret;
uint8_t ref;
DEBUG_RADIO_PREPARE_O(1);
if (!IS_ENABLED(CONFIG_BT_TICKER_LOW_LAT) &&
!IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT) &&
(lazy == TICKER_LAZY_MUST_EXPIRE)) {
/* FIXME: generate ISO PDU with status set to invalid */
DEBUG_RADIO_PREPARE_O(0);
return;
}
sync_iso = param;
lll = &sync_iso->lll;
/* Increment prepare reference count */
ref = ull_ref_inc(&sync_iso->ull);
LL_ASSERT(ref);
/* Append timing parameters */
p.ticks_at_expire = ticks_at_expire;
p.remainder = remainder;
p.lazy = lazy;
p.force = force;
p.param = lll;
mfy_lll_prepare.param = &p;
/* Kick LLL prepare */
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL, 0U,
&mfy_lll_prepare);
LL_ASSERT(!ret);
DEBUG_RADIO_PREPARE_O(1);
}
static void ticker_start_op_cb(uint32_t status, void *param)
{
ARG_UNUSED(param);
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
}
static void ticker_update_op_cb(uint32_t status, void *param)
{
LL_ASSERT(status == TICKER_STATUS_SUCCESS ||
param == ull_disable_mark_get());
}
static void ticker_stop_op_cb(uint32_t status, void *param)
{
static memq_link_t link;
static struct mayfly mfy = {0U, 0U, &link, NULL, sync_iso_disable};
uint32_t ret;
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
/* Check if any pending LLL events that need to be aborted */
mfy.param = param;
ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW,
TICKER_USER_ID_ULL_HIGH, 0U, &mfy);
LL_ASSERT(!ret);
}
static void sync_iso_disable(void *param)
{
struct ll_sync_iso_set *sync_iso;
struct ull_hdr *hdr;
/* Check ref count to determine if any pending LLL events in pipeline */
sync_iso = param;
hdr = &sync_iso->ull;
if (ull_ref_get(hdr)) {
static memq_link_t link;
static struct mayfly mfy = {0U, 0U, &link, NULL, lll_disable};
uint32_t ret;
mfy.param = &sync_iso->lll;
/* Setup disabled callback to be called when ref count
* returns to zero.
*/
LL_ASSERT(!hdr->disabled_cb);
hdr->disabled_param = mfy.param;
hdr->disabled_cb = disabled_cb;
/* Trigger LLL disable */
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
TICKER_USER_ID_LLL, 0U, &mfy);
LL_ASSERT(!ret);
} else {
/* No pending LLL events */
disabled_cb(&sync_iso->lll);
}
}
static void disabled_cb(void *param)
{
struct ll_sync_iso_set *sync_iso;
struct node_rx_pdu *rx;
memq_link_t *link;
/* Get reference to ULL context */
sync_iso = HDR_LLL2ULL(param);
/* Generate BIG sync lost */
rx = (void *)&sync_iso->node_rx_lost;
LL_ASSERT(rx->hdr.link);
link = rx->hdr.link;
rx->hdr.link = NULL;
/* Enqueue the BIG sync lost towards ULL context */
ll_rx_put_sched(link, rx);
}