Bluetooth: Introduce support for HCI driver-side RX thread

Add support for using the context bt_recv() is called in as the RX
thread, rather than having a separate host-side RX thread.

Change-Id: I256bfe5dece5272c816f2292e58747553189963d
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2016-12-21 21:09:27 +02:00
commit 0a3a108762
4 changed files with 94 additions and 16 deletions

View file

@ -27,6 +27,7 @@
* @{ * @{
*/ */
#include <stdbool.h>
#include <net/buf.h> #include <net/buf.h>
#include <bluetooth/buf.h> #include <bluetooth/buf.h>
@ -34,6 +35,33 @@
extern "C" { extern "C" {
#endif #endif
#if defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
/** Helper for the HCI driver to know which events are ok to be passed
* through the RX thread and which must be given to bt_recv() from another
* context. If this function returns true it's safe to pass the event
* through the RX thread, however if it returns false then this risks
* a deadlock.
*
* @param evt HCI event code.
*
* @return true if the event can be processed in the RX thread, false
* if it cannot.
*/
static inline bool bt_hci_evt_is_prio(uint8_t evt)
{
switch (evt) {
case BT_HCI_EVT_CMD_COMPLETE:
case BT_HCI_EVT_CMD_STATUS:
#if defined(CONFIG_BLUETOOTH_CONN)
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
#endif
return true;
default:
return false;
}
}
#endif
/* Receive data from the controller/HCI driver */ /* Receive data from the controller/HCI driver */
int bt_recv(struct net_buf *buf); int bt_recv(struct net_buf *buf);
@ -56,7 +84,10 @@ struct bt_hci_driver {
/* Bus of the transport (BT_HCI_DRIVER_BUS_*) */ /* Bus of the transport (BT_HCI_DRIVER_BUS_*) */
enum bt_hci_driver_bus bus; enum bt_hci_driver_bus bus;
/* Open the HCI transport */ /* Open the HCI transport. If the driver uses its own RX thread,
* i.e. CONFIG_BLUETOOTH_RECV_IS_RX_THREAD is set, then this
* function is expected to start that thread.
*/
int (*open)(void); int (*open)(void);
/* Send HCI buffer to controller */ /* Send HCI buffer to controller */

View file

@ -44,6 +44,14 @@ config BLUETOOTH_HCI_HOST
select TINYCRYPT_SHA256_HMAC select TINYCRYPT_SHA256_HMAC
select TINYCRYPT_SHA256_HMAC_PRNG select TINYCRYPT_SHA256_HMAC_PRNG
config BLUETOOTH_RECV_IS_RX_THREAD
# Virtual option set by the HCI driver to indicate that there's
# no need for the host to have its own RX thread, rather the
# context that bt_recv() gets called in is already good enough.
# If this is set, the driver RX thread is required as the first
# thing to make a call to bt_rx_thread_ready().
bool
config BLUETOOTH_COMBINED_RX_BUF config BLUETOOTH_COMBINED_RX_BUF
# Virtual option usually set by the controller to request a # Virtual option usually set by the controller to request a
# combined pool for incoming buffers. # combined pool for incoming buffers.

View file

@ -58,10 +58,15 @@
#define RPA_TIMEOUT K_SECONDS(CONFIG_BLUETOOTH_RPA_TIMEOUT) #define RPA_TIMEOUT K_SECONDS(CONFIG_BLUETOOTH_RPA_TIMEOUT)
/* Stacks for the threads */ /* Stacks for the threads */
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
static BT_STACK_NOINIT(rx_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE); static BT_STACK_NOINIT(rx_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE);
#endif
static BT_STACK_NOINIT(cmd_tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE); static BT_STACK_NOINIT(cmd_tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
static void init_work(struct k_work *work);
struct bt_dev bt_dev = { struct bt_dev bt_dev = {
.init = K_WORK_INITIALIZER(init_work),
/* Give cmd_sem allowing to send first HCI_Reset cmd, the only /* Give cmd_sem allowing to send first HCI_Reset cmd, the only
* exception is if the controller requests to wait for an * exception is if the controller requests to wait for an
* initial Command Complete for NOP. * initial Command Complete for NOP.
@ -72,9 +77,13 @@ struct bt_dev bt_dev = {
.ncmd_sem = K_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1), .ncmd_sem = K_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1),
#endif #endif
.cmd_tx_queue = K_FIFO_INITIALIZER(bt_dev.cmd_tx_queue), .cmd_tx_queue = K_FIFO_INITIALIZER(bt_dev.cmd_tx_queue),
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
.rx_queue = K_FIFO_INITIALIZER(bt_dev.rx_queue), .rx_queue = K_FIFO_INITIALIZER(bt_dev.rx_queue),
#endif
}; };
static bt_ready_cb_t ready_cb;
const struct bt_storage *bt_storage; const struct bt_storage *bt_storage;
static bt_le_scan_cb_t *scan_dev_found_cb; static bt_le_scan_cb_t *scan_dev_found_cb;
@ -629,7 +638,9 @@ static void hci_disconn_complete(struct net_buf *buf)
conn->err = evt->reason; conn->err = evt->reason;
/* Check stacks usage (no-ops if not enabled) */ /* Check stacks usage (no-ops if not enabled) */
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
stack_analyze("rx stack", rx_thread_stack, sizeof(rx_thread_stack)); stack_analyze("rx stack", rx_thread_stack, sizeof(rx_thread_stack));
#endif
stack_analyze("cmd tx stack", cmd_tx_thread_stack, stack_analyze("cmd tx stack", cmd_tx_thread_stack,
sizeof(cmd_tx_thread_stack)); sizeof(cmd_tx_thread_stack));
stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack)); stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack));
@ -3549,7 +3560,11 @@ static inline void handle_event(struct net_buf *buf)
break; break;
} }
#if defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
hci_event(net_buf_ref(buf));
#else
net_buf_put(&bt_dev.rx_queue, net_buf_ref(buf)); net_buf_put(&bt_dev.rx_queue, net_buf_ref(buf));
#endif
break; break;
} }
@ -3569,9 +3584,15 @@ int bt_recv(struct net_buf *buf)
} }
switch (bt_buf_get_type(buf)) { switch (bt_buf_get_type(buf)) {
#if defined(CONFIG_BLUETOOTH_CONN)
case BT_BUF_ACL_IN: case BT_BUF_ACL_IN:
#if defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
hci_acl(buf);
#else
net_buf_put(&bt_dev.rx_queue, buf); net_buf_put(&bt_dev.rx_queue, buf);
#endif
return 0; return 0;
#endif /* BLUETOOTH_CONN */
case BT_BUF_EVT: case BT_BUF_EVT:
handle_event(buf); handle_event(buf);
return 0; return 0;
@ -3643,17 +3664,8 @@ static int irk_init(void)
static int bt_init(void) static int bt_init(void)
{ {
struct bt_hci_driver *drv = bt_dev.drv;
int err; int err;
bt_hci_ecc_init();
err = drv->open();
if (err) {
BT_ERR("HCI driver open failed (%d)", err);
return err;
}
err = hci_init(); err = hci_init();
if (err) { if (err) {
return err; return err;
@ -3682,16 +3694,23 @@ static int bt_init(void)
return 0; return 0;
} }
static void hci_rx_thread(bt_ready_cb_t ready_cb) static void init_work(struct k_work *work)
{
int err;
err = bt_init();
if (ready_cb) {
ready_cb(err);
}
}
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
static void hci_rx_thread(void)
{ {
struct net_buf *buf; struct net_buf *buf;
BT_DBG("started"); BT_DBG("started");
if (ready_cb) {
ready_cb(bt_init());
}
while (1) { while (1) {
BT_DBG("calling fifo_get_wait"); BT_DBG("calling fifo_get_wait");
buf = net_buf_get(&bt_dev.rx_queue, K_FOREVER); buf = net_buf_get(&bt_dev.rx_queue, K_FOREVER);
@ -3720,9 +3739,12 @@ static void hci_rx_thread(bt_ready_cb_t ready_cb)
k_yield(); k_yield();
} }
} }
#endif /* !CONFIG_BLUETOOTH_RECV_IS_RX_THREAD */
int bt_enable(bt_ready_cb_t cb) int bt_enable(bt_ready_cb_t cb)
{ {
int err;
if (!bt_dev.drv) { if (!bt_dev.drv) {
BT_ERR("No HCI driver registered"); BT_ERR("No HCI driver registered");
return -ENODEV; return -ENODEV;
@ -3732,20 +3754,33 @@ int bt_enable(bt_ready_cb_t cb)
return -EALREADY; return -EALREADY;
} }
ready_cb = cb;
/* TX thread */ /* TX thread */
k_thread_spawn(cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack), k_thread_spawn(cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack),
(k_thread_entry_t)hci_cmd_tx_thread, NULL, NULL, NULL, (k_thread_entry_t)hci_cmd_tx_thread, NULL, NULL, NULL,
K_PRIO_COOP(7), 0, K_NO_WAIT); K_PRIO_COOP(7), 0, K_NO_WAIT);
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
/* RX thread */ /* RX thread */
k_thread_spawn(rx_thread_stack, sizeof(rx_thread_stack), k_thread_spawn(rx_thread_stack, sizeof(rx_thread_stack),
(k_thread_entry_t)hci_rx_thread, cb, NULL, NULL, (k_thread_entry_t)hci_rx_thread, NULL, NULL, NULL,
K_PRIO_COOP(7), 0, K_NO_WAIT); K_PRIO_COOP(7), 0, K_NO_WAIT);
#endif
bt_hci_ecc_init();
err = bt_dev.drv->open();
if (err) {
BT_ERR("HCI driver open failed (%d)", err);
return err;
}
if (!cb) { if (!cb) {
return bt_init(); return bt_init();
} }
k_work_submit(&bt_dev.init);
return 0; return 0;
} }

View file

@ -92,6 +92,8 @@ struct bt_dev {
/* Supported commands */ /* Supported commands */
uint8_t supported_commands[64]; uint8_t supported_commands[64];
struct k_work init;
ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS); ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS);
/* LE controller specific features */ /* LE controller specific features */
@ -108,8 +110,10 @@ struct bt_dev {
/* Last sent HCI command */ /* Last sent HCI command */
struct net_buf *sent_cmd; struct net_buf *sent_cmd;
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
/* Queue for incoming HCI events & ACL data */ /* Queue for incoming HCI events & ACL data */
struct k_fifo rx_queue; struct k_fifo rx_queue;
#endif
/* Queue for high priority HCI events which may unlock waiters /* Queue for high priority HCI events which may unlock waiters
* in other threads. Such events include Number of Completed * in other threads. Such events include Number of Completed