blob: cf9cc600d5cf5fb132bf44695ee1d2125611e574 [file] [log] [blame]
/* Copyright (c) 2023 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/time_units.h>
#include <zephyr/toolchain/common.h>
#include <zephyr/drivers/bluetooth/hci_driver.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/hci_raw.h>
LOG_MODULE_REGISTER(hci_uart_async, LOG_LEVEL_DBG);
static const struct device *const hci_uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_bt_c2h_uart));
static K_THREAD_STACK_DEFINE(h2c_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
static struct k_thread h2c_thread;
enum h4_type {
H4_CMD = 0x01,
H4_ACL = 0x02,
H4_SCO = 0x03,
H4_EVT = 0x04,
H4_ISO = 0x05,
};
struct k_poll_signal uart_h2c_rx_sig;
struct k_poll_signal uart_c2h_tx_sig;
static K_FIFO_DEFINE(c2h_queue);
/** Send raw data on c2h UART.
*
* Blocks until completion. Not thread-safe.
*
* @retval 0 on success
* @retval -EBUSY Another transmission is in progress. This a
* thread-safety violation.
* @retval -errno @ref uart_tx error.
*/
static int uart_c2h_tx(const uint8_t *data, size_t size)
{
int err;
struct k_poll_signal *sig = &uart_c2h_tx_sig;
struct k_poll_event done[] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig),
};
k_poll_signal_reset(sig);
err = uart_tx(hci_uart_dev, data, size, SYS_FOREVER_US);
if (err) {
LOG_ERR("uart c2h tx: err %d", err);
return err;
}
err = k_poll(done, ARRAY_SIZE(done), K_FOREVER);
__ASSERT_NO_MSG(err == 0);
return 0;
}
/* Function expects that type is validated and only CMD, ISO or ACL will be used. */
static uint32_t hci_payload_size(const uint8_t *hdr_buf, enum h4_type type)
{
switch (type) {
case H4_CMD:
return ((const struct bt_hci_cmd_hdr *)hdr_buf)->param_len;
case H4_ACL:
return sys_le16_to_cpu(((const struct bt_hci_acl_hdr *)hdr_buf)->len);
case H4_ISO:
return bt_iso_hdr_len(
sys_le16_to_cpu(((const struct bt_hci_iso_hdr *)hdr_buf)->len));
default:
LOG_ERR("Invalid type: %u", type);
return 0;
}
}
static uint8_t hci_hdr_size(enum h4_type type)
{
switch (type) {
case H4_CMD:
return sizeof(struct bt_hci_cmd_hdr);
case H4_ACL:
return sizeof(struct bt_hci_acl_hdr);
case H4_ISO:
return sizeof(struct bt_hci_iso_hdr);
default:
LOG_ERR("Unexpected h4 type: %u", type);
return 0;
}
}
/** Send raw data on c2h UART.
*
* Blocks until either @p size has been received or special UART
* condition occurs on the UART RX line, like an UART break or parity
* error.
*
* Not thread-safe.
*
* @retval 0 on success
* @retval -EBUSY Another transmission is in progress. This a
* thread-safety violation.
* @retval -errno @ref uart_rx_enable error.
* @retval +stop_reason Special condition @ref uart_rx_stop_reason.
*/
static int uart_h2c_rx(uint8_t *dst, size_t size)
{
int err;
struct k_poll_signal *sig = &uart_h2c_rx_sig;
struct k_poll_event done[] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig),
};
k_poll_signal_reset(sig);
err = uart_rx_enable(hci_uart_dev, dst, size, SYS_FOREVER_US);
if (err) {
LOG_ERR("uart h2c rx: err %d", err);
return err;
}
k_poll(done, ARRAY_SIZE(done), K_FOREVER);
return sig->result;
}
/** Inject a HCI EVT Hardware error into the c2h packet stream.
*
* This uses `bt_recv`, just as if the controller is sending the error.
*/
static void send_hw_error(void)
{
const uint8_t err_code = 0;
const uint8_t hci_evt_hw_err[] = {BT_HCI_EVT_HARDWARE_ERROR,
sizeof(struct bt_hci_evt_hardware_error), err_code};
struct net_buf *buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
net_buf_add_mem(buf, hci_evt_hw_err, sizeof(hci_evt_hw_err));
/* Inject the message into the c2h queue. */
bt_recv(buf);
/* The c2h thread will send the message at some point. The host
* will receive it and reset the controller.
*/
}
static void recover_sync_by_reset_pattern(void)
{
/* { H4_CMD, le_16(HCI_CMD_OP_RESET), len=0 } */
const uint8_t h4_cmd_reset[] = {0x01, 0x03, 0x0C, 0x00};
const uint32_t reset_pattern = sys_get_be32(h4_cmd_reset);
int err;
struct net_buf *h2c_cmd_reset;
uint32_t shift_register = 0;
LOG_DBG("Looking for reset pattern");
while (shift_register != reset_pattern) {
uint8_t read_byte;
uart_h2c_rx(&read_byte, sizeof(uint8_t));
LOG_DBG("h2c: 0x%02x", read_byte);
shift_register = (shift_register * 0x100) + read_byte;
}
LOG_DBG("Pattern found");
h2c_cmd_reset = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, h4_cmd_reset, sizeof(h4_cmd_reset));
LOG_DBG("Fowarding reset");
err = bt_send(h2c_cmd_reset);
__ASSERT(!err, "Failed to send reset: %d", err);
}
static void h2c_h4_transport(void)
{
/* When entering this function, the h2c stream should be
* 'synchronized'. I.e. The stream should be at a H4 packet
* boundary.
*
* This function returns to signal a desynchronization.
* When this happens, the caller should resynchronize before
* entering this function again. It's up to the caller to decide
* how to resynchronize.
*/
for (;;) {
int err;
struct net_buf *buf;
uint8_t h4_type;
uint8_t hdr_size;
uint8_t *hdr_buf;
uint16_t payload_size;
LOG_DBG("h2c: listening");
/* Read H4 type. */
err = uart_h2c_rx(&h4_type, sizeof(uint8_t));
if (err) {
return;
}
LOG_DBG("h2c: h4_type %d", h4_type);
/* Allocate buf. */
buf = bt_buf_get_tx(BT_BUF_H4, K_FOREVER, &h4_type, sizeof(h4_type));
LOG_DBG("h2c: buf %p", buf);
if (!buf) {
/* `h4_type` was invalid. */
__ASSERT_NO_MSG(hci_hdr_size(h4_type) == 0);
LOG_WRN("bt_buf_get_tx failed h4_type %d", h4_type);
return;
}
/* Read HCI header. */
hdr_size = hci_hdr_size(h4_type);
hdr_buf = net_buf_add(buf, hdr_size);
err = uart_h2c_rx(hdr_buf, hdr_size);
if (err) {
net_buf_unref(buf);
return;
}
LOG_HEXDUMP_DBG(hdr_buf, hdr_size, "h2c: hci hdr");
/* Read HCI payload. */
payload_size = hci_payload_size(hdr_buf, h4_type);
LOG_DBG("h2c: payload_size %u", payload_size);
if (payload_size == 0) {
/* Done, dont rx zero bytes */
} else if (payload_size <= net_buf_tailroom(buf)) {
uint8_t *payload_dst = net_buf_add(buf, payload_size);
err = uart_h2c_rx(payload_dst, payload_size);
if (err) {
net_buf_unref(buf);
return;
}
LOG_HEXDUMP_DBG(payload_dst, payload_size, "h2c: hci payload");
} else {
/* Discard oversize packet. */
uint8_t *discard_dst;
uint16_t discard_size;
LOG_WRN("h2c: Discarding oversize h4_type %d payload_size %d.", h4_type,
payload_size);
/* Reset `buf` so all of it is available. */
net_buf_reset(buf);
discard_dst = net_buf_tail(buf);
discard_size = net_buf_max_len(buf);
while (payload_size) {
uint16_t read_size = MIN(payload_size, discard_size);
err = uart_h2c_rx(discard_dst, read_size);
if (err) {
net_buf_unref(buf);
return;
}
payload_size -= read_size;
}
net_buf_unref(buf);
buf = NULL;
}
LOG_DBG("h2c: packet done");
/* Route buf to Controller. */
if (buf) {
err = bt_send(buf);
if (err) {
/* This is not a transport error. */
LOG_ERR("bt_send err %d", err);
net_buf_unref(buf);
buf = NULL;
}
}
k_yield();
}
}
static void h2c_thread_entry(void *p1, void *p2, void *p3)
{
k_thread_name_set(k_current_get(), "HCI TX (h2c)");
for (;;) {
LOG_DBG("Synchronized");
h2c_h4_transport();
LOG_WRN("Desynchronized");
send_hw_error();
recover_sync_by_reset_pattern();
}
}
void callback(const struct device *dev, struct uart_event *evt, void *user_data)
{
ARG_UNUSED(user_data);
if (evt->type == UART_RX_DISABLED) {
(void)k_poll_signal_raise(&uart_h2c_rx_sig, 0);
} else if (evt->type == UART_RX_STOPPED) {
(void)k_poll_signal_raise(&uart_h2c_rx_sig, evt->data.rx_stop.reason);
} else if (evt->type == UART_TX_DONE) {
(void)k_poll_signal_raise(&uart_c2h_tx_sig, 0);
}
}
static int hci_uart_init(void)
{
int err;
k_poll_signal_init(&uart_h2c_rx_sig);
k_poll_signal_init(&uart_c2h_tx_sig);
LOG_DBG("");
if (!device_is_ready(hci_uart_dev)) {
LOG_ERR("HCI UART %s is not ready", hci_uart_dev->name);
return -EINVAL;
}
BUILD_ASSERT(IS_ENABLED(CONFIG_UART_ASYNC_API));
err = uart_callback_set(hci_uart_dev, callback, NULL);
/* Note: Asserts if CONFIG_UART_ASYNC_API is not enabled for `hci_uart_dev`. */
__ASSERT(!err, "err %d", err);
return 0;
}
SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
const struct {
uint8_t h4;
struct bt_hci_evt_hdr hdr;
struct bt_hci_evt_cmd_complete cc;
} __packed cc_evt = {
.h4 = H4_EVT,
.hdr = {.evt = BT_HCI_EVT_CMD_COMPLETE, .len = sizeof(struct bt_hci_evt_cmd_complete)},
.cc = {.ncmd = 1, .opcode = sys_cpu_to_le16(BT_OP_NOP)},
};
static void c2h_thread_entry(void)
{
k_thread_name_set(k_current_get(), "HCI RX (c2h)");
if (IS_ENABLED(CONFIG_BT_WAIT_NOP)) {
uart_c2h_tx((char *)&cc_evt, sizeof(cc_evt));
}
for (;;) {
struct net_buf *buf;
buf = net_buf_get(&c2h_queue, K_FOREVER);
uart_c2h_tx(buf->data, buf->len);
net_buf_unref(buf);
}
}
void hci_uart_main(void)
{
int err;
err = bt_enable_raw(&c2h_queue);
__ASSERT_NO_MSG(!err);
/* TX thread. */
k_thread_create(&h2c_thread, h2c_thread_stack, K_THREAD_STACK_SIZEOF(h2c_thread_stack),
h2c_thread_entry, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
/* Reuse current thread as RX thread. */
c2h_thread_entry();
}