Bluetooth: drivers/h4: Add support for discarding certain events
If we are low on RX buffers and receive an LE Advertising Report or a BR/EDR Inquiry response we should just discard this. If we don't discard the event we increase the risk of deadlock where the RX interrupt is enabled but bt_recv() is doing a synchronous HCI command sending, i.e. waiting for a cmd_status/cmd_complete (which will never come since the RX interrupt is disabled). Change-Id: I6266625c9790d68bcf8e8718c8c36f127946c4c6 Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
800ed72293
commit
bbf389aae6
1 changed files with 50 additions and 15 deletions
|
@ -46,11 +46,15 @@ static struct {
|
||||||
uint16_t discard;
|
uint16_t discard;
|
||||||
|
|
||||||
bool have_hdr;
|
bool have_hdr;
|
||||||
|
bool discardable;
|
||||||
|
|
||||||
|
uint8_t hdr_len;
|
||||||
|
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
union {
|
union {
|
||||||
struct bt_hci_evt_hdr evt;
|
struct bt_hci_evt_hdr evt;
|
||||||
struct bt_hci_acl_hdr acl;
|
struct bt_hci_acl_hdr acl;
|
||||||
|
uint8_t hdr[4];
|
||||||
};
|
};
|
||||||
} rx = {
|
} rx = {
|
||||||
.fifo = K_FIFO_INITIALIZER(rx.fifo),
|
.fifo = K_FIFO_INITIALIZER(rx.fifo),
|
||||||
|
@ -78,9 +82,11 @@ static inline void h4_get_type(void)
|
||||||
switch (rx.type) {
|
switch (rx.type) {
|
||||||
case H4_EVT:
|
case H4_EVT:
|
||||||
rx.remaining = sizeof(rx.evt);
|
rx.remaining = sizeof(rx.evt);
|
||||||
|
rx.hdr_len = rx.remaining;
|
||||||
break;
|
break;
|
||||||
case H4_ACL:
|
case H4_ACL:
|
||||||
rx.remaining = sizeof(rx.acl);
|
rx.remaining = sizeof(rx.acl);
|
||||||
|
rx.hdr_len = rx.remaining;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BT_ERR("Unknown H:4 type 0x%02x", rx.type);
|
BT_ERR("Unknown H:4 type 0x%02x", rx.type);
|
||||||
|
@ -105,13 +111,34 @@ static inline void get_acl_hdr(void)
|
||||||
static inline void get_evt_hdr(void)
|
static inline void get_evt_hdr(void)
|
||||||
{
|
{
|
||||||
struct bt_hci_evt_hdr *hdr = &rx.evt;
|
struct bt_hci_evt_hdr *hdr = &rx.evt;
|
||||||
int to_read = sizeof(*hdr) - rx.remaining;
|
int to_read = rx.hdr_len - rx.remaining;
|
||||||
|
|
||||||
rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read,
|
rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read,
|
||||||
rx.remaining);
|
rx.remaining);
|
||||||
|
if (rx.hdr_len == sizeof(*hdr) && rx.remaining < sizeof(*hdr)) {
|
||||||
|
switch (rx.evt.evt) {
|
||||||
|
case BT_HCI_EVT_LE_META_EVENT:
|
||||||
|
rx.remaining++;
|
||||||
|
rx.hdr_len++;
|
||||||
|
break;
|
||||||
|
#if defined(CONFIG_BLUETOOTH_BREDR)
|
||||||
|
case BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI:
|
||||||
|
case BT_HCI_EVT_EXTENDED_INQUIRY_RESULT:
|
||||||
|
rx.discardable = true;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!rx.remaining) {
|
if (!rx.remaining) {
|
||||||
rx.remaining = hdr->len;
|
if (rx.evt.evt == BT_HCI_EVT_LE_META_EVENT &&
|
||||||
BT_DBG("Got event header. Payload %u bytes", rx.remaining);
|
rx.hdr[sizeof(*hdr)] == BT_HCI_EVT_LE_ADVERTISING_REPORT) {
|
||||||
|
BT_DBG("Marking adv report as discardable");
|
||||||
|
rx.discardable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx.remaining = hdr->len - (rx.hdr_len - sizeof(*hdr));
|
||||||
|
BT_DBG("Got event header. Payload %u bytes", hdr->len);
|
||||||
rx.have_hdr = true;
|
rx.have_hdr = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,13 +146,7 @@ static inline void get_evt_hdr(void)
|
||||||
|
|
||||||
static inline void copy_hdr(struct net_buf *buf)
|
static inline void copy_hdr(struct net_buf *buf)
|
||||||
{
|
{
|
||||||
if (rx.type == H4_EVT) {
|
net_buf_add_mem(buf, rx.hdr, rx.hdr_len);
|
||||||
net_buf_add_mem(buf, &rx.evt, sizeof(rx.evt));
|
|
||||||
BT_DBG("buf %p EVT len %u", buf, sys_le16_to_cpu(rx.evt.len));
|
|
||||||
} else {
|
|
||||||
net_buf_add_mem(buf, &rx.acl, sizeof(rx.acl));
|
|
||||||
BT_DBG("buf %p ACL len %u", buf, sys_le16_to_cpu(rx.acl.len));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rx_thread(void *p1, void *p2, void *p3)
|
static void rx_thread(void *p1, void *p2, void *p3)
|
||||||
|
@ -183,6 +204,15 @@ 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 void reset_rx(void)
|
||||||
|
{
|
||||||
|
rx.type = H4_NONE;
|
||||||
|
rx.remaining = 0;
|
||||||
|
rx.have_hdr = false;
|
||||||
|
rx.hdr_len = 0;
|
||||||
|
rx.discardable = false;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void read_payload(void)
|
static inline void read_payload(void)
|
||||||
{
|
{
|
||||||
struct net_buf *buf;
|
struct net_buf *buf;
|
||||||
|
@ -192,7 +222,14 @@ static inline void read_payload(void)
|
||||||
if (!rx.buf) {
|
if (!rx.buf) {
|
||||||
rx.buf = bt_buf_get_rx(K_NO_WAIT);
|
rx.buf = bt_buf_get_rx(K_NO_WAIT);
|
||||||
if (!rx.buf) {
|
if (!rx.buf) {
|
||||||
BT_DBG("Failed to allocate, deferring to rx_thread");
|
if (rx.discardable) {
|
||||||
|
BT_WARN("Discarding event 0x%02x", rx.evt.evt);
|
||||||
|
rx.discard = rx.remaining;
|
||||||
|
reset_rx();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_WARN("Failed to allocate, deferring to rx_thread");
|
||||||
uart_irq_rx_disable(h4_dev);
|
uart_irq_rx_disable(h4_dev);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -202,8 +239,7 @@ static inline void read_payload(void)
|
||||||
if (rx.remaining > net_buf_tailroom(rx.buf)) {
|
if (rx.remaining > net_buf_tailroom(rx.buf)) {
|
||||||
BT_ERR("Not enough space in buffer");
|
BT_ERR("Not enough space in buffer");
|
||||||
rx.discard = rx.remaining;
|
rx.discard = rx.remaining;
|
||||||
rx.remaining = 0;
|
reset_rx();
|
||||||
rx.have_hdr = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,8 +269,7 @@ static inline void read_payload(void)
|
||||||
bt_buf_set_type(buf, BT_BUF_ACL_IN);
|
bt_buf_set_type(buf, BT_BUF_ACL_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
rx.type = H4_NONE;
|
reset_rx();
|
||||||
rx.have_hdr = false;
|
|
||||||
|
|
||||||
if (prio) {
|
if (prio) {
|
||||||
BT_DBG("Calling bt_recv_prio(%p)", buf);
|
BT_DBG("Calling bt_recv_prio(%p)", buf);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue