zephyr/subsys/usb/class/bluetooth.c
Andrei Emeltchenko 53410af994 usb: Add Bluetooth device class core functionality
Implement Bluetooth over USB functionality through Bluetooth raw
access to the Bluetooth controller. Most devices with Bluetooth and
USB controllers supported by Zephyr can export themselves as USB
Bluetooth dongles.

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
2018-04-20 21:04:42 -07:00

237 lines
5.1 KiB
C

/*
* Wireless / Bluetooth USB class
*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <init.h>
#include <usb/usb_device.h>
#include <usb/usb_common.h>
#include <usb_descriptor.h>
#include <net/buf.h>
#include <bluetooth/buf.h>
#include <bluetooth/hci_raw.h>
#include <bluetooth/l2cap.h>
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_WARNING
#define SYS_LOG_DOMAIN "usb/bluetooth"
#include <logging/sys_log.h>
#if !defined(CONFIG_USB_COMPOSITE_DEVICE)
static u8_t interface_data[64];
#endif
static K_FIFO_DEFINE(rx_queue);
/* HCI command buffers */
#define CMD_BUF_SIZE BT_BUF_RX_SIZE
NET_BUF_POOL_DEFINE(tx_pool, CONFIG_BT_HCI_CMD_COUNT, CMD_BUF_SIZE,
sizeof(u8_t), NULL);
#define BT_L2CAP_MTU 64
/* Data size needed for ACL buffers */
#define BT_BUF_ACL_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_MTU)
NET_BUF_POOL_DEFINE(acl_tx_pool, 2, BT_BUF_ACL_SIZE, sizeof(u8_t), NULL);
/* Stack for thread */
static K_THREAD_STACK_DEFINE(bluetooth_thread_stack, 512);
static struct k_thread bluetooth_thread_data;
static void bluetooth_status_cb(enum usb_dc_status_code status, u8_t *param)
{
/* Check the USB status and do needed action if required */
switch (status) {
case USB_DC_ERROR:
SYS_LOG_DBG("USB device error");
break;
case USB_DC_RESET:
SYS_LOG_DBG("USB device reset detected");
break;
case USB_DC_CONNECTED:
SYS_LOG_DBG("USB device connected");
break;
case USB_DC_CONFIGURED:
SYS_LOG_DBG("USB device configured");
break;
case USB_DC_DISCONNECTED:
SYS_LOG_DBG("USB device disconnected");
break;
case USB_DC_SUSPEND:
SYS_LOG_DBG("USB device suspended");
break;
case USB_DC_RESUME:
SYS_LOG_DBG("USB device resumed");
break;
case USB_DC_UNKNOWN:
default:
SYS_LOG_DBG("USB unknown state");
break;
}
}
static void bluetooth_thread(void)
{
SYS_LOG_DBG("Start USB Bluetooth thread");
while (true) {
struct net_buf *buf;
buf = net_buf_get(&rx_queue, K_FOREVER);
switch (bt_buf_get_type(buf)) {
case BT_BUF_EVT:
usb_transfer_sync(CONFIG_BLUETOOTH_INT_EP_ADDR,
buf->data, buf->len,
USB_TRANS_WRITE);
break;
case BT_BUF_ACL_IN:
usb_transfer_sync(CONFIG_BLUETOOTH_IN_EP_ADDR,
buf->data, buf->len,
USB_TRANS_WRITE);
break;
default:
SYS_LOG_ERR("Unknown type %u", bt_buf_get_type(buf));
break;
}
net_buf_unref(buf);
}
}
static void bluetooth_bulk_out_callback(u8_t ep,
enum usb_dc_ep_cb_status_code ep_status)
{
struct net_buf *buf;
u32_t len;
SYS_LOG_DBG("ep %x status %d", ep, ep_status);
/* Read number of bytes to read */
usb_read(ep, NULL, 0, &len);
if (!len || len > BT_BUF_ACL_SIZE) {
SYS_LOG_ERR("Incorrect length: %u\n", len);
return;
}
buf = net_buf_alloc(&acl_tx_pool, K_NO_WAIT);
if (!buf) {
SYS_LOG_ERR("Cannot get free buffer\n");
return;
}
usb_read(ep, net_buf_add(buf, len), len, NULL);
bt_buf_set_type(buf, BT_BUF_ACL_OUT);
bt_send(buf);
}
static int bluetooth_class_handler(struct usb_setup_packet *setup,
s32_t *len, u8_t **data)
{
struct net_buf *buf;
SYS_LOG_DBG("len %u", *len);
if (!*len || *len > CMD_BUF_SIZE) {
SYS_LOG_ERR("Incorrect length: %d\n", *len);
return -EINVAL;
}
buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
if (!buf) {
SYS_LOG_ERR("Cannot get free buffer\n");
return -ENOMEM;
}
bt_buf_set_type(buf, BT_BUF_CMD);
net_buf_add_mem(buf, *data, *len);
bt_send(buf);
return 0;
}
static struct usb_ep_cfg_data bluetooth_ep_data[] = {
{
.ep_cb = usb_transfer_ep_callback,
.ep_addr = CONFIG_BLUETOOTH_INT_EP_ADDR,
},
{
.ep_cb = bluetooth_bulk_out_callback,
.ep_addr = CONFIG_BLUETOOTH_OUT_EP_ADDR,
},
{
.ep_cb = usb_transfer_ep_callback,
.ep_addr = CONFIG_BLUETOOTH_IN_EP_ADDR,
},
};
static struct usb_cfg_data bluetooth_config = {
.usb_device_description = NULL,
.cb_usb_status = bluetooth_status_cb,
.interface = {
.class_handler = bluetooth_class_handler,
.custom_handler = NULL,
.vendor_handler = NULL,
.payload_data = NULL,
},
.num_endpoints = NUMOF_ENDPOINTS_BLUETOOTH,
.endpoint = bluetooth_ep_data,
};
static int bluetooth_init(struct device *dev)
{
int ret;
SYS_LOG_DBG("Initialization");
ret = bt_enable_raw(&rx_queue);
if (ret) {
SYS_LOG_ERR("Failed to open Bluetooth raw channel: %d", ret);
return ret;
}
#ifdef CONFIG_USB_COMPOSITE_DEVICE
ret = composite_add_function(&bluetooth_config,
FIRST_IFACE_BLUETOOTH);
if (ret < 0) {
SYS_LOG_ERR("Failed to add bluetooth function");
return ret;
}
#else
bluetooth_config.interface.payload_data = interface_data;
bluetooth_config.usb_device_description =
usb_get_device_descriptor();
/* Initialize the USB driver with the right configuration */
ret = usb_set_config(&bluetooth_config);
if (ret < 0) {
SYS_LOG_ERR("Failed to config USB");
return ret;
}
/* Enable USB driver */
ret = usb_enable(&bluetooth_config);
if (ret < 0) {
SYS_LOG_ERR("Failed to enable USB");
return ret;
}
#endif
k_thread_create(&bluetooth_thread_data, bluetooth_thread_stack,
K_THREAD_STACK_SIZEOF(bluetooth_thread_stack),
(k_thread_entry_t)bluetooth_thread,
NULL, NULL, NULL, K_PRIO_COOP(8), 0, K_NO_WAIT);
return 0;
}
SYS_INIT(bluetooth_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);