| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/zephyr.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 "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_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" |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) |
| #define LOG_MODULE_NAME bt_ctlr_ull_sync_iso |
| #include "common/log.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; |
| |
| /* 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->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]; |
| } |
| |
| 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) |
| { |
| 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); |
| |
| 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; |
| sync_iso_offset_us -= PDU_BIS_US(pdu->len, lll->enc, 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(rx->hdr.link, rx); |
| ll_rx_sched(); |
| |
| 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; |
| 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(link, rx); |
| ll_rx_sched(); |
| } |