/* * 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 #include #include #include #include #include #include #include #include #include #include "addr_internal.h" #include "hci_core.h" #include "conn_internal.h" #include "direction_internal.h" #include "id.h" #include "common/bt_str.h" #include "scan.h" #define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_scan); struct scanner_state { ATOMIC_DEFINE(scan_flags, BT_LE_SCAN_USER_NUM_FLAGS); struct bt_le_scan_param explicit_scan_param; struct bt_le_scan_param used_scan_param; struct k_mutex scan_update_mutex; struct k_mutex scan_explicit_params_mutex; }; enum scan_action { SCAN_ACTION_NONE, SCAN_ACTION_START, SCAN_ACTION_STOP, SCAN_ACTION_UPDATE, }; static bt_le_scan_cb_t *scan_dev_found_cb; static sys_slist_t scan_cbs = SYS_SLIST_STATIC_INIT(&scan_cbs); static struct scanner_state scan_state; #if defined(CONFIG_BT_EXT_ADV) /* A buffer used to reassemble advertisement data from the controller. */ NET_BUF_SIMPLE_DEFINE(ext_scan_buf, CONFIG_BT_EXT_SCAN_BUF_SIZE); struct fragmented_advertiser { bt_addr_le_t addr; uint8_t sid; enum { FRAG_ADV_INACTIVE, FRAG_ADV_REASSEMBLING, FRAG_ADV_DISCARDING, } state; }; static struct fragmented_advertiser reassembling_advertiser; static bool fragmented_advertisers_equal(const struct fragmented_advertiser *a, const bt_addr_le_t *addr, uint8_t sid) { /* Two advertisers are equal if they are the same adv set from the same device */ return a->sid == sid && bt_addr_le_eq(&a->addr, addr); } /* Sets the address and sid of the advertiser to be reassembled. */ static void init_reassembling_advertiser(const bt_addr_le_t *addr, uint8_t sid) { bt_addr_le_copy(&reassembling_advertiser.addr, addr); reassembling_advertiser.sid = sid; reassembling_advertiser.state = FRAG_ADV_REASSEMBLING; } static void reset_reassembling_advertiser(void) { net_buf_simple_reset(&ext_scan_buf); reassembling_advertiser.state = FRAG_ADV_INACTIVE; } #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_softreset(void) { scan_dev_found_cb = NULL; #if defined(CONFIG_BT_EXT_ADV) reset_reassembling_advertiser(); #endif } void bt_scan_reset(void) { memset(&scan_state, 0x0, sizeof(scan_state)); k_mutex_init(&scan_state.scan_update_mutex); k_mutex_init(&scan_state.scan_explicit_params_mutex); bt_scan_softreset(); } static int cmd_le_set_ext_scan_enable(bool enable, bool filter_duplicates, 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)); cp->filter_dup = filter_duplicates; 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 cmd_le_set_scan_enable_legacy(bool enable, bool filter_duplicates) { 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)); cp->filter_dup = filter_duplicates; 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; } static int cmd_le_set_scan_enable(bool enable, bool filter_duplicates) { if (IS_ENABLED(CONFIG_BT_EXT_ADV) && BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features)) { return cmd_le_set_ext_scan_enable(enable, filter_duplicates, 0); } return cmd_le_set_scan_enable_legacy(enable, filter_duplicates); } int bt_le_scan_set_enable(uint8_t enable) { return cmd_le_set_scan_enable(enable, scan_state.used_scan_param.options & BT_LE_SCAN_OPT_FILTER_DUPLICATE); } static int start_le_scan_ext(struct bt_le_scan_param *scan_param) { 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 (!(scan_param->options & BT_LE_SCAN_OPT_NO_1M)) { param_1m.type = scan_param->type; param_1m.interval = sys_cpu_to_le16(scan_param->interval); param_1m.window = sys_cpu_to_le16(scan_param->window); phy_1m = ¶m_1m; } if (scan_param->options & BT_LE_SCAN_OPT_CODED) { uint16_t interval = scan_param->interval_coded ? scan_param->interval_coded : scan_param->interval; uint16_t window = scan_param->window_coded ? scan_param->window_coded : scan_param->window; param_coded.type = scan_param->type; param_coded.interval = sys_cpu_to_le16(interval); param_coded.window = sys_cpu_to_le16(window); phy_coded = ¶m_coded; } 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 (scan_param->timeout > 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; set_param->filter_policy = scan_param->options & BT_LE_SCAN_OPT_FILTER_ACCEPT_LIST ? BT_HCI_LE_SCAN_FP_BASIC_FILTER : BT_HCI_LE_SCAN_FP_BASIC_NO_FILTER; 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 = cmd_le_set_ext_scan_enable(BT_HCI_LE_SCAN_ENABLE, scan_param->options & BT_LE_SCAN_OPT_FILTER_DUPLICATE, scan_param->timeout); if (err) { return err; } return 0; } static int start_le_scan_legacy(struct bt_le_scan_param *param) { 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 = param->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(param->interval); set_param.window = sys_cpu_to_le16(param->window); if (IS_ENABLED(CONFIG_BT_FILTER_ACCEPT_LIST) && param->options & BT_LE_SCAN_OPT_FILTER_ACCEPT_LIST) { set_param.filter_policy = BT_HCI_LE_SCAN_FP_BASIC_FILTER; } else { set_param.filter_policy = BT_HCI_LE_SCAN_FP_BASIC_NO_FILTER; } active_scan = param->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 = cmd_le_set_scan_enable(BT_HCI_LE_SCAN_ENABLE, param->options & BT_LE_SCAN_OPT_FILTER_DUPLICATE); if (err) { return err; } return 0; } bool bt_le_scan_active_scanner_running(void) { return atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && scan_state.used_scan_param.type == BT_LE_SCAN_TYPE_ACTIVE; } static void select_scan_params(struct bt_le_scan_param *scan_param) { /* From high priority to low priority: select parameters */ /* 1. Priority: explicitly chosen parameters */ if (atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_EXPLICIT_SCAN)) { memcpy(scan_param, &scan_state.explicit_scan_param, sizeof(*scan_param)); } /* Below this, the scanner module chooses the parameters. */ /* 2. Priority: reuse parameters from initiator */ else if (atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING)) { *scan_param = (struct bt_le_scan_param){ .type = BT_LE_SCAN_TYPE_PASSIVE, .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval = bt_dev.create_param.interval, .window = bt_dev.create_param.window, .timeout = 0, .interval_coded = bt_dev.create_param.interval_coded, .window_coded = bt_dev.create_param.window_coded, }; } /* 3. Priority: choose custom parameters */ else { *scan_param = (struct bt_le_scan_param){ .type = BT_LE_SCAN_TYPE_PASSIVE, .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval = CONFIG_BT_BACKGROUND_SCAN_INTERVAL, .window = CONFIG_BT_BACKGROUND_SCAN_WINDOW, .timeout = 0, .interval_coded = 0, .window_coded = 0, }; if (BT_FEAT_LE_PHY_CODED(bt_dev.le.features)) { scan_param->options |= BT_LE_SCAN_OPT_CODED; } if (atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_PER_SYNC) || atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_CONN)) { scan_param->window = BT_GAP_SCAN_FAST_WINDOW; scan_param->interval = BT_GAP_SCAN_FAST_INTERVAL; } } } static int start_scan(struct bt_le_scan_param *scan_param) { if (IS_ENABLED(CONFIG_BT_EXT_ADV) && BT_DEV_FEAT_LE_EXT_ADV(bt_dev.le.features)) { return start_le_scan_ext(scan_param); } return start_le_scan_legacy(scan_param); } static bool is_already_using_same_params(struct bt_le_scan_param *scan_param) { return !memcmp(scan_param, &scan_state.used_scan_param, sizeof(*scan_param)); } static enum scan_action get_scan_action(struct bt_le_scan_param *scan_param) { bool is_scanning = atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING); /* Check if there is reason to have the scanner running */ if (atomic_get(scan_state.scan_flags) != 0) { if (is_scanning) { if (is_already_using_same_params(scan_param)) { /* Already scanning with the desired parameters */ return SCAN_ACTION_NONE; } else { return SCAN_ACTION_UPDATE; } } else { return SCAN_ACTION_START; } } else { /* Scanner should not run */ if (is_scanning) { return SCAN_ACTION_STOP; } else { return SCAN_ACTION_NONE; } } } static int scan_update(void) { int32_t err; struct bt_le_scan_param scan_param; /* Prevent partial updates of the scanner state. */ err = k_mutex_lock(&scan_state.scan_update_mutex, K_NO_WAIT); if (err) { return err; } select_scan_params(&scan_param); enum scan_action action = get_scan_action(&scan_param); /* start/stop/update if required and allowed */ switch (action) { case SCAN_ACTION_NONE: break; case SCAN_ACTION_STOP: err = cmd_le_set_scan_enable(BT_HCI_LE_SCAN_DISABLE, BT_HCI_LE_SCAN_FILTER_DUP_DISABLE); if (err) { LOG_DBG("Could not stop scanner: %d", err); break; } memset(&scan_state.used_scan_param, 0x0, sizeof(scan_state.used_scan_param)); break; case SCAN_ACTION_UPDATE: err = cmd_le_set_scan_enable(BT_HCI_LE_SCAN_DISABLE, BT_HCI_LE_SCAN_FILTER_DUP_DISABLE); if (err) { LOG_DBG("Could not stop scanner to update: %d", err); break; } __fallthrough; case SCAN_ACTION_START: err = start_scan(&scan_param); if (err) { LOG_DBG("Could not start scanner: %d", err); break; } memcpy(&scan_state.used_scan_param, &scan_param, sizeof(scan_param)); break; } k_mutex_unlock(&scan_state.scan_update_mutex); return err; } static int scan_check_if_state_allowed(enum bt_le_scan_user flag) { /* check if state is already set */ if (atomic_test_bit(scan_state.scan_flags, flag)) { return -EALREADY; } if (flag == BT_LE_SCAN_USER_EXPLICIT_SCAN && !BT_LE_STATES_SCAN_INIT(bt_dev.le.states) && atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING)) { return -EPERM; } return 0; } int bt_le_scan_user_add(enum bt_le_scan_user flag) { uint32_t err; if (flag == BT_LE_SCAN_USER_NONE) { /* Only check if the scanner parameters should be updated / the scanner should be * started. This is mainly triggered once connections are established. */ return scan_update(); } err = scan_check_if_state_allowed(flag); if (err) { return err; } atomic_set_bit(scan_state.scan_flags, flag); err = scan_update(); if (err) { atomic_clear_bit(scan_state.scan_flags, flag); } return err; } int bt_le_scan_user_remove(enum bt_le_scan_user flag) { if (flag == BT_LE_SCAN_USER_NONE) { /* Only check if the scanner parameters should be updated / the scanner should be * started. This is mainly triggered once connections are established. */ } else { atomic_clear_bit(scan_state.scan_flags, flag); } return scan_update(); } #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; int err; /* No connections are allowed during explicit scanning * when the controller does not support concurrent scanning and initiating. */ if (!BT_LE_STATES_SCAN_INIT(bt_dev.le.states) && atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_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_SCAN_BEFORE_INITIATING); if (!conn) { return; } /* Stop the scanner if there is no other reason to have it running. * Ignore possible failures here, since the user is guaranteed to be removed * and the scanner state is updated once the initiator starts / stops. */ err = bt_le_scan_user_remove(BT_LE_SCAN_USER_CONN); if (err) { LOG_DBG("Error while removing conn user from scanner (%d)", err); } bt_addr_le_copy(&conn->le.resp_addr, addr); if (bt_le_create_conn(conn)) { goto failed; } bt_conn_set_state(conn, BT_CONN_INITIATING); bt_conn_unref(conn); return; failed: conn->err = BT_HCI_ERR_UNSPECIFIED; bt_conn_set_state(conn, BT_CONN_DISCONNECTED); bt_conn_unref(conn); /* Just a best-effort check if the scanner should be started. */ err = bt_le_scan_user_remove(BT_LE_SCAN_USER_NONE); if (err) { LOG_WRN("Error while updating the scanner (%d)", err); } } #endif /* CONFIG_BT_CENTRAL */ /* Convert Legacy adv report evt_type field to adv props */ static uint8_t get_adv_props_legacy(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_simple *buf, uint16_t len) { struct bt_le_scan_cb *listener, *next; struct net_buf_simple_state state; bt_addr_le_t id_addr; LOG_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(scan_state.scan_flags, BT_LE_SCAN_USER_EXPLICIT_SCAN) && (info->adv_props & BT_HCI_LE_ADV_PROP_DIRECT)) { LOG_DBG("Dropped direct adv report"); return; } if (bt_addr_le_is_resolved(addr)) { bt_addr_le_copy_resolved(&id_addr, addr); } 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)); } if (scan_dev_found_cb) { net_buf_simple_save(buf, &state); buf->len = len; scan_dev_found_cb(&id_addr, info->rssi, info->adv_type, buf); net_buf_simple_restore(buf, &state); } info->addr = &id_addr; SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&scan_cbs, listener, next, node) { if (listener->recv) { net_buf_simple_save(buf, &state); buf->len = len; listener->recv(info, buf); net_buf_simple_restore(buf, &state); } } /* Clear pointer to this stack frame before returning to calling function */ info->addr = NULL; #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; int err = bt_le_scan_user_remove(BT_LE_SCAN_USER_EXPLICIT_SCAN); if (err) { k_yield(); err = bt_le_scan_user_remove(BT_LE_SCAN_USER_EXPLICIT_SCAN); } if (err) { LOG_WRN("Could not stop the explicit scanner (%d)", err); } 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; } } /* Convert extended adv report evt_type field to adv props */ static uint16_t get_adv_props_extended(uint16_t evt_type) { /* Converts from BT_HCI_LE_ADV_EVT_TYPE_* to BT_GAP_ADV_PROP_* * The first 4 bits are the same (conn, scan, direct, scan_rsp). * Bit 4 must be flipped as the meaning of 1 is opposite (legacy -> extended) * The rest of the bits are zeroed out. */ return (evt_type ^ BT_HCI_LE_ADV_EVT_TYPE_LEGACY) & BIT_MASK(5); } static void create_ext_adv_info(struct bt_hci_evt_le_ext_advertising_info const *const evt, struct bt_le_scan_recv_info *const scan_info) { scan_info->primary_phy = bt_get_phy(evt->prim_phy); scan_info->secondary_phy = bt_get_phy(evt->sec_phy); scan_info->tx_power = evt->tx_power; scan_info->rssi = evt->rssi; scan_info->sid = evt->sid; scan_info->interval = sys_le16_to_cpu(evt->interval); scan_info->adv_type = get_adv_type(sys_le16_to_cpu(evt->evt_type)); scan_info->adv_props = get_adv_props_extended(sys_le16_to_cpu(evt->evt_type)); } void bt_hci_le_adv_ext_report(struct net_buf *buf) { uint8_t num_reports = net_buf_pull_u8(buf); LOG_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 scan_info; uint16_t data_status; uint16_t evt_type; bool is_report_complete; bool more_to_come; bool is_new_advertiser; if (!atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_EXPLICIT_SCAN)) { /* The application has not requested explicit scan, so it is not expecting * advertising reports. Discard, and reset the reassembler if not inactive * This is done in the loop as this flag can change between each iteration, * and it is not uncommon that scanning is disabled in the callback called * from le_adv_recv */ if (reassembling_advertiser.state != FRAG_ADV_INACTIVE) { reset_reassembling_advertiser(); } break; } if (buf->len < sizeof(*evt)) { LOG_ERR("Unexpected end of buffer"); break; } evt = net_buf_pull_mem(buf, sizeof(*evt)); evt_type = sys_le16_to_cpu(evt->evt_type); data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(evt_type); is_report_complete = data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE; more_to_come = data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL; if (evt->length > buf->len) { LOG_WRN("Adv report corrupted (wants %u out of %u)", evt->length, buf->len); net_buf_reset(buf); if (evt_type & BT_HCI_LE_ADV_EVT_TYPE_LEGACY) { return; } /* Start discarding irrespective of the `more_to_come` flag. We * assume we may have lost a partial adv report in the truncated * data. */ reassembling_advertiser.state = FRAG_ADV_DISCARDING; return; } if (evt_type & BT_HCI_LE_ADV_EVT_TYPE_LEGACY) { /* Legacy advertising reports are complete. * Create event immediately. */ create_ext_adv_info(evt, &scan_info); le_adv_recv(&evt->addr, &scan_info, &buf->b, evt->length); goto cont; } is_new_advertiser = reassembling_advertiser.state == FRAG_ADV_INACTIVE || !fragmented_advertisers_equal(&reassembling_advertiser, &evt->addr, evt->sid); if (is_new_advertiser && is_report_complete) { /* Only advertising report from this advertiser. * Create event immediately. */ create_ext_adv_info(evt, &scan_info); le_adv_recv(&evt->addr, &scan_info, &buf->b, evt->length); goto cont; } if (is_new_advertiser && reassembling_advertiser.state == FRAG_ADV_REASSEMBLING) { LOG_WRN("Received an incomplete advertising report while reassembling " "advertising reports from a different advertiser. The advertising " "report is discarded and future scan results may be incomplete. " "Interleaving of fragmented advertising reports from different " "advertisers is not yet supported."); goto cont; } if (data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE) { /* Got HCI_LE_Extended_Advertising_Report: Incomplete, data truncated, no * more to come. This means the Controller is aborting the reassembly. We * discard the partially received report, and the application is not * notified. * * See the Controller's documentation for possible reasons for aborting. * Hint: CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX. */ LOG_DBG("Discarding incomplete advertisement."); reset_reassembling_advertiser(); goto cont; } if (is_new_advertiser) { /* We are not reassembling reports from an advertiser and * this is the first report from the new advertiser. * Initialize the new advertiser. */ __ASSERT_NO_MSG(reassembling_advertiser.state == FRAG_ADV_INACTIVE); init_reassembling_advertiser(&evt->addr, evt->sid); } if (evt->length + ext_scan_buf.len > ext_scan_buf.size) { /* The report does not fit in the reassemby buffer * Discard this and future reports from the advertiser. */ reassembling_advertiser.state = FRAG_ADV_DISCARDING; } if (reassembling_advertiser.state == FRAG_ADV_DISCARDING) { if (!more_to_come) { /* We do no longer need to keep track of this advertiser as * all the expected data is received. */ reset_reassembling_advertiser(); } goto cont; } net_buf_simple_add_mem(&ext_scan_buf, buf->data, evt->length); if (more_to_come) { /* The controller will send additional reports to be reassembled */ continue; } /* No more data coming from the controller. * Create event. */ __ASSERT_NO_MSG(is_report_complete); create_ext_adv_info(evt, &scan_info); le_adv_recv(&evt->addr, &scan_info, &ext_scan_buf, ext_scan_buf.len); /* We do no longer need to keep track of this advertiser. */ reset_reassembling_advertiser(); cont: 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); #if CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0 net_buf_simple_init_with_data(&per_adv_sync->reassembly, per_adv_sync->reassembly_data, CONFIG_BT_PER_ADV_SYNC_BUF_SIZE); net_buf_simple_reset(&per_adv_sync->reassembly); #endif /* CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0 */ 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; } void bt_periodic_sync_disable(void) { for (size_t i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { per_adv_sync_delete(&per_adv_sync_pool[i]); } } struct bt_le_per_adv_sync *bt_hci_per_adv_sync_lookup_handle(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_recv(struct bt_le_per_adv_sync *per_adv_sync, struct net_buf_simple *buf, const struct bt_le_per_adv_sync_recv_info *info) { struct net_buf_simple_state state; struct bt_le_per_adv_sync_cb *listener; SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { if (listener->recv) { net_buf_simple_save(buf, &state); listener->recv(per_adv_sync, info, buf); net_buf_simple_restore(buf, &state); } } } #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) && (CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0) static void bt_hci_le_per_adv_report_recv_failure(struct bt_le_per_adv_sync *per_adv_sync, const struct bt_le_per_adv_sync_recv_info *info) { struct bt_le_per_adv_sync_cb *listener; SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { if (listener->recv) { listener->recv(per_adv_sync, info, NULL); } } } #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) && (CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0) */ static void bt_hci_le_per_adv_report_common(struct net_buf *buf) { #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) struct bt_hci_evt_le_per_advertising_report_v2 *evt; #else struct bt_hci_evt_le_per_advertising_report *evt; #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) */ struct bt_le_per_adv_sync *per_adv_sync; struct bt_le_per_adv_sync_recv_info info; if (buf->len < sizeof(*evt)) { LOG_ERR("Unexpected end of buffer"); return; } evt = net_buf_pull_mem(buf, sizeof(*evt)); per_adv_sync = bt_hci_per_adv_sync_lookup_handle(sys_le16_to_cpu(evt->handle)); if (!per_adv_sync) { LOG_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)) { LOG_ERR("Received PA adv report when receive disabled"); return; } info.tx_power = evt->tx_power; info.rssi = evt->rssi; info.cte_type = bt_get_df_cte_type(evt->cte_type); info.addr = &per_adv_sync->addr; info.sid = per_adv_sync->sid; #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) info.periodic_event_counter = sys_le16_to_cpu(evt->periodic_event_counter); info.subevent = evt->subevent; #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ if (!per_adv_sync->report_truncated) { #if CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0 if (net_buf_simple_tailroom(&per_adv_sync->reassembly) < evt->length) { /* The buffer is too small for the entire report. Drop it */ LOG_WRN("Buffer is too small to reassemble the report. " "Use CONFIG_BT_PER_ADV_SYNC_BUF_SIZE to change " "the buffer size."); per_adv_sync->report_truncated = true; net_buf_simple_reset(&per_adv_sync->reassembly); return; } if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE) { if (per_adv_sync->reassembly.len == 0) { /* We have not received any partial data before. * This buffer can be forwarded without an extra copy. */ bt_hci_le_per_adv_report_recv(per_adv_sync, &buf->b, &info); } else { net_buf_simple_add_mem(&per_adv_sync->reassembly, buf->data, evt->length); bt_hci_le_per_adv_report_recv(per_adv_sync, &per_adv_sync->reassembly, &info); net_buf_simple_reset(&per_adv_sync->reassembly); } } else if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE) { LOG_DBG("Received incomplete advertising data. " "Advertising report dropped."); net_buf_simple_reset(&per_adv_sync->reassembly); } else if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL) { net_buf_simple_add_mem(&per_adv_sync->reassembly, buf->data, evt->length); #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) } else if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_RX_FAILED && per_adv_sync->num_subevents) { bt_hci_le_per_adv_report_recv_failure(per_adv_sync, &info); #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ } else { __ASSERT(false, "Invalid data status 0x%02X", evt->data_status); } #else /* CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0 */ if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE) { bt_hci_le_per_adv_report_recv(per_adv_sync, &buf->b, &info); } else { per_adv_sync->report_truncated = true; } #endif /* CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0 */ } else if (evt->data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE) { per_adv_sync->report_truncated = false; } } void bt_hci_le_per_adv_report(struct net_buf *buf) { if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_RSP)) { LOG_ERR("The controller shall raise the latest unmasked version of the event"); return; } bt_hci_le_per_adv_report_common(buf); } 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 per_adv_sync_terminated(struct bt_le_per_adv_sync *per_adv_sync, uint8_t reason) { /* Terminate the PA sync and notify app */ const struct bt_le_per_adv_sync_term_info term_info = { .addr = &per_adv_sync->addr, .sid = per_adv_sync->sid, .reason = reason, }; struct bt_le_per_adv_sync_cb *listener; /* 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); } } } static void bt_hci_le_per_adv_sync_established_common(struct net_buf *buf) { #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) struct bt_hci_evt_le_per_adv_sync_established_v2 *evt = (struct bt_hci_evt_le_per_adv_sync_established_v2 *)buf->data; #else struct bt_hci_evt_le_per_adv_sync_established *evt = (struct bt_hci_evt_le_per_adv_sync_established *)buf->data; #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) */ 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; bt_addr_le_t id_addr; bool unexpected_evt; 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_user_remove(BT_LE_SCAN_USER_PER_SYNC); if (err) { LOG_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 { LOG_ERR("Unexpected per adv sync cancelled event"); } return; } if (bt_addr_le_is_resolved(&evt->adv_addr)) { bt_addr_le_copy_resolved(&id_addr, &evt->adv_addr); } else { bt_addr_le_copy(&id_addr, bt_lookup_id_addr(BT_ID_DEFAULT, &evt->adv_addr)); } if (!pending_per_adv_sync || (!atomic_test_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING_USE_LIST) && ((pending_per_adv_sync->sid != evt->sid) || !bt_addr_le_eq(&pending_per_adv_sync->addr, &id_addr)))) { LOG_ERR("Unexpected per adv sync established event"); /* Request terminate of pending periodic advertising in controller */ per_adv_sync_terminate(sys_le16_to_cpu(evt->handle)); unexpected_evt = true; } else { unexpected_evt = false; } if (unexpected_evt || evt->status != BT_HCI_ERR_SUCCESS) { if (pending_per_adv_sync) { const uint8_t reason = unexpected_evt ? BT_HCI_ERR_UNSPECIFIED : evt->status; if (atomic_test_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING_USE_LIST)) { /* Update the addr and sid for the callback * Already set if not using the sync list */ bt_addr_le_copy(&pending_per_adv_sync->addr, &id_addr); pending_per_adv_sync->sid = evt->sid; } per_adv_sync_terminated(pending_per_adv_sync, reason); } return; } pending_per_adv_sync->report_truncated = false; 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 = bt_get_phy(evt->phy); memset(&sync_info, 0, sizeof(sync_info)); sync_info.interval = pending_per_adv_sync->interval; sync_info.phy = pending_per_adv_sync->phy; if (atomic_test_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING_USE_LIST)) { /* Now we know which address and SID we synchronized to. */ pending_per_adv_sync->sid = evt->sid; if (bt_addr_le_is_resolved(&pending_per_adv_sync->addr)) { bt_addr_le_copy_resolved(&pending_per_adv_sync->addr, &id_addr); } else { bt_addr_le_copy(&pending_per_adv_sync->addr, &id_addr); } } sync_info.addr = &pending_per_adv_sync->addr; sync_info.sid = pending_per_adv_sync->sid; #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) sync_info.num_subevents = evt->num_subevents; sync_info.subevent_interval = evt->subevent_interval; sync_info.response_slot_delay = evt->response_slot_delay; sync_info.response_slot_spacing = evt->response_slot_spacing; pending_per_adv_sync->num_subevents = evt->num_subevents; pending_per_adv_sync->subevent_interval = evt->subevent_interval; pending_per_adv_sync->response_slot_delay = evt->response_slot_delay; pending_per_adv_sync->response_slot_spacing = evt->response_slot_spacing; #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ 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_established(struct net_buf *buf) { if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_RSP)) { LOG_ERR("The controller shall raise the latest unmasked version of the event"); return; } bt_hci_le_per_adv_sync_established_common(buf); } #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) int bt_le_per_adv_sync_subevent(struct bt_le_per_adv_sync *per_adv_sync, struct bt_le_per_adv_sync_subevent_params *params) { struct bt_hci_cp_le_set_pawr_sync_subevent *cp; struct net_buf *buf; if (params->num_subevents > BT_HCI_PAWR_SUBEVENT_MAX) { return -EINVAL; } buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_SYNC_SUBEVENT, sizeof(*cp) + params->num_subevents); if (!buf) { return -ENOBUFS; } cp = net_buf_add(buf, sizeof(*cp)); (void)memset(cp, 0, sizeof(*cp)); cp->sync_handle = sys_cpu_to_le16(per_adv_sync->handle); cp->periodic_adv_properties = sys_cpu_to_le16(params->properties); cp->num_subevents = params->num_subevents; net_buf_add_mem(buf, params->subevents, cp->num_subevents); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_SYNC_SUBEVENT, buf, NULL); } int bt_le_per_adv_set_response_data(struct bt_le_per_adv_sync *per_adv_sync, const struct bt_le_per_adv_response_params *param, const struct net_buf_simple *data) { struct bt_hci_cp_le_set_pawr_response_data *cp; struct net_buf *buf; if (per_adv_sync->num_subevents == 0) { return -EINVAL; } if (param->request_subevent >= per_adv_sync->num_subevents) { return -EINVAL; } if (param->response_subevent >= per_adv_sync->num_subevents) { return -EINVAL; } if (data->len > 247) { return -EINVAL; } buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_RESPONSE_DATA, sizeof(*cp) + data->len); if (!buf) { return -ENOBUFS; } cp = net_buf_add(buf, sizeof(*cp)); (void)memset(cp, 0, sizeof(*cp)); cp->sync_handle = sys_cpu_to_le16(per_adv_sync->handle); cp->request_event = sys_cpu_to_le16(param->request_event); cp->request_subevent = param->request_subevent; cp->response_subevent = param->response_subevent; cp->response_slot = param->response_slot; cp->response_data_length = data->len; net_buf_add_mem(buf, data->data, cp->response_data_length); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_RESPONSE_DATA, buf, NULL); } #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ 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 *per_adv_sync; per_adv_sync = bt_hci_per_adv_sync_lookup_handle(sys_le16_to_cpu(evt->handle)); if (!per_adv_sync) { LOG_ERR("Unknown handle 0x%04Xfor periodic adv sync lost", sys_le16_to_cpu(evt->handle)); return; } /* There is no status in the per. adv. sync lost event */ per_adv_sync_terminated(per_adv_sync, BT_HCI_ERR_UNSPECIFIED); } #if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER) static uint8_t conn_past_modes[CONFIG_BT_MAX_CONN]; static uint8_t default_past_mode; static void past_disconnected_cb(struct bt_conn *conn, uint8_t reason) { /* The core spec does not explicit state that the mode of a connection handle is cleared on * disconnect, but let's assume it is. */ conn_past_modes[bt_conn_index(conn)] = BT_HCI_LE_PAST_MODE_NO_SYNC; } BT_CONN_CB_DEFINE(past_conn_callbacks) = { .disconnected = past_disconnected_cb, }; static void bt_hci_le_past_received_common(struct net_buf *buf) { #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) struct bt_hci_evt_le_past_received_v2 *evt = (struct bt_hci_evt_le_past_received_v2 *)buf->data; #else struct bt_hci_evt_le_past_received *evt = (struct bt_hci_evt_le_past_received *)buf->data; #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) */ 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; bt_addr_le_t id_addr; if (evt->status) { /* No sync created, don't notify app */ LOG_DBG("PAST receive failed with status 0x%02X %s", evt->status, bt_hci_err_to_str(evt->status)); return; } sync_info.conn = bt_conn_lookup_handle( sys_le16_to_cpu(evt->conn_handle), BT_CONN_TYPE_LE); if (!sync_info.conn) { LOG_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) { LOG_WRN("Could not allocate new PA sync from PAST"); per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle)); bt_conn_unref(sync_info.conn); return; } atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED); if (bt_addr_le_is_resolved(&evt->addr)) { bt_addr_le_copy_resolved(&id_addr, &evt->addr); } else { bt_addr_le_copy(&id_addr, bt_lookup_id_addr(BT_ID_DEFAULT, &evt->addr)); } 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 = bt_get_phy(evt->phy); bt_addr_le_copy(&per_adv_sync->addr, &id_addr); per_adv_sync->sid = evt->adv_sid; #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) per_adv_sync->num_subevents = evt->num_subevents; per_adv_sync->subevent_interval = evt->subevent_interval; per_adv_sync->response_slot_delay = evt->response_slot_delay; per_adv_sync->response_slot_spacing = evt->response_slot_spacing; #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) */ sync_info.interval = per_adv_sync->interval; sync_info.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); const uint8_t mode = conn_past_modes[bt_conn_index(sync_info.conn)]; if (mode == BT_HCI_LE_PAST_MODE_NO_SYNC) { /* Use the default parameter mode as the conn specific mode is not set */ sync_info.recv_enabled = default_past_mode == BT_HCI_LE_PAST_MODE_SYNC || default_past_mode == BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES; } else { sync_info.recv_enabled = mode == BT_HCI_LE_PAST_MODE_SYNC || mode == BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES; } #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) sync_info.num_subevents = per_adv_sync->num_subevents; sync_info.subevent_interval = per_adv_sync->subevent_interval; sync_info.response_slot_delay = per_adv_sync->response_slot_delay; sync_info.response_slot_spacing = per_adv_sync->response_slot_spacing; #endif /* defined(CONFIG_BT_PER_ADV_SYNC_RSP) */ SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { if (listener->synced) { listener->synced(per_adv_sync, &sync_info); } } bt_conn_unref(sync_info.conn); } void bt_hci_le_past_received(struct net_buf *buf) { if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_RSP)) { LOG_ERR("The controller shall raise the latest unmasked version of the event"); return; } bt_hci_le_past_received_common(buf); } #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) void bt_hci_le_past_received_v2(struct net_buf *buf) { bt_hci_le_past_received_common(buf); } #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER */ #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) void bt_hci_le_per_adv_sync_established_v2(struct net_buf *buf) { bt_hci_le_per_adv_sync_established_common(buf); } void bt_hci_le_per_adv_report_v2(struct net_buf *buf) { bt_hci_le_per_adv_report_common(buf); } #endif /* CONFIG_BT_PER_ADV_SYNC_RSP */ #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 = bt_hci_per_adv_sync_lookup_handle(sys_le16_to_cpu(evt->sync_handle)); if (!per_adv_sync) { LOG_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 = bt_get_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 /* CONFIG_BT_ISO_BROADCAST */ #if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) static void bt_hci_le_df_connectionless_iq_report_common(uint8_t event, struct net_buf *buf) { int err; struct bt_df_per_adv_sync_iq_samples_report cte_report; struct bt_le_per_adv_sync *per_adv_sync; struct bt_le_per_adv_sync_cb *listener; if (event == BT_HCI_EVT_LE_CONNECTIONLESS_IQ_REPORT) { err = hci_df_prepare_connectionless_iq_report(buf, &cte_report, &per_adv_sync); if (err) { LOG_ERR("Prepare CTE conn IQ report failed %d", err); return; } } else if (IS_ENABLED(CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES) && event == BT_HCI_EVT_VS_LE_CONNECTIONLESS_IQ_REPORT) { err = hci_df_vs_prepare_connectionless_iq_report(buf, &cte_report, &per_adv_sync); if (err) { LOG_ERR("Prepare CTE conn IQ report failed %d", err); return; } } else { LOG_ERR("Unhandled VS connectionless IQ report"); return; } SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) { if (listener->cte_report_cb) { listener->cte_report_cb(per_adv_sync, &cte_report); } } } void bt_hci_le_df_connectionless_iq_report(struct net_buf *buf) { bt_hci_le_df_connectionless_iq_report_common(BT_HCI_EVT_LE_CONNECTIONLESS_IQ_REPORT, buf); } #if defined(CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES) void bt_hci_le_vs_df_connectionless_iq_report(struct net_buf *buf) { bt_hci_le_df_connectionless_iq_report_common(BT_HCI_EVT_VS_LE_CONNECTIONLESS_IQ_REPORT, buf); } #endif /* CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES */ #endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX */ #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; LOG_DBG("Adv number of reports %u", num_reports); while (num_reports--) { struct bt_le_scan_recv_info adv_info; if (!atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_EXPLICIT_SCAN)) { /* The application has not requested explicit scan, so it is not expecting * advertising reports. Discard. * This is done in the loop as this flag can change between each iteration, * and it is not uncommon that scanning is disabled in the callback called * from le_adv_recv */ break; } if (buf->len < sizeof(*evt)) { LOG_ERR("Unexpected end of buffer"); break; } evt = net_buf_pull_mem(buf, sizeof(*evt)); if (buf->len < evt->length + sizeof(adv_info.rssi)) { LOG_ERR("Unexpected end of buffer"); break; } 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_legacy(evt->evt_type); le_adv_recv(&evt->addr, &adv_info, &buf->b, 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 (IS_ENABLED(CONFIG_BT_PRIVACY) && param->type == BT_LE_SCAN_TYPE_ACTIVE && param->timeout != 0) { /* This is marked as not supported as a stopgap until the (scan, * adv, init) roles are reworked into proper state machines. * * Having proper state machines is necessary to be able to * suspend all roles that use the (resolvable) private address, * update the RPA and resume them again with the right * parameters. * * Else we lower the privacy of the device as either the RPA * update will fail or the scanner will not use the newly * generated RPA. */ return false; } if (param->type != BT_LE_SCAN_TYPE_PASSIVE && param->type != BT_LE_SCAN_TYPE_ACTIVE) { return false; } if (param->options & ~(BT_LE_SCAN_OPT_FILTER_DUPLICATE | BT_LE_SCAN_OPT_FILTER_ACCEPT_LIST | 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; } /* Prevent multiple threads to try to enable explicit scanning at the same time. * That could lead to unwanted overwriting of scan_state.explicit_scan_param. */ err = k_mutex_lock(&scan_state.scan_explicit_params_mutex, K_NO_WAIT); if (err) { return err; } err = scan_check_if_state_allowed(BT_LE_SCAN_USER_EXPLICIT_SCAN); if (err) { k_mutex_unlock(&scan_state.scan_explicit_params_mutex); return err; } /* store the parameters that were used to start the scanner */ memcpy(&scan_state.explicit_scan_param, param, sizeof(scan_state.explicit_scan_param)); scan_dev_found_cb = cb; err = bt_le_scan_user_add(BT_LE_SCAN_USER_EXPLICIT_SCAN); k_mutex_unlock(&scan_state.scan_explicit_params_mutex); return err; } int bt_le_scan_stop(void) { bt_scan_softreset(); 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_user_remove(BT_LE_SCAN_USER_EXPLICIT_SCAN); } int bt_le_scan_cb_register(struct bt_le_scan_cb *cb) { if (sys_slist_find(&scan_cbs, &cb->node, NULL)) { return -EEXIST; } sys_slist_append(&scan_cbs, &cb->node); return 0; } 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(index >= 0 && ARRAY_SIZE(per_adv_sync_pool) > index, "Invalid per_adv_sync pointer"); return (uint8_t)index; } struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_index(uint8_t index) { if (index >= ARRAY_SIZE(per_adv_sync_pool)) { return NULL; } return &per_adv_sync_pool[index]; } int bt_le_per_adv_sync_get_info(struct bt_le_per_adv_sync *per_adv_sync, struct bt_le_per_adv_sync_info *info) { CHECKIF(per_adv_sync == NULL || info == NULL) { return -EINVAL; } bt_addr_le_copy(&info->addr, &per_adv_sync->addr); info->sid = per_adv_sync->sid; info->phy = per_adv_sync->phy; info->interval = per_adv_sync->interval; return 0; } struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_addr(const bt_addr_le_t *adv_addr, uint8_t sid) { for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) { struct bt_le_per_adv_sync *sync = &per_adv_sync_pool[i]; if (!atomic_test_bit(per_adv_sync_pool[i].flags, BT_PER_ADV_SYNC_CREATED)) { continue; } if (bt_addr_le_eq(&sync->addr, adv_addr) && sync->sid == sid) { return sync; } } return NULL; } 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_SKIP || param->timeout > BT_GAP_PER_ADV_MAX_TIMEOUT || param->timeout < BT_GAP_PER_ADV_MIN_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)); if (param->options & BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST) { atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING_USE_LIST); cp->options |= BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST; } else { /* If BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST is set, then the * address and SID are ignored by the controller, so we only * copy/assign them in case that the periodic advertising list * is not used. */ bt_addr_le_copy(&cp->addr, ¶m->addr); cp->sid = param->sid; } 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); } if (param->options & BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE) { cp->options |= BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE; } 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; } 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. */ err = bt_le_scan_user_add(BT_LE_SCAN_USER_PER_SYNC); if (err) { int per_sync_remove_err = bt_le_scan_user_remove(BT_LE_SCAN_USER_PER_SYNC); if (per_sync_remove_err) { LOG_WRN("Error while updating the scanner (%d)", per_sync_remove_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; } err = bt_le_scan_user_remove(BT_LE_SCAN_USER_PER_SYNC); if (err) { return err; } 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 (!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)) { err = bt_le_per_adv_sync_terminate(per_adv_sync); if (!err) { per_adv_sync_terminated(per_adv_sync, BT_HCI_ERR_LOCALHOST_TERM_CONN); } } 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; } int bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb) { if (sys_slist_find(&pa_sync_cbs, &cb->node, NULL)) { return -EEXIST; } sys_slist_append(&pa_sync_cbs, &cb->node); return 0; } 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_PER_ADV_SYNC_TRANSFER_SENDER) 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_EXT_PER_ADV(bt_dev.le.features)) { return -ENOTSUP; } else if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) { return -ENOTSUP; } 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); } #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */ #if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER) 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; } if ((param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_REPORTING_INITIALLY_DISABLED) && (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_FILTER_DUPLICATES)) { 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; uint8_t mode = BT_HCI_LE_PAST_MODE_SYNC; int err; if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { return -ENOTSUP; } else if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { return -ENOTSUP; } 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 (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_REPORTING_INITIALLY_DISABLED) { mode = BT_HCI_LE_PAST_MODE_NO_REPORTS; } else if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_FILTER_DUPLICATES) { mode = BT_HCI_LE_PAST_MODE_SYNC_FILTER_DUPLICATES; } if (conn) { const uint8_t conn_idx = bt_conn_index(conn); const uint8_t old_mode = conn_past_modes[conn_idx]; conn_past_modes[conn_idx] = mode; err = past_param_set(conn, mode, param->skip, param->timeout, cte_type); if (err != 0) { /* Restore old mode */ conn_past_modes[conn_idx] = old_mode; } } else { const uint8_t old_mode = default_past_mode; default_past_mode = mode; err = default_past_param_set(mode, param->skip, param->timeout, cte_type); if (err != 0) { /* Restore old mode */ default_past_mode = old_mode; } } return err; } int bt_le_per_adv_sync_transfer_unsubscribe(const struct bt_conn *conn) { int err; if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) { return -ENOTSUP; } else if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) { return -ENOTSUP; } if (conn) { const uint8_t conn_idx = bt_conn_index(conn); const uint8_t old_mode = conn_past_modes[conn_idx]; conn_past_modes[conn_idx] = BT_HCI_LE_PAST_MODE_NO_SYNC; err = past_param_set(conn, BT_HCI_LE_PAST_MODE_NO_SYNC, 0, 0x0a, 0); if (err != 0) { /* Restore old mode */ conn_past_modes[conn_idx] = old_mode; } } else { const uint8_t old_mode = default_past_mode; default_past_mode = BT_HCI_LE_PAST_MODE_NO_SYNC; err = default_past_param_set(BT_HCI_LE_PAST_MODE_NO_SYNC, 0, 0x0a, 0); if (err != 0) { /* Restore old mode */ default_past_mode = old_mode; } } return err; } #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER */ 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) { LOG_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) { LOG_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) { LOG_ERR("Failed to clear periodic advertiser list"); return err; } return 0; } #endif /* defined(CONFIG_BT_PER_ADV_SYNC) */ bool bt_le_explicit_scanner_running(void) { return atomic_test_bit(scan_state.scan_flags, BT_LE_SCAN_USER_EXPLICIT_SCAN); } bool bt_le_explicit_scanner_uses_same_params(const struct bt_conn_le_create_param *create_param) { if (scan_state.explicit_scan_param.window != create_param->window || scan_state.explicit_scan_param.interval != create_param->interval){ return false; } if (scan_state.explicit_scan_param.options & BT_LE_SCAN_OPT_CODED) { if (scan_state.explicit_scan_param.window_coded != create_param->window_coded || scan_state.explicit_scan_param.interval_coded != create_param->interval_coded){ return false; } } return true; }