/* * Copyright (c) 2018-2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "hal/ccm.h" #include "hal/radio.h" #include "hal/ticker.h" #include "util/util.h" #include "util/memq.h" #include "util/mayfly.h" #include "util/dbuf.h" #include "util/mem.h" #include "ticker/ticker.h" #include "pdu_df.h" #include "lll/pdu_vendor.h" #include "pdu.h" #include "lll.h" #include "lll/lll_vendor.h" #include "lll/lll_adv_types.h" #include "lll_adv.h" #include "lll/lll_adv_pdu.h" #include "lll_adv_sync.h" #include "lll_scan.h" #include "lll/lll_df_types.h" #include "lll_conn.h" #include "lll_conn_iso.h" #include "ll_sw/ull_tx_queue.h" #include "ull_adv_types.h" #include "ull_scan_types.h" #include "ull_conn_types.h" #include "isoal.h" #include "ull_iso_types.h" #include "ull_conn_iso_types.h" #include "ull_internal.h" #include "ull_adv_internal.h" #include "ull_conn_internal.h" #include "ull_conn_iso_internal.h" #include "ll_feat.h" #include "hal/debug.h" #if defined(CONFIG_BT_CENTRAL) static void after_cen_offset_get(uint16_t conn_interval, uint32_t ticks_slot, uint32_t ticks_anchor, uint32_t *win_offset_us); static bool ticker_match_cen_op_cb(uint8_t ticker_id, uint32_t ticks_slot, uint32_t ticks_to_expire, void *op_context); #endif /* CONFIG_BT_CENTRAL */ typedef struct ull_hdr *(*ull_hdr_get_func)(uint8_t ticker_id, uint32_t *ticks_slot); static uint8_t after_match_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, ticker_op_match_func ticker_match_op_cb, ull_hdr_get_func ull_hdr_get_cb_fn, uint32_t *ticks_anchor, uint32_t *ticks_to_expire_match, uint32_t *remainder_match, uint32_t *ticks_slot_match); static void ticker_op_cb(uint32_t status, void *param); static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot); #if (defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER)) || \ defined(CONFIG_BT_CTLR_CENTRAL_ISO) static int group_free_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, uint32_t *ticks_to_expire_prev, uint32_t *ticks_slot_prev, uint32_t *ticks_anchor); static bool ticker_match_any_op_cb(uint8_t ticker_id, uint32_t ticks_slot, uint32_t ticks_to_expire, void *op_context); #endif /* (CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER) || * CONFIG_BT_CTLR_CENTRAL_ISO */ #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER) int ull_sched_adv_aux_sync_free_anchor_get(uint32_t ticks_slot_abs, uint32_t *ticks_anchor) { uint32_t ticks_to_expire; uint32_t ticks_slot; return group_free_slot_get(TICKER_USER_ID_THREAD, ticks_slot_abs, &ticks_to_expire, &ticks_slot, ticks_anchor); } #endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER */ #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) int ull_sched_conn_iso_free_offset_get(uint32_t ticks_slot_abs, uint32_t *ticks_to_expire) { uint32_t ticks_anchor; uint32_t ticks_slot; int err; err = group_free_slot_get(TICKER_USER_ID_ULL_LOW, ticks_slot_abs, ticks_to_expire, &ticks_slot, &ticks_anchor); if (err) { return err; } *ticks_to_expire += ticks_slot; return err; } #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #if defined(CONFIG_BT_CONN) #if defined(CONFIG_BT_CENTRAL) int ull_sched_after_cen_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, uint32_t *ticks_anchor, uint32_t *us_offset) { uint32_t ticks_to_expire; uint32_t ticks_slot; uint32_t remainder; uint8_t ticker_id; ticker_id = after_match_slot_get(user_id, ticks_slot_abs, ticker_match_cen_op_cb, ull_hdr_get_cb, ticks_anchor, &ticks_to_expire, &remainder, &ticks_slot); if (ticker_id != TICKER_NULL) { uint32_t central_spacing_us; uint32_t remainder_us; uint32_t slot_us; /* When CONFIG_BT_CTLR_CENTRAL_SPACING is used then ticks_slot * returned converts to slot_us that is less than or equal to * CONFIG_BT_CTLR_CENTRAL_SPACING, because floor value in ticks * is returned as ticks_slot. * Hence, use CONFIG_BT_CTLR_CENTRAL_SPACING without margin. * * If actual ticks_slot in microseconds is larger than * CONFIG_BT_CTLR_CENTRAL_SPACING, then add margin between * central radio events. */ slot_us = HAL_TICKER_TICKS_TO_US(ticks_slot); if (slot_us > CONFIG_BT_CTLR_CENTRAL_SPACING) { central_spacing_us = slot_us; central_spacing_us += (EVENT_TICKER_RES_MARGIN_US << 1); } else { central_spacing_us = CONFIG_BT_CTLR_CENTRAL_SPACING; } remainder_us = remainder; hal_ticker_remove_jitter(&ticks_to_expire, &remainder_us); *us_offset = HAL_TICKER_TICKS_TO_US(ticks_to_expire) + remainder_us + central_spacing_us; return 0; } return -ECHILD; } void ull_sched_mfy_after_cen_offset_get(void *param) { struct lll_prepare_param *p = param; struct lll_scan *lll = p->param; uint32_t ticks_slot_overhead; uint32_t ticks_at_expire; uint32_t remainder_us; struct ll_conn *conn; conn = HDR_LLL2ULL(lll->conn); if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { ticks_slot_overhead = MAX(conn->ull.ticks_active_to_start, conn->ull.ticks_prepare_to_start); } else { ticks_slot_overhead = 0U; } ticks_at_expire = p->ticks_at_expire; remainder_us = p->remainder; hal_ticker_remove_jitter(&ticks_at_expire, &remainder_us); after_cen_offset_get(lll->conn->interval, (ticks_slot_overhead + conn->ull.ticks_slot), ticks_at_expire, &lll->conn_win_offset_us); if (lll->conn_win_offset_us) { lll->conn_win_offset_us += HAL_TICKER_TICKS_TO_US(p->ticks_at_expire - ticks_at_expire) - remainder_us; } } #endif /* CONFIG_BT_CENTRAL */ void ull_sched_mfy_win_offset_use(void *param) { struct ll_conn *conn = param; uint32_t ticks_slot_overhead; if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { ticks_slot_overhead = MAX(conn->ull.ticks_active_to_start, conn->ull.ticks_prepare_to_start); } else { ticks_slot_overhead = 0U; } /* * TODO: update calculationg of the win_offset * when updating the connection update procedure * see the legacy code from Zephyr v3.3 for inspiration */ } #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) void ull_sched_mfy_free_win_offset_calc(void *param) { uint32_t ticks_to_offset_default = 0U; uint32_t *ticks_to_offset_next; ticks_to_offset_next = &ticks_to_offset_default; /* * TODO: update when updating the connection update procedure * see the legacy code from Zephyr v3.3 for inspiration */ } void ull_sched_mfy_win_offset_select(void *param) { #define OFFSET_S_MAX 6 #define OFFSET_M_MAX 6 /* * TODO: update calculation of win_offset when * updating the connection update procedure * see the legacy code from Zephyr v3.3 for inspiration */ #undef OFFSET_S_MAX #undef OFFSET_M_MAX } /* * TODO: probably we need a function for calculating the window offset * see the legacy code from Zephyr v3.3 for inspiration */ #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ #if defined(CONFIG_BT_CENTRAL) static void after_cen_offset_get(uint16_t conn_interval, uint32_t ticks_slot, uint32_t ticks_anchor, uint32_t *win_offset_us) { uint32_t ticks_anchor_offset = ticks_anchor; int err; err = ull_sched_after_cen_slot_get(TICKER_USER_ID_ULL_LOW, ticks_slot, &ticks_anchor_offset, win_offset_us); if (err) { return; } if ((ticks_anchor_offset - ticks_anchor) & BIT(HAL_TICKER_CNTR_MSBIT)) { *win_offset_us -= HAL_TICKER_TICKS_TO_US( ticker_ticks_diff_get(ticks_anchor, ticks_anchor_offset)); } else { *win_offset_us += HAL_TICKER_TICKS_TO_US( ticker_ticks_diff_get(ticks_anchor_offset, ticks_anchor)); } /* Round positive offset value in the future to within one connection * interval value. * Offsets in the past, value with MSBit set, are handled by caller by * adding radio end time and connection interval as necessary to get a * window offset in future when establishing a connection. */ if ((*win_offset_us & BIT(31)) == 0) { uint32_t conn_interval_us = conn_interval * CONN_INT_UNIT_US; while (*win_offset_us > conn_interval_us) { *win_offset_us -= conn_interval_us; } } } #endif /* CONFIG_BT_CENTRAL */ #endif /* CONFIG_BT_CONN */ #if (defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER)) || \ defined(CONFIG_BT_CTLR_CENTRAL_ISO) static int group_free_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, uint32_t *ticks_to_expire_prev, uint32_t *ticks_slot_prev, uint32_t *ticks_anchor) { uint32_t remainder_prev; uint8_t ticker_id; ticker_id = after_match_slot_get(user_id, ticks_slot_abs, ticker_match_any_op_cb, ull_hdr_get_cb, ticks_anchor, ticks_to_expire_prev, &remainder_prev, ticks_slot_prev); if (ticker_id != TICKER_NULL) { uint32_t ticks_to_expire; uint32_t ticks_slot; ticks_to_expire = *ticks_to_expire_prev; ticks_slot = *ticks_slot_prev; if (false) { #if defined(CONFIG_BT_BROADCASTER) && CONFIG_BT_CTLR_ADV_AUX_SET > 0 } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_AUX_BASE, TICKER_ID_ADV_AUX_LAST)) { *ticks_anchor += ticks_to_expire; *ticks_anchor += ticks_slot; *ticks_anchor += HAL_TICKER_US_TO_TICKS( EVENT_TICKER_RES_MARGIN_US << 1); #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) const struct ll_adv_aux_set *aux; aux = ull_adv_aux_get(ticker_id - TICKER_ID_ADV_AUX_BASE); if (aux->lll.adv->sync) { const struct ll_adv_sync_set *sync; sync = HDR_LLL2ULL(aux->lll.adv->sync); if (sync->is_started) { *ticks_anchor += sync->ull.ticks_slot; *ticks_anchor += HAL_TICKER_US_TO_TICKS( EVENT_TICKER_RES_MARGIN_US << 1); } } #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ return 0; #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_SYNC_BASE, TICKER_ID_ADV_SYNC_LAST)) { *ticks_anchor += ticks_to_expire; *ticks_anchor += ticks_slot; *ticks_anchor += HAL_TICKER_US_TO_TICKS( EVENT_TICKER_RES_MARGIN_US << 1); return 0; #if defined(CONFIG_BT_CTLR_ADV_ISO) } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_ISO_BASE, TICKER_ID_ADV_ISO_LAST)) { *ticks_anchor += ticks_to_expire; *ticks_anchor += ticks_slot; *ticks_anchor += HAL_TICKER_US_TO_TICKS( EVENT_TICKER_RES_MARGIN_US << 1); return 0; #endif /* CONFIG_BT_CTLR_ADV_ISO */ #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ #endif /* CONFIG_BT_BROADCASTER && CONFIG_BT_CTLR_ADV_AUX_SET > 0 */ #if defined(CONFIG_BT_CONN) } else if (IN_RANGE(ticker_id, TICKER_ID_CONN_BASE, TICKER_ID_CONN_LAST)) { *ticks_anchor += ticks_to_expire; *ticks_anchor += ticks_slot; *ticks_anchor += HAL_TICKER_US_TO_TICKS( EVENT_TICKER_RES_MARGIN_US << 1); return 0; #endif /* CONFIG_BT_CONN */ } } return -ECHILD; } #endif /* (CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER) || * CONFIG_BT_CTLR_CENTRAL_ISO */ static uint8_t after_match_slot_get(uint8_t user_id, uint32_t ticks_slot_abs, ticker_op_match_func ticker_match_op_cb, ull_hdr_get_func ull_hdr_get_cb_fn, uint32_t *ticks_anchor, uint32_t *ticks_to_expire_match, uint32_t *remainder_match, uint32_t *ticks_slot_match) { uint32_t ticks_to_expire_prev; uint32_t ticks_slot_abs_prev; uint32_t ticks_anchor_prev; uint32_t ticks_to_expire; uint32_t remainder_prev; uint32_t remainder; uint8_t ticker_id_prev; uint8_t ticker_id; uint8_t retry; /* As we may place the event between two other events, ensure there is * space for 4 times +/- 16 us jitter. I.e. with 32KHz sleep clock, * ~30.517 us after previous event, ~30.517 us before and after current * event, and an ~30.517 us before next event. Hence 8 time of ceil * value 16 us (30.517 / 2) or 4 times EVENT_TICKER_RES_MARGIN_US. */ ticks_slot_abs += HAL_TICKER_US_TO_TICKS(EVENT_TICKER_RES_MARGIN_US << 2); /* There is a possibility that ticker nodes expire during iterations in * this function causing the reference ticks_anchor returned for the * found ticker to change. In this case the iterations have to be * restarted with the new reference ticks_anchor value. * Simultaneous continuous scanning on 1M and Coded PHY, alongwith * directed advertising and N other state/role could expire in quick * succession, hence have a retry count of UINT8_MAX, which is possible * maximum implementation limit for ticker nodes. */ retry = UINT8_MAX; /* Initialize variable required for iterations to find a free slot */ ticker_id = ticker_id_prev = TICKER_NULL; ticks_anchor_prev = 0U; ticks_to_expire = ticks_to_expire_prev = 0U; remainder = remainder_prev = 0U; ticks_slot_abs_prev = 0U; while (1) { uint32_t ticks_slot_abs_curr = 0U; uint32_t ticks_to_expire_normal; uint32_t volatile ret_cb; uint32_t ticks_slot; struct ull_hdr *hdr; uint32_t ret; bool success; ret_cb = TICKER_STATUS_BUSY; #if defined(CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH) ret = ticker_next_slot_get_ext(TICKER_INSTANCE_ID_CTLR, user_id, &ticker_id, ticks_anchor, &ticks_to_expire, &remainder, NULL, /* lazy */ ticker_match_op_cb, NULL, /* match_op_context */ ticker_op_cb, (void *)&ret_cb); #else /* !CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH */ ret = ticker_next_slot_get(TICKER_INSTANCE_ID_CTLR, user_id, &ticker_id, ticks_anchor, &ticks_to_expire, ticker_op_cb, (void *)&ret_cb); #endif /* !CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH */ if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { ticker_job_sched(TICKER_INSTANCE_ID_CTLR, user_id); } } /* Using a local variable to address the Coverity rule: * Incorrect expression (ASSERT_SIDE_EFFECT) * Argument "ret_cb" of LL_ASSERT() has a side effect * because the variable is volatile. The containing function * might work differently in a non-debug build. */ success = (ret_cb == TICKER_STATUS_SUCCESS); LL_ASSERT(success); /* There is a possibility that tickers expire while we * iterate through the active list of tickers, start over with * a fresh iteration. */ if ((ticker_id_prev != TICKER_NULL) && (*ticks_anchor != ticks_anchor_prev)) { LL_ASSERT(retry); retry--; ticker_id = ticker_id_prev = TICKER_NULL; continue; } /* No more active tickers with slot */ if (ticker_id == TICKER_NULL) { break; } #if !defined(CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH) if (!ticker_match_op_cb(ticker_id, 0, 0, NULL)) { continue; } #endif /* CONFIG_BT_TICKER_NEXT_SLOT_GET_MATCH */ hdr = ull_hdr_get_cb_fn(ticker_id, &ticks_slot); if (!hdr) { continue; } ticks_to_expire_normal = ticks_to_expire; #if defined(CONFIG_BT_CTLR_LOW_LAT) #if defined(CONFIG_BT_CTLR_XTAL_ADVANCED) if (hdr->ticks_prepare_to_start & XON_BITMASK) { const uint32_t ticks_prepare_to_start = MAX(hdr->ticks_active_to_start, hdr->ticks_preempt_to_start); ticks_slot_abs_curr = hdr->ticks_prepare_to_start & ~XON_BITMASK; ticks_to_expire_normal -= ticks_slot_abs_curr - ticks_prepare_to_start; } else #endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */ { const uint32_t ticks_prepare_to_start = MAX(hdr->ticks_active_to_start, hdr->ticks_prepare_to_start); ticks_slot_abs_curr = ticks_prepare_to_start; } #endif ticks_slot_abs_curr += ticks_slot; if ((ticker_id_prev != TICKER_NULL) && (ticker_ticks_diff_get(ticks_to_expire_normal, ticks_to_expire_prev) > (ticks_slot_abs_prev + ticks_slot_abs))) { break; } ticks_anchor_prev = *ticks_anchor; ticker_id_prev = ticker_id; ticks_to_expire_prev = ticks_to_expire_normal; remainder_prev = remainder; ticks_slot_abs_prev = ticks_slot_abs_curr; } if (ticker_id_prev != TICKER_NULL) { *ticks_to_expire_match = ticks_to_expire_prev; *remainder_match = remainder_prev; *ticks_slot_match = ticks_slot_abs_prev; } return ticker_id_prev; } static void ticker_op_cb(uint32_t status, void *param) { *((uint32_t volatile *)param) = status; } #if (defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER)) || \ defined(CONFIG_BT_CTLR_CENTRAL_ISO) static bool ticker_match_any_op_cb(uint8_t ticker_id, uint32_t ticks_slot, uint32_t ticks_to_expire, void *op_context) { ARG_UNUSED(ticks_slot); ARG_UNUSED(ticks_to_expire); ARG_UNUSED(op_context); return false || #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER) IN_RANGE(ticker_id, TICKER_ID_ADV_AUX_BASE, TICKER_ID_ADV_AUX_LAST) || #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) IN_RANGE(ticker_id, TICKER_ID_ADV_SYNC_BASE, TICKER_ID_ADV_SYNC_LAST) || #if defined(CONFIG_BT_CTLR_ADV_ISO) IN_RANGE(ticker_id, TICKER_ID_ADV_ISO_BASE, TICKER_ID_ADV_ISO_LAST) || #endif /* CONFIG_BT_CTLR_ADV_ISO */ #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ #endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER */ #if defined(CONFIG_BT_CONN) IN_RANGE(ticker_id, TICKER_ID_CONN_BASE, TICKER_ID_CONN_LAST) || #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) IN_RANGE(ticker_id, TICKER_ID_CONN_ISO_BASE, TICKER_ID_CONN_ISO_LAST) || #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #endif /* CONFIG_BT_CONN */ false; } #endif /* (CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER) || * CONFIG_BT_CTLR_CENTRAL_ISO */ #if defined(CONFIG_BT_CENTRAL) static bool ticker_match_cen_op_cb(uint8_t ticker_id, uint32_t ticks_slot, uint32_t ticks_to_expire, void *op_context) { ARG_UNUSED(ticks_slot); ARG_UNUSED(ticks_to_expire); ARG_UNUSED(op_context); return IN_RANGE(ticker_id, TICKER_ID_CONN_BASE, TICKER_ID_CONN_LAST) || #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) && \ (CONFIG_BT_CTLR_CENTRAL_SPACING == 0) IN_RANGE(ticker_id, TICKER_ID_CONN_ISO_BASE, TICKER_ID_CONN_ISO_LAST) || #endif /* CONFIG_BT_CTLR_CENTRAL_ISO && * (CONFIG_BT_CTLR_CENTRAL_SPACING == 0) */ false; } #endif /* CONFIG_BT_CENTRAL */ static struct ull_hdr *ull_hdr_get_cb(uint8_t ticker_id, uint32_t *ticks_slot) { if (false) { #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_BROADCASTER) } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_AUX_BASE, TICKER_ID_ADV_AUX_LAST)) { struct ll_adv_aux_set *aux; aux = ull_adv_aux_get(ticker_id - TICKER_ID_ADV_AUX_BASE); if (aux) { if (IS_ENABLED(CONFIG_BT_CTLR_ADV_RESERVE_MAX)) { uint32_t time_us; time_us = ull_adv_aux_time_get(aux, PDU_AC_PAYLOAD_SIZE_MAX, PDU_AC_PAYLOAD_SIZE_MAX); *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = aux->ull.ticks_slot; #if defined(CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET) && \ (CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET != 0) struct ll_adv_sync_set *sync; sync = HDR_LLL2ULL(aux->lll.adv->sync); if (sync->ull.ticks_slot > *ticks_slot) { *ticks_slot = sync->ull.ticks_slot; } #endif /* CONFIG_BT_CTLR_ADV_AUX_SYNC_OFFSET */ } return &aux->ull; } #if defined(CONFIG_BT_CTLR_ADV_PERIODIC) } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_SYNC_BASE, TICKER_ID_ADV_SYNC_LAST)) { struct ll_adv_sync_set *sync; sync = ull_adv_sync_get(ticker_id - TICKER_ID_ADV_SYNC_BASE); if (sync) { if (IS_ENABLED(CONFIG_BT_CTLR_ADV_RESERVE_MAX)) { uint32_t time_us; time_us = ull_adv_sync_time_get(sync, PDU_AC_PAYLOAD_SIZE_MAX); *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = sync->ull.ticks_slot; } return &sync->ull; } #if defined(CONFIG_BT_CTLR_ADV_ISO) } else if (IN_RANGE(ticker_id, TICKER_ID_ADV_ISO_BASE, TICKER_ID_ADV_ISO_LAST)) { struct ll_adv_iso_set *adv_iso; adv_iso = ull_adv_iso_get(ticker_id - TICKER_ID_ADV_ISO_BASE); if (adv_iso) { uint32_t time_us; time_us = ull_adv_iso_max_time_get(adv_iso); *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); return &adv_iso->ull; } #endif /* CONFIG_BT_CTLR_ADV_ISO */ #endif /* CONFIG_BT_CTLR_ADV_PERIODIC */ #endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_BROADCASTER */ #if defined(CONFIG_BT_CONN) } else if (IN_RANGE(ticker_id, TICKER_ID_CONN_BASE, TICKER_ID_CONN_LAST)) { struct ll_conn *conn; conn = ll_conn_get(ticker_id - TICKER_ID_CONN_BASE); if (conn && !conn->lll.role) { if (IS_ENABLED(CONFIG_BT_CTLR_CENTRAL_RESERVE_MAX)) { uint32_t ready_delay_us; uint16_t max_tx_time; uint16_t max_rx_time; uint32_t time_us; #if defined(CONFIG_BT_CTLR_PHY) ready_delay_us = lll_radio_tx_ready_delay_get(conn->lll.phy_tx, conn->lll.phy_flags); #else ready_delay_us = lll_radio_tx_ready_delay_get(0U, 0U); #endif #if defined(CONFIG_BT_CTLR_PHY_CODED) max_tx_time = PDU_DC_MAX_US(LL_LENGTH_OCTETS_TX_MAX, PHY_CODED); max_rx_time = PDU_DC_MAX_US(LL_LENGTH_OCTETS_RX_MAX, PHY_CODED); #else /* !CONFIG_BT_CTLR_PHY_CODED */ max_tx_time = PDU_DC_MAX_US(LL_LENGTH_OCTETS_TX_MAX, PHY_1M); max_rx_time = PDU_DC_MAX_US(LL_LENGTH_OCTETS_RX_MAX, PHY_1M); #endif /* !CONFIG_BT_CTLR_PHY_CODED */ time_us = EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US + ready_delay_us + max_rx_time + EVENT_IFS_US + max_tx_time + (EVENT_CLOCK_JITTER_US << 1); *ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(time_us); } else { *ticks_slot = conn->ull.ticks_slot; } if (*ticks_slot < HAL_TICKER_US_TO_TICKS(CONFIG_BT_CTLR_CENTRAL_SPACING)) { *ticks_slot = HAL_TICKER_US_TO_TICKS(CONFIG_BT_CTLR_CENTRAL_SPACING); } return &conn->ull; } #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) } else if (IN_RANGE(ticker_id, TICKER_ID_CONN_ISO_BASE, TICKER_ID_CONN_ISO_LAST)) { struct ll_conn_iso_group *cig; cig = ll_conn_iso_group_get(ticker_id - TICKER_ID_CONN_ISO_BASE); if (cig) { *ticks_slot = cig->ull.ticks_slot; return &cig->ull; } #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ #endif /* CONFIG_BT_CONN */ } return NULL; }