Bluetooth: h4: Convert TX path to be interrupt based
Use interrupt based transmission instead of polling. Change-Id: Iebfd67372044d3e6de9bdbdd6f0c9c6d01d46cb9 Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
1094f00253
commit
3341d7d2b5
1 changed files with 86 additions and 48 deletions
|
@ -70,6 +70,14 @@ static struct {
|
||||||
.fifo = K_FIFO_INITIALIZER(rx.fifo),
|
.fifo = K_FIFO_INITIALIZER(rx.fifo),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
uint8_t type;
|
||||||
|
struct net_buf *buf;
|
||||||
|
struct k_fifo fifo;
|
||||||
|
} tx = {
|
||||||
|
.fifo = K_FIFO_INITIALIZER(tx.fifo),
|
||||||
|
};
|
||||||
|
|
||||||
static struct device *h4_dev;
|
static struct device *h4_dev;
|
||||||
|
|
||||||
static inline void h4_get_type(void)
|
static inline void h4_get_type(void)
|
||||||
|
@ -189,8 +197,7 @@ 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns false if RX IRQ was disabled and control given to the RX thread */
|
static inline void read_payload(void)
|
||||||
static inline bool read_payload(void)
|
|
||||||
{
|
{
|
||||||
struct net_buf *buf;
|
struct net_buf *buf;
|
||||||
bool prio;
|
bool prio;
|
||||||
|
@ -201,7 +208,7 @@ static inline bool read_payload(void)
|
||||||
if (!rx.buf) {
|
if (!rx.buf) {
|
||||||
BT_DBG("Failed to allocate, deferring to rx_thread");
|
BT_DBG("Failed to allocate, deferring to rx_thread");
|
||||||
uart_irq_rx_disable(h4_dev);
|
uart_irq_rx_disable(h4_dev);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("Allocated rx.buf %p", rx.buf);
|
BT_DBG("Allocated rx.buf %p", rx.buf);
|
||||||
|
@ -211,7 +218,7 @@ static inline bool read_payload(void)
|
||||||
rx.discard = rx.remaining;
|
rx.discard = rx.remaining;
|
||||||
rx.remaining = 0;
|
rx.remaining = 0;
|
||||||
rx.have_hdr = false;
|
rx.have_hdr = false;
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_hdr(rx.buf);
|
copy_hdr(rx.buf);
|
||||||
|
@ -226,7 +233,7 @@ static inline bool read_payload(void)
|
||||||
bt_hex(rx.buf->data, rx.buf->len));
|
bt_hex(rx.buf->data, rx.buf->len));
|
||||||
|
|
||||||
if (rx.remaining) {
|
if (rx.remaining) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prio = (rx.type == H4_EVT && bt_hci_evt_is_prio(rx.evt.evt));
|
prio = (rx.type == H4_EVT && bt_hci_evt_is_prio(rx.evt.evt));
|
||||||
|
@ -250,8 +257,6 @@ static inline bool read_payload(void)
|
||||||
BT_DBG("Putting buf %p to rx fifo", buf);
|
BT_DBG("Putting buf %p to rx fifo", buf);
|
||||||
net_buf_put(&rx.fifo, buf);
|
net_buf_put(&rx.fifo, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void read_header(void)
|
static inline void read_header(void)
|
||||||
|
@ -283,38 +288,85 @@ static inline void read_header(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void process_tx(void)
|
||||||
|
{
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
if (!tx.buf) {
|
||||||
|
tx.buf = net_buf_get(&tx.fifo, K_NO_WAIT);
|
||||||
|
if (!tx.buf) {
|
||||||
|
BT_ERR("TX interrupt but no pending buffer!");
|
||||||
|
uart_irq_tx_disable(h4_dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tx.type) {
|
||||||
|
switch (bt_buf_get_type(tx.buf)) {
|
||||||
|
case BT_BUF_ACL_OUT:
|
||||||
|
tx.type = H4_ACL;
|
||||||
|
break;
|
||||||
|
case BT_BUF_CMD:
|
||||||
|
tx.type = H4_CMD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BT_ERR("Unknown buffer type");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = uart_fifo_fill(h4_dev, &tx.type, 1);
|
||||||
|
if (bytes != 1) {
|
||||||
|
BT_WARN("Unable to send H:4 type");
|
||||||
|
tx.type = H4_NONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = uart_fifo_fill(h4_dev, tx.buf->data, tx.buf->len);
|
||||||
|
net_buf_pull(tx.buf, bytes);
|
||||||
|
|
||||||
|
if (tx.buf->len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
tx.type = H4_NONE;
|
||||||
|
net_buf_unref(tx.buf);
|
||||||
|
tx.buf = net_buf_get(&tx.fifo, K_NO_WAIT);
|
||||||
|
if (!tx.buf) {
|
||||||
|
uart_irq_tx_disable(h4_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void process_rx(void)
|
||||||
|
{
|
||||||
|
BT_DBG("remaining %u discard %u have_hdr %u rx.buf %p len %u",
|
||||||
|
rx.remaining, rx.discard, rx.have_hdr, rx.buf,
|
||||||
|
rx.buf ? rx.buf->len : 0);
|
||||||
|
|
||||||
|
if (rx.discard) {
|
||||||
|
rx.discard -= h4_discard(h4_dev, rx.discard);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx.have_hdr) {
|
||||||
|
read_payload();
|
||||||
|
} else {
|
||||||
|
read_header();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void bt_uart_isr(struct device *unused)
|
static void bt_uart_isr(struct device *unused)
|
||||||
{
|
{
|
||||||
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)) {
|
||||||
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");
|
process_tx();
|
||||||
} else {
|
|
||||||
BT_DBG("spurious interrupt");
|
|
||||||
}
|
|
||||||
/* Only the UART RX path is interrupt-enabled */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("remaining %u discard %u have_hdr %u buf %p len %u",
|
if (uart_irq_rx_ready(h4_dev)) {
|
||||||
rx.remaining, rx.discard, rx.have_hdr, rx.buf,
|
process_rx();
|
||||||
rx.buf->len);
|
|
||||||
|
|
||||||
if (rx.discard) {
|
|
||||||
rx.discard -= h4_discard(h4_dev, rx.discard);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rx.have_hdr) {
|
|
||||||
read_header();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns false if RX interrupt was disabled */
|
|
||||||
if (!read_payload()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,22 +375,8 @@ static int h4_send(struct net_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
|
BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
|
||||||
|
|
||||||
switch (bt_buf_get_type(buf)) {
|
net_buf_put(&tx.fifo, buf);
|
||||||
case BT_BUF_ACL_OUT:
|
uart_irq_tx_enable(h4_dev);
|
||||||
uart_poll_out(h4_dev, H4_ACL);
|
|
||||||
break;
|
|
||||||
case BT_BUF_CMD:
|
|
||||||
uart_poll_out(h4_dev, H4_CMD);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (buf->len) {
|
|
||||||
uart_poll_out(h4_dev, net_buf_pull_u8(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
net_buf_unref(buf);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue