Bluetooth: Controller: Fix Central CIS offset for dissimilar intervals

Fix Central CIS offset calculation for dissimilar ACL and
ISO intervals in use.

Mayfly execution of `mfy_cig_offset_get()` could be after
"LLL Prepare" or before depending on whether a previous radio
event is being preempted or not, respectively; the
`conn->lll.event_counter` may not be pre-incremented.
This race condition is fixed by the fact that we use a
constant instant delta value now.

Dissimilar ACL and ISO intervals may lead to ACL overlapping
or be too close to ISO event, causing preemption; under this
case ACLs "LLL Prepare" would run after `mfy_cig_offset_get`
causing incorrect calculation of CIS offset without the fix.

Remove redundant `instant` member in `ll_conn_iso_stream`
structure as a constant CIS Create instant delta is now
used.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2025-03-30 06:50:53 +02:00 committed by Benjamin Cabé
parent 153be1b142
commit fc14570585
2 changed files with 16 additions and 23 deletions

View File

@ -65,6 +65,14 @@
#define PHY_VALID_MASK (BT_HCI_ISO_PHY_VALID_MASK & ~BIT(2))
#endif
/* CIS Create Procedure uses 3 PDU transmissions, and one connection interval to process the LLCP
* requested, hence minimum relative instant not be less than 4. I.e. the CIS_REQ PDU will be
* transmitted in the next ACL interval.
* The +1 also helps with the fact that currently we do not have Central implementation to handle
* event latencies at the instant. Refer to `ull_conn_iso_start()` implementation.
*/
#define CIS_CREATE_INSTANT_DELTA_MIN 4U
#if (CONFIG_BT_CTLR_CENTRAL_SPACING == 0)
static void cig_offset_get(struct ll_conn_iso_stream *cis);
static void mfy_cig_offset_get(void *param);
@ -925,10 +933,10 @@ uint8_t ull_central_iso_setup(uint16_t cis_handle,
cis->lll.prepared = 0U;
#endif /* !CONFIG_BT_CTLR_JIT_SCHEDULING */
cis->central.instant = instant;
#if defined(CONFIG_BT_CTLR_ISOAL_PSN_IGNORE)
cis->pkt_seq_num = 0U;
#endif /* CONFIG_BT_CTLR_ISOAL_PSN_IGNORE */
/* It is intentional to initialize to the 39 bit maximum value and rollover to 0 in the
* prepare function, the event counter is pre-incremented in prepare function for the
* current ISO event.
@ -975,19 +983,11 @@ int ull_central_iso_cis_offset_get(uint16_t cis_handle,
conn = ll_conn_get(cis->lll.acl_handle);
/* NOTE: CIS Create Procedure uses 3 PDU transmissions, hence minimum relative instant not
* be less than 3. As the CIS_REQ PDU will be transmitted in the next ACL interval,
* add +1 to the instant. The +1 also helps with the fact that currently we do not
* have Central implementation to handle event latencies at the instant. Refer to
* `ull_conn_iso_start()` implementation.
*
* `ull_conn_llcp()` is called before `ull_ref_inc()` hence we do not need to use
* `ull_conn_event_counter()`.
/* `ull_conn_llcp()` (caller of this function) is called before `ull_ref_inc()` hence we do
* not need to use `ull_conn_event_counter()`.
*/
cis->central.instant = conn->lll.event_counter + conn->lll.latency_prepare +
conn->llcp.prep.lazy + 4U;
*conn_event_count = cis->central.instant;
*conn_event_count = conn->lll.event_counter + conn->lll.latency_prepare +
conn->llcp.prep.lazy + CIS_CREATE_INSTANT_DELTA_MIN;
/* Provide CIS offset range
* CIS_Offset_Max < (connInterval - (CIG_Sync_Delay + T_MSS))
@ -1090,7 +1090,6 @@ static void cis_offset_get(struct ll_conn_iso_stream *cis)
static void mfy_cis_offset_get(void *param)
{
uint32_t elapsed_acl_us, elapsed_cig_us;
uint16_t latency_acl, latency_cig;
struct ll_conn_iso_stream *cis;
struct ll_conn_iso_group *cig;
uint32_t cig_remainder_us;
@ -1098,10 +1097,11 @@ static void mfy_cis_offset_get(void *param)
uint32_t cig_interval_us;
uint32_t offset_limit_us;
uint32_t ticks_to_expire;
uint32_t remainder = 0U;
uint32_t ticks_current;
uint32_t offset_min_us;
struct ll_conn *conn;
uint32_t remainder = 0U;
uint16_t latency_cig;
uint8_t ticker_id;
uint16_t lazy;
uint8_t retry;
@ -1188,13 +1188,7 @@ static void mfy_cis_offset_get(void *param)
* and latency counts (typically 3) is low enough to avoid 32-bit
* overflow. Refer to ull_central_iso_cis_offset_get().
*/
/* FIXME: Mayfly execution of `mfy_cig_offset_get()` could be before "LLL Prepare" or after
* conn->lll.event_counter could have been pre-incremented.
* This race condition needs a fix.
*/
latency_acl = cis->central.instant - conn->lll.event_counter - conn->lll.latency_prepare -
conn->llcp.prep.lazy;
elapsed_acl_us = latency_acl * conn->lll.interval * CONN_INT_UNIT_US;
elapsed_acl_us = CIS_CREATE_INSTANT_DELTA_MIN * conn->lll.interval * CONN_INT_UNIT_US;
/* Calculate elapsed CIG intervals until the instant */
cig_interval_us = cig->iso_interval * ISO_INT_UNIT_US;

View File

@ -34,7 +34,6 @@ struct ll_conn_iso_stream {
struct {
uint8_t c_rtn;
uint8_t p_rtn;
uint16_t instant;
} central;
};