From be6797ed64f6d8f72b5a0d9c713cec3875eb3a2e Mon Sep 17 00:00:00 2001 From: Joakim Andersson Date: Mon, 8 Mar 2021 14:30:19 +0100 Subject: [PATCH] Bluetooth: host: Refactor out scan roles from hci_core to scan Refactor out the scan roles handling from hci_core.c to its own source file in scan.c. Scan roles consists of regular scanning and synchronization to periodic advertiser. Signed-off-by: Joakim Andersson --- subsys/bluetooth/host/CMakeLists.txt | 4 + subsys/bluetooth/host/hci_core.c | 1577 +------------------------- subsys/bluetooth/host/hci_core.h | 13 +- subsys/bluetooth/host/id.c | 1 + subsys/bluetooth/host/scan.c | 1562 +++++++++++++++++++++++++ subsys/bluetooth/host/scan.h | 12 + 6 files changed, 1608 insertions(+), 1561 deletions(-) create mode 100644 subsys/bluetooth/host/scan.c create mode 100644 subsys/bluetooth/host/scan.h diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index 047129e913e..fdcdb4d79da 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -36,6 +36,10 @@ if(CONFIG_BT_HCI_HOST) hci_common.c id.c ) + zephyr_library_sources_ifdef( + CONFIG_BT_OBSERVER + scan.c + ) zephyr_library_sources_ifdef( CONFIG_BT_HOST_CRYPTO crypto.c diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 35c30afd3ef..59fbb425097 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -39,6 +39,7 @@ #include "hci_ecc.h" #include "ecc.h" #include "id.h" +#include "scan.h" #include "conn_internal.h" #include "iso_internal.h" @@ -88,20 +89,8 @@ struct bt_dev bt_dev = { static bt_ready_cb_t ready_cb; -static bt_le_scan_cb_t *scan_dev_found_cb; - -#if defined(CONFIG_BT_OBSERVER) -static sys_slist_t scan_cbs = SYS_SLIST_STATIC_INIT(&scan_cbs); -#endif /* defined(CONFIG_BT_OBSERVER) */ - #if defined(CONFIG_BT_EXT_ADV) static struct bt_le_ext_adv adv_pool[CONFIG_BT_EXT_ADV_MAX_ADV_SET]; - -#if defined(CONFIG_BT_PER_ADV_SYNC) -static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void); -static struct bt_le_per_adv_sync per_adv_sync_pool[CONFIG_BT_PER_ADV_SYNC_MAX]; -static sys_slist_t pa_sync_cbs = SYS_SLIST_STATIC_INIT(&pa_sync_cbs); -#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ #endif /* defined(CONFIG_BT_EXT_ADV) */ #if defined(CONFIG_BT_HCI_VS_EVT_USER) @@ -532,88 +521,6 @@ static void adv_id_check_connectable_func(struct bt_le_ext_adv *adv, void *data) } } -#if defined(CONFIG_BT_OBSERVER) -static int set_le_ext_scan_enable(uint8_t enable, uint16_t duration) -{ - struct bt_hci_cp_le_set_ext_scan_enable *cp; - struct bt_hci_cmd_state_set state; - struct net_buf *buf; - int err; - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - - if (enable == BT_HCI_LE_SCAN_ENABLE) { - cp->filter_dup = atomic_test_bit(bt_dev.flags, - BT_DEV_SCAN_FILTER_DUP); - } else { - cp->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; - } - - cp->enable = enable; - cp->duration = sys_cpu_to_le16(duration); - cp->period = 0; - - bt_hci_cmd_state_set_init(buf, &state, bt_dev.flags, BT_DEV_SCANNING, - enable == BT_HCI_LE_SCAN_ENABLE); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, buf, NULL); - if (err) { - return err; - } - - return 0; -} - -static int bt_le_scan_set_enable_legacy(uint8_t enable) -{ - struct bt_hci_cp_le_set_scan_enable *cp; - struct bt_hci_cmd_state_set state; - struct net_buf *buf; - int err; - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - - if (enable == BT_HCI_LE_SCAN_ENABLE) { - cp->filter_dup = atomic_test_bit(bt_dev.flags, - BT_DEV_SCAN_FILTER_DUP); - } else { - cp->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; - } - - cp->enable = enable; - - bt_hci_cmd_state_set_init(buf, &state, bt_dev.flags, BT_DEV_SCANNING, - enable == BT_HCI_LE_SCAN_ENABLE); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, NULL); - if (err) { - return err; - } - - return 0; -} - -int bt_le_scan_set_enable(uint8_t enable) -{ - if (IS_ENABLED(CONFIG_BT_EXT_ADV) && - BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { - return set_le_ext_scan_enable(enable, 0); - } - - return bt_le_scan_set_enable_legacy(enable); -} -#endif /* CONFIG_BT_OBSERVER */ - static int hci_le_read_max_data_len(uint16_t *tx_octets, uint16_t *tx_time) { struct bt_hci_rp_le_read_max_data_len *rp; @@ -634,9 +541,7 @@ static int hci_le_read_max_data_len(uint16_t *tx_octets, uint16_t *tx_time) return 0; } -#if (defined(CONFIG_BT_OBSERVER) && defined(CONFIG_BT_EXT_ADV)) \ - || defined(CONFIG_BT_USER_PHY_UPDATE) -static uint8_t get_phy(uint8_t hci_phy) +uint8_t bt_get_phy(uint8_t hci_phy) { switch (hci_phy) { case BT_HCI_LE_PHY_1M: @@ -649,7 +554,6 @@ static uint8_t get_phy(uint8_t hci_phy) return 0; } } -#endif /* (BT_OBSERVER && BT_EXT_ADV) || USER_PHY_UPDATE */ #if defined(CONFIG_BT_CONN) static void hci_acl(struct net_buf *buf) @@ -1117,8 +1021,8 @@ static int hci_le_read_phy(struct bt_conn *conn) } rp = (void *)rsp->data; - conn->le.phy.tx_phy = get_phy(rp->tx_phy); - conn->le.phy.rx_phy = get_phy(rp->rx_phy); + conn->le.phy.tx_phy = bt_get_phy(rp->tx_phy); + conn->le.phy.rx_phy = bt_get_phy(rp->rx_phy); net_buf_unref(rsp); return 0; @@ -1655,8 +1559,8 @@ static void le_phy_update_complete(struct net_buf *buf) } #if defined(CONFIG_BT_USER_PHY_UPDATE) - conn->le.phy.tx_phy = get_phy(evt->tx_phy); - conn->le.phy.rx_phy = get_phy(evt->rx_phy); + conn->le.phy.tx_phy = bt_get_phy(evt->tx_phy); + conn->le.phy.rx_phy = bt_get_phy(evt->rx_phy); notify_le_phy_updated(conn); #endif @@ -1796,50 +1700,6 @@ static void le_conn_update_complete(struct net_buf *buf) bt_conn_unref(conn); } -#if defined(CONFIG_BT_CENTRAL) -static void check_pending_conn(const bt_addr_le_t *id_addr, - const bt_addr_le_t *addr, uint8_t adv_props) -{ - struct bt_conn *conn; - - /* No connections are allowed during explicit scanning */ - if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { - return; - } - - /* Return if event is not connectable */ - if (!(adv_props & BT_HCI_LE_ADV_EVT_TYPE_CONN)) { - return; - } - - conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, id_addr, - BT_CONN_CONNECT_SCAN); - if (!conn) { - return; - } - - if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && - bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE)) { - goto failed; - } - - bt_addr_le_copy(&conn->le.resp_addr, addr); - if (bt_le_create_conn(conn)) { - goto failed; - } - - bt_conn_set_state(conn, BT_CONN_CONNECT); - bt_conn_unref(conn); - return; - -failed: - conn->err = BT_HCI_ERR_UNSPECIFIED; - bt_conn_set_state(conn, BT_CONN_DISCONNECTED); - bt_conn_unref(conn); - bt_le_scan_update(false); -} -#endif /* CONFIG_BT_CENTRAL */ - #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) static int set_flow_control(void) { @@ -2925,7 +2785,10 @@ static void hci_reset_complete(struct net_buf *buf) return; } - scan_dev_found_cb = NULL; + if (IS_ENABLED(CONFIG_BT_OBSERVER)) { + bt_scan_reset(); + } + #if defined(CONFIG_BT_BREDR) discovery_cb = NULL; discovery_results = NULL; @@ -3011,786 +2874,6 @@ static void hci_cmd_status(struct net_buf *buf) } } -#if defined(CONFIG_BT_OBSERVER) -static int start_le_scan_ext(struct bt_hci_ext_scan_phy *phy_1m, - struct bt_hci_ext_scan_phy *phy_coded, - uint16_t duration) -{ - struct bt_hci_cp_le_set_ext_scan_param *set_param; - struct net_buf *buf; - uint8_t own_addr_type; - bool active_scan; - int err; - - active_scan = (phy_1m && phy_1m->type == BT_HCI_LE_SCAN_ACTIVE) || - (phy_coded && phy_coded->type == BT_HCI_LE_SCAN_ACTIVE); - - if (duration > 0) { - atomic_set_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED); - - /* Allow bt_le_oob_get_local to be called directly before - * starting a scan limited by timeout. - */ - if (IS_ENABLED(CONFIG_BT_PRIVACY) && !bt_id_rpa_is_new()) { - atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); - } - } - - err = bt_id_set_scan_own_addr(active_scan, &own_addr_type); - if (err) { - return err; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_SCAN_PARAM, - sizeof(*set_param) + - (phy_1m ? sizeof(*phy_1m) : 0) + - (phy_coded ? sizeof(*phy_coded) : 0)); - if (!buf) { - return -ENOBUFS; - } - - set_param = net_buf_add(buf, sizeof(*set_param)); - set_param->own_addr_type = own_addr_type; - set_param->phys = 0; - - if (IS_ENABLED(CONFIG_BT_WHITELIST) && - atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_WL)) { - set_param->filter_policy = BT_HCI_LE_SCAN_FP_USE_WHITELIST; - } else { - set_param->filter_policy = BT_HCI_LE_SCAN_FP_NO_WHITELIST; - } - - if (phy_1m) { - set_param->phys |= BT_HCI_LE_EXT_SCAN_PHY_1M; - net_buf_add_mem(buf, phy_1m, sizeof(*phy_1m)); - } - - if (phy_coded) { - set_param->phys |= BT_HCI_LE_EXT_SCAN_PHY_CODED; - net_buf_add_mem(buf, phy_coded, sizeof(*phy_coded)); - } - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_SCAN_PARAM, buf, NULL); - if (err) { - return err; - } - - err = set_le_ext_scan_enable(BT_HCI_LE_SCAN_ENABLE, duration); - if (err) { - return err; - } - - atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, active_scan); - - return 0; -} - -static int start_le_scan_legacy(uint8_t scan_type, uint16_t interval, uint16_t window) -{ - struct bt_hci_cp_le_set_scan_param set_param; - struct net_buf *buf; - int err; - bool active_scan; - - (void)memset(&set_param, 0, sizeof(set_param)); - - set_param.scan_type = scan_type; - - /* for the rest parameters apply default values according to - * spec 4.2, vol2, part E, 7.8.10 - */ - set_param.interval = sys_cpu_to_le16(interval); - set_param.window = sys_cpu_to_le16(window); - - if (IS_ENABLED(CONFIG_BT_WHITELIST) && - atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_WL)) { - set_param.filter_policy = BT_HCI_LE_SCAN_FP_USE_WHITELIST; - } else { - set_param.filter_policy = BT_HCI_LE_SCAN_FP_NO_WHITELIST; - } - - active_scan = scan_type == BT_HCI_LE_SCAN_ACTIVE; - err = bt_id_set_scan_own_addr(active_scan, &set_param.addr_type); - if (err) { - return err; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAM, sizeof(set_param)); - if (!buf) { - return -ENOBUFS; - } - - net_buf_add_mem(buf, &set_param, sizeof(set_param)); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_PARAM, buf, NULL); - if (err) { - return err; - } - - err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_ENABLE); - if (err) { - return err; - } - - atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, active_scan); - - return 0; -} - -static int start_passive_scan(bool fast_scan) -{ - uint16_t interval, window; - - if (fast_scan) { - interval = BT_GAP_SCAN_FAST_INTERVAL; - window = BT_GAP_SCAN_FAST_WINDOW; - } else { - interval = CONFIG_BT_BACKGROUND_SCAN_INTERVAL; - window = CONFIG_BT_BACKGROUND_SCAN_WINDOW; - } - - if (IS_ENABLED(CONFIG_BT_EXT_ADV) && - BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { - struct bt_hci_ext_scan_phy scan; - - scan.type = BT_HCI_LE_SCAN_PASSIVE; - scan.interval = sys_cpu_to_le16(interval); - scan.window = sys_cpu_to_le16(window); - - return start_le_scan_ext(&scan, NULL, 0); - } - - return start_le_scan_legacy(BT_HCI_LE_SCAN_PASSIVE, interval, window); -} - -int bt_le_scan_update(bool fast_scan) -{ - if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { - return 0; - } - - if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { - int err; - - err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE); - if (err) { - return err; - } - } - - if (IS_ENABLED(CONFIG_BT_CENTRAL)) { - struct bt_conn *conn; - - /* don't restart scan if we have pending connection */ - conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, - BT_CONN_CONNECT); - if (conn) { - bt_conn_unref(conn); - return 0; - } - - conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, - BT_CONN_CONNECT_SCAN); - if (conn) { - atomic_set_bit(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP); - - bt_conn_unref(conn); - - return start_passive_scan(fast_scan); - } - } - -#if defined(CONFIG_BT_PER_ADV_SYNC) - if (get_pending_per_adv_sync()) { - return start_passive_scan(fast_scan); - } -#endif - - return 0; -} - -void bt_data_parse(struct net_buf_simple *ad, - bool (*func)(struct bt_data *data, void *user_data), - void *user_data) -{ - while (ad->len > 1) { - struct bt_data data; - uint8_t len; - - len = net_buf_simple_pull_u8(ad); - if (len == 0U) { - /* Early termination */ - return; - } - - if (len > ad->len) { - BT_WARN("Malformed data"); - return; - } - - data.type = net_buf_simple_pull_u8(ad); - data.data_len = len - 1; - data.data = ad->data; - - if (!func(&data, user_data)) { - return; - } - - net_buf_simple_pull(ad, len - 1); - } -} - -/* Convert Legacy adv report evt_type field to adv props */ -static uint8_t get_adv_props(uint8_t evt_type) -{ - switch (evt_type) { - case BT_GAP_ADV_TYPE_ADV_IND: - return BT_GAP_ADV_PROP_CONNECTABLE | - BT_GAP_ADV_PROP_SCANNABLE; - - case BT_GAP_ADV_TYPE_ADV_DIRECT_IND: - return BT_GAP_ADV_PROP_CONNECTABLE | - BT_GAP_ADV_PROP_DIRECTED; - - case BT_GAP_ADV_TYPE_ADV_SCAN_IND: - return BT_GAP_ADV_PROP_SCANNABLE; - - case BT_GAP_ADV_TYPE_ADV_NONCONN_IND: - return 0; - - /* In legacy advertising report, we don't know if the scan - * response come from a connectable advertiser, so don't - * set connectable property bit. - */ - case BT_GAP_ADV_TYPE_SCAN_RSP: - return BT_GAP_ADV_PROP_SCAN_RESPONSE | - BT_GAP_ADV_PROP_SCANNABLE; - - default: - return 0; - } -} - -static void le_adv_recv(bt_addr_le_t *addr, struct bt_le_scan_recv_info *info, - struct net_buf *buf, uint8_t len) -{ - struct bt_le_scan_cb *listener, *next; - struct net_buf_simple_state state; - bt_addr_le_t id_addr; - - BT_DBG("%s event %u, len %u, rssi %d dBm", bt_addr_le_str(addr), - info->adv_type, len, info->rssi); - - if (!IS_ENABLED(CONFIG_BT_PRIVACY) && - !IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) && - atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN) && - (info->adv_props & BT_HCI_LE_ADV_PROP_DIRECT)) { - BT_DBG("Dropped direct adv report"); - return; - } - - if (addr->type == BT_ADDR_LE_PUBLIC_ID || - addr->type == BT_ADDR_LE_RANDOM_ID) { - bt_addr_le_copy(&id_addr, addr); - id_addr.type -= BT_ADDR_LE_PUBLIC_ID; - } else if (addr->type == BT_HCI_PEER_ADDR_ANONYMOUS) { - bt_addr_le_copy(&id_addr, BT_ADDR_LE_ANY); - } else { - bt_addr_le_copy(&id_addr, - bt_lookup_id_addr(BT_ID_DEFAULT, addr)); - } - - info->addr = &id_addr; - - if (scan_dev_found_cb) { - net_buf_simple_save(&buf->b, &state); - - buf->len = len; - scan_dev_found_cb(&id_addr, info->rssi, info->adv_type, - &buf->b); - - net_buf_simple_restore(&buf->b, &state); - } - - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&scan_cbs, listener, next, node) { - if (listener->recv) { - net_buf_simple_save(&buf->b, &state); - - buf->len = len; - listener->recv(info, &buf->b); - - net_buf_simple_restore(&buf->b, &state); - } - } - -#if defined(CONFIG_BT_CENTRAL) - check_pending_conn(&id_addr, addr, info->adv_props); -#endif /* CONFIG_BT_CENTRAL */ -} - -#if defined(CONFIG_BT_EXT_ADV) -static void le_scan_timeout(struct net_buf *buf) -{ - struct bt_le_scan_cb *listener, *next; - - atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING); - atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); - - atomic_clear_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED); - atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); - -#if defined(CONFIG_BT_SMP) - bt_id_pending_keys_update(); -#endif - - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&scan_cbs, listener, next, node) { - if (listener->timeout) { - listener->timeout(); - } - } -} - -/* Convert Extended adv report evt_type field into adv type */ -static uint8_t get_adv_type(uint8_t evt_type) -{ - switch (evt_type) { - case (BT_HCI_LE_ADV_EVT_TYPE_CONN | - BT_HCI_LE_ADV_EVT_TYPE_SCAN | - BT_HCI_LE_ADV_EVT_TYPE_LEGACY): - return BT_GAP_ADV_TYPE_ADV_IND; - - case (BT_HCI_LE_ADV_EVT_TYPE_CONN | - BT_HCI_LE_ADV_EVT_TYPE_DIRECT | - BT_HCI_LE_ADV_EVT_TYPE_LEGACY): - return BT_GAP_ADV_TYPE_ADV_DIRECT_IND; - - case (BT_HCI_LE_ADV_EVT_TYPE_SCAN | - BT_HCI_LE_ADV_EVT_TYPE_LEGACY): - return BT_GAP_ADV_TYPE_ADV_SCAN_IND; - - case BT_HCI_LE_ADV_EVT_TYPE_LEGACY: - return BT_GAP_ADV_TYPE_ADV_NONCONN_IND; - - case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP | - BT_HCI_LE_ADV_EVT_TYPE_CONN | - BT_HCI_LE_ADV_EVT_TYPE_SCAN | - BT_HCI_LE_ADV_EVT_TYPE_LEGACY): - case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP | - BT_HCI_LE_ADV_EVT_TYPE_SCAN | - BT_HCI_LE_ADV_EVT_TYPE_LEGACY): - /* Scan response from connectable or non-connectable advertiser. - */ - return BT_GAP_ADV_TYPE_SCAN_RSP; - - default: - return BT_GAP_ADV_TYPE_EXT_ADV; - } -} - -static void le_adv_ext_report(struct net_buf *buf) -{ - uint8_t num_reports = net_buf_pull_u8(buf); - - BT_DBG("Adv number of reports %u", num_reports); - - while (num_reports--) { - struct bt_hci_evt_le_ext_advertising_info *evt; - struct bt_le_scan_recv_info adv_info; - - if (buf->len < sizeof(*evt)) { - BT_ERR("Unexpected end of buffer"); - break; - } - - evt = net_buf_pull_mem(buf, sizeof(*evt)); - - adv_info.primary_phy = get_phy(evt->prim_phy); - adv_info.secondary_phy = get_phy(evt->sec_phy); - adv_info.tx_power = evt->tx_power; - adv_info.rssi = evt->rssi; - adv_info.sid = evt->sid; - adv_info.interval = sys_le16_to_cpu(evt->interval); - - adv_info.adv_type = get_adv_type(evt->evt_type); - /* Convert "Legacy" property to Extended property. */ - adv_info.adv_props = evt->evt_type ^ BT_HCI_LE_ADV_PROP_LEGACY; - - le_adv_recv(&evt->addr, &adv_info, buf, evt->length); - - net_buf_pull(buf, evt->length); - } -} - -#if defined(CONFIG_BT_PER_ADV_SYNC) -static void per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync) -{ - atomic_clear(per_adv_sync->flags); -} - -static struct bt_le_per_adv_sync *per_adv_sync_new(void) -{ - struct bt_le_per_adv_sync *per_adv_sync = NULL; - - for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { - if (!atomic_test_bit(per_adv_sync_pool[i].flags, - BT_PER_ADV_SYNC_CREATED)) { - per_adv_sync = &per_adv_sync_pool[i]; - break; - } - } - - if (!per_adv_sync) { - return NULL; - } - - (void)memset(per_adv_sync, 0, sizeof(*per_adv_sync)); - atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_CREATED); - - return per_adv_sync; -} - -static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void) -{ - for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { - if (atomic_test_bit(per_adv_sync_pool[i].flags, - BT_PER_ADV_SYNC_SYNCING)) { - return &per_adv_sync_pool[i]; - } - } - - return NULL; -} - -static struct bt_le_per_adv_sync *get_per_adv_sync(uint16_t handle) -{ - for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { - if (per_adv_sync_pool[i].handle == handle && - atomic_test_bit(per_adv_sync_pool[i].flags, - BT_PER_ADV_SYNC_SYNCED)) { - return &per_adv_sync_pool[i]; - } - } - - return NULL; -} - -static void le_per_adv_report(struct net_buf *buf) -{ - struct bt_hci_evt_le_per_advertising_report *evt; - struct bt_le_per_adv_sync *per_adv_sync; - struct bt_le_per_adv_sync_recv_info info; - struct bt_le_per_adv_sync_cb *listener; - struct net_buf_simple_state state; - - if (buf->len < sizeof(*evt)) { - BT_ERR("Unexpected end of buffer"); - return; - } - - evt = net_buf_pull_mem(buf, sizeof(*evt)); - - per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle)); - - if (!per_adv_sync) { - BT_ERR("Unknown handle 0x%04X for periodic advertising report", - sys_le16_to_cpu(evt->handle)); - return; - } - - if (atomic_test_bit(per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED)) { - BT_ERR("Received PA adv report when receive disabled"); - return; - } - - info.tx_power = evt->tx_power; - info.rssi = evt->rssi; - info.cte_type = evt->cte_type; - info.addr = &per_adv_sync->addr; - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->recv) { - net_buf_simple_save(&buf->b, &state); - - buf->len = evt->length; - listener->recv(per_adv_sync, &info, &buf->b); - - net_buf_simple_restore(&buf->b, &state); - } - } -} - -static int per_adv_sync_terminate(uint16_t handle) -{ - struct bt_hci_cp_le_per_adv_terminate_sync *cp; - struct net_buf *buf; - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC, - sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - cp->handle = sys_cpu_to_le16(handle); - - return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC, buf, - NULL); -} - -static void le_per_adv_sync_established(struct net_buf *buf) -{ - struct bt_hci_evt_le_per_adv_sync_established *evt = - (struct bt_hci_evt_le_per_adv_sync_established *)buf->data; - struct bt_le_per_adv_sync_synced_info sync_info; - struct bt_le_per_adv_sync *pending_per_adv_sync; - struct bt_le_per_adv_sync_cb *listener; - int err; - - pending_per_adv_sync = get_pending_per_adv_sync(); - - if (pending_per_adv_sync) { - atomic_clear_bit(pending_per_adv_sync->flags, - BT_PER_ADV_SYNC_SYNCING); - err = bt_le_scan_update(false); - - if (err) { - BT_ERR("Could not update scan (%d)", err); - } - } - - if (evt->status == BT_HCI_ERR_OP_CANCELLED_BY_HOST) { - /* Cancelled locally, don't call CB */ - if (pending_per_adv_sync) { - per_adv_sync_delete(pending_per_adv_sync); - } else { - BT_ERR("Unexpected per adv sync cancelled event"); - } - - return; - } - - if (!pending_per_adv_sync || - pending_per_adv_sync->sid != evt->sid || - bt_addr_le_cmp(&pending_per_adv_sync->addr, &evt->adv_addr)) { - struct bt_le_per_adv_sync_term_info term_info; - - BT_ERR("Unexpected per adv sync established event"); - per_adv_sync_terminate(sys_le16_to_cpu(evt->handle)); - - if (pending_per_adv_sync) { - /* Terminate the pending PA sync and notify app */ - term_info.addr = &pending_per_adv_sync->addr; - term_info.sid = pending_per_adv_sync->sid; - - /* Deleting before callback, so the caller will be able - * to restart sync in the callback. - */ - per_adv_sync_delete(pending_per_adv_sync); - - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, - listener, - node) { - if (listener->term) { - listener->term(pending_per_adv_sync, - &term_info); - } - } - } - return; - } - - atomic_set_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED); - - pending_per_adv_sync->handle = sys_le16_to_cpu(evt->handle); - pending_per_adv_sync->interval = sys_le16_to_cpu(evt->interval); - pending_per_adv_sync->clock_accuracy = - sys_le16_to_cpu(evt->clock_accuracy); - pending_per_adv_sync->phy = evt->phy; - - memset(&sync_info, 0, sizeof(sync_info)); - sync_info.interval = pending_per_adv_sync->interval; - sync_info.phy = get_phy(pending_per_adv_sync->phy); - sync_info.addr = &pending_per_adv_sync->addr; - sync_info.sid = pending_per_adv_sync->sid; - - sync_info.recv_enabled = - !atomic_test_bit(pending_per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED); - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->synced) { - listener->synced(pending_per_adv_sync, &sync_info); - } - } -} - -static void le_per_adv_sync_lost(struct net_buf *buf) -{ - struct bt_hci_evt_le_per_adv_sync_lost *evt = - (struct bt_hci_evt_le_per_adv_sync_lost *)buf->data; - struct bt_le_per_adv_sync_term_info term_info; - struct bt_le_per_adv_sync *per_adv_sync; - struct bt_le_per_adv_sync_cb *listener; - - per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle)); - - if (!per_adv_sync) { - BT_ERR("Unknown handle 0x%04Xfor periodic adv sync lost", - sys_le16_to_cpu(evt->handle)); - return; - } - - term_info.addr = &per_adv_sync->addr; - term_info.sid = per_adv_sync->sid; - - /* Deleting before callback, so the caller will be able to restart - * sync in the callback - */ - per_adv_sync_delete(per_adv_sync); - - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->term) { - listener->term(per_adv_sync, &term_info); - } - } -} - -#if defined(CONFIG_BT_CONN) -static void le_past_received(struct net_buf *buf) -{ - struct bt_hci_evt_le_past_received *evt = - (struct bt_hci_evt_le_past_received *)buf->data; - struct bt_le_per_adv_sync_synced_info sync_info; - struct bt_le_per_adv_sync_cb *listener; - struct bt_le_per_adv_sync *per_adv_sync; - - if (evt->status) { - /* No sync created, don't notify app */ - BT_DBG("PAST receive failed with status 0x%02X", evt->status); - return; - } - - sync_info.conn = bt_conn_lookup_handle( - sys_le16_to_cpu(evt->conn_handle)); - - if (!sync_info.conn) { - BT_ERR("Could not lookup connection handle from PAST"); - per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle)); - return; - } - - per_adv_sync = per_adv_sync_new(); - if (!per_adv_sync) { - BT_WARN("Could not allocate new PA sync from PAST"); - per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle)); - return; - } - - atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED); - - per_adv_sync->handle = sys_le16_to_cpu(evt->sync_handle); - per_adv_sync->interval = sys_le16_to_cpu(evt->interval); - per_adv_sync->clock_accuracy = sys_le16_to_cpu(evt->clock_accuracy); - per_adv_sync->phy = evt->phy; - bt_addr_le_copy(&per_adv_sync->addr, &evt->addr); - per_adv_sync->sid = evt->adv_sid; - - sync_info.interval = per_adv_sync->interval; - sync_info.phy = get_phy(per_adv_sync->phy); - sync_info.addr = &per_adv_sync->addr; - sync_info.sid = per_adv_sync->sid; - sync_info.service_data = sys_le16_to_cpu(evt->service_data); - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->synced) { - listener->synced(per_adv_sync, &sync_info); - } - } -} -#endif /* CONFIG_BT_CONN */ - -#if defined(CONFIG_BT_ISO_BROADCAST) -static void hci_le_biginfo_adv_report(struct net_buf *buf) -{ - struct bt_hci_evt_le_biginfo_adv_report *evt; - struct bt_le_per_adv_sync *per_adv_sync; - struct bt_le_per_adv_sync_cb *listener; - struct bt_iso_biginfo biginfo; - - evt = net_buf_pull_mem(buf, sizeof(*evt)); - - per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->sync_handle)); - - if (!per_adv_sync) { - BT_ERR("Unknown handle 0x%04X for periodic advertising report", - sys_le16_to_cpu(evt->sync_handle)); - return; - } - - biginfo.addr = &per_adv_sync->addr; - biginfo.sid = per_adv_sync->sid; - biginfo.num_bis = evt->num_bis; - biginfo.sub_evt_count = evt->nse; - biginfo.iso_interval = sys_le16_to_cpu(evt->iso_interval); - biginfo.burst_number = evt->bn; - biginfo.offset = evt->pto; - biginfo.rep_count = evt->irc; - biginfo.max_pdu = sys_le16_to_cpu(evt->max_pdu); - biginfo.sdu_interval = sys_get_le24(evt->sdu_interval); - biginfo.max_sdu = sys_le16_to_cpu(evt->max_sdu); - biginfo.phy = evt->phy; - biginfo.framing = evt->framing; - biginfo.encryption = evt->encryption ? true : false; - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->biginfo) { - listener->biginfo(per_adv_sync, &biginfo); - } - } -} -#endif /* defined(CONFIG_BT_ISO_BROADCAST) */ -#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ -#endif /* defined(CONFIG_BT_EXT_ADV) */ - -static void le_adv_report(struct net_buf *buf) -{ - uint8_t num_reports = net_buf_pull_u8(buf); - struct bt_hci_evt_le_advertising_info *evt; - - BT_DBG("Adv number of reports %u", num_reports); - - while (num_reports--) { - struct bt_le_scan_recv_info adv_info; - - if (buf->len < sizeof(*evt)) { - BT_ERR("Unexpected end of buffer"); - break; - } - - evt = net_buf_pull_mem(buf, sizeof(*evt)); - - adv_info.primary_phy = BT_GAP_LE_PHY_1M; - adv_info.secondary_phy = 0; - adv_info.tx_power = BT_GAP_TX_POWER_INVALID; - adv_info.rssi = evt->data[evt->length]; - adv_info.sid = BT_GAP_SID_INVALID; - adv_info.interval = 0U; - - adv_info.adv_type = evt->evt_type; - adv_info.adv_props = get_adv_props(evt->evt_type); - - le_adv_recv(&evt->addr, &adv_info, buf, evt->length); - - net_buf_pull(buf, evt->length + sizeof(adv_info.rssi)); - } -} -#endif /* CONFIG_BT_OBSERVER */ - static void le_adv_stop_free_conn(const struct bt_le_ext_adv *adv, uint8_t status) { struct bt_conn *conn; @@ -3972,7 +3055,7 @@ static void hci_vendor_event(struct net_buf *buf) static const struct event_handler meta_events[] = { #if defined(CONFIG_BT_OBSERVER) - EVENT_HANDLER(BT_HCI_EVT_LE_ADVERTISING_REPORT, le_adv_report, + EVENT_HANDLER(BT_HCI_EVT_LE_ADVERTISING_REPORT, bt_hci_le_adv_report, sizeof(struct bt_hci_evt_le_advertising_report)), #endif /* CONFIG_BT_OBSERVER */ #if defined(CONFIG_BT_CONN) @@ -4018,21 +3101,21 @@ static const struct event_handler meta_events[] = { sizeof(struct bt_hci_evt_le_scan_req_received)), #endif #if defined(CONFIG_BT_OBSERVER) - EVENT_HANDLER(BT_HCI_EVT_LE_SCAN_TIMEOUT, le_scan_timeout, + EVENT_HANDLER(BT_HCI_EVT_LE_SCAN_TIMEOUT, bt_hci_le_scan_timeout, 0), - EVENT_HANDLER(BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT, le_adv_ext_report, + EVENT_HANDLER(BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT, bt_hci_le_adv_ext_report, sizeof(struct bt_hci_evt_le_ext_advertising_report)), #endif /* defined(CONFIG_BT_OBSERVER) */ #if defined(CONFIG_BT_PER_ADV_SYNC) EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED, - le_per_adv_sync_established, + bt_hci_le_per_adv_sync_established, sizeof(struct bt_hci_evt_le_per_adv_sync_established)), - EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT, le_per_adv_report, + EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT, bt_hci_le_per_adv_report, sizeof(struct bt_hci_evt_le_per_advertising_report)), - EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADV_SYNC_LOST, le_per_adv_sync_lost, + EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADV_SYNC_LOST, bt_hci_le_per_adv_sync_lost, sizeof(struct bt_hci_evt_le_per_adv_sync_lost)), #if defined(CONFIG_BT_CONN) - EVENT_HANDLER(BT_HCI_EVT_LE_PAST_RECEIVED, le_past_received, + EVENT_HANDLER(BT_HCI_EVT_LE_PAST_RECEIVED, bt_hci_le_past_received, sizeof(struct bt_hci_evt_le_past_received)), #endif /* CONFIG_BT_CONN */ #endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ @@ -4056,7 +3139,7 @@ static const struct event_handler meta_events[] = { hci_le_big_sync_lost, sizeof(struct bt_hci_evt_le_big_sync_lost)), EVENT_HANDLER(BT_HCI_EVT_LE_BIGINFO_ADV_REPORT, - hci_le_biginfo_adv_report, + bt_hci_le_biginfo_adv_report, sizeof(struct bt_hci_evt_le_biginfo_adv_report)), #endif /* (CONFIG_BT_ISO_BROADCAST) */ #endif /* (CONFIG_BT_ISO) */ @@ -5940,479 +5023,6 @@ int bt_le_per_adv_set_info_transfer(const struct bt_le_ext_adv *adv, #endif /* CONFIG_BT_CONN */ #endif /* CONFIG_BT_PER_ADV */ -#if defined(CONFIG_BT_PER_ADV_SYNC) -uint8_t bt_le_per_adv_sync_get_index(struct bt_le_per_adv_sync *per_adv_sync) -{ - ptrdiff_t index = per_adv_sync - per_adv_sync_pool; - - __ASSERT(0 <= index && index < ARRAY_SIZE(per_adv_sync_pool), - "Invalid per_adv_sync pointer"); - return (uint8_t)index; -} - -int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param, - struct bt_le_per_adv_sync **out_sync) -{ - struct bt_hci_cp_le_per_adv_create_sync *cp; - struct net_buf *buf; - struct bt_le_per_adv_sync *per_adv_sync; - int err; - - if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { - return -ENOTSUP; - } - - if (get_pending_per_adv_sync()) { - return -EBUSY; - } - - if (param->sid > BT_GAP_SID_MAX || - param->skip > BT_GAP_PER_ADV_MAX_MAX_SKIP || - param->timeout > BT_GAP_PER_ADV_MAX_MAX_TIMEOUT) { - return -EINVAL; - } - - per_adv_sync = per_adv_sync_new(); - if (!per_adv_sync) { - return -ENOMEM; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, sizeof(*cp)); - if (!buf) { - per_adv_sync_delete(per_adv_sync); - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - - bt_addr_le_copy(&cp->addr, ¶m->addr); - - if (param->options & BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST) { - cp->options |= BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST; - } - - if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOA) { - cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOA; - } - - if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_1US) { - cp->cte_type |= - BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_1US; - } - - if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_2US) { - cp->cte_type |= - BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_2US; - } - - if (param->options & BT_LE_PER_ADV_SYNC_OPT_SYNC_ONLY_CONST_TONE_EXT) { - cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE; - } - - if (param->options & - BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED) { - cp->options |= - BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED; - - atomic_set_bit(per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED); - } - - cp->sid = param->sid; - cp->skip = sys_cpu_to_le16(param->skip); - cp->sync_timeout = sys_cpu_to_le16(param->timeout); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, buf, NULL); - if (err) { - per_adv_sync_delete(per_adv_sync); - return err; - } - - atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING); - - /* Syncing requires that scan is enabled. If the caller doesn't enable - * scan first, we enable it here, and disable it once the sync has been - * established. We don't need to use any callbacks since we rely on - * the advertiser address in the sync params. - */ - if (!atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { - err = bt_le_scan_update(true); - - if (err) { - bt_le_per_adv_sync_delete(per_adv_sync); - return err; - } - } - - *out_sync = per_adv_sync; - bt_addr_le_copy(&per_adv_sync->addr, ¶m->addr); - per_adv_sync->sid = param->sid; - - return 0; -} - -static int bt_le_per_adv_sync_create_cancel( - struct bt_le_per_adv_sync *per_adv_sync) -{ - struct net_buf *buf; - int err; - - if (get_pending_per_adv_sync() != per_adv_sync) { - return -EINVAL; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, 0); - if (!buf) { - return -ENOBUFS; - } - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, buf, - NULL); - if (err) { - return err; - } - - return 0; -} - -static int bt_le_per_adv_sync_terminate(struct bt_le_per_adv_sync *per_adv_sync) -{ - int err; - - if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { - return -EINVAL; - } - - err = per_adv_sync_terminate(per_adv_sync->handle); - - if (err) { - return err; - } - - return 0; -} - -int bt_le_per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync) -{ - int err = 0; - - if (atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { - err = bt_le_per_adv_sync_terminate(per_adv_sync); - - if (!err) { - per_adv_sync_delete(per_adv_sync); - } - } else if (get_pending_per_adv_sync() == per_adv_sync) { - err = bt_le_per_adv_sync_create_cancel(per_adv_sync); - /* Delete of the per_adv_sync will be done in the event - * handler when cancelling */ - } - - return err; -} - -void bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb) -{ - sys_slist_append(&pa_sync_cbs, &cb->node); -} - -static int bt_le_set_per_adv_recv_enable( - struct bt_le_per_adv_sync *per_adv_sync, bool enable) -{ - struct bt_hci_cp_le_set_per_adv_recv_enable *cp; - struct bt_le_per_adv_sync_cb *listener; - struct bt_le_per_adv_sync_state_info info; - struct net_buf *buf; - struct bt_hci_cmd_state_set state; - int err; - - if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { - return -EAGAIN; - } - - if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { - return -ENOTSUP; - } - - if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { - return -EINVAL; - } - - if ((enable && !atomic_test_bit(per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED)) || - (!enable && atomic_test_bit(per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED))) { - return -EALREADY; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE, - sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - cp->handle = sys_cpu_to_le16(per_adv_sync->handle); - cp->enable = enable ? 1 : 0; - - bt_hci_cmd_state_set_init(buf, &state, per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED, - enable); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE, - buf, NULL); - - if (err) { - return err; - } - - info.recv_enabled = !atomic_test_bit(per_adv_sync->flags, - BT_PER_ADV_SYNC_RECV_DISABLED); - - SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { - if (listener->state_changed) { - listener->state_changed(per_adv_sync, &info); - } - } - - return 0; -} - -int bt_le_per_adv_sync_recv_enable(struct bt_le_per_adv_sync *per_adv_sync) -{ - return bt_le_set_per_adv_recv_enable(per_adv_sync, true); -} - -int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync) -{ - return bt_le_set_per_adv_recv_enable(per_adv_sync, false); -} - -#if defined(CONFIG_BT_CONN) -int bt_le_per_adv_sync_transfer(const struct bt_le_per_adv_sync *per_adv_sync, - const struct bt_conn *conn, - uint16_t service_data) -{ - struct bt_hci_cp_le_per_adv_sync_transfer *cp; - struct net_buf *buf; - - if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) { - return -EOPNOTSUPP; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER, - sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - cp->conn_handle = sys_cpu_to_le16(conn->handle); - cp->sync_handle = sys_cpu_to_le16(per_adv_sync->handle); - cp->service_data = sys_cpu_to_le16(service_data); - - return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER, buf, - NULL); -} - -static bool valid_past_param( - const struct bt_le_per_adv_sync_transfer_param *param) -{ - if (param->skip > 0x01f3 || - param->timeout < 0x000A || - param->timeout > 0x4000) { - return false; - } - - return true; -} - -static int past_param_set(const struct bt_conn *conn, uint8_t mode, - uint16_t skip, uint16_t timeout, uint8_t cte_type) -{ - struct bt_hci_cp_le_past_param *cp; - struct net_buf *buf; - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_PAST_PARAM, sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - cp->conn_handle = sys_cpu_to_le16(conn->handle); - cp->mode = mode; - cp->skip = sys_cpu_to_le16(skip); - cp->timeout = sys_cpu_to_le16(timeout); - cp->cte_type = cte_type; - - return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PAST_PARAM, buf, NULL); -} - -static int default_past_param_set(uint8_t mode, uint16_t skip, uint16_t timeout, - uint8_t cte_type) -{ - struct bt_hci_cp_le_default_past_param *cp; - struct net_buf *buf; - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - (void)memset(cp, 0, sizeof(*cp)); - - cp->mode = mode; - cp->skip = sys_cpu_to_le16(skip); - cp->timeout = sys_cpu_to_le16(timeout); - cp->cte_type = cte_type; - - return bt_hci_cmd_send_sync(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, buf, NULL); -} - -int bt_le_per_adv_sync_transfer_subscribe( - const struct bt_conn *conn, - const struct bt_le_per_adv_sync_transfer_param *param) -{ - uint8_t cte_type = 0; - - if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { - return -EOPNOTSUPP; - } - - if (!valid_past_param(param)) { - return -EINVAL; - } - - if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOA) { - cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOA; - } - - if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_1US) { - cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_1US; - } - - if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_2US) { - cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_2US; - } - - if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_ONLY_CTE) { - cte_type |= BT_HCI_LE_PAST_CTE_TYPE_ONLY_CTE; - } - - if (conn) { - return past_param_set(conn, BT_HCI_LE_PAST_MODE_SYNC, - param->skip, param->timeout, cte_type); - } else { - return default_past_param_set(BT_HCI_LE_PAST_MODE_SYNC, - param->skip, param->timeout, - cte_type); - } -} - -int bt_le_per_adv_sync_transfer_unsubscribe(const struct bt_conn *conn) -{ - if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { - return -EOPNOTSUPP; - } - - if (conn) { - return past_param_set(conn, BT_HCI_LE_PAST_MODE_NO_SYNC, 0, - 0x0a, 0); - } else { - return default_past_param_set(BT_HCI_LE_PAST_MODE_NO_SYNC, 0, - 0x0a, 0); - } -} -#endif /* CONFIG_BT_CONN */ - -int bt_le_per_adv_list_add(const bt_addr_le_t *addr, uint8_t sid) -{ - struct bt_hci_cp_le_add_dev_to_per_adv_list *cp; - struct net_buf *buf; - int err; - - if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { - return -EAGAIN; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST, - sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - bt_addr_le_copy(&cp->addr, addr); - cp->sid = sid; - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST, buf, - NULL); - if (err) { - BT_ERR("Failed to add device to periodic advertiser list"); - - return err; - } - - return 0; -} - -int bt_le_per_adv_list_remove(const bt_addr_le_t *addr, uint8_t sid) -{ - struct bt_hci_cp_le_rem_dev_from_per_adv_list *cp; - struct net_buf *buf; - int err; - - if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { - return -EAGAIN; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST, - sizeof(*cp)); - if (!buf) { - return -ENOBUFS; - } - - cp = net_buf_add(buf, sizeof(*cp)); - bt_addr_le_copy(&cp->addr, addr); - cp->sid = sid; - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST, buf, - NULL); - if (err) { - BT_ERR("Failed to remove device from periodic advertiser list"); - return err; - } - - return 0; -} - -int bt_le_per_adv_list_clear(void) -{ - int err; - - if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { - return -EAGAIN; - } - - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CLEAR_PER_ADV_LIST, NULL, NULL); - if (err) { - BT_ERR("Failed to clear periodic advertiser list"); - return err; - } - - return 0; -} -#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ - static bool valid_adv_ext_param(const struct bt_le_adv_param *param) { if (IS_ENABLED(CONFIG_BT_EXT_ADV) && @@ -7401,157 +6011,6 @@ int bt_le_ext_adv_delete(struct bt_le_ext_adv *adv) } #endif /* defined(CONFIG_BT_EXT_ADV) */ -#if defined(CONFIG_BT_OBSERVER) -static bool valid_le_scan_param(const struct bt_le_scan_param *param) -{ - if (param->type != BT_HCI_LE_SCAN_PASSIVE && - param->type != BT_HCI_LE_SCAN_ACTIVE) { - return false; - } - - if (param->options & ~(BT_LE_SCAN_OPT_FILTER_DUPLICATE | - BT_LE_SCAN_OPT_FILTER_WHITELIST | - BT_LE_SCAN_OPT_CODED | - BT_LE_SCAN_OPT_NO_1M)) { - return false; - } - - if (param->interval < 0x0004 || param->interval > 0x4000) { - return false; - } - - if (param->window < 0x0004 || param->window > 0x4000) { - return false; - } - - if (param->window > param->interval) { - return false; - } - - return true; -} - -int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) -{ - int err; - - if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { - return -EAGAIN; - } - - /* Check that the parameters have valid values */ - if (!valid_le_scan_param(param)) { - return -EINVAL; - } - - if (param->type && !bt_id_scan_random_addr_check()) { - return -EINVAL; - } - - /* Return if active scan is already enabled */ - if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { - return -EALREADY; - } - - if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { - err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE); - if (err) { - atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); - return err; - } - } - - atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP, - param->options & BT_LE_SCAN_OPT_FILTER_DUPLICATE); - -#if defined(CONFIG_BT_WHITELIST) - atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_WL, - param->options & BT_LE_SCAN_OPT_FILTER_WHITELIST); -#endif /* defined(CONFIG_BT_WHITELIST) */ - - if (IS_ENABLED(CONFIG_BT_EXT_ADV) && - BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { - struct bt_hci_ext_scan_phy param_1m; - struct bt_hci_ext_scan_phy param_coded; - - struct bt_hci_ext_scan_phy *phy_1m = NULL; - struct bt_hci_ext_scan_phy *phy_coded = NULL; - - if (!(param->options & BT_LE_SCAN_OPT_NO_1M)) { - param_1m.type = param->type; - param_1m.interval = sys_cpu_to_le16(param->interval); - param_1m.window = sys_cpu_to_le16(param->window); - - phy_1m = ¶m_1m; - } - - if (param->options & BT_LE_SCAN_OPT_CODED) { - uint16_t interval = param->interval_coded ? - param->interval_coded : - param->interval; - uint16_t window = param->window_coded ? - param->window_coded : - param->window; - - param_coded.type = param->type; - param_coded.interval = sys_cpu_to_le16(interval); - param_coded.window = sys_cpu_to_le16(window); - phy_coded = ¶m_coded; - } - - err = start_le_scan_ext(phy_1m, phy_coded, param->timeout); - } else { - if (param->timeout) { - atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); - return -ENOTSUP; - } - - err = start_le_scan_legacy(param->type, param->interval, - param->window); - } - - if (err) { - atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); - return err; - } - - scan_dev_found_cb = cb; - - return 0; -} - -int bt_le_scan_stop(void) -{ - /* Return if active scanning is already disabled */ - if (!atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { - return -EALREADY; - } - - scan_dev_found_cb = NULL; - - if (IS_ENABLED(CONFIG_BT_EXT_ADV) && - atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED)) { - atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); - -#if defined(CONFIG_BT_SMP) - bt_id_pending_keys_update(); -#endif - } - - return bt_le_scan_update(false); -} - -void bt_le_scan_cb_register(struct bt_le_scan_cb *cb) -{ - sys_slist_append(&scan_cbs, &cb->node); -} - -void bt_le_scan_cb_unregister(struct bt_le_scan_cb *cb) -{ - sys_slist_find_and_remove(&scan_cbs, &cb->node); -} -#endif /* CONFIG_BT_OBSERVER */ - #if defined(CONFIG_BT_WHITELIST) int bt_le_whitelist_add(const bt_addr_le_t *addr) { diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 9f4211f5cf6..ca5019fe729 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -342,6 +342,7 @@ bool bt_le_conn_params_valid(const struct bt_le_conn_param *param); int bt_le_set_data_len(struct bt_conn *conn, uint16_t tx_octets, uint16_t tx_time); int bt_le_set_phy(struct bt_conn *conn, uint8_t all_phys, uint8_t pref_tx_phy, uint8_t pref_rx_phy, uint8_t phy_opts); +uint8_t bt_get_phy(uint8_t hci_phy); int bt_le_scan_update(bool fast_scan); @@ -378,8 +379,6 @@ int bt_le_adv_set_enable(struct bt_le_ext_adv *adv, bool enable); void bt_le_ext_adv_foreach(void (*func)(struct bt_le_ext_adv *adv, void *data), void *data); -int bt_le_scan_set_enable(uint8_t enable); - void bt_hci_host_num_completed_packets(struct net_buf *buf); /* HCI event handlers */ @@ -397,3 +396,13 @@ void hci_evt_auth_complete(struct net_buf *buf); /* ECC HCI event handlers */ void bt_hci_evt_le_pkey_complete(struct net_buf *buf); void bt_hci_evt_le_dhkey_complete(struct net_buf *buf); + +/* Scan HCI event handlers */ +void bt_hci_le_adv_report(struct net_buf *buf); +void bt_hci_le_scan_timeout(struct net_buf *buf); +void bt_hci_le_adv_ext_report(struct net_buf *buf); +void bt_hci_le_per_adv_sync_established(struct net_buf *buf); +void bt_hci_le_per_adv_report(struct net_buf *buf); +void bt_hci_le_per_adv_sync_lost(struct net_buf *buf); +void bt_hci_le_biginfo_adv_report(struct net_buf *buf); +void bt_hci_le_past_received(struct net_buf *buf); diff --git a/subsys/bluetooth/host/id.c b/subsys/bluetooth/host/id.c index a5e4d731600..af3f44eb5da 100644 --- a/subsys/bluetooth/host/id.c +++ b/subsys/bluetooth/host/id.c @@ -13,6 +13,7 @@ #include "hci_core.h" #include "id.h" +#include "scan.h" #include "smp.h" #include "conn_internal.h" #include "keys.h" diff --git a/subsys/bluetooth/host/scan.c b/subsys/bluetooth/host/scan.c new file mode 100644 index 00000000000..17364153a01 --- /dev/null +++ b/subsys/bluetooth/host/scan.c @@ -0,0 +1,1562 @@ +/* + * Copyright (c) 2017-2021 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include + +#include +#include +#include + +#include "hci_core.h" +#include "conn_internal.h" +#include "id.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#define LOG_MODULE_NAME bt_scan +#include "common/log.h" + +static bt_le_scan_cb_t *scan_dev_found_cb; +static sys_slist_t scan_cbs = SYS_SLIST_STATIC_INIT(&scan_cbs); + +#if defined(CONFIG_BT_EXT_ADV) +#if defined(CONFIG_BT_PER_ADV_SYNC) +static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void); +static struct bt_le_per_adv_sync per_adv_sync_pool[CONFIG_BT_PER_ADV_SYNC_MAX]; +static sys_slist_t pa_sync_cbs = SYS_SLIST_STATIC_INIT(&pa_sync_cbs); +#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ +#endif /* defined(CONFIG_BT_EXT_ADV) */ + +void bt_scan_reset(void) +{ + scan_dev_found_cb = NULL; +} + +static int set_le_ext_scan_enable(uint8_t enable, uint16_t duration) +{ + struct bt_hci_cp_le_set_ext_scan_enable *cp; + struct bt_hci_cmd_state_set state; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + if (enable == BT_HCI_LE_SCAN_ENABLE) { + cp->filter_dup = atomic_test_bit(bt_dev.flags, + BT_DEV_SCAN_FILTER_DUP); + } else { + cp->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + } + + cp->enable = enable; + cp->duration = sys_cpu_to_le16(duration); + cp->period = 0; + + bt_hci_cmd_state_set_init(buf, &state, bt_dev.flags, BT_DEV_SCANNING, + enable == BT_HCI_LE_SCAN_ENABLE); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +static int bt_le_scan_set_enable_legacy(uint8_t enable) +{ + struct bt_hci_cp_le_set_scan_enable *cp; + struct bt_hci_cmd_state_set state; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + if (enable == BT_HCI_LE_SCAN_ENABLE) { + cp->filter_dup = atomic_test_bit(bt_dev.flags, + BT_DEV_SCAN_FILTER_DUP); + } else { + cp->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + } + + cp->enable = enable; + + bt_hci_cmd_state_set_init(buf, &state, bt_dev.flags, BT_DEV_SCANNING, + enable == BT_HCI_LE_SCAN_ENABLE); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +int bt_le_scan_set_enable(uint8_t enable) +{ + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { + return set_le_ext_scan_enable(enable, 0); + } + + return bt_le_scan_set_enable_legacy(enable); +} + +static int start_le_scan_ext(struct bt_hci_ext_scan_phy *phy_1m, + struct bt_hci_ext_scan_phy *phy_coded, + uint16_t duration) +{ + struct bt_hci_cp_le_set_ext_scan_param *set_param; + struct net_buf *buf; + uint8_t own_addr_type; + bool active_scan; + int err; + + active_scan = (phy_1m && phy_1m->type == BT_HCI_LE_SCAN_ACTIVE) || + (phy_coded && phy_coded->type == BT_HCI_LE_SCAN_ACTIVE); + + if (duration > 0) { + atomic_set_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED); + + /* Allow bt_le_oob_get_local to be called directly before + * starting a scan limited by timeout. + */ + if (IS_ENABLED(CONFIG_BT_PRIVACY) && !bt_id_rpa_is_new()) { + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + } + } + + err = bt_id_set_scan_own_addr(active_scan, &own_addr_type); + if (err) { + return err; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_SCAN_PARAM, + sizeof(*set_param) + + (phy_1m ? sizeof(*phy_1m) : 0) + + (phy_coded ? sizeof(*phy_coded) : 0)); + if (!buf) { + return -ENOBUFS; + } + + set_param = net_buf_add(buf, sizeof(*set_param)); + set_param->own_addr_type = own_addr_type; + set_param->phys = 0; + + if (IS_ENABLED(CONFIG_BT_WHITELIST) && + atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_WL)) { + set_param->filter_policy = BT_HCI_LE_SCAN_FP_USE_WHITELIST; + } else { + set_param->filter_policy = BT_HCI_LE_SCAN_FP_NO_WHITELIST; + } + + if (phy_1m) { + set_param->phys |= BT_HCI_LE_EXT_SCAN_PHY_1M; + net_buf_add_mem(buf, phy_1m, sizeof(*phy_1m)); + } + + if (phy_coded) { + set_param->phys |= BT_HCI_LE_EXT_SCAN_PHY_CODED; + net_buf_add_mem(buf, phy_coded, sizeof(*phy_coded)); + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_SCAN_PARAM, buf, NULL); + if (err) { + return err; + } + + err = set_le_ext_scan_enable(BT_HCI_LE_SCAN_ENABLE, duration); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, active_scan); + + return 0; +} + +static int start_le_scan_legacy(uint8_t scan_type, uint16_t interval, uint16_t window) +{ + struct bt_hci_cp_le_set_scan_param set_param; + struct net_buf *buf; + int err; + bool active_scan; + + (void)memset(&set_param, 0, sizeof(set_param)); + + set_param.scan_type = scan_type; + + /* for the rest parameters apply default values according to + * spec 4.2, vol2, part E, 7.8.10 + */ + set_param.interval = sys_cpu_to_le16(interval); + set_param.window = sys_cpu_to_le16(window); + + if (IS_ENABLED(CONFIG_BT_WHITELIST) && + atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_WL)) { + set_param.filter_policy = BT_HCI_LE_SCAN_FP_USE_WHITELIST; + } else { + set_param.filter_policy = BT_HCI_LE_SCAN_FP_NO_WHITELIST; + } + + active_scan = scan_type == BT_HCI_LE_SCAN_ACTIVE; + err = bt_id_set_scan_own_addr(active_scan, &set_param.addr_type); + if (err) { + return err; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_PARAM, buf, NULL); + if (err) { + return err; + } + + err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_ENABLE); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, active_scan); + + return 0; +} + +static int start_passive_scan(bool fast_scan) +{ + uint16_t interval, window; + + if (fast_scan) { + interval = BT_GAP_SCAN_FAST_INTERVAL; + window = BT_GAP_SCAN_FAST_WINDOW; + } else { + interval = CONFIG_BT_BACKGROUND_SCAN_INTERVAL; + window = CONFIG_BT_BACKGROUND_SCAN_WINDOW; + } + + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { + struct bt_hci_ext_scan_phy scan; + + scan.type = BT_HCI_LE_SCAN_PASSIVE; + scan.interval = sys_cpu_to_le16(interval); + scan.window = sys_cpu_to_le16(window); + + return start_le_scan_ext(&scan, NULL, 0); + } + + return start_le_scan_legacy(BT_HCI_LE_SCAN_PASSIVE, interval, window); +} + +int bt_le_scan_update(bool fast_scan) +{ + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return 0; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + int err; + + err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + return err; + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + struct bt_conn *conn; + + /* don't restart scan if we have pending connection */ + conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, + BT_CONN_CONNECT); + if (conn) { + bt_conn_unref(conn); + return 0; + } + + conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, + BT_CONN_CONNECT_SCAN); + if (conn) { + atomic_set_bit(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP); + + bt_conn_unref(conn); + + return start_passive_scan(fast_scan); + } + } + +#if defined(CONFIG_BT_PER_ADV_SYNC) + if (get_pending_per_adv_sync()) { + return start_passive_scan(fast_scan); + } +#endif + + return 0; +} + +void bt_data_parse(struct net_buf_simple *ad, + bool (*func)(struct bt_data *data, void *user_data), + void *user_data) +{ + while (ad->len > 1) { + struct bt_data data; + uint8_t len; + + len = net_buf_simple_pull_u8(ad); + if (len == 0U) { + /* Early termination */ + return; + } + + if (len > ad->len) { + BT_WARN("Malformed data"); + return; + } + + data.type = net_buf_simple_pull_u8(ad); + data.data_len = len - 1; + data.data = ad->data; + + if (!func(&data, user_data)) { + return; + } + + net_buf_simple_pull(ad, len - 1); + } +} + +#if defined(CONFIG_BT_CENTRAL) +static void check_pending_conn(const bt_addr_le_t *id_addr, + const bt_addr_le_t *addr, uint8_t adv_props) +{ + struct bt_conn *conn; + + /* No connections are allowed during explicit scanning */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return; + } + + /* Return if event is not connectable */ + if (!(adv_props & BT_HCI_LE_ADV_EVT_TYPE_CONN)) { + return; + } + + conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, id_addr, + BT_CONN_CONNECT_SCAN); + if (!conn) { + return; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && + bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE)) { + goto failed; + } + + bt_addr_le_copy(&conn->le.resp_addr, addr); + if (bt_le_create_conn(conn)) { + goto failed; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + bt_conn_unref(conn); + return; + +failed: + conn->err = BT_HCI_ERR_UNSPECIFIED; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_conn_unref(conn); + bt_le_scan_update(false); +} +#endif /* CONFIG_BT_CENTRAL */ + +/* Convert Legacy adv report evt_type field to adv props */ +static uint8_t get_adv_props(uint8_t evt_type) +{ + switch (evt_type) { + case BT_GAP_ADV_TYPE_ADV_IND: + return BT_GAP_ADV_PROP_CONNECTABLE | + BT_GAP_ADV_PROP_SCANNABLE; + + case BT_GAP_ADV_TYPE_ADV_DIRECT_IND: + return BT_GAP_ADV_PROP_CONNECTABLE | + BT_GAP_ADV_PROP_DIRECTED; + + case BT_GAP_ADV_TYPE_ADV_SCAN_IND: + return BT_GAP_ADV_PROP_SCANNABLE; + + case BT_GAP_ADV_TYPE_ADV_NONCONN_IND: + return 0; + + /* In legacy advertising report, we don't know if the scan + * response come from a connectable advertiser, so don't + * set connectable property bit. + */ + case BT_GAP_ADV_TYPE_SCAN_RSP: + return BT_GAP_ADV_PROP_SCAN_RESPONSE | + BT_GAP_ADV_PROP_SCANNABLE; + + default: + return 0; + } +} + +static void le_adv_recv(bt_addr_le_t *addr, struct bt_le_scan_recv_info *info, + struct net_buf *buf, uint8_t len) +{ + struct bt_le_scan_cb *listener, *next; + struct net_buf_simple_state state; + bt_addr_le_t id_addr; + + BT_DBG("%s event %u, len %u, rssi %d dBm", bt_addr_le_str(addr), + info->adv_type, len, info->rssi); + + if (!IS_ENABLED(CONFIG_BT_PRIVACY) && + !IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) && + atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN) && + (info->adv_props & BT_HCI_LE_ADV_PROP_DIRECT)) { + BT_DBG("Dropped direct adv report"); + return; + } + + if (addr->type == BT_ADDR_LE_PUBLIC_ID || + addr->type == BT_ADDR_LE_RANDOM_ID) { + bt_addr_le_copy(&id_addr, addr); + id_addr.type -= BT_ADDR_LE_PUBLIC_ID; + } else if (addr->type == BT_HCI_PEER_ADDR_ANONYMOUS) { + bt_addr_le_copy(&id_addr, BT_ADDR_LE_ANY); + } else { + bt_addr_le_copy(&id_addr, + bt_lookup_id_addr(BT_ID_DEFAULT, addr)); + } + + info->addr = &id_addr; + + if (scan_dev_found_cb) { + net_buf_simple_save(&buf->b, &state); + + buf->len = len; + scan_dev_found_cb(&id_addr, info->rssi, info->adv_type, + &buf->b); + + net_buf_simple_restore(&buf->b, &state); + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&scan_cbs, listener, next, node) { + if (listener->recv) { + net_buf_simple_save(&buf->b, &state); + + buf->len = len; + listener->recv(info, &buf->b); + + net_buf_simple_restore(&buf->b, &state); + } + } + +#if defined(CONFIG_BT_CENTRAL) + check_pending_conn(&id_addr, addr, info->adv_props); +#endif /* CONFIG_BT_CENTRAL */ +} + +#if defined(CONFIG_BT_EXT_ADV) +void bt_hci_le_scan_timeout(struct net_buf *buf) +{ + struct bt_le_scan_cb *listener, *next; + + atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING); + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + + atomic_clear_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED); + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + +#if defined(CONFIG_BT_SMP) + bt_id_pending_keys_update(); +#endif + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&scan_cbs, listener, next, node) { + if (listener->timeout) { + listener->timeout(); + } + } +} + +/* Convert Extended adv report evt_type field into adv type */ +static uint8_t get_adv_type(uint8_t evt_type) +{ + switch (evt_type) { + case (BT_HCI_LE_ADV_EVT_TYPE_CONN | + BT_HCI_LE_ADV_EVT_TYPE_SCAN | + BT_HCI_LE_ADV_EVT_TYPE_LEGACY): + return BT_GAP_ADV_TYPE_ADV_IND; + + case (BT_HCI_LE_ADV_EVT_TYPE_CONN | + BT_HCI_LE_ADV_EVT_TYPE_DIRECT | + BT_HCI_LE_ADV_EVT_TYPE_LEGACY): + return BT_GAP_ADV_TYPE_ADV_DIRECT_IND; + + case (BT_HCI_LE_ADV_EVT_TYPE_SCAN | + BT_HCI_LE_ADV_EVT_TYPE_LEGACY): + return BT_GAP_ADV_TYPE_ADV_SCAN_IND; + + case BT_HCI_LE_ADV_EVT_TYPE_LEGACY: + return BT_GAP_ADV_TYPE_ADV_NONCONN_IND; + + case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP | + BT_HCI_LE_ADV_EVT_TYPE_CONN | + BT_HCI_LE_ADV_EVT_TYPE_SCAN | + BT_HCI_LE_ADV_EVT_TYPE_LEGACY): + case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP | + BT_HCI_LE_ADV_EVT_TYPE_SCAN | + BT_HCI_LE_ADV_EVT_TYPE_LEGACY): + /* Scan response from connectable or non-connectable advertiser. + */ + return BT_GAP_ADV_TYPE_SCAN_RSP; + + default: + return BT_GAP_ADV_TYPE_EXT_ADV; + } +} + +void bt_hci_le_adv_ext_report(struct net_buf *buf) +{ + uint8_t num_reports = net_buf_pull_u8(buf); + + BT_DBG("Adv number of reports %u", num_reports); + + while (num_reports--) { + struct bt_hci_evt_le_ext_advertising_info *evt; + struct bt_le_scan_recv_info adv_info; + + if (buf->len < sizeof(*evt)) { + BT_ERR("Unexpected end of buffer"); + break; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + + adv_info.primary_phy = bt_get_phy(evt->prim_phy); + adv_info.secondary_phy = bt_get_phy(evt->sec_phy); + adv_info.tx_power = evt->tx_power; + adv_info.rssi = evt->rssi; + adv_info.sid = evt->sid; + adv_info.interval = sys_le16_to_cpu(evt->interval); + + adv_info.adv_type = get_adv_type(evt->evt_type); + /* Convert "Legacy" property to Extended property. */ + adv_info.adv_props = evt->evt_type ^ BT_HCI_LE_ADV_PROP_LEGACY; + + le_adv_recv(&evt->addr, &adv_info, buf, evt->length); + + net_buf_pull(buf, evt->length); + } +} + + +#if defined(CONFIG_BT_PER_ADV_SYNC) +static void per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync) +{ + atomic_clear(per_adv_sync->flags); +} + +static struct bt_le_per_adv_sync *per_adv_sync_new(void) +{ + struct bt_le_per_adv_sync *per_adv_sync = NULL; + + for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { + if (!atomic_test_bit(per_adv_sync_pool[i].flags, + BT_PER_ADV_SYNC_CREATED)) { + per_adv_sync = &per_adv_sync_pool[i]; + break; + } + } + + if (!per_adv_sync) { + return NULL; + } + + (void)memset(per_adv_sync, 0, sizeof(*per_adv_sync)); + atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_CREATED); + + return per_adv_sync; +} + +static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void) +{ + for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { + if (atomic_test_bit(per_adv_sync_pool[i].flags, + BT_PER_ADV_SYNC_SYNCING)) { + return &per_adv_sync_pool[i]; + } + } + + return NULL; +} + +static struct bt_le_per_adv_sync *get_per_adv_sync(uint16_t handle) +{ + for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { + if (per_adv_sync_pool[i].handle == handle && + atomic_test_bit(per_adv_sync_pool[i].flags, + BT_PER_ADV_SYNC_SYNCED)) { + return &per_adv_sync_pool[i]; + } + } + + return NULL; +} + +void bt_hci_le_per_adv_report(struct net_buf *buf) +{ + struct bt_hci_evt_le_per_advertising_report *evt; + struct bt_le_per_adv_sync *per_adv_sync; + struct bt_le_per_adv_sync_recv_info info; + struct bt_le_per_adv_sync_cb *listener; + struct net_buf_simple_state state; + + if (buf->len < sizeof(*evt)) { + BT_ERR("Unexpected end of buffer"); + return; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + + per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle)); + + if (!per_adv_sync) { + BT_ERR("Unknown handle 0x%04X for periodic advertising report", + sys_le16_to_cpu(evt->handle)); + return; + } + + if (atomic_test_bit(per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED)) { + BT_ERR("Received PA adv report when receive disabled"); + return; + } + + info.tx_power = evt->tx_power; + info.rssi = evt->rssi; + info.cte_type = evt->cte_type; + info.addr = &per_adv_sync->addr; + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->recv) { + net_buf_simple_save(&buf->b, &state); + + buf->len = evt->length; + listener->recv(per_adv_sync, &info, &buf->b); + + net_buf_simple_restore(&buf->b, &state); + } + } +} + +static int per_adv_sync_terminate(uint16_t handle) +{ + struct bt_hci_cp_le_per_adv_terminate_sync *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(handle); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC, buf, + NULL); +} + +void bt_hci_le_per_adv_sync_established(struct net_buf *buf) +{ + struct bt_hci_evt_le_per_adv_sync_established *evt = + (struct bt_hci_evt_le_per_adv_sync_established *)buf->data; + struct bt_le_per_adv_sync_synced_info sync_info; + struct bt_le_per_adv_sync *pending_per_adv_sync; + struct bt_le_per_adv_sync_cb *listener; + int err; + + pending_per_adv_sync = get_pending_per_adv_sync(); + + if (pending_per_adv_sync) { + atomic_clear_bit(pending_per_adv_sync->flags, + BT_PER_ADV_SYNC_SYNCING); + err = bt_le_scan_update(false); + + if (err) { + BT_ERR("Could not update scan (%d)", err); + } + } + + if (evt->status == BT_HCI_ERR_OP_CANCELLED_BY_HOST) { + /* Cancelled locally, don't call CB */ + if (pending_per_adv_sync) { + per_adv_sync_delete(pending_per_adv_sync); + } else { + BT_ERR("Unexpected per adv sync cancelled event"); + } + + return; + } + + if (!pending_per_adv_sync || + pending_per_adv_sync->sid != evt->sid || + bt_addr_le_cmp(&pending_per_adv_sync->addr, &evt->adv_addr)) { + struct bt_le_per_adv_sync_term_info term_info; + + BT_ERR("Unexpected per adv sync established event"); + per_adv_sync_terminate(sys_le16_to_cpu(evt->handle)); + + if (pending_per_adv_sync) { + /* Terminate the pending PA sync and notify app */ + term_info.addr = &pending_per_adv_sync->addr; + term_info.sid = pending_per_adv_sync->sid; + + /* Deleting before callback, so the caller will be able + * to restart sync in the callback. + */ + per_adv_sync_delete(pending_per_adv_sync); + + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, + listener, + node) { + if (listener->term) { + listener->term(pending_per_adv_sync, + &term_info); + } + } + } + return; + } + + atomic_set_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED); + + pending_per_adv_sync->handle = sys_le16_to_cpu(evt->handle); + pending_per_adv_sync->interval = sys_le16_to_cpu(evt->interval); + pending_per_adv_sync->clock_accuracy = + sys_le16_to_cpu(evt->clock_accuracy); + pending_per_adv_sync->phy = evt->phy; + + memset(&sync_info, 0, sizeof(sync_info)); + sync_info.interval = pending_per_adv_sync->interval; + sync_info.phy = bt_get_phy(pending_per_adv_sync->phy); + sync_info.addr = &pending_per_adv_sync->addr; + sync_info.sid = pending_per_adv_sync->sid; + + sync_info.recv_enabled = + !atomic_test_bit(pending_per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED); + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->synced) { + listener->synced(pending_per_adv_sync, &sync_info); + } + } +} + +void bt_hci_le_per_adv_sync_lost(struct net_buf *buf) +{ + struct bt_hci_evt_le_per_adv_sync_lost *evt = + (struct bt_hci_evt_le_per_adv_sync_lost *)buf->data; + struct bt_le_per_adv_sync_term_info term_info; + struct bt_le_per_adv_sync *per_adv_sync; + struct bt_le_per_adv_sync_cb *listener; + + per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle)); + + if (!per_adv_sync) { + BT_ERR("Unknown handle 0x%04Xfor periodic adv sync lost", + sys_le16_to_cpu(evt->handle)); + return; + } + + term_info.addr = &per_adv_sync->addr; + term_info.sid = per_adv_sync->sid; + + /* Deleting before callback, so the caller will be able to restart + * sync in the callback + */ + per_adv_sync_delete(per_adv_sync); + + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->term) { + listener->term(per_adv_sync, &term_info); + } + } +} + +#if defined(CONFIG_BT_CONN) +void bt_hci_le_past_received(struct net_buf *buf) +{ + struct bt_hci_evt_le_past_received *evt = + (struct bt_hci_evt_le_past_received *)buf->data; + struct bt_le_per_adv_sync_synced_info sync_info; + struct bt_le_per_adv_sync_cb *listener; + struct bt_le_per_adv_sync *per_adv_sync; + + if (evt->status) { + /* No sync created, don't notify app */ + BT_DBG("PAST receive failed with status 0x%02X", evt->status); + return; + } + + sync_info.conn = bt_conn_lookup_handle( + sys_le16_to_cpu(evt->conn_handle)); + + if (!sync_info.conn) { + BT_ERR("Could not lookup connection handle from PAST"); + per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle)); + return; + } + + per_adv_sync = per_adv_sync_new(); + if (!per_adv_sync) { + BT_WARN("Could not allocate new PA sync from PAST"); + per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle)); + return; + } + + atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED); + + per_adv_sync->handle = sys_le16_to_cpu(evt->sync_handle); + per_adv_sync->interval = sys_le16_to_cpu(evt->interval); + per_adv_sync->clock_accuracy = sys_le16_to_cpu(evt->clock_accuracy); + per_adv_sync->phy = evt->phy; + bt_addr_le_copy(&per_adv_sync->addr, &evt->addr); + per_adv_sync->sid = evt->adv_sid; + + sync_info.interval = per_adv_sync->interval; + sync_info.phy = bt_get_phy(per_adv_sync->phy); + sync_info.addr = &per_adv_sync->addr; + sync_info.sid = per_adv_sync->sid; + sync_info.service_data = sys_le16_to_cpu(evt->service_data); + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->synced) { + listener->synced(per_adv_sync, &sync_info); + } + } +} +#endif /* CONFIG_BT_CONN */ + +#if defined(CONFIG_BT_ISO_BROADCAST) +void bt_hci_le_biginfo_adv_report(struct net_buf *buf) +{ + struct bt_hci_evt_le_biginfo_adv_report *evt; + struct bt_le_per_adv_sync *per_adv_sync; + struct bt_le_per_adv_sync_cb *listener; + struct bt_iso_biginfo biginfo; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + + per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->sync_handle)); + + if (!per_adv_sync) { + BT_ERR("Unknown handle 0x%04X for periodic advertising report", + sys_le16_to_cpu(evt->sync_handle)); + return; + } + + biginfo.addr = &per_adv_sync->addr; + biginfo.sid = per_adv_sync->sid; + biginfo.num_bis = evt->num_bis; + biginfo.sub_evt_count = evt->nse; + biginfo.iso_interval = sys_le16_to_cpu(evt->iso_interval); + biginfo.burst_number = evt->bn; + biginfo.offset = evt->pto; + biginfo.rep_count = evt->irc; + biginfo.max_pdu = sys_le16_to_cpu(evt->max_pdu); + biginfo.sdu_interval = sys_get_le24(evt->sdu_interval); + biginfo.max_sdu = sys_le16_to_cpu(evt->max_sdu); + biginfo.phy = evt->phy; + biginfo.framing = evt->framing; + biginfo.encryption = evt->encryption ? true : false; + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->biginfo) { + listener->biginfo(per_adv_sync, &biginfo); + } + } +} +#endif /* defined(CONFIG_BT_ISO_BROADCAST) */ +#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ +#endif /* defined(CONFIG_BT_EXT_ADV) */ + +void bt_hci_le_adv_report(struct net_buf *buf) +{ + uint8_t num_reports = net_buf_pull_u8(buf); + struct bt_hci_evt_le_advertising_info *evt; + + BT_DBG("Adv number of reports %u", num_reports); + + while (num_reports--) { + struct bt_le_scan_recv_info adv_info; + + if (buf->len < sizeof(*evt)) { + BT_ERR("Unexpected end of buffer"); + break; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + + adv_info.primary_phy = BT_GAP_LE_PHY_1M; + adv_info.secondary_phy = 0; + adv_info.tx_power = BT_GAP_TX_POWER_INVALID; + adv_info.rssi = evt->data[evt->length]; + adv_info.sid = BT_GAP_SID_INVALID; + adv_info.interval = 0U; + + adv_info.adv_type = evt->evt_type; + adv_info.adv_props = get_adv_props(evt->evt_type); + + le_adv_recv(&evt->addr, &adv_info, buf, evt->length); + + net_buf_pull(buf, evt->length + sizeof(adv_info.rssi)); + } +} + +static bool valid_le_scan_param(const struct bt_le_scan_param *param) +{ + if (param->type != BT_HCI_LE_SCAN_PASSIVE && + param->type != BT_HCI_LE_SCAN_ACTIVE) { + return false; + } + + if (param->options & ~(BT_LE_SCAN_OPT_FILTER_DUPLICATE | + BT_LE_SCAN_OPT_FILTER_WHITELIST | + BT_LE_SCAN_OPT_CODED | + BT_LE_SCAN_OPT_NO_1M)) { + return false; + } + + if (param->interval < 0x0004 || param->interval > 0x4000) { + return false; + } + + if (param->window < 0x0004 || param->window > 0x4000) { + return false; + } + + if (param->window > param->interval) { + return false; + } + + return true; +} + +int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + /* Check that the parameters have valid values */ + if (!valid_le_scan_param(param)) { + return -EINVAL; + } + + if (param->type && !bt_id_scan_random_addr_check()) { + return -EINVAL; + } + + /* Return if active scan is already enabled */ + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP, + param->options & BT_LE_SCAN_OPT_FILTER_DUPLICATE); + +#if defined(CONFIG_BT_WHITELIST) + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_WL, + param->options & BT_LE_SCAN_OPT_FILTER_WHITELIST); +#endif /* defined(CONFIG_BT_WHITELIST) */ + + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { + struct bt_hci_ext_scan_phy param_1m; + struct bt_hci_ext_scan_phy param_coded; + + struct bt_hci_ext_scan_phy *phy_1m = NULL; + struct bt_hci_ext_scan_phy *phy_coded = NULL; + + if (!(param->options & BT_LE_SCAN_OPT_NO_1M)) { + param_1m.type = param->type; + param_1m.interval = sys_cpu_to_le16(param->interval); + param_1m.window = sys_cpu_to_le16(param->window); + + phy_1m = ¶m_1m; + } + + if (param->options & BT_LE_SCAN_OPT_CODED) { + uint16_t interval = param->interval_coded ? + param->interval_coded : + param->interval; + uint16_t window = param->window_coded ? + param->window_coded : + param->window; + + param_coded.type = param->type; + param_coded.interval = sys_cpu_to_le16(interval); + param_coded.window = sys_cpu_to_le16(window); + phy_coded = ¶m_coded; + } + + err = start_le_scan_ext(phy_1m, phy_coded, param->timeout); + } else { + if (param->timeout) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return -ENOTSUP; + } + + err = start_le_scan_legacy(param->type, param->interval, + param->window); + } + + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + + scan_dev_found_cb = cb; + + return 0; +} + +int bt_le_scan_stop(void) +{ + /* Return if active scanning is already disabled */ + if (!atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + scan_dev_found_cb = NULL; + + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && + atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED)) { + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + +#if defined(CONFIG_BT_SMP) + bt_id_pending_keys_update(); +#endif + } + + return bt_le_scan_update(false); +} + +void bt_le_scan_cb_register(struct bt_le_scan_cb *cb) +{ + sys_slist_append(&scan_cbs, &cb->node); +} + +void bt_le_scan_cb_unregister(struct bt_le_scan_cb *cb) +{ + sys_slist_find_and_remove(&scan_cbs, &cb->node); +} + +#if defined(CONFIG_BT_PER_ADV_SYNC) +uint8_t bt_le_per_adv_sync_get_index(struct bt_le_per_adv_sync *per_adv_sync) +{ + ptrdiff_t index = per_adv_sync - per_adv_sync_pool; + + __ASSERT(0 <= index && index < ARRAY_SIZE(per_adv_sync_pool), + "Invalid per_adv_sync pointer"); + return (uint8_t)index; +} + +int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param, + struct bt_le_per_adv_sync **out_sync) +{ + struct bt_hci_cp_le_per_adv_create_sync *cp; + struct net_buf *buf; + struct bt_le_per_adv_sync *per_adv_sync; + int err; + + if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (get_pending_per_adv_sync()) { + return -EBUSY; + } + + if (param->sid > BT_GAP_SID_MAX || + param->skip > BT_GAP_PER_ADV_MAX_MAX_SKIP || + param->timeout > BT_GAP_PER_ADV_MAX_MAX_TIMEOUT) { + return -EINVAL; + } + + per_adv_sync = per_adv_sync_new(); + if (!per_adv_sync) { + return -ENOMEM; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, sizeof(*cp)); + if (!buf) { + per_adv_sync_delete(per_adv_sync); + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + + bt_addr_le_copy(&cp->addr, ¶m->addr); + + if (param->options & BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST) { + cp->options |= BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST; + } + + if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOA) { + cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOA; + } + + if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_1US) { + cp->cte_type |= + BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_1US; + } + + if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_2US) { + cp->cte_type |= + BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_2US; + } + + if (param->options & BT_LE_PER_ADV_SYNC_OPT_SYNC_ONLY_CONST_TONE_EXT) { + cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE; + } + + if (param->options & + BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED) { + cp->options |= + BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED; + + atomic_set_bit(per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED); + } + + cp->sid = param->sid; + cp->skip = sys_cpu_to_le16(param->skip); + cp->sync_timeout = sys_cpu_to_le16(param->timeout); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, buf, NULL); + if (err) { + per_adv_sync_delete(per_adv_sync); + return err; + } + + atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING); + + /* Syncing requires that scan is enabled. If the caller doesn't enable + * scan first, we enable it here, and disable it once the sync has been + * established. We don't need to use any callbacks since we rely on + * the advertiser address in the sync params. + */ + if (!atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = bt_le_scan_update(true); + + if (err) { + bt_le_per_adv_sync_delete(per_adv_sync); + return err; + } + } + + *out_sync = per_adv_sync; + bt_addr_le_copy(&per_adv_sync->addr, ¶m->addr); + per_adv_sync->sid = param->sid; + + return 0; +} + +static int bt_le_per_adv_sync_create_cancel( + struct bt_le_per_adv_sync *per_adv_sync) +{ + struct net_buf *buf; + int err; + + if (get_pending_per_adv_sync() != per_adv_sync) { + return -EINVAL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, 0); + if (!buf) { + return -ENOBUFS; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, buf, + NULL); + if (err) { + return err; + } + + return 0; +} + +static int bt_le_per_adv_sync_terminate(struct bt_le_per_adv_sync *per_adv_sync) +{ + int err; + + if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { + return -EINVAL; + } + + err = per_adv_sync_terminate(per_adv_sync->handle); + + if (err) { + return err; + } + + return 0; +} + +int bt_le_per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync) +{ + int err = 0; + + if (atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { + err = bt_le_per_adv_sync_terminate(per_adv_sync); + + if (!err) { + per_adv_sync_delete(per_adv_sync); + } + } else if (get_pending_per_adv_sync() == per_adv_sync) { + err = bt_le_per_adv_sync_create_cancel(per_adv_sync); + /* Delete of the per_adv_sync will be done in the event + * handler when cancelling */ + } + + return err; +} + +void bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb) +{ + sys_slist_append(&pa_sync_cbs, &cb->node); +} + +static int bt_le_set_per_adv_recv_enable( + struct bt_le_per_adv_sync *per_adv_sync, bool enable) +{ + struct bt_hci_cp_le_set_per_adv_recv_enable *cp; + struct bt_le_per_adv_sync_cb *listener; + struct bt_le_per_adv_sync_state_info info; + struct net_buf *buf; + struct bt_hci_cmd_state_set state; + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) { + return -EINVAL; + } + + if ((enable && !atomic_test_bit(per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED)) || + (!enable && atomic_test_bit(per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED))) { + return -EALREADY; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(per_adv_sync->handle); + cp->enable = enable ? 1 : 0; + + bt_hci_cmd_state_set_init(buf, &state, per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED, + enable); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE, + buf, NULL); + + if (err) { + return err; + } + + info.recv_enabled = !atomic_test_bit(per_adv_sync->flags, + BT_PER_ADV_SYNC_RECV_DISABLED); + + SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { + if (listener->state_changed) { + listener->state_changed(per_adv_sync, &info); + } + } + + return 0; +} + +int bt_le_per_adv_sync_recv_enable(struct bt_le_per_adv_sync *per_adv_sync) +{ + return bt_le_set_per_adv_recv_enable(per_adv_sync, true); +} + +int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync) +{ + return bt_le_set_per_adv_recv_enable(per_adv_sync, false); +} + +#if defined(CONFIG_BT_CONN) +int bt_le_per_adv_sync_transfer(const struct bt_le_per_adv_sync *per_adv_sync, + const struct bt_conn *conn, + uint16_t service_data) +{ + struct bt_hci_cp_le_per_adv_sync_transfer *cp; + struct net_buf *buf; + + if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) { + return -EOPNOTSUPP; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->conn_handle = sys_cpu_to_le16(conn->handle); + cp->sync_handle = sys_cpu_to_le16(per_adv_sync->handle); + cp->service_data = sys_cpu_to_le16(service_data); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER, buf, + NULL); +} + +static bool valid_past_param( + const struct bt_le_per_adv_sync_transfer_param *param) +{ + if (param->skip > 0x01f3 || + param->timeout < 0x000A || + param->timeout > 0x4000) { + return false; + } + + return true; +} + +static int past_param_set(const struct bt_conn *conn, uint8_t mode, + uint16_t skip, uint16_t timeout, uint8_t cte_type) +{ + struct bt_hci_cp_le_past_param *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_PAST_PARAM, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->conn_handle = sys_cpu_to_le16(conn->handle); + cp->mode = mode; + cp->skip = sys_cpu_to_le16(skip); + cp->timeout = sys_cpu_to_le16(timeout); + cp->cte_type = cte_type; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PAST_PARAM, buf, NULL); +} + +static int default_past_param_set(uint8_t mode, uint16_t skip, uint16_t timeout, + uint8_t cte_type) +{ + struct bt_hci_cp_le_default_past_param *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->mode = mode; + cp->skip = sys_cpu_to_le16(skip); + cp->timeout = sys_cpu_to_le16(timeout); + cp->cte_type = cte_type; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, buf, NULL); +} + +int bt_le_per_adv_sync_transfer_subscribe( + const struct bt_conn *conn, + const struct bt_le_per_adv_sync_transfer_param *param) +{ + uint8_t cte_type = 0; + + if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { + return -EOPNOTSUPP; + } + + if (!valid_past_param(param)) { + return -EINVAL; + } + + if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOA) { + cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOA; + } + + if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_1US) { + cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_1US; + } + + if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_2US) { + cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_2US; + } + + if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_ONLY_CTE) { + cte_type |= BT_HCI_LE_PAST_CTE_TYPE_ONLY_CTE; + } + + if (conn) { + return past_param_set(conn, BT_HCI_LE_PAST_MODE_SYNC, + param->skip, param->timeout, cte_type); + } else { + return default_past_param_set(BT_HCI_LE_PAST_MODE_SYNC, + param->skip, param->timeout, + cte_type); + } +} + +int bt_le_per_adv_sync_transfer_unsubscribe(const struct bt_conn *conn) +{ + if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { + return -EOPNOTSUPP; + } + + if (conn) { + return past_param_set(conn, BT_HCI_LE_PAST_MODE_NO_SYNC, 0, + 0x0a, 0); + } else { + return default_past_param_set(BT_HCI_LE_PAST_MODE_NO_SYNC, 0, + 0x0a, 0); + } +} +#endif /* CONFIG_BT_CONN */ + +int bt_le_per_adv_list_add(const bt_addr_le_t *addr, uint8_t sid) +{ + struct bt_hci_cp_le_add_dev_to_per_adv_list *cp; + struct net_buf *buf; + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->addr, addr); + cp->sid = sid; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST, buf, + NULL); + if (err) { + BT_ERR("Failed to add device to periodic advertiser list"); + + return err; + } + + return 0; +} + +int bt_le_per_adv_list_remove(const bt_addr_le_t *addr, uint8_t sid) +{ + struct bt_hci_cp_le_rem_dev_from_per_adv_list *cp; + struct net_buf *buf; + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->addr, addr); + cp->sid = sid; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST, buf, + NULL); + if (err) { + BT_ERR("Failed to remove device from periodic advertiser list"); + return err; + } + + return 0; +} + +int bt_le_per_adv_list_clear(void) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CLEAR_PER_ADV_LIST, NULL, NULL); + if (err) { + BT_ERR("Failed to clear periodic advertiser list"); + return err; + } + + return 0; +} +#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ diff --git a/subsys/bluetooth/host/scan.h b/subsys/bluetooth/host/scan.h new file mode 100644 index 00000000000..650850540b3 --- /dev/null +++ b/subsys/bluetooth/host/scan.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017-2021 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_scan_reset(void); + +bool bt_id_scan_random_addr_check(void); + +int bt_le_scan_set_enable(uint8_t enable);