zephyr/net/bluetooth/hci_core.c

1261 lines
27 KiB
C

/* hci_core.c - HCI core Bluetooth handling */
/*
* Copyright (c) 2015 Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3) Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <nanokernel.h>
#include <toolchain.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <misc/util.h>
#include <misc/byteorder.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include "hci_core.h"
#include "conn.h"
#include "l2cap.h"
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif
/* How many buffers to use for incoming ACL data */
#define ACL_IN_MAX 7
#define ACL_OUT_MAX 7
/* Stacks for the fibers */
#if defined(CONFIG_BLUETOOTH_DEBUG)
#define RX_STACK_SIZE 2048
#define CMD_RX_STACK_SIZE 1024
#define CMD_TX_STACK_SIZE 512
#else
#define RX_STACK_SIZE 1024
#define CMD_RX_STACK_SIZE 256
#define CMD_TX_STACK_SIZE 256
#endif
static const uint8_t BDADDR_ANY[6] = { 0, 0, 0, 0, 0 };
static char rx_fiber_stack[RX_STACK_SIZE];
static char cmd_rx_fiber_stack[CMD_RX_STACK_SIZE];
static char cmd_tx_fiber_stack[CMD_TX_STACK_SIZE];
#if defined(CONFIG_BLUETOOTH_DEBUG)
static nano_context_id_t cmd_rx_fiber_id;
#endif
static struct bt_dev dev;
static struct bt_keys key_list[CONFIG_BLUETOOTH_MAX_PAIRED];
#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);
sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
addr->val[5], addr->val[4], addr->val[3],
addr->val[2], addr->val[1], addr->val[0]);
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, type[7];
str = bufs[cur++];
cur %= ARRAY_SIZE(bufs);
switch (addr->type) {
case BT_ADDR_LE_PUBLIC:
strcpy(type, "public");
break;
case BT_ADDR_LE_RANDOM:
strcpy(type, "random");
break;
default:
sprintf(type, "0x%02x", addr->type);
break;
}
sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)",
addr->val[5], addr->val[4], addr->val[3],
addr->val[2], addr->val[1], addr->val[0], type);
return str;
}
#endif /* CONFIG_BLUETOOTH_DEBUG */
struct bt_keys *bt_keys_create(const bt_addr_le_t *addr)
{
struct bt_keys *keys;
int i;
BT_DBG("%s\n", bt_addr_le_str(addr));
keys = bt_keys_find(addr);
if (keys) {
return keys;
}
for (i = 0; i < ARRAY_SIZE(key_list); i++) {
keys = &key_list[i];
if (!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY)) {
bt_addr_le_copy(&keys->addr, addr);
BT_DBG("created keys %p\n", keys);
return keys;
}
}
BT_DBG("no match\n");
return NULL;
}
struct bt_keys *bt_keys_find(const bt_addr_le_t *addr)
{
int i;
BT_DBG("%s\n", bt_addr_le_str(addr));
for (i = 0; i < ARRAY_SIZE(key_list); i++) {
struct bt_keys *keys = &key_list[i];
if (!bt_addr_le_cmp(&keys->addr, addr)) {
BT_DBG("found keys %p\n", keys);
return keys;
}
}
BT_DBG("no match\n");
return NULL;
}
void bt_keys_clear(struct bt_keys *keys)
{
BT_DBG("keys %p\n", keys);
memset(keys, 0, sizeof(*keys));
}
struct bt_buf *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len)
{
struct bt_hci_cmd_hdr *hdr;
struct bt_buf *buf;
BT_DBG("opcode %x param_len %u\n", opcode, param_len);
buf = bt_buf_get(BT_CMD, dev.drv->head_reserve);
if (!buf) {
BT_ERR("Cannot get free buffer\n");
return NULL;
}
BT_DBG("buf %p\n", buf);
buf->hci.opcode = opcode;
buf->hci.sync = NULL;
hdr = bt_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 bt_buf *buf)
{
if (!buf) {
buf = bt_hci_cmd_create(opcode, 0);
if (!buf) {
return -ENOBUFS;
}
}
BT_DBG("opcode %x len %u\n", 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) {
dev.drv->send(buf);
bt_buf_put(buf);
return 0;
}
nano_fifo_put(&dev.cmd_tx_queue, buf);
return 0;
}
int bt_hci_cmd_send_sync(uint16_t opcode, struct bt_buf *buf,
struct bt_buf **rsp)
{
struct nano_sem sync_sem;
int err;
/* This function cannot be called from the rx fiber since it
* relies on the very same fiber in processing the cmd_complete
* event and giving back the blocking semaphore.
*/
#if defined(CONFIG_BLUETOOTH_DEBUG)
if (context_self_get() == cmd_rx_fiber_id) {
BT_ERR("called from invalid context!\n");
return -EDEADLK;
}
#endif
if (!buf) {
buf = bt_hci_cmd_create(opcode, 0);
if (!buf) {
return -ENOBUFS;
}
}
BT_DBG("opcode %x len %u\n", opcode, buf->len);
nano_sem_init(&sync_sem);
buf->hci.sync = &sync_sem;
nano_fifo_put(&dev.cmd_tx_queue, buf);
nano_sem_take_wait(&sync_sem);
/* Indicate failure if we failed to get the return parameters */
if (!buf->hci.sync) {
err = -EIO;
} else {
err = 0;
}
if (rsp) {
*rsp = buf->hci.sync;
} else if (buf->hci.sync) {
bt_buf_put(buf->hci.sync);
}
bt_buf_put(buf);
return err;
}
static void hci_acl(struct bt_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\n", buf);
handle = sys_le16_to_cpu(hdr->handle);
flags = (handle >> 12);
buf->acl.handle = bt_acl_handle(handle);
bt_buf_pull(buf, sizeof(*hdr));
BT_DBG("handle %u len %u flags %u\n", buf->acl.handle, len, flags);
if (buf->len != len) {
BT_ERR("ACL data length mismatch (%u != %u)\n", buf->len, len);
bt_buf_put(buf);
return;
}
conn = bt_conn_lookup(buf->acl.handle);
if (!conn) {
BT_ERR("Unable to find conn for handle %u\n", buf->acl.handle);
bt_buf_put(buf);
return;
}
bt_conn_recv(conn, buf, flags);
}
/* HCI event processing */
static void hci_disconn_complete(struct bt_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\n", evt->status, handle,
evt->reason);
if (evt->status) {
return;
}
conn = bt_conn_lookup(handle);
if (!conn) {
BT_ERR("Unable to look up conn with handle %u\n", handle);
return;
}
bt_l2cap_disconnected(conn);
bt_conn_del(conn);
if (dev.adv_enable) {
struct bt_buf *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
if (buf) {
memcpy(bt_buf_add(buf, 1), &dev.adv_enable, 1);
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_ENABLE, buf);
}
}
}
static void hci_encrypt_change(struct bt_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\n", evt->status, handle,
evt->encrypt);
if (evt->status) {
return;
}
conn = bt_conn_lookup(handle);
if (!conn) {
BT_ERR("Unable to look up conn with handle %u\n", handle);
return;
}
conn->encrypt = evt->encrypt;
bt_l2cap_encrypt_change(conn);
}
static void hci_reset_complete(struct bt_buf *buf)
{
uint8_t status = buf->data[0];
BT_DBG("status %u\n", status);
if (status) {
return;
}
}
static void hci_cmd_done(uint16_t opcode, uint8_t status, struct bt_buf *buf)
{
struct bt_buf *sent = dev.sent_cmd;
if (!sent) {
return;
}
if (dev.sent_cmd->hci.opcode != opcode) {
BT_ERR("Unexpected completion of opcode 0x%04x\n", opcode);
return;
}
dev.sent_cmd = NULL;
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
if (sent->hci.sync) {
struct nano_sem *sem = sent->hci.sync;
if (status) {
sent->hci.sync = NULL;
} else {
sent->hci.sync = bt_buf_hold(buf);
}
nano_fiber_sem_give(sem);
} else {
bt_buf_put(sent);
}
}
static void hci_cmd_complete(struct bt_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 %x\n", opcode);
bt_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;
switch (opcode) {
case BT_HCI_OP_RESET:
hci_reset_complete(buf);
break;
default:
BT_DBG("Unhandled opcode %x\n", opcode);
break;
}
hci_cmd_done(opcode, *status, buf);
if (evt->ncmd && !dev.ncmd) {
/* Allow next command to be sent */
dev.ncmd = 1;
nano_fiber_sem_give(&dev.ncmd_sem);
}
}
static void hci_cmd_status(struct bt_buf *buf)
{
struct bt_hci_evt_cmd_status *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
BT_DBG("opcode %x\n", opcode);
bt_buf_pull(buf, sizeof(*evt));
switch (opcode) {
default:
BT_DBG("Unhandled opcode %x\n", opcode);
break;
}
hci_cmd_done(opcode, evt->status, NULL);
if (evt->ncmd && !dev.ncmd) {
/* Allow next command to be sent */
dev.ncmd = 1;
nano_fiber_sem_give(&dev.ncmd_sem);
}
}
static void hci_num_completed_packets(struct bt_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\n", num_handles);
for (i = 0; i < num_handles; i++) {
uint16_t handle, count;
handle = sys_le16_to_cpu(evt->h[i].handle);
count = sys_le16_to_cpu(evt->h[i].count);
BT_DBG("handle %u count %u\n", handle, count);
while (count--)
nano_fiber_sem_give(&dev.le_pkts_sem);
}
}
static void le_conn_complete(struct bt_buf *buf)
{
struct bt_hci_evt_le_conn_complete *evt = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(evt->handle);
struct bt_conn *conn;
BT_DBG("status %u handle %u role %u %s (%u)\n", evt->status, handle,
evt->role, bt_bdaddr_str(evt->peer_addr), evt->peer_addr_type);
if (evt->status) {
return;
}
conn = bt_conn_add(&dev, handle);
if (!conn) {
BT_ERR("Unable to add new conn for handle %u\n", handle);
return;
}
conn->src.type = BT_ADDR_LE_PUBLIC;
memcpy(conn->src.val, dev.bdaddr.val, sizeof(dev.bdaddr.val));
bt_addr_le_copy(&conn->dst, &evt->peer_addr);
conn->le_conn_interval = sys_le16_to_cpu(evt->interval);
bt_l2cap_connected(conn);
}
static void le_adv_report(struct bt_buf *buf)
{
uint8_t num_reports = buf->data[0];
struct bt_hci_ev_le_advertising_info *info;
BT_DBG("Adv number of reports %u\n", num_reports);
info = bt_buf_pull(buf, sizeof(num_reports));
while (num_reports--) {
int8_t rssi = info->data[info->length];
BT_DBG("addr [%s], type:%u, event:%u, len:%u, rssi:%d dBm\n",
bt_bdaddr_str(info->bdaddr), info->bdaddr_type,
info->evt_type, info->length, rssi);
/* 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 = bt_buf_pull(buf, sizeof(*info) + info->length +
sizeof(rssi));
}
}
static void le_ltk_request(struct bt_buf *buf)
{
struct bt_hci_evt_le_ltk_request *evt = (void *)buf->data;
struct bt_conn *conn;
struct bt_keys *keys;
uint16_t handle;
handle = sys_le16_to_cpu(evt->handle);
BT_DBG("handle %u\n", handle);
conn = bt_conn_lookup(handle);
if (!conn) {
BT_ERR("Unable to lookup conn for handle %u\n", handle);
return;
}
keys = bt_keys_find(&conn->dst);
if (keys && keys->slave_ltk.rand == evt->rand &&
keys->slave_ltk.ediv == evt->ediv) {
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\n");
return;
}
cp = bt_buf_add(buf, sizeof(*cp));
cp->handle = evt->handle;
memcpy(cp->ltk, keys->slave_ltk.val, 16);
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
} else {
struct bt_hci_cp_le_ltk_req_neg_reply *cp;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY,
sizeof(*cp));
if (!buf) {
BT_ERR("Out of command buffers\n");
return;
}
cp = bt_buf_add(buf, sizeof(*cp));
cp->handle = evt->handle;
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf);
}
}
static void hci_le_meta_event(struct bt_buf *buf)
{
struct bt_hci_evt_le_meta_event *evt = (void *)buf->data;
bt_buf_pull(buf, sizeof(*evt));
switch (evt->subevent) {
case BT_HCI_EVT_LE_CONN_COMPLETE:
le_conn_complete(buf);
break;
case BT_HCI_EVT_LE_ADVERTISING_REPORT:
le_adv_report(buf);
break;
case BT_HCI_EVT_LE_LTK_REQUEST:
le_ltk_request(buf);
break;
default:
BT_DBG("Unhandled LE event %x\n", evt->subevent);
break;
}
}
static void hci_event(struct bt_buf *buf)
{
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
BT_DBG("event %u\n", hdr->evt);
bt_buf_pull(buf, sizeof(*hdr));
switch (hdr->evt) {
case BT_HCI_EVT_DISCONN_COMPLETE:
hci_disconn_complete(buf);
break;
case BT_HCI_EVT_ENCRYPT_CHANGE:
hci_encrypt_change(buf);
break;
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
hci_num_completed_packets(buf);
break;
case BT_HCI_EVT_LE_META_EVENT:
hci_le_meta_event(buf);
break;
default:
BT_WARN("Unhandled event 0x%02x\n", hdr->evt);
break;
}
bt_buf_put(buf);
}
static void hci_cmd_tx_fiber(void)
{
struct bt_driver *drv = dev.drv;
BT_DBG("started\n");
while (1) {
struct bt_buf *buf;
/* Wait until ncmd > 0 */
BT_DBG("calling sem_take_wait\n");
nano_fiber_sem_take_wait(&dev.ncmd_sem);
/* Get next command - wait if necessary */
BT_DBG("calling fifo_get_wait\n");
buf = nano_fifo_get_wait(&dev.cmd_tx_queue);
dev.ncmd = 0;
BT_DBG("Sending command %x (buf %p) to driver\n",
buf->hci.opcode, buf);
drv->send(buf);
/* Clear out any existing sent command */
if (dev.sent_cmd) {
BT_ERR("Uncleared pending sent_cmd\n");
bt_buf_put(dev.sent_cmd);
dev.sent_cmd = NULL;
}
dev.sent_cmd = buf;
}
}
static void hci_rx_fiber(void)
{
struct bt_buf *buf;
BT_DBG("started\n");
while (1) {
BT_DBG("calling fifo_get_wait\n");
buf = nano_fifo_get_wait(&dev.rx_queue);
BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len);
switch (buf->type) {
case BT_ACL_IN:
hci_acl(buf);
break;
case BT_EVT:
hci_event(buf);
break;
default:
BT_ERR("Unknown buf type %u\n", buf->type);
bt_buf_put(buf);
break;
}
}
}
static void cmd_rx_fiber(void)
{
struct bt_buf *buf;
BT_DBG("started\n");
/* So we can avoid bt_hci_cmd_send_sync deadlocks */
#if defined(CONFIG_BLUETOOTH_DEBUG)
cmd_rx_fiber_id = context_self_get();
#endif
while (1) {
struct bt_hci_evt_hdr *hdr;
BT_DBG("calling fifo_get_wait\n");
buf = nano_fifo_get_wait(&dev.cmd_rx_queue);
BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len);
if (buf->type != BT_EVT) {
BT_ERR("Unknown buf type %u\n", buf->type);
bt_buf_put(buf);
continue;
}
hdr = (void *)buf->data;
bt_buf_pull(buf, sizeof(*hdr));
switch (hdr->evt) {
case BT_HCI_EVT_CMD_COMPLETE:
hci_cmd_complete(buf);
break;
case BT_HCI_EVT_CMD_STATUS:
hci_cmd_status(buf);
break;
default:
BT_ERR("Unknown event 0x%02x\n", hdr->evt);
break;
}
bt_buf_put(buf);
}
}
static void read_local_features_complete(struct bt_buf *buf)
{
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
memcpy(dev.features, rp->features, sizeof(dev.features));
}
static void read_local_ver_complete(struct bt_buf *buf)
{
struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
dev.hci_version = rp->hci_version;
dev.hci_revision = sys_le16_to_cpu(rp->hci_revision);
dev.manufacturer = sys_le16_to_cpu(rp->manufacturer);
}
static void read_bdaddr_complete(struct bt_buf *buf)
{
struct bt_hci_rp_read_bd_addr *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
bt_addr_copy(&dev.bdaddr, &rp->bdaddr);
}
static void read_le_features_complete(struct bt_buf *buf)
{
struct bt_hci_rp_le_read_local_features *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
memcpy(dev.le_features, rp->features, sizeof(dev.le_features));
}
static void read_buffer_size_complete(struct bt_buf *buf)
{
struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
/* If LE-side has buffers we can ignore the BR/EDR values */
if (dev.le_mtu) {
return;
}
dev.le_mtu = sys_le16_to_cpu(rp->acl_max_len);
dev.le_pkts = sys_le16_to_cpu(rp->acl_max_num);
}
static void le_read_buffer_size_complete(struct bt_buf *buf)
{
struct bt_hci_rp_le_read_buffer_size *rp = (void *)buf->data;
BT_DBG("status %u\n", rp->status);
dev.le_mtu = sys_le16_to_cpu(rp->le_max_len);
dev.le_pkts = rp->le_max_num;
}
static int hci_init(void)
{
struct bt_hci_cp_host_buffer_size *hbs;
struct bt_hci_cp_set_event_mask *ev;
struct bt_buf *buf, *rsp;
uint8_t *enable;
int i, err;
/* Send HCI_RESET */
bt_hci_cmd_send(BT_HCI_OP_RESET, NULL);
/* 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);
bt_buf_put(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);
bt_buf_put(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);
bt_buf_put(rsp);
/* For now we only support LE capable controllers */
if (!lmp_le_capable(dev)) {
BT_ERR("Non-LE capable controller detected!\n");
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);
bt_buf_put(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);
bt_buf_put(rsp);
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev));
if (!buf) {
return -ENOBUFS;
}
ev = bt_buf_add(buf, sizeof(*ev));
memset(ev, 0, sizeof(*ev));
ev->events[0] |= 0x10; /* Disconnection Complete */
ev->events[1] |= 0x08; /* Read Remote Version Information Complete */
ev->events[1] |= 0x20; /* Command Complete */
ev->events[1] |= 0x40; /* Command Status */
ev->events[1] |= 0x80; /* Hardware Error */
ev->events[2] |= 0x04; /* Number of Completed Packets */
ev->events[3] |= 0x02; /* Data Buffer Overflow */
ev->events[7] |= 0x20; /* LE Meta-Event */
if (dev.le_features[0] & BT_HCI_LE_ENCRYPTION) {
ev->events[0] |= 0x80; /* Encryption Change */
ev->events[5] |= 0x80; /* Encryption Key Refresh Complete */
}
bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL);
buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, sizeof(*hbs));
if (!buf) {
return -ENOBUFS;
}
hbs = bt_buf_add(buf, sizeof(*hbs));
memset(hbs, 0, sizeof(*hbs));
hbs->acl_mtu = sys_cpu_to_le16(BT_BUF_MAX_DATA -
sizeof(struct bt_hci_acl_hdr) -
dev.drv->head_reserve);
hbs->acl_pkts = sys_cpu_to_le16(ACL_IN_MAX);
err = bt_hci_cmd_send(BT_HCI_OP_HOST_BUFFER_SIZE, buf);
if (err) {
return err;
}
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1);
if (!buf) {
return -ENOBUFS;
}
enable = bt_buf_add(buf, sizeof(*enable));
*enable = 0x01;
err = bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL);
if (err) {
return err;
}
if (lmp_bredr_capable(dev)) {
struct bt_hci_cp_write_le_host_supp *cp;
/* Use BR/EDR buffer size if LE reports zero buffers */
if (!dev.le_mtu) {
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE,
NULL, &rsp);
if (err) {
return err;
}
read_buffer_size_complete(rsp);
bt_buf_put(rsp);
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = bt_buf_add(buf, sizeof*cp);
/* Excplicitly enable LE for dual-mode controllers */
cp->le = 0x01;
cp->simul = 0x00;
bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf,
NULL);
}
BT_DBG("HCI ver %u rev %u, manufacturer %u\n", dev.hci_version,
dev.hci_revision, dev.manufacturer);
BT_DBG("ACL buffers: pkts %u mtu %u\n", dev.le_pkts, dev.le_mtu);
/* Initialize & prime the semaphore for counting controller-side
* available ACL packet buffers.
*/
nano_sem_init(&dev.le_pkts_sem);
for (i = 0; i < dev.le_pkts; i++) {
nano_sem_give(&dev.le_pkts_sem);
}
return 0;
}
int bt_hci_reset(void)
{
return hci_init();
}
/* Interface to HCI driver layer */
void bt_recv(struct bt_buf *buf)
{
struct bt_hci_evt_hdr *hdr;
BT_DBG("buf %p len %u\n", buf, buf->len);
if (buf->type == BT_ACL_IN) {
nano_fifo_put(&dev.rx_queue, buf);
return;
}
if (buf->type != BT_EVT) {
BT_ERR("Invalid buf type %u\n", buf->type);
bt_buf_put(buf);
return;
}
/* Command Complete/Status events have their own cmd_rx queue,
* all other events go through rx queue.
*/
hdr = (void *)buf->data;
if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE ||
hdr->evt == BT_HCI_EVT_CMD_STATUS) {
nano_fifo_put(&dev.cmd_rx_queue, buf);
return;
}
nano_fifo_put(&dev.rx_queue, buf);
}
int bt_driver_register(struct bt_driver *drv)
{
if (dev.drv) {
return -EALREADY;
}
if (!drv->open || !drv->send) {
return -EINVAL;
}
dev.drv = drv;
return 0;
}
void bt_driver_unregister(struct bt_driver *drv)
{
dev.drv = NULL;
}
/* fibers, fifos and semaphores initialization */
static void cmd_queue_init(void)
{
nano_fifo_init(&dev.cmd_tx_queue);
nano_sem_init(&dev.ncmd_sem);
/* Give cmd_sem allowing to send first HCI_Reset cmd */
dev.ncmd = 1;
nano_task_sem_give(&dev.ncmd_sem);
fiber_start(cmd_tx_fiber_stack, CMD_TX_STACK_SIZE,
(nano_fiber_entry_t)hci_cmd_tx_fiber, 0, 0, 7, 0);
}
static void rx_queue_init(void)
{
nano_fifo_init(&dev.rx_queue);
fiber_start(rx_fiber_stack, RX_STACK_SIZE,
(nano_fiber_entry_t)hci_rx_fiber, 0, 0, 7, 0);
nano_fifo_init(&dev.cmd_rx_queue);
fiber_start(cmd_rx_fiber_stack, RX_STACK_SIZE,
(nano_fiber_entry_t)cmd_rx_fiber, 0, 0, 7, 0);
}
int bt_init(void)
{
struct bt_driver *drv = dev.drv;
int err;
if (!drv) {
BT_ERR("No HCI driver registered\n");
return -ENODEV;
}
bt_buf_init(ACL_IN_MAX, ACL_OUT_MAX);
cmd_queue_init();
rx_queue_init();
err = drv->open();
if (err) {
BT_ERR("HCI driver open failed (%d)\n", err);
return err;
}
bt_l2cap_init();
return hci_init();
}
int bt_start_advertising(uint8_t type, const struct bt_eir *ad,
const struct bt_eir *sd)
{
struct bt_buf *buf;
struct bt_hci_cp_le_set_adv_data *set_data;
struct bt_hci_cp_le_set_adv_data *scan_rsp;
struct bt_hci_cp_le_set_adv_parameters *set_param;
int i;
if (!ad) {
goto send_scan_rsp;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_DATA, sizeof(*set_data));
if (!buf) {
return -ENOBUFS;
}
set_data = bt_buf_add(buf, sizeof(*set_data));
memset(set_data, 0, sizeof(*set_data));
for (i = 0; ad[i].len; i++) {
/* Check if ad fit in the remaining buffer */
if (set_data->len + ad[i].len + 1 > 29) {
break;
}
memcpy(&set_data->data[set_data->len], &ad[i], ad[i].len + 1);
set_data->len += ad[i].len + 1;
}
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_DATA, buf);
send_scan_rsp:
if (!sd) {
goto send_set_param;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_RSP_DATA,
sizeof(*scan_rsp));
if (!buf) {
return -ENOBUFS;
}
scan_rsp = bt_buf_add(buf, sizeof(*scan_rsp));
memset(scan_rsp, 0, sizeof(*scan_rsp));
for (i = 0; sd[i].len; i++) {
/* Check if ad fit in the remaining buffer */
if (scan_rsp->len + sd[i].len + 1 > 29) {
break;
}
memcpy(&scan_rsp->data[scan_rsp->len], &sd[i], sd[i].len + 1);
scan_rsp->len += sd[i].len + 1;
}
bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, buf);
send_set_param:
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAMETERS,
sizeof(*set_param));
if (!buf) {
return -ENOBUFS;
}
set_param = bt_buf_add(buf, sizeof(*set_param));
memset(set_param, 0, sizeof(*set_param));
set_param->min_interval = sys_cpu_to_le16(0x0800);
set_param->max_interval = sys_cpu_to_le16(0x0800);
set_param->type = type;
set_param->channel_map = 0x07;
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_PARAMETERS, buf);
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
if (!buf) {
return -ENOBUFS;
}
dev.adv_enable = 0x01;
memcpy(bt_buf_add(buf, 1), &dev.adv_enable, 1);
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
}
int bt_start_scanning(uint8_t scan_type, uint8_t scan_filter)
{
struct bt_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;
if (dev.scan_enable == BT_LE_SCAN_ENABLE) {
return -EALREADY;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS,
sizeof(*set_param));
if (!buf) {
return -ENOBUFS;
}
set_param = bt_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(0x0010);
set_param->window = sys_cpu_to_le16(0x0010);
set_param->filter_policy = 0x00;
set_param->addr_type = 0x00;
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 = bt_buf_add(buf, sizeof(*scan_enable));
memset(scan_enable, 0, sizeof(*scan_enable));
scan_enable->filter_dup = scan_filter;
scan_enable->enable = BT_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 */
if (!rsp->data[0]) {
dev.scan_enable = BT_LE_SCAN_ENABLE;
}
bt_buf_put(rsp);
return 0;
}
int bt_stop_scanning()
{
struct bt_buf *buf, *rsp;
struct bt_hci_cp_le_set_scan_enable *scan_enable;
int err;
if (dev.scan_enable == BT_LE_SCAN_DISABLE) {
return -EALREADY;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
sizeof(*scan_enable));
if (!buf) {
return -ENOBUFS;
}
scan_enable = bt_buf_add(buf, sizeof(*scan_enable));
memset(scan_enable, 0x0, sizeof(*scan_enable));
scan_enable->filter_dup = 0x00;
scan_enable->enable = BT_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 */
if (!rsp->data[0]) {
dev.scan_enable = BT_LE_SCAN_DISABLE;
}
bt_buf_put(rsp);
return 0;
}