zephyr/subsys/usb/device_next/class/bt_hci.c

694 lines
20 KiB
C

/*
* Copyright (c) 2018 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Bluetooth HCI USB transport layer implementation with following
* endpoint configuration:
* - HCI commands through control endpoint (host-to-device only)
* - HCI events through interrupt IN endpoint
* - ACL data through one bulk IN and one bulk OUT endpoints
*
* TODO: revise transfer handling so that Bluetooth net_bufs can be used
* directly without intermediate copying.
*
* Limitations:
* - Remote wakeup before IN transfer is not yet supported.
* - H4 transport layer is not yet supported
*/
#include <zephyr/init.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/usb/usb_ch9.h>
#include <zephyr/drivers/usb/udc.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/hci_raw.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/hci_vs.h>
#include <zephyr/drivers/bluetooth/hci_driver.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_hci, CONFIG_USBD_BT_HCI_LOG_LEVEL);
#define BT_HCI_SUBCLASS 0x01
#define BT_HCI_PROTOCOL 0x01
/*
* The actual endpoint addresses may differ from the following because
* the resources may be reallocated by the stack depending on the number
* of functions in a configuration and the properties of the controller.
*/
#define BT_HCI_EP_EVENTS 0x81
#define BT_HCI_EP_ACL_DATA_IN 0x82
#define BT_HCI_EP_ACL_DATA_OUT 0x02
#define BT_HCI_EP_VOICE_IN 0x83
#define BT_HCI_EP_VOICE_OUT 0x03
#define BT_HCI_EP_MPS_EVENTS 16
#define BT_HCI_EP_FS_MPS_ACL_DATA 64
#define BT_HCI_EP_HS_MPS_ACL_DATA 512
#define BT_HCI_EP_MPS_VOICE 9
#define BT_HCI_EP_INTERVAL_EVENTS 1
#define BT_HCI_EP_INTERVAL_VOICE 3
#define BT_HCI_CLASS_ENABLED 0
#define BT_HCI_ACL_RX_ENGAGED 1
static K_FIFO_DEFINE(bt_hci_rx_queue);
static K_FIFO_DEFINE(bt_hci_tx_queue);
/*
* Transfers through three endpoints proceed in a synchronous manner,
* with maximum packet size of high speed bulk endpoint.
*
* REVISE: global (bulk, interrupt, iso) specific pools would be more
* RAM usage efficient.
*/
UDC_BUF_POOL_DEFINE(bt_hci_ep_pool,
3, 512,
sizeof(struct udc_buf_info), NULL);
/* HCI RX/TX threads */
static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
static struct k_thread rx_thread_data;
static K_KERNEL_STACK_DEFINE(tx_thread_stack, CONFIG_USBD_BT_HCI_TX_STACK_SIZE);
static struct k_thread tx_thread_data;
/*
* We do not support voice channels and we do not implement
* isochronous endpoints handling, these are only available to match
* the recomendet configuration in the Bluetooth specification and
* avoid issues with Linux kernel btusb driver.
*/
struct usbd_bt_hci_desc {
struct usb_association_descriptor iad;
struct usb_if_descriptor if0;
struct usb_ep_descriptor if0_int_ep;
struct usb_ep_descriptor if0_in_ep;
struct usb_ep_descriptor if0_out_ep;
struct usb_ep_descriptor if0_hs_in_ep;
struct usb_ep_descriptor if0_hs_out_ep;
struct usb_if_descriptor if1_0;
struct usb_ep_descriptor if1_0_iso_in_ep;
struct usb_ep_descriptor if1_0_iso_out_ep;
struct usb_if_descriptor if1_1;
struct usb_ep_descriptor if1_1_iso_in_ep;
struct usb_ep_descriptor if1_1_iso_out_ep;
struct usb_desc_header nil_desc;
};
struct bt_hci_data {
struct net_buf *acl_buf;
struct usbd_bt_hci_desc *const desc;
const struct usb_desc_header **const fs_desc;
const struct usb_desc_header **const hs_desc;
uint16_t acl_len;
struct k_sem sync_sem;
atomic_t state;
};
/*
* Make supported device request visible for the stack
* bRequest 0x00 and bRequest 0xE0.
*/
static const struct usbd_cctx_vendor_req bt_hci_vregs =
USBD_VENDOR_REQ(0x00, 0xe0);
static uint8_t bt_hci_get_int_in(struct usbd_class_data *const c_data)
{
struct bt_hci_data *data = usbd_class_get_private(c_data);
struct usbd_bt_hci_desc *desc = data->desc;
return desc->if0_int_ep.bEndpointAddress;
}
static uint8_t bt_hci_get_bulk_in(struct usbd_class_data *const c_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
struct bt_hci_data *data = usbd_class_get_private(c_data);
struct usbd_bt_hci_desc *desc = data->desc;
if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return desc->if0_hs_in_ep.bEndpointAddress;
}
return desc->if0_in_ep.bEndpointAddress;
}
static uint8_t bt_hci_get_bulk_out(struct usbd_class_data *const c_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
struct bt_hci_data *data = usbd_class_get_private(c_data);
struct usbd_bt_hci_desc *desc = data->desc;
if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return desc->if0_hs_out_ep.bEndpointAddress;
}
return desc->if0_out_ep.bEndpointAddress;
}
struct net_buf *bt_hci_buf_alloc(const uint8_t ep)
{
struct net_buf *buf = NULL;
struct udc_buf_info *bi;
buf = net_buf_alloc(&bt_hci_ep_pool, K_NO_WAIT);
if (!buf) {
return NULL;
}
bi = udc_get_buf_info(buf);
memset(bi, 0, sizeof(struct udc_buf_info));
bi->ep = ep;
return buf;
}
static void bt_hci_tx_sync_in(struct usbd_class_data *const c_data,
struct net_buf *const bt_buf, const uint8_t ep)
{
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
struct net_buf *buf;
buf = bt_hci_buf_alloc(ep);
if (buf == NULL) {
LOG_ERR("Failed to allocate buffer");
return;
}
net_buf_add_mem(buf, bt_buf->data, bt_buf->len);
usbd_ep_enqueue(c_data, buf);
k_sem_take(&hci_data->sync_sem, K_FOREVER);
net_buf_unref(buf);
}
static void bt_hci_tx_thread(void *p1, void *p2, void *p3)
{
struct usbd_class_data *const c_data = p1;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
while (true) {
struct net_buf *bt_buf;
uint8_t ep;
bt_buf = net_buf_get(&bt_hci_tx_queue, K_FOREVER);
switch (bt_buf_get_type(bt_buf)) {
case BT_BUF_EVT:
ep = bt_hci_get_int_in(c_data);
break;
case BT_BUF_ACL_IN:
ep = bt_hci_get_bulk_in(c_data);
break;
default:
LOG_ERR("Unknown type %u", bt_buf_get_type(bt_buf));
continue;
}
bt_hci_tx_sync_in(c_data, bt_buf, ep);
net_buf_unref(bt_buf);
}
}
static void bt_hci_rx_thread(void *a, void *b, void *c)
{
while (true) {
struct net_buf *buf;
int err;
/* FIXME: Do we need a separate thread for bt_send()? */
buf = net_buf_get(&bt_hci_rx_queue, K_FOREVER);
err = bt_send(buf);
if (err) {
LOG_ERR("Error sending to driver");
net_buf_unref(buf);
}
}
}
static int bt_hci_acl_out_start(struct usbd_class_data *const c_data)
{
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
struct net_buf *buf;
uint8_t ep;
int ret;
if (!atomic_test_bit(&hci_data->state, BT_HCI_CLASS_ENABLED)) {
return -EPERM;
}
if (atomic_test_and_set_bit(&hci_data->state, BT_HCI_ACL_RX_ENGAGED)) {
return -EBUSY;
}
ep = bt_hci_get_bulk_out(c_data);
buf = bt_hci_buf_alloc(ep);
if (buf == NULL) {
return -ENOMEM;
}
ret = usbd_ep_enqueue(c_data, buf);
if (ret) {
LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep);
net_buf_unref(buf);
}
return ret;
}
static uint16_t hci_pkt_get_len(struct net_buf *const buf,
const uint8_t *data, const size_t size)
{
size_t hdr_len = 0;
uint16_t len = 0;
switch (bt_buf_get_type(buf)) {
case BT_BUF_CMD: {
struct bt_hci_cmd_hdr *cmd_hdr;
hdr_len = sizeof(*cmd_hdr);
cmd_hdr = (struct bt_hci_cmd_hdr *)data;
len = cmd_hdr->param_len + hdr_len;
break;
}
case BT_BUF_ACL_OUT: {
struct bt_hci_acl_hdr *acl_hdr;
hdr_len = sizeof(*acl_hdr);
acl_hdr = (struct bt_hci_acl_hdr *)data;
len = sys_le16_to_cpu(acl_hdr->len) + hdr_len;
break;
}
case BT_BUF_ISO_OUT: {
struct bt_hci_iso_hdr *iso_hdr;
hdr_len = sizeof(*iso_hdr);
iso_hdr = (struct bt_hci_iso_hdr *)data;
len = bt_iso_hdr_len(sys_le16_to_cpu(iso_hdr->len)) + hdr_len;
break;
}
default:
LOG_ERR("Unknown BT buffer type");
return 0;
}
return (size < hdr_len) ? 0 : len;
}
static int bt_hci_acl_out_cb(struct usbd_class_data *const c_data,
struct net_buf *const buf, const int err)
{
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
if (err) {
goto restart_out_transfer;
}
if (hci_data->acl_buf == NULL) {
hci_data->acl_buf = bt_buf_get_tx(BT_BUF_ACL_OUT, K_FOREVER,
buf->data, buf->len);
if (hci_data->acl_buf == NULL) {
LOG_ERR("Failed to allocate net_buf");
goto restart_out_transfer;
}
hci_data->acl_len = hci_pkt_get_len(hci_data->acl_buf,
buf->data,
buf->len);
LOG_DBG("acl_len %u, chunk %u", hci_data->acl_len, buf->len);
if (hci_data->acl_len == 0) {
LOG_ERR("Failed to get packet length");
net_buf_unref(hci_data->acl_buf);
hci_data->acl_buf = NULL;
}
} else {
if (net_buf_tailroom(hci_data->acl_buf) < buf->len) {
LOG_ERR("Buffer tailroom too small");
net_buf_unref(hci_data->acl_buf);
hci_data->acl_buf = NULL;
goto restart_out_transfer;
}
/*
* Take over the next chunk if HCI packet is
* larger than USB_MAX_FS_BULK_MPS.
*/
net_buf_add_mem(hci_data->acl_buf, buf->data, buf->len);
LOG_INF("len %u, chunk %u", hci_data->acl_buf->len, buf->len);
}
if (hci_data->acl_buf != NULL && hci_data->acl_len == hci_data->acl_buf->len) {
net_buf_put(&bt_hci_rx_queue, hci_data->acl_buf);
hci_data->acl_buf = NULL;
hci_data->acl_len = 0;
}
restart_out_transfer:
/* TODO: add function to reset transfer buffer and allow to reuse it */
net_buf_unref(buf);
atomic_clear_bit(&hci_data->state, BT_HCI_ACL_RX_ENGAGED);
return bt_hci_acl_out_start(c_data);
}
static int bt_hci_request(struct usbd_class_data *const c_data,
struct net_buf *buf, int err)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
struct udc_buf_info *bi;
bi = udc_get_buf_info(buf);
if (bi->ep == bt_hci_get_bulk_out(c_data)) {
return bt_hci_acl_out_cb(c_data, buf, err);
}
if (bi->ep == bt_hci_get_bulk_in(c_data) ||
bi->ep == bt_hci_get_int_in(c_data)) {
k_sem_give(&hci_data->sync_sem);
return 0;
}
return usbd_ep_buf_free(uds_ctx, buf);
}
static void bt_hci_update(struct usbd_class_data *const c_data,
uint8_t iface, uint8_t alternate)
{
LOG_DBG("New configuration, interface %u alternate %u",
iface, alternate);
}
static void bt_hci_enable(struct usbd_class_data *const c_data)
{
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
atomic_set_bit(&hci_data->state, BT_HCI_CLASS_ENABLED);
LOG_INF("Configuration enabled");
if (bt_hci_acl_out_start(c_data)) {
LOG_ERR("Failed to start ACL OUT transfer");
}
}
static void bt_hci_disable(struct usbd_class_data *const c_data)
{
struct bt_hci_data *hci_data = usbd_class_get_private(c_data);
atomic_clear_bit(&hci_data->state, BT_HCI_CLASS_ENABLED);
LOG_INF("Configuration disabled");
}
static int bt_hci_ctd(struct usbd_class_data *const c_data,
const struct usb_setup_packet *const setup,
const struct net_buf *const buf)
{
struct net_buf *cmd_buf;
/* We expect host-to-device class request */
if (setup->RequestType.type != USB_REQTYPE_TYPE_CLASS) {
errno = -ENOTSUP;
return 0;
}
LOG_DBG("bmRequestType 0x%02x bRequest 0x%02x",
setup->bmRequestType, setup->bRequest);
cmd_buf = bt_buf_get_tx(BT_BUF_CMD, K_NO_WAIT, buf->data, buf->len);
if (!cmd_buf) {
LOG_ERR("Cannot get free buffer");
return -ENOMEM;
}
net_buf_put(&bt_hci_rx_queue, cmd_buf);
return 0;
}
static void *bt_hci_get_desc(struct usbd_class_data *const c_data,
const enum usbd_speed speed)
{
struct bt_hci_data *data = usbd_class_get_private(c_data);
if (speed == USBD_SPEED_HS) {
return data->hs_desc;
}
return data->fs_desc;
}
static int bt_hci_init(struct usbd_class_data *const c_data)
{
struct bt_hci_data *data = usbd_class_get_private(c_data);
struct usbd_bt_hci_desc *desc = data->desc;
desc->iad.bFirstInterface = desc->if0.bInterfaceNumber;
return 0;
}
static struct usbd_class_api bt_hci_api = {
.request = bt_hci_request,
.update = bt_hci_update,
.enable = bt_hci_enable,
.disable = bt_hci_disable,
.control_to_dev = bt_hci_ctd,
.get_desc = bt_hci_get_desc,
.init = bt_hci_init,
};
#define BT_HCI_DESCRIPTOR_DEFINE(n) \
static struct usbd_bt_hci_desc bt_hci_desc_##n = { \
.iad = { \
.bLength = sizeof(struct usb_association_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
.bFirstInterface = 0, \
.bInterfaceCount = 0x02, \
.bFunctionClass = USB_BCC_WIRELESS_CONTROLLER, \
.bFunctionSubClass = BT_HCI_SUBCLASS, \
.bFunctionProtocol = BT_HCI_PROTOCOL, \
.iFunction = 0, \
}, \
\
.if0 = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 0, \
.bAlternateSetting = 0, \
.bNumEndpoints = 3, \
.bInterfaceClass = USB_BCC_WIRELESS_CONTROLLER, \
.bInterfaceSubClass = BT_HCI_SUBCLASS, \
.bInterfaceProtocol = BT_HCI_PROTOCOL, \
.iInterface = 0, \
}, \
\
.if0_int_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_EVENTS, \
.bmAttributes = USB_EP_TYPE_INTERRUPT, \
.wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_MPS_EVENTS), \
.bInterval = BT_HCI_EP_INTERVAL_EVENTS, \
}, \
\
.if0_in_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_ACL_DATA_IN, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_FS_MPS_ACL_DATA), \
.bInterval = 0, \
}, \
\
.if0_out_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_ACL_DATA_OUT, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_FS_MPS_ACL_DATA), \
.bInterval = 0, \
}, \
\
.if0_hs_in_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_ACL_DATA_IN, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_HS_MPS_ACL_DATA), \
.bInterval = 0, \
}, \
\
.if0_hs_out_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_ACL_DATA_OUT, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_HS_MPS_ACL_DATA), \
.bInterval = 0, \
}, \
.if1_0 = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 1, \
.bAlternateSetting = 0, \
.bNumEndpoints = 2, \
.bInterfaceClass = USB_BCC_WIRELESS_CONTROLLER, \
.bInterfaceSubClass = BT_HCI_SUBCLASS, \
.bInterfaceProtocol = BT_HCI_PROTOCOL, \
.iInterface = 0, \
}, \
\
.if1_0_iso_in_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_VOICE_IN, \
.bmAttributes = USB_EP_TYPE_ISO, \
.wMaxPacketSize = 0, \
.bInterval = BT_HCI_EP_INTERVAL_VOICE, \
}, \
\
.if1_0_iso_out_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_VOICE_OUT, \
.bmAttributes = USB_EP_TYPE_ISO, \
.wMaxPacketSize = 0, \
.bInterval = BT_HCI_EP_INTERVAL_VOICE, \
}, \
\
.if1_1 = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 1, \
.bAlternateSetting = 1, \
.bNumEndpoints = 2, \
.bInterfaceClass = USB_BCC_WIRELESS_CONTROLLER, \
.bInterfaceSubClass = BT_HCI_SUBCLASS, \
.bInterfaceProtocol = BT_HCI_PROTOCOL, \
.iInterface = 0, \
}, \
\
.if1_1_iso_in_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_VOICE_IN, \
.bmAttributes = USB_EP_TYPE_ISO, \
.wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \
.bInterval = BT_HCI_EP_INTERVAL_VOICE, \
}, \
\
.if1_1_iso_out_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = BT_HCI_EP_VOICE_OUT, \
.bmAttributes = USB_EP_TYPE_ISO, \
.wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \
.bInterval = BT_HCI_EP_INTERVAL_VOICE, \
}, \
\
.nil_desc = { \
.bLength = 0, \
.bDescriptorType = 0, \
}, \
}; \
\
const static struct usb_desc_header *bt_hci_fs_desc_##n[] = { \
(struct usb_desc_header *) &bt_hci_desc_##n.iad, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_int_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0_iso_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0_iso_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1_iso_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1_iso_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.nil_desc, \
}; \
\
const static struct usb_desc_header *bt_hci_hs_desc_##n[] = { \
(struct usb_desc_header *) &bt_hci_desc_##n.iad, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_int_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_hs_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if0_hs_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0_iso_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_0_iso_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1_iso_in_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.if1_1_iso_out_ep, \
(struct usb_desc_header *) &bt_hci_desc_##n.nil_desc, \
};
#define BT_HCI_CLASS_DATA_DEFINE(n) \
static struct bt_hci_data bt_hci_data_##n = { \
.sync_sem = Z_SEM_INITIALIZER(bt_hci_data_##n.sync_sem, 0, 1), \
.desc = &bt_hci_desc_##n, \
.fs_desc = bt_hci_fs_desc_##n, \
.hs_desc = bt_hci_hs_desc_##n, \
}; \
\
USBD_DEFINE_CLASS(bt_hci_##n, &bt_hci_api, \
&bt_hci_data_##n, &bt_hci_vregs);
/*
* Bluetooth subsystem does not support multiple HCI instances,
* but we are almost ready for it.
*/
BT_HCI_DESCRIPTOR_DEFINE(0)
BT_HCI_CLASS_DATA_DEFINE(0)
static int bt_hci_preinit(void)
{
int ret;
ret = bt_enable_raw(&bt_hci_tx_queue);
if (ret) {
LOG_ERR("Failed to open Bluetooth raw channel: %d", ret);
return ret;
}
k_thread_create(&rx_thread_data, rx_thread_stack,
K_KERNEL_STACK_SIZEOF(rx_thread_stack),
bt_hci_rx_thread, NULL, NULL, NULL,
K_PRIO_COOP(CONFIG_USBD_BT_HCI_RX_THREAD_PRIORITY),
0, K_NO_WAIT);
k_thread_name_set(&rx_thread_data, "bt_hci_rx");
k_thread_create(&tx_thread_data, tx_thread_stack,
K_KERNEL_STACK_SIZEOF(tx_thread_stack),
bt_hci_tx_thread, (void *)&bt_hci_0, NULL, NULL,
K_PRIO_COOP(CONFIG_USBD_BT_HCI_TX_THREAD_PRIORITY),
0, K_NO_WAIT);
k_thread_name_set(&tx_thread_data, "bt_hci_tx");
return 0;
}
SYS_INIT(bt_hci_preinit, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);