Bluetooth: Convert H:4 HCI driver to RECV_IS_RX_THREAD

Completely redesign the H:4 driver to utilize its own thread and
select the new RECV_IS_RX_THREAD Kconfig option.

Jira: ZEP-1483

Change-Id: I0ca0661b66d564e5edc1b8505706d6bb76632e79
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2016-12-25 15:21:39 +02:00
commit 95049fb99b
2 changed files with 207 additions and 109 deletions

View file

@ -33,6 +33,7 @@ config BLUETOOTH_H4
bool "H:4 UART" bool "H:4 UART"
select UART_INTERRUPT_DRIVEN select UART_INTERRUPT_DRIVEN
select BLUETOOTH_UART select BLUETOOTH_UART
select BLUETOOTH_RECV_IS_RX_THREAD
depends on SERIAL depends on SERIAL
help help
Bluetooth H:4 UART driver. Requires hardware flow control Bluetooth H:4 UART driver. Requires hardware flow control

View file

@ -44,37 +44,143 @@
#include "../nrf51_pm.h" #include "../nrf51_pm.h"
#endif #endif
#define H4_NONE 0x00
#define H4_CMD 0x01 #define H4_CMD 0x01
#define H4_ACL 0x02 #define H4_ACL 0x02
#define H4_SCO 0x03 #define H4_SCO 0x03
#define H4_EVT 0x04 #define H4_EVT 0x04
static BT_STACK_NOINIT(rx_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE);
static struct {
struct net_buf *buf;
struct k_sem sem;
uint16_t remaining;
uint16_t discard;
bool have_hdr;
uint8_t type;
union {
struct bt_hci_evt_hdr evt;
struct bt_hci_acl_hdr acl;
};
} rx = {
.sem = K_SEM_INITIALIZER(rx.sem, 0, 1),
};
static struct device *h4_dev; static struct device *h4_dev;
static int h4_read(struct device *uart, uint8_t *buf, static inline void h4_get_type(void)
size_t len, size_t min)
{ {
int total = 0; /* Get packet type */
if (uart_fifo_read(h4_dev, &rx.type, 1) != 1) {
while (len) { BT_WARN("Unable to read H:4 packet type");
int rx; rx.type = H4_NONE;
return;
rx = uart_fifo_read(uart, buf, len);
if (rx == 0) {
BT_DBG("Got zero bytes from UART");
if (total < min) {
continue;
} }
switch (rx.type) {
case H4_EVT:
rx.remaining = sizeof(rx.evt);
break; break;
case H4_ACL:
rx.remaining = sizeof(rx.acl);
break;
default:
BT_ERR("Unknown H:4 type 0x%02x", rx.type);
rx.type = H4_NONE;
}
} }
BT_DBG("read %d remaining %zu", rx, len - rx); static inline void get_acl_hdr(void)
len -= rx; {
total += rx; struct bt_hci_acl_hdr *hdr = &rx.acl;
buf += rx; int to_read = sizeof(*hdr) - rx.remaining;
rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read,
rx.remaining);
if (!rx.remaining) {
BT_DBG("Got ACL header");
rx.remaining = sys_le16_to_cpu(hdr->len);
rx.have_hdr = true;
}
} }
return total; static inline void get_evt_hdr(void)
{
struct bt_hci_evt_hdr *hdr = &rx.evt;
int to_read = sizeof(*hdr) - rx.remaining;
rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read,
rx.remaining);
if (!rx.remaining) {
BT_DBG("Got event header");
rx.remaining = hdr->len;
rx.have_hdr = true;
}
}
static inline void buf_set_type(struct net_buf *buf)
{
if (rx.type == H4_EVT) {
bt_buf_set_type(buf, BT_BUF_EVT);
} else {
bt_buf_set_type(buf, BT_BUF_ACL_IN);
}
}
static inline void copy_hdr(struct net_buf *buf)
{
if (rx.type == H4_EVT) {
net_buf_add_mem(buf, &rx.evt, sizeof(rx.evt));
} else {
net_buf_add_mem(buf, &rx.acl, sizeof(rx.acl));
}
}
static void rx_thread(void *p1, void *p2, void *p3)
{
struct net_buf *buf;
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
BT_DBG("started");
while (1) {
BT_DBG("waiting for semaphore");
k_sem_take(&rx.sem, K_FOREVER);
BT_DBG("got semaphore, rx.buf %p have_hdr %u",
rx.buf, rx.have_hdr);
if (rx.buf) {
buf = rx.buf;
buf_set_type(buf);
rx.buf = NULL;
rx.have_hdr = false;
rx.type = H4_NONE;
uart_irq_rx_enable(h4_dev);
BT_DBG("Calling bt_recv()");
bt_recv(buf);
} else {
rx.buf = bt_buf_get_rx(K_FOREVER);
if (rx.remaining > net_buf_tailroom(rx.buf)) {
BT_ERR("Not enough space in buffer");
rx.discard = rx.remaining;
rx.remaining = 0;
rx.have_hdr = 0;
} else {
copy_hdr(rx.buf);
}
uart_irq_rx_enable(h4_dev);
}
}
} }
static size_t h4_discard(struct device *uart, size_t len) static size_t h4_discard(struct device *uart, size_t len)
@ -84,60 +190,84 @@ static size_t h4_discard(struct device *uart, size_t len)
return uart_fifo_read(uart, buf, min(len, sizeof(buf))); return uart_fifo_read(uart, buf, min(len, sizeof(buf)));
} }
static struct net_buf *h4_evt_recv(int *remaining) /* Returns false if RX IRQ was disabled and control given to the RX thread */
static inline bool read_payload(void)
{ {
struct bt_hci_evt_hdr hdr; int read;
struct net_buf *buf;
/* We can ignore the return value since we pass len == min */ if (!rx.buf) {
h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr)); rx.buf = bt_buf_get_rx(K_NO_WAIT);
if (!rx.buf) {
uart_irq_rx_disable(h4_dev);
BT_DBG("giving semaphore");
k_sem_give(&rx.sem);
return false;
}
*remaining = hdr.len; copy_hdr(rx.buf);
}
buf = bt_buf_get_evt(hdr.evt, K_NO_WAIT); read = uart_fifo_read(h4_dev, net_buf_tail(rx.buf), rx.remaining);
if (buf) { net_buf_add(rx.buf, read);
net_buf_add_mem(buf, &hdr, sizeof(hdr)); rx.remaining -= read;
BT_DBG("got %d bytes, remaining %u", read, rx.remaining);
BT_DBG("Payload (len %u): %s", rx.buf->len,
bt_hex(rx.buf->data, rx.buf->len));
if (rx.remaining) {
return true;
}
if (rx.type == H4_EVT && bt_hci_evt_is_prio(rx.evt.evt)) {
buf_set_type(rx.buf);
BT_DBG("Calling bt_recv()");
bt_recv(rx.buf);
rx.buf = NULL;
rx.have_hdr = false;
rx.type = H4_NONE;
return true;
} else { } else {
BT_ERR("No available event buffers!"); uart_irq_rx_disable(h4_dev);
k_sem_give(&rx.sem);
return false;
}
} }
BT_DBG("len %u", hdr.len); static inline void read_header(void)
return buf;
}
static struct net_buf *h4_acl_recv(int *remaining)
{ {
struct bt_hci_acl_hdr hdr; switch (rx.type) {
struct net_buf *buf; case H4_NONE:
h4_get_type();
/* We can ignore the return value since we pass len == min */ return;
h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr)); case H4_EVT:
get_evt_hdr();
buf = bt_buf_get_acl(K_NO_WAIT); break;
if (buf) { case H4_ACL:
net_buf_add_mem(buf, &hdr, sizeof(hdr)); get_acl_hdr();
} else { break;
BT_ERR("No available ACL buffers!"); default:
CODE_UNREACHABLE;
return;
} }
*remaining = sys_le16_to_cpu(hdr.len); if (rx.have_hdr && rx.buf) {
if (rx.remaining > net_buf_tailroom(rx.buf)) {
BT_DBG("len %u", *remaining); BT_ERR("Not enough space in buffer");
rx.discard = rx.remaining;
return buf; rx.remaining = 0;
rx.have_hdr = 0;
} else {
copy_hdr(rx.buf);
}
}
} }
static void bt_uart_isr(struct device *unused) static void bt_uart_isr(struct device *unused)
{ {
static struct net_buf *buf;
static int remaining;
ARG_UNUSED(unused); ARG_UNUSED(unused);
while (uart_irq_update(h4_dev) && uart_irq_is_pending(h4_dev)) { while (uart_irq_update(h4_dev) && uart_irq_is_pending(h4_dev)) {
int read;
if (!uart_irq_rx_ready(h4_dev)) { if (!uart_irq_rx_ready(h4_dev)) {
if (uart_irq_tx_ready(h4_dev)) { if (uart_irq_tx_ready(h4_dev)) {
BT_DBG("transmit ready"); BT_DBG("transmit ready");
@ -148,59 +278,23 @@ static void bt_uart_isr(struct device *unused)
break; break;
} }
/* Beginning of a new packet */ BT_DBG("remaining %u discard %u have_hdr %u buf %p len %u",
if (!remaining) { rx.remaining, rx.discard, rx.have_hdr, rx.buf,
uint8_t type; rx.buf->len);
/* Get packet type */ if (rx.discard) {
read = h4_read(h4_dev, &type, sizeof(type), 0); rx.discard -= h4_discard(h4_dev, rx.discard);
if (read != sizeof(type)) {
BT_WARN("Unable to read H4 packet type");
continue; continue;
} }
switch (type) { if (!rx.have_hdr) {
case H4_EVT: read_header();
buf = h4_evt_recv(&remaining); continue;
break; }
case H4_ACL:
buf = h4_acl_recv(&remaining); if (!read_payload()) {
break;
default:
BT_ERR("Unknown H4 type %u", type);
return; return;
} }
BT_DBG("need to get %u bytes", remaining);
if (buf && remaining > net_buf_tailroom(buf)) {
BT_ERR("Not enough space in buffer");
net_buf_unref(buf);
buf = NULL;
}
}
if (!buf) {
read = h4_discard(h4_dev, remaining);
BT_WARN("Discarded %d bytes", read);
remaining -= read;
continue;
}
read = h4_read(h4_dev, net_buf_tail(buf), remaining, 0);
buf->len += read;
remaining -= read;
BT_DBG("received %d bytes", read);
if (!remaining) {
BT_DBG("full packet received");
/* Pass buffer to the stack */
bt_recv(buf);
buf = NULL;
}
} }
} }
@ -240,13 +334,16 @@ static int h4_open(void)
return -EIO; return -EIO;
} }
#else #else
bt_uart_drain(h4_dev); h4_discard(h4_dev, 32);
#endif #endif
uart_irq_callback_set(h4_dev, bt_uart_isr); uart_irq_callback_set(h4_dev, bt_uart_isr);
uart_irq_rx_enable(h4_dev); uart_irq_rx_enable(h4_dev);
k_thread_spawn(rx_thread_stack, sizeof(rx_thread_stack), rx_thread,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
return 0; return 0;
} }