blob: 5752a2fb02473d4e88f70cd7346939ff6dfe2069 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2011-2012 Intel Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
* Copyright 2023-2024 NXP
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <sys/uio.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "src/shared/util.h"
#include "src/shared/timeout.h"
#include "src/shared/crypto.h"
#include "src/shared/ecc.h"
#include "src/shared/queue.h"
#include "monitor/bt.h"
#include "monitor/msft.h"
#include "monitor/emulator.h"
#include "btdev.h"
#define AL_SIZE 16
#define RL_SIZE 16
#define CIS_SIZE 3
#define BIS_SIZE 3
#define CIG_SIZE 3
#define MAX_PA_DATA_LEN 252
#define has_bredr(btdev) (!((btdev)->features[4] & 0x20))
#define has_le(btdev) (!!((btdev)->features[4] & 0x40))
#define ACL_HANDLE 42
#define ISO_HANDLE 257
#define SCO_HANDLE 257
#define SYC_HANDLE 1
#define INV_HANDLE 0xffff
struct hook {
btdev_hook_func handler;
void *user_data;
enum btdev_hook_type type;
uint16_t opcode;
};
#define MAX_HOOK_ENTRIES 16
#define MAX_EXT_ADV_SETS 3
#define MAX_PENDING_CONN 16
struct btdev_conn {
uint16_t handle;
uint8_t type;
struct btdev *dev;
struct btdev_conn *link;
void *data;
};
struct btdev_al {
uint8_t type;
bdaddr_t addr;
};
struct btdev_rl {
uint8_t type;
bdaddr_t addr;
uint8_t mode;
uint8_t peer_irk[16];
uint8_t local_irk[16];
};
struct le_ext_adv {
struct btdev *dev;
uint8_t handle;
uint8_t enable;
uint8_t type; /* evt_properties */
uint8_t own_addr_type; /* own_addr_type */
uint8_t direct_addr_type; /* peer_addr_type */
uint8_t direct_addr[6]; /* peer_addr */
uint8_t filter_policy; /* filter_policy */
uint8_t random_addr[6];
bool rpa;
uint8_t adv_data[252];
uint8_t adv_data_len;
uint8_t scan_data[252];
uint8_t scan_data_len;
unsigned int id;
};
struct le_cig {
struct bt_hci_cmd_le_set_cig_params params;
struct bt_hci_cis_params cis[CIS_SIZE];
bool activated;
} __attribute__ ((packed));
struct btdev {
enum btdev_type type;
uint16_t id;
struct queue *conns;
bool auth_init;
uint8_t link_key[16];
uint16_t pin[16];
uint8_t pin_len;
uint8_t io_cap;
uint8_t auth_req;
bool ssp_auth_complete;
uint8_t ssp_status;
btdev_command_func command_handler;
void *command_data;
btdev_send_func send_handler;
void *send_data;
unsigned int inquiry_id;
unsigned int inquiry_timeout_id;
struct hook *hook_list[MAX_HOOK_ENTRIES];
struct bt_crypto *crypto;
uint16_t manufacturer;
uint8_t version;
uint16_t revision;
uint8_t commands[64];
uint8_t max_page;
uint8_t features[8];
uint8_t feat_page_2[8];
uint16_t acl_mtu;
uint16_t acl_max_pkt;
uint16_t sco_mtu;
uint16_t sco_max_pkt;
uint16_t iso_mtu;
uint16_t iso_max_pkt;
uint8_t country_code;
uint8_t bdaddr[6];
uint8_t random_addr[6];
uint8_t le_features[8];
uint8_t le_states[8];
const struct btdev_cmd *cmds;
uint16_t msft_opcode;
const struct btdev_cmd *msft_cmds;
uint16_t emu_opcode;
const struct btdev_cmd *emu_cmds;
bool aosp_capable;
uint16_t default_link_policy;
uint8_t event_mask[8];
uint8_t event_mask_page2[8];
uint8_t event_filter;
uint8_t name[248];
uint8_t dev_class[3];
uint16_t voice_setting;
uint16_t conn_accept_timeout;
uint16_t page_timeout;
uint8_t scan_enable;
uint16_t page_scan_interval;
uint16_t page_scan_window;
uint16_t page_scan_type;
uint8_t auth_enable;
uint16_t inquiry_scan_interval;
uint16_t inquiry_scan_window;
uint8_t inquiry_mode;
uint8_t afh_assessment_mode;
uint8_t ext_inquiry_fec;
uint8_t ext_inquiry_rsp[240];
uint8_t simple_pairing_mode;
uint8_t ssp_debug_mode;
uint8_t secure_conn_support;
uint8_t host_flow_control;
uint8_t le_supported;
uint8_t le_simultaneous;
uint8_t le_event_mask[8];
uint8_t le_adv_data[31];
uint8_t le_adv_data_len;
uint8_t le_adv_type;
uint8_t le_adv_own_addr;
uint8_t le_adv_direct_addr_type;
uint8_t le_adv_direct_addr[6];
uint8_t le_adv_filter_policy;
uint8_t le_scan_data[31];
uint8_t le_scan_data_len;
uint8_t le_scan_enable;
uint8_t le_scan_type;
uint8_t le_scan_own_addr_type;
uint8_t le_scan_filter_policy;
uint8_t le_filter_dup;
uint8_t le_adv_enable;
uint8_t le_pa_enable;
uint16_t le_pa_properties;
uint16_t le_pa_min_interval;
uint16_t le_pa_max_interval;
uint8_t le_pa_data_len;
uint8_t le_pa_data[MAX_PA_DATA_LEN];
struct bt_hci_cmd_le_pa_create_sync pa_sync_cmd;
uint16_t le_pa_sync_handle;
uint8_t big_handle;
uint8_t le_ltk[16];
struct le_cig le_cig[CIG_SIZE];
uint8_t le_iso_path[2];
/* Real time length of AL array */
uint8_t le_al_len;
/* Real time length of RL array */
uint8_t le_rl_len;
struct btdev_al le_al[AL_SIZE];
struct btdev_rl le_rl[RL_SIZE];
uint8_t le_rl_enable;
uint16_t le_rl_timeout;
struct btdev *pending_conn[MAX_PENDING_CONN];
uint8_t le_local_sk256[32];
uint16_t sync_train_interval;
uint32_t sync_train_timeout;
uint8_t sync_train_service_data;
uint16_t le_ext_adv_type;
struct queue *le_ext_adv;
btdev_debug_func_t debug_callback;
btdev_destroy_func_t debug_destroy;
void *debug_data;
};
struct inquiry_data {
struct btdev *btdev;
int num_resp;
int sent_count;
int iter;
};
#define DEFAULT_INQUIRY_INTERVAL 100 /* 100 miliseconds */
#define MAX_BTDEV_ENTRIES 16
static const uint8_t LINK_KEY_NONE[16] = { 0 };
static const uint8_t LINK_KEY_DUMMY[16] = { 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 0, 1, 2, 3, 4, 5 };
static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
static int get_hook_index(struct btdev *btdev, enum btdev_hook_type type,
uint16_t opcode)
{
int i;
for (i = 0; i < MAX_HOOK_ENTRIES; i++) {
if (btdev->hook_list[i] == NULL)
continue;
if (btdev->hook_list[i]->type == type &&
btdev->hook_list[i]->opcode == opcode)
return i;
}
return -1;
}
static bool run_hooks(struct btdev *btdev, enum btdev_hook_type type,
uint16_t opcode, const void *data, uint16_t len)
{
int index = get_hook_index(btdev, type, opcode);
if (index < 0)
return true;
return btdev->hook_list[index]->handler(data, len,
btdev->hook_list[index]->user_data);
}
static inline int add_btdev(struct btdev *btdev)
{
int i, index = -1;
for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
if (btdev_list[i] == NULL) {
index = i;
btdev_list[index] = btdev;
break;
}
}
return index;
}
static inline int del_btdev(struct btdev *btdev)
{
int i, index = -1;
for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
if (btdev_list[i] == btdev) {
index = i;
btdev_list[index] = NULL;
break;
}
}
return index;
}
static inline bool valid_btdev(struct btdev *btdev)
{
int i;
for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
if (btdev_list[i] == btdev)
return true;
}
return false;
}
static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr)
{
int i;
for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6))
return btdev_list[i];
}
return NULL;
}
static bool match_adv_addr(const void *data, const void *match_data)
{
const struct le_ext_adv *adv = data;
const uint8_t *bdaddr = match_data;
return !memcmp(adv->random_addr, bdaddr, 6);
}
static inline struct btdev *find_btdev_by_bdaddr_type(const uint8_t *bdaddr,
uint8_t bdaddr_type)
{
int i;
for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
struct btdev *dev = btdev_list[i];
int cmp;
struct le_ext_adv *adv;
if (!dev)
continue;
if (bdaddr_type == 0x01)
cmp = memcmp(dev->random_addr, bdaddr, 6);
else
cmp = memcmp(dev->bdaddr, bdaddr, 6);
if (!cmp)
return dev;
/* Check for instance own Random addresses */
if (bdaddr_type == 0x01) {
adv = queue_find(dev->le_ext_adv, match_adv_addr,
bdaddr);
if (adv)
return dev;
}
}
return NULL;
}
static void get_bdaddr(uint16_t id, uint8_t index, uint8_t *bdaddr)
{
bdaddr[0] = id & 0xff;
bdaddr[1] = id >> 8;
bdaddr[2] = index;
bdaddr[3] = 0x01;
bdaddr[4] = 0xaa;
bdaddr[5] = 0x00;
}
struct btdev_cmd {
uint16_t opcode;
int (*func)(struct btdev *dev, const void *data, uint8_t len);
int (*complete)(struct btdev *dev, const void *data, uint8_t len);
};
#define CMD(_opcode, _func, _complete) \
{ \
.opcode = _opcode, \
.func = _func, \
.complete = _complete, \
}
static void send_packet(struct btdev *btdev, const struct iovec *iov,
int iovlen)
{
int i;
if (!btdev->send_handler)
return;
for (i = 0; i < iovlen; i++) {
if (!i)
util_hexdump('<', iov[i].iov_base, iov[i].iov_len,
btdev->debug_callback, btdev->debug_data);
else
util_hexdump(' ', iov[i].iov_base, iov[i].iov_len,
btdev->debug_callback, btdev->debug_data);
}
btdev->send_handler(iov, iovlen, btdev->send_data);
}
static void send_cmd(struct btdev *btdev, uint8_t evt, uint16_t opcode,
const struct iovec *iov, int iovlen)
{
struct bt_hci_evt_hdr hdr;
struct iovec iov2[2 + iovlen];
uint8_t pkt = BT_H4_EVT_PKT;
int i;
util_debug(btdev->debug_callback, btdev->debug_data,
"event 0x%02x opcode 0x%04x", evt, opcode);
iov2[0].iov_base = &pkt;
iov2[0].iov_len = sizeof(pkt);
hdr.evt = evt;
hdr.plen = 0;
iov2[1].iov_base = &hdr;
iov2[1].iov_len = sizeof(hdr);
for (i = 0; i < iovlen; i++) {
hdr.plen += iov[i].iov_len;
iov2[2 + i].iov_base = iov[i].iov_base;
iov2[2 + i].iov_len = iov[i].iov_len;
}
if (run_hooks(btdev, BTDEV_HOOK_POST_CMD, opcode, iov[i -1].iov_base,
iov[i -1].iov_len))
send_packet(btdev, iov2, 2 + iovlen);
}
static void cmd_complete(struct btdev *btdev, uint16_t opcode,
const void *data, uint8_t len)
{
struct bt_hci_evt_cmd_complete cc;
struct iovec iov[2];
cc.ncmd = 0x01;
cc.opcode = cpu_to_le16(opcode);
iov[0].iov_base = &cc;
iov[0].iov_len = sizeof(cc);
iov[1].iov_base = (void *) data;
iov[1].iov_len = len;
send_cmd(btdev, BT_HCI_EVT_CMD_COMPLETE, opcode, iov, 2);
}
static int cmd_set_event_mask(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_set_event_mask *cmd = data;
uint8_t status;
memcpy(dev->event_mask, cmd->mask, 8);
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status));
return 0;
}
static void al_reset(struct btdev_al *al)
{
al->type = 0xff;
bacpy(&al->addr, BDADDR_ANY);
}
static void al_clear(struct btdev *dev)
{
int i;
for (i = 0; i < AL_SIZE; i++)
al_reset(&dev->le_al[i]);
}
static void rl_reset(struct btdev_rl *rl)
{
rl->type = 0xff;
bacpy(&rl->addr, BDADDR_ANY);
memset(rl->peer_irk, 0, 16);
memset(rl->local_irk, 0, 16);
}
static void rl_clear(struct btdev *dev)
{
int i;
for (i = 0; i < RL_SIZE; i++)
rl_reset(&dev->le_rl[i]);
}
/* Set the real time length of AL array */
void btdev_set_al_len(struct btdev *btdev, uint8_t len)
{
btdev->le_al_len = len;
}
/* Set the real time length of RL array */
void btdev_set_rl_len(struct btdev *btdev, uint8_t len)
{
btdev->le_rl_len = len;
}
static void conn_unlink(struct btdev_conn *conn1, struct btdev_conn *conn2)
{
conn1->link = NULL;
conn2->link = NULL;
}
static void conn_remove(void *data)
{
struct btdev_conn *conn = data;
if (conn->link) {
struct btdev_conn *link = conn->link;
conn_unlink(conn, conn->link);
conn_remove(link);
}
queue_remove(conn->dev->conns, conn);
free(conn->data);
free(conn);
}
static void le_ext_adv_free(void *data)
{
struct le_ext_adv *ext_adv = data;
/* Remove to queue */
queue_remove(ext_adv->dev->le_ext_adv, ext_adv);
if (ext_adv->id)
timeout_remove(ext_adv->id);
free(ext_adv);
}
static void btdev_reset(struct btdev *btdev)
{
/* FIXME: include here clearing of all states that should be
* cleared upon HCI_Reset
*/
btdev->le_scan_enable = 0x00;
btdev->le_adv_enable = 0x00;
btdev->le_pa_enable = 0x00;
btdev->le_pa_sync_handle = 0x0000;
btdev->big_handle = 0xff;
al_clear(btdev);
rl_clear(btdev);
btdev->le_al_len = AL_SIZE;
btdev->le_rl_len = RL_SIZE;
queue_remove_all(btdev->conns, NULL, NULL, conn_remove);
queue_remove_all(btdev->le_ext_adv, NULL, NULL, le_ext_adv_free);
}
static int cmd_reset(struct btdev *dev, const void *data, uint8_t len)
{
uint8_t status;
btdev_reset(dev);
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_RESET, &status, sizeof(status));
return 0;
}
static int cmd_read_local_version(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_local_version rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.hci_ver = dev->version;
rsp.hci_rev = cpu_to_le16(dev->revision);
rsp.lmp_ver = dev->version;
rsp.manufacturer = cpu_to_le16(dev->manufacturer);
rsp.lmp_subver = cpu_to_le16(dev->revision);
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_local_commands(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_local_commands rsp;
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.commands, dev->commands, 64);
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_local_features(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_local_features rsp;
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.features, dev->features, 8);
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_buffer_size(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_buffer_size rsp;
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.acl_mtu = cpu_to_le16(dev->acl_mtu);
rsp.sco_mtu = cpu_to_le16(dev->sco_mtu);
rsp.acl_max_pkt = cpu_to_le16(dev->acl_max_pkt);
rsp.sco_max_pkt = cpu_to_le16(dev->sco_max_pkt);
cmd_complete(dev, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
return 0;
}
#define CMD_COMMON_ALL \
CMD(BT_HCI_CMD_SET_EVENT_MASK, cmd_set_event_mask, NULL), \
CMD(BT_HCI_CMD_RESET, cmd_reset, NULL), \
CMD(BT_HCI_CMD_READ_LOCAL_VERSION, cmd_read_local_version, NULL), \
CMD(BT_HCI_CMD_READ_LOCAL_COMMANDS, cmd_read_local_commands, NULL), \
CMD(BT_HCI_CMD_READ_LOCAL_FEATURES, cmd_read_local_features, NULL), \
CMD(BT_HCI_CMD_READ_BUFFER_SIZE, cmd_read_buffer_size, NULL)
static void set_common_commands_all(struct btdev *btdev)
{
btdev->commands[5] |= 0x40; /* Set Event Mask */
btdev->commands[5] |= 0x80; /* Reset */
btdev->commands[14] |= 0x08; /* Read Local Version */
btdev->commands[14] |= 0x10; /* Read Local Supported Commands */
btdev->commands[14] |= 0x20; /* Read Local Supported Features */
btdev->commands[14] |= 0x80; /* Read Buffer Size */
}
static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode)
{
struct bt_hci_evt_cmd_status cs;
struct iovec iov;
cs.status = status;
cs.ncmd = 0x01;
cs.opcode = cpu_to_le16(opcode);
iov.iov_base = &cs;
iov.iov_len = sizeof(cs);
send_cmd(btdev, BT_HCI_EVT_CMD_STATUS, opcode, &iov, 1);
}
static int cmd_disconnect(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_DISCONNECT);
return 0;
}
static void send_event(struct btdev *btdev, uint8_t event,
const void *data, uint8_t len)
{
struct bt_hci_evt_hdr hdr;
struct iovec iov[3];
uint8_t pkt = BT_H4_EVT_PKT;
util_debug(btdev->debug_callback, btdev->debug_data,
"event 0x%02x", event);
iov[0].iov_base = &pkt;
iov[0].iov_len = sizeof(pkt);
hdr.evt = event;
hdr.plen = len;
iov[1].iov_base = &hdr;
iov[1].iov_len = sizeof(hdr);
if (len > 0) {
iov[2].iov_base = (void *) data;
iov[2].iov_len = len;
}
if (run_hooks(btdev, BTDEV_HOOK_POST_EVT, event, data, len))
send_packet(btdev, iov, len > 0 ? 3 : 2);
}
static bool match_handle(const void *data, const void *match_data)
{
const struct btdev_conn *conn = data;
uint16_t handle = PTR_TO_UINT(match_data);
return conn->handle == handle;
}
static void disconnect_complete(struct btdev *dev, uint16_t handle,
uint8_t status, uint8_t reason)
{
struct bt_hci_evt_disconnect_complete rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = status;
rsp.handle = cpu_to_le16(handle);
rsp.reason = reason;
send_event(dev, BT_HCI_EVT_DISCONNECT_COMPLETE, &rsp, sizeof(rsp));
}
static int cmd_disconnect_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_disconnect *cmd = data;
struct bt_hci_evt_disconnect_complete rsp;
struct btdev_conn *conn;
memset(&rsp, 0, sizeof(rsp));
conn = queue_remove_if(dev->conns, match_handle,
UINT_TO_PTR(cpu_to_le16(cmd->handle)));
if (!conn) {
disconnect_complete(dev, 0x0000, BT_HCI_ERR_UNKNOWN_CONN_ID,
0x00);
return 0;
}
/* Local host has different reason (Core v5.3 Vol 4 Part E Sec 7.1.6) */
disconnect_complete(dev, conn->handle, BT_HCI_ERR_SUCCESS,
BT_HCI_ERR_LOCAL_HOST_TERM);
if (conn->link)
disconnect_complete(conn->link->dev, conn->link->handle,
BT_HCI_ERR_SUCCESS, cmd->reason);
conn_remove(conn);
return 0;
}
static int cmd_remote_version(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_REMOTE_VERSION);
return 0;
}
static int cmd_remote_version_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_read_remote_version *cmd = data;
struct bt_hci_evt_remote_version_complete ev;
struct btdev_conn *conn;
memset(&ev, 0, sizeof(ev));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(cpu_to_le16(cmd->handle)));
if (conn) {
ev.status = BT_HCI_ERR_SUCCESS;
ev.handle = cpu_to_le16(cmd->handle);
ev.lmp_ver = conn->link->dev->version;
ev.manufacturer = cpu_to_le16(conn->link->dev->manufacturer);
ev.lmp_subver = cpu_to_le16(conn->link->dev->revision);
} else {
ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
ev.handle = cpu_to_le16(cmd->handle);
ev.lmp_ver = 0x00;
ev.manufacturer = cpu_to_le16(0);
ev.lmp_subver = cpu_to_le16(0);
}
send_event(dev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE, &ev, sizeof(ev));
return 0;
}
static int cmd_set_host_flowctl(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_set_host_flow_control *cmd = data;
uint8_t status;
if (cmd->enable > 0x03) {
status = BT_HCI_ERR_INVALID_PARAMETERS;
} else {
dev->host_flow_control = cmd->enable;
status = BT_HCI_ERR_SUCCESS;
}
cmd_complete(dev, BT_HCI_CMD_SET_HOST_FLOW_CONTROL, &status,
sizeof(status));
return 0;
}
static int cmd_host_buffer_size(struct btdev *dev, const void *data,
uint8_t len)
{
uint8_t status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_HOST_BUFFER_SIZE, &status, sizeof(status));
return 0;
}
static int cmd_host_num_completed_pkts(struct btdev *dev, const void *data,
uint8_t len)
{
/* This command is special in the sense that no event is
* normally generated after the command has completed.
*/
return 0;
}
static int cmd_read_bdaddr(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_bd_addr rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, dev->bdaddr, 6);
cmd_complete(dev, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp));
return 0;
}
#define CMD_COMMON_BREDR_LE \
CMD(BT_HCI_CMD_DISCONNECT, cmd_disconnect, cmd_disconnect_complete), \
CMD(BT_HCI_CMD_READ_REMOTE_VERSION, cmd_remote_version, \
cmd_remote_version_complete), \
CMD(BT_HCI_CMD_SET_HOST_FLOW_CONTROL, cmd_set_host_flowctl, NULL), \
CMD(BT_HCI_CMD_HOST_BUFFER_SIZE, cmd_host_buffer_size, NULL), \
CMD(BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS, \
cmd_host_num_completed_pkts, NULL), \
CMD(BT_HCI_CMD_READ_BD_ADDR, cmd_read_bdaddr, NULL)
static void set_common_commands_bredrle(struct btdev *btdev)
{
btdev->commands[0] |= 0x20; /* Disconnect */
btdev->commands[2] |= 0x80; /* Read Remote Version Information */
btdev->commands[10] |= 0x20; /* Set Host Flow Control */
btdev->commands[10] |= 0x40; /* Host Buffer Size */
btdev->commands[15] |= 0x02; /* Read BD ADDR */
}
static int cmd_inquiry(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_INQUIRY);
return 0;
}
static bool inquiry_callback(void *user_data)
{
struct inquiry_data *data = user_data;
struct btdev *btdev = data->btdev;
struct bt_hci_evt_inquiry_complete ic;
int sent = data->sent_count;
int i;
/*Report devices only once and wait for inquiry timeout*/
if (data->iter == MAX_BTDEV_ENTRIES)
return true;
for (i = data->iter; i < MAX_BTDEV_ENTRIES; i++) {
/*Lets sent 10 inquiry results at once */
if (sent + 10 == data->sent_count)
break;
if (!btdev_list[i] || btdev_list[i] == btdev)
continue;
if (!(btdev_list[i]->scan_enable & 0x02))
continue;
if (btdev->inquiry_mode == 0x02 &&
btdev_list[i]->ext_inquiry_rsp[0]) {
struct bt_hci_evt_ext_inquiry_result ir;
ir.num_resp = 0x01;
memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
ir.pscan_rep_mode = 0x00;
ir.pscan_period_mode = 0x00;
memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
ir.clock_offset = 0x0000;
ir.rssi = -60;
memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240);
send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
&ir, sizeof(ir));
data->sent_count++;
continue;
}
if (btdev->inquiry_mode > 0x00) {
struct bt_hci_evt_inquiry_result_with_rssi ir;
ir.num_resp = 0x01;
memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
ir.pscan_rep_mode = 0x00;
ir.pscan_period_mode = 0x00;
memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
ir.clock_offset = 0x0000;
ir.rssi = -60;
send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
&ir, sizeof(ir));
data->sent_count++;
} else {
struct bt_hci_evt_inquiry_result ir;
ir.num_resp = 0x01;
memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
ir.pscan_rep_mode = 0x00;
ir.pscan_period_mode = 0x00;
ir.pscan_mode = 0x00;
memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
ir.clock_offset = 0x0000;
send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
&ir, sizeof(ir));
data->sent_count++;
}
}
data->iter = i;
/* Check if we sent already required amount of responses*/
if (data->num_resp && data->sent_count == data->num_resp)
goto finish;
return true;
finish:
/* Note that destroy will be called */
ic.status = BT_HCI_ERR_SUCCESS;
send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
return false;
}
static void inquiry_destroy(void *user_data)
{
struct inquiry_data *data = user_data;
struct btdev *btdev = data->btdev;
if (!btdev)
goto finish;
btdev->inquiry_id = 0;
if (btdev->inquiry_timeout_id > 0) {
timeout_remove(btdev->inquiry_timeout_id);
btdev->inquiry_timeout_id = 0;
}
finish:
free(data);
}
static bool inquiry_timeout(void *user_data)
{
struct inquiry_data *data = user_data;
struct btdev *btdev = data->btdev;
struct bt_hci_evt_inquiry_complete ic;
timeout_remove(btdev->inquiry_id);
btdev->inquiry_timeout_id = 0;
/* Inquiry is stopped, send Inquiry complete event. */
ic.status = BT_HCI_ERR_SUCCESS;
send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
return false;
}
static int cmd_inquiry_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_inquiry *cmd = data;
struct inquiry_data *idata;
struct bt_hci_evt_inquiry_complete ic;
int status = BT_HCI_ERR_HARDWARE_FAILURE;
unsigned int inquiry_len_ms;
if (dev->inquiry_id > 0) {
status = BT_HCI_ERR_COMMAND_DISALLOWED;
goto failed;
}
idata = malloc0(sizeof(*idata));
if (!idata)
goto failed;
idata->btdev = dev;
idata->num_resp = cmd->num_resp;
/* Add timeout to cancel inquiry */
inquiry_len_ms = 1280 * cmd->length;
if (inquiry_len_ms)
dev->inquiry_timeout_id = timeout_add(inquiry_len_ms,
inquiry_timeout,
idata, NULL);
dev->inquiry_id = timeout_add(DEFAULT_INQUIRY_INTERVAL,
inquiry_callback, idata,
inquiry_destroy);
/* Return if success */
if (dev->inquiry_id > 0)
return 0;
failed:
ic.status = status;
send_event(dev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
return 0;
}
static int cmd_inquiry_cancel(struct btdev *dev, const void *data, uint8_t len)
{
uint8_t status;
if (!dev->inquiry_id) {
status = BT_HCI_ERR_COMMAND_DISALLOWED;
goto done;
}
timeout_remove(dev->inquiry_timeout_id);
dev->inquiry_timeout_id = 0;
timeout_remove(dev->inquiry_id);
dev->inquiry_id = 0;
status = BT_HCI_ERR_SUCCESS;
done:
cmd_complete(dev, BT_HCI_CMD_INQUIRY_CANCEL, &status, sizeof(status));
return 0;
}
static int cmd_create_conn(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_CREATE_CONN);
return 0;
}
static struct btdev_conn *conn_new(struct btdev *dev, uint16_t handle,
uint8_t type)
{
struct btdev_conn *conn;
while ((conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(handle))))
handle++;
conn = new0(struct btdev_conn, 1);
conn->handle = handle;
conn->type = type;
conn->dev = dev;
if (!queue_push_tail(dev->conns, conn)) {
free(conn);
return NULL;
}
return conn;
}
static struct btdev_conn *conn_link(struct btdev *dev, struct btdev *remote,
uint16_t handle, uint8_t type)
{
struct btdev_conn *conn1, *conn2;
conn1 = conn_new(dev, handle, type);
if (!conn1)
return NULL;
conn2 = conn_new(remote, handle, type);
if (!conn2) {
free(conn1);
return NULL;
}
conn1->link = conn2;
conn2->link = conn1;
util_debug(dev->debug_callback, dev->debug_data,
"conn1 %p handle 0x%04x", conn1, conn1->handle);
util_debug(dev->debug_callback, dev->debug_data,
"conn2 %p handle 0x%04x", conn2, conn2->handle);
return conn1;
}
static struct btdev_conn *conn_add(struct btdev *dev,
const uint8_t *bdaddr, uint8_t bdaddr_type,
uint16_t handle, uint8_t type)
{
struct btdev *remote;
remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type);
if (!remote)
return NULL;
return conn_link(dev, remote, handle, type);
}
static struct btdev_conn *conn_add_acl(struct btdev *dev,
const uint8_t *bdaddr, uint8_t bdaddr_type)
{
return conn_add(dev, bdaddr, bdaddr_type, ACL_HANDLE, HCI_ACLDATA_PKT);
}
static struct btdev_conn *conn_add_sco(struct btdev_conn *acl)
{
return conn_link(acl->dev, acl->link->dev, SCO_HANDLE, HCI_SCODATA_PKT);
}
static struct btdev_conn *conn_add_cis(struct btdev_conn *acl, uint16_t handle)
{
return conn_link(acl->dev, acl->link->dev, handle, HCI_ISODATA_PKT);
}
static struct btdev_conn *conn_add_bis(struct btdev *dev, uint16_t handle,
const struct bt_hci_bis *bis)
{
struct btdev_conn *conn;
conn = conn_new(dev, handle, HCI_ISODATA_PKT);
if (!conn)
return conn;
conn->data = util_memdup(bis, sizeof(*bis));
return conn;
}
static struct btdev_conn *find_bis_index(const struct btdev *remote,
uint8_t index)
{
struct btdev_conn *conn;
const struct queue_entry *entry;
for (entry = queue_get_entries(remote->conns); entry;
entry = entry->next) {
conn = entry->data;
/* Skip if not a broadcast */
if (conn->type != HCI_ISODATA_PKT || conn->link)
continue;
if (!index)
return conn;
index--;
}
return NULL;
}
static struct btdev_conn *conn_link_bis(struct btdev *dev, struct btdev *remote,
uint8_t index)
{
struct btdev_conn *conn;
struct btdev_conn *bis;
bis = find_bis_index(remote, index);
if (!bis)
return NULL;
conn = conn_add_bis(dev, ISO_HANDLE, bis->data);
if (!conn)
return NULL;
bis->link = conn;
conn->link = bis;
util_debug(dev->debug_callback, dev->debug_data,
"bis %p handle 0x%04x", bis, bis->handle);
util_debug(dev->debug_callback, dev->debug_data,
"conn %p handle 0x%04x", conn, conn->handle);
return conn;
}
static void pending_conn_add(struct btdev *btdev, struct btdev *remote)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) {
if (!btdev->pending_conn[i]) {
btdev->pending_conn[i] = remote;
return;
}
}
}
static bool pending_conn_del(struct btdev *btdev, struct btdev *remote)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) {
if (btdev->pending_conn[i] == remote) {
btdev->pending_conn[i] = NULL;
return true;
}
}
return false;
}
static void conn_complete(struct btdev *btdev,
const uint8_t *bdaddr, uint8_t status)
{
struct bt_hci_evt_conn_complete cc;
struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
if (!remote)
return;
if (!status) {
struct btdev_conn *conn;
conn = conn_add_acl(btdev, bdaddr, BDADDR_BREDR);
if (!conn)
return;
pending_conn_del(conn->link->dev, btdev);
cc.status = status;
memcpy(cc.bdaddr, btdev->bdaddr, 6);
cc.encr_mode = 0x00;
cc.handle = cpu_to_le16(conn->link->handle);
cc.link_type = 0x01;
send_event(conn->link->dev, BT_HCI_EVT_CONN_COMPLETE, &cc,
sizeof(cc));
cc.handle = cpu_to_le16(conn->handle);
cc.link_type = 0x01;
} else {
cc.handle = cpu_to_le16(0x0000);
cc.link_type = 0x01;
}
pending_conn_del(btdev, remote);
cc.status = status;
memcpy(cc.bdaddr, bdaddr, 6);
cc.encr_mode = 0x00;
send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
}
struct page_timeout_data {
struct btdev *btdev;
uint8_t bdaddr[6];
unsigned int timeout_id;
};
static bool page_timeout(void *user_data)
{
struct page_timeout_data *pt_data = user_data;
struct btdev *btdev = pt_data->btdev;
const uint8_t *bdaddr = pt_data->bdaddr;
timeout_remove(pt_data->timeout_id);
pt_data->timeout_id = 0;
if (valid_btdev(btdev))
conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
free(pt_data);
return false;
}
static int cmd_create_conn_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_create_conn *cmd = data;
struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr);
if (remote && remote->scan_enable & 0x02) {
struct bt_hci_evt_conn_request cr;
memcpy(cr.bdaddr, dev->bdaddr, 6);
memcpy(cr.dev_class, dev->dev_class, 3);
cr.link_type = 0x01;
pending_conn_add(dev, remote);
send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr));
} else {
struct page_timeout_data *pt_data =
new0(struct page_timeout_data, 1);
pt_data->btdev = dev;
memcpy(pt_data->bdaddr, cmd->bdaddr, 6);
/* Send page timeout after 5.12 seconds to emulate real
* paging.
*/
pt_data->timeout_id = timeout_add(5120,
page_timeout,
pt_data, NULL);
}
return 0;
}
static int cmd_add_sco_conn(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_add_sco_conn *cmd = data;
struct bt_hci_evt_conn_complete cc;
struct btdev_conn *conn;
memset(&cc, 0, sizeof(cc));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(cpu_to_le16(cmd->handle)));
if (!conn) {
cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
goto done;
}
conn = conn_add_sco(conn);
if (!conn) {
cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
goto done;
}
cc.status = BT_HCI_ERR_SUCCESS;
memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6);
cc.handle = cpu_to_le16(conn->handle);
cc.link_type = 0x00;
cc.encr_mode = 0x00;
done:
pending_conn_del(dev, conn->link->dev);
send_event(dev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
return 0;
}
static bool match_bdaddr(const void *data, const void *match_data)
{
const struct btdev_conn *conn = data;
const uint8_t *bdaddr = match_data;
return !memcmp(conn->link->dev->bdaddr, bdaddr, 6);
}
static int cmd_create_conn_cancel(struct btdev *dev, const void *data,
uint8_t len)
{
return 0;
}
static int cmd_create_conn_cancel_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_create_conn_cancel *cmd = data;
struct bt_hci_rsp_create_conn_cancel rp;
struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr);
struct btdev_conn *conn;
/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E page 1848
*
* If the connection is already established, and the
* HCI_Connection_Complete event has been sent, then the Controller
* shall return an HCI_Command_Complete event with the error code
* Connection Already Exists (0x0B). If the HCI_Create_Connection_Cancel
* command is sent to the Controller without a preceding
* HCI_Create_Connection command to the same device, the BR/EDR
* Controller shall return an HCI_Command_Complete event with the error
* code Unknown Connection Identifier (0x02).
*/
if (pending_conn_del(dev, remote)) {
rp.status = BT_HCI_ERR_SUCCESS;
} else {
conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
if (conn)
rp.status = BT_HCI_ERR_CONN_ALREADY_EXISTS;
else
rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
}
memcpy(rp.bdaddr, cmd->bdaddr, sizeof(rp.bdaddr));
cmd_complete(dev, BT_HCI_CMD_CREATE_CONN_CANCEL, &rp, sizeof(rp));
if (!rp.status)
conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
return 0;
}
static int cmd_accept_conn(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_ACCEPT_CONN_REQUEST);
return 0;
}
static int cmd_accept_conn_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_accept_conn_request *cmd = data;
struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr);
if (!remote)
return 0;
if (dev->auth_enable || remote->auth_enable)
send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST, dev->bdaddr, 6);
else
conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_SUCCESS);
return 0;
}
static int cmd_reject_conn(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_REJECT_CONN_REQUEST);
return 0;
}
static int cmd_reject_conn_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_reject_conn_request *cmd = data;
conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
return 0;
}
static int cmd_link_key_reply(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_link_key_request_reply rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, data, 6);
cmd_complete(dev, BT_HCI_CMD_LINK_KEY_REQUEST_REPLY, &rsp, sizeof(rsp));
return 0;
}
static void auth_complete(struct btdev_conn *conn, uint8_t status)
{
struct bt_hci_evt_auth_complete ev;
memset(&ev, 0, sizeof(ev));
ev.handle = conn ? cpu_to_le16(conn->handle) : 0x0000;
ev.status = status;
send_event(conn->dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
conn->dev->ssp_status = 0;
conn->dev->ssp_auth_complete = false;
conn->link->dev->ssp_status = 0;
conn->link->dev->ssp_auth_complete = false;
}
static int cmd_link_key_reply_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_link_key_request_reply *cmd = data;
struct btdev_conn *conn;
uint8_t status;
conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
if (!conn) {
status = BT_HCI_ERR_INVALID_PARAMETERS;
goto done;
}
memcpy(dev->link_key, cmd->link_key, 16);
if (!memcmp(conn->link->dev->link_key, LINK_KEY_NONE, 16)) {
send_event(conn->link->dev, BT_HCI_EVT_LINK_KEY_REQUEST,
dev->bdaddr, 6);
return 0;
}
if (!memcmp(dev->link_key, conn->link->dev->link_key, 16))
status = BT_HCI_ERR_SUCCESS;
else
status = BT_HCI_ERR_AUTH_FAILURE;
done:
auth_complete(conn, status);
if (conn)
auth_complete(conn->link, status);
return 0;
}
static int cmd_link_key_neg_reply(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_link_key_request_neg_reply rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, data, 6);
cmd_complete(dev, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, &rsp,
sizeof(rsp));
return 0;
}
static bool use_ssp(struct btdev *btdev1, struct btdev *btdev2)
{
if (btdev1->auth_enable || btdev2->auth_enable)
return false;
return (btdev1->simple_pairing_mode && btdev2->simple_pairing_mode);
}
static int cmd_link_key_neg_reply_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_link_key_request_neg_reply *cmd = data;
struct btdev *remote;
remote = find_btdev_by_bdaddr(cmd->bdaddr);
if (!remote)
return 0;
if (use_ssp(dev, remote)) {
struct bt_hci_evt_io_capability_request io_req;
memcpy(io_req.bdaddr, cmd->bdaddr, 6);
send_event(dev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, &io_req,
sizeof(io_req));
} else {
struct bt_hci_evt_pin_code_request pin_req;
memcpy(pin_req.bdaddr, cmd->bdaddr, 6);
send_event(dev, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req,
sizeof(pin_req));
}
return 0;
}
static int cmd_pin_code_reply(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_pin_code_request_neg_reply rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, data, 6);
cmd_complete(dev, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, &rsp, sizeof(rsp));
return 0;
}
static uint8_t get_link_key_type(struct btdev *btdev, const uint8_t *bdaddr)
{
struct btdev_conn *conn;
uint8_t auth, unauth;
conn = queue_find(btdev->conns, match_bdaddr, bdaddr);
if (!conn)
return 0x00;
if (!btdev->simple_pairing_mode)
return 0x00;
if (btdev->ssp_debug_mode || conn->link->dev->ssp_debug_mode)
return 0x03;
if (btdev->secure_conn_support &&
conn->link->dev->secure_conn_support) {
unauth = 0x07;
auth = 0x08;
} else {
unauth = 0x04;
auth = 0x05;
}
if (btdev->io_cap == 0x03 || conn->link->dev->io_cap == 0x03)
return unauth;
if (!(btdev->auth_req & 0x01) && !(conn->link->dev->auth_req & 0x01))
return unauth;
/* DisplayOnly only produces authenticated with KeyboardOnly */
if (btdev->io_cap == 0x00 && conn->link->dev->io_cap != 0x02)
return unauth;
/* DisplayOnly only produces authenticated with KeyboardOnly */
if (conn->link->dev->io_cap == 0x00 && btdev->io_cap != 0x02)
return unauth;
return auth;
}
static void link_key_notify(struct btdev *btdev, const uint8_t *bdaddr,
const uint8_t *key)
{
struct bt_hci_evt_link_key_notify ev;
memcpy(btdev->link_key, key, 16);
memcpy(ev.bdaddr, bdaddr, 6);
memcpy(ev.link_key, key, 16);
ev.key_type = get_link_key_type(btdev, bdaddr);
send_event(btdev, BT_HCI_EVT_LINK_KEY_NOTIFY, &ev, sizeof(ev));
}
static int cmd_pin_code_reply_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_pin_code_request_reply *cmd = data;
struct btdev *remote;
struct btdev_conn *conn;
uint8_t status;
conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
if (!conn) {
remote = find_btdev_by_bdaddr(cmd->bdaddr);
if (!remote)
return 0;
} else
remote = conn->link->dev;
memcpy(dev->pin, cmd->pin_code, cmd->pin_len);
dev->pin_len = cmd->pin_len;
if (!remote->pin_len) {
struct bt_hci_evt_pin_code_request pin_req;
memcpy(pin_req.bdaddr, dev->bdaddr, 6);
send_event(remote, BT_HCI_EVT_PIN_CODE_REQUEST,
&pin_req, sizeof(pin_req));
return 0;
}
if (dev->pin_len == remote->pin_len &&
!memcmp(dev->pin, remote->pin, dev->pin_len)) {
link_key_notify(dev, remote->bdaddr, LINK_KEY_DUMMY);
link_key_notify(remote, dev->bdaddr, LINK_KEY_DUMMY);
status = BT_HCI_ERR_SUCCESS;
} else {
status = BT_HCI_ERR_AUTH_FAILURE;
}
if (conn)
auth_complete(conn->link, status);
else
conn_complete(remote, dev->bdaddr, status);
dev->pin_len = 0;
remote->pin_len = 0;
return 0;
}
static int cmd_pin_code_neg_reply(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_pin_code_request_neg_reply rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, data, 6);
cmd_complete(dev, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_pin_code_neg_reply_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_pin_code_request_neg_reply *cmd = data;
struct btdev *remote;
struct btdev_conn *conn;
uint8_t status;
remote = find_btdev_by_bdaddr(cmd->bdaddr);
if (!remote)
return 0;
status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
if (conn)
auth_complete(conn, status);
else
conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING);
if (conn) {
if (remote->pin_len)
auth_complete(conn->link, status);
} else {
conn_complete(remote, dev->bdaddr,
BT_HCI_ERR_PIN_OR_KEY_MISSING);
}
return 0;
}
static int cmd_auth_requested(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_AUTH_REQUESTED);
return 0;
}
static int cmd_auth_requested_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_auth_requested *cmd = data;
struct btdev_conn *conn;
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (!conn) {
struct bt_hci_evt_auth_complete ev;
ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
ev.handle = cpu_to_le16(cmd->handle);
send_event(dev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
return 0;
}
dev->auth_init = true;
send_event(dev, BT_HCI_EVT_LINK_KEY_REQUEST, conn->link->dev->bdaddr,
sizeof(conn->link->dev->bdaddr));
return 0;
}
static int cmd_set_conn_encrypt(struct btdev *dev, const void *data,
uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_SET_CONN_ENCRYPT);
return 0;
}
static void encrypt_change(struct btdev_conn *conn, uint8_t mode,
uint8_t status)
{
struct bt_hci_evt_encrypt_change ev;
if (!conn)
return;
memset(&ev, 0, sizeof(ev));
ev.status = status;
ev.handle = cpu_to_le16(conn->handle);
ev.encr_mode = mode;
send_event(conn->dev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
}
static int cmd_set_conn_encrypt_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_set_conn_encrypt *cmd = data;
struct btdev_conn *conn;
uint8_t mode;
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (!conn)
return 0;
if (!cmd->encr_mode)
mode = 0x00;
else if (dev->secure_conn_support &&
conn->link->dev->secure_conn_support)
mode = 0x02;
else
mode = 0x01;
encrypt_change(conn, mode, BT_HCI_ERR_SUCCESS);
encrypt_change(conn->link, mode, BT_HCI_ERR_SUCCESS);
return 0;
}
static int cmd_remote_name(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_REMOTE_NAME_REQUEST);
return 0;
}
static void name_request_complete(struct btdev *btdev,
const uint8_t *bdaddr, uint8_t status)
{
struct bt_hci_evt_remote_name_request_complete nc;
nc.status = status;
memcpy(nc.bdaddr, bdaddr, 6);
memset(nc.name, 0, 248);
if (!status) {
struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
if (remote)
memcpy(nc.name, remote->name, 248);
else
nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
}
send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE,
&nc, sizeof(nc));
}
static int cmd_remote_name_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_remote_name_request *cmd = data;
name_request_complete(dev, cmd->bdaddr, BT_HCI_ERR_SUCCESS);
return 0;
}
static int cmd_remote_name_cancel(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_remote_name_request_cancel *cmd = data;
struct bt_hci_rsp_remote_name_request_cancel rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.bdaddr, cmd->bdaddr, 6);
cmd_complete(dev, BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_remote_name_cancel_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_remote_name_request_cancel *cmd = data;
name_request_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
return 0;
}
static int cmd_read_remote_features(struct btdev *dev, const void *data,
uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_REMOTE_FEATURES);
return 0;
}
static int cmd_read_remote_features_complete(struct btdev *dev,
const void *data, uint8_t len)
{
const struct bt_hci_cmd_read_remote_features *cmd = data;
struct bt_hci_evt_remote_features_complete rfc;
struct btdev_conn *conn;
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (conn) {
rfc.status = BT_HCI_ERR_SUCCESS;
rfc.handle = cpu_to_le16(cmd->handle);
memcpy(rfc.features, conn->link->dev->features, 8);
} else {
rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
rfc.handle = cpu_to_le16(cmd->handle);
memset(rfc.features, 0, 8);
}
send_event(dev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE, &rfc, sizeof(rfc));
return 0;
}
static int cmd_read_remote_ext_features(struct btdev *dev, const void *data,
uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS,
BT_HCI_CMD_READ_REMOTE_EXT_FEATURES);
return 0;
}
static void btdev_get_host_features(struct btdev *btdev, uint8_t features[8])
{
memset(features, 0, 8);
if (btdev->simple_pairing_mode)
features[0] |= 0x01;
if (btdev->le_supported)
features[0] |= 0x02;
if (btdev->le_simultaneous)
features[0] |= 0x04;
if (btdev->secure_conn_support)
features[0] |= 0x08;
}
static int cmd_read_remote_ext_features_compl(struct btdev *dev,
const void *data, uint8_t len)
{
const struct bt_hci_cmd_read_remote_ext_features *cmd = data;
struct bt_hci_evt_remote_ext_features_complete ev;
struct btdev_conn *conn;
memset(&ev, 0, sizeof(ev));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (conn && cmd->page < 0x02) {
ev.handle = cpu_to_le16(cmd->handle);
ev.page = cmd->page;
ev.max_page = 0x01;
switch (cmd->page) {
case 0x00:
ev.status = BT_HCI_ERR_SUCCESS;
memcpy(ev.features, conn->link->dev->features, 8);
break;
case 0x01:
ev.status = BT_HCI_ERR_SUCCESS;
btdev_get_host_features(conn->link->dev, ev.features);
break;
default:
ev.status = BT_HCI_ERR_INVALID_PARAMETERS;
memset(ev.features, 0, 8);
break;
}
} else {
ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
ev.handle = cpu_to_le16(cmd->handle);
ev.page = cmd->page;
ev.max_page = 0x01;
memset(ev.features, 0, 8);
}
send_event(dev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE, &ev,
sizeof(ev));
return 0;
}
static int cmd_read_clock_offset(struct btdev *dev, const void *data,
uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_READ_CLOCK_OFFSET);
return 0;
}
static int cmd_read_clock_offset_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_read_clock_offset *cmd = data;
struct bt_hci_evt_clock_offset_complete ev;
struct btdev_conn *conn;
memset(&ev, 0, sizeof(ev));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (conn) {
ev.status = BT_HCI_ERR_SUCCESS;
ev.handle = cpu_to_le16(cmd->handle);
ev.clock_offset = 0;
} else {
ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
ev.handle = cpu_to_le16(cmd->handle);
ev.clock_offset = 0;
}
send_event(dev, BT_HCI_EVT_CLOCK_OFFSET_COMPLETE, &ev, sizeof(ev));
return 0;
}
static int cmd_read_link_policy(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_default_link_policy rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.policy = cpu_to_le16(dev->default_link_policy);
cmd_complete(dev, BT_HCI_CMD_READ_DEFAULT_LINK_POLICY, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_link_policy(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_default_link_policy *cmd = data;
uint8_t status;
dev->default_link_policy = le16_to_cpu(cmd->policy);
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY, &status,
sizeof(status));
return 0;
}
static int cmd_set_event_filter(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_set_event_filter *cmd = data;
uint8_t status;
dev->event_filter = cmd->type;
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_SET_EVENT_FILTER, &status, sizeof(status));
return 0;
}
static int cmd_read_link_key(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_stored_link_key rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.max_num_keys = cpu_to_le16(0);
rsp.num_keys = cpu_to_le16(0);
cmd_complete(dev, BT_HCI_CMD_READ_STORED_LINK_KEY, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_link_key(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_write_stored_link_key rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.num_keys = 0;
cmd_complete(dev, BT_HCI_CMD_WRITE_STORED_LINK_KEY, &rsp, sizeof(rsp));
return 0;
}
static int cmd_delete_link_key(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_delete_stored_link_key rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.num_keys = cpu_to_le16(0);
cmd_complete(dev, BT_HCI_CMD_DELETE_STORED_LINK_KEY, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_local_name(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_local_name rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.name, dev->name, 248);
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_NAME, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_local_name(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_local_name *cmd = data;
uint8_t status;
memcpy(dev->name, cmd->name, 248);
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_WRITE_LOCAL_NAME, &status, sizeof(status));
return 0;
}
static int cmd_read_accept_timeout(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_conn_accept_timeout rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.timeout = cpu_to_le16(dev->conn_accept_timeout);
cmd_complete(dev, BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_accept_timeout(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_conn_accept_timeout *cmd = data;
uint8_t status;
dev->conn_accept_timeout = le16_to_cpu(cmd->timeout);
status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT, &status,
sizeof(status));
return 0;
}
static int cmd_read_page_timeout(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_page_timeout rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.timeout = cpu_to_le16(dev->page_timeout);
cmd_complete(dev, BT_HCI_CMD_READ_PAGE_TIMEOUT, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_page_timeout(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_page_timeout *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->page_timeout = le16_to_cpu(cmd->timeout);
cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_TIMEOUT, &status,
sizeof(status));
return 0;
}
static int cmd_read_scan_enable(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_scan_enable rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.enable = dev->scan_enable;
cmd_complete(dev, BT_HCI_CMD_READ_SCAN_ENABLE, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_scan_enable(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_scan_enable *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->scan_enable = cmd->enable;
cmd_complete(dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &status,
sizeof(status));
return 0;
}
static int cmd_read_page_scan(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_page_scan_activity rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.interval = cpu_to_le16(dev->page_scan_interval);
rsp.window = cpu_to_le16(dev->page_scan_window);
cmd_complete(dev, BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_page_scan(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_page_scan_activity *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->page_scan_interval = le16_to_cpu(cmd->interval);
dev->page_scan_window = le16_to_cpu(cmd->window);
cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY, &status,
sizeof(status));
return 0;
}
static int cmd_read_inquiry_scan(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_inquiry_scan_activity rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.interval = cpu_to_le16(dev->inquiry_scan_interval);
rsp.window = cpu_to_le16(dev->inquiry_scan_window);
cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_inquiry_scan(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_inquiry_scan_activity *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->inquiry_scan_interval = le16_to_cpu(cmd->interval);
dev->inquiry_scan_window = le16_to_cpu(cmd->window);
cmd_complete(dev, BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY, &status,
sizeof(status));
return 0;
}
static int cmd_read_auth_enable(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_auth_enable rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.enable = dev->auth_enable;
cmd_complete(dev, BT_HCI_CMD_READ_AUTH_ENABLE, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_auth_enable(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_auth_enable *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->auth_enable = cmd->enable;
cmd_complete(dev, BT_HCI_CMD_WRITE_AUTH_ENABLE, &status,
sizeof(status));
return 0;
}
static int cmd_read_class(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_class_of_dev rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
memcpy(rsp.dev_class, dev->dev_class, 3);
cmd_complete(dev, BT_HCI_CMD_READ_CLASS_OF_DEV, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_class(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_write_class_of_dev *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
memcpy(dev->dev_class, cmd->dev_class, 3);
cmd_complete(dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, &status,
sizeof(status));
return 0;
}
static int cmd_read_voice(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_voice_setting rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.setting = cpu_to_le16(dev->voice_setting);
cmd_complete(dev, BT_HCI_CMD_READ_VOICE_SETTING, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_voice(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_write_voice_setting *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->voice_setting = le16_to_cpu(cmd->setting);
cmd_complete(dev, BT_HCI_CMD_WRITE_VOICE_SETTING, &status,
sizeof(status));
return 0;
}
static int cmd_read_tx_power_level(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_read_tx_power *cmd = data;
struct bt_hci_rsp_read_tx_power rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.handle = le16_to_cpu(cmd->handle);
rsp.status = BT_HCI_ERR_SUCCESS;
if (cmd->type)
rsp.level = 4;
else
rsp.level = -1;
cmd_complete(dev, BT_HCI_CMD_READ_TX_POWER, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_num_iac(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_num_supported_iac rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.num_iac = 0x01;
cmd_complete(dev, BT_HCI_CMD_READ_NUM_SUPPORTED_IAC, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_current_iac_lap(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_current_iac_lap *rsp;
rsp = alloca(sizeof(*rsp) + 3);
rsp->status = BT_HCI_ERR_SUCCESS;
rsp->num_iac = 0x01;
rsp->iac_lap[0] = 0x33;
rsp->iac_lap[1] = 0x8b;
rsp->iac_lap[2] = 0x9e;
cmd_complete(dev, BT_HCI_CMD_READ_CURRENT_IAC_LAP, rsp,
sizeof(*rsp) + 3);
return 0;
}
static int cmd_write_current_iac_lap(struct btdev *dev, const void *data,
uint8_t len)
{
uint8_t status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_WRITE_CURRENT_IAC_LAP, &status,
sizeof(status));
return 0;
}
static int cmd_read_inquiry_mode(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_inquiry_mode rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.mode = dev->inquiry_mode;
cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_MODE, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_inquiry_mode(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_inquiry_mode *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->inquiry_mode = cmd->mode;
cmd_complete(dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &status,
sizeof(status));
return 0;
}
static int cmd_read_page_scan_type(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_page_scan_type rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.type = dev->page_scan_type;
cmd_complete(dev, BT_HCI_CMD_READ_PAGE_SCAN_TYPE, &rsp, sizeof(rsp));
return 0;
}
static int cmd_write_page_scan_type(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_page_scan_type *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->page_scan_type = cmd->type;
cmd_complete(dev, BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE, &status,
sizeof(status));
return 0;
}
static int cmd_read_afh_mode(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_afh_assessment_mode rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.mode = dev->afh_assessment_mode;
cmd_complete(dev, BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_afh_mode(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_write_afh_assessment_mode *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->afh_assessment_mode = cmd->mode;
cmd_complete(dev, BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE, &status,
sizeof(status));
return 0;
}
static int cmd_read_local_ext_features(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_local_ext_features rsp;
uint8_t page = ((const uint8_t *) data)[0];
memset(&rsp, 0, sizeof(rsp));
rsp.page = page;
rsp.max_page = dev->max_page;
if (page > dev->max_page) {
rsp.status = BT_HCI_ERR_INVALID_PARAMETERS;
goto done;
}
rsp.status = BT_HCI_ERR_SUCCESS;
switch (page) {
case 0x00:
memcpy(rsp.features, dev->features, 8);
break;
case 0x01:
btdev_get_host_features(dev, rsp.features);
break;
case 0x02:
memcpy(rsp.features, dev->feat_page_2, 8);
break;
default:
rsp.status = BT_HCI_ERR_INVALID_PARAMETERS;
break;
}
done:
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_read_country_code(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_country_code rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.code = dev->country_code;
cmd_complete(dev, BT_HCI_CMD_READ_COUNTRY_CODE, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_rssi(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_read_rssi *cmd = data;
struct bt_hci_rsp_read_rssi rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.handle = le16_to_cpu(cmd->handle);
rsp.rssi = -1;
cmd_complete(dev, BT_HCI_CMD_READ_RSSI, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_clock(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_read_clock *cmd = data;
struct bt_hci_rsp_read_clock rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.handle = le16_to_cpu(cmd->handle);
rsp.clock = 0x11223344;
rsp.accuracy = 0x5566;
cmd_complete(dev, BT_HCI_CMD_READ_CLOCK, &rsp, sizeof(rsp));
return 0;
}
static int cmd_enable_dut_mode(struct btdev *dev, const void *data,
uint8_t len)
{
uint8_t status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_ENABLE_DUT_MODE, &status,
sizeof(status));
return 0;
}
#define CMD_COMMON_BREDR_20 \
CMD(BT_HCI_CMD_INQUIRY, cmd_inquiry, cmd_inquiry_complete), \
CMD(BT_HCI_CMD_INQUIRY_CANCEL, cmd_inquiry_cancel, NULL), \
CMD(BT_HCI_CMD_CREATE_CONN, cmd_create_conn, \
cmd_create_conn_complete), \
CMD(BT_HCI_CMD_ADD_SCO_CONN, cmd_add_sco_conn, NULL), \
CMD(BT_HCI_CMD_CREATE_CONN_CANCEL, cmd_create_conn_cancel, \
cmd_create_conn_cancel_complete), \
CMD(BT_HCI_CMD_ACCEPT_CONN_REQUEST, cmd_accept_conn, \
cmd_accept_conn_complete), \
CMD(BT_HCI_CMD_REJECT_CONN_REQUEST, cmd_reject_conn, \
cmd_reject_conn_complete), \
CMD(BT_HCI_CMD_LINK_KEY_REQUEST_REPLY, cmd_link_key_reply, \
cmd_link_key_reply_complete), \
CMD(BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, \
cmd_link_key_neg_reply, \
cmd_link_key_neg_reply_complete), \
CMD(BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, cmd_pin_code_reply, \
cmd_pin_code_reply_complete), \
CMD(BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, \
cmd_pin_code_neg_reply, \
cmd_pin_code_neg_reply_complete), \
CMD(BT_HCI_CMD_AUTH_REQUESTED, cmd_auth_requested, \
cmd_auth_requested_complete), \
CMD(BT_HCI_CMD_SET_CONN_ENCRYPT, cmd_set_conn_encrypt, \
cmd_set_conn_encrypt_complete), \
CMD(BT_HCI_CMD_REMOTE_NAME_REQUEST, cmd_remote_name, \
cmd_remote_name_complete), \
CMD(BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL, cmd_remote_name_cancel, \
cmd_remote_name_cancel_complete), \
CMD(BT_HCI_CMD_READ_REMOTE_FEATURES, cmd_read_remote_features, \
cmd_read_remote_features_complete), \
CMD(BT_HCI_CMD_READ_REMOTE_EXT_FEATURES, \
cmd_read_remote_ext_features, \
cmd_read_remote_ext_features_compl), \
CMD(BT_HCI_CMD_READ_CLOCK_OFFSET, cmd_read_clock_offset, \
cmd_read_clock_offset_complete), \
CMD(BT_HCI_CMD_READ_DEFAULT_LINK_POLICY, cmd_read_link_policy, NULL), \
CMD(BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY, cmd_write_link_policy, \
NULL), \
CMD(BT_HCI_CMD_SET_EVENT_FILTER, cmd_set_event_filter, NULL), \
CMD(BT_HCI_CMD_READ_STORED_LINK_KEY, cmd_read_link_key, NULL), \
CMD(BT_HCI_CMD_WRITE_STORED_LINK_KEY, cmd_write_link_key, NULL), \
CMD(BT_HCI_CMD_DELETE_STORED_LINK_KEY, cmd_delete_link_key, NULL), \
CMD(BT_HCI_CMD_READ_LOCAL_NAME, cmd_read_local_name, NULL), \
CMD(BT_HCI_CMD_WRITE_LOCAL_NAME, cmd_write_local_name, NULL), \
CMD(BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT, cmd_read_accept_timeout, \
NULL), \
CMD(BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT, cmd_write_accept_timeout, \
NULL), \
CMD(BT_HCI_CMD_READ_PAGE_TIMEOUT, cmd_read_page_timeout, NULL), \
CMD(BT_HCI_CMD_WRITE_PAGE_TIMEOUT, cmd_write_page_timeout, NULL), \
CMD(BT_HCI_CMD_READ_SCAN_ENABLE, cmd_read_scan_enable, NULL), \
CMD(BT_HCI_CMD_WRITE_SCAN_ENABLE, cmd_write_scan_enable, NULL), \
CMD(BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY, cmd_read_page_scan, NULL), \
CMD(BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY, cmd_write_page_scan, NULL), \
CMD(BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY, cmd_read_inquiry_scan, \
NULL), \
CMD(BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY, cmd_write_inquiry_scan, \
NULL), \
CMD(BT_HCI_CMD_READ_AUTH_ENABLE, cmd_read_auth_enable, NULL), \
CMD(BT_HCI_CMD_WRITE_AUTH_ENABLE, cmd_write_auth_enable, NULL), \
CMD(BT_HCI_CMD_READ_CLASS_OF_DEV, cmd_read_class, NULL), \
CMD(BT_HCI_CMD_WRITE_CLASS_OF_DEV, cmd_write_class, NULL), \
CMD(BT_HCI_CMD_READ_VOICE_SETTING, cmd_read_voice, NULL), \
CMD(BT_HCI_CMD_WRITE_VOICE_SETTING, cmd_write_voice, NULL), \
CMD(BT_HCI_CMD_READ_TX_POWER, cmd_read_tx_power_level, NULL), \
CMD(BT_HCI_CMD_READ_NUM_SUPPORTED_IAC, cmd_read_num_iac, NULL), \
CMD(BT_HCI_CMD_READ_CURRENT_IAC_LAP, cmd_read_current_iac_lap, \
NULL), \
CMD(BT_HCI_CMD_WRITE_CURRENT_IAC_LAP, cmd_write_current_iac_lap, \
NULL), \
CMD(BT_HCI_CMD_READ_INQUIRY_MODE, cmd_read_inquiry_mode, NULL), \
CMD(BT_HCI_CMD_WRITE_INQUIRY_MODE, cmd_write_inquiry_mode, NULL), \
CMD(BT_HCI_CMD_READ_PAGE_SCAN_TYPE, cmd_read_page_scan_type, NULL), \
CMD(BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE, cmd_write_page_scan_type, NULL), \
CMD(BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE, cmd_read_afh_mode, NULL), \
CMD(BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE, cmd_write_afh_mode, NULL), \
CMD(BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, cmd_read_local_ext_features, \
NULL), \
CMD(BT_HCI_CMD_READ_COUNTRY_CODE, cmd_read_country_code, NULL), \
CMD(BT_HCI_CMD_READ_RSSI, cmd_read_rssi, NULL), \
CMD(BT_HCI_CMD_READ_CLOCK, cmd_read_clock, NULL), \
CMD(BT_HCI_CMD_ENABLE_DUT_MODE, cmd_enable_dut_mode, NULL)
static void set_common_commands_bredr20(struct btdev *btdev)
{
btdev->commands[0] |= 0x01; /* Inquiry */
btdev->commands[0] |= 0x02; /* Inquiry Cancel */
btdev->commands[0] |= 0x10; /* Create Connection */
btdev->commands[0] |= 0x40; /* Add SCO Connection */
btdev->commands[0] |= 0x80; /* Cancel Create Connection */
btdev->commands[1] |= 0x01; /* Accept Connection Request */
btdev->commands[1] |= 0x02; /* Reject Connection Request */
btdev->commands[1] |= 0x04; /* Link Key Request Reply */
btdev->commands[1] |= 0x08; /* Link Key Request Negative Reply */
btdev->commands[1] |= 0x10; /* PIN Code Request Reply */
btdev->commands[1] |= 0x20; /* PIN Code Request Negative Reply */
btdev->commands[1] |= 0x80; /* Authentication Requested */
btdev->commands[2] |= 0x01; /* Set Connection Encryption */
btdev->commands[2] |= 0x08; /* Remote Name Request */
btdev->commands[2] |= 0x10; /* Cancel Remote Name Request */
btdev->commands[2] |= 0x20; /* Read Remote Supported Features */
btdev->commands[2] |= 0x40; /* Read Remote Extended Features */
btdev->commands[3] |= 0x01; /* Read Clock Offset */
btdev->commands[5] |= 0x08; /* Read Default Link Policy */
btdev->commands[5] |= 0x10; /* Write Default Link Policy */
btdev->commands[6] |= 0x01; /* Set Event Filter */
btdev->commands[6] |= 0x20; /* Read Stored Link Key */
btdev->commands[6] |= 0x40; /* Write Stored Link Key */
btdev->commands[6] |= 0x80; /* Delete Stored Link Key */
btdev->commands[7] |= 0x01; /* Write Local Name */
btdev->commands[7] |= 0x02; /* Read Local Name */
btdev->commands[7] |= 0x04; /* Read Connection Accept Timeout */
btdev->commands[7] |= 0x08; /* Write Connection Accept Timeout */
btdev->commands[7] |= 0x10; /* Read Page Timeout */
btdev->commands[7] |= 0x20; /* Write Page Timeout */
btdev->commands[7] |= 0x40; /* Read Scan Enable */
btdev->commands[7] |= 0x80; /* Write Scan Enable */
btdev->commands[8] |= 0x01; /* Read Page Scan Activity */
btdev->commands[8] |= 0x02; /* Write Page Scan Activity */
btdev->commands[8] |= 0x04; /* Read Inquiry Scan Activity */
btdev->commands[8] |= 0x08; /* Write Inquiry Scan Activity */
btdev->commands[8] |= 0x10; /* Read Authentication Enable */
btdev->commands[8] |= 0x20; /* Write Authentication Enable */
btdev->commands[9] |= 0x01; /* Read Class Of Device */
btdev->commands[9] |= 0x02; /* Write Class Of Device */
btdev->commands[9] |= 0x04; /* Read Voice Setting */
btdev->commands[9] |= 0x08; /* Write Voice Setting */
btdev->commands[10] |= 0x04; /* Read TX Power Level */
btdev->commands[11] |= 0x04; /* Read Number of Supported IAC */
btdev->commands[11] |= 0x08; /* Read Current IAC LAP */
btdev->commands[11] |= 0x10; /* Write Current IAC LAP */
btdev->commands[12] |= 0x40; /* Read Inquiry Mode */
btdev->commands[12] |= 0x80; /* Write Inquiry Mode */
btdev->commands[13] |= 0x01; /* Read Page Scan Type */
btdev->commands[13] |= 0x02; /* Write Page Scan Type */
btdev->commands[13] |= 0x04; /* Read AFH Assess Mode */
btdev->commands[13] |= 0x08; /* Write AFH Assess Mode */
btdev->commands[14] |= 0x40; /* Read Local Extended Features */
btdev->commands[15] |= 0x01; /* Read Country Code */
btdev->commands[15] |= 0x20; /* Read RSSI */
btdev->commands[15] |= 0x80; /* Read Clock */
btdev->commands[16] |= 0x04; /* Enable Device Under Test Mode */
}
static int cmd_enhanced_setup_sync_conn(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
if (cmd->tx_coding_format[0] > 5)
status = BT_HCI_ERR_INVALID_PARAMETERS;
cmd_status(dev, status, BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN);
return 0;
}
static int cmd_enhanced_setup_sync_conn_complete(struct btdev *dev,
const void *data, uint8_t len)
{
const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data;
struct bt_hci_evt_sync_conn_complete cc;
struct btdev_conn *conn;
memset(&cc, 0, sizeof(cc));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (!conn) {
cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
goto done;
}
conn = conn_add_sco(conn);
if (!conn) {
cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
goto done;
}
/* TODO: HCI_Connection_Request connection flow */
cc.status = BT_HCI_ERR_SUCCESS;
memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6);
cc.handle = cpu_to_le16(conn->handle);
cc.link_type = 0x02;
cc.tx_interval = 0x000c;
cc.retrans_window = 0x06;
cc.rx_pkt_len = 60;
cc.tx_pkt_len = 60;
cc.air_mode = cmd->tx_coding_format[0];
done:
send_event(dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc));
return 0;
}
static int cmd_setup_sync_conn(struct btdev *dev, const void *data, uint8_t len)
{
cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_SETUP_SYNC_CONN);
return 0;
}
static int cmd_setup_sync_conn_complete(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_setup_sync_conn *cmd = data;
struct bt_hci_evt_sync_conn_complete cc;
struct btdev_conn *conn;
memset(&cc, 0, sizeof(cc));
conn = queue_find(dev->conns, match_handle,
UINT_TO_PTR(le16_to_cpu(cmd->handle)));
if (!conn) {
cc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
goto done;
}
conn = conn_add_sco(conn);
if (!conn) {
cc.status = BT_HCI_ERR_MEM_CAPACITY_EXCEEDED;
goto done;
}
cc.status = BT_HCI_ERR_SUCCESS;
memcpy(cc.bdaddr, conn->link->dev->bdaddr, 6);
cc.handle = cpu_to_le16(conn->handle);
cc.link_type = 0x02;
cc.tx_interval = 0x000c;
cc.retrans_window = 0x06;
cc.rx_pkt_len = 60;
cc.tx_pkt_len = 60;
cc.air_mode = (cmd->voice_setting == 0x0060) ? 0x02 : 0x03;
done:
send_event(dev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc));
return 0;
}
static int cmd_read_ext_inquiry(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_ext_inquiry_response rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.fec = dev->ext_inquiry_fec;
memcpy(rsp.data, dev->ext_inquiry_rsp, 240);
cmd_complete(dev, BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_ext_inquiry(struct btdev *dev, const void *data,
uint8_t len)
{
const struct bt_hci_cmd_write_ext_inquiry_response *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->ext_inquiry_fec = cmd->fec;
memcpy(dev->ext_inquiry_rsp, cmd->data, 240);
cmd_complete(dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE, &status,
sizeof(status));
return 0;
}
static int cmd_read_ssp_mode(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_simple_pairing_mode rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.mode = dev->simple_pairing_mode;
cmd_complete(dev, BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_ssp_mode(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_write_simple_pairing_mode *cmd = data;
uint8_t status = BT_HCI_ERR_SUCCESS;
dev->simple_pairing_mode = cmd->mode;
cmd_complete(dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &status,
sizeof(status));
return 0;
}
static int cmd_read_oob_data(struct btdev *dev, const void *data, uint8_t len)
{
struct bt_hci_rsp_read_local_oob_data rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
cmd_complete(dev, BT_HCI_CMD_READ_LOCAL_OOB_DATA, &rsp, sizeof(rsp));
return 0;
}
static int cmd_read_inquiry_tx_power(struct btdev *dev, const void *data,
uint8_t len)
{
struct bt_hci_rsp_read_inquiry_resp_tx_power rsp;
memset(&rsp, 0, sizeof(rsp));
rsp.status = BT_HCI_ERR_SUCCESS;
rsp.level = 0;
cmd_complete(dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, &rsp,
sizeof(rsp));
return 0;
}
static int cmd_write_inquiry_tx_power(struct btdev *dev, const void *data,
uint8_t len)
{
return -ENOTSUP;
}
static int cmd_io_cap_reply(struct btdev *dev, const void *data, uint8_t len)
{
const struct bt_hci_cmd_io_capability_request_reply *cmd = data;
struct bt_hci_evt_io_capability_response ev;
struct bt_hci_rsp_io_capability_request_reply rsp;
struct btdev_conn *conn;
uint8_t status;
conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
if (!conn) {
status = BT_HCI_ERR_UNKNOWN_CONN_ID;
goto done;
}
status = BT_HCI_ERR_SUCCESS;
dev->io_cap = cmd->capability;
dev->auth_req = cmd->authentication;
memcpy(ev.bdaddr, dev->bdaddr, 6);
ev.capability = cmd->capability;
ev.oob_data = cmd->oob_data;
ev.authentication = cmd->authentication;
send_event(conn->link->dev, BT_HCI_EVT_IO_CAPABILITY_RESPONSE, &ev,
sizeof(ev));
if (conn->link->dev->io_cap) {
struct bt_hci_evt_user_confirm_request cfm;
memcpy(cfm.bdaddr, dev->bdaddr, 6);
cfm.passkey = 0;
send_event(conn->link->dev, BT_HCI_EVT_USER_CONFIRM_REQUEST,
&cfm, sizeof(cfm));
memcpy(cfm.bdaddr, cmd->bdaddr, 6);
send_event(dev, BT_HCI_EVT_USER_CONFIRM_REQUEST,