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 <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 */
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue