From abfe5f17a949752ff0affecf8932e77b7d57e430 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Wed, 23 Jan 2019 23:36:52 +0530 Subject: [PATCH] Bluetooth: Controller: 1 ms connection 1 ms connection interval support. Signed-off-by: Vinayak Kariappa Chettimada --- .../bluetooth/controller/Kconfig.ll_sw_split | 13 ++ subsys/bluetooth/controller/ll_sw/lll.h | 11 +- subsys/bluetooth/controller/ll_sw/lll_conn.h | 5 +- .../controller/ll_sw/nordic/lll/lll.c | 13 +- .../controller/ll_sw/nordic/lll/lll_central.c | 4 +- .../controller/ll_sw/nordic/lll/lll_conn.c | 34 +++- .../ll_sw/nordic/lll/lll_peripheral.c | 4 +- subsys/bluetooth/controller/ll_sw/ull_conn.c | 164 +++++++++++++++--- .../controller/ll_sw/ull_conn_types.h | 1 + .../bluetooth/controller/ll_sw/ull_internal.h | 8 +- 10 files changed, 215 insertions(+), 42 deletions(-) diff --git a/subsys/bluetooth/controller/Kconfig.ll_sw_split b/subsys/bluetooth/controller/Kconfig.ll_sw_split index 5924fcb6035..f5a2abe228a 100644 --- a/subsys/bluetooth/controller/Kconfig.ll_sw_split +++ b/subsys/bluetooth/controller/Kconfig.ll_sw_split @@ -1090,6 +1090,19 @@ config BT_CTLR_ALLOW_SAME_PEER_CONN WARNING: This option enables behavior that violates the Bluetooth specification. +config BT_CTLR_CONN_INTERVAL_LOW_LATENCY + bool "Allow low latency connection intervals" + help + Allow low latency connection intervals. + +config BT_CTLR_EVENT_IFS_LOW_LAT_US + prompt "Low latency tIFS value in microseconds" if BT_CTLR_CONN_INTERVAL_LOW_LATENCY + int + default 52 if BT_CTLR_CONN_INTERVAL_LOW_LATENCY + default 150 + help + Set low latency tIFS value. + endif # BT_CONN config BT_CTLR_ADV_INDICATION diff --git a/subsys/bluetooth/controller/ll_sw/lll.h b/subsys/bluetooth/controller/ll_sw/lll.h index 9a002e4852d..bfe9be02500 100644 --- a/subsys/bluetooth/controller/ll_sw/lll.h +++ b/subsys/bluetooth/controller/ll_sw/lll.h @@ -16,11 +16,12 @@ #define EVENT_PIPELINE_MAX 7 -#define ADV_INT_UNIT_US 625U -#define SCAN_INT_UNIT_US 625U -#define CONN_INT_UNIT_US 1250U -#define ISO_INT_UNIT_US CONN_INT_UNIT_US -#define PERIODIC_INT_UNIT_US CONN_INT_UNIT_US +#define ADV_INT_UNIT_US 625U +#define SCAN_INT_UNIT_US 625U +#define CONN_INT_UNIT_US 1250U +#define ISO_INT_UNIT_US CONN_INT_UNIT_US +#define PERIODIC_INT_UNIT_US CONN_INT_UNIT_US +#define CONN_LOW_LAT_INT_UNIT_US 500U #define ISO_INTERVAL_TO_US(interval) ((interval) * ISO_INT_UNIT_US) diff --git a/subsys/bluetooth/controller/ll_sw/lll_conn.h b/subsys/bluetooth/controller/ll_sw/lll_conn.h index 5e196c0ad5a..19a7820ed94 100644 --- a/subsys/bluetooth/controller/ll_sw/lll_conn.h +++ b/subsys/bluetooth/controller/ll_sw/lll_conn.h @@ -175,7 +175,10 @@ int lll_conn_reset(void); void lll_conn_flush(uint16_t handle, struct lll_conn *lll); void lll_conn_prepare_reset(void); -int lll_conn_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb); +int lll_conn_central_is_abort_cb(void *next, void *curr, + lll_prepare_cb_t *resume_cb); +int lll_conn_peripheral_is_abort_cb(void *next, void *curr, + lll_prepare_cb_t *resume_cb); void lll_conn_abort_cb(struct lll_prepare_param *prepare_param, void *param); void lll_conn_isr_rx(void *param); void lll_conn_isr_tx(void *param); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index de8cffb388a..ebfdf364941 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -1311,10 +1311,15 @@ preempt_find_preemptor: /* Check if current event want to continue */ err = event.curr.is_abort_cb(ready->prepare_param.param, event.curr.param, &resume_cb); - if (!err) { - /* Let preemptor LLL know about the cancelled prepare */ - ready->is_aborted = 1; - ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); + if (!err || (err == -EBUSY)) { + /* Returns -EBUSY when same curr and next state/role, do not + * abort same curr and next event. + */ + if (err != -EBUSY) { + /* Let preemptor LLL know about the cancelled prepare */ + ready->is_aborted = 1; + ready->abort_cb(&ready->prepare_param, ready->prepare_param.param); + } return; } 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 a66de337cfc..0fd8a368047 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_central.c @@ -75,8 +75,8 @@ void lll_central_prepare(void *param) LL_ASSERT(err >= 0); /* Invoke common pipeline handling of prepare */ - err = lll_prepare(lll_is_abort_cb, lll_conn_abort_cb, prepare_cb, 0, - param); + err = lll_prepare(lll_conn_central_is_abort_cb, lll_conn_abort_cb, + prepare_cb, 0, param); LL_ASSERT(!err || err == -EINPROGRESS); } 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 52a1f326c29..4f61cba9665 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_conn.c @@ -157,7 +157,9 @@ void lll_conn_prepare_reset(void) #endif /* CONFIG_BT_CTLR_LE_ENC */ } -int lll_conn_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) +#if defined(CONFIG_BT_CENTRAL) +int lll_conn_central_is_abort_cb(void *next, void *curr, + lll_prepare_cb_t *resume_cb) { struct lll_conn *lll = curr; @@ -166,8 +168,38 @@ int lll_conn_is_abort_cb(void *next, void *curr, lll_prepare_cb_t *resume_cb) return 0; } + /* Do not be aborted by same event if a single central trx has not been + * exchanged. + */ + if ((next == curr) && (trx_cnt < 1U)) { + return -EBUSY; + } + return -ECANCELED; } +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) +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; + } + + /* Do not be aborted by same event if a single peripheral trx has not + * been exchanged. + */ + if ((next == curr) && (trx_cnt <= 1U)) { + return -EBUSY; + } + + return -ECANCELED; +} +#endif /* CONFIG_BT_PERIPHERAL */ void lll_conn_abort_cb(struct lll_prepare_param *prepare_param, void *param) { 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 13e2adf05b1..c15ca24eb90 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_peripheral.c @@ -80,8 +80,8 @@ void lll_periph_prepare(void *param) lll = p->param; /* Invoke common pipeline handling of prepare */ - err = lll_prepare(lll_conn_is_abort_cb, lll_conn_abort_cb, prepare_cb, - 0U, p); + err = lll_prepare(lll_conn_peripheral_is_abort_cb, lll_conn_abort_cb, + prepare_cb, 0U, param); LL_ASSERT(!err || err == -EINPROGRESS); } diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c index c9ebeb9c274..93a610fa705 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c @@ -711,7 +711,13 @@ uint8_t ll_apto_get(uint16_t handle, uint16_t *apto) return BT_HCI_ERR_UNKNOWN_CONN_ID; } - *apto = conn->apto_reload * conn->lll.interval * 125U / 1000; + if (conn->lll.interval >= BT_HCI_LE_INTERVAL_MIN) { + *apto = conn->apto_reload * conn->lll.interval * + CONN_INT_UNIT_US / (10U * USEC_PER_MSEC); + } else { + *apto = conn->apto_reload * (conn->lll.interval + 1U) * + CONN_LOW_LAT_INT_UNIT_US / (10U * USEC_PER_MSEC); + } return 0; } @@ -725,9 +731,17 @@ uint8_t ll_apto_set(uint16_t handle, uint16_t apto) return BT_HCI_ERR_UNKNOWN_CONN_ID; } - conn->apto_reload = RADIO_CONN_EVENTS(apto * 10U * 1000U, - conn->lll.interval * - CONN_INT_UNIT_US); + if (conn->lll.interval >= BT_HCI_LE_INTERVAL_MIN) { + conn->apto_reload = + RADIO_CONN_EVENTS(apto * 10U * USEC_PER_MSEC, + conn->lll.interval * + CONN_INT_UNIT_US); + } else { + conn->apto_reload = + RADIO_CONN_EVENTS(apto * 10U * USEC_PER_MSEC, + (conn->lll.interval + 1U) * + CONN_LOW_LAT_INT_UNIT_US); + } return 0; } @@ -1061,8 +1075,17 @@ void ull_conn_done(struct node_rx_event_done *done) if (0) { #if defined(CONFIG_BT_PERIPHERAL) } else if (lll->role) { - ull_drift_ticks_get(done, &ticks_drift_plus, - &ticks_drift_minus); + if (!conn->periph.drift_skip) { + ull_drift_ticks_get(done, &ticks_drift_plus, + &ticks_drift_minus); + + if (ticks_drift_plus || ticks_drift_minus) { + conn->periph.drift_skip = + ull_ref_get(&conn->ull); + } + } else { + conn->periph.drift_skip--; + } if (!ull_tx_q_peek(&conn->tx_q)) { ull_conn_tx_demux(UINT8_MAX); @@ -1106,10 +1129,18 @@ void ull_conn_done(struct node_rx_event_done *done) else { /* Start supervision timeout, if not started already */ if (!conn->supervision_expire) { - const uint32_t conn_interval_us = conn->lll.interval * CONN_INT_UNIT_US; + uint32_t conn_interval_us; + + if (conn->lll.interval >= BT_HCI_LE_INTERVAL_MIN) { + conn_interval_us = conn->lll.interval * + CONN_INT_UNIT_US; + } else { + conn_interval_us = (conn->lll.interval + 1U) * + CONN_LOW_LAT_INT_UNIT_US; + } conn->supervision_expire = RADIO_CONN_EVENTS( - (conn->supervision_timeout * 10U * 1000U), + (conn->supervision_timeout * 10U * USEC_PER_MSEC), conn_interval_us); } } @@ -2186,17 +2217,22 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_ uint32_t win_offset_us, uint16_t interval, uint16_t latency, uint16_t timeout, uint16_t instant) { - struct lll_conn *lll; + uint16_t conn_interval_unit_old; + uint16_t conn_interval_unit_new; uint32_t ticks_win_offset = 0U; + uint16_t conn_interval_old_us; + uint16_t conn_interval_new_us; uint32_t ticks_slot_overhead; uint16_t conn_interval_old; uint16_t conn_interval_new; uint32_t conn_interval_us; + uint32_t ticks_at_expire; + uint16_t instant_latency; + uint32_t ready_delay_us; + uint16_t event_counter; uint32_t periodic_us; uint16_t latency_upd; - uint16_t instant_latency; - uint16_t event_counter; - uint32_t ticks_at_expire; + struct lll_conn *lll; lll = &conn->lll; @@ -2220,16 +2256,89 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_ } #endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */ +#if defined(CONFIG_BT_CTLR_PHY) + ready_delay_us = lll_radio_tx_ready_delay_get(lll->phy_tx, + lll->phy_flags); +#else + ready_delay_us = lll_radio_tx_ready_delay_get(0U, 0U); +#endif + /* compensate for instant_latency due to laziness */ - conn_interval_old = instant_latency * lll->interval; - latency_upd = conn_interval_old / interval; - conn_interval_new = latency_upd * interval; - if (conn_interval_new > conn_interval_old) { - ticks_at_expire += HAL_TICKER_US_TO_TICKS((conn_interval_new - conn_interval_old) * - CONN_INT_UNIT_US); + if (lll->interval >= BT_HCI_LE_INTERVAL_MIN) { + conn_interval_old = instant_latency * lll->interval; + conn_interval_unit_old = CONN_INT_UNIT_US; } else { - ticks_at_expire -= HAL_TICKER_US_TO_TICKS((conn_interval_old - conn_interval_new) * - CONN_INT_UNIT_US); + conn_interval_old = instant_latency * (lll->interval + 1U); + conn_interval_unit_old = CONN_LOW_LAT_INT_UNIT_US; + } + + if (interval >= BT_HCI_LE_INTERVAL_MIN) { + uint16_t max_tx_time; + uint16_t max_rx_time; + uint32_t slot_us; + + conn_interval_new = interval; + conn_interval_unit_new = CONN_INT_UNIT_US; + lll->tifs_tx_us = EVENT_IFS_DEFAULT_US; + lll->tifs_rx_us = EVENT_IFS_DEFAULT_US; + lll->tifs_hcto_us = EVENT_IFS_DEFAULT_US; + +#if defined(CONFIG_BT_CTLR_DATA_LENGTH) && \ + defined(CONFIG_BT_CTLR_SLOT_RESERVATION_UPDATE) + max_tx_time = lll->dle.eff.max_tx_time; + max_rx_time = lll->dle.eff.max_rx_time; + +#else /* !CONFIG_BT_CTLR_DATA_LENGTH || + * !CONFIG_BT_CTLR_SLOT_RESERVATION_UPDATE + */ + max_tx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M); + max_rx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M); +#if defined(CONFIG_BT_CTLR_PHY) + max_tx_time = MAX(max_tx_time, PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, lll->phy_tx)); + max_rx_time = MAX(max_rx_time, PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, lll->phy_rx)); +#endif /* !CONFIG_BT_CTLR_PHY */ +#endif /* !CONFIG_BT_CTLR_DATA_LENGTH || + * !CONFIG_BT_CTLR_SLOT_RESERVATION_UPDATE + */ + + /* Calculate event time reservation */ + slot_us = max_tx_time + max_rx_time; + slot_us += lll->tifs_rx_us + (EVENT_CLOCK_JITTER_US << 1); + slot_us += ready_delay_us; + + if (IS_ENABLED(CONFIG_BT_CTLR_EVENT_OVERHEAD_RESERVE_MAX) || + (lll->role == BT_HCI_ROLE_CENTRAL)) { + slot_us += EVENT_OVERHEAD_START_US + EVENT_OVERHEAD_END_US; + } + + conn->ull.ticks_slot = HAL_TICKER_US_TO_TICKS_CEIL(slot_us); + + } else { + conn_interval_new = interval + 1U; + conn_interval_unit_new = CONN_LOW_LAT_INT_UNIT_US; + lll->tifs_tx_us = CONFIG_BT_CTLR_EVENT_IFS_LOW_LAT_US; + lll->tifs_rx_us = CONFIG_BT_CTLR_EVENT_IFS_LOW_LAT_US; + lll->tifs_hcto_us = CONFIG_BT_CTLR_EVENT_IFS_LOW_LAT_US; + /* Reserve only the processing overhead, on overlap the + * is_abort_cb mechanism will ensure to continue the event so + * as to not loose anchor point sync. + */ + conn->ull.ticks_slot = + HAL_TICKER_US_TO_TICKS_CEIL(EVENT_OVERHEAD_START_US); + } + + conn_interval_us = conn_interval_new * conn_interval_unit_new; + periodic_us = conn_interval_us; + + conn_interval_old_us = conn_interval_old * conn_interval_unit_old; + latency_upd = conn_interval_old_us / conn_interval_us; + conn_interval_new_us = latency_upd * conn_interval_us; + if (conn_interval_new_us > conn_interval_old_us) { + ticks_at_expire += HAL_TICKER_US_TO_TICKS( + conn_interval_new_us - conn_interval_old_us); + } else { + ticks_at_expire -= HAL_TICKER_US_TO_TICKS( + conn_interval_old_us - conn_interval_new_us); } lll->latency_prepare += conn->llcp.prep.lazy; @@ -2238,15 +2347,14 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_ /* calculate the offset */ if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { ticks_slot_overhead = - MAX(conn->ull.ticks_active_to_start, conn->ull.ticks_prepare_to_start); + MAX(conn->ull.ticks_active_to_start, + conn->ull.ticks_prepare_to_start); + } else { ticks_slot_overhead = 0U; } /* calculate the window widening and interval */ - conn_interval_us = interval * CONN_INT_UNIT_US; - periodic_us = conn_interval_us; - switch (lll->role) { #if defined(CONFIG_BT_PERIPHERAL) case BT_HCI_ROLE_PERIPHERAL: @@ -2333,7 +2441,13 @@ void ull_conn_update_peer_sca(struct ll_conn *conn) lll = &conn->lll; /* calculate the window widening and interval */ - conn_interval_us = lll->interval * CONN_INT_UNIT_US; + if (lll->interval >= BT_HCI_LE_INTERVAL_MIN) { + conn_interval_us = lll->interval * + CONN_INT_UNIT_US; + } else { + conn_interval_us = (lll->interval + 1U) * + CONN_LOW_LAT_INT_UNIT_US; + } periodic_us = conn_interval_us; lll->periph.window_widening_periodic_us = diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h index 4b77c22b499..e105ef277e6 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn_types.h +++ b/subsys/bluetooth/controller/ll_sw/ull_conn_types.h @@ -205,6 +205,7 @@ struct ll_conn { #endif /* CONFIG_BT_CTLR_CONN_META */ uint8_t latency_cancel:1; uint8_t sca:3; + uint8_t drift_skip; uint32_t force; uint32_t ticks_to_offset; } periph; diff --git a/subsys/bluetooth/controller/ll_sw/ull_internal.h b/subsys/bluetooth/controller/ll_sw/ull_internal.h index 8218d1da61b..2f29fc2ec4b 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_internal.h @@ -8,11 +8,15 @@ * User CPR Interval */ #if !defined(CONFIG_BT_CTLR_USER_CPR_INTERVAL_MIN) +#if defined(CONFIG_BT_CTLR_CONN_INTERVAL_LOW_LATENCY) +#define CONN_INTERVAL_MIN(x) (0U) +#else /* !CONFIG_BT_CTLR_CONN_INTERVAL_LOW_LATENCY */ /* Bluetooth defined CPR Interval Minimum (7.5ms) */ -#define CONN_INTERVAL_MIN(x) (6) +#define CONN_INTERVAL_MIN(x) (6U) +#endif /* !CONFIG_BT_CTLR_CONN_INTERVAL_LOW_LATENCY */ #else /* CONFIG_BT_CTLR_USER_CPR_INTERVAL_MIN */ /* Proprietary user defined CPR Interval Minimum */ -#define CONN_INTERVAL_MIN(x) (MAX(ull_conn_interval_min_get(x), 1)) +#define CONN_INTERVAL_MIN(x) (MAX(ull_conn_interval_min_get(x), 1U)) #endif /* CONFIG_BT_CTLR_USER_CPR_INTERVAL_MIN */ /**