diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan.c index 9ac2367d1b8..ef2715661fc 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_scan.c @@ -314,17 +314,22 @@ static int is_abort_cb(void *next, int prio, void *curr, /* TODO: check prio */ if (next != curr) { - int err; +#if defined(CONFIG_BT_CTLR_ADV_EXT) + if (unlikely(!lll->duration_reload || lll->duration_expire)) +#endif /* CONFIG_BT_CTLR_ADV_EXT */ + { + int err; - /* wrap back after the pre-empter */ - *resume_cb = resume_prepare_cb; - *resume_prio = 0; /* TODO: */ + /* wrap back after the pre-empter */ + *resume_cb = resume_prepare_cb; + *resume_prio = 0; /* TODO: */ - /* Retain HF clock */ - err = lll_hfclock_on(); - LL_ASSERT(err >= 0); + /* Retain HF clock */ + err = lll_hfclock_on(); + LL_ASSERT(err >= 0); - return -EAGAIN; + return -EAGAIN; + } } #if defined(CONFIG_BT_CTLR_ADV_EXT) diff --git a/subsys/bluetooth/controller/ll_sw/ull.c b/subsys/bluetooth/controller/ll_sw/ull.c index 592fd868724..fe0c22b2860 100644 --- a/subsys/bluetooth/controller/ll_sw/ull.c +++ b/subsys/bluetooth/controller/ll_sw/ull.c @@ -637,11 +637,7 @@ void ll_rx_dequeue(void) case NODE_RX_TYPE_EXT_SCAN_TERMINATE: { - struct ll_scan_set *scan; - - scan = ull_scan_set_get(rx->handle); - - scan->is_enabled = 0U; + ull_scan_term_dequeue(rx->handle); } break; #endif /* CONFIG_BT_OBSERVER */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan.c b/subsys/bluetooth/controller/ll_sw/ull_scan.c index 2bd36e8377a..ef538ff97b7 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan.c +++ b/subsys/bluetooth/controller/ll_sw/ull_scan.c @@ -51,6 +51,14 @@ static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder, uint16_t laz static uint8_t disable(uint8_t handle); #if defined(CONFIG_BT_CTLR_ADV_EXT) +static uint8_t is_scan_update(uint8_t handle, uint16_t duration, + uint16_t period, struct ll_scan_set **scan, + struct node_rx_pdu **node_rx_scan_term); +static uint8_t duration_period_setup(struct ll_scan_set *scan, + uint16_t duration, uint16_t period, + struct node_rx_pdu **node_rx_scan_term); +static uint8_t duration_period_update(struct ll_scan_set *scan, + uint8_t is_update); static void ticker_op_ext_stop_cb(uint32_t status, void *param); static void ext_disabled_cb(void *param); #endif /* CONFIG_BT_CTLR_ADV_EXT */ @@ -114,11 +122,13 @@ uint8_t ll_scan_enable(uint8_t enable, uint16_t duration, uint16_t period) uint8_t ll_scan_enable(uint8_t enable) #endif /* !CONFIG_BT_CTLR_ADV_EXT */ { + struct node_rx_pdu *node_rx_scan_term = NULL; struct ll_scan_set *scan_coded = NULL; - struct ll_scan_set *scan; + uint8_t is_update_coded = 0U; uint8_t own_addr_type = 0U; uint8_t is_coded_phy = 0U; - struct lll_scan *lll; + uint8_t is_update_1m = 0U; + struct ll_scan_set *scan; uint8_t err; if (!enable) { @@ -139,13 +149,28 @@ uint8_t ll_scan_enable(uint8_t enable) scan = ull_scan_is_disabled_get(SCAN_HANDLE_1M); if (!scan) { - return BT_HCI_ERR_CMD_DISALLOWED; +#if defined(CONFIG_BT_CTLR_ADV_EXT) + is_update_1m = is_scan_update(SCAN_HANDLE_1M, duration, period, + &scan, &node_rx_scan_term); + if (!is_update_1m) +#endif /* CONFIG_BT_CTLR_ADV_EXT */ + { + return BT_HCI_ERR_CMD_DISALLOWED; + } } #if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_CTLR_PHY_CODED) scan_coded = ull_scan_is_disabled_get(SCAN_HANDLE_PHY_CODED); if (!scan_coded) { - return BT_HCI_ERR_CMD_DISALLOWED; +#if defined(CONFIG_BT_CTLR_ADV_EXT) + is_update_coded = is_scan_update(SCAN_HANDLE_PHY_CODED, + duration, period, &scan_coded, + &node_rx_scan_term); + if (!is_update_coded) +#endif /* CONFIG_BT_CTLR_ADV_EXT */ + { + return BT_HCI_ERR_CMD_DISALLOWED; + } } own_addr_type = scan_coded->own_addr_type; @@ -153,68 +178,38 @@ uint8_t ll_scan_enable(uint8_t enable) BT_HCI_LE_EXT_SCAN_PHY_CODED); #endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_CTLR_PHY_CODED */ - if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT) && is_coded_phy && - ((own_addr_type & 0x1) || - (scan->own_addr_type & 0x1))) { + if ((is_coded_phy && (own_addr_type & 0x1)) || + (!is_coded_phy && (scan->own_addr_type & 0x1))) { if (!mem_nz(ll_addr_get(1, NULL), BDADDR_SIZE)) { return BT_HCI_ERR_INVALID_PARAM; } } #if defined(CONFIG_BT_CTLR_ADV_EXT) - lll = &scan->lll; - if (duration) { - lll->duration_reload = ((uint32_t)duration * - EXT_SCAN_DURATION_UNIT_US / - SCAN_INTERVAL_UNIT_US) / - scan->lll.interval; - if (period) { - if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) && - (duration >= ((uint32_t)period * - EXT_SCAN_PERIOD_UNIT_US/ - EXT_SCAN_DURATION_UNIT_US))) { - return BT_HCI_ERR_INVALID_PARAM; - } - - scan->duration_lazy = ((uint32_t)period * - EXT_SCAN_PERIOD_UNIT_US / - SCAN_INTERVAL_UNIT_US) / - scan->lll.interval; - scan->duration_lazy -= lll->duration_reload; - scan->node_rx_scan_term = NULL; - } else { - struct node_rx_pdu *node_rx_scan_term; - void *link_scan_term; - - scan->duration_lazy = 0U; - - /* The alloc here used for ext scan termination event */ - link_scan_term = ll_rx_link_alloc(); - if (!link_scan_term) { - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; - } - - node_rx_scan_term = ll_rx_alloc(); - if (!node_rx_scan_term) { - ll_rx_link_release(link_scan_term); - - return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; - } - - node_rx_scan_term->hdr.link = (void *)link_scan_term; - scan->node_rx_scan_term = (void *)node_rx_scan_term; +#if defined(CONFIG_BT_CTLR_PHY_CODED) + if (!is_coded_phy || (scan->lll.phy & BT_HCI_LE_EXT_SCAN_PHY_1M)) +#endif /* CONFIG_BT_CTLR_PHY_CODED */ + { + err = duration_period_setup(scan, duration, period, + &node_rx_scan_term); + if (err) { + return err; } - } else { - lll->duration_reload = 0U; - scan->duration_lazy = 0U; - scan->node_rx_scan_term = NULL; } - lll->duration_expire = lll->duration_reload; -#else /* !CONFIG_BT_CTLR_ADV_EXT */ - ARG_UNUSED(lll); -#endif /* !CONFIG_BT_CTLR_ADV_EXT */ + + if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED) && + is_coded_phy) { + err = duration_period_setup(scan_coded, duration, period, + &node_rx_scan_term); + if (err) { + return err; + } + } +#endif /* CONFIG_BT_CTLR_ADV_EXT */ #if defined(CONFIG_BT_CTLR_PRIVACY) + struct lll_scan *lll; + if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT) && is_coded_phy) { lll = &scan_coded->lll; @@ -236,13 +231,35 @@ uint8_t ll_scan_enable(uint8_t enable) ull_filter_rpa_update(false); lll->rpa_gen = 1; } -#else /* !CONFIG_BT_CTLR_PRIVACY */ - ARG_UNUSED(lll); -#endif /* !CONFIG_BT_CTLR_PRIVACY */ +#endif /* CONFIG_BT_CTLR_PRIVACY */ -#if defined(CONFIG_BT_CTLR_ADV_EXT) && defined(CONFIG_BT_CTLR_PHY_CODED) +#if defined(CONFIG_BT_CTLR_ADV_EXT) +#if defined(CONFIG_BT_CTLR_PHY_CODED) if (!is_coded_phy || (scan->lll.phy & BT_HCI_LE_EXT_SCAN_PHY_1M)) -#endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_CTLR_PHY_CODED */ +#endif /* CONFIG_BT_CTLR_PHY_CODED */ + { + err = duration_period_update(scan, is_update_1m); + if (err) { + return err; + } else if (is_update_1m) { + return 0; + } + } + + if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED) && + is_coded_phy) { + err = duration_period_update(scan_coded, is_update_coded); + if (err) { + return err; + } else if (is_update_coded) { + return 0; + } + } + +#if defined(CONFIG_BT_CTLR_PHY_CODED) + if (!is_coded_phy || (scan->lll.phy & BT_HCI_LE_EXT_SCAN_PHY_1M)) +#endif /* CONFIG_BT_CTLR_PHY_CODED */ +#endif /* CONFIG_BT_CTLR_ADV_EXT */ { err = ull_scan_enable(scan); if (err) { @@ -251,6 +268,7 @@ uint8_t ll_scan_enable(uint8_t enable) } if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT) && + IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED) && is_coded_phy) { err = ull_scan_enable(scan_coded); if (err) { @@ -460,6 +478,92 @@ uint8_t ull_scan_disable(uint8_t handle, struct ll_scan_set *scan) return 0; } +#if defined(CONFIG_BT_CTLR_ADV_EXT) +void ull_scan_done(struct node_rx_event_done *done) +{ + struct node_rx_hdr *rx_hdr; + struct ll_scan_set *scan; + struct lll_scan *lll; + uint8_t handle; + uint32_t ret; + + lll = (void *)HDR_ULL2LLL(done->param); + scan = (void *)HDR_LLL2EVT(lll); + if (likely(scan->duration_lazy || !lll->duration_reload || + lll->duration_expire)) { + return; + } + + rx_hdr = (void *)scan->node_rx_scan_term; + if (!rx_hdr) { + /* Prevent generation if another scan instance already did so. + */ + return; + } + + handle = ull_scan_handle_get(scan); + LL_ASSERT(handle < BT_CTLR_SCAN_SET); + +#if defined(CONFIG_BT_CTLR_PHY_CODED) + /* Reset the singular node rx buffer, so that it does not get used if + * ull_scan_done get called by the other scan instance. + */ + struct ll_scan_set *scan_other; + + if (handle == SCAN_HANDLE_1M) { + scan_other = ull_scan_set_get(SCAN_HANDLE_PHY_CODED); + } else { + scan_other = ull_scan_set_get(SCAN_HANDLE_1M); + } + scan_other->node_rx_scan_term = NULL; +#endif /* CONFIG_BT_CTLR_PHY_CODED */ + + rx_hdr->type = NODE_RX_TYPE_EXT_SCAN_TERMINATE; + rx_hdr->handle = handle; + + ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, + (TICKER_ID_SCAN_BASE + handle), ticker_op_ext_stop_cb, + scan); + + LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || + (ret == TICKER_STATUS_BUSY)); +} + +void ull_scan_term_dequeue(uint8_t handle) +{ + struct ll_scan_set *scan; + + scan = ull_scan_set_get(handle); + LL_ASSERT(scan); + + scan->is_enabled = 0U; + +#if defined(CONFIG_BT_CTLR_PHY_CODED) + if (handle == SCAN_HANDLE_1M) { + struct ll_scan_set *scan_coded; + + scan_coded = ull_scan_set_get(SCAN_HANDLE_PHY_CODED); + if (scan_coded->lll.phy & BT_HCI_LE_EXT_SCAN_PHY_CODED) { + uint8_t err; + + err = disable(SCAN_HANDLE_PHY_CODED); + LL_ASSERT(!err); + } + } else { + struct ll_scan_set *scan_1m; + + scan_1m = ull_scan_set_get(SCAN_HANDLE_1M); + if (scan_1m->lll.phy & BT_HCI_LE_EXT_SCAN_PHY_1M) { + uint8_t err; + + err = disable(SCAN_HANDLE_1M); + LL_ASSERT(!err); + } + } +#endif /* CONFIG_BT_CTLR_PHY_CODED */ +} +#endif /* CONFIG_BT_CTLR_ADV_EXT */ + struct ll_scan_set *ull_scan_set_get(uint8_t handle) { if (handle >= BT_CTLR_SCAN_SET) { @@ -590,12 +694,16 @@ static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder, lll->duration_expire = 0U; } - } else if (lll->duration_reload) { + } else if (lll->duration_reload && lazy) { + uint8_t handle; + + handle = ull_scan_handle_get(scan); + LL_ASSERT(handle < BT_CTLR_SCAN_SET); + lll->duration_expire = lll->duration_reload; ret = ticker_update(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, - (TICKER_ID_SCAN_BASE + - ull_scan_handle_get(scan)), + (TICKER_ID_SCAN_BASE + handle), 0, 0, 0, 0, 1, 1, NULL, NULL); LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || (ret == TICKER_STATUS_BUSY)); @@ -622,34 +730,108 @@ static void ticker_cb(uint32_t ticks_at_expire, uint32_t remainder, } #if defined(CONFIG_BT_CTLR_ADV_EXT) -void ull_scan_done(struct node_rx_event_done *done) +static uint8_t is_scan_update(uint8_t handle, uint16_t duration, + uint16_t period, struct ll_scan_set **scan, + struct node_rx_pdu **node_rx_scan_term) { - struct node_rx_hdr *rx_hdr; - struct ll_scan_set *scan; - struct lll_scan *lll; - uint8_t handle; - uint32_t ret; + *scan = ull_scan_set_get(handle); + *node_rx_scan_term = (void *)(*scan)->node_rx_scan_term; + return duration && period && (*scan)->lll.duration_reload && + (*scan)->duration_lazy; +} - lll = (void *)HDR_ULL2LLL(done->param); - scan = (void *)HDR_LLL2EVT(lll); - if (likely(scan->duration_lazy || !lll->duration_reload || - lll->duration_expire)) { - return; +static uint8_t duration_period_setup(struct ll_scan_set *scan, + uint16_t duration, uint16_t period, + struct node_rx_pdu **node_rx_scan_term) +{ + struct lll_scan *lll; + + lll = &scan->lll; + if (duration) { + lll->duration_reload = ((uint32_t)duration * + EXT_SCAN_DURATION_UNIT_US / + SCAN_INTERVAL_UNIT_US) / + scan->lll.interval; + if (period) { + if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) && + (duration >= ((uint32_t)period * + EXT_SCAN_PERIOD_UNIT_US/ + EXT_SCAN_DURATION_UNIT_US))) { + return BT_HCI_ERR_INVALID_PARAM; + } + + scan->duration_lazy = ((uint32_t)period * + EXT_SCAN_PERIOD_UNIT_US / + SCAN_INTERVAL_UNIT_US) / + scan->lll.interval; + scan->duration_lazy -= lll->duration_reload; + scan->node_rx_scan_term = NULL; + } else { + struct node_rx_pdu *node_rx; + void *link_scan_term; + + scan->duration_lazy = 0U; + + if (*node_rx_scan_term) { + scan->node_rx_scan_term = + (void *)*node_rx_scan_term; + + return 0; + } + + /* The alloc here used for ext scan termination event */ + link_scan_term = ll_rx_link_alloc(); + if (!link_scan_term) { + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; + } + + node_rx = ll_rx_alloc(); + if (!node_rx) { + ll_rx_link_release(link_scan_term); + + return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; + } + + node_rx->hdr.link = (void *)link_scan_term; + scan->node_rx_scan_term = (void *)node_rx; + *node_rx_scan_term = node_rx; + } + } else { + lll->duration_reload = 0U; + scan->duration_lazy = 0U; + scan->node_rx_scan_term = NULL; } - handle = ull_scan_handle_get(scan); - LL_ASSERT(handle < BT_CTLR_SCAN_SET); + return 0; +} - rx_hdr = (void *)scan->node_rx_scan_term; - rx_hdr->type = NODE_RX_TYPE_EXT_SCAN_TERMINATE; - rx_hdr->handle = handle; +static uint8_t duration_period_update(struct ll_scan_set *scan, + uint8_t is_update) +{ + if (is_update) { + uint32_t volatile ret_cb; + uint32_t ret; - ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, - (TICKER_ID_SCAN_BASE + handle), ticker_op_ext_stop_cb, - scan); + scan->lll.duration_expire = 0U; - LL_ASSERT((ret == TICKER_STATUS_SUCCESS) || - (ret == TICKER_STATUS_BUSY)); + ret_cb = TICKER_STATUS_BUSY; + ret = ticker_update(TICKER_INSTANCE_ID_CTLR, + TICKER_USER_ID_THREAD, + (TICKER_ID_SCAN_BASE + + ull_scan_handle_get(scan)), + 0, 0, 0, 0, 1, 1, + ull_ticker_status_give, (void *)&ret_cb); + ret = ull_ticker_status_take(ret, &ret_cb); + if (ret != TICKER_STATUS_SUCCESS) { + return BT_HCI_ERR_CMD_DISALLOWED; + } + + return 0; + } else { + scan->lll.duration_expire = scan->lll.duration_reload; + } + + return 0; } static void ticker_op_ext_stop_cb(uint32_t status, void *param) @@ -657,6 +839,7 @@ static void ticker_op_ext_stop_cb(uint32_t status, void *param) static memq_link_t link; static struct mayfly mfy = {0, 0, &link, NULL, NULL}; struct ll_scan_set *scan; + struct ull_hdr *hdr; uint32_t ret; /* Ignore if race between thread and ULL */ @@ -667,11 +850,23 @@ static void ticker_op_ext_stop_cb(uint32_t status, void *param) } scan = param; - mfy.fp = ext_disabled_cb; + hdr = &scan->ull; mfy.param = &scan->lll; - ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW, TICKER_USER_ID_ULL_HIGH, 0, - &mfy); - LL_ASSERT(!ret); + if (ull_ref_get(hdr)) { + LL_ASSERT(!hdr->disabled_cb); + hdr->disabled_param = mfy.param; + hdr->disabled_cb = ext_disabled_cb; + + mfy.fp = lll_disable; + ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW, + TICKER_USER_ID_LLL, 0, &mfy); + LL_ASSERT(!ret); + } else { + mfy.fp = ext_disabled_cb; + ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW, + TICKER_USER_ID_ULL_HIGH, 0, &mfy); + LL_ASSERT(!ret); + } } static void ext_disabled_cb(void *param) diff --git a/subsys/bluetooth/controller/ll_sw/ull_scan_internal.h b/subsys/bluetooth/controller/ll_sw/ull_scan_internal.h index 747bbaf1f99..e785f839aad 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_scan_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_scan_internal.h @@ -26,6 +26,9 @@ uint8_t ull_scan_enable(struct ll_scan_set *scan); /* Disable scanning/initiating role */ uint8_t ull_scan_disable(uint8_t handle, struct ll_scan_set *scan); +/* Helper function to dequeue scan timeout event */ +void ull_scan_term_dequeue(uint8_t handle); + /* Return ll_scan_set context (unconditional) */ struct ll_scan_set *ull_scan_set_get(uint8_t handle);