diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index 9b890315cfd9..fe938db4728d 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -236,7 +236,8 @@ struct lll_prepare_param { #if defined(CONFIG_BT_CTLR_JIT_SCHEDULING) int8_t prio; #endif /* CONFIG_BT_CTLR_JIT_SCHEDULING */ - uint8_t force; + uint8_t force:1; + uint8_t defer:1; void *param; }; diff --git a/subsys/bluetooth/controller/ll_sw/lll_common.c b/subsys/bluetooth/controller/ll_sw/lll_common.c index ed35b4ddd083..c6c561dbb76d 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_common.c +++ b/subsys/bluetooth/controller/ll_sw/lll_common.c @@ -62,6 +62,8 @@ int lll_prepare(lll_is_abort_cb_t is_abort_cb, lll_abort_cb_t abort_cb, prepare_param->prio = prio; #endif /* CONFIG_BT_CTLR_JIT_SCHEDULING */ + prepare_param->defer = 0U; + err = lll_prepare_resolve(is_abort_cb, abort_cb, prepare_cb, prepare_param, 0U, 0U); return err; diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index 7f0a01d64b4f..bd7464b77672 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -807,6 +807,8 @@ void lll_isr_early_abort(void *param) { int err; + radio_status_reset(); + radio_isr_set(isr_race, param); if (!radio_is_idle()) { radio_disable(); @@ -1316,7 +1318,9 @@ static void preempt(void *param) /* Returns -EBUSY when same curr and next state/role, do not * abort same curr and next event. */ - if (err != -EBUSY) { + if (err == -EBUSY) { + ready->prepare_param.defer = 1U; + } else { /* Let preemptor LLL know about the cancelled prepare */ ready->is_aborted = 1; ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c index 0fd8a368047e..8935b8fc409a 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c @@ -247,12 +247,24 @@ static int prepare_cb(struct lll_prepare_param *p) overhead = lll_preempt_calc(ull, (TICKER_ID_CONN_BASE + lll->handle), ticks_at_event); /* check if preempt to start has changed */ if (overhead) { - LL_ASSERT_OVERHEAD(overhead); + int err; + + if (p->defer == 1U) { + /* We accept the overhead and abort the event; previous event has decided to + * continue and has deferred this event beyond the real time threshold to + * have the radio prepared for this event. + */ + err = 0; + } else { + LL_ASSERT_OVERHEAD(overhead); + + err = -ECANCELED; + } radio_isr_set(lll_isr_abort, lll); radio_disable(); - return -ECANCELED; + return err; } #endif /* !CONFIG_BT_CTLR_XTAL_ADVANCED */ diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c index ea5028a00f10..cf035abedfc9 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c @@ -69,6 +69,7 @@ static uint8_t crc_valid; static uint8_t is_aborted; static uint16_t tx_cnt; static uint16_t trx_cnt; +static uint8_t trx_busy_iteration; #if defined(CONFIG_BT_CTLR_LE_ENC) static uint8_t mic_state; @@ -153,6 +154,7 @@ void lll_conn_prepare_reset(void) crc_valid = 0U; crc_expire = 0U; is_aborted = 0U; + trx_busy_iteration = 0U; #if defined(CONFIG_BT_CTLR_LE_ENC) mic_state = LLL_CONN_MIC_NONE; @@ -160,20 +162,30 @@ void lll_conn_prepare_reset(void) } #if defined(CONFIG_BT_CENTRAL) +/* Number of times central event being aborted by same event instance be skipped */ +/* FIXME: Increasing this causes event pipeline overflow assertion, add LLL implementation to + * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb + * interface. + */ +#define CENTRAL_TRX_BUSY_ITERATION_MAX 1 + int lll_conn_central_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) { struct lll_conn *lll = curr; - /* Do not abort if near supervision timeout */ - if (lll->forced) { - return 0; - } + if (next != curr) { + /* Do not be aborted by a different event if near supervision timeout */ + if ((lll->forced == 1U) && (trx_cnt < 1U)) { + return 0; + } - /* Do not be aborted by same event if a single central trx has not been - * exchanged. - */ - if ((next == curr) && (trx_cnt < 1U)) { + } else if ((trx_cnt < 1U) && (trx_busy_iteration < CENTRAL_TRX_BUSY_ITERATION_MAX)) { + trx_busy_iteration++; + + /* Do not be aborted by same event if a single central's Rx has not completed. + * Cases where single trx duration can be greater than connection interval. + */ return -EBUSY; } @@ -182,20 +194,30 @@ int lll_conn_central_is_abort_cb(void *next, void *curr, #endif /* CONFIG_BT_CENTRAL */ #if defined(CONFIG_BT_PERIPHERAL) +/* Number of times peripheral event being aborted by same event instance be skipped */ +/* FIXME: Increasing this causes event pipeline overflow assertion, add LLL implementation to + * gracefully abort the deferred next event when -EBUSY is returned in this is_abort_cb + * interface. + */ +#define PERIPHERAL_TRX_BUSY_ITERATION_MAX 1 + int lll_conn_peripheral_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) { struct lll_conn *lll = curr; - /* Do not abort if near supervision timeout */ - if (lll->forced) { - return 0; - } + if (next != curr) { + /* Do not be aborted by a different event if near supervision timeout */ + if ((lll->forced == 1U) && (tx_cnt < 1U)) { + return 0; + } - /* Do not be aborted by same event if a single peripheral trx has not - * been exchanged. - */ - if ((next == curr) && (tx_cnt < 1U)) { + } else if ((tx_cnt < 1U) && (trx_busy_iteration < PERIPHERAL_TRX_BUSY_ITERATION_MAX)) { + trx_busy_iteration++; + + /* Do not be aborted by same event if a single peripheral's Tx has not completed. + * Cases where single trx duration can be greater than connection interval. + */ return -EBUSY; } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c index 0ed7a63af8d8..9d569b28116f 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c @@ -339,12 +339,24 @@ static int prepare_cb(struct lll_prepare_param *p) overhead = lll_preempt_calc(ull, (TICKER_ID_CONN_BASE + lll->handle), ticks_at_event); /* check if preempt to start has changed */ if (overhead) { - LL_ASSERT_OVERHEAD(overhead); + int err; + + if (p->defer == 1U) { + /* We accept the overhead and abort the event; previous event has decided to + * continue and has deferred this event beyond the real time threshold to + * have the radio prepared for this event. + */ + err = 0; + } else { + LL_ASSERT_OVERHEAD(overhead); + + err = -ECANCELED; + } radio_isr_set(lll_isr_abort, lll); radio_disable(); - return -ECANCELED; + return err; } #endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */