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"
select UART_INTERRUPT_DRIVEN
select BLUETOOTH_UART
select BLUETOOTH_RECV_IS_RX_THREAD
depends on SERIAL
help
Bluetooth H:4 UART driver. Requires hardware flow control

View file

@ -44,37 +44,143 @@
#include "../nrf51_pm.h"
#endif
#define H4_CMD 0x01
#define H4_ACL 0x02
#define H4_SCO 0x03
#define H4_EVT 0x04
#define H4_NONE 0x00
#define H4_CMD 0x01
#define H4_ACL 0x02
#define H4_SCO 0x03
#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 int h4_read(struct device *uart, uint8_t *buf,
size_t len, size_t min)
static inline void h4_get_type(void)
{
int total = 0;
while (len) {
int rx;
rx = uart_fifo_read(uart, buf, len);
if (rx == 0) {
BT_DBG("Got zero bytes from UART");
if (total < min) {
continue;
}
break;
}
BT_DBG("read %d remaining %zu", rx, len - rx);
len -= rx;
total += rx;
buf += rx;
/* Get packet type */
if (uart_fifo_read(h4_dev, &rx.type, 1) != 1) {
BT_WARN("Unable to read H:4 packet type");
rx.type = H4_NONE;
return;
}
return total;
switch (rx.type) {
case H4_EVT:
rx.remaining = sizeof(rx.evt);
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;
}
}
static inline void get_acl_hdr(void)
{
struct bt_hci_acl_hdr *hdr = &rx.acl;
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;
}
}
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)
@ -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)));
}
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;
struct net_buf *buf;
int read;
/* We can ignore the return value since we pass len == min */
h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr));
if (!rx.buf) {
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;
buf = bt_buf_get_evt(hdr.evt, K_NO_WAIT);
if (buf) {
net_buf_add_mem(buf, &hdr, sizeof(hdr));
} else {
BT_ERR("No available event buffers!");
copy_hdr(rx.buf);
}
BT_DBG("len %u", hdr.len);
read = uart_fifo_read(h4_dev, net_buf_tail(rx.buf), rx.remaining);
net_buf_add(rx.buf, read);
rx.remaining -= read;
return buf;
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 {
uart_irq_rx_disable(h4_dev);
k_sem_give(&rx.sem);
return false;
}
}
static struct net_buf *h4_acl_recv(int *remaining)
static inline void read_header(void)
{
struct bt_hci_acl_hdr hdr;
struct net_buf *buf;
/* We can ignore the return value since we pass len == min */
h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr));
buf = bt_buf_get_acl(K_NO_WAIT);
if (buf) {
net_buf_add_mem(buf, &hdr, sizeof(hdr));
} else {
BT_ERR("No available ACL buffers!");
switch (rx.type) {
case H4_NONE:
h4_get_type();
return;
case H4_EVT:
get_evt_hdr();
break;
case H4_ACL:
get_acl_hdr();
break;
default:
CODE_UNREACHABLE;
return;
}
*remaining = sys_le16_to_cpu(hdr.len);
BT_DBG("len %u", *remaining);
return buf;
if (rx.have_hdr && rx.buf) {
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);
}
}
}
static void bt_uart_isr(struct device *unused)
{
static struct net_buf *buf;
static int remaining;
ARG_UNUSED(unused);
while (uart_irq_update(h4_dev) && uart_irq_is_pending(h4_dev)) {
int read;
if (!uart_irq_rx_ready(h4_dev)) {
if (uart_irq_tx_ready(h4_dev)) {
BT_DBG("transmit ready");
@ -148,58 +278,22 @@ static void bt_uart_isr(struct device *unused)
break;
}
/* Beginning of a new packet */
if (!remaining) {
uint8_t type;
BT_DBG("remaining %u discard %u have_hdr %u buf %p len %u",
rx.remaining, rx.discard, rx.have_hdr, rx.buf,
rx.buf->len);
/* Get packet type */
read = h4_read(h4_dev, &type, sizeof(type), 0);
if (read != sizeof(type)) {
BT_WARN("Unable to read H4 packet type");
continue;
}
switch (type) {
case H4_EVT:
buf = h4_evt_recv(&remaining);
break;
case H4_ACL:
buf = h4_acl_recv(&remaining);
break;
default:
BT_ERR("Unknown H4 type %u", type);
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;
if (rx.discard) {
rx.discard -= h4_discard(h4_dev, rx.discard);
continue;
}
read = h4_read(h4_dev, net_buf_tail(buf), remaining, 0);
if (!rx.have_hdr) {
read_header();
continue;
}
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;
if (!read_payload()) {
return;
}
}
}
@ -240,13 +334,16 @@ static int h4_open(void)
return -EIO;
}
#else
bt_uart_drain(h4_dev);
h4_discard(h4_dev, 32);
#endif
uart_irq_callback_set(h4_dev, bt_uart_isr);
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;
}