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:
parent
ea0dcd7587
commit
0a3a108762
4 changed files with 94 additions and 16 deletions
|
@ -27,6 +27,7 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <net/buf.h>
|
||||
#include <bluetooth/buf.h>
|
||||
|
||||
|
@ -34,6 +35,33 @@
|
|||
extern "C" {
|
||||
#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 */
|
||||
int bt_recv(struct net_buf *buf);
|
||||
|
||||
|
@ -56,7 +84,10 @@ struct bt_hci_driver {
|
|||
/* Bus of the transport (BT_HCI_DRIVER_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);
|
||||
|
||||
/* Send HCI buffer to controller */
|
||||
|
|
|
@ -44,6 +44,14 @@ config BLUETOOTH_HCI_HOST
|
|||
select TINYCRYPT_SHA256_HMAC
|
||||
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
|
||||
# Virtual option usually set by the controller to request a
|
||||
# combined pool for incoming buffers.
|
||||
|
|
|
@ -58,10 +58,15 @@
|
|||
#define RPA_TIMEOUT K_SECONDS(CONFIG_BLUETOOTH_RPA_TIMEOUT)
|
||||
|
||||
/* Stacks for the threads */
|
||||
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
|
||||
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 void init_work(struct k_work *work);
|
||||
|
||||
struct bt_dev bt_dev = {
|
||||
.init = K_WORK_INITIALIZER(init_work),
|
||||
/* Give cmd_sem allowing to send first HCI_Reset cmd, the only
|
||||
* exception is if the controller requests to wait for an
|
||||
* 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),
|
||||
#endif
|
||||
.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),
|
||||
#endif
|
||||
};
|
||||
|
||||
static bt_ready_cb_t ready_cb;
|
||||
|
||||
const struct bt_storage *bt_storage;
|
||||
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
#endif
|
||||
stack_analyze("cmd tx stack", cmd_tx_thread_stack,
|
||||
sizeof(cmd_tx_thread_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;
|
||||
}
|
||||
|
||||
#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));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3569,9 +3584,15 @@ int bt_recv(struct net_buf *buf)
|
|||
}
|
||||
|
||||
switch (bt_buf_get_type(buf)) {
|
||||
#if defined(CONFIG_BLUETOOTH_CONN)
|
||||
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);
|
||||
#endif
|
||||
return 0;
|
||||
#endif /* BLUETOOTH_CONN */
|
||||
case BT_BUF_EVT:
|
||||
handle_event(buf);
|
||||
return 0;
|
||||
|
@ -3643,17 +3664,8 @@ static int irk_init(void)
|
|||
|
||||
static int bt_init(void)
|
||||
{
|
||||
struct bt_hci_driver *drv = bt_dev.drv;
|
||||
int err;
|
||||
|
||||
bt_hci_ecc_init();
|
||||
|
||||
err = drv->open();
|
||||
if (err) {
|
||||
BT_ERR("HCI driver open failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = hci_init();
|
||||
if (err) {
|
||||
return err;
|
||||
|
@ -3682,16 +3694,23 @@ static int bt_init(void)
|
|||
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;
|
||||
|
||||
BT_DBG("started");
|
||||
|
||||
if (ready_cb) {
|
||||
ready_cb(bt_init());
|
||||
}
|
||||
|
||||
while (1) {
|
||||
BT_DBG("calling fifo_get_wait");
|
||||
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();
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_BLUETOOTH_RECV_IS_RX_THREAD */
|
||||
|
||||
int bt_enable(bt_ready_cb_t cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!bt_dev.drv) {
|
||||
BT_ERR("No HCI driver registered");
|
||||
return -ENODEV;
|
||||
|
@ -3732,20 +3754,33 @@ int bt_enable(bt_ready_cb_t cb)
|
|||
return -EALREADY;
|
||||
}
|
||||
|
||||
ready_cb = cb;
|
||||
|
||||
/* TX thread */
|
||||
k_thread_spawn(cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack),
|
||||
(k_thread_entry_t)hci_cmd_tx_thread, NULL, NULL, NULL,
|
||||
K_PRIO_COOP(7), 0, K_NO_WAIT);
|
||||
|
||||
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
|
||||
/* RX thread */
|
||||
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);
|
||||
#endif
|
||||
|
||||
bt_hci_ecc_init();
|
||||
|
||||
err = bt_dev.drv->open();
|
||||
if (err) {
|
||||
BT_ERR("HCI driver open failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!cb) {
|
||||
return bt_init();
|
||||
}
|
||||
|
||||
k_work_submit(&bt_dev.init);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,8 @@ struct bt_dev {
|
|||
/* Supported commands */
|
||||
uint8_t supported_commands[64];
|
||||
|
||||
struct k_work init;
|
||||
|
||||
ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS);
|
||||
|
||||
/* LE controller specific features */
|
||||
|
@ -108,8 +110,10 @@ struct bt_dev {
|
|||
/* Last sent HCI command */
|
||||
struct net_buf *sent_cmd;
|
||||
|
||||
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
|
||||
/* Queue for incoming HCI events & ACL data */
|
||||
struct k_fifo rx_queue;
|
||||
#endif
|
||||
|
||||
/* Queue for high priority HCI events which may unlock waiters
|
||||
* in other threads. Such events include Number of Completed
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue