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:
parent
53b1503ac8
commit
95049fb99b
2 changed files with 207 additions and 109 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue