/* * Copyright (c) 2016-2019 Nordic Semiconductor ASA * Copyright (c) 2016 Vinayak Kariappa Chettimada * * SPDX-License-Identifier: Apache-2.0 */ #include #include #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 #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; }