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"
|
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
|
||||||
|
|
|
@ -44,37 +44,143 @@
|
||||||
#include "../nrf51_pm.h"
|
#include "../nrf51_pm.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define H4_CMD 0x01
|
#define H4_NONE 0x00
|
||||||
#define H4_ACL 0x02
|
#define H4_CMD 0x01
|
||||||
#define H4_SCO 0x03
|
#define H4_ACL 0x02
|
||||||
#define H4_EVT 0x04
|
#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 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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_DBG("read %d remaining %zu", rx, len - rx);
|
|
||||||
len -= rx;
|
|
||||||
total += rx;
|
|
||||||
buf += rx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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);
|
|
||||||
if (buf) {
|
|
||||||
net_buf_add_mem(buf, &hdr, sizeof(hdr));
|
|
||||||
} else {
|
|
||||||
BT_ERR("No available event buffers!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
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,58 +278,22 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
read = h4_read(h4_dev, net_buf_tail(buf), remaining, 0);
|
if (!rx.have_hdr) {
|
||||||
|
read_header();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
buf->len += read;
|
if (!read_payload()) {
|
||||||
remaining -= read;
|
return;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue