Bluetooth: Fix UART driver robustness when out of buffers
If we're out of buffers the UART driver would previously loose sync of where the packet boundaries go, because it was using the buffer itself to store and parse the HCI packet headers. This patch modifies the UART driver to use stack variable for initial header parsing, thereby being able to always determine how many remaining bytes there are and cleanly discard the bytes until the beginning of the next packet. Change-Id: I278f3fc0c983e6a2a6904356ef1af1d25c9f06e4 Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
d63d0a832d
commit
935198e715
|
@ -39,6 +39,7 @@
|
|||
#include <board.h>
|
||||
#include <drivers/uart.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
|
@ -74,43 +75,66 @@ static int bt_uart_read(int uart, uint8_t *buf, size_t len)
|
|||
return total;
|
||||
}
|
||||
|
||||
static int bt_uart_evt_recv(struct bt_buf *buf)
|
||||
static size_t bt_uart_discard(int uart, size_t len)
|
||||
{
|
||||
struct bt_hci_evt_hdr *hdr;
|
||||
uint8_t buf[33];
|
||||
|
||||
if (len > sizeof(buf))
|
||||
len = sizeof(buf);
|
||||
|
||||
return uart_fifo_read(uart, buf, len);
|
||||
}
|
||||
|
||||
static struct bt_buf *bt_uart_evt_recv(int *remaining)
|
||||
{
|
||||
struct bt_hci_evt_hdr hdr;
|
||||
struct bt_buf *buf;
|
||||
int read;
|
||||
|
||||
hdr = (void *)bt_buf_add(buf, sizeof(*hdr));
|
||||
|
||||
read = bt_uart_read(UART, (void *)hdr, sizeof(*hdr));
|
||||
if (read != sizeof(*hdr)) {
|
||||
read = bt_uart_read(UART, (void *)&hdr, sizeof(hdr));
|
||||
if (read != sizeof(hdr)) {
|
||||
BT_ERR("Cannot read event header\n");
|
||||
return -EIO;
|
||||
*remaining = -EIO;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BT_DBG("len %u\n", hdr->len);
|
||||
*remaining = hdr.len;
|
||||
|
||||
return hdr->len;
|
||||
buf = bt_buf_get(BT_EVT, 0);
|
||||
if (buf)
|
||||
memcpy(bt_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr));
|
||||
else
|
||||
BT_ERR("No available event buffers!\n");
|
||||
|
||||
BT_DBG("len %u\n", hdr.len);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int bt_uart_acl_recv(struct bt_buf *buf)
|
||||
static struct bt_buf *bt_uart_acl_recv(int *remaining)
|
||||
{
|
||||
struct bt_hci_acl_hdr *hdr;
|
||||
uint16_t len;
|
||||
struct bt_hci_acl_hdr hdr;
|
||||
struct bt_buf *buf;
|
||||
int read;
|
||||
|
||||
hdr = (void *)bt_buf_add(buf, sizeof(*hdr));
|
||||
|
||||
read = bt_uart_read(UART, (void *)hdr, sizeof(*hdr));
|
||||
if (read != sizeof(*hdr)) {
|
||||
read = bt_uart_read(UART, (void *)&hdr, sizeof(hdr));
|
||||
if (read != sizeof(hdr)) {
|
||||
BT_ERR("Cannot read ACL header\n");
|
||||
return -EIO;
|
||||
*remaining = -EIO;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = sys_le16_to_cpu(hdr->len);
|
||||
buf = bt_buf_get(BT_ACL_IN, 0);
|
||||
if (buf)
|
||||
memcpy(bt_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr));
|
||||
else
|
||||
BT_ERR("No available ACL buffers!\n");
|
||||
|
||||
BT_DBG("len %u\n", len);
|
||||
*remaining = sys_le16_to_cpu(hdr.len);
|
||||
|
||||
return len;
|
||||
BT_DBG("len %u\n", *remaining);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void bt_uart_isr(void *unused)
|
||||
|
@ -131,8 +155,8 @@ void bt_uart_isr(void *unused)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Character(s) have been received */
|
||||
if (!buf) {
|
||||
/* Beginning of a new packet */
|
||||
if (!remaining) {
|
||||
uint8_t type;
|
||||
|
||||
/* Get packet type */
|
||||
|
@ -144,20 +168,10 @@ void bt_uart_isr(void *unused)
|
|||
|
||||
switch (type) {
|
||||
case H4_EVT:
|
||||
buf = bt_buf_get(BT_EVT, 0);
|
||||
if (!buf) {
|
||||
BT_ERR("No event buffers!\n");
|
||||
return;
|
||||
}
|
||||
remaining = bt_uart_evt_recv(buf);
|
||||
buf = bt_uart_evt_recv(&remaining);
|
||||
break;
|
||||
case H4_ACL:
|
||||
buf = bt_buf_get(BT_ACL_IN, 0);
|
||||
if (!buf) {
|
||||
BT_ERR("No ACL buffers!\n");
|
||||
return;
|
||||
}
|
||||
remaining = bt_uart_acl_recv(buf);
|
||||
buf = bt_uart_acl_recv(&remaining);
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unknown H4 type %u\n", type);
|
||||
|
@ -166,7 +180,8 @@ void bt_uart_isr(void *unused)
|
|||
|
||||
if (remaining < 0) {
|
||||
BT_ERR("Corrupted data received\n");
|
||||
goto failed;
|
||||
remaining = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (remaining > bt_buf_tailroom(buf)) {
|
||||
|
@ -177,6 +192,13 @@ void bt_uart_isr(void *unused)
|
|||
BT_DBG("need to get %u bytes\n", remaining);
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
read = bt_uart_discard(UART, remaining);
|
||||
BT_WARN("Discarded %d bytes\n", read);
|
||||
remaining -= read;
|
||||
continue;
|
||||
}
|
||||
|
||||
read = bt_uart_read(UART, bt_buf_tail(buf), remaining);
|
||||
if (read < 0) {
|
||||
BT_ERR("Error reading UART\n");
|
||||
|
|
Loading…
Reference in a new issue