From 019f88ec2162ce64fcdef54f47daa2506e9b0ba3 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Sun, 11 Jun 2023 06:45:04 +0530 Subject: [PATCH] Bluetooth: Controller: Fix CIS failed to be established Fix CIS create implementation to consider CIG events with laziness before the new CIS in an already active CIG is made active. Previous laziness value of the CIG events is determined and at the CIG event where the new CIS event_count is calculated the lazy_active value determined is decremented from the total lazy value. Without this fix, event_count of the new active CIS events where incorrect, causing CIS create resulting in fail to be established. Signed-off-by: Vinayak Kariappa Chettimada --- .../bluetooth/controller/ll_sw/lll_conn_iso.h | 9 ++ .../bluetooth/controller/ll_sw/ull_conn_iso.c | 135 ++++++++++++++++-- 2 files changed, 135 insertions(+), 9 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h b/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h index 5a8702d012c4f8..dadaa37ac3a5d5 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h +++ b/subsys/bluetooth/controller/ll_sw/lll_conn_iso.h @@ -46,6 +46,15 @@ struct lll_conn_iso_stream { uint8_t active:1; /* 1 if CIS LLL is active */ uint8_t datapath_ready_rx:1;/* 1 if datapath for RX is ready */ +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) + /* Lazy at CIS active. Number of previously skipped CIG events that is + * determined when CIS is made active and subtracted from total CIG + * events that where skipped when this CIS gets to use radio for the + * first time. + */ + uint16_t lazy_active; +#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ + /* Resumption information */ uint8_t next_subevent; /* Next subevent to schedule */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c index d405b392b2cb8e..570f1e3000f85c 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_iso.c @@ -65,6 +65,12 @@ static int init_reset(void); +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) +static void cis_lazy_fill(struct ll_conn_iso_stream *cis); +static void mfy_cis_lazy_fill(void *param); +static void ticker_next_slot_get_op_cb(uint32_t status, void *param); +#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ +static void ticker_start_op_cb(uint32_t status, void *param); static void ticker_update_cig_op_cb(uint32_t status, void *param); static void ticker_resume_op_cb(uint32_t status, void *param); static void ticker_resume_cb(uint32_t ticks_at_expire, uint32_t ticks_drift, @@ -686,6 +692,11 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift, if (cis->lll.handle != 0xFFFF && cis->lll.active) { cis->lll.event_count += (lazy + 1U); +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) + cis->lll.event_count -= cis->lll.lazy_active; + cis->lll.lazy_active = 0U; +#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ + leading_event_count = MAX(leading_event_count, cis->lll.event_count); @@ -760,13 +771,6 @@ void ull_conn_iso_ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift, ull_conn_iso_transmit_test_cig_interval(cig->lll.handle, ticks_at_expire); } -static void ticker_op_cb(uint32_t status, void *param) -{ - ARG_UNUSED(param); - - LL_ASSERT(status == TICKER_STATUS_SUCCESS); -} - void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, uint32_t ticks_at_expire, uint32_t remainder, uint16_t instant_latency) @@ -790,7 +794,6 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, cis->lll.offset = cis_offs_to_cig_ref; cis->lll.handle = cis_handle; - cis->lll.active = 1U; #if defined(CONFIG_BT_CTLR_LE_ENC) if (conn->lll.enc_tx) { @@ -844,6 +847,13 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, * validated handle. */ if (cig->state == CIG_STATE_ACTIVE) { +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) + cis_lazy_fill(cis); +#else /* CONFIG_BT_CTLR_JIT_SCHEDULING */ + /* Set CIS active in already active CIG */ + cis->lll.active = 1U; +#endif /* CONFIG_BT_CTLR_JIT_SCHEDULING */ + /* We're done */ return; } @@ -994,11 +1004,118 @@ void ull_conn_iso_start(struct ll_conn *conn, uint16_t cis_handle, ticks_periodic, ticks_remainder, TICKER_NULL_LAZY, ticks_slot, ull_conn_iso_ticker_cb, cig, - ticker_op_cb, NULL); + ticker_start_op_cb, NULL); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); + /* Set CIG and the first CIS state as active */ cig->state = CIG_STATE_ACTIVE; + cis->lll.active = 1U; + +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) + /* CIS event lazy at CIS create */ + cis->lll.lazy_active = 0U; +#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ +} + +#if !defined(CONFIG_BT_CTLR_JIT_SCHEDULING) +static void cis_lazy_fill(struct ll_conn_iso_stream *cis) +{ + static memq_link_t link; + static struct mayfly mfy = {0U, 0U, &link, NULL, mfy_cis_lazy_fill}; + uint32_t ret; + + mfy.param = cis; + ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1U, &mfy); + LL_ASSERT(!ret); +} + +static void mfy_cis_lazy_fill(void *param) +{ + struct ll_conn_iso_stream *cis; + struct ll_conn_iso_group *cig; + uint32_t ticks_to_expire; + uint32_t ticks_current; + uint32_t remainder; + uint8_t ticker_id; + uint16_t lazy; + uint8_t retry; + uint8_t id; + + cis = param; + cig = cis->group; + ticker_id = TICKER_ID_CONN_ISO_BASE + ll_conn_iso_group_handle_get(cig); + + id = TICKER_NULL; + ticks_to_expire = 0U; + ticks_current = 0U; + + /* In the first iteration the actual ticks_current value is returned + * which will be different from the initial value of 0 that is set. + * Subsequent iterations should return the same ticks_current as the + * reference tick. + * In order to avoid infinite updates to ticker's reference due to any + * race condition due to expiring tickers, we try upto 3 more times. + * Hence, first iteration to get an actual ticks_current and 3 more as + * retries when there could be race conditions that changes the value + * of ticks_current. + * + * ticker_next_slot_get_ext() restarts iterating when updated value of + * ticks_current is returned. + */ + retry = 4U; + do { + uint32_t volatile ret_cb; + uint32_t ticks_previous; + uint32_t ret; + bool success; + + ticks_previous = ticks_current; + + ret_cb = TICKER_STATUS_BUSY; + ret = ticker_next_slot_get_ext(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_LOW, &id, + &ticks_current, &ticks_to_expire, &remainder, &lazy, + NULL, NULL, ticker_next_slot_get_op_cb, + (void *)&ret_cb); + if (ret == TICKER_STATUS_BUSY) { + /* Busy wait until Ticker Job is enabled after any Radio + * event is done using the Radio hardware. Ticker Job + * ISR is disabled during Radio events in LOW_LAT + * feature to avoid Radio ISR latencies. + */ + while (ret_cb == TICKER_STATUS_BUSY) { + ticker_job_sched(TICKER_INSTANCE_ID_CTLR, + TICKER_USER_ID_ULL_LOW); + } + } + + success = (ret_cb == TICKER_STATUS_SUCCESS); + LL_ASSERT(success); + + LL_ASSERT((ticks_current == ticks_previous) || retry--); + + LL_ASSERT(id != TICKER_NULL); + } while (id != ticker_id); + + /* Set CIS active in already active CIG and any previous laziness in + * CIG before the CIS gets active that be decremented when event_count + * is incremented in ull_conn_iso_ticker_cb(). + */ + cis->lll.active = 1U; + cis->lll.lazy_active = lazy; +} + +static void ticker_next_slot_get_op_cb(uint32_t status, void *param) +{ + *((uint32_t volatile *)param) = status; +} +#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */ + +static void ticker_start_op_cb(uint32_t status, void *param) +{ + ARG_UNUSED(param); + + LL_ASSERT(status == TICKER_STATUS_SUCCESS); } static void ticker_update_cig_op_cb(uint32_t status, void *param)