PRNG needs to be initialized right at the begining so that it is safe to use it from initialization sequence. This fix a race when ECC emulation code was using uninitiliazed PRNG for public key generation. Change-Id: Ia396f964b915368cee39ea6be998ef5774062591 Signed-off-by: Szymon Janc <ext.szymon.janc@tieto.com>
3770 lines
84 KiB
C
3770 lines
84 KiB
C
/* hci_core.c - HCI core Bluetooth handling */
|
|
|
|
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <nanokernel.h>
|
|
#include <arch/cpu.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <atomic.h>
|
|
#include <misc/util.h>
|
|
#include <misc/byteorder.h>
|
|
#include <misc/stack.h>
|
|
|
|
#ifdef CONFIG_MICROKERNEL
|
|
#include <microkernel.h>
|
|
#endif
|
|
|
|
#include <bluetooth/log.h>
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/conn.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/driver.h>
|
|
#include <bluetooth/storage.h>
|
|
|
|
#include <tinycrypt/hmac_prng.h>
|
|
#include <tinycrypt/utils.h>
|
|
|
|
#include "keys.h"
|
|
#include "monitor.h"
|
|
#include "hci_core.h"
|
|
#include "hci_ecc.h"
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
#include "conn_internal.h"
|
|
#include "l2cap_internal.h"
|
|
#include "smp.h"
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
|
|
#undef BT_DBG
|
|
#define BT_DBG(fmt, ...)
|
|
#endif
|
|
|
|
/* Stacks for the fibers */
|
|
static BT_STACK_NOINIT(rx_fiber_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE);
|
|
static BT_STACK_NOINIT(cmd_tx_fiber_stack, 256);
|
|
|
|
struct bt_dev bt_dev;
|
|
|
|
const struct bt_storage *bt_storage;
|
|
|
|
static bt_le_scan_cb_t *scan_dev_found_cb;
|
|
|
|
#if defined(CONFIG_BLUETOOTH_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_BLUETOOTH_BREDR */
|
|
|
|
struct cmd_data {
|
|
/** BT_BUF_CMD */
|
|
uint8_t type;
|
|
|
|
/** The command OpCode that the buffer contains */
|
|
uint16_t opcode;
|
|
|
|
/** Used by bt_hci_cmd_send_sync. Initially contains the waiting
|
|
* semaphore, as the semaphore is given back contains the bt_buf
|
|
* for the return parameters.
|
|
*/
|
|
void *sync;
|
|
|
|
};
|
|
|
|
struct acl_data {
|
|
/** BT_BUF_ACL_IN */
|
|
uint8_t type;
|
|
|
|
/** ACL connection handle */
|
|
uint16_t handle;
|
|
};
|
|
|
|
#define cmd(buf) ((struct cmd_data *)net_buf_user_data(buf))
|
|
#define acl(buf) ((struct acl_data *)net_buf_user_data(buf))
|
|
|
|
/* HCI command buffers */
|
|
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + \
|
|
sizeof(struct bt_hci_cmd_hdr) + \
|
|
CONFIG_BLUETOOTH_MAX_CMD_LEN)
|
|
static struct nano_fifo avail_hci_cmd;
|
|
static NET_BUF_POOL(hci_cmd_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT, CMD_BUF_SIZE,
|
|
&avail_hci_cmd, NULL, sizeof(struct cmd_data));
|
|
|
|
#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS)
|
|
/* HCI event buffers */
|
|
static struct nano_fifo avail_hci_evt;
|
|
static NET_BUF_POOL(hci_evt_pool, CONFIG_BLUETOOTH_HCI_EVT_COUNT,
|
|
BT_BUF_EVT_SIZE, &avail_hci_evt, NULL,
|
|
BT_BUF_USER_DATA_MIN);
|
|
#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */
|
|
|
|
static struct tc_hmac_prng_struct prng;
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN) && defined(CONFIG_BLUETOOTH_HOST_BUFFERS)
|
|
static void report_completed_packet(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;
|
|
|
|
nano_fifo_put(buf->free, buf);
|
|
|
|
/* Do nothing if controller to host flow control is not supported */
|
|
if (!(bt_dev.supported_commands[10] & 0x20)) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static struct nano_fifo avail_acl_in;
|
|
static NET_BUF_POOL(acl_in_pool, CONFIG_BLUETOOTH_ACL_IN_COUNT,
|
|
BT_BUF_ACL_IN_SIZE, &avail_acl_in, report_completed_packet,
|
|
sizeof(struct acl_data));
|
|
#endif /* CONFIG_BLUETOOTH_CONN && CONFIG_BLUETOOTH_HOST_BUFFERS */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_DEBUG)
|
|
const char *bt_addr_str(const bt_addr_t *addr)
|
|
{
|
|
static char bufs[2][18];
|
|
static uint8_t cur;
|
|
char *str;
|
|
|
|
str = bufs[cur++];
|
|
cur %= ARRAY_SIZE(bufs);
|
|
bt_addr_to_str(addr, str, sizeof(bufs[cur]));
|
|
|
|
return str;
|
|
}
|
|
|
|
const char *bt_addr_le_str(const bt_addr_le_t *addr)
|
|
{
|
|
static char bufs[2][27];
|
|
static uint8_t cur;
|
|
char *str;
|
|
|
|
str = bufs[cur++];
|
|
cur %= ARRAY_SIZE(bufs);
|
|
bt_addr_le_to_str(addr, str, sizeof(bufs[cur]));
|
|
|
|
return str;
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_DEBUG */
|
|
|
|
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_get(&avail_hci_cmd, CONFIG_BLUETOOTH_HCI_SEND_RESERVE);
|
|
if (!buf) {
|
|
BT_ERR("Cannot get free buffer");
|
|
return NULL;
|
|
}
|
|
|
|
BT_DBG("buf %p", buf);
|
|
|
|
cmd(buf)->type = BT_BUF_CMD;
|
|
cmd(buf)->opcode = opcode;
|
|
cmd(buf)->sync = 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;
|
|
}
|
|
|
|
nano_fifo_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 nano_sem sync_sem;
|
|
int err;
|
|
|
|
if (!buf) {
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
BT_DBG("opcode 0x%04x len %u", opcode, buf->len);
|
|
|
|
nano_sem_init(&sync_sem);
|
|
cmd(buf)->sync = &sync_sem;
|
|
|
|
nano_fifo_put(&bt_dev.cmd_tx_queue, buf);
|
|
|
|
nano_sem_take(&sync_sem, TICKS_UNLIMITED);
|
|
|
|
/* Indicate failure if we failed to get the return parameters */
|
|
if (!cmd(buf)->sync) {
|
|
err = -EIO;
|
|
} else {
|
|
err = 0;
|
|
}
|
|
|
|
if (rsp) {
|
|
*rsp = cmd(buf)->sync;
|
|
} else if (cmd(buf)->sync) {
|
|
net_buf_unref(cmd(buf)->sync);
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bt_hci_stop_scanning(void)
|
|
{
|
|
struct net_buf *buf, *rsp;
|
|
struct bt_hci_cp_le_set_scan_enable *scan_enable;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
|
|
sizeof(*scan_enable));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
scan_enable = net_buf_add(buf, sizeof(*scan_enable));
|
|
memset(scan_enable, 0, sizeof(*scan_enable));
|
|
scan_enable->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE;
|
|
scan_enable->enable = BT_HCI_LE_SCAN_DISABLE;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Update scan state in case of success (0) status */
|
|
err = rsp->data[0];
|
|
if (!err) {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING);
|
|
}
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
return err;
|
|
}
|
|
|
|
static const bt_addr_le_t *find_id_addr(const bt_addr_le_t *addr)
|
|
{
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_find_irk(addr);
|
|
if (keys) {
|
|
BT_DBG("Identity %s matched RPA %s",
|
|
bt_addr_le_str(&keys->addr), bt_addr_le_str(addr));
|
|
return &keys->addr;
|
|
}
|
|
#endif
|
|
return addr;
|
|
}
|
|
|
|
static int set_advertise_enable(void)
|
|
{
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_ADVERTISING);
|
|
return 0;
|
|
}
|
|
|
|
static int set_advertise_disable(void)
|
|
{
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
|
|
return 0;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING);
|
|
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;
|
|
}
|
|
|
|
memcpy(net_buf_add(buf, sizeof(*addr)), 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;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
static void hci_acl(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_acl_hdr *hdr = (void *)buf->data;
|
|
uint16_t handle, len = sys_le16_to_cpu(hdr->len);
|
|
struct bt_conn *conn;
|
|
uint8_t flags;
|
|
|
|
BT_DBG("buf %p", buf);
|
|
|
|
handle = sys_le16_to_cpu(hdr->handle);
|
|
flags = bt_acl_flags(handle);
|
|
|
|
acl(buf)->handle = bt_acl_handle(handle);
|
|
|
|
net_buf_pull(buf, sizeof(*hdr));
|
|
|
|
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;
|
|
}
|
|
|
|
bt_conn_recv(conn, buf, flags);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void hci_num_completed_packets(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_num_completed_packets *evt = (void *)buf->data;
|
|
uint16_t i, num_handles = sys_le16_to_cpu(evt->num_handles);
|
|
|
|
BT_DBG("num_handles %u", num_handles);
|
|
|
|
for (i = 0; i < 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;
|
|
}
|
|
|
|
if (conn->pending_pkts >= count) {
|
|
conn->pending_pkts -= count;
|
|
} else {
|
|
BT_ERR("completed packets mismatch: %u > %u",
|
|
count, conn->pending_pkts);
|
|
conn->pending_pkts = 0;
|
|
}
|
|
|
|
while (count--) {
|
|
nano_fiber_sem_give(bt_conn_get_pkts(conn));
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
}
|
|
|
|
static int hci_le_create_conn(const struct bt_conn *conn)
|
|
{
|
|
struct net_buf *buf;
|
|
struct bt_hci_cp_le_create_conn *cp;
|
|
|
|
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));
|
|
|
|
/* Interval == window for continuous scanning */
|
|
cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL);
|
|
cp->scan_window = cp->scan_interval;
|
|
|
|
bt_addr_le_copy(&cp->peer_addr, &conn->le.resp_addr);
|
|
cp->own_addr_type = conn->le.init_addr.type;
|
|
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);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
|
|
}
|
|
|
|
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 %u handle %u reason %u", 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;
|
|
|
|
/* Check stacks usage (no-ops if not enabled) */
|
|
stack_analyze("rx stack", rx_fiber_stack, sizeof(rx_fiber_stack));
|
|
stack_analyze("cmd tx stack", cmd_tx_fiber_stack,
|
|
sizeof(cmd_tx_fiber_stack));
|
|
stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack));
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
conn->handle = 0;
|
|
|
|
if (conn->type != BT_CONN_TYPE_LE) {
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
/*
|
|
* 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_clear(conn->keys, BT_KEYS_LINK_KEY);
|
|
}
|
|
#endif
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) {
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
|
|
bt_le_scan_update(false);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
|
|
set_advertise_enable();
|
|
}
|
|
}
|
|
|
|
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 update_conn_param(struct bt_conn *conn)
|
|
{
|
|
const struct bt_le_conn_param *param;
|
|
|
|
param = BT_LE_CONN_PARAM(conn->le.interval_min,
|
|
conn->le.interval_max,
|
|
conn->le.latency,
|
|
conn->le.timeout);
|
|
return bt_conn_le_param_update(conn, param);
|
|
}
|
|
|
|
static void le_conn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_conn_complete *evt = (void *)buf->data;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
const bt_addr_le_t *id_addr;
|
|
struct bt_conn *conn;
|
|
int err;
|
|
|
|
BT_DBG("status %u handle %u role %u %s", evt->status, handle,
|
|
evt->role, bt_addr_le_str(&evt->peer_addr));
|
|
|
|
if (evt->status) {
|
|
/*
|
|
* if there was an error we are only interested in pending
|
|
* connection so there is no need to check ID address as
|
|
* only one connection can be in that state
|
|
*
|
|
* Depending on error code address might not be valid anyway.
|
|
*/
|
|
conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT);
|
|
if (!conn) {
|
|
return;
|
|
}
|
|
|
|
conn->err = evt->status;
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
/* Drop the reference got by lookup call in CONNECT state.
|
|
* We are now in DISCONNECTED state since no successful LE
|
|
* link been made.
|
|
*/
|
|
bt_conn_unref(conn);
|
|
|
|
return;
|
|
}
|
|
|
|
id_addr = find_id_addr(&evt->peer_addr);
|
|
|
|
/*
|
|
* Make lookup to check if there's a connection object in
|
|
* CONNECT state associated with passed peer LE address.
|
|
*/
|
|
conn = bt_conn_lookup_state_le(id_addr, BT_CONN_CONNECT);
|
|
|
|
if (evt->role == BT_CONN_ROLE_SLAVE) {
|
|
/*
|
|
* clear advertising even if we are not able to add connection
|
|
* object to keep host in sync with controller state
|
|
*/
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING);
|
|
|
|
/* only for slave we may need to add new connection */
|
|
if (!conn) {
|
|
conn = bt_conn_add_le(id_addr);
|
|
}
|
|
}
|
|
|
|
if (!conn) {
|
|
BT_ERR("Unable to add new conn for handle %u", handle);
|
|
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;
|
|
|
|
/*
|
|
* 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 (conn->role == BT_HCI_ROLE_SLAVE) {
|
|
bt_addr_le_copy(&conn->le.init_addr, &evt->peer_addr);
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
bt_addr_le_copy(&conn->le.resp_addr, &bt_dev.random_addr);
|
|
#else
|
|
bt_addr_le_copy(&conn->le.resp_addr, &bt_dev.id_addr);
|
|
#endif /* CONFIG_BLUETOOTH_PRIVACY */
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
|
|
/*
|
|
* it is possible that connection was disconnected directly from
|
|
* connected callback so we must check state before doing connection
|
|
* parameters update
|
|
*/
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
goto done;
|
|
}
|
|
|
|
if ((evt->role == BT_HCI_ROLE_MASTER) ||
|
|
(bt_dev.le.features[0] & BT_HCI_LE_SLAVE_FEATURES)) {
|
|
err = hci_le_read_remote_features(conn);
|
|
if (!err) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
update_conn_param(conn);
|
|
|
|
done:
|
|
bt_conn_unref(conn);
|
|
bt_le_scan_update(false);
|
|
}
|
|
|
|
static void le_remote_feat_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_ev_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));
|
|
}
|
|
|
|
update_conn_param(conn);
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static int 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) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
cp->reason = sys_cpu_to_le16(reason);
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, buf);
|
|
}
|
|
|
|
static int le_conn_param_req_reply(uint16_t handle, uint16_t min, uint16_t max,
|
|
uint16_t latency, uint16_t timeout)
|
|
{
|
|
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));
|
|
memset(cp, 0, sizeof(*cp));
|
|
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
cp->interval_min = sys_cpu_to_le16(min);
|
|
cp->interval_max = sys_cpu_to_le16(max);
|
|
cp->latency = sys_cpu_to_le16(latency);
|
|
cp->timeout = sys_cpu_to_le16(timeout);
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY, buf);
|
|
}
|
|
|
|
static int le_conn_param_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_conn_param_req *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
uint16_t handle, min, max, latency, timeout;
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
min = sys_le16_to_cpu(evt->interval_min);
|
|
max = sys_le16_to_cpu(evt->interval_max);
|
|
latency = sys_le16_to_cpu(evt->latency);
|
|
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);
|
|
return le_conn_param_neg_reply(handle,
|
|
BT_HCI_ERR_UNKNOWN_CONN_ID);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
if (!bt_le_conn_params_valid(min, max, latency, timeout)) {
|
|
return le_conn_param_neg_reply(handle,
|
|
BT_HCI_ERR_INVALID_LL_PARAMS);
|
|
}
|
|
|
|
return le_conn_param_req_reply(handle, min, max, latency, timeout);
|
|
}
|
|
|
|
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 %u, 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);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
static int le_set_rpa(void)
|
|
{
|
|
bt_addr_t rpa;
|
|
int err;
|
|
|
|
err = bt_smp_create_rpa(bt_dev.irk, &rpa);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return set_random_address(&rpa);
|
|
}
|
|
#endif
|
|
|
|
static void check_pending_conn(const bt_addr_le_t *id_addr,
|
|
const bt_addr_le_t *addr, uint8_t evtype)
|
|
{
|
|
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 (evtype != BT_LE_ADV_IND && evtype != BT_LE_ADV_DIRECT_IND) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_state_le(id_addr, BT_CONN_CONNECT_SCAN);
|
|
if (!conn) {
|
|
return;
|
|
}
|
|
|
|
if (bt_hci_stop_scanning()) {
|
|
goto failed;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
if (le_set_rpa()) {
|
|
goto failed;
|
|
}
|
|
|
|
bt_addr_le_copy(&conn->le.init_addr, &bt_dev.random_addr);
|
|
#else
|
|
/* 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 (atomic_test_bit(bt_dev.flags, BT_DEV_ID_STATIC_RANDOM)) {
|
|
set_random_address(&bt_dev.id_addr.a);
|
|
}
|
|
|
|
bt_addr_le_copy(&conn->le.init_addr, &bt_dev.id_addr);
|
|
#endif /* CONFIG_BLUETOOTH_PRIVACY */
|
|
|
|
bt_addr_le_copy(&conn->le.resp_addr, addr);
|
|
|
|
if (hci_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);
|
|
}
|
|
|
|
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_dev.supported_commands[10] & 0x20)) {
|
|
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));
|
|
memset(hbs, 0, sizeof(*hbs));
|
|
hbs->acl_mtu = sys_cpu_to_le16(CONFIG_BLUETOOTH_L2CAP_IN_MTU +
|
|
sizeof(struct bt_l2cap_hdr));
|
|
hbs->acl_pkts = sys_cpu_to_le16(CONFIG_BLUETOOTH_ACL_IN_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_BLUETOOTH_CONN */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
static void reset_pairing(struct bt_conn *conn)
|
|
{
|
|
atomic_clear_bit(conn->flags, BT_CONN_BR_PAIRING);
|
|
atomic_clear_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR);
|
|
atomic_clear_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE);
|
|
|
|
/* Reset required security level to current operational */
|
|
conn->required_sec_level = conn->sec_level;
|
|
}
|
|
|
|
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_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 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);
|
|
|
|
/* Reject SCO connections until we have support for them */
|
|
if (evt->link_type != BT_HCI_ACL) {
|
|
reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES);
|
|
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 void update_sec_level_br(struct bt_conn *conn)
|
|
{
|
|
if (!conn->encrypt) {
|
|
conn->sec_level = BT_SECURITY_LOW;
|
|
return;
|
|
}
|
|
|
|
if (conn->keys && (conn->keys->keys & BT_KEYS_LINK_KEY)) {
|
|
conn->sec_level = BT_SECURITY_MEDIUM;
|
|
if (atomic_test_bit(&conn->keys->flags,
|
|
BT_KEYS_AUTHENTICATED)) {
|
|
conn->sec_level = BT_SECURITY_HIGH;
|
|
}
|
|
} else {
|
|
BT_WARN("No BR/EDR link key found");
|
|
conn->sec_level = BT_SECURITY_MEDIUM;
|
|
}
|
|
|
|
if (conn->required_sec_level > conn->sec_level) {
|
|
BT_ERR("Failed to set required security level");
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL);
|
|
}
|
|
}
|
|
|
|
static void conn_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_conn_complete *evt = (void *)buf->data;
|
|
struct bt_conn *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);
|
|
|
|
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->encrypt = evt->encr_enabled;
|
|
update_sec_level_br(conn);
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void pin_code_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_pin_code_req *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("");
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
bt_conn_pin_code_req(conn);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void link_key_notify(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_ev_link_key_notify *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type);
|
|
|
|
if (!conn->keys) {
|
|
conn->keys = bt_keys_get_link_key(&evt->bdaddr);
|
|
}
|
|
if (!conn->keys) {
|
|
BT_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr));
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
switch (evt->key_type) {
|
|
case BT_LK_COMBINATION:
|
|
atomic_set_bit(&conn->keys->flags, BT_KEYS_BR_LEGACY);
|
|
/*
|
|
* Setting Combination Link Key as AUTHENTICATED means it was
|
|
* successfully generated by 16 digits wide PIN code.
|
|
*/
|
|
if (atomic_test_and_clear_bit(conn->flags,
|
|
BT_CONN_BR_LEGACY_SECURE)) {
|
|
atomic_set_bit(&conn->keys->flags,
|
|
BT_KEYS_AUTHENTICATED);
|
|
}
|
|
memcpy(conn->keys->link_key.val, evt->link_key, 16);
|
|
break;
|
|
case BT_LK_UNAUTH_COMBINATION_P192:
|
|
case BT_LK_AUTH_COMBINATION_P192:
|
|
if (evt->key_type == BT_LK_AUTH_COMBINATION_P192) {
|
|
atomic_set_bit(&conn->keys->flags,
|
|
BT_KEYS_AUTHENTICATED);
|
|
}
|
|
/*
|
|
* Update keys database if authentication bond is required to
|
|
* be persistent. Mark no-bond link key flag for connection on
|
|
* the contrary.
|
|
*/
|
|
if (bt_conn_ssp_get_auth(conn) > BT_HCI_NO_BONDING_MITM) {
|
|
memcpy(conn->keys->link_key.val, evt->link_key, 16);
|
|
} else {
|
|
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
|
|
}
|
|
break;
|
|
default:
|
|
BT_WARN("Link key type unsupported/unimplemented");
|
|
break;
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void link_key_neg_reply(const bt_addr_t *bdaddr)
|
|
{
|
|
struct bt_hci_cp_link_key_neg_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("");
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_NEG_REPLY, buf, NULL);
|
|
}
|
|
|
|
static void link_key_reply(const bt_addr_t *bdaddr, const uint8_t *lk)
|
|
{
|
|
struct bt_hci_cp_link_key_reply *cp;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("");
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
memcpy(cp->link_key, lk, 16);
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_REPLY, buf, NULL);
|
|
}
|
|
|
|
static void link_key_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_link_key_req *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("%s", bt_addr_str(&evt->bdaddr));
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
link_key_neg_reply(&evt->bdaddr);
|
|
return;
|
|
}
|
|
|
|
if (!conn->keys) {
|
|
conn->keys = bt_keys_find_link_key(&evt->bdaddr);
|
|
}
|
|
|
|
if (!conn->keys) {
|
|
link_key_neg_reply(&evt->bdaddr);
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Enforce regenerate by controller stronger link key since found one
|
|
* in database not covers requested security level.
|
|
*/
|
|
if (!atomic_test_bit(&conn->keys->flags, BT_KEYS_AUTHENTICATED) &&
|
|
conn->required_sec_level > BT_SECURITY_MEDIUM) {
|
|
link_key_neg_reply(&evt->bdaddr);
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
link_key_reply(&evt->bdaddr, conn->keys->link_key.val);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void io_capa_neg_reply(const bt_addr_t *bdaddr, const uint8_t reason)
|
|
{
|
|
struct bt_hci_cp_io_capability_neg_reply *cp;
|
|
struct net_buf *resp_buf;
|
|
|
|
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
|
sizeof(*cp));
|
|
if (!resp_buf) {
|
|
BT_ERR("Out of command buffers");
|
|
return;
|
|
}
|
|
|
|
cp = net_buf_add(resp_buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
cp->reason = reason;
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, resp_buf, NULL);
|
|
}
|
|
|
|
static void io_capa_resp(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("remote %s, IOcapa 0x%02x, auth 0x%02x",
|
|
bt_addr_str(&evt->bdaddr), evt->capability, evt->authentication);
|
|
|
|
if (evt->authentication > BT_HCI_GENERAL_BONDING_MITM) {
|
|
BT_ERR("Invalid remote authentication requirements");
|
|
io_capa_neg_reply(&evt->bdaddr,
|
|
BT_HCI_ERR_UNSUPP_FEATURE_PARAMS_VAL);
|
|
return;
|
|
}
|
|
|
|
if (evt->capability > BT_IO_NO_INPUT_OUTPUT) {
|
|
BT_ERR("Invalid remote io capability requirements");
|
|
io_capa_neg_reply(&evt->bdaddr,
|
|
BT_HCI_ERR_UNSUPP_FEATURE_PARAMS_VAL);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
conn->br.remote_io_capa = evt->capability;
|
|
conn->br.remote_auth = evt->authentication;
|
|
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void io_capa_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_io_capa_req *evt = (void *)buf->data;
|
|
struct net_buf *resp_buf;
|
|
struct bt_conn *conn;
|
|
struct bt_hci_cp_io_capability_reply *cp;
|
|
uint8_t auth;
|
|
|
|
BT_DBG("");
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY,
|
|
sizeof(*cp));
|
|
if (!resp_buf) {
|
|
BT_ERR("Out of command buffers");
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set authentication requirements when acting as pairing initiator to
|
|
* 'dedicated bond' with MITM protection set if local IO capa
|
|
* potentially allows it, and for acceptor, based on local IO capa and
|
|
* remote's authentication set.
|
|
*/
|
|
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR)) {
|
|
if (bt_conn_get_io_capa() != BT_IO_NO_INPUT_OUTPUT) {
|
|
auth = BT_HCI_DEDICATED_BONDING_MITM;
|
|
} else {
|
|
auth = BT_HCI_DEDICATED_BONDING;
|
|
}
|
|
} else {
|
|
auth = bt_conn_ssp_get_auth(conn);
|
|
}
|
|
|
|
cp = net_buf_add(resp_buf, sizeof(*cp));
|
|
bt_addr_copy(&cp->bdaddr, &evt->bdaddr);
|
|
cp->capability = bt_conn_get_io_capa();
|
|
cp->authentication = auth;
|
|
cp->oob_data = 0;
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_REPLY, resp_buf, NULL);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void ssp_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_ssp_complete *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("status %u", evt->status);
|
|
|
|
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->status) {
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void user_confirm_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_user_confirm_req *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void user_passkey_notify(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
BT_DBG("");
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void user_passkey_req(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_user_passkey_req *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
|
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
return;
|
|
}
|
|
|
|
bt_conn_ssp_auth(conn, 0);
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
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].private;
|
|
|
|
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 = 1;
|
|
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 void discovery_results_full(void)
|
|
{
|
|
int err;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY_CANCEL, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("Failed to cancel discovery (%d)", err);
|
|
return;
|
|
}
|
|
|
|
report_discovery_results();
|
|
}
|
|
|
|
static struct bt_br_discovery_result *get_result_slot(const bt_addr_t *addr)
|
|
{
|
|
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];
|
|
}
|
|
}
|
|
|
|
/* get new slot from results */
|
|
if (discovery_results_count < discovery_results_size) {
|
|
bt_addr_copy(&discovery_results[discovery_results_count].addr,
|
|
addr);
|
|
return &discovery_results[discovery_results_count++];
|
|
}
|
|
|
|
discovery_results_full();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void inquiry_result_with_rssi(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_inquiry_result_with_rssi *evt;
|
|
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);
|
|
|
|
evt = (void *)buf->data;
|
|
while (num_reports--) {
|
|
struct bt_br_discovery_result *result;
|
|
struct discovery_priv *priv;
|
|
|
|
BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi);
|
|
|
|
result = get_result_slot(&evt->addr);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->private;
|
|
priv->pscan_rep_mode = evt->pscan_rep_mode;
|
|
priv->clock_offset = evt->clock_offset;
|
|
|
|
memcpy(result->cod, evt->cod, 3);
|
|
result->rssi = evt->rssi;
|
|
|
|
/*
|
|
* Get next report iteration by moving pointer to right offset
|
|
* in buf according to spec 4.2, Vol 2, Part E, 7.7.33.
|
|
*/
|
|
evt = net_buf_pull(buf, sizeof(*evt));
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->private;
|
|
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);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
priv = (struct discovery_priv *)&result->private;
|
|
priv->resolving = 0;
|
|
|
|
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(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].private;
|
|
|
|
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 link_encr(const uint16_t handle)
|
|
{
|
|
struct bt_hci_cp_set_conn_encrypt *encr;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("");
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
return;
|
|
}
|
|
|
|
encr = net_buf_add(buf, sizeof(*encr));
|
|
encr->handle = sys_cpu_to_le16(handle);
|
|
encr->encrypt = 0x01;
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_SET_CONN_ENCRYPT, buf, NULL);
|
|
}
|
|
|
|
static void auth_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_auth_complete *evt = (void *)buf->data;
|
|
struct bt_conn *conn;
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
BT_DBG("status %u, 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) {
|
|
reset_pairing(conn);
|
|
} else {
|
|
link_encr(handle);
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_BREDR */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
|
|
static void update_sec_level(struct bt_conn *conn)
|
|
{
|
|
if (!conn->encrypt) {
|
|
conn->sec_level = BT_SECURITY_LOW;
|
|
return;
|
|
}
|
|
|
|
if (conn->keys && atomic_test_bit(&conn->keys->flags,
|
|
BT_KEYS_AUTHENTICATED)) {
|
|
if (conn->keys->keys & BT_KEYS_LTK_P256) {
|
|
conn->sec_level = BT_SECURITY_FIPS;
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_HIGH;
|
|
}
|
|
} else {
|
|
conn->sec_level = BT_SECURITY_MEDIUM;
|
|
}
|
|
|
|
if (conn->required_sec_level > conn->sec_level) {
|
|
BT_ERR("Failed to set required security level");
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL);
|
|
}
|
|
}
|
|
|
|
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 %u 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) {
|
|
/* TODO report error */
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
/* reset required security level in case of error */
|
|
conn->required_sec_level = conn->sec_level;
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
} else {
|
|
reset_pairing(conn);
|
|
#endif /* CONFIG_BLUETOOTH_BREDR */
|
|
}
|
|
bt_conn_unref(conn);
|
|
return;
|
|
}
|
|
|
|
conn->encrypt = evt->encrypt;
|
|
|
|
/*
|
|
* 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 && conn->type == BT_CONN_TYPE_LE) {
|
|
bt_smp_update_keys(conn);
|
|
}
|
|
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
update_sec_level(conn);
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
} else {
|
|
update_sec_level_br(conn);
|
|
reset_pairing(conn);
|
|
#endif /* CONFIG_BLUETOOTH_BREDR */
|
|
}
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
|
bt_conn_security_changed(conn);
|
|
|
|
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 %u handle %u", evt->status, handle);
|
|
|
|
if (evt->status) {
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn) {
|
|
BT_ERR("Unable to look up conn with handle %u", handle);
|
|
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 (conn->type == BT_CONN_TYPE_LE) {
|
|
bt_smp_update_keys(conn);
|
|
update_sec_level(conn);
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
} else {
|
|
update_sec_level_br(conn);
|
|
#endif /* CONFIG_BLUETOOTH_BREDR */
|
|
}
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
|
bt_conn_security_changed(conn);
|
|
bt_conn_unref(conn);
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
static void le_ltk_request(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_ltk_request *evt = (void *)buf->data;
|
|
struct bt_hci_cp_le_ltk_req_neg_reply *cp;
|
|
struct bt_conn *conn;
|
|
uint16_t handle;
|
|
uint8_t tk[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 TK is present use it, that means pairing is in progress and
|
|
* we should use new TK for encryption
|
|
*
|
|
* Both legacy STK and LE SC LTK have rand and ediv equal to zero.
|
|
*/
|
|
if (evt->rand == 0 && evt->ediv == 0 && bt_smp_get_tk(conn, tk)) {
|
|
struct bt_hci_cp_le_ltk_req_reply *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
goto done;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
memcpy(cp->ltk, tk, sizeof(cp->ltk));
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
|
|
goto done;
|
|
}
|
|
|
|
if (!conn->keys) {
|
|
conn->keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
|
|
if (!conn->keys) {
|
|
conn->keys = bt_keys_find(BT_KEYS_SLAVE_LTK,
|
|
&conn->le.dst);
|
|
}
|
|
}
|
|
|
|
if (conn->keys && (conn->keys->keys & BT_KEYS_LTK_P256) &&
|
|
evt->rand == 0 && evt->ediv == 0) {
|
|
struct bt_hci_cp_le_ltk_req_reply *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY,
|
|
sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
goto done;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
|
|
/* use only enc_size bytes of key for encryption */
|
|
memcpy(cp->ltk, conn->keys->ltk.val, conn->keys->enc_size);
|
|
if (conn->keys->enc_size < sizeof(cp->ltk)) {
|
|
memset(cp->ltk + conn->keys->enc_size, 0,
|
|
sizeof(cp->ltk) - conn->keys->enc_size);
|
|
}
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
|
|
goto done;
|
|
}
|
|
|
|
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
|
|
if (conn->keys && (conn->keys->keys & BT_KEYS_SLAVE_LTK) &&
|
|
conn->keys->slave_ltk.rand == evt->rand &&
|
|
conn->keys->slave_ltk.ediv == evt->ediv) {
|
|
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");
|
|
goto done;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
|
|
/* use only enc_size bytes of key for encryption */
|
|
memcpy(cp->ltk, conn->keys->slave_ltk.val,
|
|
conn->keys->enc_size);
|
|
if (conn->keys->enc_size < sizeof(cp->ltk)) {
|
|
memset(cp->ltk + conn->keys->enc_size, 0,
|
|
sizeof(cp->ltk) - conn->keys->enc_size);
|
|
}
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
|
|
goto done;
|
|
}
|
|
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, sizeof(*cp));
|
|
if (!buf) {
|
|
BT_ERR("Out of command buffers");
|
|
goto done;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf);
|
|
|
|
done:
|
|
bt_conn_unref(conn);
|
|
}
|
|
|
|
static void le_pkey_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_p256_public_key_complete *evt = (void *)buf->data;
|
|
|
|
BT_DBG("status: 0x%x", evt->status);
|
|
|
|
if (evt->status) {
|
|
return;
|
|
}
|
|
|
|
bt_smp_pkey_ready(evt->key);
|
|
}
|
|
|
|
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%x", evt->status);
|
|
|
|
if (evt->status) {
|
|
bt_smp_dhkey_ready(NULL);
|
|
return;
|
|
}
|
|
|
|
bt_smp_dhkey_ready(evt->dhkey);
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
|
|
static void hci_reset_complete(struct net_buf *buf)
|
|
{
|
|
uint8_t status = buf->data[0];
|
|
|
|
BT_DBG("status %u", status);
|
|
|
|
if (status) {
|
|
return;
|
|
}
|
|
|
|
scan_dev_found_cb = NULL;
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
discovery_cb = NULL;
|
|
discovery_results = NULL;
|
|
discovery_results_size = 0;
|
|
discovery_results_count = 0;
|
|
#endif /* CONFIG_BLUETOOTH_BREDR */
|
|
|
|
/* we only allow to enable once so this bit must be keep set */
|
|
atomic_set(bt_dev.flags, BIT(BT_DEV_ENABLE));
|
|
}
|
|
|
|
static void hci_cmd_done(uint16_t opcode, uint8_t status, struct net_buf *buf)
|
|
{
|
|
struct net_buf *sent = bt_dev.sent_cmd;
|
|
|
|
if (!sent) {
|
|
return;
|
|
}
|
|
|
|
if (cmd(sent)->opcode != opcode) {
|
|
BT_ERR("Unexpected completion of opcode 0x%04x expected 0x%04x",
|
|
opcode, cmd(sent)->opcode);
|
|
return;
|
|
}
|
|
|
|
bt_dev.sent_cmd = NULL;
|
|
|
|
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
|
|
if (cmd(sent)->sync) {
|
|
struct nano_sem *sem = cmd(sent)->sync;
|
|
|
|
if (status) {
|
|
cmd(sent)->sync = NULL;
|
|
} else {
|
|
cmd(sent)->sync = net_buf_ref(buf);
|
|
}
|
|
|
|
nano_fiber_sem_give(sem);
|
|
} else {
|
|
net_buf_unref(sent);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_complete(struct net_buf *buf)
|
|
{
|
|
struct hci_evt_cmd_complete *evt = (void *)buf->data;
|
|
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
|
|
uint8_t status;
|
|
|
|
BT_DBG("opcode 0x%04x", opcode);
|
|
|
|
net_buf_pull(buf, sizeof(*evt));
|
|
|
|
/* 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);
|
|
|
|
if (evt->ncmd && !bt_dev.ncmd) {
|
|
/* Allow next command to be sent */
|
|
bt_dev.ncmd = 1;
|
|
nano_fiber_sem_give(&bt_dev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_status(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_cmd_status *evt = (void *)buf->data;
|
|
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
|
|
|
|
BT_DBG("opcode 0x%04x", opcode);
|
|
|
|
net_buf_pull(buf, sizeof(*evt));
|
|
|
|
hci_cmd_done(opcode, evt->status, buf);
|
|
|
|
if (evt->ncmd && !bt_dev.ncmd) {
|
|
/* Allow next command to be sent */
|
|
bt_dev.ncmd = 1;
|
|
nano_fiber_sem_give(&bt_dev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
static int prng_reseed(struct tc_hmac_prng_struct *h)
|
|
{
|
|
uint8_t seed[32];
|
|
int64_t extra;
|
|
int ret, i;
|
|
|
|
for (i = 0; i < (sizeof(seed) / 8); i++) {
|
|
struct bt_hci_rp_le_rand *rp;
|
|
struct net_buf *rsp;
|
|
|
|
#if defined(CONFIG_BOARD_ARDUINO_101)
|
|
/* FIXME: Temporary hack for MyNewt HCI firmware which
|
|
* crashes if it receives too rapid LE_Rand commands.
|
|
*/
|
|
if (sys_execution_context_type_get() == NANO_CTX_FIBER) {
|
|
fiber_sleep(MSEC(30));
|
|
} else {
|
|
task_sleep(MSEC(30));
|
|
}
|
|
#endif
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
memcpy(&seed[i * 8], rp->rand, 8);
|
|
|
|
net_buf_unref(rsp);
|
|
}
|
|
|
|
extra = sys_tick_get();
|
|
|
|
ret = tc_hmac_prng_reseed(h, seed, sizeof(seed), (uint8_t *)&extra,
|
|
sizeof(extra));
|
|
if (ret == TC_FAIL) {
|
|
BT_ERR("Failed to re-seed PRNG");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prng_init(struct tc_hmac_prng_struct *h)
|
|
{
|
|
struct bt_hci_rp_le_rand *rp;
|
|
struct net_buf *rsp;
|
|
int ret;
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
|
|
ret = tc_hmac_prng_init(h, rp->rand, sizeof(rp->rand));
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
if (ret == TC_FAIL) {
|
|
BT_ERR("Failed to initialize PRNG");
|
|
return -EIO;
|
|
}
|
|
|
|
/* re-seed is needed after init */
|
|
return prng_reseed(h);
|
|
}
|
|
|
|
int bt_rand(void *buf, size_t len)
|
|
{
|
|
int ret;
|
|
|
|
ret = tc_hmac_prng_generate(buf, len, &prng);
|
|
if (ret == TC_HMAC_PRNG_RESEED_REQ) {
|
|
ret = prng_reseed(&prng);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = tc_hmac_prng_generate(buf, len, &prng);
|
|
}
|
|
|
|
if (ret == TC_SUCCESS) {
|
|
return 0;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int le_set_nrpa(void)
|
|
{
|
|
bt_addr_t nrpa;
|
|
int err;
|
|
|
|
err = bt_rand(nrpa.val, sizeof(nrpa.val));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
nrpa.val[5] &= 0x3f;
|
|
|
|
return set_random_address(&nrpa);
|
|
}
|
|
|
|
static int start_le_scan(uint8_t scan_type, uint16_t interval, uint16_t window,
|
|
uint8_t filter_dup)
|
|
{
|
|
struct net_buf *buf, *rsp;
|
|
struct bt_hci_cp_le_set_scan_params *set_param;
|
|
struct bt_hci_cp_le_set_scan_enable *scan_enable;
|
|
int err;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS,
|
|
sizeof(*set_param));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_param = net_buf_add(buf, sizeof(*set_param));
|
|
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);
|
|
set_param->filter_policy = 0x00;
|
|
|
|
if (scan_type == BT_HCI_LE_SCAN_ACTIVE) {
|
|
err = le_set_nrpa();
|
|
if (err) {
|
|
net_buf_unref(buf);
|
|
return err;
|
|
}
|
|
|
|
set_param->addr_type = BT_ADDR_LE_RANDOM;
|
|
} else {
|
|
set_param->addr_type = BT_ADDR_LE_PUBLIC;
|
|
}
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_PARAMS, buf);
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
|
|
sizeof(*scan_enable));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
scan_enable = net_buf_add(buf, sizeof(*scan_enable));
|
|
memset(scan_enable, 0, sizeof(*scan_enable));
|
|
scan_enable->filter_dup = filter_dup;
|
|
scan_enable->enable = BT_HCI_LE_SCAN_ENABLE;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Update scan state in case of success (0) status */
|
|
err = rsp->data[0];
|
|
if (!err) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_SCANNING);
|
|
}
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_le_scan_update(bool fast_scan)
|
|
{
|
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
|
uint16_t interval, window;
|
|
struct bt_conn *conn;
|
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) {
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
|
|
int err;
|
|
|
|
err = bt_hci_stop_scanning();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
|
conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT_SCAN);
|
|
if (!conn) {
|
|
return 0;
|
|
}
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
if (fast_scan) {
|
|
interval = BT_GAP_SCAN_FAST_INTERVAL;
|
|
window = BT_GAP_SCAN_FAST_WINDOW;
|
|
} else {
|
|
interval = BT_GAP_SCAN_SLOW_INTERVAL_1;
|
|
window = BT_GAP_SCAN_SLOW_WINDOW_1;
|
|
}
|
|
|
|
return start_le_scan(BT_HCI_LE_SCAN_PASSIVE, interval, window, 0x01);
|
|
#else
|
|
return 0;
|
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
|
}
|
|
|
|
static void le_adv_report(struct net_buf *buf)
|
|
{
|
|
uint8_t num_reports = net_buf_pull_u8(buf);
|
|
struct bt_hci_ev_le_advertising_info *info;
|
|
|
|
BT_DBG("Adv number of reports %u", num_reports);
|
|
|
|
info = (void *)buf->data;
|
|
while (num_reports--) {
|
|
int8_t rssi = info->data[info->length];
|
|
const bt_addr_le_t *addr;
|
|
|
|
BT_DBG("%s event %u, len %u, rssi %d dBm",
|
|
bt_addr_le_str(&info->addr),
|
|
info->evt_type, info->length, rssi);
|
|
|
|
addr = find_id_addr(&info->addr);
|
|
|
|
if (scan_dev_found_cb) {
|
|
scan_dev_found_cb(addr, rssi, info->evt_type,
|
|
info->data, info->length);
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
check_pending_conn(addr, &info->addr, info->evt_type);
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
/* Get next report iteration by moving pointer to right offset
|
|
* in buf according to spec 4.2, Vol 2, Part E, 7.7.65.2.
|
|
*/
|
|
info = net_buf_pull(buf, sizeof(*info) + info->length +
|
|
sizeof(rssi));
|
|
}
|
|
}
|
|
|
|
static void hci_le_meta_event(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_meta_event *evt = (void *)buf->data;
|
|
|
|
net_buf_pull(buf, sizeof(*evt));
|
|
|
|
switch (evt->subevent) {
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
case BT_HCI_EVT_LE_CONN_COMPLETE:
|
|
le_conn_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE:
|
|
le_conn_update_complete(buf);
|
|
break;
|
|
case BT_HCI_EV_LE_REMOTE_FEAT_COMPLETE:
|
|
le_remote_feat_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_LE_CONN_PARAM_REQ:
|
|
le_conn_param_req(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
case BT_HCI_EVT_LE_LTK_REQUEST:
|
|
le_ltk_request(buf);
|
|
break;
|
|
case BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE:
|
|
le_pkey_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE:
|
|
le_dhkey_complete(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
case BT_HCI_EVT_LE_ADVERTISING_REPORT:
|
|
le_adv_report(buf);
|
|
break;
|
|
default:
|
|
BT_DBG("Unhandled LE event %x", evt->subevent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void hci_event(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
|
|
|
|
BT_DBG("event %u", hdr->evt);
|
|
|
|
net_buf_pull(buf, sizeof(*hdr));
|
|
|
|
switch (hdr->evt) {
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
case BT_HCI_EVT_CONN_REQUEST:
|
|
conn_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_CONN_COMPLETE:
|
|
conn_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_PIN_CODE_REQ:
|
|
pin_code_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_LINK_KEY_NOTIFY:
|
|
link_key_notify(buf);
|
|
break;
|
|
case BT_HCI_EVT_LINK_KEY_REQ:
|
|
link_key_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_IO_CAPA_RESP:
|
|
io_capa_resp(buf);
|
|
break;
|
|
case BT_HCI_EVT_IO_CAPA_REQ:
|
|
io_capa_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_SSP_COMPLETE:
|
|
ssp_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_USER_CONFIRM_REQ:
|
|
user_confirm_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_USER_PASSKEY_NOTIFY:
|
|
user_passkey_notify(buf);
|
|
break;
|
|
case BT_HCI_EVT_USER_PASSKEY_REQ:
|
|
user_passkey_req(buf);
|
|
break;
|
|
case BT_HCI_EVT_INQUIRY_COMPLETE:
|
|
inquiry_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI:
|
|
inquiry_result_with_rssi(buf);
|
|
break;
|
|
case BT_HCI_EVT_EXTENDED_INQUIRY_RESULT:
|
|
extended_inquiry_result(buf);
|
|
break;
|
|
case BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE:
|
|
remote_name_request_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_AUTH_COMPLETE:
|
|
auth_complete(buf);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
case BT_HCI_EVT_DISCONN_COMPLETE:
|
|
hci_disconn_complete(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
|
|
case BT_HCI_EVT_ENCRYPT_CHANGE:
|
|
hci_encrypt_change(buf);
|
|
break;
|
|
case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE:
|
|
hci_encrypt_key_refresh_complete(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */
|
|
case BT_HCI_EVT_LE_META_EVENT:
|
|
hci_le_meta_event(buf);
|
|
break;
|
|
default:
|
|
BT_WARN("Unhandled event 0x%02x", hdr->evt);
|
|
break;
|
|
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
static void hci_cmd_tx_fiber(void)
|
|
{
|
|
BT_DBG("started");
|
|
|
|
while (1) {
|
|
struct net_buf *buf;
|
|
int err;
|
|
|
|
/* Wait until ncmd > 0 */
|
|
BT_DBG("calling sem_take_wait");
|
|
nano_fiber_sem_take(&bt_dev.ncmd_sem, TICKS_UNLIMITED);
|
|
|
|
/* Get next command - wait if necessary */
|
|
BT_DBG("calling fifo_get_wait");
|
|
buf = nano_fifo_get(&bt_dev.cmd_tx_queue, TICKS_UNLIMITED);
|
|
bt_dev.ncmd = 0;
|
|
|
|
/* 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);
|
|
nano_fiber_sem_give(&bt_dev.ncmd_sem);
|
|
hci_cmd_done(cmd(buf)->opcode, BT_HCI_ERR_UNSPECIFIED,
|
|
NULL);
|
|
net_buf_unref(buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void read_local_features_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status %u", rp->status);
|
|
|
|
memcpy(bt_dev.features, rp->features, sizeof(bt_dev.features));
|
|
}
|
|
|
|
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 %u", rp->status);
|
|
|
|
bt_dev.hci_version = rp->hci_version;
|
|
bt_dev.hci_revision = sys_le16_to_cpu(rp->hci_revision);
|
|
bt_dev.manufacturer = sys_le16_to_cpu(rp->manufacturer);
|
|
}
|
|
|
|
static void read_bdaddr_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_bd_addr *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status %u", rp->status);
|
|
|
|
bt_addr_copy(&bt_dev.id_addr.a, &rp->bdaddr);
|
|
bt_dev.id_addr.type = BT_ADDR_LE_PUBLIC;
|
|
}
|
|
|
|
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 %u", rp->status);
|
|
|
|
memcpy(bt_dev.le.features, rp->features, sizeof(bt_dev.le.features));
|
|
}
|
|
|
|
static void init_sem(struct nano_sem *sem, size_t count)
|
|
{
|
|
/* Initialize & prime the semaphore for counting controller-side
|
|
* available ACL packet buffers.
|
|
*/
|
|
nano_sem_init(sem);
|
|
while (count--) {
|
|
nano_sem_give(sem);
|
|
};
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_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 %u", 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);
|
|
|
|
init_sem(&bt_dev.br.pkts, pkts);
|
|
}
|
|
#else
|
|
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 %u", rp->status);
|
|
|
|
/* If LE-side has buffers we can ignore the BR/EDR values */
|
|
if (bt_dev.le.mtu) {
|
|
return;
|
|
}
|
|
|
|
bt_dev.le.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.mtu);
|
|
|
|
init_sem(&bt_dev.le.pkts, pkts);
|
|
}
|
|
#endif
|
|
|
|
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 %u", rp->status);
|
|
|
|
bt_dev.le.mtu = sys_le16_to_cpu(rp->le_max_len);
|
|
|
|
if (bt_dev.le.mtu) {
|
|
init_sem(&bt_dev.le.pkts, rp->le_max_num);
|
|
BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->le_max_num,
|
|
bt_dev.le.mtu);
|
|
}
|
|
}
|
|
|
|
static void read_supported_commands_complete(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_rp_read_supported_commands *rp = (void *)buf->data;
|
|
|
|
BT_DBG("status %u", rp->status);
|
|
|
|
memcpy(bt_dev.supported_commands, rp->commands,
|
|
sizeof(bt_dev.supported_commands));
|
|
|
|
#if defined(CONFIG_BLUETOOTH_TINYCRYPT_ECC)
|
|
/*
|
|
* Report "LE Read Local P-256 Public Key" and "LE Generate DH Key" as
|
|
* supported if TinyCrypt ECC is used for emulation.
|
|
*/
|
|
bt_dev.supported_commands[34] |= 0x02;
|
|
bt_dev.supported_commands[34] |= 0x04;
|
|
#endif /* CONFIG_BLUETOOTH_TINYCRYPT_ECC */
|
|
}
|
|
|
|
static int common_init(void)
|
|
{
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
/* 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);
|
|
|
|
/*
|
|
* initialize PRNG right after reset so that it is safe to use it later
|
|
* on in initialization process
|
|
*/
|
|
err = prng_init(&prng);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* 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 Bluetooth Address */
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
read_bdaddr_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 defined(CONFIG_BLUETOOTH_CONN)
|
|
err = set_flow_control();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int le_init(void)
|
|
{
|
|
struct bt_hci_cp_write_le_host_supp *cp_le;
|
|
struct bt_hci_cp_le_set_event_mask *cp_mask;
|
|
struct net_buf *buf;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
/* For now we only support LE capable controllers */
|
|
if (!lmp_le_capable(bt_dev)) {
|
|
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);
|
|
|
|
/* 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);
|
|
|
|
if (lmp_bredr_capable(bt_dev)) {
|
|
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));
|
|
|
|
/* Excplicitly 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;
|
|
}
|
|
}
|
|
|
|
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));
|
|
memset(cp_mask, 0, sizeof(*cp_mask));
|
|
|
|
cp_mask->events[0] |= 0x02; /* LE Advertising Report Event */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
cp_mask->events[0] |= 0x01; /* LE Connection Complete Event */
|
|
cp_mask->events[0] |= 0x04; /* LE Connection Update Complete Event */
|
|
cp_mask->events[0] |= 0x08; /* LE Read Remote Used Features Compl Evt */
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
cp_mask->events[0] |= 0x10; /* LE Long Term Key Request Event */
|
|
|
|
/*
|
|
* 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 ((bt_dev.supported_commands[34] & 0x02) &&
|
|
(bt_dev.supported_commands[34] & 0x04)) {
|
|
cp_mask->events[0] |= 0x80; /* LE Read Local P-256 PKey Compl */
|
|
cp_mask->events[1] |= 0x01; /* LE Generate DHKey Compl Event */
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
/*
|
|
* We check for both "LE Read Local P-256 Public Key" and
|
|
* "LE Generate DH Key" support here since both commands are needed for
|
|
* LE SC support. If "LE Generate DH Key" is not supported then there
|
|
* is no point in reading local public key.
|
|
*/
|
|
if ((bt_dev.supported_commands[34] & 0x02) &&
|
|
(bt_dev.supported_commands[34] & 0x04)) {
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_P256_PUBLIC_KEY, NULL,
|
|
NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
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;
|
|
int err;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int br_init(void)
|
|
{
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
if (bt_dev.le.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);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int set_event_mask(void)
|
|
{
|
|
struct bt_hci_cp_set_event_mask *ev;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
ev = net_buf_add(buf, sizeof(*ev));
|
|
memset(ev, 0, sizeof(*ev));
|
|
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
ev->events[0] |= 0x01; /* Inquiry Complete */
|
|
ev->events[0] |= 0x04; /* Connection Complete */
|
|
ev->events[0] |= 0x08; /* Connection Request */
|
|
ev->events[0] |= 0x20; /* Authentication Complete */
|
|
ev->events[0] |= 0x40; /* Remote Name Request Complete */
|
|
ev->events[2] |= 0x20; /* Pin Code Request */
|
|
ev->events[2] |= 0x40; /* Link Key Request */
|
|
ev->events[2] |= 0x80; /* Link Key Notif */
|
|
ev->events[4] |= 0x02; /* Inquiry Result With RSSI */
|
|
ev->events[5] |= 0x40; /* Extended Inquiry Result */
|
|
ev->events[6] |= 0x01; /* IO Capability Request */
|
|
ev->events[6] |= 0x02; /* IO Capability Response */
|
|
ev->events[6] |= 0x04; /* User Confirmation Request */
|
|
ev->events[6] |= 0x08; /* User Passkey Request */
|
|
ev->events[6] |= 0x20; /* Simple Pairing Complete */
|
|
ev->events[7] |= 0x04; /* User Passkey Notification */
|
|
#endif
|
|
ev->events[1] |= 0x20; /* Command Complete */
|
|
ev->events[1] |= 0x40; /* Command Status */
|
|
ev->events[1] |= 0x80; /* Hardware Error */
|
|
ev->events[3] |= 0x02; /* Data Buffer Overflow */
|
|
ev->events[7] |= 0x20; /* LE Meta-Event */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
ev->events[0] |= 0x10; /* Disconnection Complete */
|
|
ev->events[1] |= 0x08; /* Read Remote Version Information Complete */
|
|
ev->events[2] |= 0x04; /* Number of Completed Packets */
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
if (bt_dev.le.features[0] & BT_HCI_LE_ENCRYPTION) {
|
|
ev->events[0] |= 0x80; /* Encryption Change */
|
|
ev->events[5] |= 0x80; /* Encryption Key Refresh Complete */
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL);
|
|
}
|
|
|
|
static int set_static_addr(void)
|
|
{
|
|
int err;
|
|
|
|
if (bt_storage) {
|
|
ssize_t ret;
|
|
|
|
ret = bt_storage->read(NULL, BT_STORAGE_ID_ADDR,
|
|
&bt_dev.id_addr, sizeof(bt_dev.id_addr));
|
|
if (ret == sizeof(bt_dev.id_addr)) {
|
|
goto set_addr;
|
|
}
|
|
}
|
|
|
|
BT_DBG("Generating new static random address");
|
|
|
|
bt_dev.id_addr.type = BT_ADDR_LE_RANDOM;
|
|
|
|
err = bt_rand(bt_dev.id_addr.a.val, 6);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Make sure the address bits indicate static address */
|
|
bt_dev.id_addr.a.val[5] |= 0xc0;
|
|
|
|
if (bt_storage) {
|
|
ssize_t ret;
|
|
|
|
ret = bt_storage->write(NULL, BT_STORAGE_ID_ADDR,
|
|
&bt_dev.id_addr,
|
|
sizeof(bt_dev.id_addr));
|
|
if (ret != sizeof(bt_dev.id_addr)) {
|
|
BT_ERR("Unable to store static address");
|
|
}
|
|
} else {
|
|
BT_WARN("Using temporary static random address");
|
|
}
|
|
|
|
set_addr:
|
|
if (bt_dev.id_addr.type != BT_ADDR_LE_RANDOM ||
|
|
(bt_dev.id_addr.a.val[5] & 0xc0) != 0xc0) {
|
|
BT_ERR("Only static random address supported as identity");
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = set_random_address(&bt_dev.id_addr.a);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_ID_STATIC_RANDOM);
|
|
return 0;
|
|
}
|
|
|
|
static int hci_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = common_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = le_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (lmp_bredr_capable(bt_dev)) {
|
|
err = br_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
} else {
|
|
BT_DBG("Non-BR/EDR controller detected! Skipping BR init.");
|
|
}
|
|
|
|
err = set_event_mask();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (!bt_addr_le_cmp(&bt_dev.id_addr, BT_ADDR_LE_ANY)) {
|
|
BT_DBG("No public address. Trying to set static random.");
|
|
err = set_static_addr();
|
|
if (err) {
|
|
BT_ERR("Unable to set identity address");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
BT_DBG("HCI ver %u rev %u, manufacturer %u", bt_dev.hci_version,
|
|
bt_dev.hci_revision, bt_dev.manufacturer);
|
|
|
|
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);
|
|
|
|
return bt_dev.drv->send(buf);
|
|
}
|
|
|
|
/* Interface to HCI driver layer */
|
|
|
|
int bt_recv(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_hdr *hdr;
|
|
|
|
#if defined(CONFIG_MICROKERNEL)
|
|
if (sys_execution_context_type_get() == NANO_CTX_TASK) {
|
|
return task_offload_to_fiber(bt_recv, buf);
|
|
}
|
|
#endif /* CONFIG_MICROKERNEL */
|
|
|
|
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
|
|
|
BT_DBG("buf %p len %u", buf, buf->len);
|
|
|
|
if (buf->user_data_size < BT_BUF_USER_DATA_MIN) {
|
|
BT_ERR("Too small user data size");
|
|
net_buf_unref(buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (bt_buf_get_type(buf) == BT_BUF_ACL_IN) {
|
|
nano_fifo_put(&bt_dev.rx_queue, buf);
|
|
return 0;
|
|
}
|
|
|
|
if (bt_buf_get_type(buf) != BT_BUF_EVT) {
|
|
BT_ERR("Invalid buf type %u", bt_buf_get_type(buf));
|
|
net_buf_unref(buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hdr = (void *)buf->data;
|
|
|
|
switch (hdr->evt) {
|
|
case BT_HCI_EVT_CMD_COMPLETE:
|
|
net_buf_pull(buf, sizeof(*hdr));
|
|
hci_cmd_complete(buf);
|
|
break;
|
|
case BT_HCI_EVT_CMD_STATUS:
|
|
net_buf_pull(buf, sizeof(*hdr));
|
|
hci_cmd_status(buf);
|
|
break;
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
|
|
net_buf_pull(buf, sizeof(*hdr));
|
|
hci_num_completed_packets(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
default:
|
|
nano_fifo_put(&bt_dev.rx_queue, net_buf_ref(buf));
|
|
break;
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
return 0;
|
|
}
|
|
|
|
int bt_driver_register(struct bt_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_driver_unregister(struct bt_driver *drv)
|
|
{
|
|
bt_dev.drv = NULL;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
static int irk_init(void)
|
|
{
|
|
ssize_t err;
|
|
|
|
if (bt_storage) {
|
|
err = bt_storage->read(NULL, BT_STORAGE_LOCAL_IRK, &bt_dev.irk,
|
|
sizeof(bt_dev.irk));
|
|
if (err == sizeof(bt_dev.irk)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
BT_DBG("Generating new IRK");
|
|
|
|
err = bt_rand(bt_dev.irk, sizeof(bt_dev.irk));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (bt_storage) {
|
|
err = bt_storage->write(NULL, BT_STORAGE_LOCAL_IRK, bt_dev.irk,
|
|
sizeof(bt_dev.irk));
|
|
if (err != sizeof(bt_dev.irk)) {
|
|
BT_ERR("Unable to store IRK");
|
|
}
|
|
} else {
|
|
BT_WARN("Using temporary IRK");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_PRIVACY */
|
|
|
|
static int bt_init(void)
|
|
{
|
|
struct bt_driver *drv = bt_dev.drv;
|
|
int err;
|
|
|
|
bt_hci_ecc_init();
|
|
|
|
err = drv->open();
|
|
if (err) {
|
|
BT_ERR("HCI driver open failed (%d)", err);
|
|
return err;
|
|
}
|
|
|
|
err = hci_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
err = bt_conn_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
err = irk_init();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
bt_monitor_send(BT_MONITOR_OPEN_INDEX, NULL, 0);
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_READY);
|
|
bt_le_scan_update(false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hci_rx_fiber(bt_ready_cb_t ready_cb)
|
|
{
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("started");
|
|
|
|
if (ready_cb) {
|
|
ready_cb(bt_init());
|
|
}
|
|
|
|
while (1) {
|
|
BT_DBG("calling fifo_get_wait");
|
|
buf = nano_fifo_get(&bt_dev.rx_queue, TICKS_UNLIMITED);
|
|
|
|
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_BLUETOOTH_CONN)
|
|
case BT_BUF_ACL_IN:
|
|
hci_acl(buf);
|
|
break;
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
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.
|
|
*/
|
|
fiber_yield();
|
|
}
|
|
}
|
|
|
|
int bt_enable(bt_ready_cb_t cb)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* Initialize the buffer pools */
|
|
net_buf_pool_init(hci_cmd_pool);
|
|
#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS)
|
|
net_buf_pool_init(hci_evt_pool);
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
net_buf_pool_init(acl_in_pool);
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */
|
|
|
|
nano_sem_init(&bt_dev.ncmd_sem);
|
|
|
|
/* 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_BLUETOOTH_WAIT_NOP)
|
|
bt_dev.ncmd = 1;
|
|
nano_task_sem_give(&bt_dev.ncmd_sem);
|
|
#endif /* !CONFIG_BLUETOOTH_WAIT_NOP */
|
|
|
|
/* TX fiber */
|
|
nano_fifo_init(&bt_dev.cmd_tx_queue);
|
|
fiber_start(cmd_tx_fiber_stack, sizeof(cmd_tx_fiber_stack),
|
|
(nano_fiber_entry_t)hci_cmd_tx_fiber, 0, 0, 7, 0);
|
|
|
|
/* RX fiber */
|
|
nano_fifo_init(&bt_dev.rx_queue);
|
|
fiber_start(rx_fiber_stack, sizeof(rx_fiber_stack),
|
|
(nano_fiber_entry_t)hci_rx_fiber, (int)cb, 0, 7, 0);
|
|
|
|
if (!cb) {
|
|
return bt_init();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool bt_addr_le_is_bonded(const bt_addr_le_t *addr)
|
|
{
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
struct bt_keys *keys = bt_keys_find_addr(addr);
|
|
|
|
/* if there are any keys stored then device is bonded */
|
|
return keys && keys->keys;
|
|
#else
|
|
return false;
|
|
#endif /* defined(CONFIG_BLUETOOTH_SMP) */
|
|
}
|
|
|
|
static bool valid_adv_param(const struct bt_le_adv_param *param)
|
|
{
|
|
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 (param->interval_min < 0x00a0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (param->interval_min > param->interval_max ||
|
|
param->interval_min < 0x0020 || param->interval_max > 0x4000) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int set_ad(uint16_t hci_op, const struct bt_data *ad, size_t ad_len)
|
|
{
|
|
struct bt_hci_cp_le_set_adv_data *set_data;
|
|
struct net_buf *buf;
|
|
int i;
|
|
|
|
buf = bt_hci_cmd_create(hci_op, sizeof(*set_data));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_data = net_buf_add(buf, sizeof(*set_data));
|
|
|
|
memset(set_data, 0, sizeof(*set_data));
|
|
|
|
for (i = 0; i < ad_len; i++) {
|
|
/* Check if ad fit in the remaining buffer */
|
|
if (set_data->len + ad[i].data_len + 2 > 31) {
|
|
net_buf_unref(buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
set_data->data[set_data->len++] = ad[i].data_len + 1;
|
|
set_data->data[set_data->len++] = ad[i].type;
|
|
|
|
memcpy(&set_data->data[set_data->len], ad[i].data,
|
|
ad[i].data_len);
|
|
set_data->len += ad[i].data_len;
|
|
}
|
|
|
|
return bt_hci_cmd_send_sync(hci_op, buf, NULL);
|
|
}
|
|
|
|
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 net_buf *buf;
|
|
struct bt_hci_cp_le_set_adv_param *set_param;
|
|
int err;
|
|
|
|
if (!valid_adv_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = set_advertise_disable();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = set_ad(BT_HCI_OP_LE_SET_ADV_DATA, ad, ad_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* We need to set SCAN_RSP when enabling advertising type that allows
|
|
* for Scan Requests.
|
|
*
|
|
* If sd was not provided but we enable connectable undirected
|
|
* advertising sd needs to be cleared from values set by previous calls.
|
|
* Clearing sd is done by calling set_ad() with NULL data and zero len.
|
|
* So following condition check is unusual but correct.
|
|
*/
|
|
if (sd || (param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
|
|
err = set_ad(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, sd, sd_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(*set_param));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_param = net_buf_add(buf, sizeof(*set_param));
|
|
|
|
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 = 0x07;
|
|
|
|
if (param->options & BT_LE_ADV_OPT_CONNECTABLE) {
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
err = le_set_rpa();
|
|
if (err) {
|
|
net_buf_unref(buf);
|
|
return err;
|
|
}
|
|
|
|
set_param->own_addr_type = BT_ADDR_LE_RANDOM;
|
|
#else
|
|
set_param->own_addr_type = bt_dev.id_addr.type;
|
|
#endif /* CONFIG_BLUETOOTH_PRIVACY */
|
|
set_param->type = BT_LE_ADV_IND;
|
|
} else {
|
|
#if defined(CONFIG_BLUETOOTH_PRIVACY)
|
|
err = le_set_nrpa();
|
|
if (err) {
|
|
net_buf_unref(buf);
|
|
return err;
|
|
}
|
|
|
|
set_param->own_addr_type = BT_ADDR_LE_RANDOM;
|
|
#else
|
|
set_param->own_addr_type = bt_dev.id_addr.type;
|
|
#endif /* CONFIG_BLUETOOTH_PRIVACY */
|
|
|
|
if (sd) {
|
|
set_param->type = BT_LE_ADV_SCAN_IND;
|
|
} else {
|
|
set_param->type = BT_LE_ADV_NONCONN_IND;
|
|
}
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_PARAM, buf, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = set_advertise_enable();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_adv_stop(void)
|
|
{
|
|
int err;
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = set_advertise_disable();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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->filter_dup != BT_HCI_LE_SCAN_FILTER_DUP_DISABLE &&
|
|
param->filter_dup != BT_HCI_LE_SCAN_FILTER_DUP_ENABLE) {
|
|
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;
|
|
|
|
/* Check that the parameters have valid values */
|
|
if (!valid_le_scan_param(param)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Return if active scan is already enabled */
|
|
if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
|
|
err = bt_hci_stop_scanning();
|
|
if (err) {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = start_le_scan(param->type, param->interval, param->window,
|
|
param->filter_dup);
|
|
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;
|
|
|
|
return bt_le_scan_update(false);
|
|
}
|
|
|
|
#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS)
|
|
struct net_buf *bt_buf_get_evt(void)
|
|
{
|
|
struct net_buf *buf;
|
|
|
|
buf = net_buf_get(&avail_hci_evt, CONFIG_BLUETOOTH_HCI_RECV_RESERVE);
|
|
if (buf) {
|
|
bt_buf_set_type(buf, BT_BUF_EVT);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
struct net_buf *bt_buf_get_acl(void)
|
|
{
|
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
|
struct net_buf *buf;
|
|
|
|
buf = net_buf_get(&avail_acl_in, CONFIG_BLUETOOTH_HCI_RECV_RESERVE);
|
|
if (buf) {
|
|
bt_buf_set_type(buf, BT_BUF_ACL_IN);
|
|
}
|
|
|
|
return buf;
|
|
#else
|
|
return NULL;
|
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
|
}
|
|
#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
|
static int br_start_inquiry(bool limited)
|
|
{
|
|
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));
|
|
|
|
/* do inquiry for maximum allowed time without results limit */
|
|
cp->length = 0x30;
|
|
cp->num_rsp = 0x00;
|
|
|
|
memcpy(cp->lap, iac, 3);
|
|
if (limited) {
|
|
cp->lap[0] = 0x00;
|
|
}
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY, buf, NULL);
|
|
}
|
|
|
|
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 (atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = br_start_inquiry(param->limited_discovery);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_INQUIRY);
|
|
|
|
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].private;
|
|
|
|
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;
|
|
}
|
|
|
|
if (scan & BT_BREDR_SCAN_INQUIRY) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_ISCAN);
|
|
} else {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_ISCAN);
|
|
}
|
|
|
|
if (scan & BT_BREDR_SCAN_PAGE) {
|
|
atomic_set_bit(bt_dev.flags, BT_DEV_PSCAN);
|
|
} else {
|
|
atomic_clear_bit(bt_dev.flags, BT_DEV_PSCAN);
|
|
}
|
|
|
|
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_BLUETOOTH_BREDR */
|
|
|
|
void bt_storage_register(const struct bt_storage *storage)
|
|
{
|
|
bt_storage = storage;
|
|
}
|
|
|
|
int bt_storage_clear(bt_addr_le_t *addr)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
uint16_t bt_hci_get_cmd_opcode(struct net_buf *buf)
|
|
{
|
|
return cmd(buf)->opcode;
|
|
}
|