| /* |
| * Copyright (c) 2016-2019 Nordic Semiconductor ASA |
| * Copyright (c) 2016 Vinayak Kariappa Chettimada |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr.h> |
| #include <bluetooth/hci.h> |
| |
| #include "hal/ccm.h" |
| #include "hal/ticker.h" |
| #include "hal/radio.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 "ll.h" |
| |
| #include "lll.h" |
| #include "lll_vendor.h" |
| #include "lll_adv.h" |
| #include "lll_scan.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" |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) |
| #define LOG_MODULE_NAME bt_ctlr_ull_scan |
| #include "common/log.h" |
| #include <soc.h> |
| #include "hal/debug.h" |
| |
| static int init_reset(void); |
| static void ticker_cb(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, |
| void *param); |
| static u8_t disable(u16_t handle); |
| |
| #define BT_CTLR_SCAN_MAX 1 |
| static struct ll_scan_set ll_scan[BT_CTLR_SCAN_MAX]; |
| |
| u8_t ll_scan_params_set(u8_t type, u16_t interval, u16_t window, |
| u8_t own_addr_type, u8_t filter_policy) |
| { |
| struct ll_scan_set *scan; |
| |
| scan = ull_scan_is_disabled_get(0); |
| if (!scan) { |
| return BT_HCI_ERR_CMD_DISALLOWED; |
| } |
| |
| scan->own_addr_type = own_addr_type; |
| |
| ull_scan_params_set(&scan->lll, type, interval, window, filter_policy); |
| |
| return 0; |
| } |
| |
| u8_t ll_scan_enable(u8_t enable) |
| { |
| struct ll_scan_set *scan; |
| |
| if (!enable) { |
| return disable(0); |
| } |
| |
| scan = ull_scan_is_disabled_get(0); |
| if (!scan) { |
| return BT_HCI_ERR_CMD_DISALLOWED; |
| } |
| |
| if (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_PRIVACY) |
| struct lll_scan *lll = &scan->lll; |
| ull_filter_scan_update(lll->filter_policy); |
| |
| lll->rl_idx = FILTER_IDX_NONE; |
| lll->rpa_gen = 0; |
| |
| if ((lll->type & 0x1) && |
| (scan->own_addr_type == BT_ADDR_LE_PUBLIC_ID || |
| scan->own_addr_type == BT_ADDR_LE_RANDOM_ID)) { |
| /* Generate RPAs if required */ |
| ull_filter_rpa_update(false); |
| lll->rpa_gen = 1; |
| } |
| #endif |
| |
| return ull_scan_enable(scan); |
| } |
| |
| int ull_scan_init(void) |
| { |
| int err; |
| |
| err = init_reset(); |
| if (err) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| int ull_scan_reset(void) |
| { |
| u16_t handle; |
| int err; |
| |
| for (handle = 0U; handle < BT_CTLR_SCAN_MAX; handle++) { |
| (void)disable(handle); |
| } |
| |
| err = init_reset(); |
| if (err) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void ull_scan_params_set(struct lll_scan *lll, u8_t type, u16_t interval, |
| u16_t window, u8_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; |
| |
| #if defined(CONFIG_BT_CTLR_ADV_EXT) |
| lll->phy = type >> 1; |
| #endif /* CONFIG_BT_CTLR_ADV_EXT */ |
| |
| lll->filter_policy = filter_policy; |
| lll->interval = interval; |
| lll->ticks_window = HAL_TICKER_US_TO_TICKS((u64_t)window * 625U); |
| } |
| |
| u8_t ull_scan_enable(struct ll_scan_set *scan) |
| { |
| volatile u32_t ret_cb = TICKER_STATUS_BUSY; |
| struct lll_scan *lll = &scan->lll; |
| u32_t ticks_slot_overhead; |
| u32_t ticks_slot_offset; |
| u32_t ticks_interval; |
| u32_t ticks_anchor; |
| u32_t ret; |
| |
| lll->chan = 0; |
| lll->init_addr_type = scan->own_addr_type; |
| ll_addr_get(lll->init_addr_type, lll->init_addr); |
| |
| #if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL) |
| lll->tx_pwr_lvl = RADIO_TXP_DEFAULT; |
| #endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */ |
| |
| ull_hdr_init(&scan->ull); |
| lll_hdr_init(lll, scan); |
| |
| ticks_interval = HAL_TICKER_US_TO_TICKS((u64_t)lll->interval * 625U); |
| |
| /* TODO: active_to_start feature port */ |
| scan->evt.ticks_active_to_start = 0U; |
| scan->evt.ticks_xtal_to_start = |
| HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US); |
| scan->evt.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->evt.ticks_slot = |
| (lll->ticks_window + |
| HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US)); |
| } else { |
| scan->evt.ticks_slot = |
| (ticks_interval - |
| HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_XTAL_US)); |
| lll->ticks_window = 0; |
| } |
| |
| ticks_slot_offset = MAX(scan->evt.ticks_active_to_start, |
| scan->evt.ticks_xtal_to_start); |
| |
| if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { |
| ticks_slot_overhead = ticks_slot_offset; |
| } 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) { |
| u32_t ticks_ref = 0U; |
| u32_t offset_us = 0U; |
| |
| ull_sched_after_mstr_slot_get(TICKER_USER_ID_THREAD, |
| (ticks_slot_offset + |
| scan->evt.ticks_slot), |
| &ticks_ref, &offset_us); |
| |
| /* Use the ticks_ref as scanner's anchor if a free time space |
| * after any master 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 */ |
| |
| ret = ticker_start(TICKER_INSTANCE_ID_CTLR, |
| TICKER_USER_ID_THREAD, TICKER_ID_SCAN_BASE, |
| ticks_anchor, 0, ticks_interval, |
| HAL_TICKER_REMAINDER((u64_t)lll->interval * 625U), |
| TICKER_NULL_LAZY, |
| (scan->evt.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; |
| } |
| |
| u8_t ull_scan_disable(u16_t handle, struct ll_scan_set *scan) |
| { |
| volatile u32_t ret_cb = TICKER_STATUS_BUSY; |
| void *mark; |
| u32_t ret; |
| |
| mark = ull_disable_mark(scan); |
| LL_ASSERT(mark == scan); |
| |
| ret = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_THREAD, |
| TICKER_ID_SCAN_BASE + handle, |
| ull_ticker_status_give, (void *)&ret_cb); |
| |
| ret = ull_ticker_status_take(ret, &ret_cb); |
| if (ret) { |
| mark = ull_disable_unmark(scan); |
| LL_ASSERT(mark == scan); |
| |
| return BT_HCI_ERR_CMD_DISALLOWED; |
| } |
| |
| ret = ull_disable(&scan->lll); |
| LL_ASSERT(!ret); |
| |
| mark = ull_disable_unmark(scan); |
| LL_ASSERT(mark == scan); |
| |
| return 0; |
| } |
| |
| struct ll_scan_set *ull_scan_set_get(u16_t handle) |
| { |
| if (handle >= BT_CTLR_SCAN_MAX) { |
| return NULL; |
| } |
| |
| return &ll_scan[handle]; |
| } |
| |
| u16_t ull_scan_handle_get(struct ll_scan_set *scan) |
| { |
| return ((u8_t *)scan - (u8_t *)ll_scan) / sizeof(*scan); |
| } |
| |
| u16_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_enabled_get(u16_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(u16_t handle) |
| { |
| struct ll_scan_set *scan; |
| |
| scan = ull_scan_set_get(handle); |
| if (!scan || scan->is_enabled) { |
| return NULL; |
| } |
| |
| return scan; |
| } |
| |
| u32_t ull_scan_is_enabled(u16_t handle) |
| { |
| struct ll_scan_set *scan; |
| |
| scan = ull_scan_is_enabled_get(handle); |
| if (!scan) { |
| return 0; |
| } |
| |
| /* NOTE: BIT(0) - passive scanning enabled |
| * BIT(1) - active scanning enabled |
| * BIT(2) - initiator enabled |
| */ |
| return (((u32_t)scan->is_enabled << scan->lll.type) | |
| #if defined(CONFIG_BT_CENTRAL) |
| (scan->lll.conn ? BIT(2) : 0) | |
| #endif |
| 0); |
| } |
| |
| u32_t ull_scan_filter_pol_get(u16_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) |
| { |
| return 0; |
| } |
| |
| static void ticker_cb(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, |
| 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 = param; |
| u32_t ret; |
| u8_t ref; |
| |
| DEBUG_RADIO_PREPARE_O(1); |
| |
| /* 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 = &scan->lll; |
| 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_CENTRAL) && defined(CONFIG_BT_CTLR_SCHED_ADVANCED) |
| /* calc next group in us for the anchor where first connection event |
| * to be placed |
| */ |
| if (scan->lll.conn) { |
| static memq_link_t s_link; |
| static struct mayfly s_mfy_sched_after_mstr_offset_get = { |
| 0, 0, &s_link, NULL, |
| ull_sched_mfy_after_mstr_offset_get}; |
| u32_t retval; |
| |
| s_mfy_sched_after_mstr_offset_get.param = (void *)scan; |
| |
| retval = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, |
| TICKER_USER_ID_ULL_LOW, 1, |
| &s_mfy_sched_after_mstr_offset_get); |
| LL_ASSERT(!retval); |
| } |
| #endif /* CONFIG_BT_CENTRAL && CONFIG_BT_CTLR_SCHED_ADVANCED */ |
| |
| DEBUG_RADIO_PREPARE_O(1); |
| } |
| |
| static u8_t disable(u16_t handle) |
| { |
| struct ll_scan_set *scan; |
| u8_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_PRIVACY) |
| #if defined(CONFIG_BT_BROADCASTER) |
| if (!ull_adv_is_enabled_get(0)) |
| #endif |
| { |
| ull_filter_adv_scan_state_cb(0); |
| } |
| #endif |
| |
| return 0; |
| } |