From d8e9e71acd058092f120aef7bd0525e33dbfd6bc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 23 May 2015 19:58:06 +0300 Subject: [PATCH] Bluetooth: Add dedicated stack for Command Complete/Status events These events are special since they release any blocking bt_hci_cmd_send_sync() calls. In order to be able to use the send_sync API from HCI Event handlers without deadlocking having the Command Complete/Status events processed through a dedicated fiber is essential. Change-Id: Id5c597d618bf326a8efb7c9ca2500af60a502d77 Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 91 +++++++++++++++++++++++++++++++++------- net/bluetooth/hci_core.h | 3 ++ 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b1d32eaadaa..4e8d8d107a8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -57,19 +57,22 @@ /* Stacks for the fibers */ #if defined(CONFIG_BLUETOOTH_DEBUG) #define RX_STACK_SIZE 2048 +#define CMD_RX_STACK_SIZE 512 #define CMD_TX_STACK_SIZE 512 #else #define RX_STACK_SIZE 1024 +#define CMD_RX_STACK_SIZE 256 #define CMD_TX_STACK_SIZE 256 #endif static const uint8_t BDADDR_ANY[6] = { 0, 0, 0, 0, 0 }; static char rx_fiber_stack[RX_STACK_SIZE]; +static char cmd_rx_fiber_stack[CMD_RX_STACK_SIZE]; static char cmd_tx_fiber_stack[CMD_TX_STACK_SIZE]; #if defined(CONFIG_BLUETOOTH_DEBUG) -static nano_context_id_t rx_fiber_id; +static nano_context_id_t cmd_rx_fiber_id; #endif static struct bt_dev dev; @@ -202,7 +205,7 @@ int bt_hci_cmd_send_sync(uint16_t opcode, struct bt_buf *buf, * event and giving back the blocking semaphore. */ #if defined(CONFIG_BLUETOOTH_DEBUG) - if (context_self_get() == rx_fiber_id) { + if (context_self_get() == cmd_rx_fiber_id) { BT_ERR("called from invalid context!\n"); return -EDEADLK; } @@ -592,12 +595,6 @@ static void hci_event(struct bt_buf *buf) case BT_HCI_EVT_ENCRYPT_CHANGE: hci_encrypt_change(buf); break; - case BT_HCI_EVT_CMD_COMPLETE: - hci_cmd_complete(buf); - break; - case BT_HCI_EVT_CMD_STATUS: - hci_cmd_status(buf); - break; case BT_HCI_EVT_NUM_COMPLETED_PACKETS: hci_num_completed_packets(buf); break; @@ -653,11 +650,6 @@ static void hci_rx_fiber(void) BT_DBG("started\n"); - /* So we can avoid bt_hci_cmd_send_sync deadlocks */ -#if defined(CONFIG_BLUETOOTH_DEBUG) - rx_fiber_id = context_self_get(); -#endif - while (1) { BT_DBG("calling fifo_get_wait\n"); buf = nano_fifo_get_wait(&dev.rx_queue); @@ -680,6 +672,50 @@ static void hci_rx_fiber(void) } } +static void cmd_rx_fiber(void) +{ + struct bt_buf *buf; + + BT_DBG("started\n"); + + /* So we can avoid bt_hci_cmd_send_sync deadlocks */ +#if defined(CONFIG_BLUETOOTH_DEBUG) + cmd_rx_fiber_id = context_self_get(); +#endif + + while (1) { + struct bt_hci_evt_hdr *hdr; + + BT_DBG("calling fifo_get_wait\n"); + buf = nano_fifo_get_wait(&dev.cmd_rx_queue); + + BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len); + + if (buf->type != BT_EVT) { + BT_ERR("Unknown buf type %u\n", buf->type); + bt_buf_put(buf); + continue; + } + + hdr = (void *)buf->data; + bt_buf_pull(buf, sizeof(*hdr)); + + switch (hdr->evt) { + case BT_HCI_EVT_CMD_COMPLETE: + hci_cmd_complete(buf); + break; + case BT_HCI_EVT_CMD_STATUS: + hci_cmd_status(buf); + break; + default: + BT_ERR("Unknown event 0x%02x\n", hdr->evt); + break; + } + + bt_buf_put(buf); + } +} + static void read_local_features_complete(struct bt_buf *buf) { struct bt_hci_rp_read_local_features *rp = (void *)buf->data; @@ -907,7 +943,31 @@ int bt_hci_reset(void) void bt_recv(struct bt_buf *buf) { + struct bt_hci_evt_hdr *hdr; + BT_DBG("buf %p len %u\n", buf, buf->len); + + if (buf->type == BT_ACL_IN) { + nano_fifo_put(&dev.rx_queue, buf); + return; + } + + if (buf->type != BT_EVT) { + BT_ERR("Invalid buf type %u\n", buf->type); + bt_buf_put(buf); + return; + } + + /* Command Complete/Status events have their own cmd_rx queue, + * all other events go through rx queue. + */ + hdr = (void *)buf->data; + if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE || + hdr->evt == BT_HCI_EVT_CMD_STATUS) { + nano_fifo_put(&dev.cmd_rx_queue, buf); + return; + } + nano_fifo_put(&dev.rx_queue, buf); } @@ -949,9 +1009,12 @@ static void cmd_queue_init(void) static void rx_queue_init(void) { nano_fifo_init(&dev.rx_queue); - fiber_start(rx_fiber_stack, RX_STACK_SIZE, (nano_fiber_entry_t) hci_rx_fiber, 0, 0, 7, 0); + + nano_fifo_init(&dev.cmd_rx_queue); + fiber_start(cmd_rx_fiber_stack, RX_STACK_SIZE, + (nano_fiber_entry_t) cmd_rx_fiber, 0, 0, 7, 0); } int bt_init(void) diff --git a/net/bluetooth/hci_core.h b/net/bluetooth/hci_core.h index ef4c6039ef1..29fd8afb09e 100644 --- a/net/bluetooth/hci_core.h +++ b/net/bluetooth/hci_core.h @@ -71,6 +71,9 @@ struct bt_dev { /* Queue for incoming HCI events & ACL data */ struct nano_fifo rx_queue; + /* Queue for incoming HCI Command Complete/Status events */ + struct nano_fifo cmd_rx_queue; + /* Queue for outgoing HCI commands */ struct nano_fifo cmd_tx_queue;