Prevent the bt_rand function from being called before bt_enable. Depending on the implementation of bt_rand this function cannot be called before bluetooth has been initialized. With host supplied crypto functions the HCI LE rand command is used for example. The use case for calling bt_id_create before bt_enable is meant for when the application has storage for the identity instead of the stack. So we add the requirement that the application has to have storage for the identity resolving key (IRK) in addition when the local device is privacy-enabled. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
9559 lines
227 KiB
C
9559 lines
227 KiB
C
/* hci_core.c - HCI core Bluetooth handling */
|
|
|
|
/*
|
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <sys/atomic.h>
|
|
#include <sys/util.h>
|
|
#include <sys/slist.h>
|
|
#include <sys/byteorder.h>
|
|
#include <debug/stack.h>
|
|
#include <sys/__assert.h>
|
|
#include <soc.h>
|
|
|
|
#include <settings/settings.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/conn.h>
|
|
#include <bluetooth/l2cap.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/hci_vs.h>
|
|
#include <drivers/bluetooth/hci_driver.h>
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
|
#define LOG_MODULE_NAME bt_hci_core
|
|
#include "common/log.h"
|
|
|
|
#include "common/rpa.h"
|
|
#include "keys.h"
|
|
#include "monitor.h"
|
|
#include "hci_core.h"
|
|
#include "hci_ecc.h"
|
|
#include "ecc.h"
|
|
|
|
#include "conn_internal.h"
|
|
#include "audio/iso_internal.h"
|
|
#include "l2cap_internal.h"
|
|
#include "gatt_internal.h"
|
|
#include "smp.h"
|
|
#include "crypto.h"
|
|
#include "settings.h"
|
|
|
|
#if IS_ENABLED(CONFIG_BT_DF)
|
|
#include "direction_internal.h"
|
|
#endif /* CONFIG_BT_DF */
|
|
|
|
#if !defined(CONFIG_BT_EXT_ADV_LEGACY_SUPPORT)
|
|
#undef BT_FEAT_LE_EXT_ADV
|
|
#define BT_FEAT_LE_EXT_ADV(feat) 1
|
|
#endif
|
|
|
|
#define RPA_TIMEOUT_MS (CONFIG_BT_RPA_TIMEOUT * MSEC_PER_SEC)
|
|
#define RPA_TIMEOUT K_MSEC(RPA_TIMEOUT_MS)
|
|
|
|
#define HCI_CMD_TIMEOUT K_SECONDS(10)
|
|
|
|
/* Stacks for the threads */
|
|
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
static struct k_thread rx_thread_data;
|
|
static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_RX_STACK_SIZE);
|
|
#endif
|
|
static struct k_thread tx_thread_data;
|
|
static K_KERNEL_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
|
|
|
|
static void init_work(struct k_work *work);
|
|
|
|
struct bt_dev bt_dev = {
|
|
.init = Z_WORK_INITIALIZER(init_work),
|
|
/* Give cmd_sem allowing to send first HCI_Reset cmd, the only
|
|
* exception is if the controller requests to wait for an
|
|
* initial Command Complete for NOP.
|
|
*/
|
|
#if !defined(CONFIG_BT_WAIT_NOP)
|
|
.ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 1, 1),
|
|
#else
|
|
.ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1),
|
|
#endif
|
|
.cmd_tx_queue = Z_FIFO_INITIALIZER(bt_dev.cmd_tx_queue),
|
|
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
.rx_queue = Z_FIFO_INITIALIZER(bt_dev.rx_queue),
|
|
#endif
|
|
};
|
|
|
|
static bt_ready_cb_t ready_cb;
|
|
|
|
static bt_le_scan_cb_t *scan_dev_found_cb;
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
static int set_le_scan_enable(uint8_t enable);
|
|
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)
|
|
static bt_hci_vnd_evt_cb_t *hci_vnd_evt_cb;
|
|
#endif /* CONFIG_BT_HCI_VS_EVT_USER */
|
|
|
|
#if defined(CONFIG_BT_ECC)
|
|
static uint8_t pub_key[64];
|
|
static struct bt_pub_key_cb *pub_key_cb;
|
|
static bt_dh_key_cb_t dh_key_cb;
|
|
#endif /* CONFIG_BT_ECC */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static bt_br_discovery_cb_t *discovery_cb;
|
|
struct bt_br_discovery_result *discovery_results;
|
|
static size_t discovery_results_size;
|
|
static size_t discovery_results_count;
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
struct cmd_data {
|
|
/** HCI status of the command completion */
|
|
uint8_t status;
|
|
|
|
/** The command OpCode that the buffer contains */
|
|
uint16_t opcode;
|
|
|
|
/** The state to update when command completes with success. */
|
|
struct bt_hci_cmd_state_set *state;
|
|
|
|
/** Used by bt_hci_cmd_send_sync. */
|
|
struct k_sem *sync;
|
|
};
|
|
|
|
static struct cmd_data cmd_data[CONFIG_BT_HCI_CMD_COUNT];
|
|
|
|
#define cmd(buf) (&cmd_data[net_buf_id(buf)])
|
|
#define acl(buf) ((struct acl_data *)net_buf_user_data(buf))
|
|
|
|
void bt_hci_cmd_data_state_set(struct net_buf *buf,
|
|
struct bt_hci_cmd_state_set *state)
|
|
{
|
|
cmd(buf)->state = state;
|
|
}
|
|
|
|
/* HCI command buffers. Derive the needed size from BT_BUF_RX_SIZE since
|
|
* the same buffer is also used for the response.
|
|
*/
|
|
#define CMD_BUF_SIZE BT_BUF_RX_SIZE
|
|
NET_BUF_POOL_FIXED_DEFINE(hci_cmd_pool, CONFIG_BT_HCI_CMD_COUNT,
|
|
CMD_BUF_SIZE, NULL);
|
|
|
|
struct event_handler {
|
|
uint8_t event;
|
|
uint8_t min_len;
|
|
void (*handler)(struct net_buf *buf);
|
|
};
|
|
|
|
#define EVENT_HANDLER(_evt, _handler, _min_len) \
|
|
{ \
|
|
.event = _evt, \
|
|
.handler = _handler, \
|
|
.min_len = _min_len, \
|
|
}
|
|
|
|
static inline void handle_event(uint8_t event, struct net_buf *buf,
|
|
const struct event_handler *handlers,
|
|
size_t num_handlers)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_handlers; i++) {
|
|
const struct event_handler *handler = &handlers[i];
|
|
|
|
if (handler->event != event) {
|
|
continue;
|
|
}
|
|
|
|
if (buf->len < handler->min_len) {
|
|
BT_ERR("Too small (%u bytes) event 0x%02x",
|
|
buf->len, event);
|
|
return;
|
|
}
|
|
|
|
handler->handler(buf);
|
|
return;
|
|
}
|
|
|
|
BT_WARN("Unhandled event 0x%02x len %u: %s", event,
|
|
buf->len, bt_hex(buf->data, buf->len));
|
|
}
|
|
|
|
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
|
|
void bt_hci_host_num_completed_packets(struct net_buf *buf)
|
|
{
|
|
|
|
struct bt_hci_cp_host_num_completed_packets *cp;
|
|
uint16_t handle = acl(buf)->handle;
|
|
struct bt_hci_handle_count *hc;
|
|
struct bt_conn *conn;
|
|
|
|
net_buf_destroy(buf);
|
|
|
|
/* Do nothing if controller to host flow control is not supported */
|
|
if (!BT_CMD_TEST(bt_dev.supported_commands, 10, 5)) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_index(acl(buf)->index);
|
|
if (!conn) {
|
|
BT_WARN("Unable to look up conn with index 0x%02x",
|
|
acl(buf)->index);
|
|
return;
|
|
}
|
|
|
|
if (!bt_conn_is_handle_valid(conn)) {
|
|
BT_WARN("Not reporting packet for non-connected conn");
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
BT_DBG("Reporting completed packet for handle %u", handle);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS,
|
|
sizeof(*cp) + sizeof(*hc));
|
|
if (!buf) {
|
|
BT_ERR("Unable to allocate new HCI command");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->num_handles = sys_cpu_to_le16(1);
|
|
|
|
hc = net_buf_add(buf, sizeof(*hc));
|
|
hc->handle = sys_cpu_to_le16(handle);
|
|
hc->count = sys_cpu_to_le16(1);
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, buf);
|
|
}
|
|
#endif /* defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) */
|
|
|
|
struct net_buf *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len)
|
|
{
|
|
struct bt_hci_cmd_hdr *hdr;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("opcode 0x%04x param_len %u", opcode, param_len);
|
|
|
|
buf = net_buf_alloc(&hci_cmd_pool, K_FOREVER);
|
|
__ASSERT_NO_MSG(buf);
|
|
|
|
BT_DBG("buf %p", buf);
|
|
|
|
net_buf_reserve(buf, BT_BUF_RESERVE);
|
|
|
|
bt_buf_set_type(buf, BT_BUF_CMD);
|
|
|
|
cmd(buf)->opcode = opcode;
|
|
cmd(buf)->sync = NULL;
|
|
cmd(buf)->state = NULL;
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->opcode = sys_cpu_to_le16(opcode);
|
|
hdr->param_len = param_len;
|
|
|
|
return buf;
|
|
}
|
|
|
|
int bt_hci_cmd_send(uint16_t opcode, struct net_buf *buf)
|
|
{
|
|
if (!buf) {
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
BT_DBG("opcode 0x%04x len %u", opcode, buf->len);
|
|
|
|
/* Host Number of Completed Packets can ignore the ncmd value
|
|
* and does not generate any cmd complete/status events.
|
|
*/
|
|
if (opcode == BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS) {
|
|
int err;
|
|
|
|
err = bt_send(buf);
|
|
if (err) {
|
|
BT_ERR("Unable to send to driver (err %d)", err);
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
net_buf_put(&bt_dev.cmd_tx_queue, buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_hci_cmd_send_sync(uint16_t opcode, struct net_buf *buf,
|
|
struct net_buf **rsp)
|
|
{
|
|
struct k_sem sync_sem;
|
|
uint8_t status;
|
|
int err;
|
|
|
|
if (!buf) {
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
BT_DBG("buf %p opcode 0x%04x len %u", buf, opcode, buf->len);
|
|
|
|
k_sem_init(&sync_sem, 0, 1);
|
|
cmd(buf)->sync = &sync_sem;
|
|
|
|
/* Make sure the buffer stays around until the command completes */
|
|
net_buf_ref(buf);
|
|
|
|
net_buf_put(&bt_dev.cmd_tx_queue, buf);
|
|
|
|
err = k_sem_take(&sync_sem, HCI_CMD_TIMEOUT);
|
|
BT_ASSERT_MSG(err == 0, "k_sem_take failed with err %d", err);
|
|
|
|
status = cmd(buf)->status;
|
|
if (status) {
|
|
BT_WARN("opcode 0x%04x status 0x%02x", opcode, status);
|
|
net_buf_unref(buf);
|
|
|
|
switch (status) {
|
|
case BT_HCI_ERR_CONN_LIMIT_EXCEEDED:
|
|
return -ECONNREFUSED;
|
|
default:
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
BT_DBG("rsp %p opcode 0x%04x len %u", buf, opcode, buf->len);
|
|
|
|
if (rsp) {
|
|
*rsp = buf;
|
|
} else {
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER) || defined(CONFIG_BT_BROADCASTER)
|
|
const bt_addr_le_t *bt_lookup_id_addr(uint8_t id, const bt_addr_le_t *addr)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_find_irk(id, addr);
|
|
if (keys) {
|
|
BT_DBG("Identity %s matched RPA %s",
|
|
bt_addr_le_str(&keys->addr),
|
|
bt_addr_le_str(addr));
|
|
return &keys->addr;
|
|
}
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER || CONFIG_BT_CONN */
|
|
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
uint8_t bt_le_ext_adv_get_index(struct bt_le_ext_adv *adv)
|
|
{
|
|
ptrdiff_t index = adv - adv_pool;
|
|
|
|
__ASSERT(0 <= index && index < ARRAY_SIZE(adv_pool),
|
|
"Invalid bt_adv pointer");
|
|
return (uint8_t)index;
|
|
}
|
|
|
|
static struct bt_le_ext_adv *adv_new(void)
|
|
{
|
|
struct bt_le_ext_adv *adv = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(adv_pool); i++) {
|
|
if (!atomic_test_bit(adv_pool[i].flags, BT_ADV_CREATED)) {
|
|
adv = &adv_pool[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!adv) {
|
|
return NULL;
|
|
}
|
|
|
|
(void)memset(adv, 0, sizeof(*adv));
|
|
atomic_set_bit(adv_pool[i].flags, BT_ADV_CREATED);
|
|
adv->handle = i;
|
|
|
|
return adv;
|
|
}
|
|
|
|
static void adv_delete(struct bt_le_ext_adv *adv)
|
|
{
|
|
atomic_clear_bit(adv->flags, BT_ADV_CREATED);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BROADCASTER)
|
|
static struct bt_le_ext_adv *bt_adv_lookup_handle(uint8_t handle)
|
|
{
|
|
if (handle < ARRAY_SIZE(adv_pool) &&
|
|
atomic_test_bit(adv_pool[handle].flags, BT_ADV_CREATED)) {
|
|
return &adv_pool[handle];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* CONFIG_BT_BROADCASTER */
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
|
|
static void bt_adv_foreach(void (*func)(struct bt_le_ext_adv *adv, void *data),
|
|
void *data)
|
|
{
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
for (size_t i = 0; i < ARRAY_SIZE(adv_pool); i++) {
|
|
if (atomic_test_bit(adv_pool[i].flags, BT_ADV_CREATED)) {
|
|
func(&adv_pool[i], data);
|
|
}
|
|
}
|
|
#else
|
|
func(&bt_dev.adv, data);
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
}
|
|
|
|
static struct bt_le_ext_adv *adv_new_legacy(void)
|
|
{
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
if (bt_dev.adv) {
|
|
return NULL;
|
|
}
|
|
|
|
bt_dev.adv = adv_new();
|
|
return bt_dev.adv;
|
|
#else
|
|
return &bt_dev.adv;
|
|
#endif
|
|
}
|
|
|
|
static void adv_delete_legacy(void)
|
|
{
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
if (bt_dev.adv) {
|
|
atomic_clear_bit(bt_dev.adv->flags, BT_ADV_CREATED);
|
|
bt_dev.adv = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct bt_le_ext_adv *bt_adv_lookup_legacy(void)
|
|
{
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
return bt_dev.adv;
|
|
#else
|
|
return &bt_dev.adv;
|
|
#endif
|
|
}
|
|
|
|
static int set_le_adv_enable_legacy(struct bt_le_ext_adv *adv, bool enable)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cmd_state_set state;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (enable) {
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
|
|
} else {
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
|
|
}
|
|
|
|
bt_hci_cmd_state_set_init(&state, adv->flags, BT_ADV_ENABLED, enable);
|
|
cmd(buf)->state = &state;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_random_address(const bt_addr_t *addr)
|
|
{
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
BT_DBG("%s", bt_addr_str(addr));
|
|
|
|
/* Do nothing if we already have the right address */
|
|
if (!bt_addr_cmp(addr, &bt_dev.random_addr.a)) {
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, sizeof(*addr));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_mem(buf, addr, sizeof(*addr));
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
bt_addr_copy(&bt_dev.random_addr.a, addr);
|
|
bt_dev.random_addr.type = BT_ADDR_LE_RANDOM;
|
|
return 0;
|
|
}
|
|
|
|
static int set_le_adv_enable_ext(struct bt_le_ext_adv *adv,
|
|
bool enable,
|
|
const struct bt_le_ext_adv_start_param *param)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cmd_state_set state;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE, 6);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (enable) {
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
|
|
} else {
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
|
|
}
|
|
|
|
net_buf_add_u8(buf, 1);
|
|
|
|
net_buf_add_u8(buf, adv->handle);
|
|
net_buf_add_le16(buf, param ? sys_cpu_to_le16(param->timeout) : 0);
|
|
net_buf_add_u8(buf, param ? param->num_events : 0);
|
|
|
|
bt_hci_cmd_state_set_init(&state, adv->flags, BT_ADV_ENABLED, enable);
|
|
cmd(buf)->state = &state;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_le_adv_enable(struct bt_le_ext_adv *adv, bool enable)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
return set_le_adv_enable_ext(adv, enable, NULL);
|
|
}
|
|
|
|
return set_le_adv_enable_legacy(adv, enable);
|
|
}
|
|
|
|
static int set_adv_random_address(struct bt_le_ext_adv *adv,
|
|
const bt_addr_t *addr)
|
|
{
|
|
struct bt_hci_cp_le_set_adv_set_random_addr *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
return set_random_address(addr);
|
|
}
|
|
|
|
BT_DBG("%s", bt_addr_str(addr));
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_PARAMS_SET)) {
|
|
bt_addr_copy(&adv->random_addr.a, addr);
|
|
adv->random_addr.type = BT_ADDR_LE_RANDOM;
|
|
atomic_set_bit(adv->flags, BT_ADV_RANDOM_ADDR_PENDING);
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->handle = adv->handle;
|
|
bt_addr_copy(&cp->bdaddr, addr);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR, buf,
|
|
NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
bt_addr_copy(&adv->random_addr.a, addr);
|
|
adv->random_addr.type = BT_ADDR_LE_RANDOM;
|
|
return 0;
|
|
}
|
|
static void adv_rpa_invalidate(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_LIMITED)) {
|
|
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
}
|
|
}
|
|
|
|
static void le_rpa_invalidate(void)
|
|
{
|
|
/* RPA must be submitted */
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_TIMEOUT_SET);
|
|
|
|
/* Invalidate RPA */
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED))) {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID);
|
|
}
|
|
|
|
bt_adv_foreach(adv_rpa_invalidate, NULL);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
static void le_rpa_timeout_submit(void)
|
|
{
|
|
/* Check if RPA timer is running. */
|
|
if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_RPA_TIMEOUT_SET)) {
|
|
return;
|
|
}
|
|
|
|
k_delayed_work_submit(&bt_dev.rpa_update, RPA_TIMEOUT);
|
|
}
|
|
|
|
/* this function sets new RPA only if current one is no longer valid */
|
|
static int le_set_private_addr(uint8_t id)
|
|
{
|
|
bt_addr_t rpa;
|
|
int err;
|
|
|
|
/* check if RPA is valid */
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_RPA_VALID)) {
|
|
return 0;
|
|
}
|
|
|
|
err = bt_rpa_create(bt_dev.irk[id], &rpa);
|
|
if (!err) {
|
|
err = set_random_address(&rpa);
|
|
if (!err) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_RPA_VALID);
|
|
}
|
|
}
|
|
|
|
le_rpa_timeout_submit();
|
|
return err;
|
|
}
|
|
|
|
static int le_adv_set_private_addr(struct bt_le_ext_adv *adv)
|
|
{
|
|
bt_addr_t rpa;
|
|
int err;
|
|
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
return le_set_private_addr(adv->id);
|
|
}
|
|
|
|
/* check if RPA is valid */
|
|
if (atomic_test_bit(adv->flags, BT_ADV_RPA_VALID)) {
|
|
return 0;
|
|
}
|
|
|
|
if (adv == bt_adv_lookup_legacy() && adv->id == BT_ID_DEFAULT) {
|
|
/* Make sure that a Legacy advertiser using default ID has same
|
|
* RPA address as scanner roles.
|
|
*/
|
|
err = le_set_private_addr(BT_ID_DEFAULT);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = set_adv_random_address(adv, &bt_dev.random_addr.a);
|
|
if (!err) {
|
|
atomic_set_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
err = bt_rpa_create(bt_dev.irk[adv->id], &rpa);
|
|
if (!err) {
|
|
err = set_adv_random_address(adv, &rpa);
|
|
if (!err) {
|
|
atomic_set_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
}
|
|
}
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_LIMITED)) {
|
|
le_rpa_timeout_submit();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#else
|
|
static int le_set_private_addr(uint8_t id)
|
|
{
|
|
bt_addr_t nrpa;
|
|
int err;
|
|
|
|
err = bt_rand(nrpa.val, sizeof(nrpa.val));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
BT_ADDR_SET_NRPA(&nrpa);
|
|
|
|
return set_random_address(&nrpa);
|
|
}
|
|
|
|
static int le_adv_set_private_addr(struct bt_le_ext_adv *adv)
|
|
{
|
|
bt_addr_t nrpa;
|
|
int err;
|
|
|
|
err = bt_rand(nrpa.val, sizeof(nrpa.val));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
BT_ADDR_SET_NRPA(&nrpa);
|
|
|
|
return set_adv_random_address(adv, &nrpa);
|
|
}
|
|
#endif /* defined(CONFIG_BT_PRIVACY) */
|
|
|
|
static void adv_update_rpa(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_LIMITED) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
int err;
|
|
|
|
set_le_adv_enable_ext(adv, false, NULL);
|
|
|
|
err = le_adv_set_private_addr(adv);
|
|
if (err) {
|
|
BT_WARN("Failed to update advertiser RPA address (%d)",
|
|
err);
|
|
}
|
|
|
|
set_le_adv_enable_ext(adv, true, NULL);
|
|
}
|
|
}
|
|
|
|
static void le_update_private_addr(void)
|
|
{
|
|
struct bt_le_ext_adv *adv = NULL;
|
|
bool adv_enabled = false;
|
|
uint8_t id = BT_ID_DEFAULT;
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
bt_adv_foreach(adv_update_rpa, NULL);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
bool scan_enabled = false;
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN) &&
|
|
!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED))) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE);
|
|
scan_enabled = true;
|
|
}
|
|
#endif
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
IS_ENABLED(CONFIG_BT_WHITELIST) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING)) {
|
|
/* Canceled initiating procedure will be restarted by
|
|
* connection complete event.
|
|
*/
|
|
bt_le_create_conn_cancel();
|
|
}
|
|
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
adv = bt_adv_lookup_legacy();
|
|
|
|
if (adv &&
|
|
atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
adv_enabled = true;
|
|
id = adv->id;
|
|
set_le_adv_enable_legacy(adv, false);
|
|
}
|
|
}
|
|
|
|
/* If both advertiser and scanner is running then the advertiser
|
|
* ID must be BT_ID_DEFAULT, this will update the RPA address
|
|
* for both roles.
|
|
*/
|
|
err = le_set_private_addr(id);
|
|
if (err) {
|
|
BT_WARN("Failed to update RPA address (%d)", err);
|
|
return;
|
|
}
|
|
|
|
if (adv && adv_enabled) {
|
|
set_le_adv_enable_legacy(adv, true);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct adv_id_check_data {
|
|
uint8_t id;
|
|
bool adv_enabled;
|
|
};
|
|
|
|
static void adv_id_check_func(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
struct adv_id_check_data *check_data = data;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV)) {
|
|
/* Only check if the ID is in use, as the advertiser can be
|
|
* started and stopped without reconfiguring parameters.
|
|
*/
|
|
if (check_data->id == adv->id) {
|
|
check_data->adv_enabled = true;
|
|
}
|
|
} else {
|
|
if (check_data->id == adv->id &&
|
|
atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
check_data->adv_enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void adv_id_check_connectable_func(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
struct adv_id_check_data *check_data = data;
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE) &&
|
|
check_data->id != adv->id) {
|
|
check_data->adv_enabled = true;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
static void adv_is_limited_enabled(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
bool *adv_enabled = data;
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_LIMITED)) {
|
|
*adv_enabled = true;
|
|
}
|
|
}
|
|
|
|
static void adv_pause_enabled(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
atomic_set_bit(adv->flags, BT_ADV_PAUSED);
|
|
set_le_adv_enable(adv, false);
|
|
}
|
|
}
|
|
|
|
static void adv_unpause_enabled(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_PAUSED)) {
|
|
set_le_adv_enable(adv, true);
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_SMP) */
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
static void adv_is_private_enabled(struct bt_le_ext_adv *adv, void *data)
|
|
{
|
|
bool *adv_enabled = data;
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
*adv_enabled = true;
|
|
}
|
|
}
|
|
|
|
static void rpa_timeout(struct k_work *work)
|
|
{
|
|
bool adv_enabled = false;
|
|
|
|
BT_DBG("");
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL)) {
|
|
struct bt_conn *conn =
|
|
bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL,
|
|
BT_CONN_CONNECT_SCAN);
|
|
|
|
if (conn) {
|
|
bt_conn_unref(conn);
|
|
bt_le_create_conn_cancel();
|
|
}
|
|
}
|
|
|
|
le_rpa_invalidate();
|
|
|
|
bt_adv_foreach(adv_is_private_enabled, &adv_enabled);
|
|
|
|
/* IF no roles using the RPA is running we can stop the RPA timer */
|
|
if (!(adv_enabled ||
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING) ||
|
|
(atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)))) {
|
|
return;
|
|
}
|
|
|
|
le_update_private_addr();
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
bool bt_le_scan_random_addr_check(void)
|
|
{
|
|
struct bt_le_ext_adv *adv;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
/* Advertiser and scanner using different random address */
|
|
return true;
|
|
}
|
|
|
|
adv = bt_adv_lookup_legacy();
|
|
if (!adv) {
|
|
return true;
|
|
}
|
|
|
|
/* If the advertiser is not enabled or not active there is no issue */
|
|
if (!IS_ENABLED(CONFIG_BT_BROADCASTER) ||
|
|
!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return true;
|
|
}
|
|
|
|
/* When privacy is enabled the random address will not be set
|
|
* immediately before starting the role, because the RPA might still be
|
|
* valid and only updated on RPA timeout.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
/* Cannot start scannor or initiator if the random address is
|
|
* used by the advertiser for an RPA with a different identity
|
|
* or for a random static identity address.
|
|
*/
|
|
if ((atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY) &&
|
|
bt_dev.id_addr[adv->id].type == BT_ADDR_LE_RANDOM) ||
|
|
adv->id != BT_ID_DEFAULT) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* If privacy is not enabled then the random address will be attempted
|
|
* to be set before enabling the role. If another role is already using
|
|
* the random address then this command will fail, and should return
|
|
* the error code to the application.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
static bool bt_le_adv_random_addr_check(const struct bt_le_adv_param *param)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
/* Advertiser and scanner using different random address */
|
|
return true;
|
|
}
|
|
|
|
/* If scanner roles are not enabled or not active there is no issue. */
|
|
if (!IS_ENABLED(CONFIG_BT_OBSERVER) ||
|
|
!(atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING) ||
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING))) {
|
|
return true;
|
|
}
|
|
|
|
/* When privacy is enabled the random address will not be set
|
|
* immediately before starting the role, because the RPA might still be
|
|
* valid and only updated on RPA timeout.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
/* Cannot start an advertiser with random static identity or
|
|
* using an RPA generated for a different identity than scanner
|
|
* roles.
|
|
*/
|
|
if (((param->options & BT_LE_ADV_OPT_USE_IDENTITY) &&
|
|
bt_dev.id_addr[param->id].type == BT_ADDR_LE_RANDOM) ||
|
|
param->id != BT_ID_DEFAULT) {
|
|
return false;
|
|
}
|
|
} else if (IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) &&
|
|
bt_dev.id_addr[BT_ID_DEFAULT].type == BT_ADDR_LE_RANDOM) {
|
|
/* Scanning with random static identity. Stop the advertiser
|
|
* from overwriting the passive scanner identity address.
|
|
* In this case the LE Set Random Address command does not
|
|
* protect us in the case of a passive scanner.
|
|
* Explicitly stop it here.
|
|
*/
|
|
|
|
if (!(param->options & BT_LE_ADV_OPT_CONNECTABLE) &&
|
|
(param->options & BT_LE_ADV_OPT_USE_IDENTITY)) {
|
|
/* Attempt to set non-connectable NRPA */
|
|
return false;
|
|
} else if (bt_dev.id_addr[param->id].type ==
|
|
BT_ADDR_LE_RANDOM &&
|
|
param->id != BT_ID_DEFAULT) {
|
|
/* Attempt to set connectable, or non-connectable with
|
|
* identity different than scanner.
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* If privacy is not enabled then the random address will be attempted
|
|
* to be set before enabling the role. If another role is already using
|
|
* the random address then this command will fail, and should return
|
|
* the error code to the application.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
|
|
#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(&state, bt_dev.flags, BT_DEV_SCANNING,
|
|
enable == BT_HCI_LE_SCAN_ENABLE);
|
|
cmd(buf)->state = &state;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_le_scan_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(&state, bt_dev.flags, BT_DEV_SCANNING,
|
|
enable == BT_HCI_LE_SCAN_ENABLE);
|
|
cmd(buf)->state = &state;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_le_scan_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 set_le_scan_enable_legacy(enable);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
static inline bool rpa_is_new(void)
|
|
{
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
/* RPA is considered new if there is less than half a second since the
|
|
* timeout was started.
|
|
*/
|
|
return k_delayed_work_remaining_get(&bt_dev.rpa_update) >
|
|
(RPA_TIMEOUT_MS - 500);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MAX_DATA_LEN, NULL, &rsp);
|
|
if (err) {
|
|
BT_ERR("Failed to read DLE max data len");
|
|
return err;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
*tx_octets = sys_le16_to_cpu(rp->max_tx_octets);
|
|
*tx_time = sys_le16_to_cpu(rp->max_tx_time);
|
|
net_buf_unref(rsp);
|
|
|
|
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)
|
|
{
|
|
switch (hci_phy) {
|
|
case BT_HCI_LE_PHY_1M:
|
|
return BT_GAP_LE_PHY_1M;
|
|
case BT_HCI_LE_PHY_2M:
|
|
return BT_GAP_LE_PHY_2M;
|
|
case BT_HCI_LE_PHY_CODED:
|
|
return BT_GAP_LE_PHY_CODED;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /* (BT_OBSERVER && BT_EXT_ADV) || USER_PHY_UPDATE */
|
|
|
|
#if defined(CONFIG_BT_CONN)
|
|
static void hci_acl(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_acl_hdr *hdr;
|
|
uint16_t handle, len;
|
|
struct bt_conn *conn;
|
|
uint8_t flags;
|
|
|
|
BT_DBG("buf %p", buf);
|
|
|
|
BT_ASSERT(buf->len >= sizeof(*hdr));
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
|
len = sys_le16_to_cpu(hdr->len);
|
|
handle = sys_le16_to_cpu(hdr->handle);
|
|
flags = bt_acl_flags(handle);
|
|
|
|
acl(buf)->handle = bt_acl_handle(handle);
|
|
acl(buf)->index = BT_CONN_INDEX_INVALID;
|
|
|
|
BT_DBG("handle %u len %u flags %u", acl(buf)->handle, len, flags);
|
|
|
|
if (buf->len != len) {
|
|
BT_ERR("ACL data length mismatch (%u != %u)", buf->len, len);
|
|
net_buf_unref(buf);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(acl(buf)->handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to find conn for handle %u", acl(buf)->handle);
|
|
net_buf_unref(buf);
|
|
return;
|
|
}
|
|
|
|
acl(buf)->index = bt_conn_index(conn);
|
|
|
|
bt_conn_recv(conn, buf, flags);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void hci_data_buf_overflow(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_data_buf_overflow *evt = (void *)buf->data;
|
|
|
|
BT_WARN("Data buffer overflow (link type 0x%02x)", evt->link_type);
|
|
}
|
|
|
|
static void hci_num_completed_packets(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_num_completed_packets *evt = (void *)buf->data;
|
|
int i;
|
|
|
|
BT_DBG("num_handles %u", evt->num_handles);
|
|
|
|
for (i = 0; i < evt->num_handles; i++) {
|
|
uint16_t handle, count;
|
|
struct bt_conn *conn;
|
|
|
|
handle = sys_le16_to_cpu(evt->h[i].handle);
|
|
count = sys_le16_to_cpu(evt->h[i].count);
|
|
|
|
BT_DBG("handle %u count %u", handle, count);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("No connection for handle %u", handle);
|
|
continue;
|
|
}
|
|
|
|
while (count--) {
|
|
struct bt_conn_tx *tx;
|
|
sys_snode_t *node;
|
|
unsigned int key;
|
|
|
|
key = irq_lock();
|
|
|
|
if (conn->pending_no_cb) {
|
|
conn->pending_no_cb--;
|
|
irq_unlock(key);
|
|
k_sem_give(bt_conn_get_pkts(conn));
|
|
continue;
|
|
}
|
|
|
|
node = sys_slist_get(&conn->tx_pending);
|
|
irq_unlock(key);
|
|
|
|
if (!node) {
|
|
BT_ERR("packets count mismatch");
|
|
break;
|
|
}
|
|
|
|
tx = CONTAINER_OF(node, struct bt_conn_tx, node);
|
|
|
|
key = irq_lock();
|
|
conn->pending_no_cb = tx->pending_no_cb;
|
|
tx->pending_no_cb = 0U;
|
|
sys_slist_append(&conn->tx_complete, &tx->node);
|
|
irq_unlock(key);
|
|
|
|
k_work_submit(&conn->tx_complete_work);
|
|
k_sem_give(bt_conn_get_pkts(conn));
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
}
|
|
|
|
static inline bool rpa_timeout_valid_check(void)
|
|
{
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
/* Check if create conn timeout will happen before RPA timeout. */
|
|
return k_delayed_work_remaining_get(&bt_dev.rpa_update) >
|
|
(10 * bt_dev.create_param.timeout);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
static int le_create_conn_set_random_addr(bool use_filter, uint8_t *own_addr_type)
|
|
{
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
if (use_filter || rpa_timeout_valid_check()) {
|
|
err = le_set_private_addr(BT_ID_DEFAULT);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else {
|
|
/* Force new RPA timeout so that RPA timeout is not
|
|
* triggered while direct initiator is active.
|
|
*/
|
|
le_rpa_invalidate();
|
|
le_update_private_addr();
|
|
}
|
|
|
|
if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) {
|
|
*own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM;
|
|
} else {
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
}
|
|
} else {
|
|
const bt_addr_le_t *addr = &bt_dev.id_addr[BT_ID_DEFAULT];
|
|
|
|
/* If Static Random address is used as Identity address we
|
|
* need to restore it before creating connection. Otherwise
|
|
* NRPA used for active scan could be used for connection.
|
|
*/
|
|
if (addr->type == BT_ADDR_LE_RANDOM) {
|
|
err = set_random_address(&addr->a);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
*own_addr_type = addr->type;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_phy_conn_param(const struct bt_conn *conn,
|
|
struct bt_hci_ext_conn_phy *phy)
|
|
{
|
|
phy->conn_interval_min = sys_cpu_to_le16(conn->le.interval_min);
|
|
phy->conn_interval_max = sys_cpu_to_le16(conn->le.interval_max);
|
|
phy->conn_latency = sys_cpu_to_le16(conn->le.latency);
|
|
phy->supervision_timeout = sys_cpu_to_le16(conn->le.timeout);
|
|
|
|
phy->min_ce_len = 0;
|
|
phy->max_ce_len = 0;
|
|
}
|
|
|
|
int bt_le_create_conn_ext(const struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_le_ext_create_conn *cp;
|
|
struct bt_hci_ext_conn_phy *phy;
|
|
struct bt_hci_cmd_state_set state;
|
|
bool use_filter = false;
|
|
struct net_buf *buf;
|
|
uint8_t own_addr_type;
|
|
uint8_t num_phys;
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_WHITELIST)) {
|
|
use_filter = atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT);
|
|
}
|
|
|
|
err = le_create_conn_set_random_addr(use_filter, &own_addr_type);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
num_phys = (!(bt_dev.create_param.options &
|
|
BT_CONN_LE_OPT_NO_1M) ? 1 : 0) +
|
|
((bt_dev.create_param.options &
|
|
BT_CONN_LE_OPT_CODED) ? 1 : 0);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_EXT_CREATE_CONN, sizeof(*cp) +
|
|
num_phys * sizeof(*phy));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
if (use_filter) {
|
|
/* User Initiated procedure use fast scan parameters. */
|
|
bt_addr_le_copy(&cp->peer_addr, BT_ADDR_LE_ANY);
|
|
cp->filter_policy = BT_HCI_LE_CREATE_CONN_FP_WHITELIST;
|
|
} else {
|
|
const bt_addr_le_t *peer_addr = &conn->le.dst;
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
if (!bt_dev.le.rl_size ||
|
|
bt_dev.le.rl_entries > bt_dev.le.rl_size) {
|
|
/* Host resolving is used, use the RPA directly. */
|
|
peer_addr = &conn->le.resp_addr;
|
|
}
|
|
#endif
|
|
bt_addr_le_copy(&cp->peer_addr, peer_addr);
|
|
cp->filter_policy = BT_HCI_LE_CREATE_CONN_FP_DIRECT;
|
|
}
|
|
|
|
cp->own_addr_type = own_addr_type;
|
|
cp->phys = 0;
|
|
|
|
if (!(bt_dev.create_param.options & BT_CONN_LE_OPT_NO_1M)) {
|
|
cp->phys |= BT_HCI_LE_EXT_SCAN_PHY_1M;
|
|
phy = net_buf_add(buf, sizeof(*phy));
|
|
phy->scan_interval = sys_cpu_to_le16(
|
|
bt_dev.create_param.interval);
|
|
phy->scan_window = sys_cpu_to_le16(
|
|
bt_dev.create_param.window);
|
|
set_phy_conn_param(conn, phy);
|
|
}
|
|
|
|
if (bt_dev.create_param.options & BT_CONN_LE_OPT_CODED) {
|
|
cp->phys |= BT_HCI_LE_EXT_SCAN_PHY_CODED;
|
|
phy = net_buf_add(buf, sizeof(*phy));
|
|
phy->scan_interval = sys_cpu_to_le16(
|
|
bt_dev.create_param.interval_coded);
|
|
phy->scan_window = sys_cpu_to_le16(
|
|
bt_dev.create_param.window_coded);
|
|
set_phy_conn_param(conn, phy);
|
|
}
|
|
|
|
bt_hci_cmd_state_set_init(&state, bt_dev.flags,
|
|
BT_DEV_INITIATING, true);
|
|
cmd(buf)->state = &state;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_EXT_CREATE_CONN, buf, NULL);
|
|
}
|
|
|
|
int bt_le_create_conn_legacy(const struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_le_create_conn *cp;
|
|
struct bt_hci_cmd_state_set state;
|
|
bool use_filter = false;
|
|
struct net_buf *buf;
|
|
uint8_t own_addr_type;
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_WHITELIST)) {
|
|
use_filter = atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT);
|
|
}
|
|
|
|
err = le_create_conn_set_random_addr(use_filter, &own_addr_type);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
memset(cp, 0, sizeof(*cp));
|
|
cp->own_addr_type = own_addr_type;
|
|
|
|
if (use_filter) {
|
|
/* User Initiated procedure use fast scan parameters. */
|
|
bt_addr_le_copy(&cp->peer_addr, BT_ADDR_LE_ANY);
|
|
cp->filter_policy = BT_HCI_LE_CREATE_CONN_FP_WHITELIST;
|
|
} else {
|
|
const bt_addr_le_t *peer_addr = &conn->le.dst;
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
if (!bt_dev.le.rl_size ||
|
|
bt_dev.le.rl_entries > bt_dev.le.rl_size) {
|
|
/* Host resolving is used, use the RPA directly. */
|
|
peer_addr = &conn->le.resp_addr;
|
|
}
|
|
#endif
|
|
bt_addr_le_copy(&cp->peer_addr, peer_addr);
|
|
cp->filter_policy = BT_HCI_LE_CREATE_CONN_FP_DIRECT;
|
|
}
|
|
|
|
cp->scan_interval = sys_cpu_to_le16(bt_dev.create_param.interval);
|
|
cp->scan_window = sys_cpu_to_le16(bt_dev.create_param.window);
|
|
|
|
cp->conn_interval_min = sys_cpu_to_le16(conn->le.interval_min);
|
|
cp->conn_interval_max = sys_cpu_to_le16(conn->le.interval_max);
|
|
cp->conn_latency = sys_cpu_to_le16(conn->le.latency);
|
|
cp->supervision_timeout = sys_cpu_to_le16(conn->le.timeout);
|
|
|
|
bt_hci_cmd_state_set_init(&state, bt_dev.flags,
|
|
BT_DEV_INITIATING, true);
|
|
cmd(buf)->state = &state;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
|
|
}
|
|
|
|
int bt_le_create_conn(const struct bt_conn *conn)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
return bt_le_create_conn_ext(conn);
|
|
}
|
|
|
|
return bt_le_create_conn_legacy(conn);
|
|
}
|
|
|
|
int bt_le_create_conn_cancel(void)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cmd_state_set state;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN_CANCEL, 0);
|
|
|
|
bt_hci_cmd_state_set_init(&state, bt_dev.flags,
|
|
BT_DEV_INITIATING, false);
|
|
cmd(buf)->state = &state;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, buf, NULL);
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
int bt_hci_disconnect(uint16_t handle, uint8_t reason)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cp_disconnect *disconn;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
disconn = net_buf_add(buf, sizeof(*disconn));
|
|
disconn->handle = sys_cpu_to_le16(handle);
|
|
disconn->reason = reason;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_DISCONNECT, buf, NULL);
|
|
}
|
|
|
|
static void hci_disconn_complete_prio(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_disconn_complete *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u reason 0x%02x", evt->status, handle,
|
|
evt->reason);
|
|
|
|
if (evt->status) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to look up conn with handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECT_COMPLETE);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void hci_disconn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_disconn_complete *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u reason 0x%02x", evt->status, handle,
|
|
evt->reason);
|
|
|
|
if (evt->status) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to look up conn with handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
conn->err = evt->reason;
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
conn->handle = 0U;
|
|
|
|
if (conn->type != BT_CONN_TYPE_LE) {
|
|
#if defined(CONFIG_BT_BREDR)
|
|
if (conn->type == BT_CONN_TYPE_SCO) {
|
|
bt_sco_cleanup(conn);
|
|
return;
|
|
}
|
|
/*
|
|
* If only for one connection session bond was set, clear keys
|
|
* database row for this connection.
|
|
*/
|
|
if (conn->type == BT_CONN_TYPE_BR &&
|
|
atomic_test_and_clear_bit(conn->flags, BT_CONN_BR_NOBOND)) {
|
|
bt_keys_link_key_clear(conn->br.link_key);
|
|
}
|
|
#endif
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CENTRAL) && !defined(CONFIG_BT_WHITELIST)
|
|
if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) {
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
|
|
bt_le_scan_update(false);
|
|
}
|
|
#endif /* defined(CONFIG_BT_CENTRAL) && !defined(CONFIG_BT_WHITELIST) */
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static int hci_le_read_remote_features(struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_le_read_remote_features *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_REMOTE_FEATURES,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_READ_REMOTE_FEATURES, buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hci_read_remote_version(struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_read_remote_version_info *cp;
|
|
struct net_buf *buf;
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* Remote version cannot change. */
|
|
if (atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO)) {
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_VERSION_INFO,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_VERSION_INFO, buf,
|
|
NULL);
|
|
}
|
|
|
|
/* LE Data Length Change Event is optional so this function just ignore
|
|
* error and stack will continue to use default values.
|
|
*/
|
|
int bt_le_set_data_len(struct bt_conn *conn, uint16_t tx_octets, uint16_t tx_time)
|
|
{
|
|
struct bt_hci_cp_le_set_data_len *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_DATA_LEN, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
cp->tx_octets = sys_cpu_to_le16(tx_octets);
|
|
cp->tx_time = sys_cpu_to_le16(tx_time);
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_SET_DATA_LEN, buf);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_USER_PHY_UPDATE)
|
|
static int hci_le_read_phy(struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_le_read_phy *cp;
|
|
struct bt_hci_rp_le_read_phy *rp;
|
|
struct net_buf *buf, *rsp;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_PHY, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_PHY, buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
conn->le.phy.tx_phy = get_phy(rp->tx_phy);
|
|
conn->le.phy.rx_phy = get_phy(rp->rx_phy);
|
|
net_buf_unref(rsp);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_BT_USER_PHY_UPDATE) */
|
|
|
|
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)
|
|
{
|
|
struct bt_hci_cp_le_set_phy *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PHY, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
cp->all_phys = all_phys;
|
|
cp->tx_phys = pref_tx_phy;
|
|
cp->rx_phys = pref_rx_phy;
|
|
cp->phy_opts = phy_opts;
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_SET_PHY, buf);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
static void pending_id_update(struct bt_keys *keys, void *data)
|
|
{
|
|
if (keys->state & BT_KEYS_ID_PENDING_ADD) {
|
|
keys->state &= ~BT_KEYS_ID_PENDING_ADD;
|
|
bt_id_add(keys);
|
|
return;
|
|
}
|
|
|
|
if (keys->state & BT_KEYS_ID_PENDING_DEL) {
|
|
keys->state &= ~BT_KEYS_ID_PENDING_DEL;
|
|
bt_id_del(keys);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void pending_id_keys_update_set(struct bt_keys *keys, uint8_t flag)
|
|
{
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_ID_PENDING);
|
|
keys->state |= flag;
|
|
}
|
|
|
|
static void pending_id_keys_update(void)
|
|
{
|
|
if (atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_ID_PENDING)) {
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
bt_keys_foreach(BT_KEYS_ALL, pending_id_update, NULL);
|
|
} else {
|
|
bt_keys_foreach(BT_KEYS_IRK, pending_id_update, NULL);
|
|
}
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_SMP) */
|
|
|
|
static struct bt_conn *find_pending_connect(uint8_t role, bt_addr_le_t *peer_addr)
|
|
{
|
|
struct bt_conn *conn;
|
|
|
|
/*
|
|
* Make lookup to check if there's a connection object in
|
|
* CONNECT or CONNECT_AUTO state associated with passed peer LE address.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) && role == BT_HCI_ROLE_MASTER) {
|
|
conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, peer_addr,
|
|
BT_CONN_CONNECT);
|
|
if (IS_ENABLED(CONFIG_BT_WHITELIST) && !conn) {
|
|
conn = bt_conn_lookup_state_le(BT_ID_DEFAULT,
|
|
BT_ADDR_LE_NONE,
|
|
BT_CONN_CONNECT_AUTO);
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && role == BT_HCI_ROLE_SLAVE) {
|
|
conn = bt_conn_lookup_state_le(bt_dev.adv_conn_id, peer_addr,
|
|
BT_CONN_CONNECT_DIR_ADV);
|
|
if (!conn) {
|
|
conn = bt_conn_lookup_state_le(bt_dev.adv_conn_id,
|
|
BT_ADDR_LE_NONE,
|
|
BT_CONN_CONNECT_ADV);
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void conn_auto_initiate(struct bt_conn *conn)
|
|
{
|
|
int err;
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
/* It is possible that connection was disconnected directly from
|
|
* connected callback so we must check state before doing
|
|
* connection parameters update.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if (!atomic_test_bit(conn->flags, BT_CONN_AUTO_FEATURE_EXCH) &&
|
|
((conn->role == BT_HCI_ROLE_MASTER) ||
|
|
BT_FEAT_LE_SLAVE_FEATURE_XCHG(bt_dev.le.features))) {
|
|
err = hci_le_read_remote_features(conn);
|
|
if (!err) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_REMOTE_VERSION) &&
|
|
!atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO)) {
|
|
err = hci_read_remote_version(conn);
|
|
if (!err) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) &&
|
|
!atomic_test_bit(conn->flags, BT_CONN_AUTO_PHY_COMPLETE) &&
|
|
BT_FEAT_LE_PHY_2M(bt_dev.le.features)) {
|
|
err = bt_le_set_phy(conn, 0U, BT_HCI_LE_PHY_PREFER_2M,
|
|
BT_HCI_LE_PHY_PREFER_2M,
|
|
BT_HCI_LE_PHY_CODED_ANY);
|
|
if (!err) {
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_PHY_UPDATE);
|
|
return;
|
|
}
|
|
|
|
BT_ERR("Failed to set LE PHY (%d)", err);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUTO_DATA_LEN_UPDATE) &&
|
|
BT_FEAT_LE_DLE(bt_dev.le.features)) {
|
|
if (IS_BT_QUIRK_NO_AUTO_DLE(&bt_dev)) {
|
|
uint16_t tx_octets, tx_time;
|
|
|
|
err = hci_le_read_max_data_len(&tx_octets, &tx_time);
|
|
if (!err) {
|
|
err = bt_le_set_data_len(conn,
|
|
tx_octets, tx_time);
|
|
if (err) {
|
|
BT_ERR("Failed to set data len (%d)", err);
|
|
}
|
|
}
|
|
} else {
|
|
/* No need to auto-initiate DLE procedure.
|
|
* It is done by the controller.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
static void le_conn_complete_cancel(void)
|
|
{
|
|
struct bt_conn *conn;
|
|
|
|
/* Handle create connection cancel.
|
|
*
|
|
* There is no need to check ID address as only one
|
|
* connection in master role can be in pending state.
|
|
*/
|
|
conn = find_pending_connect(BT_HCI_ROLE_MASTER, NULL);
|
|
if (!conn) {
|
|
BT_ERR("No pending master connection");
|
|
return;
|
|
}
|
|
|
|
conn->err = BT_HCI_ERR_UNKNOWN_CONN_ID;
|
|
|
|
/* Handle cancellation of outgoing connection attempt. */
|
|
if (!IS_ENABLED(CONFIG_BT_WHITELIST)) {
|
|
/* We notify before checking autoconnect flag
|
|
* as application may choose to change it from
|
|
* callback.
|
|
*/
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
/* Check if device is marked for autoconnect. */
|
|
if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) {
|
|
/* Restart passive scanner for device */
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
|
|
}
|
|
} else {
|
|
if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) {
|
|
/* Restart whitelist initiator after RPA timeout. */
|
|
bt_le_create_conn(conn);
|
|
} else {
|
|
/* Create connection canceled by timeout */
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
}
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void le_conn_complete_adv_timeout(void)
|
|
{
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
struct bt_conn *conn;
|
|
|
|
/* Handle advertising timeout after high duty cycle directed
|
|
* advertising.
|
|
*/
|
|
|
|
atomic_clear_bit(adv->flags, BT_ADV_ENABLED);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
!BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
/* No advertising set terminated event, must be a
|
|
* legacy advertiser set.
|
|
*/
|
|
adv_delete_legacy();
|
|
}
|
|
|
|
/* There is no need to check ID address as only one
|
|
* connection in slave role can be in pending state.
|
|
*/
|
|
conn = find_pending_connect(BT_HCI_ROLE_SLAVE, NULL);
|
|
if (!conn) {
|
|
BT_ERR("No pending slave connection");
|
|
return;
|
|
}
|
|
|
|
conn->err = BT_HCI_ERR_ADV_TIMEOUT;
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
}
|
|
|
|
static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt)
|
|
{
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
bt_addr_le_t peer_addr, id_addr;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u role %u peer %s peer RPA %s",
|
|
evt->status, handle, evt->role, bt_addr_le_str(&evt->peer_addr),
|
|
bt_addr_str(&evt->peer_rpa));
|
|
BT_DBG("local RPA %s", bt_addr_str(&evt->local_rpa));
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
pending_id_keys_update();
|
|
#endif
|
|
|
|
if (evt->status) {
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
evt->status == BT_HCI_ERR_ADV_TIMEOUT) {
|
|
le_conn_complete_adv_timeout();
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
evt->status == BT_HCI_ERR_UNKNOWN_CONN_ID) {
|
|
le_conn_complete_cancel();
|
|
bt_le_scan_update(false);
|
|
return;
|
|
}
|
|
|
|
BT_WARN("Unexpected status 0x%02x", evt->status);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Translate "enhanced" identity address type to normal one */
|
|
if (evt->peer_addr.type == BT_ADDR_LE_PUBLIC_ID ||
|
|
evt->peer_addr.type == BT_ADDR_LE_RANDOM_ID) {
|
|
bt_addr_le_copy(&id_addr, &evt->peer_addr);
|
|
id_addr.type -= BT_ADDR_LE_PUBLIC_ID;
|
|
|
|
bt_addr_copy(&peer_addr.a, &evt->peer_rpa);
|
|
peer_addr.type = BT_ADDR_LE_RANDOM;
|
|
} else {
|
|
uint8_t id = evt->role == BT_HCI_ROLE_SLAVE ? bt_dev.adv_conn_id :
|
|
BT_ID_DEFAULT;
|
|
|
|
bt_addr_le_copy(&id_addr,
|
|
bt_lookup_id_addr(id, &evt->peer_addr));
|
|
bt_addr_le_copy(&peer_addr, &evt->peer_addr);
|
|
}
|
|
|
|
conn = find_pending_connect(evt->role, &id_addr);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
evt->role == BT_HCI_ROLE_SLAVE &&
|
|
!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
/* Clear advertising even if we are not able to add connection
|
|
* object to keep host in sync with controller state.
|
|
*/
|
|
atomic_clear_bit(adv->flags, BT_ADV_ENABLED);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
evt->role == BT_HCI_ROLE_MASTER) {
|
|
/* Clear initiating even if we are not able to add connection
|
|
* object to keep the host in sync with controller state.
|
|
*/
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_INITIATING);
|
|
}
|
|
|
|
if (!conn) {
|
|
BT_ERR("No pending conn for peer %s",
|
|
bt_addr_le_str(&evt->peer_addr));
|
|
bt_hci_disconnect(handle, BT_HCI_ERR_UNSPECIFIED);
|
|
return;
|
|
}
|
|
|
|
conn->handle = handle;
|
|
bt_addr_le_copy(&conn->le.dst, &id_addr);
|
|
conn->le.interval = sys_le16_to_cpu(evt->interval);
|
|
conn->le.latency = sys_le16_to_cpu(evt->latency);
|
|
conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout);
|
|
conn->role = evt->role;
|
|
conn->err = 0U;
|
|
|
|
#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
|
|
conn->le.data_len.tx_max_len = BT_GAP_DATA_LEN_DEFAULT;
|
|
conn->le.data_len.tx_max_time = BT_GAP_DATA_TIME_DEFAULT;
|
|
conn->le.data_len.rx_max_len = BT_GAP_DATA_LEN_DEFAULT;
|
|
conn->le.data_len.rx_max_time = BT_GAP_DATA_TIME_DEFAULT;
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_USER_PHY_UPDATE)
|
|
conn->le.phy.tx_phy = BT_GAP_LE_PHY_1M;
|
|
conn->le.phy.rx_phy = BT_GAP_LE_PHY_1M;
|
|
#endif
|
|
/*
|
|
* Use connection address (instead of identity address) as initiator
|
|
* or responder address. Only slave needs to be updated. For master all
|
|
* was set during outgoing connection creation.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
conn->role == BT_HCI_ROLE_SLAVE) {
|
|
bt_addr_le_copy(&conn->le.init_addr, &peer_addr);
|
|
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
conn->le.resp_addr.type = BT_ADDR_LE_RANDOM;
|
|
if (bt_addr_cmp(&evt->local_rpa,
|
|
BT_ADDR_ANY) != 0) {
|
|
bt_addr_copy(&conn->le.resp_addr.a,
|
|
&evt->local_rpa);
|
|
} else {
|
|
bt_addr_copy(&conn->le.resp_addr.a,
|
|
&bt_dev.random_addr.a);
|
|
}
|
|
} else {
|
|
bt_addr_le_copy(&conn->le.resp_addr,
|
|
&bt_dev.id_addr[conn->id]);
|
|
}
|
|
} else {
|
|
/* Copy the local RPA and handle this in advertising set
|
|
* terminated event.
|
|
*/
|
|
bt_addr_copy(&conn->le.resp_addr.a, &evt->local_rpa);
|
|
}
|
|
|
|
/* if the controller supports, lets advertise for another
|
|
* slave connection.
|
|
* check for connectable advertising state is sufficient as
|
|
* this is how this le connection complete for slave occurred.
|
|
*/
|
|
if (BT_LE_STATES_SLAVE_CONN_ADV(bt_dev.le.states)) {
|
|
bt_le_adv_resume();
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
!BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
/* No advertising set terminated event, must be a
|
|
* legacy advertiser set.
|
|
*/
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_PERSIST)) {
|
|
adv_delete_legacy();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_MASTER) {
|
|
bt_addr_le_copy(&conn->le.resp_addr, &peer_addr);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
conn->le.init_addr.type = BT_ADDR_LE_RANDOM;
|
|
if (bt_addr_cmp(&evt->local_rpa, BT_ADDR_ANY) != 0) {
|
|
bt_addr_copy(&conn->le.init_addr.a,
|
|
&evt->local_rpa);
|
|
} else {
|
|
bt_addr_copy(&conn->le.init_addr.a,
|
|
&bt_dev.random_addr.a);
|
|
}
|
|
} else {
|
|
bt_addr_le_copy(&conn->le.init_addr,
|
|
&bt_dev.id_addr[conn->id]);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_USER_PHY_UPDATE)
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
int err;
|
|
|
|
err = hci_le_read_phy(conn);
|
|
if (err) {
|
|
BT_WARN("Failed to read PHY (%d)", err);
|
|
} else {
|
|
if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) &&
|
|
conn->le.phy.tx_phy == BT_HCI_LE_PHY_PREFER_2M &&
|
|
conn->le.phy.rx_phy == BT_HCI_LE_PHY_PREFER_2M) {
|
|
/* Already on 2M, skip auto-phy update. */
|
|
atomic_set_bit(conn->flags,
|
|
BT_CONN_AUTO_PHY_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_USER_PHY_UPDATE) */
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
|
|
/* Start auto-initiated procedures */
|
|
conn_auto_initiate(conn);
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_MASTER) {
|
|
bt_le_scan_update(false);
|
|
}
|
|
}
|
|
|
|
static void le_enh_conn_complete(struct net_buf *buf)
|
|
{
|
|
enh_conn_complete((void *)buf->data);
|
|
}
|
|
|
|
static void le_legacy_conn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_conn_complete *evt = (void *)buf->data;
|
|
struct bt_hci_evt_le_enh_conn_complete enh;
|
|
|
|
BT_DBG("status 0x%02x role %u %s", evt->status, evt->role,
|
|
bt_addr_le_str(&evt->peer_addr));
|
|
|
|
enh.status = evt->status;
|
|
enh.handle = evt->handle;
|
|
enh.role = evt->role;
|
|
enh.interval = evt->interval;
|
|
enh.latency = evt->latency;
|
|
enh.supv_timeout = evt->supv_timeout;
|
|
enh.clock_accuracy = evt->clock_accuracy;
|
|
|
|
bt_addr_le_copy(&enh.peer_addr, &evt->peer_addr);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
bt_addr_copy(&enh.local_rpa, &bt_dev.random_addr.a);
|
|
} else {
|
|
bt_addr_copy(&enh.local_rpa, BT_ADDR_ANY);
|
|
}
|
|
|
|
bt_addr_copy(&enh.peer_rpa, BT_ADDR_ANY);
|
|
|
|
enh_conn_complete(&enh);
|
|
}
|
|
|
|
static void le_remote_feat_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_remote_feat_complete *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (!evt->status) {
|
|
memcpy(conn->le.features, evt->features,
|
|
sizeof(conn->le.features));
|
|
}
|
|
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_FEATURE_EXCH);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_REMOTE_INFO) &&
|
|
!IS_ENABLED(CONFIG_BT_REMOTE_VERSION)) {
|
|
notify_remote_info(conn);
|
|
}
|
|
|
|
/* Continue with auto-initiated procedures */
|
|
conn_auto_initiate(conn);
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_DATA_LEN_UPDATE)
|
|
static void le_data_len_change(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_data_len_change *evt = (void *)buf->data;
|
|
uint16_t max_tx_octets = sys_le16_to_cpu(evt->max_tx_octets);
|
|
uint16_t max_rx_octets = sys_le16_to_cpu(evt->max_rx_octets);
|
|
uint16_t max_tx_time = sys_le16_to_cpu(evt->max_tx_time);
|
|
uint16_t max_rx_time = sys_le16_to_cpu(evt->max_rx_time);
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("max. tx: %u (%uus), max. rx: %u (%uus)", max_tx_octets,
|
|
max_tx_time, max_rx_octets, max_rx_time);
|
|
|
|
#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
|
|
if (IS_ENABLED(CONFIG_BT_AUTO_DATA_LEN_UPDATE)) {
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_DATA_LEN_COMPLETE);
|
|
}
|
|
|
|
conn->le.data_len.tx_max_len = max_tx_octets;
|
|
conn->le.data_len.tx_max_time = max_tx_time;
|
|
conn->le.data_len.rx_max_len = max_rx_octets;
|
|
conn->le.data_len.rx_max_time = max_rx_time;
|
|
notify_le_data_len_updated(conn);
|
|
#endif
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_DATA_LEN_UPDATE */
|
|
|
|
#if defined(CONFIG_BT_PHY_UPDATE)
|
|
static void le_phy_update_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_phy_update_complete *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("PHY updated: status: 0x%02x, tx: %u, rx: %u",
|
|
evt->status, evt->tx_phy, evt->rx_phy);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) &&
|
|
atomic_test_and_clear_bit(conn->flags, BT_CONN_AUTO_PHY_UPDATE)) {
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_PHY_COMPLETE);
|
|
|
|
/* Continue with auto-initiated procedures */
|
|
conn_auto_initiate(conn);
|
|
}
|
|
|
|
#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);
|
|
notify_le_phy_updated(conn);
|
|
#endif
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_PHY_UPDATE */
|
|
|
|
bool bt_le_conn_params_valid(const struct bt_le_conn_param *param)
|
|
{
|
|
/* All limits according to BT Core spec 5.0 [Vol 2, Part E, 7.8.12] */
|
|
|
|
if (param->interval_min > param->interval_max ||
|
|
param->interval_min < 6 || param->interval_max > 3200) {
|
|
return false;
|
|
}
|
|
|
|
if (param->latency > 499) {
|
|
return false;
|
|
}
|
|
|
|
if (param->timeout < 10 || param->timeout > 3200 ||
|
|
((param->timeout * 4U) <=
|
|
((1U + param->latency) * param->interval_max))) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void le_conn_param_neg_reply(uint16_t handle, uint8_t reason)
|
|
{
|
|
struct bt_hci_cp_le_conn_param_req_neg_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Unable to allocate buffer");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
cp->reason = sys_cpu_to_le16(reason);
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, buf);
|
|
}
|
|
|
|
static int le_conn_param_req_reply(uint16_t handle,
|
|
const struct bt_le_conn_param *param)
|
|
{
|
|
struct bt_hci_cp_le_conn_param_req_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY, 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);
|
|
cp->interval_min = sys_cpu_to_le16(param->interval_min);
|
|
cp->interval_max = sys_cpu_to_le16(param->interval_max);
|
|
cp->latency = sys_cpu_to_le16(param->latency);
|
|
cp->timeout = sys_cpu_to_le16(param->timeout);
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY, buf);
|
|
}
|
|
|
|
static void le_conn_param_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_conn_param_req *evt = (void *)buf->data;
|
|
struct bt_le_conn_param param;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
param.interval_min = sys_le16_to_cpu(evt->interval_min);
|
|
param.interval_max = sys_le16_to_cpu(evt->interval_max);
|
|
param.latency = sys_le16_to_cpu(evt->latency);
|
|
param.timeout = sys_le16_to_cpu(evt->timeout);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
le_conn_param_neg_reply(handle, BT_HCI_ERR_UNKNOWN_CONN_ID);
|
|
return;
|
|
}
|
|
|
|
if (!le_param_req(conn, ¶m)) {
|
|
le_conn_param_neg_reply(handle, BT_HCI_ERR_INVALID_LL_PARAM);
|
|
} else {
|
|
le_conn_param_req_reply(handle, ¶m);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void le_conn_update_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_conn_update_complete *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("status 0x%02x, handle %u", evt->status, handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (!evt->status) {
|
|
conn->le.interval = sys_le16_to_cpu(evt->interval);
|
|
conn->le.latency = sys_le16_to_cpu(evt->latency);
|
|
conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout);
|
|
notify_le_param_updated(conn);
|
|
} else if (evt->status == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE &&
|
|
conn->role == BT_HCI_ROLE_SLAVE &&
|
|
!atomic_test_and_set_bit(conn->flags,
|
|
BT_CONN_SLAVE_PARAM_L2CAP)) {
|
|
/* CPR not supported, let's try L2CAP CPUP instead */
|
|
struct bt_le_conn_param param;
|
|
|
|
param.interval_min = conn->le.interval_min;
|
|
param.interval_max = conn->le.interval_max;
|
|
param.latency = conn->le.pending_latency;
|
|
param.timeout = conn->le.pending_timeout;
|
|
|
|
bt_l2cap_update_conn_param(conn, ¶m);
|
|
}
|
|
|
|
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) &&
|
|
set_le_scan_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)
|
|
{
|
|
struct bt_hci_cp_host_buffer_size *hbs;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
/* Check if host flow control is actually supported */
|
|
if (!BT_CMD_TEST(bt_dev.supported_commands, 10, 5)) {
|
|
BT_WARN("Controller to host flow control not supported");
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE,
|
|
sizeof(*hbs));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
hbs = net_buf_add(buf, sizeof(*hbs));
|
|
(void)memset(hbs, 0, sizeof(*hbs));
|
|
hbs->acl_mtu = sys_cpu_to_le16(CONFIG_BT_L2CAP_RX_MTU +
|
|
sizeof(struct bt_l2cap_hdr));
|
|
hbs->acl_pkts = sys_cpu_to_le16(CONFIG_BT_ACL_RX_COUNT);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_HOST_BUFFER_SIZE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_u8(buf, BT_HCI_CTL_TO_HOST_FLOW_ENABLE);
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL);
|
|
}
|
|
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
|
|
|
|
static void unpair(uint8_t id, const bt_addr_le_t *addr)
|
|
{
|
|
struct bt_keys *keys = NULL;
|
|
struct bt_conn *conn = bt_conn_lookup_addr_le(id, addr);
|
|
|
|
if (conn) {
|
|
/* Clear the conn->le.keys pointer since we'll invalidate it,
|
|
* and don't want any subsequent code (like disconnected
|
|
* callbacks) accessing it.
|
|
*/
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
keys = conn->le.keys;
|
|
conn->le.keys = NULL;
|
|
}
|
|
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_BREDR)) {
|
|
/* LE Public may indicate BR/EDR as well */
|
|
if (addr->type == BT_ADDR_LE_PUBLIC) {
|
|
bt_keys_link_key_clear_addr(&addr->a);
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
if (!keys) {
|
|
keys = bt_keys_find_addr(id, addr);
|
|
}
|
|
|
|
if (keys) {
|
|
bt_keys_clear(keys);
|
|
}
|
|
}
|
|
|
|
bt_gatt_clear(id, addr);
|
|
|
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
|
if (bt_auth && bt_auth->bond_deleted) {
|
|
bt_auth->bond_deleted(id, addr);
|
|
}
|
|
#endif /* defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) */
|
|
}
|
|
|
|
static void unpair_remote(const struct bt_bond_info *info, void *data)
|
|
{
|
|
uint8_t *id = (uint8_t *) data;
|
|
|
|
unpair(*id, &info->addr);
|
|
}
|
|
|
|
int bt_unpair(uint8_t id, const bt_addr_le_t *addr)
|
|
{
|
|
if (id >= CONFIG_BT_ID_MAX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP) &&
|
|
(!addr || !bt_addr_le_cmp(addr, BT_ADDR_LE_ANY))) {
|
|
bt_foreach_bond(id, unpair_remote, &id);
|
|
return 0;
|
|
}
|
|
|
|
unpair(id, addr);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_BT_CONN */
|
|
|
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
|
enum bt_security_err bt_security_err_get(uint8_t hci_err)
|
|
{
|
|
switch (hci_err) {
|
|
case BT_HCI_ERR_SUCCESS:
|
|
return BT_SECURITY_ERR_SUCCESS;
|
|
case BT_HCI_ERR_AUTH_FAIL:
|
|
return BT_SECURITY_ERR_AUTH_FAIL;
|
|
case BT_HCI_ERR_PIN_OR_KEY_MISSING:
|
|
return BT_SECURITY_ERR_PIN_OR_KEY_MISSING;
|
|
case BT_HCI_ERR_PAIRING_NOT_SUPPORTED:
|
|
return BT_SECURITY_ERR_PAIR_NOT_SUPPORTED;
|
|
case BT_HCI_ERR_PAIRING_NOT_ALLOWED:
|
|
return BT_SECURITY_ERR_PAIR_NOT_ALLOWED;
|
|
case BT_HCI_ERR_INVALID_PARAM:
|
|
return BT_SECURITY_ERR_INVALID_PARAM;
|
|
default:
|
|
return BT_SECURITY_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static int reject_conn(const bt_addr_t *bdaddr, uint8_t reason)
|
|
{
|
|
struct bt_hci_cp_reject_conn_req *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_REJECT_CONN_REQ, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
cp->reason = reason;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_REJECT_CONN_REQ, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int accept_sco_conn(const bt_addr_t *bdaddr, struct bt_conn *sco_conn)
|
|
{
|
|
struct bt_hci_cp_accept_sync_conn_req *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
cp->pkt_type = sco_conn->sco.pkt_type;
|
|
cp->tx_bandwidth = 0x00001f40;
|
|
cp->rx_bandwidth = 0x00001f40;
|
|
cp->max_latency = 0x0007;
|
|
cp->retrans_effort = 0x01;
|
|
cp->content_format = BT_VOICE_CVSD_16BIT;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int accept_conn(const bt_addr_t *bdaddr)
|
|
{
|
|
struct bt_hci_cp_accept_conn_req *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_CONN_REQ, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
cp->role = BT_HCI_ROLE_SLAVE;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_CONN_REQ, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bt_esco_conn_req(struct bt_hci_evt_conn_request *evt)
|
|
{
|
|
struct bt_conn *sco_conn;
|
|
|
|
sco_conn = bt_conn_add_sco(&evt->bdaddr, evt->link_type);
|
|
if (!sco_conn) {
|
|
reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES);
|
|
return;
|
|
}
|
|
|
|
if (accept_sco_conn(&evt->bdaddr, sco_conn)) {
|
|
BT_ERR("Error accepting connection from %s",
|
|
bt_addr_str(&evt->bdaddr));
|
|
reject_conn(&evt->bdaddr, BT_HCI_ERR_UNSPECIFIED);
|
|
bt_sco_cleanup(sco_conn);
|
|
return;
|
|
}
|
|
|
|
sco_conn->role = BT_HCI_ROLE_SLAVE;
|
|
bt_conn_set_state(sco_conn, BT_CONN_CONNECT);
|
|
bt_conn_unref(sco_conn);
|
|
}
|
|
|
|
static void conn_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_conn_request *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("conn req from %s, type 0x%02x", bt_addr_str(&evt->bdaddr),
|
|
evt->link_type);
|
|
|
|
if (evt->link_type != BT_HCI_ACL) {
|
|
bt_esco_conn_req(evt);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_add_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES);
|
|
return;
|
|
}
|
|
|
|
accept_conn(&evt->bdaddr);
|
|
conn->role = BT_HCI_ROLE_SLAVE;
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static bool br_sufficient_key_size(struct bt_conn *conn)
|
|
{
|
|
struct bt_hci_cp_read_encryption_key_size *cp;
|
|
struct bt_hci_rp_read_encryption_key_size *rp;
|
|
struct net_buf *buf, *rsp;
|
|
uint8_t key_size;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Failed to allocate command buffer");
|
|
return false;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE,
|
|
buf, &rsp);
|
|
if (err) {
|
|
BT_ERR("Failed to read encryption key size (err %d)", err);
|
|
return false;
|
|
}
|
|
|
|
if (rsp->len < sizeof(*rp)) {
|
|
BT_ERR("Too small command complete for encryption key size");
|
|
net_buf_unref(rsp);
|
|
return false;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
key_size = rp->key_size;
|
|
net_buf_unref(rsp);
|
|
|
|
BT_DBG("Encryption key size is %u", key_size);
|
|
|
|
if (conn->sec_level == BT_SECURITY_L4) {
|
|
return key_size == BT_HCI_ENCRYPTION_KEY_SIZE_MAX;
|
|
}
|
|
|
|
return key_size >= BT_HCI_ENCRYPTION_KEY_SIZE_MIN;
|
|
}
|
|
|
|
static bool update_sec_level_br(struct bt_conn *conn)
|
|
{
|
|
if (!conn->encrypt) {
|
|
conn->sec_level = BT_SECURITY_L1;
|
|
return true;
|
|
}
|
|
|
|
if (conn->br.link_key) {
|
|
if (conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) {
|
|
if (conn->encrypt == 0x02) {
|
|
conn->sec_level = BT_SECURITY_L4;
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_L3;
|
|
}
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_L2;
|
|
}
|
|
} else {
|
|
BT_WARN("No BR/EDR link key found");
|
|
conn->sec_level = BT_SECURITY_L2;
|
|
}
|
|
|
|
if (!br_sufficient_key_size(conn)) {
|
|
BT_ERR("Encryption key size is not sufficient");
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
|
|
return false;
|
|
}
|
|
|
|
if (conn->required_sec_level > conn->sec_level) {
|
|
BT_ERR("Failed to set required security level");
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void synchronous_conn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_sync_conn_complete *evt = (void *)buf->data;
|
|
struct bt_conn *sco_conn;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle,
|
|
evt->link_type);
|
|
|
|
sco_conn = bt_conn_lookup_addr_sco(&evt->bdaddr);
|
|
if (!sco_conn) {
|
|
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
if (evt->status) {
|
|
sco_conn->err = evt->status;
|
|
bt_conn_set_state(sco_conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(sco_conn);
|
|
return;
|
|
}
|
|
|
|
sco_conn->handle = handle;
|
|
bt_conn_set_state(sco_conn, BT_CONN_CONNECTED);
|
|
bt_conn_unref(sco_conn);
|
|
}
|
|
|
|
static void conn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_conn_complete *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
struct bt_hci_cp_read_remote_features *cp;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle,
|
|
evt->link_type);
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
if (evt->status) {
|
|
conn->err = evt->status;
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
conn->handle = handle;
|
|
conn->err = 0U;
|
|
conn->encrypt = evt->encr_enabled;
|
|
|
|
if (!update_sec_level_br(conn)) {
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
bt_conn_unref(conn);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_FEATURES, sizeof(*cp));
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL);
|
|
}
|
|
|
|
struct discovery_priv {
|
|
uint16_t clock_offset;
|
|
uint8_t pscan_rep_mode;
|
|
uint8_t resolving;
|
|
} __packed;
|
|
|
|
static int request_name(const bt_addr_t *addr, uint8_t pscan, uint16_t offset)
|
|
{
|
|
struct bt_hci_cp_remote_name_request *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_REQUEST, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
|
|
bt_addr_copy(&cp->bdaddr, addr);
|
|
cp->pscan_rep_mode = pscan;
|
|
cp->reserved = 0x00; /* reserver, should be set to 0x00 */
|
|
cp->clock_offset = offset;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_REQUEST, buf, NULL);
|
|
}
|
|
|
|
#define EIR_SHORT_NAME 0x08
|
|
#define EIR_COMPLETE_NAME 0x09
|
|
|
|
static bool eir_has_name(const uint8_t *eir)
|
|
{
|
|
int len = 240;
|
|
|
|
while (len) {
|
|
if (len < 2) {
|
|
break;
|
|
};
|
|
|
|
/* Look for early termination */
|
|
if (!eir[0]) {
|
|
break;
|
|
}
|
|
|
|
/* Check if field length is correct */
|
|
if (eir[0] > len - 1) {
|
|
break;
|
|
}
|
|
|
|
switch (eir[1]) {
|
|
case EIR_SHORT_NAME:
|
|
case EIR_COMPLETE_NAME:
|
|
if (eir[0] > 1) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Parse next AD Structure */
|
|
len -= eir[0] + 1;
|
|
eir += eir[0] + 1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void report_discovery_results(void)
|
|
{
|
|
bool resolving_names = false;
|
|
int i;
|
|
|
|
for (i = 0; i < discovery_results_count; i++) {
|
|
struct discovery_priv *priv;
|
|
|
|
priv = (struct discovery_priv *)&discovery_results[i]._priv;
|
|
|
|
if (eir_has_name(discovery_results[i].eir)) {
|
|
continue;
|
|
}
|
|
|
|
if (request_name(&discovery_results[i].addr,
|
|
priv->pscan_rep_mode, priv->clock_offset)) {
|
|
continue;
|
|
}
|
|
|
|
priv->resolving = 1U;
|
|
resolving_names = true;
|
|
}
|
|
|
|
if (resolving_names) {
|
|
return;
|
|
}
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
|
|
|
|
discovery_cb(discovery_results, discovery_results_count);
|
|
|
|
discovery_cb = NULL;
|
|
discovery_results = NULL;
|
|
discovery_results_size = 0;
|
|
discovery_results_count = 0;
|
|
}
|
|
|
|
static void inquiry_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_inquiry_complete *evt = (void *)buf->data;
|
|
|
|
if (evt->status) {
|
|
BT_ERR("Failed to complete inquiry");
|
|
}
|
|
|
|
report_discovery_results();
|
|
}
|
|
|
|
static struct bt_br_discovery_result *get_result_slot(const bt_addr_t *addr,
|
|
int8_t rssi)
|
|
{
|
|
struct bt_br_discovery_result *result = NULL;
|
|
size_t i;
|
|
|
|
/* check if already present in results */
|
|
for (i = 0; i < discovery_results_count; i++) {
|
|
if (!bt_addr_cmp(addr, &discovery_results[i].addr)) {
|
|
return &discovery_results[i];
|
|
}
|
|
}
|
|
|
|
/* Pick a new slot (if available) */
|
|
if (discovery_results_count < discovery_results_size) {
|
|
bt_addr_copy(&discovery_results[discovery_results_count].addr,
|
|
addr);
|
|
return &discovery_results[discovery_results_count++];
|
|
}
|
|
|
|
/* ignore if invalid RSSI */
|
|
if (rssi == 0xff) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Pick slot with smallest RSSI that is smaller then passed RSSI
|
|
* TODO handle TX if present
|
|
*/
|
|
for (i = 0; i < discovery_results_size; i++) {
|
|
if (discovery_results[i].rssi > rssi) {
|
|
continue;
|
|
}
|
|
|
|
if (!result || result->rssi > discovery_results[i].rssi) {
|
|
result = &discovery_results[i];
|
|
}
|
|
}
|
|
|
|
if (result) {
|
|
BT_DBG("Reusing slot (old %s rssi %d dBm)",
|
|
bt_addr_str(&result->addr), result->rssi);
|
|
|
|
bt_addr_copy(&result->addr, addr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void inquiry_result_with_rssi(struct net_buf *buf)
|
|
{
|
|
uint8_t num_reports = net_buf_pull_u8(buf);
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
|
|
return;
|
|
}
|
|
|
|
BT_DBG("number of results: %u", num_reports);
|
|
|
|
while (num_reports--) {
|
|
struct bt_hci_evt_inquiry_result_with_rssi *evt;
|
|
struct bt_br_discovery_result *result;
|
|
struct discovery_priv *priv;
|
|
|
|
if (buf->len < sizeof(*evt)) {
|
|
BT_ERR("Unexpected end to buffer");
|
|
return;
|
|
}
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi);
|
|
|
|
result = get_result_slot(&evt->addr, evt->rssi);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->_priv;
|
|
priv->pscan_rep_mode = evt->pscan_rep_mode;
|
|
priv->clock_offset = evt->clock_offset;
|
|
|
|
memcpy(result->cod, evt->cod, 3);
|
|
result->rssi = evt->rssi;
|
|
|
|
/* we could reuse slot so make sure EIR is cleared */
|
|
(void)memset(result->eir, 0, sizeof(result->eir));
|
|
}
|
|
}
|
|
|
|
static void extended_inquiry_result(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_extended_inquiry_result *evt = (void *)buf->data;
|
|
struct bt_br_discovery_result *result;
|
|
struct discovery_priv *priv;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
|
|
return;
|
|
}
|
|
|
|
BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi);
|
|
|
|
result = get_result_slot(&evt->addr, evt->rssi);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->_priv;
|
|
priv->pscan_rep_mode = evt->pscan_rep_mode;
|
|
priv->clock_offset = evt->clock_offset;
|
|
|
|
result->rssi = evt->rssi;
|
|
memcpy(result->cod, evt->cod, 3);
|
|
memcpy(result->eir, evt->eir, sizeof(result->eir));
|
|
}
|
|
|
|
static void remote_name_request_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_remote_name_req_complete *evt = (void *)buf->data;
|
|
struct bt_br_discovery_result *result;
|
|
struct discovery_priv *priv;
|
|
int eir_len = 240;
|
|
uint8_t *eir;
|
|
int i;
|
|
|
|
result = get_result_slot(&evt->bdaddr, 0xff);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->_priv;
|
|
priv->resolving = 0U;
|
|
|
|
if (evt->status) {
|
|
goto check_names;
|
|
}
|
|
|
|
eir = result->eir;
|
|
|
|
while (eir_len) {
|
|
if (eir_len < 2) {
|
|
break;
|
|
};
|
|
|
|
/* Look for early termination */
|
|
if (!eir[0]) {
|
|
size_t name_len;
|
|
|
|
eir_len -= 2;
|
|
|
|
/* name is null terminated */
|
|
name_len = strlen((const char *)evt->name);
|
|
|
|
if (name_len > eir_len) {
|
|
eir[0] = eir_len + 1;
|
|
eir[1] = EIR_SHORT_NAME;
|
|
} else {
|
|
eir[0] = name_len + 1;
|
|
eir[1] = EIR_SHORT_NAME;
|
|
}
|
|
|
|
memcpy(&eir[2], evt->name, eir[0] - 1);
|
|
|
|
break;
|
|
}
|
|
|
|
/* Check if field length is correct */
|
|
if (eir[0] > eir_len - 1) {
|
|
break;
|
|
}
|
|
|
|
/* next EIR Structure */
|
|
eir_len -= eir[0] + 1;
|
|
eir += eir[0] + 1;
|
|
}
|
|
|
|
check_names:
|
|
/* if still waiting for names */
|
|
for (i = 0; i < discovery_results_count; i++) {
|
|
struct discovery_priv *priv;
|
|
|
|
priv = (struct discovery_priv *)&discovery_results[i]._priv;
|
|
|
|
if (priv->resolving) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* all names resolved, report discovery results */
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
|
|
|
|
discovery_cb(discovery_results, discovery_results_count);
|
|
|
|
discovery_cb = NULL;
|
|
discovery_results = NULL;
|
|
discovery_results_size = 0;
|
|
discovery_results_count = 0;
|
|
}
|
|
|
|
static void read_remote_features_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_remote_features *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_hci_cp_read_remote_ext_features *cp;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u", evt->status, handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (evt->status) {
|
|
goto done;
|
|
}
|
|
|
|
memcpy(conn->br.features[0], evt->features, sizeof(evt->features));
|
|
|
|
if (!BT_FEAT_EXT_FEATURES(conn->br.features)) {
|
|
goto done;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_EXT_FEATURES,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
goto done;
|
|
}
|
|
|
|
/* Read remote host features (page 1) */
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
cp->page = 0x01;
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_EXT_FEATURES, buf, NULL);
|
|
|
|
done:
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void read_remote_ext_features_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_remote_ext_features *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u", evt->status, handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (!evt->status && evt->page == 0x01) {
|
|
memcpy(conn->br.features[1], evt->features,
|
|
sizeof(conn->br.features[1]));
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void role_change(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_role_change *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x role %u addr %s", evt->status, evt->role,
|
|
bt_addr_str(&evt->bdaddr));
|
|
|
|
if (evt->status) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
if (evt->role) {
|
|
conn->role = BT_CONN_ROLE_SLAVE;
|
|
} else {
|
|
conn->role = BT_CONN_ROLE_MASTER;
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
static int le_set_privacy_mode(const bt_addr_le_t *addr, uint8_t mode)
|
|
{
|
|
struct bt_hci_cp_le_set_privacy_mode cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
/* Check if set privacy mode command is supported */
|
|
if (!BT_CMD_TEST(bt_dev.supported_commands, 39, 2)) {
|
|
BT_WARN("Set privacy mode command is not supported");
|
|
return 0;
|
|
}
|
|
|
|
BT_DBG("addr %s mode 0x%02x", bt_addr_le_str(addr), mode);
|
|
|
|
bt_addr_le_copy(&cp.id_addr, addr);
|
|
cp.mode = mode;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PRIVACY_MODE, sizeof(cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_mem(buf, &cp, sizeof(cp));
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PRIVACY_MODE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int addr_res_enable(uint8_t enable)
|
|
{
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("%s", enable ? "enabled" : "disabled");
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADDR_RES_ENABLE, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_u8(buf, enable);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADDR_RES_ENABLE,
|
|
buf, NULL);
|
|
}
|
|
|
|
static int hci_id_add(uint8_t id, const bt_addr_le_t *addr, uint8_t peer_irk[16])
|
|
{
|
|
struct bt_hci_cp_le_add_dev_to_rl *cp;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("addr %s", bt_addr_le_str(addr));
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_ADD_DEV_TO_RL, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_le_copy(&cp->peer_id_addr, addr);
|
|
memcpy(cp->peer_irk, peer_irk, 16);
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
memcpy(cp->local_irk, bt_dev.irk[id], 16);
|
|
#else
|
|
(void)memset(cp->local_irk, 0, 16);
|
|
#endif
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_RL, buf, NULL);
|
|
}
|
|
|
|
void bt_id_add(struct bt_keys *keys)
|
|
{
|
|
struct bt_conn *conn;
|
|
int err;
|
|
|
|
BT_DBG("addr %s", bt_addr_le_str(&keys->addr));
|
|
|
|
/* Nothing to be done if host-side resolving is used */
|
|
if (!bt_dev.le.rl_size || bt_dev.le.rl_entries > bt_dev.le.rl_size) {
|
|
bt_dev.le.rl_entries++;
|
|
keys->state |= BT_KEYS_ID_ADDED;
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, BT_CONN_CONNECT);
|
|
if (conn) {
|
|
pending_id_keys_update_set(keys, BT_KEYS_ID_PENDING_ADD);
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV)) {
|
|
bool adv_enabled = false;
|
|
|
|
bt_adv_foreach(adv_is_limited_enabled, &adv_enabled);
|
|
if (adv_enabled) {
|
|
pending_id_keys_update_set(keys,
|
|
BT_KEYS_ID_PENDING_ADD);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
bool scan_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) && scan_enabled &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED)) {
|
|
pending_id_keys_update_set(keys, BT_KEYS_ID_PENDING_ADD);
|
|
}
|
|
#endif
|
|
|
|
bt_adv_foreach(adv_pause_enabled, NULL);
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
/* If there are any existing entries address resolution will be on */
|
|
if (bt_dev.le.rl_entries) {
|
|
err = addr_res_enable(BT_HCI_ADDR_RES_DISABLE);
|
|
if (err) {
|
|
BT_WARN("Failed to disable address resolution");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (bt_dev.le.rl_entries == bt_dev.le.rl_size) {
|
|
BT_WARN("Resolving list size exceeded. Switching to host.");
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CLEAR_RL, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("Failed to clear resolution list");
|
|
goto done;
|
|
}
|
|
|
|
bt_dev.le.rl_entries++;
|
|
keys->state |= BT_KEYS_ID_ADDED;
|
|
|
|
goto done;
|
|
}
|
|
|
|
err = hci_id_add(keys->id, &keys->addr, keys->irk.val);
|
|
if (err) {
|
|
BT_ERR("Failed to add IRK to controller");
|
|
goto done;
|
|
}
|
|
|
|
bt_dev.le.rl_entries++;
|
|
keys->state |= BT_KEYS_ID_ADDED;
|
|
|
|
/*
|
|
* According to Core Spec. 5.0 Vol 1, Part A 5.4.5 Privacy Feature
|
|
*
|
|
* By default, network privacy mode is used when private addresses are
|
|
* resolved and generated by the Controller, so advertising packets from
|
|
* peer devices that contain private addresses will only be accepted.
|
|
* By changing to the device privacy mode device is only concerned about
|
|
* its privacy and will accept advertising packets from peer devices
|
|
* that contain their identity address as well as ones that contain
|
|
* a private address, even if the peer device has distributed its IRK in
|
|
* the past.
|
|
*/
|
|
err = le_set_privacy_mode(&keys->addr, BT_HCI_LE_PRIVACY_MODE_DEVICE);
|
|
if (err) {
|
|
BT_ERR("Failed to set privacy mode");
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
addr_res_enable(BT_HCI_ADDR_RES_ENABLE);
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
bt_adv_foreach(adv_unpause_enabled, NULL);
|
|
}
|
|
|
|
static void keys_add_id(struct bt_keys *keys, void *data)
|
|
{
|
|
if (keys->state & BT_KEYS_ID_ADDED) {
|
|
hci_id_add(keys->id, &keys->addr, keys->irk.val);
|
|
}
|
|
}
|
|
|
|
static int hci_id_del(const bt_addr_le_t *addr)
|
|
{
|
|
struct bt_hci_cp_le_rem_dev_from_rl *cp;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("addr %s", bt_addr_le_str(addr));
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_REM_DEV_FROM_RL, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_le_copy(&cp->peer_id_addr, addr);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_RL, buf, NULL);
|
|
}
|
|
|
|
void bt_id_del(struct bt_keys *keys)
|
|
{
|
|
struct bt_conn *conn;
|
|
int err;
|
|
|
|
BT_DBG("addr %s", bt_addr_le_str(&keys->addr));
|
|
|
|
if (!bt_dev.le.rl_size ||
|
|
bt_dev.le.rl_entries > bt_dev.le.rl_size + 1) {
|
|
bt_dev.le.rl_entries--;
|
|
keys->state &= ~BT_KEYS_ID_ADDED;
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL, BT_CONN_CONNECT);
|
|
if (conn) {
|
|
pending_id_keys_update_set(keys, BT_KEYS_ID_PENDING_DEL);
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV)) {
|
|
bool adv_enabled = false;
|
|
|
|
bt_adv_foreach(adv_is_limited_enabled, &adv_enabled);
|
|
if (adv_enabled) {
|
|
pending_id_keys_update_set(keys,
|
|
BT_KEYS_ID_PENDING_ADD);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
bool scan_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) && scan_enabled &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_LIMITED)) {
|
|
pending_id_keys_update_set(keys, BT_KEYS_ID_PENDING_DEL);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
bt_adv_foreach(adv_pause_enabled, NULL);
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
err = addr_res_enable(BT_HCI_ADDR_RES_DISABLE);
|
|
if (err) {
|
|
BT_ERR("Disabling address resolution failed (err %d)", err);
|
|
goto done;
|
|
}
|
|
|
|
/* We checked size + 1 earlier, so here we know we can fit again */
|
|
if (bt_dev.le.rl_entries > bt_dev.le.rl_size) {
|
|
bt_dev.le.rl_entries--;
|
|
keys->state &= ~BT_KEYS_ID_ADDED;
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
bt_keys_foreach(BT_KEYS_ALL, keys_add_id, NULL);
|
|
} else {
|
|
bt_keys_foreach(BT_KEYS_IRK, keys_add_id, NULL);
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
err = hci_id_del(&keys->addr);
|
|
if (err) {
|
|
BT_ERR("Failed to remove IRK from controller");
|
|
goto done;
|
|
}
|
|
|
|
bt_dev.le.rl_entries--;
|
|
keys->state &= ~BT_KEYS_ID_ADDED;
|
|
|
|
done:
|
|
/* Only re-enable if there are entries to do resolving with */
|
|
if (bt_dev.le.rl_entries) {
|
|
addr_res_enable(BT_HCI_ADDR_RES_ENABLE);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE);
|
|
}
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
|
|
bt_adv_foreach(adv_unpause_enabled, NULL);
|
|
}
|
|
|
|
static void update_sec_level(struct bt_conn *conn)
|
|
{
|
|
if (!conn->encrypt) {
|
|
conn->sec_level = BT_SECURITY_L1;
|
|
return;
|
|
}
|
|
|
|
if (conn->le.keys && (conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) {
|
|
if (conn->le.keys->flags & BT_KEYS_SC &&
|
|
conn->le.keys->enc_size == BT_SMP_MAX_ENC_KEY_SIZE) {
|
|
conn->sec_level = BT_SECURITY_L4;
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_L3;
|
|
}
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_L2;
|
|
}
|
|
|
|
if (conn->required_sec_level > conn->sec_level) {
|
|
BT_ERR("Failed to set required security level");
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_SMP */
|
|
|
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
|
static void hci_encrypt_change(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_encrypt_change *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status 0x%02x handle %u encrypt 0x%02x", evt->status, handle,
|
|
evt->encrypt);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to look up conn with handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (evt->status) {
|
|
bt_conn_security_changed(conn, evt->status,
|
|
bt_security_err_get(evt->status));
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
conn->encrypt = evt->encrypt;
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
/*
|
|
* we update keys properties only on successful encryption to
|
|
* avoid losing valid keys if encryption was not successful.
|
|
*
|
|
* Update keys with last pairing info for proper sec level
|
|
* update. This is done only for LE transport, for BR/EDR keys
|
|
* are updated on HCI 'Link Key Notification Event'
|
|
*/
|
|
if (conn->encrypt) {
|
|
bt_smp_update_keys(conn);
|
|
}
|
|
update_sec_level(conn);
|
|
}
|
|
#endif /* CONFIG_BT_SMP */
|
|
#if defined(CONFIG_BT_BREDR)
|
|
if (conn->type == BT_CONN_TYPE_BR) {
|
|
if (!update_sec_level_br(conn)) {
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
/*
|
|
* Start SMP over BR/EDR if we are pairing and are
|
|
* master on the link
|
|
*/
|
|
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING) &&
|
|
conn->role == BT_CONN_ROLE_MASTER) {
|
|
bt_smp_br_send_pairing_req(conn);
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
bt_conn_security_changed(conn, evt->status, BT_SECURITY_ERR_SUCCESS);
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void hci_encrypt_key_refresh_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_encrypt_key_refresh_complete *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("status 0x%02x handle %u", evt->status, handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to look up conn with handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (evt->status) {
|
|
bt_conn_security_changed(conn, evt->status,
|
|
bt_security_err_get(evt->status));
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Update keys with last pairing info for proper sec level update.
|
|
* This is done only for LE transport. For BR/EDR transport keys are
|
|
* updated on HCI 'Link Key Notification Event', therefore update here
|
|
* only security level based on available keys and encryption state.
|
|
*/
|
|
#if defined(CONFIG_BT_SMP)
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
bt_smp_update_keys(conn);
|
|
update_sec_level(conn);
|
|
}
|
|
#endif /* CONFIG_BT_SMP */
|
|
#if defined(CONFIG_BT_BREDR)
|
|
if (conn->type == BT_CONN_TYPE_BR) {
|
|
if (!update_sec_level_br(conn)) {
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
bt_conn_security_changed(conn, evt->status, BT_SECURITY_ERR_SUCCESS);
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
|
|
|
#if defined(CONFIG_BT_REMOTE_VERSION)
|
|
static void bt_hci_evt_read_remote_version_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_remote_version_info *evt;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("No connection for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (!evt->status) {
|
|
conn->rv.version = evt->version;
|
|
conn->rv.manufacturer = sys_le16_to_cpu(evt->manufacturer);
|
|
conn->rv.subversion = sys_le16_to_cpu(evt->subversion);
|
|
}
|
|
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_REMOTE_INFO)) {
|
|
/* Remote features is already present */
|
|
notify_remote_info(conn);
|
|
}
|
|
|
|
/* Continue with auto-initiated procedures */
|
|
conn_auto_initiate(conn);
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_REMOTE_VERSION */
|
|
|
|
static void hci_hardware_error(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_hardware_error *evt;
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
|
|
BT_ERR("Hardware error, hardware code: %d", evt->hardware_code);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
static void le_ltk_neg_reply(uint16_t handle)
|
|
{
|
|
struct bt_hci_cp_le_ltk_req_neg_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf);
|
|
}
|
|
|
|
static void le_ltk_reply(uint16_t handle, uint8_t *ltk)
|
|
{
|
|
struct bt_hci_cp_le_ltk_req_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
memcpy(cp->ltk, ltk, sizeof(cp->ltk));
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
|
|
}
|
|
|
|
static void le_ltk_request(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_ltk_request *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
uint8_t ltk[16];
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("handle %u", handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to lookup conn for handle %u", handle);
|
|
return;
|
|
}
|
|
|
|
if (bt_smp_request_ltk(conn, evt->rand, evt->ediv, ltk)) {
|
|
le_ltk_reply(handle, ltk);
|
|
} else {
|
|
le_ltk_neg_reply(handle);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BT_SMP */
|
|
|
|
#if defined(CONFIG_BT_ECC)
|
|
static void le_pkey_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_p256_public_key_complete *evt = (void *)buf->data;
|
|
struct bt_pub_key_cb *cb;
|
|
|
|
BT_DBG("status: 0x%02x", evt->status);
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY);
|
|
|
|
if (!evt->status) {
|
|
memcpy(pub_key, evt->key, 64);
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY);
|
|
}
|
|
|
|
for (cb = pub_key_cb; cb; cb = cb->_next) {
|
|
cb->func(evt->status ? NULL : pub_key);
|
|
}
|
|
|
|
pub_key_cb = NULL;
|
|
}
|
|
|
|
static void le_dhkey_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_generate_dhkey_complete *evt = (void *)buf->data;
|
|
|
|
BT_DBG("status: 0x%02x", evt->status);
|
|
|
|
if (dh_key_cb) {
|
|
bt_dh_key_cb_t cb = dh_key_cb;
|
|
|
|
dh_key_cb = NULL;
|
|
cb(evt->status ? NULL : evt->dhkey);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_ECC */
|
|
|
|
static void hci_reset_complete(struct net_buf *buf)
|
|
{
|
|
uint8_t status = buf->data[0];
|
|
atomic_t flags;
|
|
|
|
BT_DBG("status 0x%02x", status);
|
|
|
|
if (status) {
|
|
return;
|
|
}
|
|
|
|
scan_dev_found_cb = NULL;
|
|
#if defined(CONFIG_BT_BREDR)
|
|
discovery_cb = NULL;
|
|
discovery_results = NULL;
|
|
discovery_results_size = 0;
|
|
discovery_results_count = 0;
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
flags = (atomic_get(bt_dev.flags) & BT_DEV_PERSISTENT_FLAGS);
|
|
atomic_set(bt_dev.flags, flags);
|
|
}
|
|
|
|
static void hci_cmd_done(uint16_t opcode, uint8_t status, struct net_buf *buf)
|
|
{
|
|
BT_DBG("opcode 0x%04x status 0x%02x buf %p", opcode, status, buf);
|
|
|
|
if (net_buf_pool_get(buf->pool_id) != &hci_cmd_pool) {
|
|
BT_WARN("opcode 0x%04x pool id %u pool %p != &hci_cmd_pool %p",
|
|
opcode, buf->pool_id, net_buf_pool_get(buf->pool_id),
|
|
&hci_cmd_pool);
|
|
return;
|
|
}
|
|
|
|
if (cmd(buf)->opcode != opcode) {
|
|
BT_WARN("OpCode 0x%04x completed instead of expected 0x%04x",
|
|
opcode, cmd(buf)->opcode);
|
|
}
|
|
|
|
if (cmd(buf)->state && !status) {
|
|
struct bt_hci_cmd_state_set *update = cmd(buf)->state;
|
|
|
|
atomic_set_bit_to(update->target, update->bit, update->val);
|
|
}
|
|
|
|
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
|
|
if (cmd(buf)->sync) {
|
|
cmd(buf)->status = status;
|
|
k_sem_give(cmd(buf)->sync);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_cmd_complete *evt;
|
|
uint8_t status, ncmd;
|
|
uint16_t opcode;
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
ncmd = evt->ncmd;
|
|
opcode = sys_le16_to_cpu(evt->opcode);
|
|
|
|
BT_DBG("opcode 0x%04x", opcode);
|
|
|
|
/* All command return parameters have a 1-byte status in the
|
|
* beginning, so we can safely make this generalization.
|
|
*/
|
|
status = buf->data[0];
|
|
|
|
hci_cmd_done(opcode, status, buf);
|
|
|
|
/* Allow next command to be sent */
|
|
if (ncmd) {
|
|
k_sem_give(&bt_dev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_status(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_cmd_status *evt;
|
|
uint16_t opcode;
|
|
uint8_t ncmd;
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
opcode = sys_le16_to_cpu(evt->opcode);
|
|
ncmd = evt->ncmd;
|
|
|
|
BT_DBG("opcode 0x%04x", opcode);
|
|
|
|
hci_cmd_done(opcode, evt->status, buf);
|
|
|
|
/* Allow next command to be sent */
|
|
if (ncmd) {
|
|
k_sem_give(&bt_dev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
static bool is_adv_using_rand_addr(void)
|
|
{
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
/* When advertising using extended advertising HCI commands
|
|
* then the advertiser has it's own random address command.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
return adv && atomic_test_bit(adv->flags, BT_ADV_ENABLED);
|
|
}
|
|
|
|
static int le_scan_set_random_addr(bool active_scan, uint8_t *own_addr_type)
|
|
{
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY)) {
|
|
err = le_set_private_addr(BT_ID_DEFAULT);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) {
|
|
*own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM;
|
|
} else {
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
}
|
|
} else {
|
|
*own_addr_type = bt_dev.id_addr[0].type;
|
|
|
|
/* Use NRPA unless identity has been explicitly requested
|
|
* (through Kconfig).
|
|
* Use same RPA as legacy advertiser if advertising.
|
|
*/
|
|
if (!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) &&
|
|
!is_adv_using_rand_addr()) {
|
|
err = le_set_private_addr(BT_ID_DEFAULT);
|
|
if (err) {
|
|
if (active_scan || !is_adv_using_rand_addr()) {
|
|
return err;
|
|
} else {
|
|
BT_WARN("Ignoring failure to set "
|
|
"address for passive scan (%d)",
|
|
err);
|
|
}
|
|
}
|
|
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
} else if (IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) &&
|
|
*own_addr_type == BT_ADDR_LE_RANDOM) {
|
|
/* If scanning with Identity Address we must set the
|
|
* random identity address for both active and passive
|
|
* scanner in order to receive adv reports that are
|
|
* directed towards this identity.
|
|
*/
|
|
err = set_random_address(&bt_dev.id_addr[0].a);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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) && !rpa_is_new()) {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID);
|
|
}
|
|
}
|
|
|
|
err = le_scan_set_random_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 = le_scan_set_random_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 = set_le_scan_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 = set_le_scan_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)
|
|
pending_id_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 */
|
|
#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;
|
|
|
|
if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) {
|
|
conn = bt_conn_lookup_state_le(adv->id, BT_ADDR_LE_NONE,
|
|
BT_CONN_CONNECT_ADV);
|
|
} else {
|
|
conn = bt_conn_lookup_state_le(adv->id, &adv->target_addr,
|
|
BT_CONN_CONNECT_DIR_ADV);
|
|
}
|
|
|
|
if (conn) {
|
|
conn->err = status;
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(conn);
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
#if defined(CONFIG_BT_BROADCASTER)
|
|
static void le_adv_set_terminated(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_adv_set_terminated *evt;
|
|
struct bt_le_ext_adv *adv;
|
|
uint16_t conn_handle;
|
|
|
|
evt = (void *)buf->data;
|
|
adv = bt_adv_lookup_handle(evt->adv_handle);
|
|
conn_handle = sys_le16_to_cpu(evt->conn_handle);
|
|
|
|
BT_DBG("status 0x%02x adv_handle %u conn_handle 0x%02x num %u",
|
|
evt->status, evt->adv_handle, conn_handle,
|
|
evt->num_completed_ext_adv_evts);
|
|
|
|
if (!adv) {
|
|
BT_ERR("No valid adv");
|
|
return;
|
|
}
|
|
|
|
atomic_clear_bit(adv->flags, BT_ADV_ENABLED);
|
|
|
|
if (evt->status && IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
/* Only set status for legacy advertising API.
|
|
* This will call connected callback for high duty cycle
|
|
* directed advertiser timeout.
|
|
*/
|
|
le_adv_stop_free_conn(adv, adv == bt_dev.adv ? evt->status : 0);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN) && !evt->status) {
|
|
struct bt_conn *conn = bt_conn_lookup_handle(conn_handle);
|
|
|
|
if (conn) {
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
/* Set Responder address unless already set */
|
|
conn->le.resp_addr.type = BT_ADDR_LE_RANDOM;
|
|
if (bt_addr_cmp(&conn->le.resp_addr.a,
|
|
BT_ADDR_ANY) == 0) {
|
|
bt_addr_copy(&conn->le.resp_addr.a,
|
|
&adv->random_addr.a);
|
|
}
|
|
} else {
|
|
bt_addr_le_copy(&conn->le.resp_addr,
|
|
&bt_dev.id_addr[conn->id]);
|
|
}
|
|
|
|
if (adv->cb && adv->cb->connected) {
|
|
struct bt_le_ext_adv_connected_info info = {
|
|
.conn = conn,
|
|
};
|
|
|
|
adv->cb->connected(adv, &info);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
}
|
|
|
|
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_LIMITED)) {
|
|
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
pending_id_keys_update();
|
|
#endif
|
|
|
|
if (adv->cb && adv->cb->sent) {
|
|
struct bt_le_ext_adv_sent_info info = {
|
|
.num_sent = evt->num_completed_ext_adv_evts,
|
|
};
|
|
|
|
adv->cb->sent(adv, &info);
|
|
}
|
|
}
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_PERSIST) && adv == bt_dev.adv) {
|
|
adv_delete_legacy();
|
|
}
|
|
}
|
|
|
|
static void le_scan_req_received(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_scan_req_received *evt;
|
|
struct bt_le_ext_adv *adv;
|
|
|
|
evt = (void *)buf->data;
|
|
adv = bt_adv_lookup_handle(evt->handle);
|
|
|
|
BT_DBG("handle %u peer %s", evt->handle, bt_addr_le_str(&evt->addr));
|
|
|
|
if (!adv) {
|
|
BT_ERR("No valid adv");
|
|
return;
|
|
}
|
|
|
|
if (adv->cb && adv->cb->scanned) {
|
|
struct bt_le_ext_adv_scanned_info info;
|
|
bt_addr_le_t id_addr;
|
|
|
|
if (evt->addr.type == BT_ADDR_LE_PUBLIC_ID ||
|
|
evt->addr.type == BT_ADDR_LE_RANDOM_ID) {
|
|
bt_addr_le_copy(&id_addr, &evt->addr);
|
|
id_addr.type -= BT_ADDR_LE_PUBLIC_ID;
|
|
} else {
|
|
bt_addr_le_copy(&id_addr,
|
|
bt_lookup_id_addr(adv->id, &evt->addr));
|
|
}
|
|
|
|
info.addr = &id_addr;
|
|
adv->cb->scanned(adv, &info);
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_BROADCASTER) */
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
|
|
int bt_hci_get_conn_handle(const struct bt_conn *conn, uint16_t *conn_handle)
|
|
{
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
*conn_handle = conn->handle;
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EVT_USER)
|
|
int bt_hci_register_vnd_evt_cb(bt_hci_vnd_evt_cb_t cb)
|
|
{
|
|
hci_vnd_evt_cb = cb;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_HCI_VS_EVT_USER */
|
|
|
|
static void hci_vendor_event(struct net_buf *buf)
|
|
{
|
|
bool handled = false;
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EVT_USER)
|
|
if (hci_vnd_evt_cb) {
|
|
struct net_buf_simple_state state;
|
|
|
|
net_buf_simple_save(&buf->b, &state);
|
|
|
|
handled = hci_vnd_evt_cb(&buf->b);
|
|
|
|
net_buf_simple_restore(&buf->b, &state);
|
|
}
|
|
#endif /* CONFIG_BT_HCI_VS_EVT_USER */
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT) && !handled) {
|
|
/* do nothing at present time */
|
|
BT_WARN("Unhandled vendor-specific event: %s",
|
|
bt_hex(buf->data, buf->len));
|
|
}
|
|
}
|
|
|
|
static const struct event_handler meta_events[] = {
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_ADVERTISING_REPORT, le_adv_report,
|
|
sizeof(struct bt_hci_evt_le_advertising_report)),
|
|
#endif /* CONFIG_BT_OBSERVER */
|
|
#if defined(CONFIG_BT_CONN)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_CONN_COMPLETE, le_legacy_conn_complete,
|
|
sizeof(struct bt_hci_evt_le_conn_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_ENH_CONN_COMPLETE, le_enh_conn_complete,
|
|
sizeof(struct bt_hci_evt_le_enh_conn_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE,
|
|
le_conn_update_complete,
|
|
sizeof(struct bt_hci_evt_le_conn_update_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_REMOTE_FEAT_COMPLETE,
|
|
le_remote_feat_complete,
|
|
sizeof(struct bt_hci_evt_le_remote_feat_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_CONN_PARAM_REQ, le_conn_param_req,
|
|
sizeof(struct bt_hci_evt_le_conn_param_req)),
|
|
#if defined(CONFIG_BT_DATA_LEN_UPDATE)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_DATA_LEN_CHANGE, le_data_len_change,
|
|
sizeof(struct bt_hci_evt_le_data_len_change)),
|
|
#endif /* CONFIG_BT_DATA_LEN_UPDATE */
|
|
#if defined(CONFIG_BT_PHY_UPDATE)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE,
|
|
le_phy_update_complete,
|
|
sizeof(struct bt_hci_evt_le_phy_update_complete)),
|
|
#endif /* CONFIG_BT_PHY_UPDATE */
|
|
#endif /* CONFIG_BT_CONN */
|
|
#if defined(CONFIG_BT_SMP)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_LTK_REQUEST, le_ltk_request,
|
|
sizeof(struct bt_hci_evt_le_ltk_request)),
|
|
#endif /* CONFIG_BT_SMP */
|
|
#if defined(CONFIG_BT_ECC)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE, le_pkey_complete,
|
|
sizeof(struct bt_hci_evt_le_p256_public_key_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE, le_dhkey_complete,
|
|
sizeof(struct bt_hci_evt_le_generate_dhkey_complete)),
|
|
#endif /* CONFIG_BT_SMP */
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
#if defined(CONFIG_BT_BROADCASTER)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_ADV_SET_TERMINATED, le_adv_set_terminated,
|
|
sizeof(struct bt_hci_evt_le_adv_set_terminated)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_SCAN_REQ_RECEIVED, le_scan_req_received,
|
|
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,
|
|
0),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT, 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,
|
|
sizeof(struct bt_hci_evt_le_per_adv_sync_established)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT, 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,
|
|
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,
|
|
sizeof(struct bt_hci_evt_le_past_received)),
|
|
#endif /* CONFIG_BT_CONN */
|
|
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
#if defined(CONFIG_BT_ISO)
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_estabilished,
|
|
sizeof(struct bt_hci_evt_le_cis_established)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_CIS_REQ, hci_le_cis_req,
|
|
sizeof(struct bt_hci_evt_le_cis_req)),
|
|
#endif /* (CONFIG_BT_ISO) */
|
|
};
|
|
|
|
static void hci_le_meta_event(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_meta_event *evt;
|
|
|
|
evt = net_buf_pull_mem(buf, sizeof(*evt));
|
|
|
|
BT_DBG("subevent 0x%02x", evt->subevent);
|
|
|
|
handle_event(evt->subevent, buf, meta_events, ARRAY_SIZE(meta_events));
|
|
}
|
|
|
|
static const struct event_handler normal_events[] = {
|
|
EVENT_HANDLER(BT_HCI_EVT_VENDOR, hci_vendor_event,
|
|
sizeof(struct bt_hci_evt_vs)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LE_META_EVENT, hci_le_meta_event,
|
|
sizeof(struct bt_hci_evt_le_meta_event)),
|
|
#if defined(CONFIG_BT_BREDR)
|
|
EVENT_HANDLER(BT_HCI_EVT_CONN_REQUEST, conn_req,
|
|
sizeof(struct bt_hci_evt_conn_request)),
|
|
EVENT_HANDLER(BT_HCI_EVT_CONN_COMPLETE, conn_complete,
|
|
sizeof(struct bt_hci_evt_conn_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, hci_evt_pin_code_req,
|
|
sizeof(struct bt_hci_evt_pin_code_req)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, hci_evt_link_key_notify,
|
|
sizeof(struct bt_hci_evt_link_key_notify)),
|
|
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, hci_evt_link_key_req,
|
|
sizeof(struct bt_hci_evt_link_key_req)),
|
|
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, hci_evt_io_capa_resp,
|
|
sizeof(struct bt_hci_evt_io_capa_resp)),
|
|
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, hci_evt_io_capa_req,
|
|
sizeof(struct bt_hci_evt_io_capa_req)),
|
|
EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, hci_evt_ssp_complete,
|
|
sizeof(struct bt_hci_evt_ssp_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, hci_evt_user_confirm_req,
|
|
sizeof(struct bt_hci_evt_user_confirm_req)),
|
|
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY,
|
|
hci_evt_user_passkey_notify,
|
|
sizeof(struct bt_hci_evt_user_passkey_notify)),
|
|
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, hci_evt_user_passkey_req,
|
|
sizeof(struct bt_hci_evt_user_passkey_req)),
|
|
EVENT_HANDLER(BT_HCI_EVT_INQUIRY_COMPLETE, inquiry_complete,
|
|
sizeof(struct bt_hci_evt_inquiry_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
|
|
inquiry_result_with_rssi,
|
|
sizeof(struct bt_hci_evt_inquiry_result_with_rssi)),
|
|
EVENT_HANDLER(BT_HCI_EVT_EXTENDED_INQUIRY_RESULT,
|
|
extended_inquiry_result,
|
|
sizeof(struct bt_hci_evt_extended_inquiry_result)),
|
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE,
|
|
remote_name_request_complete,
|
|
sizeof(struct bt_hci_evt_remote_name_req_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, hci_evt_auth_complete,
|
|
sizeof(struct bt_hci_evt_auth_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_FEATURES,
|
|
read_remote_features_complete,
|
|
sizeof(struct bt_hci_evt_remote_features)),
|
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_EXT_FEATURES,
|
|
read_remote_ext_features_complete,
|
|
sizeof(struct bt_hci_evt_remote_ext_features)),
|
|
EVENT_HANDLER(BT_HCI_EVT_ROLE_CHANGE, role_change,
|
|
sizeof(struct bt_hci_evt_role_change)),
|
|
EVENT_HANDLER(BT_HCI_EVT_SYNC_CONN_COMPLETE, synchronous_conn_complete,
|
|
sizeof(struct bt_hci_evt_sync_conn_complete)),
|
|
#endif /* CONFIG_BT_BREDR */
|
|
#if defined(CONFIG_BT_CONN)
|
|
EVENT_HANDLER(BT_HCI_EVT_DISCONN_COMPLETE, hci_disconn_complete,
|
|
sizeof(struct bt_hci_evt_disconn_complete)),
|
|
#endif /* CONFIG_BT_CONN */
|
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
|
EVENT_HANDLER(BT_HCI_EVT_ENCRYPT_CHANGE, hci_encrypt_change,
|
|
sizeof(struct bt_hci_evt_encrypt_change)),
|
|
EVENT_HANDLER(BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE,
|
|
hci_encrypt_key_refresh_complete,
|
|
sizeof(struct bt_hci_evt_encrypt_key_refresh_complete)),
|
|
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
|
#if defined(CONFIG_BT_REMOTE_VERSION)
|
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_VERSION_INFO,
|
|
bt_hci_evt_read_remote_version_complete,
|
|
sizeof(struct bt_hci_evt_remote_version_info)),
|
|
#endif /* CONFIG_BT_REMOTE_VERSION */
|
|
EVENT_HANDLER(BT_HCI_EVT_HARDWARE_ERROR, hci_hardware_error,
|
|
sizeof(struct bt_hci_evt_hardware_error)),
|
|
};
|
|
|
|
static void hci_event(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_hdr *hdr;
|
|
|
|
BT_ASSERT(buf->len >= sizeof(*hdr));
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
|
BT_DBG("event 0x%02x", hdr->evt);
|
|
BT_ASSERT(bt_hci_evt_get_flags(hdr->evt) & BT_HCI_EVT_FLAG_RECV);
|
|
|
|
handle_event(hdr->evt, buf, normal_events, ARRAY_SIZE(normal_events));
|
|
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
static void send_cmd(void)
|
|
{
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
/* Get next command */
|
|
BT_DBG("calling net_buf_get");
|
|
buf = net_buf_get(&bt_dev.cmd_tx_queue, K_NO_WAIT);
|
|
BT_ASSERT(buf);
|
|
|
|
/* Wait until ncmd > 0 */
|
|
BT_DBG("calling sem_take_wait");
|
|
k_sem_take(&bt_dev.ncmd_sem, K_FOREVER);
|
|
|
|
/* Clear out any existing sent command */
|
|
if (bt_dev.sent_cmd) {
|
|
BT_ERR("Uncleared pending sent_cmd");
|
|
net_buf_unref(bt_dev.sent_cmd);
|
|
bt_dev.sent_cmd = NULL;
|
|
}
|
|
|
|
bt_dev.sent_cmd = net_buf_ref(buf);
|
|
|
|
BT_DBG("Sending command 0x%04x (buf %p) to driver",
|
|
cmd(buf)->opcode, buf);
|
|
|
|
err = bt_send(buf);
|
|
if (err) {
|
|
BT_ERR("Unable to send to driver (err %d)", err);
|
|
k_sem_give(&bt_dev.ncmd_sem);
|
|
hci_cmd_done(cmd(buf)->opcode, BT_HCI_ERR_UNSPECIFIED, buf);
|
|
net_buf_unref(bt_dev.sent_cmd);
|
|
bt_dev.sent_cmd = NULL;
|
|
net_buf_unref(buf);
|
|
}
|
|
}
|
|
|
|
static void process_events(struct k_poll_event *ev, int count)
|
|
{
|
|
BT_DBG("count %d", count);
|
|
|
|
for (; count; ev++, count--) {
|
|
BT_DBG("ev->state %u", ev->state);
|
|
|
|
switch (ev->state) {
|
|
case K_POLL_STATE_SIGNALED:
|
|
break;
|
|
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
|
|
if (ev->tag == BT_EVENT_CMD_TX) {
|
|
send_cmd();
|
|
} else if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
struct bt_conn *conn;
|
|
|
|
if (ev->tag == BT_EVENT_CONN_TX_QUEUE) {
|
|
conn = CONTAINER_OF(ev->fifo,
|
|
struct bt_conn,
|
|
tx_queue);
|
|
bt_conn_process_tx(conn);
|
|
}
|
|
}
|
|
break;
|
|
case K_POLL_STATE_NOT_READY:
|
|
break;
|
|
default:
|
|
BT_WARN("Unexpected k_poll event state %u", ev->state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CONN)
|
|
#if defined(CONFIG_BT_ISO)
|
|
/* command FIFO + conn_change signal + MAX_CONN + MAX_ISO_CONN */
|
|
#define EV_COUNT (2 + CONFIG_BT_MAX_CONN + CONFIG_BT_MAX_ISO_CONN)
|
|
#else
|
|
/* command FIFO + conn_change signal + MAX_CONN */
|
|
#define EV_COUNT (2 + CONFIG_BT_MAX_CONN)
|
|
#endif /* CONFIG_BT_ISO */
|
|
#else
|
|
/* command FIFO */
|
|
#define EV_COUNT 1
|
|
#endif /* CONFIG_BT_CONN */
|
|
|
|
static void hci_tx_thread(void *p1, void *p2, void *p3)
|
|
{
|
|
static struct k_poll_event events[EV_COUNT] = {
|
|
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
|
|
K_POLL_MODE_NOTIFY_ONLY,
|
|
&bt_dev.cmd_tx_queue,
|
|
BT_EVENT_CMD_TX),
|
|
};
|
|
|
|
BT_DBG("Started");
|
|
|
|
while (1) {
|
|
int ev_count, err;
|
|
|
|
events[0].state = K_POLL_STATE_NOT_READY;
|
|
ev_count = 1;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
ev_count += bt_conn_prepare_events(&events[1]);
|
|
}
|
|
|
|
BT_DBG("Calling k_poll with %d events", ev_count);
|
|
|
|
err = k_poll(events, ev_count, K_FOREVER);
|
|
BT_ASSERT(err == 0);
|
|
|
|
process_events(events, ev_count);
|
|
|
|
/* Make sure we don't hog the CPU if there's all the time
|
|
* some ready events.
|
|
*/
|
|
k_yield();
|
|
}
|
|
}
|
|
|
|
|
|
static void read_local_ver_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
bt_dev.hci_version = rp->hci_version;
|
|
bt_dev.hci_revision = sys_le16_to_cpu(rp->hci_revision);
|
|
bt_dev.lmp_version = rp->lmp_version;
|
|
bt_dev.lmp_subversion = sys_le16_to_cpu(rp->lmp_subversion);
|
|
bt_dev.manufacturer = sys_le16_to_cpu(rp->manufacturer);
|
|
}
|
|
|
|
static void read_le_features_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_le_read_local_features *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
memcpy(bt_dev.le.features, rp->features, sizeof(bt_dev.le.features));
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static void read_buffer_size_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data;
|
|
uint16_t pkts;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
bt_dev.br.mtu = sys_le16_to_cpu(rp->acl_max_len);
|
|
pkts = sys_le16_to_cpu(rp->acl_max_num);
|
|
|
|
BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.br.mtu);
|
|
|
|
k_sem_init(&bt_dev.br.pkts, pkts, pkts);
|
|
}
|
|
#elif defined(CONFIG_BT_CONN)
|
|
static void read_buffer_size_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data;
|
|
uint16_t pkts;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
/* If LE-side has buffers we can ignore the BR/EDR values */
|
|
if (bt_dev.le.acl_mtu) {
|
|
return;
|
|
}
|
|
|
|
bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->acl_max_len);
|
|
pkts = sys_le16_to_cpu(rp->acl_max_num);
|
|
|
|
BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.le.acl_mtu);
|
|
|
|
k_sem_init(&bt_dev.le.acl_pkts, pkts, pkts);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_CONN)
|
|
static void le_read_buffer_size_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_le_read_buffer_size *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->le_max_len);
|
|
if (!bt_dev.le.acl_mtu) {
|
|
return;
|
|
}
|
|
|
|
BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->le_max_num,
|
|
bt_dev.le.acl_mtu);
|
|
|
|
k_sem_init(&bt_dev.le.acl_pkts, rp->le_max_num, rp->le_max_num);
|
|
}
|
|
|
|
static void read_buffer_size_v2_complete(struct net_buf *buf)
|
|
{
|
|
#if defined(CONFIG_BT_ISO)
|
|
struct bt_hci_rp_le_read_buffer_size_v2 *rp = (void *)buf->data;
|
|
uint8_t max_num;
|
|
|
|
BT_DBG("status %u", rp->status);
|
|
|
|
bt_dev.le.acl_mtu = sys_le16_to_cpu(rp->acl_mtu);
|
|
if (!bt_dev.le.acl_mtu) {
|
|
return;
|
|
}
|
|
|
|
BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->acl_max_pkt,
|
|
bt_dev.le.acl_mtu);
|
|
|
|
max_num = MIN(rp->acl_max_pkt, CONFIG_BT_CONN_TX_MAX);
|
|
k_sem_init(&bt_dev.le.acl_pkts, max_num, max_num);
|
|
|
|
bt_dev.le.iso_mtu = sys_le16_to_cpu(rp->iso_mtu);
|
|
if (!bt_dev.le.iso_mtu) {
|
|
BT_ERR("ISO buffer size not set");
|
|
return;
|
|
}
|
|
|
|
BT_DBG("ISO buffers: pkts %u mtu %u", rp->iso_max_pkt,
|
|
bt_dev.le.iso_mtu);
|
|
|
|
max_num = MIN(rp->iso_max_pkt, CONFIG_BT_ISO_TX_BUF_COUNT);
|
|
k_sem_init(&bt_dev.le.iso_pkts, max_num, max_num);
|
|
#endif /* CONFIG_BT_ISO */
|
|
}
|
|
|
|
static int le_set_host_feature(uint8_t bit_number, uint8_t bit_value)
|
|
{
|
|
#if defined(CONFIG_BT_ISO)
|
|
struct bt_hci_cp_le_set_host_feature *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_HOST_FEATURE, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->bit_number = bit_number;
|
|
cp->bit_value = bit_value;
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_HOST_FEATURE, buf, NULL);
|
|
#else
|
|
return -ENOTSUP;
|
|
#endif /* CONFIG_BT_ISO */
|
|
}
|
|
|
|
#endif /* CONFIG_BT_CONN */
|
|
|
|
static void read_supported_commands_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_supported_commands *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
memcpy(bt_dev.supported_commands, rp->commands,
|
|
sizeof(bt_dev.supported_commands));
|
|
|
|
/*
|
|
* Report "LE Read Local P-256 Public Key" and "LE Generate DH Key" as
|
|
* supported if TinyCrypt ECC is used for emulation.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
|
|
bt_dev.supported_commands[34] |= 0x02;
|
|
bt_dev.supported_commands[34] |= 0x04;
|
|
}
|
|
}
|
|
|
|
static void read_local_features_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
memcpy(bt_dev.features[0], rp->features, sizeof(bt_dev.features[0]));
|
|
}
|
|
|
|
static void le_read_supp_states_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_le_read_supp_states *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status 0x%02x", rp->status);
|
|
|
|
bt_dev.le.states = sys_get_le64(rp->le_states);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
static void le_read_resolving_list_size_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_le_read_rl_size *rp = (void *)buf->data;
|
|
|
|
BT_DBG("Resolving List size %u", rp->rl_size);
|
|
|
|
bt_dev.le.rl_size = rp->rl_size;
|
|
}
|
|
#endif /* defined(CONFIG_BT_SMP) */
|
|
|
|
static int common_init(void)
|
|
{
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
if (!(bt_dev.drv->quirks & BT_QUIRK_NO_RESET)) {
|
|
/* Send HCI_RESET */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
hci_reset_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
}
|
|
|
|
/* Read Local Supported Features */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
read_local_features_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
|
|
/* Read Local Version Information */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
read_local_ver_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
|
|
/* Read Local Supported Commands */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_SUPPORTED_COMMANDS, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
read_supported_commands_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HOST_CRYPTO)) {
|
|
/* Initialize the PRNG so that it is safe to use it later
|
|
* on in the initialization process.
|
|
*/
|
|
err = prng_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
|
|
err = set_flow_control();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int le_set_event_mask(void)
|
|
{
|
|
struct bt_hci_cp_le_set_event_mask *cp_mask;
|
|
struct net_buf *buf;
|
|
uint64_t mask = 0U;
|
|
|
|
/* Set LE event mask */
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EVENT_MASK, sizeof(*cp_mask));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp_mask = net_buf_add(buf, sizeof(*cp_mask));
|
|
|
|
mask |= BT_EVT_MASK_LE_ADVERTISING_REPORT;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_ADV_SET_TERMINATED;
|
|
mask |= BT_EVT_MASK_LE_SCAN_REQ_RECEIVED;
|
|
mask |= BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT;
|
|
mask |= BT_EVT_MASK_LE_SCAN_TIMEOUT;
|
|
if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC)) {
|
|
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED;
|
|
mask |= BT_EVT_MASK_LE_PER_ADVERTISING_REPORT;
|
|
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_LOST;
|
|
mask |= BT_EVT_MASK_LE_PAST_RECEIVED;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
if ((IS_ENABLED(CONFIG_BT_SMP) &&
|
|
BT_FEAT_LE_PRIVACY(bt_dev.le.features)) ||
|
|
(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
/* C24:
|
|
* Mandatory if the LE Controller supports Connection
|
|
* State and either LE Feature (LL Privacy) or
|
|
* LE Feature (Extended Advertising) is supported, ...
|
|
*/
|
|
mask |= BT_EVT_MASK_LE_ENH_CONN_COMPLETE;
|
|
} else {
|
|
mask |= BT_EVT_MASK_LE_CONN_COMPLETE;
|
|
}
|
|
|
|
mask |= BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE;
|
|
mask |= BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE;
|
|
|
|
if (BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_CONN_PARAM_REQ;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) &&
|
|
BT_FEAT_LE_DLE(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_DATA_LEN_CHANGE;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PHY_UPDATE) &&
|
|
(BT_FEAT_LE_PHY_2M(bt_dev.le.features) ||
|
|
BT_FEAT_LE_PHY_CODED(bt_dev.le.features))) {
|
|
mask |= BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP) &&
|
|
BT_FEAT_LE_ENCR(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_LTK_REQUEST;
|
|
}
|
|
|
|
/*
|
|
* If "LE Read Local P-256 Public Key" and "LE Generate DH Key" are
|
|
* supported we need to enable events generated by those commands.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_ECC) &&
|
|
(BT_CMD_TEST(bt_dev.supported_commands, 34, 1)) &&
|
|
(BT_CMD_TEST(bt_dev.supported_commands, 34, 2))) {
|
|
mask |= BT_EVT_MASK_LE_P256_PUBLIC_KEY_COMPLETE;
|
|
mask |= BT_EVT_MASK_LE_GENERATE_DHKEY_COMPLETE;
|
|
}
|
|
|
|
/*
|
|
* Enable CIS events only if ISO connections are enabled and controller
|
|
* support them.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_ISO) &&
|
|
BT_FEAT_LE_CIS(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_CIS_ESTABLISHED;
|
|
if (BT_FEAT_LE_CIS_SLAVE(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_LE_CIS_REQ;
|
|
}
|
|
}
|
|
|
|
sys_put_le64(mask, cp_mask->events);
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL);
|
|
}
|
|
|
|
static int le_init(void)
|
|
{
|
|
struct bt_hci_cp_write_le_host_supp *cp_le;
|
|
struct net_buf *buf, *rsp;
|
|
int err;
|
|
|
|
/* For now we only support LE capable controllers */
|
|
if (!BT_FEAT_LE(bt_dev.features)) {
|
|
BT_ERR("Non-LE capable controller detected!");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Read Low Energy Supported Features */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
read_le_features_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
|
|
#if defined(CONFIG_BT_CONN)
|
|
if (IS_ENABLED(CONFIG_BT_ISO) &&
|
|
BT_FEAT_LE_ISO(bt_dev.le.features)) {
|
|
/* Set Isochronus Channels - Host support */
|
|
err = le_set_host_feature(BT_LE_FEAT_BIT_ISO_CHANNELS, 1);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
/* Read ISO Buffer Size V2 */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE_V2,
|
|
NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
read_buffer_size_v2_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
} else {
|
|
/* Read LE Buffer Size */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE,
|
|
NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
le_read_buffer_size_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
}
|
|
#endif /* CONFIG_BT_CONN */
|
|
|
|
if (BT_FEAT_BREDR(bt_dev.features)) {
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
|
|
sizeof(*cp_le));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp_le = net_buf_add(buf, sizeof(*cp_le));
|
|
|
|
/* Explicitly enable LE for dual-mode controllers */
|
|
cp_le->le = 0x01;
|
|
cp_le->simul = 0x00;
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf,
|
|
NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Read LE Supported States */
|
|
if (BT_CMD_LE_STATES(bt_dev.supported_commands)) {
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_SUPP_STATES, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
le_read_supp_states_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN) &&
|
|
IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) &&
|
|
IS_ENABLED(CONFIG_BT_AUTO_DATA_LEN_UPDATE) &&
|
|
BT_FEAT_LE_DLE(bt_dev.le.features)) {
|
|
struct bt_hci_cp_le_write_default_data_len *cp;
|
|
uint16_t tx_octets, tx_time;
|
|
|
|
err = hci_le_read_max_data_len(&tx_octets, &tx_time);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->max_tx_octets = sys_cpu_to_le16(tx_octets);
|
|
cp->max_tx_time = sys_cpu_to_le16(tx_time);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN,
|
|
buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) {
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
struct bt_hci_cp_le_set_rpa_timeout *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_RPA_TIMEOUT,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->rpa_timeout = sys_cpu_to_le16(CONFIG_BT_RPA_TIMEOUT);
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_RPA_TIMEOUT, buf,
|
|
NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif /* defined(CONFIG_BT_PRIVACY) */
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_RL_SIZE, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
le_read_resolving_list_size_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_BT_DF)
|
|
if (BT_FEAT_LE_CONNECTIONLESS_CTE_TX(bt_dev.le.features)) {
|
|
err = le_df_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_DF */
|
|
|
|
return le_set_event_mask();
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static int read_ext_features(void)
|
|
{
|
|
int i;
|
|
|
|
/* Read Local Supported Extended Features */
|
|
for (i = 1; i < LMP_FEAT_PAGES_COUNT; i++) {
|
|
struct bt_hci_cp_read_local_ext_features *cp;
|
|
struct bt_hci_rp_read_local_ext_features *rp;
|
|
struct net_buf *buf, *rsp;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->page = i;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
|
|
buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
|
|
memcpy(&bt_dev.features[i], rp->ext_features,
|
|
sizeof(bt_dev.features[i]));
|
|
|
|
if (rp->max_page <= i) {
|
|
net_buf_unref(rsp);
|
|
break;
|
|
}
|
|
|
|
net_buf_unref(rsp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void device_supported_pkt_type(void)
|
|
{
|
|
/* Device supported features and sco packet types */
|
|
if (BT_FEAT_HV2_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV2);
|
|
}
|
|
|
|
if (BT_FEAT_HV3_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV3);
|
|
}
|
|
|
|
if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV3);
|
|
}
|
|
|
|
if (BT_FEAT_EV4_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV4);
|
|
}
|
|
|
|
if (BT_FEAT_EV5_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV5);
|
|
}
|
|
|
|
if (BT_FEAT_2EV3_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV3);
|
|
}
|
|
|
|
if (BT_FEAT_3EV3_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_3EV3);
|
|
}
|
|
|
|
if (BT_FEAT_3SLOT_PKT(bt_dev.features)) {
|
|
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV5 |
|
|
HCI_PKT_TYPE_ESCO_3EV5);
|
|
}
|
|
}
|
|
|
|
static int br_init(void)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cp_write_ssp_mode *ssp_cp;
|
|
struct bt_hci_cp_write_inquiry_mode *inq_cp;
|
|
struct bt_hci_write_local_name *name_cp;
|
|
int err;
|
|
|
|
/* Read extended local features */
|
|
if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
|
|
err = read_ext_features();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Add local supported packet types to bt_dev */
|
|
device_supported_pkt_type();
|
|
|
|
/* Get BR/EDR buffer size */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &buf);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
read_buffer_size_complete(buf);
|
|
net_buf_unref(buf);
|
|
|
|
/* Set SSP mode */
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SSP_MODE, sizeof(*ssp_cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
ssp_cp = net_buf_add(buf, sizeof(*ssp_cp));
|
|
ssp_cp->mode = 0x01;
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SSP_MODE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Enable Inquiry results with RSSI or extended Inquiry */
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_INQUIRY_MODE, sizeof(*inq_cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
inq_cp = net_buf_add(buf, sizeof(*inq_cp));
|
|
inq_cp->mode = 0x02;
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_INQUIRY_MODE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Set local name */
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
name_cp = net_buf_add(buf, sizeof(*name_cp));
|
|
strncpy((char *)name_cp->local_name, CONFIG_BT_DEVICE_NAME,
|
|
sizeof(name_cp->local_name));
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Set page timeout*/
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_PAGE_TIMEOUT, sizeof(uint16_t));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_le16(buf, CONFIG_BT_PAGE_TIMEOUT);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_PAGE_TIMEOUT, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Enable BR/EDR SC if supported */
|
|
if (BT_FEAT_SC(bt_dev.features)) {
|
|
struct bt_hci_cp_write_sc_host_supp *sc_cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP,
|
|
sizeof(*sc_cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
sc_cp = net_buf_add(buf, sizeof(*sc_cp));
|
|
sc_cp->sc_support = 0x01;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf,
|
|
NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int br_init(void)
|
|
{
|
|
#if defined(CONFIG_BT_CONN)
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
if (bt_dev.le.acl_mtu) {
|
|
return 0;
|
|
}
|
|
|
|
/* Use BR/EDR buffer size if LE reports zero buffers */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
read_buffer_size_complete(rsp);
|
|
net_buf_unref(rsp);
|
|
#endif /* CONFIG_BT_CONN */
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int set_event_mask(void)
|
|
{
|
|
struct bt_hci_cp_set_event_mask *ev;
|
|
struct net_buf *buf;
|
|
uint64_t mask = 0U;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
ev = net_buf_add(buf, sizeof(*ev));
|
|
|
|
if (IS_ENABLED(CONFIG_BT_BREDR)) {
|
|
/* Since we require LE support, we can count on a
|
|
* Bluetooth 4.0 feature set
|
|
*/
|
|
mask |= BT_EVT_MASK_INQUIRY_COMPLETE;
|
|
mask |= BT_EVT_MASK_CONN_COMPLETE;
|
|
mask |= BT_EVT_MASK_CONN_REQUEST;
|
|
mask |= BT_EVT_MASK_AUTH_COMPLETE;
|
|
mask |= BT_EVT_MASK_REMOTE_NAME_REQ_COMPLETE;
|
|
mask |= BT_EVT_MASK_REMOTE_FEATURES;
|
|
mask |= BT_EVT_MASK_ROLE_CHANGE;
|
|
mask |= BT_EVT_MASK_PIN_CODE_REQ;
|
|
mask |= BT_EVT_MASK_LINK_KEY_REQ;
|
|
mask |= BT_EVT_MASK_LINK_KEY_NOTIFY;
|
|
mask |= BT_EVT_MASK_INQUIRY_RESULT_WITH_RSSI;
|
|
mask |= BT_EVT_MASK_REMOTE_EXT_FEATURES;
|
|
mask |= BT_EVT_MASK_SYNC_CONN_COMPLETE;
|
|
mask |= BT_EVT_MASK_EXTENDED_INQUIRY_RESULT;
|
|
mask |= BT_EVT_MASK_IO_CAPA_REQ;
|
|
mask |= BT_EVT_MASK_IO_CAPA_RESP;
|
|
mask |= BT_EVT_MASK_USER_CONFIRM_REQ;
|
|
mask |= BT_EVT_MASK_USER_PASSKEY_REQ;
|
|
mask |= BT_EVT_MASK_SSP_COMPLETE;
|
|
mask |= BT_EVT_MASK_USER_PASSKEY_NOTIFY;
|
|
}
|
|
|
|
mask |= BT_EVT_MASK_HARDWARE_ERROR;
|
|
mask |= BT_EVT_MASK_DATA_BUFFER_OVERFLOW;
|
|
mask |= BT_EVT_MASK_LE_META_EVENT;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
mask |= BT_EVT_MASK_DISCONN_COMPLETE;
|
|
mask |= BT_EVT_MASK_REMOTE_VERSION_INFO;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP) &&
|
|
BT_FEAT_LE_ENCR(bt_dev.le.features)) {
|
|
mask |= BT_EVT_MASK_ENCRYPT_CHANGE;
|
|
mask |= BT_EVT_MASK_ENCRYPT_KEY_REFRESH_COMPLETE;
|
|
}
|
|
|
|
sys_put_le64(mask, ev->events);
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL);
|
|
}
|
|
|
|
static uint8_t bt_read_public_addr(bt_addr_le_t *addr)
|
|
{
|
|
struct bt_hci_rp_read_bd_addr *rp;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
/* Read Bluetooth Address */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp);
|
|
if (err) {
|
|
BT_WARN("Failed to read public address");
|
|
return 0U;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
|
|
if (!bt_addr_cmp(&rp->bdaddr, BT_ADDR_ANY) ||
|
|
!bt_addr_cmp(&rp->bdaddr, BT_ADDR_NONE)) {
|
|
BT_DBG("Controller has no public address");
|
|
net_buf_unref(rsp);
|
|
return 0U;
|
|
}
|
|
|
|
bt_addr_copy(&addr->a, &rp->bdaddr);
|
|
addr->type = BT_ADDR_LE_PUBLIC;
|
|
|
|
net_buf_unref(rsp);
|
|
return 1U;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_DEBUG)
|
|
static const char *ver_str(uint8_t ver)
|
|
{
|
|
const char * const str[] = {
|
|
"1.0b", "1.1", "1.2", "2.0", "2.1", "3.0", "4.0", "4.1", "4.2",
|
|
"5.0", "5.1", "5.2"
|
|
};
|
|
|
|
if (ver < ARRAY_SIZE(str)) {
|
|
return str[ver];
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
static void bt_dev_show_info(void)
|
|
{
|
|
int i;
|
|
|
|
BT_INFO("Identity%s: %s", bt_dev.id_count > 1 ? "[0]" : "",
|
|
bt_addr_le_str(&bt_dev.id_addr[0]));
|
|
|
|
for (i = 1; i < bt_dev.id_count; i++) {
|
|
BT_INFO("Identity[%d]: %s",
|
|
i, bt_addr_le_str(&bt_dev.id_addr[i]));
|
|
}
|
|
|
|
BT_INFO("HCI: version %s (0x%02x) revision 0x%04x, manufacturer 0x%04x",
|
|
ver_str(bt_dev.hci_version), bt_dev.hci_version,
|
|
bt_dev.hci_revision, bt_dev.manufacturer);
|
|
BT_INFO("LMP: version %s (0x%02x) subver 0x%04x",
|
|
ver_str(bt_dev.lmp_version), bt_dev.lmp_version,
|
|
bt_dev.lmp_subversion);
|
|
}
|
|
#else
|
|
static inline void bt_dev_show_info(void)
|
|
{
|
|
}
|
|
#endif /* CONFIG_BT_DEBUG */
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EXT)
|
|
#if defined(CONFIG_BT_DEBUG)
|
|
static const char *vs_hw_platform(uint16_t platform)
|
|
{
|
|
static const char * const plat_str[] = {
|
|
"reserved", "Intel Corporation", "Nordic Semiconductor",
|
|
"NXP Semiconductors" };
|
|
|
|
if (platform < ARRAY_SIZE(plat_str)) {
|
|
return plat_str[platform];
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
static const char *vs_hw_variant(uint16_t platform, uint16_t variant)
|
|
{
|
|
static const char * const nordic_str[] = {
|
|
"reserved", "nRF51x", "nRF52x", "nRF53x"
|
|
};
|
|
|
|
if (platform != BT_HCI_VS_HW_PLAT_NORDIC) {
|
|
return "unknown";
|
|
}
|
|
|
|
if (variant < ARRAY_SIZE(nordic_str)) {
|
|
return nordic_str[variant];
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
static const char *vs_fw_variant(uint8_t variant)
|
|
{
|
|
static const char * const var_str[] = {
|
|
"Standard Bluetooth controller",
|
|
"Vendor specific controller",
|
|
"Firmware loader",
|
|
"Rescue image",
|
|
};
|
|
|
|
if (variant < ARRAY_SIZE(var_str)) {
|
|
return var_str[variant];
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
#endif /* CONFIG_BT_DEBUG */
|
|
|
|
static void hci_vs_init(void)
|
|
{
|
|
union {
|
|
struct bt_hci_rp_vs_read_version_info *info;
|
|
struct bt_hci_rp_vs_read_supported_commands *cmds;
|
|
struct bt_hci_rp_vs_read_supported_features *feat;
|
|
} rp;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
/* If heuristics is enabled, try to guess HCI VS support by looking
|
|
* at the HCI version and identity address. We haven't set any addresses
|
|
* at this point. So we need to read the public address.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT)) {
|
|
bt_addr_le_t addr;
|
|
|
|
if ((bt_dev.hci_version < BT_HCI_VERSION_5_0) ||
|
|
bt_read_public_addr(&addr)) {
|
|
BT_WARN("Controller doesn't seem to support "
|
|
"Zephyr vendor HCI");
|
|
return;
|
|
}
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_VERSION_INFO, NULL, &rsp);
|
|
if (err) {
|
|
BT_WARN("Vendor HCI extensions not available");
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len != sizeof(struct bt_hci_rp_vs_read_version_info)) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_DEBUG)
|
|
rp.info = (void *)rsp->data;
|
|
BT_INFO("HW Platform: %s (0x%04x)",
|
|
vs_hw_platform(sys_le16_to_cpu(rp.info->hw_platform)),
|
|
sys_le16_to_cpu(rp.info->hw_platform));
|
|
BT_INFO("HW Variant: %s (0x%04x)",
|
|
vs_hw_variant(sys_le16_to_cpu(rp.info->hw_platform),
|
|
sys_le16_to_cpu(rp.info->hw_variant)),
|
|
sys_le16_to_cpu(rp.info->hw_variant));
|
|
BT_INFO("Firmware: %s (0x%02x) Version %u.%u Build %u",
|
|
vs_fw_variant(rp.info->fw_variant), rp.info->fw_variant,
|
|
rp.info->fw_version, sys_le16_to_cpu(rp.info->fw_revision),
|
|
sys_le32_to_cpu(rp.info->fw_build));
|
|
#endif /* CONFIG_BT_DEBUG */
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_SUPPORTED_COMMANDS,
|
|
NULL, &rsp);
|
|
if (err) {
|
|
BT_WARN("Failed to read supported vendor commands");
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len != sizeof(struct bt_hci_rp_vs_read_supported_commands)) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return;
|
|
}
|
|
|
|
rp.cmds = (void *)rsp->data;
|
|
memcpy(bt_dev.vs_commands, rp.cmds->commands, BT_DEV_VS_CMDS_MAX);
|
|
net_buf_unref(rsp);
|
|
|
|
if (BT_VS_CMD_SUP_FEAT(bt_dev.vs_commands)) {
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_SUPPORTED_FEATURES,
|
|
NULL, &rsp);
|
|
if (err) {
|
|
BT_WARN("Failed to read supported vendor features");
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len !=
|
|
sizeof(struct bt_hci_rp_vs_read_supported_features)) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return;
|
|
}
|
|
|
|
rp.feat = (void *)rsp->data;
|
|
memcpy(bt_dev.vs_features, rp.feat->features,
|
|
BT_DEV_VS_FEAT_MAX);
|
|
net_buf_unref(rsp);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_HCI_VS_EXT */
|
|
|
|
static int hci_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = common_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = le_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (BT_FEAT_BREDR(bt_dev.features)) {
|
|
err = br_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else if (IS_ENABLED(CONFIG_BT_BREDR)) {
|
|
BT_ERR("Non-BR/EDR controller detected");
|
|
return -EIO;
|
|
}
|
|
|
|
err = set_event_mask();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EXT)
|
|
hci_vs_init();
|
|
#endif
|
|
if (!IS_ENABLED(CONFIG_BT_SETTINGS) && !bt_dev.id_count) {
|
|
BT_DBG("No user identity. Trying to set public.");
|
|
|
|
bt_setup_public_id_addr();
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_SETTINGS) && !bt_dev.id_count) {
|
|
BT_DBG("No public address. Trying to set static random.");
|
|
|
|
err = bt_setup_random_id_addr();
|
|
if (err) {
|
|
BT_ERR("Unable to set identity address");
|
|
return err;
|
|
}
|
|
|
|
/* The passive scanner just sends a dummy address type in the
|
|
* command. If the first activity does this, and the dummy type
|
|
* is a random address, it needs a valid value, even though it's
|
|
* not actually used.
|
|
*/
|
|
err = set_random_address(&bt_dev.id_addr[0].a);
|
|
if (err) {
|
|
BT_ERR("Unable to set random address");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_send(struct net_buf *buf)
|
|
{
|
|
BT_DBG("buf %p len %u type %u", buf, buf->len, bt_buf_get_type(buf));
|
|
|
|
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
|
|
return bt_hci_ecc_send(buf);
|
|
}
|
|
|
|
return bt_dev.drv->send(buf);
|
|
}
|
|
|
|
static const struct event_handler prio_events[] = {
|
|
EVENT_HANDLER(BT_HCI_EVT_CMD_COMPLETE, hci_cmd_complete,
|
|
sizeof(struct bt_hci_evt_cmd_complete)),
|
|
EVENT_HANDLER(BT_HCI_EVT_CMD_STATUS, hci_cmd_status,
|
|
sizeof(struct bt_hci_evt_cmd_status)),
|
|
#if defined(CONFIG_BT_CONN)
|
|
EVENT_HANDLER(BT_HCI_EVT_DATA_BUF_OVERFLOW,
|
|
hci_data_buf_overflow,
|
|
sizeof(struct bt_hci_evt_data_buf_overflow)),
|
|
EVENT_HANDLER(BT_HCI_EVT_NUM_COMPLETED_PACKETS,
|
|
hci_num_completed_packets,
|
|
sizeof(struct bt_hci_evt_num_completed_packets)),
|
|
EVENT_HANDLER(BT_HCI_EVT_DISCONN_COMPLETE, hci_disconn_complete_prio,
|
|
sizeof(struct bt_hci_evt_disconn_complete)),
|
|
|
|
#endif /* CONFIG_BT_CONN */
|
|
};
|
|
|
|
void hci_event_prio(struct net_buf *buf)
|
|
{
|
|
struct net_buf_simple_state state;
|
|
struct bt_hci_evt_hdr *hdr;
|
|
uint8_t evt_flags;
|
|
|
|
net_buf_simple_save(&buf->b, &state);
|
|
|
|
BT_ASSERT(buf->len >= sizeof(*hdr));
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
|
evt_flags = bt_hci_evt_get_flags(hdr->evt);
|
|
BT_ASSERT(evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO);
|
|
|
|
handle_event(hdr->evt, buf, prio_events, ARRAY_SIZE(prio_events));
|
|
|
|
if (evt_flags & BT_HCI_EVT_FLAG_RECV) {
|
|
net_buf_simple_restore(&buf->b, &state);
|
|
} else {
|
|
net_buf_unref(buf);
|
|
}
|
|
}
|
|
|
|
int bt_recv(struct net_buf *buf)
|
|
{
|
|
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
|
|
|
BT_DBG("buf %p len %u", buf, buf->len);
|
|
|
|
switch (bt_buf_get_type(buf)) {
|
|
#if defined(CONFIG_BT_CONN)
|
|
case BT_BUF_ACL_IN:
|
|
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
hci_acl(buf);
|
|
#else
|
|
net_buf_put(&bt_dev.rx_queue, buf);
|
|
#endif
|
|
return 0;
|
|
#endif /* BT_CONN */
|
|
case BT_BUF_EVT:
|
|
{
|
|
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
hci_event(buf);
|
|
#else
|
|
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
|
|
uint8_t evt_flags = bt_hci_evt_get_flags(hdr->evt);
|
|
|
|
if (evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO) {
|
|
hci_event_prio(buf);
|
|
}
|
|
|
|
if (evt_flags & BT_HCI_EVT_FLAG_RECV) {
|
|
net_buf_put(&bt_dev.rx_queue, buf);
|
|
}
|
|
#endif
|
|
return 0;
|
|
|
|
}
|
|
#if defined(CONFIG_BT_ISO)
|
|
case BT_BUF_ISO_IN:
|
|
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
hci_iso(buf);
|
|
#else
|
|
net_buf_put(&bt_dev.rx_queue, buf);
|
|
#endif
|
|
return 0;
|
|
#endif /* CONFIG_BT_ISO */
|
|
default:
|
|
BT_ERR("Invalid buf type %u", bt_buf_get_type(buf));
|
|
net_buf_unref(buf);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
int bt_recv_prio(struct net_buf *buf)
|
|
{
|
|
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
|
|
|
BT_ASSERT(bt_buf_get_type(buf) == BT_BUF_EVT);
|
|
|
|
hci_event_prio(buf);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_BT_RECV_IS_RX_THREAD) */
|
|
|
|
int bt_hci_driver_register(const struct bt_hci_driver *drv)
|
|
{
|
|
if (bt_dev.drv) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (!drv->open || !drv->send) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_dev.drv = drv;
|
|
|
|
BT_DBG("Registered %s", drv->name ? drv->name : "");
|
|
|
|
bt_monitor_new_index(BT_MONITOR_TYPE_PRIMARY, drv->bus,
|
|
BT_ADDR_ANY, drv->name ? drv->name : "bt0");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_finalize_init(void)
|
|
{
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_READY);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_OBSERVER)) {
|
|
bt_le_scan_update(false);
|
|
}
|
|
|
|
bt_dev_show_info();
|
|
}
|
|
|
|
static int bt_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = hci_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
err = bt_conn_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
k_delayed_work_init(&bt_dev.rpa_update, rpa_timeout);
|
|
#endif
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
if (!bt_dev.id_count) {
|
|
BT_INFO("No ID address. App must call settings_load()");
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_PRESET_ID);
|
|
}
|
|
|
|
bt_finalize_init();
|
|
return 0;
|
|
}
|
|
|
|
static void init_work(struct k_work *work)
|
|
{
|
|
int err;
|
|
|
|
err = bt_init();
|
|
if (ready_cb) {
|
|
ready_cb(err);
|
|
}
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
static void hci_rx_thread(void)
|
|
{
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("started");
|
|
|
|
while (1) {
|
|
BT_DBG("calling fifo_get_wait");
|
|
buf = net_buf_get(&bt_dev.rx_queue, K_FOREVER);
|
|
|
|
BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf),
|
|
buf->len);
|
|
|
|
switch (bt_buf_get_type(buf)) {
|
|
#if defined(CONFIG_BT_CONN)
|
|
case BT_BUF_ACL_IN:
|
|
hci_acl(buf);
|
|
break;
|
|
#endif /* CONFIG_BT_CONN */
|
|
#if defined(CONFIG_BT_ISO)
|
|
case BT_BUF_ISO_IN:
|
|
hci_iso(buf);
|
|
break;
|
|
#endif /* CONFIG_BT_ISO */
|
|
case BT_BUF_EVT:
|
|
hci_event(buf);
|
|
break;
|
|
default:
|
|
BT_ERR("Unknown buf type %u", bt_buf_get_type(buf));
|
|
net_buf_unref(buf);
|
|
break;
|
|
}
|
|
|
|
/* Make sure we don't hog the CPU if the rx_queue never
|
|
* gets empty.
|
|
*/
|
|
k_yield();
|
|
}
|
|
}
|
|
#endif /* !CONFIG_BT_RECV_IS_RX_THREAD */
|
|
|
|
int bt_enable(bt_ready_cb_t cb)
|
|
{
|
|
int err;
|
|
|
|
if (!bt_dev.drv) {
|
|
BT_ERR("No HCI driver registered");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_ENABLE)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
err = bt_settings_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else {
|
|
bt_set_name(CONFIG_BT_DEVICE_NAME);
|
|
}
|
|
|
|
ready_cb = cb;
|
|
|
|
/* TX thread */
|
|
k_thread_create(&tx_thread_data, tx_thread_stack,
|
|
K_KERNEL_STACK_SIZEOF(tx_thread_stack),
|
|
hci_tx_thread, NULL, NULL, NULL,
|
|
K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO),
|
|
0, K_NO_WAIT);
|
|
k_thread_name_set(&tx_thread_data, "BT TX");
|
|
|
|
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
|
/* RX thread */
|
|
k_thread_create(&rx_thread_data, rx_thread_stack,
|
|
K_KERNEL_STACK_SIZEOF(rx_thread_stack),
|
|
(k_thread_entry_t)hci_rx_thread, NULL, NULL, NULL,
|
|
K_PRIO_COOP(CONFIG_BT_RX_PRIO),
|
|
0, K_NO_WAIT);
|
|
k_thread_name_set(&rx_thread_data, "BT RX");
|
|
#endif
|
|
|
|
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
|
|
bt_hci_ecc_init();
|
|
}
|
|
|
|
err = bt_dev.drv->open();
|
|
if (err) {
|
|
BT_ERR("HCI driver open failed (%d)", err);
|
|
return err;
|
|
}
|
|
|
|
bt_monitor_send(BT_MONITOR_OPEN_INDEX, NULL, 0);
|
|
|
|
if (!cb) {
|
|
return bt_init();
|
|
}
|
|
|
|
k_work_submit(&bt_dev.init);
|
|
return 0;
|
|
}
|
|
|
|
struct bt_ad {
|
|
const struct bt_data *data;
|
|
size_t len;
|
|
};
|
|
|
|
static int set_data_add(uint8_t *set_data, uint8_t set_data_len_max,
|
|
const struct bt_ad *ad, size_t ad_len, uint8_t *data_len)
|
|
{
|
|
uint8_t set_data_len = 0;
|
|
|
|
for (size_t i = 0; i < ad_len; i++) {
|
|
const struct bt_data *data = ad[i].data;
|
|
|
|
for (size_t j = 0; j < ad[i].len; j++) {
|
|
size_t len = data[j].data_len;
|
|
uint8_t type = data[j].type;
|
|
|
|
/* Check if ad fit in the remaining buffer */
|
|
if ((set_data_len + len + 2) > set_data_len_max) {
|
|
ssize_t shortened_len = set_data_len_max -
|
|
(set_data_len + 2);
|
|
|
|
if (!(type == BT_DATA_NAME_COMPLETE &&
|
|
shortened_len > 0)) {
|
|
BT_ERR("Too big advertising data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
type = BT_DATA_NAME_SHORTENED;
|
|
len = shortened_len;
|
|
}
|
|
|
|
set_data[set_data_len++] = len + 1;
|
|
set_data[set_data_len++] = type;
|
|
|
|
memcpy(&set_data[set_data_len], data[j].data, len);
|
|
set_data_len += len;
|
|
}
|
|
}
|
|
|
|
*data_len = set_data_len;
|
|
return 0;
|
|
}
|
|
|
|
static int hci_set_ad(uint16_t hci_op, const struct bt_ad *ad, size_t ad_len)
|
|
{
|
|
struct bt_hci_cp_le_set_adv_data *set_data;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(hci_op, sizeof(*set_data));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_data = net_buf_add(buf, sizeof(*set_data));
|
|
(void)memset(set_data, 0, sizeof(*set_data));
|
|
|
|
err = set_data_add(set_data->data, BT_GAP_ADV_MAX_ADV_DATA_LEN,
|
|
ad, ad_len, &set_data->len);
|
|
if (err) {
|
|
net_buf_unref(buf);
|
|
return err;
|
|
}
|
|
|
|
return bt_hci_cmd_send_sync(hci_op, buf, NULL);
|
|
}
|
|
|
|
/* Set legacy data using Extended Advertising HCI commands */
|
|
static int hci_set_ad_ext(struct bt_le_ext_adv *adv, uint16_t hci_op,
|
|
const struct bt_ad *ad, size_t ad_len)
|
|
{
|
|
struct bt_hci_cp_le_set_ext_adv_data *set_data;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(hci_op, sizeof(*set_data));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_data = net_buf_add(buf, sizeof(*set_data));
|
|
(void)memset(set_data, 0, sizeof(*set_data));
|
|
|
|
err = set_data_add(set_data->data, BT_HCI_LE_EXT_ADV_FRAG_MAX_LEN,
|
|
ad, ad_len, &set_data->len);
|
|
if (err) {
|
|
net_buf_unref(buf);
|
|
return err;
|
|
}
|
|
|
|
set_data->handle = adv->handle;
|
|
set_data->op = BT_HCI_LE_EXT_ADV_OP_COMPLETE_DATA;
|
|
set_data->frag_pref = BT_HCI_LE_EXT_ADV_FRAG_DISABLED;
|
|
|
|
return bt_hci_cmd_send_sync(hci_op, buf, NULL);
|
|
}
|
|
|
|
static int set_ad(struct bt_le_ext_adv *adv, const struct bt_ad *ad,
|
|
size_t ad_len)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
return hci_set_ad_ext(adv, BT_HCI_OP_LE_SET_EXT_ADV_DATA,
|
|
ad, ad_len);
|
|
}
|
|
|
|
return hci_set_ad(BT_HCI_OP_LE_SET_ADV_DATA, ad, ad_len);
|
|
}
|
|
|
|
static int set_sd(struct bt_le_ext_adv *adv, const struct bt_ad *sd,
|
|
size_t sd_len)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
return hci_set_ad_ext(adv, BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
|
|
sd, sd_len);
|
|
}
|
|
|
|
return hci_set_ad(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, sd, sd_len);
|
|
}
|
|
|
|
int bt_set_name(const char *name)
|
|
{
|
|
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
size_t len = strlen(name);
|
|
int err;
|
|
|
|
if (len > CONFIG_BT_DEVICE_NAME_MAX) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (!strcmp(bt_dev.name, name)) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(bt_dev.name, name, len);
|
|
bt_dev.name[len] = '\0';
|
|
|
|
/* Update advertising name if in use */
|
|
if (adv && atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME)) {
|
|
struct bt_data data[] = { BT_DATA(BT_DATA_NAME_COMPLETE, name,
|
|
len) };
|
|
struct bt_ad sd = { data, ARRAY_SIZE(data) };
|
|
|
|
set_sd(adv, &sd, 1);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
err = settings_save_one("bt/name", bt_dev.name, len);
|
|
if (err) {
|
|
BT_WARN("Unable to store name");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
#else
|
|
return -ENOMEM;
|
|
#endif
|
|
}
|
|
|
|
const char *bt_get_name(void)
|
|
{
|
|
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
|
return bt_dev.name;
|
|
#else
|
|
return CONFIG_BT_DEVICE_NAME;
|
|
#endif
|
|
}
|
|
|
|
int bt_set_id_addr(const bt_addr_le_t *addr)
|
|
{
|
|
bt_addr_le_t non_const_addr;
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
BT_ERR("Setting identity not allowed after bt_enable()");
|
|
return -EBUSY;
|
|
}
|
|
|
|
bt_addr_le_copy(&non_const_addr, addr);
|
|
|
|
return bt_id_create(&non_const_addr, NULL);
|
|
}
|
|
|
|
void bt_id_get(bt_addr_le_t *addrs, size_t *count)
|
|
{
|
|
size_t to_copy = MIN(*count, bt_dev.id_count);
|
|
|
|
memcpy(addrs, bt_dev.id_addr, to_copy * sizeof(bt_addr_le_t));
|
|
*count = to_copy;
|
|
}
|
|
|
|
static int id_find(const bt_addr_le_t *addr)
|
|
{
|
|
uint8_t id;
|
|
|
|
for (id = 0U; id < bt_dev.id_count; id++) {
|
|
if (!bt_addr_le_cmp(addr, &bt_dev.id_addr[id])) {
|
|
return id;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static void id_create(uint8_t id, bt_addr_le_t *addr, uint8_t *irk)
|
|
{
|
|
if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
|
bt_addr_le_copy(&bt_dev.id_addr[id], addr);
|
|
} else {
|
|
bt_addr_le_t new_addr;
|
|
|
|
do {
|
|
bt_addr_le_create_static(&new_addr);
|
|
/* Make sure we didn't generate a duplicate */
|
|
} while (id_find(&new_addr) >= 0);
|
|
|
|
bt_addr_le_copy(&bt_dev.id_addr[id], &new_addr);
|
|
|
|
if (addr) {
|
|
bt_addr_le_copy(addr, &bt_dev.id_addr[id]);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
{
|
|
uint8_t zero_irk[16] = { 0 };
|
|
|
|
if (irk && memcmp(irk, zero_irk, 16)) {
|
|
memcpy(&bt_dev.irk[id], irk, 16);
|
|
} else {
|
|
bt_rand(&bt_dev.irk[id], 16);
|
|
if (irk) {
|
|
memcpy(irk, &bt_dev.irk[id], 16);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/* Only store if stack was already initialized. Before initialization
|
|
* we don't know the flash content, so it's potentially harmful to
|
|
* try to write anything there.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
bt_settings_save_id();
|
|
}
|
|
}
|
|
|
|
int bt_id_create(bt_addr_le_t *addr, uint8_t *irk)
|
|
{
|
|
int new_id;
|
|
|
|
if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
|
if (addr->type != BT_ADDR_LE_RANDOM ||
|
|
!BT_ADDR_IS_STATIC(&addr->a)) {
|
|
BT_ERR("Only static random identity address supported");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (id_find(addr) >= 0) {
|
|
return -EALREADY;
|
|
}
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_PRIVACY) && irk) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (bt_dev.id_count == ARRAY_SIZE(bt_dev.id_addr)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* bt_rand is not available before Bluetooth enable has been called */
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ENABLE)) {
|
|
uint8_t zero_irk[16] = { 0 };
|
|
|
|
if (!(addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!(irk && memcmp(irk, zero_irk, 16))) {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
new_id = bt_dev.id_count++;
|
|
id_create(new_id, addr, irk);
|
|
|
|
return new_id;
|
|
}
|
|
|
|
int bt_id_reset(uint8_t id, bt_addr_le_t *addr, uint8_t *irk)
|
|
{
|
|
struct adv_id_check_data check_data = {
|
|
.id = id,
|
|
.adv_enabled = false,
|
|
};
|
|
|
|
if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
|
if (addr->type != BT_ADDR_LE_RANDOM ||
|
|
!BT_ADDR_IS_STATIC(&addr->a)) {
|
|
BT_ERR("Only static random identity address supported");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (id_find(addr) >= 0) {
|
|
return -EALREADY;
|
|
}
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_PRIVACY) && irk) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (id == BT_ID_DEFAULT || id >= bt_dev.id_count) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_adv_foreach(adv_id_check_func, &check_data);
|
|
if (check_data.adv_enabled) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN) &&
|
|
bt_addr_le_cmp(&bt_dev.id_addr[id], BT_ADDR_LE_ANY)) {
|
|
int err;
|
|
|
|
err = bt_unpair(id, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
id_create(id, addr, irk);
|
|
|
|
return id;
|
|
}
|
|
|
|
int bt_id_delete(uint8_t id)
|
|
{
|
|
struct adv_id_check_data check_data = {
|
|
.id = id,
|
|
.adv_enabled = false,
|
|
};
|
|
|
|
if (id == BT_ID_DEFAULT || id >= bt_dev.id_count) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!bt_addr_le_cmp(&bt_dev.id_addr[id], BT_ADDR_LE_ANY)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
bt_adv_foreach(adv_id_check_func, &check_data);
|
|
if (check_data.adv_enabled) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN)) {
|
|
int err;
|
|
|
|
err = bt_unpair(id, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
(void)memset(bt_dev.irk[id], 0, 16);
|
|
#endif
|
|
bt_addr_le_copy(&bt_dev.id_addr[id], BT_ADDR_LE_ANY);
|
|
|
|
if (id == bt_dev.id_count - 1) {
|
|
bt_dev.id_count--;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
bt_settings_save_id();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
static void bt_read_identity_root(uint8_t *ir)
|
|
{
|
|
/* Invalid IR */
|
|
memset(ir, 0, 16);
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EXT)
|
|
struct bt_hci_rp_vs_read_key_hierarchy_roots *rp;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
if (!BT_VS_CMD_READ_KEY_ROOTS(bt_dev.vs_commands)) {
|
|
return;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_KEY_HIERARCHY_ROOTS, NULL,
|
|
&rsp);
|
|
if (err) {
|
|
BT_WARN("Failed to read identity root");
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len != sizeof(struct bt_hci_rp_vs_read_key_hierarchy_roots)) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
memcpy(ir, rp->ir, 16);
|
|
|
|
net_buf_unref(rsp);
|
|
#endif /* defined(CONFIG_BT_HCI_VS_EXT) */
|
|
}
|
|
#endif /* defined(CONFIG_BT_PRIVACY) */
|
|
|
|
void bt_setup_public_id_addr(void)
|
|
{
|
|
bt_addr_le_t addr;
|
|
uint8_t *irk = NULL;
|
|
|
|
bt_dev.id_count = bt_read_public_addr(&addr);
|
|
|
|
if (!bt_dev.id_count) {
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
uint8_t ir_irk[16];
|
|
uint8_t ir[16];
|
|
|
|
bt_read_identity_root(ir);
|
|
|
|
if (!bt_smp_irk_get(ir, ir_irk)) {
|
|
irk = ir_irk;
|
|
} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_STORE_ID);
|
|
}
|
|
#endif /* defined(CONFIG_BT_PRIVACY) */
|
|
|
|
id_create(BT_ID_DEFAULT, &addr, irk);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_HCI_VS_EXT)
|
|
uint8_t bt_read_static_addr(struct bt_hci_vs_static_addr addrs[], uint8_t size)
|
|
{
|
|
struct bt_hci_rp_vs_read_static_addrs *rp;
|
|
struct net_buf *rsp;
|
|
int err, i;
|
|
uint8_t cnt;
|
|
|
|
if (!BT_VS_CMD_READ_STATIC_ADDRS(bt_dev.vs_commands)) {
|
|
BT_WARN("Read Static Addresses command not available");
|
|
return 0;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_STATIC_ADDRS, NULL, &rsp);
|
|
if (err) {
|
|
BT_WARN("Failed to read static addresses");
|
|
return 0;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len < sizeof(struct bt_hci_rp_vs_read_static_addrs)) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return 0;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
cnt = MIN(rp->num_addrs, size);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) &&
|
|
rsp->len != (sizeof(struct bt_hci_rp_vs_read_static_addrs) +
|
|
rp->num_addrs *
|
|
sizeof(struct bt_hci_vs_static_addr))) {
|
|
BT_WARN("Invalid Vendor HCI extensions");
|
|
net_buf_unref(rsp);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
memcpy(&addrs[i], rp->a, sizeof(struct bt_hci_vs_static_addr));
|
|
}
|
|
|
|
net_buf_unref(rsp);
|
|
if (!cnt) {
|
|
BT_WARN("No static addresses stored in controller");
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
#endif /* CONFIG_BT_HCI_VS_EXT */
|
|
|
|
int bt_setup_random_id_addr(void)
|
|
{
|
|
#if defined(CONFIG_BT_HCI_VS_EXT) || defined(CONFIG_BT_CTLR)
|
|
/* Only read the addresses if the user has not already configured one or
|
|
* more identities (!bt_dev.id_count).
|
|
*/
|
|
if (!bt_dev.id_count) {
|
|
struct bt_hci_vs_static_addr addrs[CONFIG_BT_ID_MAX];
|
|
|
|
bt_dev.id_count = bt_read_static_addr(addrs, CONFIG_BT_ID_MAX);
|
|
|
|
if (bt_dev.id_count) {
|
|
for (uint8_t i = 0; i < bt_dev.id_count; i++) {
|
|
bt_addr_le_t addr;
|
|
uint8_t *irk = NULL;
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
uint8_t ir_irk[16];
|
|
|
|
if (!bt_smp_irk_get(addrs[i].ir, ir_irk)) {
|
|
irk = ir_irk;
|
|
} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
atomic_set_bit(bt_dev.flags,
|
|
BT_DEV_STORE_ID);
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
bt_addr_copy(&addr.a, &addrs[i].bdaddr);
|
|
addr.type = BT_ADDR_LE_RANDOM;
|
|
|
|
id_create(i, &addr, irk);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_HCI_VS_EXT) || defined(CONFIG_BT_CTLR) */
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) && IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_STORE_ID);
|
|
}
|
|
|
|
return bt_id_create(NULL, NULL);
|
|
}
|
|
|
|
bool bt_addr_le_is_bonded(uint8_t id, const bt_addr_le_t *addr)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
struct bt_keys *keys = bt_keys_find_addr(id, addr);
|
|
|
|
/* if there are any keys stored then device is bonded */
|
|
return keys && keys->keys;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PER_ADV)
|
|
int bt_le_per_adv_set_param(struct bt_le_ext_adv *adv,
|
|
const struct bt_le_per_adv_param *param)
|
|
{
|
|
struct bt_hci_cp_le_set_per_adv_param *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_SCANNABLE)) {
|
|
return -EINVAL;
|
|
} else if (atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
return -EINVAL;
|
|
} else if (!atomic_test_bit(adv->flags, BT_ADV_EXT_ADV)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (param->interval_min < 0x0006 ||
|
|
param->interval_max > 0xFFFF ||
|
|
param->interval_min > param->interval_max) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_PARAM, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
cp->handle = adv->handle;
|
|
cp->min_interval = sys_cpu_to_le16(param->interval_min);
|
|
cp->max_interval = sys_cpu_to_le16(param->interval_max);
|
|
|
|
if (param->options & BT_LE_PER_ADV_OPT_USE_TX_POWER) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_TX_POWER;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_PARAM, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(adv->flags, BT_PER_ADV_PARAMS_SET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_per_adv_set_data(const struct bt_le_ext_adv *adv,
|
|
const struct bt_data *ad, size_t ad_len)
|
|
{
|
|
struct bt_hci_cp_le_set_per_adv_data *cp;
|
|
struct net_buf *buf;
|
|
struct bt_ad d = { .data = ad, .len = ad_len };
|
|
int err;
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ad_len || !ad) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ad_len > BT_HCI_LE_PER_ADV_FRAG_MAX_LEN) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_DATA, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
cp->handle = adv->handle;
|
|
|
|
/* TODO: If data is longer than what the controller can manage,
|
|
* split the data. Read size from controller on boot.
|
|
*/
|
|
cp->op = BT_HCI_LE_PER_ADV_OP_COMPLETE_DATA;
|
|
|
|
err = set_data_add(cp->data, BT_HCI_LE_PER_ADV_FRAG_MAX_LEN, &d, 1,
|
|
&cp->len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_DATA, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bt_le_per_adv_enable(struct bt_le_ext_adv *adv, bool enable)
|
|
{
|
|
struct bt_hci_cp_le_set_per_adv_enable *cp;
|
|
struct net_buf *buf;
|
|
struct bt_hci_cmd_state_set state;
|
|
int err;
|
|
|
|
/* TODO: We could setup some default ext adv params if not already set*/
|
|
if (!atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_PER_ADV_ENABLED) == enable) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_ENABLE, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
cp->handle = adv->handle;
|
|
cp->enable = enable ? 1 : 0;
|
|
|
|
bt_hci_cmd_state_set_init(&state, adv->flags,
|
|
BT_PER_ADV_ENABLED, enable);
|
|
cmd(buf)->state = &state;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_per_adv_start(struct bt_le_ext_adv *adv)
|
|
{
|
|
return bt_le_per_adv_enable(adv, true);
|
|
}
|
|
|
|
int bt_le_per_adv_stop(struct bt_le_ext_adv *adv)
|
|
{
|
|
return bt_le_per_adv_enable(adv, false);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CONN)
|
|
int bt_le_per_adv_set_info_transfer(const struct bt_le_ext_adv *adv,
|
|
const struct bt_conn *conn,
|
|
uint16_t service_data)
|
|
{
|
|
struct bt_hci_cp_le_per_adv_set_info_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_SET_INFO_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->adv_handle = adv->handle;
|
|
cp->service_data = sys_cpu_to_le16(service_data);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER, buf,
|
|
NULL);
|
|
}
|
|
#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(&state, per_adv_sync->flags,
|
|
BT_PER_ADV_SYNC_RECV_DISABLED,
|
|
enable);
|
|
cmd(buf)->state = &state;
|
|
|
|
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) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
if (param->peer &&
|
|
!(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
|
|
!(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
/* Cannot do directed non-connectable advertising
|
|
* without extended advertising.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
if (param->peer &&
|
|
(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
|
|
!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
|
|
/* High duty cycle directed connectable advertising
|
|
* shall not be used with Extended Advertising.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
if (!(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
|
|
param->options & (BT_LE_ADV_OPT_EXT_ADV |
|
|
BT_LE_ADV_OPT_NO_2M |
|
|
BT_LE_ADV_OPT_CODED |
|
|
BT_LE_ADV_OPT_ANONYMOUS |
|
|
BT_LE_ADV_OPT_USE_TX_POWER)) {
|
|
/* Extended options require extended advertising. */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
param->peer &&
|
|
(param->options & BT_LE_ADV_OPT_USE_IDENTITY) &&
|
|
(param->options & BT_LE_ADV_OPT_DIR_ADDR_RPA)) {
|
|
/* own addr type used for both RPAs in directed advertising. */
|
|
return false;
|
|
}
|
|
|
|
if (param->id >= bt_dev.id_count ||
|
|
!bt_addr_le_cmp(&bt_dev.id_addr[param->id], BT_ADDR_LE_ANY)) {
|
|
return false;
|
|
}
|
|
|
|
if (!(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
/*
|
|
* BT Core 4.2 [Vol 2, Part E, 7.8.5]
|
|
* The Advertising_Interval_Min and Advertising_Interval_Max
|
|
* shall not be set to less than 0x00A0 (100 ms) if the
|
|
* Advertising_Type is set to ADV_SCAN_IND or ADV_NONCONN_IND.
|
|
*/
|
|
if (bt_dev.hci_version < BT_HCI_VERSION_5_0 &&
|
|
param->interval_min < 0x00a0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((param->options & (BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY |
|
|
BT_LE_ADV_OPT_DIR_ADDR_RPA)) &&
|
|
!param->peer) {
|
|
return false;
|
|
}
|
|
|
|
if ((param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) ||
|
|
!param->peer) {
|
|
if (param->interval_min > param->interval_max ||
|
|
param->interval_min < 0x0020 ||
|
|
param->interval_max > 0x4000) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((param->options & BT_LE_ADV_OPT_DISABLE_CHAN_37) &&
|
|
(param->options & BT_LE_ADV_OPT_DISABLE_CHAN_38) &&
|
|
(param->options & BT_LE_ADV_OPT_DISABLE_CHAN_39)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool valid_adv_param(const struct bt_le_adv_param *param)
|
|
{
|
|
if (param->options & BT_LE_ADV_OPT_EXT_ADV) {
|
|
return false;
|
|
}
|
|
|
|
if (param->peer && !(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
return false;
|
|
}
|
|
|
|
return valid_adv_ext_param(param);
|
|
}
|
|
|
|
static inline bool ad_has_name(const struct bt_data *ad, size_t ad_len)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < ad_len; i++) {
|
|
if (ad[i].type == BT_DATA_NAME_COMPLETE ||
|
|
ad[i].type == BT_DATA_NAME_SHORTENED) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int le_adv_update(struct bt_le_ext_adv *adv,
|
|
const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len,
|
|
bool ext_adv, bool scannable, bool use_name)
|
|
{
|
|
struct bt_ad d[2] = {};
|
|
struct bt_data data;
|
|
size_t d_len;
|
|
int err;
|
|
|
|
if (use_name) {
|
|
const char *name = bt_get_name();
|
|
|
|
if ((ad && ad_has_name(ad, ad_len)) ||
|
|
(sd && ad_has_name(sd, sd_len))) {
|
|
/* Cannot use name if name is already set */
|
|
return -EINVAL;
|
|
}
|
|
|
|
data = (struct bt_data)BT_DATA(
|
|
BT_DATA_NAME_COMPLETE,
|
|
name, strlen(name));
|
|
}
|
|
|
|
if (!(ext_adv && scannable)) {
|
|
d_len = 1;
|
|
d[0].data = ad;
|
|
d[0].len = ad_len;
|
|
|
|
if (use_name && !scannable) {
|
|
d[1].data = &data;
|
|
d[1].len = 1;
|
|
d_len = 2;
|
|
}
|
|
|
|
err = set_ad(adv, d, d_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (scannable) {
|
|
d_len = 1;
|
|
d[0].data = sd;
|
|
d[0].len = sd_len;
|
|
|
|
if (use_name) {
|
|
d[1].data = &data;
|
|
d[1].len = 1;
|
|
d_len = 2;
|
|
}
|
|
|
|
err = set_sd(adv, d, d_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
atomic_set_bit(adv->flags, BT_ADV_DATA_SET);
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len)
|
|
{
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
bool scannable, use_name;
|
|
|
|
if (!adv) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
scannable = atomic_test_bit(adv->flags, BT_ADV_SCANNABLE);
|
|
use_name = atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME);
|
|
|
|
return le_adv_update(adv, ad, ad_len, sd, sd_len, false, scannable,
|
|
use_name);
|
|
}
|
|
|
|
static uint8_t get_filter_policy(uint32_t options)
|
|
{
|
|
if (!IS_ENABLED(CONFIG_BT_WHITELIST)) {
|
|
return BT_LE_ADV_FP_NO_WHITELIST;
|
|
} else if ((options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) &&
|
|
(options & BT_LE_ADV_OPT_FILTER_CONN)) {
|
|
return BT_LE_ADV_FP_WHITELIST_BOTH;
|
|
} else if (options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) {
|
|
return BT_LE_ADV_FP_WHITELIST_SCAN_REQ;
|
|
} else if (options & BT_LE_ADV_OPT_FILTER_CONN) {
|
|
return BT_LE_ADV_FP_WHITELIST_CONN_IND;
|
|
} else {
|
|
return BT_LE_ADV_FP_NO_WHITELIST;
|
|
}
|
|
}
|
|
|
|
static uint8_t get_adv_channel_map(uint32_t options)
|
|
{
|
|
uint8_t channel_map = 0x07;
|
|
|
|
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_37) {
|
|
channel_map &= ~0x01;
|
|
}
|
|
|
|
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_38) {
|
|
channel_map &= ~0x02;
|
|
}
|
|
|
|
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_39) {
|
|
channel_map &= ~0x04;
|
|
}
|
|
|
|
return channel_map;
|
|
}
|
|
|
|
static int le_adv_set_random_addr(struct bt_le_ext_adv *adv, uint32_t options,
|
|
bool dir_adv, uint8_t *own_addr_type)
|
|
{
|
|
const bt_addr_le_t *id_addr;
|
|
int err = 0;
|
|
|
|
/* Set which local identity address we're advertising with */
|
|
id_addr = &bt_dev.id_addr[adv->id];
|
|
|
|
if (options & BT_LE_ADV_OPT_CONNECTABLE) {
|
|
if (dir_adv && (options & BT_LE_ADV_OPT_DIR_ADDR_RPA) &&
|
|
!BT_FEAT_LE_PRIVACY(bt_dev.le.features)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!(options & BT_LE_ADV_OPT_USE_IDENTITY)) {
|
|
err = le_adv_set_private_addr(adv);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (dir_adv && (options & BT_LE_ADV_OPT_DIR_ADDR_RPA)) {
|
|
*own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM;
|
|
} else {
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
}
|
|
} else {
|
|
/*
|
|
* If Static Random address is used as Identity
|
|
* address we need to restore it before advertising
|
|
* is enabled. Otherwise NRPA used for active scan
|
|
* could be used for advertising.
|
|
*/
|
|
if (id_addr->type == BT_ADDR_LE_RANDOM) {
|
|
err = set_adv_random_address(adv, &id_addr->a);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
*own_addr_type = id_addr->type;
|
|
|
|
if (dir_adv && (options & BT_LE_ADV_OPT_DIR_ADDR_RPA)) {
|
|
*own_addr_type |= BT_HCI_OWN_ADDR_RPA_MASK;
|
|
}
|
|
}
|
|
} else {
|
|
if (options & BT_LE_ADV_OPT_USE_IDENTITY) {
|
|
if (id_addr->type == BT_ADDR_LE_RANDOM) {
|
|
err = set_adv_random_address(adv, &id_addr->a);
|
|
}
|
|
|
|
*own_addr_type = id_addr->type;
|
|
} else if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) {
|
|
/* In case advertising set random address is not
|
|
* available we must handle the shared random address
|
|
* problem.
|
|
*/
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
bool scan_enabled = false;
|
|
|
|
/* If active scan with NRPA is ongoing refresh NRPA */
|
|
if (!IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)) {
|
|
scan_enabled = true;
|
|
set_le_scan_enable(false);
|
|
}
|
|
#endif /* defined(CONFIG_BT_OBSERVER) */
|
|
err = le_adv_set_private_addr(adv);
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (scan_enabled) {
|
|
set_le_scan_enable(true);
|
|
}
|
|
#endif /* defined(CONFIG_BT_OBSERVER) */
|
|
} else {
|
|
err = le_adv_set_private_addr(adv);
|
|
*own_addr_type = BT_ADDR_LE_RANDOM;
|
|
}
|
|
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int le_adv_start_add_conn(const struct bt_le_ext_adv *adv,
|
|
struct bt_conn **out_conn)
|
|
{
|
|
struct adv_id_check_data check_data = {
|
|
.id = adv->id,
|
|
.adv_enabled = false
|
|
};
|
|
struct bt_conn *conn;
|
|
|
|
bt_adv_foreach(adv_id_check_connectable_func, &check_data);
|
|
if (check_data.adv_enabled) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
bt_dev.adv_conn_id = adv->id;
|
|
|
|
if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) {
|
|
/* Undirected advertising */
|
|
conn = bt_conn_add_le(adv->id, BT_ADDR_LE_NONE);
|
|
if (!conn) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_ADV);
|
|
*out_conn = conn;
|
|
return 0;
|
|
}
|
|
|
|
if (bt_conn_exists_le(adv->id, &adv->target_addr)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
conn = bt_conn_add_le(adv->id, &adv->target_addr);
|
|
if (!conn) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_DIR_ADV);
|
|
*out_conn = conn;
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_adv_start_legacy(struct bt_le_ext_adv *adv,
|
|
const struct bt_le_adv_param *param,
|
|
const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len)
|
|
{
|
|
struct bt_hci_cp_le_set_adv_param set_param;
|
|
struct bt_conn *conn = NULL;
|
|
struct net_buf *buf;
|
|
bool dir_adv = (param->peer != NULL), scannable;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (!valid_adv_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!bt_le_adv_random_addr_check(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
(void)memset(&set_param, 0, sizeof(set_param));
|
|
|
|
set_param.min_interval = sys_cpu_to_le16(param->interval_min);
|
|
set_param.max_interval = sys_cpu_to_le16(param->interval_max);
|
|
set_param.channel_map = get_adv_channel_map(param->options);
|
|
set_param.filter_policy = get_filter_policy(param->options);
|
|
|
|
if (adv->id != param->id) {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID);
|
|
}
|
|
|
|
adv->id = param->id;
|
|
bt_dev.adv_conn_id = adv->id;
|
|
|
|
err = le_adv_set_random_addr(adv, param->options, dir_adv,
|
|
&set_param.own_addr_type);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (dir_adv) {
|
|
bt_addr_le_copy(&adv->target_addr, param->peer);
|
|
} else {
|
|
bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY);
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_CONNECTABLE) {
|
|
scannable = true;
|
|
|
|
if (dir_adv) {
|
|
if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) {
|
|
set_param.type = BT_HCI_ADV_DIRECT_IND_LOW_DUTY;
|
|
} else {
|
|
set_param.type = BT_HCI_ADV_DIRECT_IND;
|
|
}
|
|
|
|
bt_addr_le_copy(&set_param.direct_addr, param->peer);
|
|
} else {
|
|
set_param.type = BT_HCI_ADV_IND;
|
|
}
|
|
} else {
|
|
scannable = sd || (param->options & BT_LE_ADV_OPT_USE_NAME);
|
|
|
|
set_param.type = scannable ? BT_HCI_ADV_SCAN_IND :
|
|
BT_HCI_ADV_NONCONN_IND;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_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_ADV_PARAM, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (!dir_adv) {
|
|
err = le_adv_update(adv, ad, ad_len, sd, sd_len, false,
|
|
scannable,
|
|
param->options & BT_LE_ADV_OPT_USE_NAME);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
err = le_adv_start_add_conn(adv, &conn);
|
|
if (err) {
|
|
if (err == -ENOMEM && !dir_adv &&
|
|
!(param->options & BT_LE_ADV_OPT_ONE_TIME)) {
|
|
goto set_adv_state;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = set_le_adv_enable(adv, true);
|
|
if (err) {
|
|
BT_ERR("Failed to start advertiser");
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
/* If undirected connectable advertiser we have created a
|
|
* connection object that we don't yet give to the application.
|
|
* Since we don't give the application a reference to manage in
|
|
* this case, we need to release this reference here
|
|
*/
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
set_adv_state:
|
|
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, !dir_adv &&
|
|
!(param->options & BT_LE_ADV_OPT_ONE_TIME));
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_INCLUDE_NAME,
|
|
param->options & BT_LE_ADV_OPT_USE_NAME);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_CONNECTABLE,
|
|
param->options & BT_LE_ADV_OPT_CONNECTABLE);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_SCANNABLE, scannable);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_USE_IDENTITY,
|
|
param->options & BT_LE_ADV_OPT_USE_IDENTITY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int le_ext_adv_param_set(struct bt_le_ext_adv *adv,
|
|
const struct bt_le_adv_param *param,
|
|
bool has_scan_data)
|
|
{
|
|
struct bt_hci_cp_le_set_ext_adv_param *cp;
|
|
bool dir_adv = param->peer != NULL, scannable;
|
|
struct net_buf *buf, *rsp;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
err = le_adv_set_random_addr(adv, param->options, dir_adv,
|
|
&cp->own_addr_type);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (dir_adv) {
|
|
bt_addr_le_copy(&adv->target_addr, param->peer);
|
|
} else {
|
|
bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY);
|
|
}
|
|
|
|
cp->handle = adv->handle;
|
|
sys_put_le24(param->interval_min, cp->prim_min_interval);
|
|
sys_put_le24(param->interval_max, cp->prim_max_interval);
|
|
cp->prim_channel_map = get_adv_channel_map(param->options);
|
|
cp->filter_policy = get_filter_policy(param->options);
|
|
cp->tx_power = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
|
|
|
|
cp->prim_adv_phy = BT_HCI_LE_PHY_1M;
|
|
if (param->options & BT_LE_ADV_OPT_EXT_ADV) {
|
|
if (param->options & BT_LE_ADV_OPT_NO_2M) {
|
|
cp->sec_adv_phy = BT_HCI_LE_PHY_1M;
|
|
} else {
|
|
cp->sec_adv_phy = BT_HCI_LE_PHY_2M;
|
|
}
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_CODED) {
|
|
cp->prim_adv_phy = BT_HCI_LE_PHY_CODED;
|
|
cp->sec_adv_phy = BT_HCI_LE_PHY_CODED;
|
|
}
|
|
|
|
if (!(param->options & BT_LE_ADV_OPT_EXT_ADV)) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_LEGACY;
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_USE_TX_POWER) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_TX_POWER;
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_ANONYMOUS) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_ANON;
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_NOTIFY_SCAN_REQ) {
|
|
cp->scan_req_notify_enable = BT_HCI_LE_ADV_SCAN_REQ_ENABLE;
|
|
}
|
|
|
|
if (param->options & BT_LE_ADV_OPT_CONNECTABLE) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_CONN;
|
|
if (!dir_adv && !(param->options & BT_LE_ADV_OPT_EXT_ADV)) {
|
|
/* When using non-extended adv packets then undirected
|
|
* advertising has to be scannable as well.
|
|
* We didn't require this option to be set before, so
|
|
* it is implicitly set instead in this case.
|
|
*/
|
|
cp->props |= BT_HCI_LE_ADV_PROP_SCAN;
|
|
}
|
|
}
|
|
|
|
if ((param->options & BT_LE_ADV_OPT_SCANNABLE) || has_scan_data) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_SCAN;
|
|
}
|
|
|
|
scannable = !!(cp->props & BT_HCI_LE_ADV_PROP_SCAN);
|
|
|
|
if (dir_adv) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_DIRECT;
|
|
if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
|
|
cp->props |= BT_HCI_LE_ADV_PROP_HI_DC_CONN;
|
|
}
|
|
|
|
bt_addr_le_copy(&cp->peer_addr, param->peer);
|
|
}
|
|
|
|
cp->sid = param->sid;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
struct bt_hci_rp_le_set_ext_adv_param *rp = (void *)rsp->data;
|
|
|
|
adv->tx_power = rp->tx_power;
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
atomic_set_bit(adv->flags, BT_ADV_PARAMS_SET);
|
|
|
|
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_RANDOM_ADDR_PENDING)) {
|
|
err = set_adv_random_address(adv, &adv->random_addr.a);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* Flag only used by bt_le_adv_start API. */
|
|
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, false);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_INCLUDE_NAME,
|
|
param->options & BT_LE_ADV_OPT_USE_NAME);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_CONNECTABLE,
|
|
param->options & BT_LE_ADV_OPT_CONNECTABLE);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_SCANNABLE, scannable);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_USE_IDENTITY,
|
|
param->options & BT_LE_ADV_OPT_USE_IDENTITY);
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_EXT_ADV,
|
|
param->options & BT_LE_ADV_OPT_EXT_ADV);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_adv_start_ext(struct bt_le_ext_adv *adv,
|
|
const struct bt_le_adv_param *param,
|
|
const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len)
|
|
{
|
|
struct bt_le_ext_adv_start_param start_param = {
|
|
.timeout = 0,
|
|
.num_events = 0,
|
|
};
|
|
bool dir_adv = (param->peer != NULL);
|
|
struct bt_conn *conn = NULL;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (!valid_adv_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
adv->id = param->id;
|
|
err = le_ext_adv_param_set(adv, param, sd ||
|
|
(param->options & BT_LE_ADV_OPT_USE_NAME));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (!dir_adv) {
|
|
err = bt_le_ext_adv_set_data(adv, ad, ad_len, sd, sd_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else {
|
|
if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
|
|
start_param.timeout =
|
|
BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT;
|
|
atomic_set_bit(adv->flags, BT_ADV_LIMITED);
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
err = le_adv_start_add_conn(adv, &conn);
|
|
if (err) {
|
|
if (err == -ENOMEM && !dir_adv &&
|
|
!(param->options & BT_LE_ADV_OPT_ONE_TIME)) {
|
|
goto set_adv_state;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = set_le_adv_enable_ext(adv, true, &start_param);
|
|
if (err) {
|
|
BT_ERR("Failed to start advertiser");
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
/* If undirected connectable advertiser we have created a
|
|
* connection object that we don't yet give to the application.
|
|
* Since we don't give the application a reference to manage in
|
|
* this case, we need to release this reference here
|
|
*/
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
set_adv_state:
|
|
/* Flag always set to false by le_ext_adv_param_set */
|
|
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, !dir_adv &&
|
|
!(param->options & BT_LE_ADV_OPT_ONE_TIME));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_adv_start(const struct bt_le_adv_param *param,
|
|
const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len)
|
|
{
|
|
struct bt_le_ext_adv *adv = adv_new_legacy();
|
|
int err;
|
|
|
|
if (!adv) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
err = bt_le_adv_start_ext(adv, param, ad, ad_len, sd, sd_len);
|
|
} else {
|
|
err = bt_le_adv_start_legacy(adv, param, ad, ad_len, sd, sd_len);
|
|
}
|
|
|
|
if (err) {
|
|
adv_delete_legacy();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_le_adv_stop(void)
|
|
{
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
int err;
|
|
|
|
if (!adv) {
|
|
BT_ERR("No valid legacy adv");
|
|
return 0;
|
|
}
|
|
|
|
/* Make sure advertising is not re-enabled later even if it's not
|
|
* currently enabled (i.e. BT_DEV_ADVERTISING is not set).
|
|
*/
|
|
atomic_clear_bit(adv->flags, BT_ADV_PERSIST);
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
/* Legacy advertiser exists, but is not currently advertising.
|
|
* This happens when keep advertising behavior is active but
|
|
* no conn object is available to do connectable advertising.
|
|
*/
|
|
adv_delete_legacy();
|
|
return 0;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
le_adv_stop_free_conn(adv, 0);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
err = set_le_adv_enable_ext(adv, false, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else {
|
|
err = set_le_adv_enable_legacy(adv, false);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
adv_delete_legacy();
|
|
|
|
#if defined(CONFIG_BT_OBSERVER)
|
|
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
|
|
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) &&
|
|
!IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY)) {
|
|
/* If scan is ongoing set back NRPA */
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE);
|
|
le_set_private_addr(BT_ID_DEFAULT);
|
|
set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE);
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_OBSERVER) */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
void bt_le_adv_resume(void)
|
|
{
|
|
struct bt_le_ext_adv *adv = bt_adv_lookup_legacy();
|
|
struct bt_conn *conn;
|
|
bool persist_paused = false;
|
|
int err;
|
|
|
|
if (!adv) {
|
|
BT_DBG("No valid legacy adv");
|
|
return;
|
|
}
|
|
|
|
if (!(atomic_test_bit(adv->flags, BT_ADV_PERSIST) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_ENABLED))) {
|
|
return;
|
|
}
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
return;
|
|
}
|
|
|
|
err = le_adv_start_add_conn(adv, &conn);
|
|
if (err) {
|
|
BT_DBG("Host cannot resume connectable advertising (%d)", err);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("Resuming connectable advertising");
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
le_adv_set_private_addr(adv);
|
|
}
|
|
|
|
err = set_le_adv_enable(adv, true);
|
|
if (err) {
|
|
BT_DBG("Controller cannot resume connectable advertising (%d)",
|
|
err);
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
/* Temporarily clear persist flag to avoid recursion in
|
|
* bt_conn_unref if the flag is still set.
|
|
*/
|
|
persist_paused = atomic_test_and_clear_bit(adv->flags,
|
|
BT_ADV_PERSIST);
|
|
}
|
|
|
|
/* Since we don't give the application a reference to manage in
|
|
* this case, we need to release this reference here.
|
|
*/
|
|
bt_conn_unref(conn);
|
|
if (persist_paused) {
|
|
atomic_set_bit(adv->flags, BT_ADV_PERSIST);
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_BT_PERIPHERAL) */
|
|
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv,
|
|
struct bt_le_ext_adv_info *info)
|
|
{
|
|
info->id = adv->id;
|
|
info->tx_power = adv->tx_power;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_ext_adv_create(const struct bt_le_adv_param *param,
|
|
const struct bt_le_ext_adv_cb *cb,
|
|
struct bt_le_ext_adv **out_adv)
|
|
{
|
|
struct bt_le_ext_adv *adv;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (!valid_adv_ext_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
adv = adv_new();
|
|
if (!adv) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
adv->id = param->id;
|
|
adv->cb = cb;
|
|
|
|
err = le_ext_adv_param_set(adv, param, false);
|
|
if (err) {
|
|
adv_delete(adv);
|
|
return err;
|
|
}
|
|
|
|
*out_adv = adv;
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_ext_adv_update_param(struct bt_le_ext_adv *adv,
|
|
const struct bt_le_adv_param *param)
|
|
{
|
|
if (!valid_adv_ext_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PER_ADV) &&
|
|
atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
|
|
/* If params for per adv has been set, do not allow setting
|
|
* connectable, scanable or use legacy adv
|
|
*/
|
|
if (param->options & BT_LE_ADV_OPT_CONNECTABLE ||
|
|
param->options & BT_LE_ADV_OPT_SCANNABLE ||
|
|
!(param->options & BT_LE_ADV_OPT_EXT_ADV) ||
|
|
param->options & BT_LE_ADV_OPT_ANONYMOUS) {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (param->id != adv->id) {
|
|
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
}
|
|
|
|
return le_ext_adv_param_set(adv, param, false);
|
|
}
|
|
|
|
int bt_le_ext_adv_start(struct bt_le_ext_adv *adv,
|
|
struct bt_le_ext_adv_start_param *param)
|
|
{
|
|
struct bt_conn *conn = NULL;
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
err = le_adv_start_add_conn(adv, &conn);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
atomic_set_bit_to(adv->flags, BT_ADV_LIMITED, param &&
|
|
(param->timeout > 0 || param->num_events > 0));
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
le_adv_set_private_addr(adv);
|
|
}
|
|
} else {
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
le_adv_set_private_addr(adv);
|
|
}
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_DATA_SET)) {
|
|
/* Set the advertiser name */
|
|
bt_le_ext_adv_set_data(adv, NULL, 0, NULL, 0);
|
|
}
|
|
|
|
err = set_le_adv_enable_ext(adv, true, param);
|
|
if (err) {
|
|
BT_ERR("Failed to start advertiser");
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
|
|
/* If undirected connectable advertiser we have created a
|
|
* connection object that we don't yet give to the application.
|
|
* Since we don't give the application a reference to manage in
|
|
* this case, we need to release this reference here
|
|
*/
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_ext_adv_stop(struct bt_le_ext_adv *adv)
|
|
{
|
|
atomic_clear_bit(adv->flags, BT_ADV_PERSIST);
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_LIMITED)) {
|
|
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
pending_id_keys_update();
|
|
#endif
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
|
|
le_adv_stop_free_conn(adv, 0);
|
|
}
|
|
|
|
return set_le_adv_enable_ext(adv, false, NULL);
|
|
}
|
|
|
|
int bt_le_ext_adv_set_data(struct bt_le_ext_adv *adv,
|
|
const struct bt_data *ad, size_t ad_len,
|
|
const struct bt_data *sd, size_t sd_len)
|
|
{
|
|
bool ext_adv, scannable, use_name;
|
|
|
|
ext_adv = atomic_test_bit(adv->flags, BT_ADV_EXT_ADV);
|
|
scannable = atomic_test_bit(adv->flags, BT_ADV_SCANNABLE);
|
|
use_name = atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME);
|
|
|
|
return le_adv_update(adv, ad, ad_len, sd, sd_len, ext_adv, scannable,
|
|
use_name);
|
|
}
|
|
|
|
int bt_le_ext_adv_delete(struct bt_le_ext_adv *adv)
|
|
{
|
|
struct bt_hci_cp_le_remove_adv_set *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (!BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Advertising set should be stopped first */
|
|
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_ADV_SET, sizeof(*cp));
|
|
if (!buf) {
|
|
BT_WARN("No HCI buffers");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = adv->handle;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_ADV_SET, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
adv_delete(adv);
|
|
|
|
return 0;
|
|
}
|
|
#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_le_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 = set_le_scan_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)
|
|
pending_id_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)
|
|
{
|
|
struct bt_hci_cp_le_add_dev_to_wl *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_WL, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_le_copy(&cp->addr, addr);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_WL, buf, NULL);
|
|
if (err) {
|
|
BT_ERR("Failed to add device to whitelist");
|
|
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_whitelist_rem(const bt_addr_le_t *addr)
|
|
{
|
|
struct bt_hci_cp_le_rem_dev_from_wl *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_WL, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_le_copy(&cp->addr, addr);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_WL, buf, NULL);
|
|
if (err) {
|
|
BT_ERR("Failed to remove device from whitelist");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_whitelist_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_WL, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("Failed to clear whitelist");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_BT_WHITELIST) */
|
|
|
|
int bt_le_set_chan_map(uint8_t chan_map[5])
|
|
{
|
|
struct bt_hci_cp_le_set_host_chan_classif *cp;
|
|
struct net_buf *buf;
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_CENTRAL)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (!BT_CMD_TEST(bt_dev.supported_commands, 27, 3)) {
|
|
BT_WARN("Set Host Channel Classification command is "
|
|
"not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
|
|
memcpy(&cp->ch_map[0], &chan_map[0], 4);
|
|
cp->ch_map[4] = chan_map[4] & BIT_MASK(5);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF,
|
|
buf, NULL);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static int br_start_inquiry(const struct bt_br_discovery_param *param)
|
|
{
|
|
const uint8_t iac[3] = { 0x33, 0x8b, 0x9e };
|
|
struct bt_hci_op_inquiry *cp;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_INQUIRY, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->length = param->length;
|
|
cp->num_rsp = 0xff; /* we limit discovery only by time */
|
|
|
|
memcpy(cp->lap, iac, 3);
|
|
if (param->limited) {
|
|
cp->lap[0] = 0x00;
|
|
}
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY, buf, NULL);
|
|
}
|
|
|
|
static bool valid_br_discov_param(const struct bt_br_discovery_param *param,
|
|
size_t num_results)
|
|
{
|
|
if (!num_results || num_results > 255) {
|
|
return false;
|
|
}
|
|
|
|
if (!param->length || param->length > 0x30) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int bt_br_discovery_start(const struct bt_br_discovery_param *param,
|
|
struct bt_br_discovery_result *results, size_t cnt,
|
|
bt_br_discovery_cb_t cb)
|
|
{
|
|
int err;
|
|
|
|
BT_DBG("");
|
|
|
|
if (!valid_br_discov_param(param, cnt)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = br_start_inquiry(param);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_INQUIRY);
|
|
|
|
(void)memset(results, 0, sizeof(*results) * cnt);
|
|
|
|
discovery_cb = cb;
|
|
discovery_results = results;
|
|
discovery_results_size = cnt;
|
|
discovery_results_count = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_br_discovery_stop(void)
|
|
{
|
|
int err;
|
|
int i;
|
|
|
|
BT_DBG("");
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY_CANCEL, NULL, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
for (i = 0; i < discovery_results_count; i++) {
|
|
struct discovery_priv *priv;
|
|
struct bt_hci_cp_remote_name_cancel *cp;
|
|
struct net_buf *buf;
|
|
|
|
priv = (struct discovery_priv *)&discovery_results[i]._priv;
|
|
|
|
if (!priv->resolving) {
|
|
continue;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_CANCEL,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
continue;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, &discovery_results[i].addr);
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_CANCEL, buf, NULL);
|
|
}
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
|
|
|
|
discovery_cb = NULL;
|
|
discovery_results = NULL;
|
|
discovery_results_size = 0;
|
|
discovery_results_count = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_scan_enable(uint8_t scan)
|
|
{
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
BT_DBG("type %u", scan);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SCAN_ENABLE, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_u8(buf, scan);
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SCAN_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit_to(bt_dev.flags, BT_DEV_ISCAN,
|
|
(scan & BT_BREDR_SCAN_INQUIRY));
|
|
atomic_set_bit_to(bt_dev.flags, BT_DEV_PSCAN,
|
|
(scan & BT_BREDR_SCAN_PAGE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_br_set_connectable(bool enable)
|
|
{
|
|
if (enable) {
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
|
|
return -EALREADY;
|
|
} else {
|
|
return write_scan_enable(BT_BREDR_SCAN_PAGE);
|
|
}
|
|
} else {
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
|
|
return -EALREADY;
|
|
} else {
|
|
return write_scan_enable(BT_BREDR_SCAN_DISABLED);
|
|
}
|
|
}
|
|
}
|
|
|
|
int bt_br_set_discoverable(bool enable)
|
|
{
|
|
if (enable) {
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
return write_scan_enable(BT_BREDR_SCAN_INQUIRY |
|
|
BT_BREDR_SCAN_PAGE);
|
|
} else {
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
return write_scan_enable(BT_BREDR_SCAN_PAGE);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
#if defined(CONFIG_BT_ECC)
|
|
int bt_pub_key_gen(struct bt_pub_key_cb *new_cb)
|
|
{
|
|
int err;
|
|
|
|
/*
|
|
* We check for both "LE Read Local P-256 Public Key" and
|
|
* "LE Generate DH Key" support here since both commands are needed for
|
|
* ECC support. If "LE Generate DH Key" is not supported then there
|
|
* is no point in reading local public key.
|
|
*/
|
|
if (!BT_CMD_TEST(bt_dev.supported_commands, 34, 1) ||
|
|
!BT_CMD_TEST(bt_dev.supported_commands, 34, 2)) {
|
|
BT_WARN("ECC HCI commands not available");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
new_cb->_next = pub_key_cb;
|
|
pub_key_cb = new_cb;
|
|
|
|
if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) {
|
|
return 0;
|
|
}
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY);
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_P256_PUBLIC_KEY, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("Sending LE P256 Public Key command failed");
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY);
|
|
pub_key_cb = NULL;
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const uint8_t *bt_pub_key_get(void)
|
|
{
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) {
|
|
return pub_key;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int bt_dh_key_gen(const uint8_t remote_pk[64], bt_dh_key_cb_t cb)
|
|
{
|
|
struct bt_hci_cp_le_generate_dhkey *cp;
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (dh_key_cb == cb) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (dh_key_cb || atomic_test_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) {
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
|
|
dh_key_cb = cb;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_GENERATE_DHKEY, sizeof(*cp));
|
|
if (!buf) {
|
|
dh_key_cb = NULL;
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
memcpy(cp->key, remote_pk, sizeof(cp->key));
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_GENERATE_DHKEY, buf, NULL);
|
|
if (err) {
|
|
dh_key_cb = NULL;
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_ECC */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
int bt_br_oob_get_local(struct bt_br_oob *oob)
|
|
{
|
|
bt_addr_copy(&oob->addr, &bt_dev.id_addr[0].a);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
int bt_le_oob_get_local(uint8_t id, struct bt_le_oob *oob)
|
|
{
|
|
struct bt_le_ext_adv *adv;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (id >= CONFIG_BT_ID_MAX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
adv = bt_adv_lookup_legacy();
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!(adv && adv->id == id &&
|
|
atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY) &&
|
|
bt_dev.id_addr[id].type == BT_ADDR_LE_RANDOM)) {
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING)) {
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, NULL,
|
|
BT_CONN_CONNECT_SCAN);
|
|
if (conn) {
|
|
/* Cannot set new RPA while creating
|
|
* connections.
|
|
*/
|
|
bt_conn_unref(conn);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (adv &&
|
|
atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
|
|
atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY) &&
|
|
(bt_dev.id_addr[id].type == BT_ADDR_LE_RANDOM)) {
|
|
/* Cannot set a new RPA address while advertising with
|
|
* random static identity address for a different
|
|
* identity.
|
|
*/
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_OBSERVER) &&
|
|
id != BT_ID_DEFAULT &&
|
|
(atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) ||
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING))) {
|
|
/* Cannot switch identity of scanner or initiator */
|
|
return -EINVAL;
|
|
}
|
|
|
|
le_rpa_invalidate();
|
|
le_update_private_addr();
|
|
|
|
bt_addr_le_copy(&oob->addr, &bt_dev.random_addr);
|
|
} else {
|
|
bt_addr_le_copy(&oob->addr, &bt_dev.id_addr[id]);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
err = bt_smp_le_oob_generate_sc_data(&oob->le_sc_data);
|
|
if (err && err != -ENOTSUP) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_EXT_ADV)
|
|
int bt_le_ext_adv_oob_get_local(struct bt_le_ext_adv *adv,
|
|
struct bt_le_oob *oob)
|
|
{
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
|
|
/* Don't refresh RPA addresses if the RPA is new.
|
|
* This allows back to back calls to this function or
|
|
* bt_le_oob_get_local to not invalidate the previously set
|
|
* RPAs.
|
|
*/
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_LIMITED) &&
|
|
!rpa_is_new()) {
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
atomic_test_bit(bt_dev.flags, BT_DEV_INITIATING)) {
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_state_le(
|
|
BT_ID_DEFAULT, NULL,
|
|
BT_CONN_CONNECT_SCAN);
|
|
|
|
if (conn) {
|
|
/* Cannot set new RPA while creating
|
|
* connections.
|
|
*/
|
|
bt_conn_unref(conn);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
le_rpa_invalidate();
|
|
le_update_private_addr();
|
|
}
|
|
|
|
bt_addr_le_copy(&oob->addr, &adv->random_addr);
|
|
} else {
|
|
bt_addr_le_copy(&oob->addr, &bt_dev.id_addr[adv->id]);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
|
err = bt_smp_le_oob_generate_sc_data(&oob->le_sc_data);
|
|
if (err && err != -ENOTSUP) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
|
|
|
#if defined(CONFIG_BT_SMP)
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk)
|
|
{
|
|
return bt_smp_le_oob_set_tk(conn, tk);
|
|
}
|
|
#endif /* !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) */
|
|
|
|
#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
int bt_le_oob_set_sc_data(struct bt_conn *conn,
|
|
const struct bt_le_oob_sc_data *oobd_local,
|
|
const struct bt_le_oob_sc_data *oobd_remote)
|
|
{
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return bt_smp_le_oob_set_sc_data(conn, oobd_local, oobd_remote);
|
|
}
|
|
|
|
int bt_le_oob_get_sc_data(struct bt_conn *conn,
|
|
const struct bt_le_oob_sc_data **oobd_local,
|
|
const struct bt_le_oob_sc_data **oobd_remote)
|
|
{
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return bt_smp_le_oob_get_sc_data(conn, oobd_local, oobd_remote);
|
|
}
|
|
#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
|
|
#endif /* defined(CONFIG_BT_SMP) */
|