zephyr/subsys/bluetooth/controller/ll_sw/ull_scan.c
Vinayak Kariappa Chettimada f8c5e0d515 Bluetooth: Controller: Cleanup scan event LLL done handling
Cleanup the scan event LLL done handling for abort on late
schedule, preemption pipeline abort, preemption current
event yield, duration expire, and connection establishment.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
2021-11-03 16:52:58 +01:00

966 lines
22 KiB
C

/*
* Copyright (c) 2016-2019 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <soc.h>
#include <bluetooth/hci.h>
#include "hal/cpu.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_vendor.h"
#include "lll/lll_adv_types.h"
#include "lll_adv.h"
#include "lll/lll_adv_pdu.h"
#include "lll_scan.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_filter.h"
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_filter.h"
#include "ull_internal.h"
#include "ull_adv_internal.h"
#include "ull_scan_internal.h"
#include "ull_sched_internal.h"
#include "ll.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_scan
#include "common/log.h"
#include "hal/debug.h"
static int init_reset(void);
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param);
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_stop_ext_op_cb(uint32_t status, void *param);
static void ext_disable(void *param);
static void ext_disabled_cb(void *param);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
static struct ll_scan_set ll_scan[BT_CTLR_SCAN_SET];
uint8_t ll_scan_params_set(uint8_t type, uint16_t interval, uint16_t window,
uint8_t own_addr_type, uint8_t filter_policy)
{
struct ll_scan_set *scan;
struct lll_scan *lll;
scan = ull_scan_is_disabled_get(SCAN_HANDLE_1M);
if (!scan) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
uint8_t phy;
phy = type >> 1;
if (phy & BT_HCI_LE_EXT_SCAN_PHY_CODED) {
struct ll_scan_set *scan_coded;
if (!IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
scan_coded = ull_scan_is_disabled_get(SCAN_HANDLE_PHY_CODED);
if (!scan_coded) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
scan = scan_coded;
}
lll = &scan->lll;
/* NOTE: Pass invalid interval value to not start scanning using this
* scan instance.
*/
if (!interval) {
/* Set PHY to 0 to not start scanning on this instance */
lll->phy = 0U;
return 0;
}
lll->phy = phy;
#else /* !CONFIG_BT_CTLR_ADV_EXT */
lll = &scan->lll;
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
scan->own_addr_type = own_addr_type;
ull_scan_params_set(lll, type, interval, window, filter_policy);
return 0;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
uint8_t ll_scan_enable(uint8_t enable, uint16_t duration, uint16_t period)
{
struct node_rx_pdu *node_rx_scan_term = NULL;
uint8_t is_update_coded = 0U;
uint8_t is_update_1m = 0U;
#else /* !CONFIG_BT_CTLR_ADV_EXT */
uint8_t ll_scan_enable(uint8_t enable)
{
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
struct ll_scan_set *scan_coded = NULL;
uint8_t own_addr_type = 0U;
uint8_t is_coded_phy = 0U;
struct ll_scan_set *scan;
uint8_t err;
if (!enable) {
err = disable(SCAN_HANDLE_1M);
if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT) &&
IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) {
uint8_t err_coded;
err_coded = disable(SCAN_HANDLE_PHY_CODED);
if (!err_coded) {
err = 0U;
}
}
return err;
}
scan = ull_scan_is_disabled_get(SCAN_HANDLE_1M);
if (!scan) {
#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) {
is_update_coded = is_scan_update(SCAN_HANDLE_PHY_CODED,
duration, period, &scan_coded,
&node_rx_scan_term);
if (!is_update_coded) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
}
own_addr_type = scan_coded->own_addr_type;
is_coded_phy = (scan_coded->lll.phy &
BT_HCI_LE_EXT_SCAN_PHY_CODED);
#endif /* CONFIG_BT_CTLR_ADV_EXT && CONFIG_BT_CTLR_PHY_CODED */
if ((is_coded_phy && (own_addr_type & 0x1)) ||
(!is_coded_phy && (scan->own_addr_type & 0x1))) {
if (!mem_nz(ll_addr_get(BT_ADDR_LE_RANDOM), BDADDR_SIZE)) {
return BT_HCI_ERR_INVALID_PARAM;
}
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_CTLR_PHY_CODED)
if (!is_coded_phy || (scan->lll.phy & PHY_1M))
#endif /* CONFIG_BT_CTLR_PHY_CODED */
{
err = duration_period_setup(scan, duration, period,
&node_rx_scan_term);
if (err) {
return err;
}
}
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;
/* TODO: Privacy support in Advertising Extensions */
} else {
lll = &scan->lll;
own_addr_type = scan->own_addr_type;
}
ull_filter_scan_update(lll->filter_policy);
lll->rl_idx = FILTER_IDX_NONE;
lll->rpa_gen = 0;
if ((lll->type & 0x1) &&
(own_addr_type == BT_ADDR_LE_PUBLIC_ID ||
own_addr_type == BT_ADDR_LE_RANDOM_ID)) {
/* Generate RPAs if required */
ull_filter_rpa_update(false);
lll->rpa_gen = 1;
}
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_CTLR_PHY_CODED)
if (!is_coded_phy || (scan->lll.phy & PHY_1M))
#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 & PHY_1M))
#endif /* CONFIG_BT_CTLR_PHY_CODED */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
{
err = ull_scan_enable(scan);
if (err) {
return err;
}
}
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) {
return err;
}
}
return 0;
}
int ull_scan_init(void)
{
int err;
if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT)) {
err = ull_scan_aux_init();
if (err) {
return err;
}
}
err = init_reset();
if (err) {
return err;
}
return 0;
}
int ull_scan_reset(void)
{
uint8_t handle;
int err;
for (handle = 0U; handle < BT_CTLR_SCAN_SET; handle++) {
(void)disable(handle);
}
if (IS_ENABLED(CONFIG_BT_CTLR_ADV_EXT)) {
err = ull_scan_aux_reset();
if (err) {
return err;
}
}
err = init_reset();
if (err) {
return err;
}
return 0;
}
void ull_scan_params_set(struct lll_scan *lll, uint8_t type, uint16_t interval,
uint16_t window, uint8_t filter_policy)
{
/* type value:
* 0000b - legacy 1M passive
* 0001b - legacy 1M active
* 0010b - Ext. 1M passive
* 0011b - Ext. 1M active
* 0100b - invalid
* 0101b - invalid
* 0110b - invalid
* 0111b - invalid
* 1000b - Ext. Coded passive
* 1001b - Ext. Coded active
*/
lll->type = type;
lll->filter_policy = filter_policy;
lll->interval = interval;
lll->ticks_window = HAL_TICKER_US_TO_TICKS((uint64_t)window *
SCAN_INT_UNIT_US);
}
uint8_t ull_scan_enable(struct ll_scan_set *scan)
{
struct lll_scan *lll = &scan->lll;
uint32_t ticks_slot_overhead;
uint32_t volatile ret_cb;
uint32_t ticks_interval;
uint32_t ticks_anchor;
uint32_t ret;
lll->init_addr_type = scan->own_addr_type;
(void)ll_addr_read(lll->init_addr_type, lll->init_addr);
lll->chan = 0U;
lll->is_stop = 0U;
ull_hdr_init(&scan->ull);
lll_hdr_init(lll, scan);
ticks_interval = HAL_TICKER_US_TO_TICKS((uint64_t)lll->interval *
SCAN_INT_UNIT_US);
/* TODO: active_to_start feature port */
scan->ull.ticks_active_to_start = 0U;
scan->ull.ticks_prepare_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
scan->ull.ticks_preempt_to_start =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_PREEMPT_MIN_US);
if ((lll->ticks_window +
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US)) <
(ticks_interval -
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US))) {
scan->ull.ticks_slot =
(lll->ticks_window +
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US));
} else {
if (IS_ENABLED(CONFIG_BT_CTLR_SCAN_UNRESERVED)) {
scan->ull.ticks_slot = 0U;
} else {
scan->ull.ticks_slot = ticks_interval -
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US);
}
lll->ticks_window = 0U;
}
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = MAX(scan->ull.ticks_active_to_start,
scan->ull.ticks_prepare_to_start);
} else {
ticks_slot_overhead = 0U;
}
ticks_anchor = ticker_ticks_now_get();
#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_CTLR_SCHED_ADVANCED)
if (!lll->conn) {
uint32_t ticks_ref = 0U;
uint32_t offset_us = 0U;
ull_sched_after_mstr_slot_get(TICKER_USER_ID_THREAD,
(scan->ull.ticks_slot +
ticks_slot_overhead),
&ticks_ref, &offset_us);
/* Use the ticks_ref as scanner's anchor if a free time space
* after any central role is available (indicated by a non-zero
* offset_us value).
*/
if (offset_us) {
ticks_anchor = ticks_ref +
HAL_TICKER_US_TO_TICKS(offset_us);
}
}
#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_CTLR_SCHED_ADVANCED */
uint8_t handle = ull_scan_handle_get(scan);
ret_cb = TICKER_STATUS_BUSY;
ret = ticker_start(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_THREAD, TICKER_ID_SCAN_BASE + handle,
ticks_anchor, 0, ticks_interval,
HAL_TICKER_REMAINDER((uint64_t)lll->interval *
SCAN_INT_UNIT_US),
TICKER_NULL_LAZY,
(scan->ull.ticks_slot + ticks_slot_overhead),
ticker_cb, scan,
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;
}
scan->is_enabled = 1U;
#if defined(CONFIG_BT_CTLR_PRIVACY)
#if defined(CONFIG_BT_BROADCASTER)
if (!ull_adv_is_enabled_get(0))
#endif
{
ull_filter_adv_scan_state_cb(BIT(1));
}
#endif
return 0;
}
uint8_t ull_scan_disable(uint8_t handle, struct ll_scan_set *scan)
{
int err;
err = ull_ticker_stop_with_mark(TICKER_ID_SCAN_BASE + handle,
scan, &scan->lll);
LL_ASSERT(err == 0 || err == -EALREADY);
if (err) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
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;
/* Get reference to ULL context */
scan = CONTAINER_OF(done->param, struct ll_scan_set, ull);
lll = &scan->lll;
if (likely(scan->duration_lazy || !lll->duration_reload ||
lll->duration_expire)) {
return;
}
/* Prevent duplicate terminate event generation */
lll->duration_reload = 0U;
handle = ull_scan_handle_get(scan);
LL_ASSERT(handle < BT_CTLR_SCAN_SET);
#if defined(CONFIG_BT_CTLR_PHY_CODED)
/* Prevent duplicate terminate event 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->lll.duration_reload = 0U;
#endif /* CONFIG_BT_CTLR_PHY_CODED */
rx_hdr = (void *)scan->node_rx_scan_term;
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_stop_ext_op_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 & 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 & 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) {
return NULL;
}
return &ll_scan[handle];
}
uint8_t ull_scan_handle_get(struct ll_scan_set *scan)
{
return ((uint8_t *)scan - (uint8_t *)ll_scan) / sizeof(*scan);
}
uint8_t ull_scan_lll_handle_get(struct lll_scan *lll)
{
return ull_scan_handle_get((void *)lll->hdr.parent);
}
struct ll_scan_set *ull_scan_is_valid_get(struct ll_scan_set *scan)
{
if (((uint8_t *)scan < (uint8_t *)ll_scan) ||
((uint8_t *)scan > ((uint8_t *)ll_scan +
(sizeof(struct ll_scan_set) *
(BT_CTLR_SCAN_SET - 1))))) {
return NULL;
}
return scan;
}
struct ll_scan_set *ull_scan_is_enabled_get(uint8_t handle)
{
struct ll_scan_set *scan;
scan = ull_scan_set_get(handle);
if (!scan || !scan->is_enabled) {
return NULL;
}
return scan;
}
struct ll_scan_set *ull_scan_is_disabled_get(uint8_t handle)
{
struct ll_scan_set *scan;
scan = ull_scan_set_get(handle);
if (!scan || scan->is_enabled) {
return NULL;
}
return scan;
}
uint32_t ull_scan_is_enabled(uint8_t handle)
{
struct ll_scan_set *scan;
scan = ull_scan_is_enabled_get(handle);
if (!scan) {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
scan = ull_scan_set_get(handle);
return scan->per_scan.sync ? ULL_SCAN_IS_SYNC : 0U;
#else
return 0U;
#endif
}
return (((uint32_t)scan->is_enabled << scan->lll.type) |
#if defined(CONFIG_BT_CENTRAL)
(scan->lll.conn ? ULL_SCAN_IS_INITIATOR : 0U) |
#endif
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
(scan->per_scan.sync ? ULL_SCAN_IS_SYNC : 0U) |
#endif
0U);
}
uint32_t ull_scan_filter_pol_get(uint8_t handle)
{
struct ll_scan_set *scan;
scan = ull_scan_is_enabled_get(handle);
if (!scan) {
return 0;
}
return scan->lll.filter_policy;
}
static int init_reset(void)
{
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) && \
!defined(CONFIG_BT_CTLR_ADV_EXT)
ll_scan[0].lll.tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL && !CONFIG_BT_CTLR_ADV_EXT */
return 0;
}
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param)
{
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, lll_scan_prepare};
static struct lll_prepare_param p;
struct ll_scan_set *scan;
struct lll_scan *lll;
uint32_t ret;
uint8_t ref;
DEBUG_RADIO_PREPARE_O(1);
scan = param;
lll = &scan->lll;
/* Increment prepare reference count */
ref = ull_ref_inc(&scan->ull);
LL_ASSERT(ref);
/* Append timing parameters */
p.ticks_at_expire = ticks_at_expire;
p.remainder = remainder;
p.lazy = lazy;
p.param = lll;
p.force = force;
mfy.param = &p;
/* Kick LLL prepare */
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL,
0, &mfy);
LL_ASSERT(!ret);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
if (lll->duration_expire) {
uint16_t elapsed;
elapsed = lazy + 1;
if (lll->duration_expire > elapsed) {
lll->duration_expire -= elapsed;
} else {
if (scan->duration_lazy) {
uint8_t handle;
uint16_t duration_lazy;
duration_lazy = lll->duration_expire +
scan->duration_lazy - elapsed;
handle = ull_scan_handle_get(scan);
LL_ASSERT(handle < BT_CTLR_SCAN_SET);
ret = ticker_update(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_HIGH,
(TICKER_ID_SCAN_BASE +
handle), 0, 0, 0, 0,
duration_lazy, 0,
NULL, NULL);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
lll->duration_expire = 0U;
}
} 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 + handle),
0, 0, 0, 0, 1, 1, NULL, NULL);
LL_ASSERT((ret == TICKER_STATUS_SUCCESS) ||
(ret == TICKER_STATUS_BUSY));
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
DEBUG_RADIO_PREPARE_O(1);
}
#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)
{
*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;
}
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 =
ULL_SCAN_DURATION_TO_EVENTS(duration,
scan->lll.interval);
if (period) {
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
(duration >= ULL_SCAN_PERIOD_TO_DURATION(period))) {
return BT_HCI_ERR_INVALID_PARAM;
}
scan->duration_lazy =
ULL_SCAN_PERIOD_TO_EVENTS(period,
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;
}
return 0;
}
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;
scan->lll.duration_expire = 0U;
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_stop_ext_op_cb(uint32_t status, void *param)
{
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, ext_disable};
uint32_t ret;
/* Ignore if race between thread and ULL */
if (status != TICKER_STATUS_SUCCESS) {
/* TODO: detect race */
return;
}
/* Check if any pending LLL events that need to be aborted */
mfy.param = param;
ret = mayfly_enqueue(TICKER_USER_ID_ULL_LOW,
TICKER_USER_ID_ULL_HIGH, 0, &mfy);
LL_ASSERT(!ret);
}
static void ext_disable(void *param)
{
struct ll_scan_set *scan;
struct ull_hdr *hdr;
/* Check ref count to determine if any pending LLL events in pipeline */
scan = param;
hdr = &scan->ull;
if (ull_ref_get(hdr)) {
static memq_link_t link;
static struct mayfly mfy = {0, 0, &link, NULL, lll_disable};
uint32_t ret;
mfy.param = &scan->lll;
/* Setup disabled callback to be called when ref count
* returns to zero.
*/
LL_ASSERT(!hdr->disabled_cb);
hdr->disabled_param = mfy.param;
hdr->disabled_cb = ext_disabled_cb;
/* Trigger LLL disable */
ret = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH,
TICKER_USER_ID_LLL, 0, &mfy);
LL_ASSERT(!ret);
} else {
/* No pending LLL events */
ext_disabled_cb(&scan->lll);
}
}
static void ext_disabled_cb(void *param)
{
struct node_rx_hdr *rx_hdr;
struct ll_scan_set *scan;
struct lll_scan *lll;
/* Under race condition, if a connection has been established then
* node_rx is already utilized to send terminate event on connection
*/
lll = (void *)param;
scan = HDR_LLL2ULL(lll);
rx_hdr = (void *)scan->node_rx_scan_term;
if (!rx_hdr) {
return;
}
/* NOTE: parameters are already populated on disable,
* just enqueue here
*/
ll_rx_put(rx_hdr->link, rx_hdr);
ll_rx_sched();
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
static uint8_t disable(uint8_t handle)
{
struct ll_scan_set *scan;
uint8_t ret;
scan = ull_scan_is_enabled_get(handle);
if (!scan) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
#if defined(CONFIG_BT_CENTRAL)
if (scan->lll.conn) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
#endif
ret = ull_scan_disable(handle, scan);
if (ret) {
return ret;
}
scan->is_enabled = 0U;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
if (scan->node_rx_scan_term) {
struct node_rx_pdu *node_rx_scan_term =
(void *)scan->node_rx_scan_term;
scan->node_rx_scan_term = NULL;
ll_rx_link_release(node_rx_scan_term->hdr.link);
ll_rx_release(node_rx_scan_term);
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CTLR_PRIVACY)
#if defined(CONFIG_BT_BROADCASTER)
if (!ull_adv_is_enabled_get(0))
#endif
{
ull_filter_adv_scan_state_cb(0);
}
#endif
return 0;
}