2015-04-14 14:38:13 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2015-04-14 15:04:46 +03:00
|
|
|
#include <nanokernel.h>
|
2015-06-03 10:35:58 -04:00
|
|
|
#include <arch/cpu.h>
|
2015-04-14 15:04:46 +03:00
|
|
|
#include <toolchain.h>
|
2015-05-25 22:59:53 +03:00
|
|
|
#include <sections.h>
|
2015-04-14 15:04:46 +03:00
|
|
|
#include <string.h>
|
2015-05-08 20:53:39 +02:00
|
|
|
#include <stdio.h>
|
2015-04-14 15:04:46 +03:00
|
|
|
#include <errno.h>
|
2015-05-21 15:25:14 +03:00
|
|
|
#include <misc/util.h>
|
2015-04-14 15:04:46 +03:00
|
|
|
#include <misc/byteorder.h>
|
|
|
|
|
2015-06-16 17:25:37 +03:00
|
|
|
#include <bluetooth/log.h>
|
2015-04-14 14:38:13 +03:00
|
|
|
#include <bluetooth/bluetooth.h>
|
2015-05-25 09:13:33 +03:00
|
|
|
#include <bluetooth/hci.h>
|
2015-04-14 14:38:13 +03:00
|
|
|
|
2015-04-20 15:11:02 +03:00
|
|
|
#include "hci_core.h"
|
2015-05-30 18:05:26 +07:00
|
|
|
#include "keys.h"
|
2015-06-15 11:05:35 +03:00
|
|
|
#include "conn_internal.h"
|
2015-05-21 18:53:13 +03:00
|
|
|
#include "l2cap.h"
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-04-24 12:02:36 +03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
|
|
|
|
#undef BT_DBG
|
|
|
|
#define BT_DBG(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2015-04-28 11:51:28 +03:00
|
|
|
/* How many buffers to use for incoming ACL data */
|
|
|
|
#define ACL_IN_MAX 7
|
|
|
|
#define ACL_OUT_MAX 7
|
|
|
|
|
2015-04-14 15:32:32 +03:00
|
|
|
/* Stacks for the fibers */
|
2015-05-26 13:20:54 +03:00
|
|
|
static BT_STACK_NOINIT(rx_fiber_stack, 1024);
|
2015-06-03 23:47:06 +07:00
|
|
|
static BT_STACK_NOINIT(rx_prio_fiber_stack, 256);
|
2015-05-26 13:20:54 +03:00
|
|
|
static BT_STACK_NOINIT(cmd_tx_fiber_stack, 256);
|
2015-04-14 15:32:32 +03:00
|
|
|
|
2015-05-22 15:45:12 +03:00
|
|
|
#if defined(CONFIG_BLUETOOTH_DEBUG)
|
2015-06-03 23:47:06 +07:00
|
|
|
static nano_context_id_t rx_prio_fiber_id;
|
2015-05-22 15:45:12 +03:00
|
|
|
#endif
|
|
|
|
|
2015-04-20 15:11:02 +03:00
|
|
|
static struct bt_dev dev;
|
2015-04-14 15:32:32 +03:00
|
|
|
|
2015-06-02 21:12:17 +02:00
|
|
|
static struct bt_conn_cb *callback_list;
|
2015-06-02 17:28:00 +02:00
|
|
|
static bt_le_scan_cb_t *scan_dev_found_cb;
|
2015-06-02 21:12:17 +02:00
|
|
|
|
2015-05-25 09:13:33 +03:00
|
|
|
#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);
|
2015-06-17 15:46:38 +02:00
|
|
|
bt_addr_to_str(addr, str, sizeof(bufs[cur]));
|
2015-05-25 09:13:33 +03:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *bt_addr_le_str(const bt_addr_le_t *addr)
|
2015-05-08 20:53:39 +02:00
|
|
|
{
|
2015-05-25 09:13:33 +03:00
|
|
|
static char bufs[2][27];
|
|
|
|
static uint8_t cur;
|
2015-06-17 15:46:38 +02:00
|
|
|
char *str;
|
2015-05-25 09:13:33 +03:00
|
|
|
|
|
|
|
str = bufs[cur++];
|
|
|
|
cur %= ARRAY_SIZE(bufs);
|
2015-06-17 15:46:38 +02:00
|
|
|
bt_addr_le_to_str(addr, str, sizeof(bufs[cur]));
|
2015-05-08 20:53:39 +02:00
|
|
|
|
2015-05-25 09:13:33 +03:00
|
|
|
return str;
|
2015-05-08 20:53:39 +02:00
|
|
|
}
|
2015-05-25 09:13:33 +03:00
|
|
|
#endif /* CONFIG_BLUETOOTH_DEBUG */
|
2015-05-08 20:53:39 +02:00
|
|
|
|
2015-06-02 21:12:17 +02:00
|
|
|
static void bt_connected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->connected) {
|
2015-06-15 11:23:04 +03:00
|
|
|
cb->connected(conn);
|
2015-06-02 21:12:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bt_disconnected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->disconnected) {
|
2015-06-15 11:23:04 +03:00
|
|
|
cb->disconnected(conn);
|
2015-06-02 21:12:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_conn_cb_register(struct bt_conn_cb *cb)
|
|
|
|
{
|
|
|
|
cb->_next = callback_list;
|
|
|
|
callback_list = cb;
|
|
|
|
}
|
|
|
|
|
2015-04-28 14:27:19 +03:00
|
|
|
struct bt_buf *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len)
|
2015-04-14 15:41:55 +03:00
|
|
|
{
|
|
|
|
struct bt_hci_cmd_hdr *hdr;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
BT_DBG("opcode %x param_len %u\n", opcode, param_len);
|
|
|
|
|
2015-04-28 11:19:26 +03:00
|
|
|
buf = bt_buf_get(BT_CMD, dev.drv->head_reserve);
|
2015-04-14 15:41:55 +03:00
|
|
|
if (!buf) {
|
|
|
|
BT_ERR("Cannot get free buffer\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("buf %p\n", buf);
|
|
|
|
|
2015-04-28 15:18:13 +03:00
|
|
|
buf->hci.opcode = opcode;
|
|
|
|
buf->hci.sync = NULL;
|
2015-04-14 15:41:55 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
2015-04-14 15:41:55 +03:00
|
|
|
hdr->opcode = sys_cpu_to_le16(opcode);
|
|
|
|
hdr->param_len = param_len;
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2015-04-28 14:27:19 +03:00
|
|
|
int bt_hci_cmd_send(uint16_t opcode, struct bt_buf *buf)
|
2015-04-14 15:41:55 +03:00
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-14 15:41:55 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("opcode %x len %u\n", opcode, buf->len);
|
|
|
|
|
2015-04-28 15:22:17 +03:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2015-05-23 19:33:51 +03:00
|
|
|
nano_fifo_put(&dev.cmd_tx_queue, buf);
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-08 12:55:03 +03:00
|
|
|
int bt_hci_cmd_send_sync(uint16_t opcode, struct bt_buf *buf,
|
|
|
|
struct bt_buf **rsp)
|
2015-04-14 15:44:48 +03:00
|
|
|
{
|
|
|
|
struct nano_sem sync_sem;
|
2015-04-30 12:01:48 +03:00
|
|
|
int err;
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-05-22 15:45:12 +03:00
|
|
|
/* 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)
|
2015-06-03 23:47:06 +07:00
|
|
|
if (context_self_get() == rx_prio_fiber_id) {
|
2015-05-22 15:45:12 +03:00
|
|
|
BT_ERR("called from invalid context!\n");
|
|
|
|
return -EDEADLK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
if (!buf) {
|
|
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-14 15:44:48 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("opcode %x len %u\n", opcode, buf->len);
|
|
|
|
|
|
|
|
nano_sem_init(&sync_sem);
|
2015-04-28 15:18:13 +03:00
|
|
|
buf->hci.sync = &sync_sem;
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-05-23 19:33:51 +03:00
|
|
|
nano_fifo_put(&dev.cmd_tx_queue, buf);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
nano_sem_take_wait(&sync_sem);
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
/* Indicate failure if we failed to get the return parameters */
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf->hci.sync) {
|
2015-04-30 12:01:48 +03:00
|
|
|
err = -EIO;
|
2015-05-05 10:50:14 +03:00
|
|
|
} else {
|
2015-04-30 12:01:48 +03:00
|
|
|
err = 0;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 12:01:48 +03:00
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (rsp) {
|
2015-04-30 12:01:48 +03:00
|
|
|
*rsp = buf->hci.sync;
|
2015-05-05 10:50:14 +03:00
|
|
|
} else if (buf->hci.sync) {
|
2015-04-30 12:01:48 +03:00
|
|
|
bt_buf_put(buf->hci.sync);
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 12:01:48 +03:00
|
|
|
|
|
|
|
bt_buf_put(buf);
|
|
|
|
|
|
|
|
return err;
|
2015-04-14 15:44:48 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
static void hci_acl(struct bt_buf *buf)
|
|
|
|
{
|
2015-04-14 15:44:48 +03:00
|
|
|
struct bt_hci_acl_hdr *hdr = (void *)buf->data;
|
|
|
|
uint16_t handle, len = sys_le16_to_cpu(hdr->len);
|
2015-04-28 10:39:50 +03:00
|
|
|
struct bt_conn *conn;
|
2015-04-14 15:44:48 +03:00
|
|
|
uint8_t flags;
|
|
|
|
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("buf %p\n", buf);
|
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
handle = sys_le16_to_cpu(hdr->handle);
|
|
|
|
flags = (handle >> 12);
|
2015-04-28 15:20:33 +03:00
|
|
|
buf->acl.handle = bt_acl_handle(handle);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
bt_buf_pull(buf, sizeof(*hdr));
|
|
|
|
|
2015-04-28 15:20:33 +03:00
|
|
|
BT_DBG("handle %u len %u flags %u\n", buf->acl.handle, len, flags);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
if (buf->len != len) {
|
|
|
|
BT_ERR("ACL data length mismatch (%u != %u)\n", buf->len, len);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-27 21:08:39 +03:00
|
|
|
conn = bt_conn_lookup_handle(buf->acl.handle);
|
2015-06-24 14:39:14 +02:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-04-28 15:20:33 +03:00
|
|
|
BT_ERR("Unable to find conn for handle %u\n", buf->acl.handle);
|
2015-04-28 10:39:50 +03:00
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_recv(conn, buf, flags);
|
2015-06-17 12:42:54 +03:00
|
|
|
bt_conn_put(conn);
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
2015-05-25 22:05:24 +03:00
|
|
|
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_PRINTK)
|
|
|
|
#include <offsets.h>
|
|
|
|
#include <misc/printk.h>
|
|
|
|
|
|
|
|
enum {
|
|
|
|
STACK_DIRECTION_UP,
|
|
|
|
STACK_DIRECTION_DOWN,
|
|
|
|
};
|
|
|
|
|
2015-05-26 14:15:26 +03:00
|
|
|
static void analyze_stack(const char *name, const char *stack, unsigned size,
|
|
|
|
int stack_growth)
|
2015-05-25 22:05:24 +03:00
|
|
|
{
|
2015-05-26 14:15:26 +03:00
|
|
|
unsigned i, stack_offset, pcnt, unused = 0;
|
2015-05-26 13:18:47 +03:00
|
|
|
|
|
|
|
/* The CCS is always placed on a 4-byte aligned boundary - if
|
|
|
|
* the stack beginning doesn't match that there will be some
|
|
|
|
* unused bytes in the beginning.
|
|
|
|
*/
|
|
|
|
stack_offset = __tCCS_SIZEOF + ((4 - ((unsigned)stack % 4)) % 4);
|
2015-05-25 22:05:24 +03:00
|
|
|
|
|
|
|
if (stack_growth == STACK_DIRECTION_DOWN) {
|
2015-05-26 13:18:47 +03:00
|
|
|
for (i = stack_offset; i < size; i++) {
|
2015-05-25 22:05:24 +03:00
|
|
|
if ((unsigned char)stack[i] == 0xaa) {
|
|
|
|
unused++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-05-26 13:18:47 +03:00
|
|
|
for (i = size - 1; i >= stack_offset; i--) {
|
2015-05-25 22:05:24 +03:00
|
|
|
if ((unsigned char)stack[i] == 0xaa) {
|
|
|
|
unused++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:15:26 +03:00
|
|
|
/* Calculate the real size reserved for the stack */
|
|
|
|
size -= stack_offset;
|
|
|
|
pcnt = ((size - unused) * 100) / size;
|
|
|
|
|
|
|
|
printk("%s (real size %u):\tunused %u\tusage %u / %u (%u %%)\n", name,
|
|
|
|
size + stack_offset, unused, size - unused, size, pcnt);
|
2015-05-25 22:05:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void analyze_stacks(struct bt_conn *conn, struct bt_conn **ref)
|
|
|
|
{
|
|
|
|
int stack_growth;
|
|
|
|
|
|
|
|
printk("sizeof(tCCS) = %u\n", __tCCS_SIZEOF);
|
|
|
|
|
|
|
|
if (conn > *ref) {
|
|
|
|
printk("stack grows up\n");
|
|
|
|
stack_growth = STACK_DIRECTION_UP;
|
|
|
|
} else {
|
|
|
|
printk("stack grows down\n");
|
|
|
|
stack_growth = STACK_DIRECTION_DOWN;
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:15:26 +03:00
|
|
|
analyze_stack("rx stack", rx_fiber_stack, sizeof(rx_fiber_stack),
|
|
|
|
stack_growth);
|
2015-06-03 23:47:06 +07:00
|
|
|
analyze_stack("cmd rx stack", rx_prio_fiber_stack,
|
|
|
|
sizeof(rx_prio_fiber_stack), stack_growth);
|
2015-05-26 14:15:26 +03:00
|
|
|
analyze_stack("cmd tx stack", cmd_tx_fiber_stack,
|
|
|
|
sizeof(cmd_tx_fiber_stack), stack_growth);
|
|
|
|
analyze_stack("conn tx stack", conn->tx_stack, sizeof(conn->tx_stack),
|
|
|
|
stack_growth);
|
2015-05-25 22:05:24 +03:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define analyze_stacks(...)
|
|
|
|
#endif
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
/* HCI event processing */
|
|
|
|
|
2015-04-18 18:52:54 +03:00
|
|
|
static void hci_disconn_complete(struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_hci_evt_disconn_complete *evt = (void *)buf->data;
|
2015-05-21 20:59:23 +03:00
|
|
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
2015-04-28 10:39:50 +03:00
|
|
|
struct bt_conn *conn;
|
2015-04-18 18:52:54 +03:00
|
|
|
|
|
|
|
BT_DBG("status %u handle %u reason %u\n", evt->status, handle,
|
|
|
|
evt->reason);
|
|
|
|
|
2015-05-21 20:59:23 +03:00
|
|
|
if (evt->status) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-27 21:08:39 +03:00
|
|
|
conn = bt_conn_lookup_handle(handle);
|
2015-04-28 10:39:50 +03:00
|
|
|
if (!conn) {
|
|
|
|
BT_ERR("Unable to look up conn with handle %u\n", handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-21 19:03:17 +03:00
|
|
|
bt_l2cap_disconnected(conn);
|
2015-06-02 21:12:17 +02:00
|
|
|
bt_disconnected(conn);
|
2015-05-21 19:03:17 +03:00
|
|
|
|
2015-05-25 22:05:24 +03:00
|
|
|
/* Check stack usage (no-op if not enabled) */
|
|
|
|
analyze_stacks(conn, &conn);
|
|
|
|
|
2015-06-19 18:56:13 +02:00
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
2015-06-17 12:42:54 +03:00
|
|
|
bt_conn_put(conn);
|
2015-06-23 11:56:50 +02:00
|
|
|
conn->handle = 0;
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-04-18 18:52:54 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 21:00:50 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-05-27 21:08:39 +03:00
|
|
|
conn = bt_conn_lookup_handle(handle);
|
2015-06-24 14:39:14 +02:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-05-21 21:00:50 +03:00
|
|
|
BT_ERR("Unable to look up conn with handle %u\n", handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->encrypt = evt->encrypt;
|
2015-05-21 19:03:17 +03:00
|
|
|
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
2015-06-17 12:42:54 +03:00
|
|
|
bt_conn_put(conn);
|
2015-05-21 21:00:50 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
static void hci_reset_complete(struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
uint8_t status = buf->data[0];
|
|
|
|
|
|
|
|
BT_DBG("status %u\n", status);
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (status) {
|
2015-04-14 15:41:55 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-06-02 17:28:00 +02:00
|
|
|
|
|
|
|
scan_dev_found_cb = NULL;
|
|
|
|
dev.scan_enable = BT_LE_SCAN_DISABLE;
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
static void hci_cmd_done(uint16_t opcode, uint8_t status, struct bt_buf *buf)
|
2015-04-14 15:41:55 +03:00
|
|
|
{
|
|
|
|
struct bt_buf *sent = dev.sent_cmd;
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!sent) {
|
2015-04-28 15:18:13 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 15:18:13 +03:00
|
|
|
|
|
|
|
if (dev.sent_cmd->hci.opcode != opcode) {
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_ERR("Unexpected completion of opcode 0x%04x\n", opcode);
|
2015-04-14 15:41:55 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev.sent_cmd = NULL;
|
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
|
2015-04-30 12:01:48 +03:00
|
|
|
if (sent->hci.sync) {
|
|
|
|
struct nano_sem *sem = sent->hci.sync;
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (status) {
|
2015-04-30 12:01:48 +03:00
|
|
|
sent->hci.sync = NULL;
|
2015-05-05 10:50:14 +03:00
|
|
|
} else {
|
2015-04-30 12:01:48 +03:00
|
|
|
sent->hci.sync = bt_buf_hold(buf);
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
nano_fiber_sem_give(sem);
|
|
|
|
} else {
|
|
|
|
bt_buf_put(sent);
|
|
|
|
}
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-04-30 12:01:48 +03:00
|
|
|
uint8_t *status;
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
BT_DBG("opcode %x\n", opcode);
|
|
|
|
|
|
|
|
bt_buf_pull(buf, sizeof(*evt));
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
/* All command return parameters have a 1-byte status in the
|
|
|
|
* beginning, so we can safely make this generalization.
|
|
|
|
*/
|
|
|
|
status = buf->data;
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
switch (opcode) {
|
|
|
|
case BT_HCI_OP_RESET:
|
|
|
|
hci_reset_complete(buf);
|
|
|
|
break;
|
|
|
|
default:
|
2015-05-13 15:00:58 +03:00
|
|
|
BT_DBG("Unhandled opcode %x\n", opcode);
|
2015-04-14 15:41:55 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
hci_cmd_done(opcode, *status, buf);
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
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:
|
2015-05-13 15:00:58 +03:00
|
|
|
BT_DBG("Unhandled opcode %x\n", opcode);
|
2015-04-14 15:41:55 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-25 13:16:49 +03:00
|
|
|
hci_cmd_done(opcode, evt->status, buf);
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
if (evt->ncmd && !dev.ncmd) {
|
|
|
|
/* Allow next command to be sent */
|
|
|
|
dev.ncmd = 1;
|
|
|
|
nano_fiber_sem_give(&dev.ncmd_sem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-18 11:56:00 +03:00
|
|
|
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);
|
|
|
|
|
2015-05-12 14:59:16 +03:00
|
|
|
while (count--)
|
2015-05-12 14:57:38 +03:00
|
|
|
nano_fiber_sem_give(&dev.le_pkts_sem);
|
2015-04-18 11:56:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 09:22:36 +07:00
|
|
|
static void hci_encrypt_key_refresh_complete(struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_hci_evt_encrypt_key_refresh_complete *evt = (void *)buf->data;
|
|
|
|
struct bt_conn *conn;
|
|
|
|
uint16_t handle;
|
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
|
2015-06-02 11:25:01 +03:00
|
|
|
BT_DBG("status %u handle %u\n", evt->status, handle);
|
2015-06-01 09:22:36 +07:00
|
|
|
|
|
|
|
if (evt->status) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
2015-06-24 14:39:14 +02:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-06-01 09:22:36 +07:00
|
|
|
BT_ERR("Unable to look up conn with handle %u\n", handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
2015-06-17 12:42:54 +03:00
|
|
|
bt_conn_put(conn);
|
2015-06-01 09:22:36 +07:00
|
|
|
}
|
|
|
|
|
2015-06-01 00:20:14 +07:00
|
|
|
static void copy_id_addr(struct bt_conn *conn, const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
struct bt_keys *keys;
|
|
|
|
|
|
|
|
/* If we have a keys struct we already know the identity */
|
|
|
|
if (conn->keys) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
keys = bt_keys_find_irk(addr);
|
|
|
|
if (keys) {
|
|
|
|
bt_addr_le_copy(&conn->dst, &keys->addr);
|
|
|
|
conn->keys = keys;
|
|
|
|
} else {
|
|
|
|
bt_addr_le_copy(&conn->dst, addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-18 18:44:38 +03:00
|
|
|
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);
|
2015-04-28 10:39:50 +03:00
|
|
|
struct bt_conn *conn;
|
2015-04-18 18:44:38 +03:00
|
|
|
|
2015-05-25 22:11:28 +03:00
|
|
|
BT_DBG("status %u handle %u role %u %s\n", evt->status, handle,
|
|
|
|
evt->role, bt_addr_le_str(&evt->peer_addr));
|
2015-05-21 20:59:23 +03:00
|
|
|
|
2015-06-23 14:38:24 +02:00
|
|
|
/* Make lookup to check if there's a connection object in CONNECT state
|
|
|
|
* associated with passed peer LE address.
|
|
|
|
*/
|
|
|
|
conn = bt_conn_lookup_addr_le(&evt->peer_addr);
|
2015-06-24 07:53:50 +02:00
|
|
|
if (conn && conn->state != BT_CONN_CONNECT &&
|
|
|
|
conn->state != BT_CONN_DISCONNECT) {
|
2015-06-30 11:59:08 +03:00
|
|
|
bt_conn_put(conn);
|
|
|
|
conn = NULL;
|
2015-06-23 14:38:24 +02:00
|
|
|
}
|
|
|
|
|
2015-05-21 20:59:23 +03:00
|
|
|
if (evt->status) {
|
2015-06-30 10:23:09 +03:00
|
|
|
if (!conn) {
|
2015-06-23 14:38:24 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
|
|
|
|
/* Drop the reference got by lookup call in CONNECT state.
|
|
|
|
* We are now in DISCONNECTED state since no successful LE
|
|
|
|
* link been made.
|
|
|
|
*/
|
|
|
|
bt_conn_put(conn);
|
|
|
|
|
2015-05-21 20:59:23 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-06-23 14:38:24 +02:00
|
|
|
if (!conn) {
|
|
|
|
conn = bt_conn_add(&dev, &evt->peer_addr, evt->role);
|
|
|
|
}
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
if (!conn) {
|
|
|
|
BT_ERR("Unable to add new conn for handle %u\n", handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-23 11:56:50 +02:00
|
|
|
conn->handle = handle;
|
2015-05-25 09:13:33 +03:00
|
|
|
conn->src.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
memcpy(conn->src.val, dev.bdaddr.val, sizeof(dev.bdaddr.val));
|
2015-06-01 00:20:14 +07:00
|
|
|
copy_id_addr(conn, &evt->peer_addr);
|
2015-04-28 10:39:50 +03:00
|
|
|
conn->le_conn_interval = sys_le16_to_cpu(evt->interval);
|
2015-06-23 14:38:24 +02:00
|
|
|
|
2015-06-23 11:56:50 +02:00
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
|
|
|
2015-06-30 10:23:41 +03:00
|
|
|
bt_l2cap_connected(conn);
|
|
|
|
|
2015-06-23 11:56:50 +02:00
|
|
|
if (evt->role == BT_HCI_ROLE_SLAVE) {
|
|
|
|
bt_l2cap_update_conn_param(conn);
|
|
|
|
}
|
2015-05-21 19:03:17 +03:00
|
|
|
|
2015-06-02 21:12:17 +02:00
|
|
|
bt_connected(conn);
|
2015-04-18 18:44:38 +03:00
|
|
|
}
|
|
|
|
|
2015-05-08 20:53:39 +02:00
|
|
|
static void le_adv_report(struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
uint8_t num_reports = buf->data[0];
|
2015-05-18 10:32:58 +03:00
|
|
|
struct bt_hci_ev_le_advertising_info *info;
|
2015-05-08 20:53:39 +02:00
|
|
|
|
|
|
|
BT_DBG("Adv number of reports %u\n", num_reports);
|
|
|
|
|
2015-05-18 10:32:58 +03:00
|
|
|
info = bt_buf_pull(buf, sizeof(num_reports));
|
|
|
|
|
2015-05-08 20:53:39 +02:00
|
|
|
while (num_reports--) {
|
|
|
|
int8_t rssi = info->data[info->length];
|
2015-06-01 10:25:05 +07:00
|
|
|
struct bt_keys *keys;
|
2015-06-02 17:28:00 +02:00
|
|
|
bt_addr_le_t addr;
|
2015-05-08 20:53:39 +02:00
|
|
|
|
2015-05-25 22:11:28 +03:00
|
|
|
BT_DBG("%s event %u, len %u, rssi %d dBm\n",
|
|
|
|
bt_addr_le_str(&info->addr),
|
2015-05-08 20:53:39 +02:00
|
|
|
info->evt_type, info->length, rssi);
|
|
|
|
|
2015-06-01 10:25:05 +07:00
|
|
|
keys = bt_keys_find_irk(&info->addr);
|
|
|
|
if (keys) {
|
2015-06-02 17:28:00 +02:00
|
|
|
bt_addr_le_copy(&addr, &keys->addr);
|
2015-06-01 10:25:05 +07:00
|
|
|
BT_DBG("Identity %s matched RPA %s\n",
|
|
|
|
bt_addr_le_str(&keys->addr),
|
|
|
|
bt_addr_le_str(&info->addr));
|
2015-06-02 17:28:00 +02:00
|
|
|
} else {
|
|
|
|
bt_addr_le_copy(&addr, &info->addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scan_dev_found_cb) {
|
|
|
|
scan_dev_found_cb(&addr, rssi, info->evt_type,
|
|
|
|
info->data, info->length);
|
2015-06-01 10:25:05 +07:00
|
|
|
}
|
|
|
|
|
2015-05-08 20:53:39 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
2015-05-18 10:24:58 +03:00
|
|
|
info = bt_buf_pull(buf, sizeof(*info) + info->length +
|
|
|
|
sizeof(rssi));
|
2015-05-08 20:53:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 15:30:08 +03:00
|
|
|
static void le_ltk_request(struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_hci_evt_le_ltk_request *evt = (void *)buf->data;
|
|
|
|
struct bt_conn *conn;
|
|
|
|
uint16_t handle;
|
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(evt->handle);
|
|
|
|
|
|
|
|
BT_DBG("handle %u\n", handle);
|
|
|
|
|
2015-05-27 21:08:39 +03:00
|
|
|
conn = bt_conn_lookup_handle(handle);
|
2015-06-24 14:39:14 +02:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-05-21 15:30:08 +03:00
|
|
|
BT_ERR("Unable to lookup conn for handle %u\n", handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-17 12:37:08 +03:00
|
|
|
if (!conn->keys) {
|
2015-06-01 00:17:48 +07:00
|
|
|
conn->keys = bt_keys_find(BT_KEYS_SLAVE_LTK, &conn->dst);
|
2015-06-17 12:37:08 +03:00
|
|
|
}
|
2015-06-01 00:17:48 +07:00
|
|
|
|
|
|
|
if (conn->keys && (conn->keys->keys & BT_KEYS_SLAVE_LTK) &&
|
|
|
|
conn->keys->slave_ltk.rand == evt->rand &&
|
|
|
|
conn->keys->slave_ltk.ediv == evt->ediv) {
|
2015-05-21 15:30:08 +03:00
|
|
|
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");
|
2015-06-17 12:42:54 +03:00
|
|
|
goto done;
|
2015-05-21 15:30:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cp = bt_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->handle = evt->handle;
|
2015-06-01 00:17:48 +07:00
|
|
|
memcpy(cp->ltk, conn->keys->slave_ltk.val, 16);
|
2015-05-21 15:30:08 +03:00
|
|
|
|
|
|
|
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");
|
2015-06-17 12:42:54 +03:00
|
|
|
goto done;
|
2015-05-21 15:30:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cp = bt_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->handle = evt->handle;
|
|
|
|
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf);
|
|
|
|
}
|
2015-06-17 12:42:54 +03:00
|
|
|
|
|
|
|
done:
|
|
|
|
bt_conn_put(conn);
|
2015-05-21 15:30:08 +03:00
|
|
|
}
|
|
|
|
|
2015-04-18 18:37:03 +03:00
|
|
|
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) {
|
2015-04-18 18:44:38 +03:00
|
|
|
case BT_HCI_EVT_LE_CONN_COMPLETE:
|
|
|
|
le_conn_complete(buf);
|
|
|
|
break;
|
2015-05-08 20:53:39 +02:00
|
|
|
case BT_HCI_EVT_LE_ADVERTISING_REPORT:
|
|
|
|
le_adv_report(buf);
|
|
|
|
break;
|
2015-05-21 15:30:08 +03:00
|
|
|
case BT_HCI_EVT_LE_LTK_REQUEST:
|
|
|
|
le_ltk_request(buf);
|
|
|
|
break;
|
2015-04-18 18:37:03 +03:00
|
|
|
default:
|
|
|
|
BT_DBG("Unhandled LE event %x\n", evt->subevent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
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) {
|
2015-04-18 18:52:54 +03:00
|
|
|
case BT_HCI_EVT_DISCONN_COMPLETE:
|
|
|
|
hci_disconn_complete(buf);
|
|
|
|
break;
|
2015-05-21 21:00:50 +03:00
|
|
|
case BT_HCI_EVT_ENCRYPT_CHANGE:
|
|
|
|
hci_encrypt_change(buf);
|
|
|
|
break;
|
2015-06-01 09:22:36 +07:00
|
|
|
case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE:
|
|
|
|
hci_encrypt_key_refresh_complete(buf);
|
|
|
|
break;
|
2015-04-18 18:37:03 +03:00
|
|
|
case BT_HCI_EVT_LE_META_EVENT:
|
|
|
|
hci_le_meta_event(buf);
|
|
|
|
break;
|
2015-04-14 15:41:55 +03:00
|
|
|
default:
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_WARN("Unhandled event 0x%02x\n", hdr->evt);
|
2015-04-14 15:41:55 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
bt_buf_put(buf);
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
2015-05-23 19:33:51 +03:00
|
|
|
static void hci_cmd_tx_fiber(void)
|
2015-04-14 15:41:55 +03:00
|
|
|
{
|
|
|
|
struct bt_driver *drv = dev.drv;
|
|
|
|
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("started\n");
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
/* Wait until ncmd > 0 */
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("calling sem_take_wait\n");
|
2015-04-14 15:41:55 +03:00
|
|
|
nano_fiber_sem_take_wait(&dev.ncmd_sem);
|
|
|
|
|
|
|
|
/* Get next command - wait if necessary */
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("calling fifo_get_wait\n");
|
2015-05-23 19:33:51 +03:00
|
|
|
buf = nano_fifo_get_wait(&dev.cmd_tx_queue);
|
2015-04-14 15:41:55 +03:00
|
|
|
dev.ncmd = 0;
|
|
|
|
|
2015-04-28 15:16:03 +03:00
|
|
|
BT_DBG("Sending command %x (buf %p) to driver\n",
|
|
|
|
buf->hci.opcode, buf);
|
2015-04-14 15:41:55 +03:00
|
|
|
|
|
|
|
drv->send(buf);
|
2015-04-28 15:16:03 +03:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
dev.sent_cmd = buf;
|
|
|
|
}
|
2015-04-14 15:32:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void hci_rx_fiber(void)
|
|
|
|
{
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("started\n");
|
2015-04-14 15:32:32 +03:00
|
|
|
|
|
|
|
while (1) {
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("calling fifo_get_wait\n");
|
2015-04-14 15:32:32 +03:00
|
|
|
buf = nano_fifo_get_wait(&dev.rx_queue);
|
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len);
|
|
|
|
|
|
|
|
switch (buf->type) {
|
2015-05-23 20:13:34 +03:00
|
|
|
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);
|
2015-05-23 20:14:03 +03:00
|
|
|
bt_buf_put(buf);
|
|
|
|
break;
|
2015-04-14 15:44:48 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 15:32:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-03 23:47:06 +07:00
|
|
|
static void rx_prio_fiber(void)
|
2015-05-23 19:58:06 +03:00
|
|
|
{
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
BT_DBG("started\n");
|
|
|
|
|
|
|
|
/* So we can avoid bt_hci_cmd_send_sync deadlocks */
|
|
|
|
#if defined(CONFIG_BLUETOOTH_DEBUG)
|
2015-06-03 23:47:06 +07:00
|
|
|
rx_prio_fiber_id = context_self_get();
|
2015-05-23 19:58:06 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct bt_hci_evt_hdr *hdr;
|
|
|
|
|
|
|
|
BT_DBG("calling fifo_get_wait\n");
|
2015-06-03 23:47:06 +07:00
|
|
|
buf = nano_fifo_get_wait(&dev.rx_prio_queue);
|
2015-05-23 19:58:06 +03:00
|
|
|
|
|
|
|
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;
|
2015-06-03 18:29:03 +07:00
|
|
|
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
|
|
|
|
hci_num_completed_packets(buf);
|
|
|
|
break;
|
2015-05-23 19:58:06 +03:00
|
|
|
default:
|
|
|
|
BT_ERR("Unknown event 0x%02x\n", hdr->evt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_buf_put(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-30 13:19:44 +03:00
|
|
|
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);
|
|
|
|
|
2015-05-25 09:13:33 +03:00
|
|
|
bt_addr_copy(&dev.bdaddr, &rp->bdaddr);
|
2015-04-30 13:19:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2015-05-20 08:19:40 +02:00
|
|
|
static void read_buffer_size_complete(struct bt_buf *buf)
|
2015-04-30 13:19:44 +03:00
|
|
|
{
|
|
|
|
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 */
|
2015-05-05 10:50:14 +03:00
|
|
|
if (dev.le_mtu) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
static int hci_init(void)
|
|
|
|
{
|
2015-04-20 12:16:57 +03:00
|
|
|
struct bt_hci_cp_host_buffer_size *hbs;
|
2015-04-14 15:44:48 +03:00
|
|
|
struct bt_hci_cp_set_event_mask *ev;
|
2015-04-30 13:19:44 +03:00
|
|
|
struct bt_buf *buf, *rsp;
|
2015-04-20 12:16:57 +03:00
|
|
|
uint8_t *enable;
|
2015-05-12 14:57:38 +03:00
|
|
|
int i, err;
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
/* Send HCI_RESET */
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_RESET, NULL);
|
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
/* Read Local Supported Features */
|
2015-04-30 13:19:44 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
read_local_features_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
/* Read Local Version Information */
|
2015-04-30 13:19:44 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL,
|
|
|
|
&rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
read_local_ver_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
/* Read Bluetooth Address */
|
2015-04-30 13:19:44 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
read_bdaddr_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
/* 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 */
|
2015-04-30 13:19:44 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL,
|
|
|
|
&rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
read_le_features_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
/* Read LE Buffer Size */
|
2015-04-30 13:19:44 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL, &rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 13:19:44 +03:00
|
|
|
le_read_buffer_size_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-05-08 12:49:09 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-14 15:44:48 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
ev = bt_buf_add(buf, sizeof(*ev));
|
2015-04-14 15:44:48 +03:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-04-20 12:16:57 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, sizeof(*hbs));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-20 12:16:57 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-20 12:16:57 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
hbs = bt_buf_add(buf, sizeof(*hbs));
|
2015-04-20 12:16:57 +03:00
|
|
|
memset(hbs, 0, sizeof(*hbs));
|
|
|
|
hbs->acl_mtu = sys_cpu_to_le16(BT_BUF_MAX_DATA -
|
2015-05-20 13:58:52 +03:00
|
|
|
sizeof(struct bt_hci_acl_hdr) -
|
|
|
|
dev.drv->head_reserve);
|
2015-04-20 12:16:57 +03:00
|
|
|
hbs->acl_pkts = sys_cpu_to_le16(ACL_IN_MAX);
|
|
|
|
|
2015-05-20 08:19:40 +02:00
|
|
|
err = bt_hci_cmd_send(BT_HCI_OP_HOST_BUFFER_SIZE, buf);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-04-30 13:19:44 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-20 12:16:57 +03:00
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-20 12:16:57 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-20 12:16:57 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
enable = bt_buf_add(buf, sizeof(*enable));
|
2015-04-20 12:16:57 +03:00
|
|
|
*enable = 0x01;
|
2015-05-18 12:04:50 +02:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2015-04-20 12:16:57 +03:00
|
|
|
|
2015-04-14 15:44:48 +03:00
|
|
|
if (lmp_bredr_capable(dev)) {
|
|
|
|
struct bt_hci_cp_write_le_host_supp *cp;
|
|
|
|
|
|
|
|
/* Use BR/EDR buffer size if LE reports zero buffers */
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!dev.le_mtu) {
|
2015-05-20 13:54:46 +03:00
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE,
|
|
|
|
NULL, &rsp);
|
2015-05-20 08:19:40 +02:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
read_buffer_size_complete(rsp);
|
|
|
|
bt_buf_put(rsp);
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
|
|
|
|
sizeof(*cp));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-14 15:44:48 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:44:48 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
cp = bt_buf_add(buf, sizeof*cp);
|
2015-04-14 15:44:48 +03:00
|
|
|
|
|
|
|
/* Excplicitly enable LE for dual-mode controllers */
|
|
|
|
cp->le = 0x01;
|
|
|
|
cp->simul = 0x00;
|
2015-04-30 12:01:48 +03:00
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf,
|
|
|
|
NULL);
|
2015-04-14 15:44:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-05-12 14:57:38 +03:00
|
|
|
/* Initialize & prime the semaphore for counting controller-side
|
|
|
|
* available ACL packet buffers.
|
|
|
|
*/
|
|
|
|
nano_sem_init(&dev.le_pkts_sem);
|
2015-05-13 15:44:14 +03:00
|
|
|
for (i = 0; i < dev.le_pkts; i++) {
|
2015-05-12 14:57:38 +03:00
|
|
|
nano_sem_give(&dev.le_pkts_sem);
|
2015-05-13 15:44:14 +03:00
|
|
|
}
|
2015-05-12 14:57:38 +03:00
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-14 15:32:32 +03:00
|
|
|
/* Interface to HCI driver layer */
|
|
|
|
|
|
|
|
void bt_recv(struct bt_buf *buf)
|
|
|
|
{
|
2015-05-23 19:58:06 +03:00
|
|
|
struct bt_hci_evt_hdr *hdr;
|
|
|
|
|
2015-05-20 13:14:37 +03:00
|
|
|
BT_DBG("buf %p len %u\n", buf, buf->len);
|
2015-05-23 19:58:06 +03:00
|
|
|
|
|
|
|
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 ||
|
2015-06-03 18:29:03 +07:00
|
|
|
hdr->evt == BT_HCI_EVT_CMD_STATUS ||
|
|
|
|
hdr->evt == BT_HCI_EVT_NUM_COMPLETED_PACKETS) {
|
2015-06-03 23:47:06 +07:00
|
|
|
nano_fifo_put(&dev.rx_prio_queue, buf);
|
2015-05-23 19:58:06 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-14 15:32:32 +03:00
|
|
|
nano_fifo_put(&dev.rx_queue, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_driver_register(struct bt_driver *drv)
|
|
|
|
{
|
2015-05-05 10:50:14 +03:00
|
|
|
if (dev.drv) {
|
2015-04-14 15:32:32 +03:00
|
|
|
return -EALREADY;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:32:32 +03:00
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!drv->open || !drv->send) {
|
2015-04-14 15:32:32 +03:00
|
|
|
return -EINVAL;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:32:32 +03:00
|
|
|
|
|
|
|
dev.drv = drv;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_driver_unregister(struct bt_driver *drv)
|
|
|
|
{
|
|
|
|
dev.drv = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fibers, fifos and semaphores initialization */
|
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
static void cmd_queue_init(void)
|
|
|
|
{
|
2015-05-23 19:33:51 +03:00
|
|
|
nano_fifo_init(&dev.cmd_tx_queue);
|
2015-04-14 15:41:55 +03:00
|
|
|
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);
|
|
|
|
|
2015-05-26 13:03:46 +03:00
|
|
|
fiber_start(cmd_tx_fiber_stack, sizeof(cmd_tx_fiber_stack),
|
2015-05-23 21:00:09 +03:00
|
|
|
(nano_fiber_entry_t)hci_cmd_tx_fiber, 0, 0, 7, 0);
|
2015-04-14 15:41:55 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 15:32:32 +03:00
|
|
|
static void rx_queue_init(void)
|
|
|
|
{
|
|
|
|
nano_fifo_init(&dev.rx_queue);
|
2015-05-26 13:03:46 +03:00
|
|
|
fiber_start(rx_fiber_stack, sizeof(rx_fiber_stack),
|
2015-05-23 21:00:09 +03:00
|
|
|
(nano_fiber_entry_t)hci_rx_fiber, 0, 0, 7, 0);
|
2015-05-23 19:58:06 +03:00
|
|
|
|
2015-06-03 23:47:06 +07:00
|
|
|
nano_fifo_init(&dev.rx_prio_queue);
|
|
|
|
fiber_start(rx_prio_fiber_stack, sizeof(rx_prio_fiber_stack),
|
|
|
|
(nano_fiber_entry_t)rx_prio_fiber, 0, 0, 7, 0);
|
2015-04-14 15:32:32 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 14:38:13 +03:00
|
|
|
int bt_init(void)
|
|
|
|
{
|
2015-04-14 15:32:32 +03:00
|
|
|
struct bt_driver *drv = dev.drv;
|
|
|
|
int err;
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!drv) {
|
2015-05-21 15:22:17 +03:00
|
|
|
BT_ERR("No HCI driver registered\n");
|
2015-04-14 15:32:32 +03:00
|
|
|
return -ENODEV;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:32:32 +03:00
|
|
|
|
2015-05-12 15:03:23 +03:00
|
|
|
bt_buf_init(ACL_IN_MAX, ACL_OUT_MAX);
|
2015-04-28 11:51:28 +03:00
|
|
|
|
2015-04-14 15:41:55 +03:00
|
|
|
cmd_queue_init();
|
2015-04-14 15:32:32 +03:00
|
|
|
rx_queue_init();
|
|
|
|
|
|
|
|
err = drv->open();
|
2015-05-05 10:50:14 +03:00
|
|
|
if (err) {
|
2015-05-21 15:22:17 +03:00
|
|
|
BT_ERR("HCI driver open failed (%d)\n", err);
|
2015-04-14 15:32:32 +03:00
|
|
|
return err;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-14 15:04:46 +03:00
|
|
|
|
2015-05-21 18:53:13 +03:00
|
|
|
bt_l2cap_init();
|
|
|
|
|
2015-05-12 15:03:23 +03:00
|
|
|
return hci_init();
|
2015-04-14 14:38:13 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
2015-04-30 16:07:09 +03:00
|
|
|
int bt_start_advertising(uint8_t type, const struct bt_eir *ad,
|
|
|
|
const struct bt_eir *sd)
|
2015-04-17 13:59:34 +03:00
|
|
|
{
|
|
|
|
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;
|
2015-04-30 16:07:09 +03:00
|
|
|
int i;
|
2015-04-17 13:59:34 +03:00
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!ad) {
|
2015-04-30 16:07:09 +03:00
|
|
|
goto send_scan_rsp;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_DATA, sizeof(*set_data));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-17 13:59:34 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
set_data = bt_buf_add(buf, sizeof(*set_data));
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
memset(set_data, 0, sizeof(*set_data));
|
|
|
|
|
2015-04-30 16:07:09 +03:00
|
|
|
for (i = 0; ad[i].len; i++) {
|
|
|
|
/* Check if ad fit in the remaining buffer */
|
2015-05-05 10:50:14 +03:00
|
|
|
if (set_data->len + ad[i].len + 1 > 29) {
|
2015-04-30 16:07:09 +03:00
|
|
|
break;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 16:07:09 +03:00
|
|
|
|
|
|
|
memcpy(&set_data->data[set_data->len], &ad[i], ad[i].len + 1);
|
|
|
|
set_data->len += ad[i].len + 1;
|
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_DATA, buf);
|
|
|
|
|
2015-04-30 16:07:09 +03:00
|
|
|
send_scan_rsp:
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!sd) {
|
2015-04-30 16:07:09 +03:00
|
|
|
goto send_set_param;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 16:07:09 +03:00
|
|
|
|
2015-04-17 13:59:34 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_RSP_DATA,
|
|
|
|
sizeof(*scan_rsp));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-17 13:59:34 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
scan_rsp = bt_buf_add(buf, sizeof(*scan_rsp));
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
memset(scan_rsp, 0, sizeof(*scan_rsp));
|
|
|
|
|
2015-04-30 16:07:09 +03:00
|
|
|
for (i = 0; sd[i].len; i++) {
|
|
|
|
/* Check if ad fit in the remaining buffer */
|
2015-05-05 10:50:14 +03:00
|
|
|
if (scan_rsp->len + sd[i].len + 1 > 29) {
|
2015-04-30 16:07:09 +03:00
|
|
|
break;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-30 16:07:09 +03:00
|
|
|
|
|
|
|
memcpy(&scan_rsp->data[scan_rsp->len], &sd[i], sd[i].len + 1);
|
|
|
|
scan_rsp->len += sd[i].len + 1;
|
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, buf);
|
|
|
|
|
2015-04-30 16:07:09 +03:00
|
|
|
send_set_param:
|
2015-04-17 13:59:34 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAMETERS,
|
|
|
|
sizeof(*set_param));
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-17 13:59:34 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
set_param = bt_buf_add(buf, sizeof(*set_param));
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
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);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-17 13:59:34 +03:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-17 13:59:34 +03:00
|
|
|
|
|
|
|
dev.adv_enable = 0x01;
|
|
|
|
memcpy(bt_buf_add(buf, 1), &dev.adv_enable, 1);
|
|
|
|
|
2015-04-30 12:01:48 +03:00
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
2015-04-17 13:59:34 +03:00
|
|
|
}
|
2015-05-08 10:33:34 +02:00
|
|
|
|
2015-06-02 17:28:00 +02:00
|
|
|
int bt_start_scanning(uint8_t scan_filter, bt_le_scan_cb_t cb)
|
2015-05-08 10:33:34 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-02 17:28:00 +02:00
|
|
|
if (scan_dev_found_cb != NULL) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
2015-05-20 13:54:46 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS,
|
|
|
|
sizeof(*set_param));
|
2015-05-08 10:33:34 +02:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
set_param = bt_buf_add(buf, sizeof(*set_param));
|
2015-05-08 10:33:34 +02:00
|
|
|
memset(set_param, 0, sizeof(*set_param));
|
2015-06-18 09:28:30 +02:00
|
|
|
set_param->scan_type = BT_LE_SCAN_ACTIVE;
|
2015-05-08 10:33:34 +02:00
|
|
|
|
|
|
|
/* 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);
|
2015-05-20 13:54:46 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
|
|
|
|
sizeof(*scan_enable));
|
2015-05-08 10:33:34 +02:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
scan_enable = bt_buf_add(buf, sizeof(*scan_enable));
|
2015-05-08 10:33:34 +02:00
|
|
|
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;
|
2015-06-02 17:28:00 +02:00
|
|
|
scan_dev_found_cb = cb;
|
2015-05-08 10:33:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bt_buf_put(rsp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-16 15:21:52 +02:00
|
|
|
int bt_stop_scanning(void)
|
2015-05-08 10:33:34 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-05-20 13:54:46 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
|
|
|
|
sizeof(*scan_enable));
|
2015-05-08 10:33:34 +02:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
scan_enable = bt_buf_add(buf, sizeof(*scan_enable));
|
2015-05-08 10:33:34 +02:00
|
|
|
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;
|
2015-06-02 17:28:00 +02:00
|
|
|
scan_dev_found_cb = NULL;
|
2015-05-08 10:33:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bt_buf_put(rsp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-05-18 15:00:58 +02:00
|
|
|
|
2015-06-12 14:47:54 +02:00
|
|
|
int bt_hci_le_conn_update(uint16_t handle, uint16_t min, uint16_t max,
|
|
|
|
uint16_t latency, uint16_t timeout)
|
|
|
|
{
|
|
|
|
struct hci_cp_le_conn_update *conn_update;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
2015-06-18 12:51:27 +03:00
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE,
|
|
|
|
sizeof(*conn_update));
|
2015-06-12 14:47:54 +02:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_update = bt_buf_add(buf, sizeof(*conn_update));
|
|
|
|
memset(conn_update, 0, sizeof(*conn_update));
|
|
|
|
conn_update->handle = sys_cpu_to_le16(handle);
|
|
|
|
conn_update->conn_interval_min = sys_cpu_to_le16(min);
|
|
|
|
conn_update->conn_interval_max = sys_cpu_to_le16(max);
|
|
|
|
conn_update->conn_latency = sys_cpu_to_le16(latency);
|
|
|
|
conn_update->supervision_timeout = sys_cpu_to_le16(timeout);
|
|
|
|
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf);
|
|
|
|
}
|
|
|
|
|
2015-05-18 15:00:58 +02:00
|
|
|
static int hci_le_create_conn(const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
struct bt_buf *buf;
|
|
|
|
struct bt_hci_cp_le_create_conn *cp;
|
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp = bt_buf_add(buf, sizeof(*cp));
|
|
|
|
memset(cp, 0x0, sizeof(*cp));
|
|
|
|
bt_addr_le_copy(&cp->peer_addr, addr);
|
|
|
|
cp->conn_interval_max = sys_cpu_to_le16(0x0028);
|
|
|
|
cp->conn_interval_min = sys_cpu_to_le16(0x0018);
|
|
|
|
cp->scan_interval = sys_cpu_to_le16(0x0060);
|
|
|
|
cp->scan_window = sys_cpu_to_le16(0x0030);
|
|
|
|
cp->supervision_timeout = sys_cpu_to_le16(0x07D0);
|
|
|
|
|
2015-06-25 13:51:31 +02:00
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
|
2015-05-18 15:00:58 +02:00
|
|
|
}
|
|
|
|
|
2015-06-23 14:38:24 +02:00
|
|
|
struct bt_conn *bt_connect_le(const bt_addr_le_t *peer)
|
2015-05-18 15:00:58 +02:00
|
|
|
{
|
2015-06-23 14:38:24 +02:00
|
|
|
struct bt_conn *conn;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
conn = bt_conn_lookup_addr_le(peer);
|
|
|
|
if (conn) {
|
|
|
|
switch (conn->state) {
|
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
return conn;
|
|
|
|
default:
|
|
|
|
bt_conn_put(conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = bt_conn_add(&dev, peer, BT_HCI_ROLE_MASTER);
|
|
|
|
if (!conn) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = hci_le_create_conn(peer);
|
|
|
|
if (err) {
|
|
|
|
bt_conn_put(conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT);
|
|
|
|
|
|
|
|
return bt_conn_get(conn);
|
|
|
|
|
2015-05-18 15:00:58 +02:00
|
|
|
}
|
2015-06-12 15:18:15 +02:00
|
|
|
|
2015-06-24 07:53:50 +02:00
|
|
|
static int bt_hci_connect_le_cancel(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = bt_hci_cmd_send(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bt_hci_disconnect(struct bt_conn *conn, uint8_t reason)
|
2015-06-12 15:18:15 +02:00
|
|
|
{
|
|
|
|
struct bt_buf *buf;
|
|
|
|
struct bt_hci_cp_disconnect *disconn;
|
2015-06-24 07:53:50 +02:00
|
|
|
int err;
|
2015-06-12 15:18:15 +02:00
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
disconn = bt_buf_add(buf, sizeof(*disconn));
|
|
|
|
disconn->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
disconn->reason = reason;
|
|
|
|
|
2015-06-24 07:53:50 +02:00
|
|
|
err = bt_hci_cmd_send(BT_HCI_OP_DISCONNECT, buf);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_disconnect(struct bt_conn *conn, uint8_t reason)
|
|
|
|
{
|
|
|
|
switch (conn->state) {
|
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
return bt_hci_connect_le_cancel(conn);
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
return bt_hci_disconnect(conn, reason);
|
|
|
|
case BT_CONN_DISCONNECT:
|
|
|
|
return 0;
|
|
|
|
case BT_CONN_DISCONNECTED:
|
|
|
|
default:
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
2015-06-12 15:18:15 +02:00
|
|
|
}
|
2015-06-25 12:12:16 +02:00
|
|
|
|
|
|
|
int bt_hci_le_start_encryption(uint16_t handle, uint64_t rand, uint16_t ediv,
|
|
|
|
const uint8_t *ltk)
|
|
|
|
{
|
|
|
|
struct bt_hci_cp_le_start_encryption *cp;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp = bt_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->handle = sys_cpu_to_le16(handle);
|
|
|
|
cp->rand = rand;
|
|
|
|
cp->ediv = ediv;
|
|
|
|
memcpy(cp->ltk, ltk, sizeof(cp->ltk));
|
|
|
|
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL);
|
|
|
|
}
|