Bluetooth: Host: Add choice select whether BT RX
Change CONFIG_BT_RECV_IS_RX_THREAD into a choice:CONFIG_BT_RECV_CONTEXT with the following options (names can be discussed further of course): CONFIG_BT_RECV_BLOCKING CONFIG_BT_RECV_WORKQ_BT CONFIG_BT_RECV_WORKQ_SYS This way users would be able to choose what to run most of the BLE stack on, they wouldn't be forced to a single model. We would default to CONFIG_BT_RECV_BLOCKING so that we wouldn't need to change the system workqueue stack size by default, instead asking users to do so if they select the CONFIG_BT_RECV_WORKQ_SYS option Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
This commit is contained in:
parent
2558d277e2
commit
37e561f42e
12 changed files with 137 additions and 78 deletions
|
@ -15,7 +15,6 @@ config BT_H4
|
|||
bool "H:4 UART"
|
||||
select UART_INTERRUPT_DRIVEN
|
||||
select BT_UART
|
||||
select BT_RECV_IS_RX_THREAD
|
||||
depends on SERIAL
|
||||
help
|
||||
Bluetooth H:4 UART driver. Requires hardware flow control
|
||||
|
|
|
@ -340,7 +340,8 @@ static inline void read_payload(void)
|
|||
|
||||
reset_rx();
|
||||
|
||||
if (evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO) {
|
||||
if (IS_ENABLED(CONFIG_BT_RECV_BLOCKING) &&
|
||||
(evt_flags & BT_HCI_EVT_FLAG_RECV_PRIO)) {
|
||||
BT_DBG("Calling bt_recv_prio(%p)", buf);
|
||||
bt_recv_prio(buf);
|
||||
}
|
||||
|
|
|
@ -1216,7 +1216,8 @@ struct bt_gatt_exchange_params {
|
|||
* @note Shall only be used once per connection.
|
||||
*
|
||||
* The Response comes in callback @p params->func. The callback is run from
|
||||
* the BT RX thread. @p params must remain valid until start of callback.
|
||||
* the context specified by 'config BT_RECV_CONTEXT'.
|
||||
* @p params must remain valid until start of callback.
|
||||
*
|
||||
* This function will block while the ATT request queue is full, except when
|
||||
* called from the BT RX thread, as this would cause a deadlock.
|
||||
|
@ -1457,7 +1458,8 @@ struct bt_gatt_read_params {
|
|||
* offset.
|
||||
*
|
||||
* The Response comes in callback @p params->func. The callback is run from
|
||||
* the BT RX thread. @p params must remain valid until start of callback.
|
||||
* the context specified by 'config BT_RECV_CONTEXT'.
|
||||
* @p params must remain valid until start of callback.
|
||||
*
|
||||
* This function will block while the ATT request queue is full, except when
|
||||
* called from the BT RX thread, as this would cause a deadlock.
|
||||
|
@ -1504,7 +1506,8 @@ struct bt_gatt_write_params {
|
|||
/** @brief Write Attribute Value by handle
|
||||
*
|
||||
* The Response comes in callback @p params->func. The callback is run from
|
||||
* the BT RX thread. @p params must remain valid until start of callback.
|
||||
* the context specified by 'config BT_RECV_CONTEXT'.
|
||||
* @p params must remain valid until start of callback.
|
||||
*
|
||||
* This function will block while the ATT request queue is full, except when
|
||||
* called from Bluetooth event context. When called from Bluetooth context,
|
||||
|
@ -1693,7 +1696,8 @@ struct bt_gatt_subscribe_params {
|
|||
* subscription was removed by this method.
|
||||
*
|
||||
* The Response comes in callback @p params->func. The callback is run from
|
||||
* the BT RX thread. @p params must remain valid until start of callback.
|
||||
* the context specified by 'config BT_RECV_CONTEXT'.
|
||||
* @p params must remain valid until start of callback.
|
||||
* The Notification callback @p params->notify is also called from the BT RX
|
||||
* thread.
|
||||
*
|
||||
|
|
|
@ -49,7 +49,7 @@ enum {
|
|||
* Helper for the HCI driver to get HCI event flags that describes rules that.
|
||||
* must be followed.
|
||||
*
|
||||
* When CONFIG_BT_RECV_IS_RX_THREAD is enabled the flags
|
||||
* When @kconfig{CONFIG_BT_RECV_BLOCKING} is enabled the flags
|
||||
* BT_HCI_EVT_FLAG_RECV and BT_HCI_EVT_FLAG_RECV_PRIO indicates if the event
|
||||
* should be given to bt_recv or bt_recv_prio.
|
||||
*
|
||||
|
@ -85,7 +85,7 @@ static inline uint8_t bt_hci_evt_get_flags(uint8_t evt)
|
|||
* host with data from the controller. The buffer needs to have its type
|
||||
* set with the help of bt_buf_set_type() before calling this API.
|
||||
*
|
||||
* When CONFIG_BT_RECV_IS_RX_THREAD is defined then this API should not be used
|
||||
* When @kconfig{CONFIG_BT_RECV_BLOCKING} is defined then this API should not be used
|
||||
* for so-called high priority HCI events, which should instead be delivered to
|
||||
* the host stack through bt_recv_prio().
|
||||
*
|
||||
|
@ -165,7 +165,7 @@ struct bt_hci_driver {
|
|||
* is safe to start calling the send() handler.
|
||||
*
|
||||
* If the driver uses its own RX thread, i.e.
|
||||
* CONFIG_BT_RECV_IS_RX_THREAD is set, then this
|
||||
* @kconfig{CONFIG_BT_RECV_BLOCKING} is set, then this
|
||||
* function is expected to start that thread.
|
||||
*
|
||||
* @return 0 on success or negative error number on failure.
|
||||
|
@ -179,7 +179,7 @@ struct bt_hci_driver {
|
|||
* transport is closed.
|
||||
*
|
||||
* If the driver uses its own RX thread, i.e.
|
||||
* CONFIG_BT_RECV_IS_RX_THREAD is set, then this
|
||||
* @kconfig{CONFIG_BT_RECV_BLOCKING} is set, then this
|
||||
* function is expected to abort that thread.
|
||||
* @return 0 on success or negative error number on failure.
|
||||
*/
|
||||
|
|
|
@ -95,7 +95,7 @@ config BT_BUF_ACL_RX_SIZE
|
|||
config BT_BUF_ACL_RX_COUNT
|
||||
int "Number of incoming ACL data buffers"
|
||||
default NET_BUF_RX_COUNT if NET_L2_BT
|
||||
default 3 if BT_RECV_IS_RX_THREAD
|
||||
default 3 if BT_RECV_BLOCKING
|
||||
default 6
|
||||
range 1 64
|
||||
help
|
||||
|
@ -126,7 +126,7 @@ config BT_BUF_EVT_RX_SIZE
|
|||
|
||||
config BT_BUF_EVT_RX_COUNT
|
||||
int "Number of HCI Event buffers"
|
||||
default 3 if BT_RECV_IS_RX_THREAD
|
||||
default 3 if BT_RECV_BLOCKING
|
||||
default 20 if (BT_MESH && !(BT_BUF_EVT_DISCARDABLE_COUNT > 0))
|
||||
default 10
|
||||
range 2 255
|
||||
|
|
|
@ -96,7 +96,6 @@ choice BT_LL_CHOICE
|
|||
|
||||
config BT_LL_SW_SPLIT
|
||||
bool "Software-based BLE Link Layer"
|
||||
select BT_RECV_IS_RX_THREAD
|
||||
select ENTROPY_GENERATOR
|
||||
select NRF_HW_TIMER0_RESERVED
|
||||
select NRF_HW_RTC0_RESERVED
|
||||
|
|
|
@ -68,19 +68,48 @@ config BT_HCI_RESERVE
|
|||
Headroom that the driver needs for sending and receiving buffers. Add a
|
||||
new 'default' entry for each new driver.
|
||||
|
||||
config BT_RECV_IS_RX_THREAD
|
||||
# Hidden option set by the HCI driver to indicate that there's
|
||||
# no need for the host to have its own RX thread.
|
||||
# If this option has been enabled it is then the responsibility of the
|
||||
# HCI driver to call bt_recv_prio from a higher priority context than
|
||||
# bt_recv in order to avoid deadlocks.
|
||||
# If this option is disabled then only bt_recv should be called.
|
||||
bool
|
||||
prompt "bt_recv is called from RX thread" if BT_NO_DRIVER
|
||||
|
||||
choice BT_RECV_CONTEXT
|
||||
prompt "BT RX Thread Selection"
|
||||
default BT_RECV_BLOCKING if BT_LL_SW_SPLIT || BT_H4
|
||||
default BT_RECV_WORKQ_BT
|
||||
help
|
||||
Selects in which context incoming low priority HCI packets are processed.
|
||||
The host defines some events as high priority to avoid race conditions and deadlocks.
|
||||
High priority events are always processed in the context of the caller of bt_recv()
|
||||
or bt_recv_prio(). The choice will influence RAM usage and how fast incoming HCI
|
||||
packets are processed.
|
||||
|
||||
config BT_RECV_BLOCKING
|
||||
bool "Process HCI packets in the context of bt_recv() and bt_recv_prio()"
|
||||
help
|
||||
When this option is selected, the host will not have its own RX thread.
|
||||
With this option it is the responsibility of the HCI driver to call
|
||||
bt_recv_prio from a higher priority context than bt_recv() in order to avoid deadlocks.
|
||||
|
||||
config BT_RECV_WORKQ_SYS
|
||||
bool "Process low priority HCI packets in the system work queue"
|
||||
help
|
||||
When this option is selected, the host will process incoming low priority HCI packets
|
||||
in the system work queue. The HCI driver shall not call bt_recv_prio().
|
||||
High priority HCI packets will processed in the context of the caller of bt_recv().
|
||||
The application needs to ensure the system workqueue stack size (SYSTEM_WORKQUEUE_STACK_SIZE)
|
||||
is large enough, refer to BT_RX_STACK_SIZE for the recommended minimum.
|
||||
Note: When this option is used, other users of the system work queue will influence the
|
||||
latency of incoming Bluetooth events.
|
||||
|
||||
config BT_RECV_WORKQ_BT
|
||||
bool "Process low priority HCI packets in the bluetooth-specific work queue"
|
||||
help
|
||||
When this option is selected, the host will process incoming low priority HCI packets
|
||||
in the bluetooth-specific work queue. The HCI driver shall not call bt_recv_prio().
|
||||
High priority HCI packets will processed in the context of the caller of bt_recv().
|
||||
The application needs to ensure the bluetooth-specific work queue size is large enough,
|
||||
refer to BT_RX_STACK_SIZE for the recommended minimum.
|
||||
endchoice
|
||||
|
||||
config BT_RX_STACK_SIZE
|
||||
int "Size of the receiving thread stack"
|
||||
depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD
|
||||
default 768 if BT_HCI_RAW
|
||||
default 3092 if BT_MESH_GATT_CLIENT
|
||||
default 2048 if BT_MESH
|
||||
|
@ -98,7 +127,6 @@ config BT_RX_STACK_SIZE
|
|||
config BT_RX_PRIO
|
||||
# Hidden option for Co-Operative Rx thread priority
|
||||
int
|
||||
depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD
|
||||
default 8
|
||||
|
||||
config BT_DRIVER_RX_HIGH_PRIO
|
||||
|
|
|
@ -61,10 +61,14 @@
|
|||
#define HCI_CMD_TIMEOUT K_SECONDS(10)
|
||||
|
||||
/* Stacks for the threads */
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
static struct k_thread rx_thread_data;
|
||||
#if !defined(CONFIG_BT_RECV_BLOCKING)
|
||||
static void rx_work_handler(struct k_work *work);
|
||||
static K_WORK_DEFINE(rx_work, rx_work_handler);
|
||||
#if defined(CONFIG_BT_RECV_WORKQ_BT)
|
||||
static struct k_work_q bt_workq;
|
||||
static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_RX_STACK_SIZE);
|
||||
#endif
|
||||
#endif /* CONFIG_BT_RECV_WORKQ_BT */
|
||||
#endif /* !CONFIG_BT_RECV_BLOCKING */
|
||||
static struct k_thread tx_thread_data;
|
||||
static K_KERNEL_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
|
||||
|
||||
|
@ -82,7 +86,7 @@ struct bt_dev bt_dev = {
|
|||
.ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1),
|
||||
#endif
|
||||
.cmd_tx_queue = Z_FIFO_INITIALIZER(bt_dev.cmd_tx_queue),
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if !defined(CONFIG_BT_RECV_BLOCKING)
|
||||
.rx_queue = Z_FIFO_INITIALIZER(bt_dev.rx_queue),
|
||||
#endif
|
||||
#if defined(CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC)
|
||||
|
@ -3413,6 +3417,22 @@ void hci_event_prio(struct net_buf *buf)
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_BLOCKING)
|
||||
static void rx_queue_put(struct net_buf *buf)
|
||||
{
|
||||
net_buf_put(&bt_dev.rx_queue, buf);
|
||||
|
||||
#if defined(CONFIG_BT_RECV_WORKQ_SYS)
|
||||
const int err = k_work_submit(&rx_work);
|
||||
#elif defined(CONFIG_BT_RECV_WORKQ_BT)
|
||||
const int err = k_work_submit_to_queue(&bt_workq, &rx_work);
|
||||
#endif /* CONFIG_BT_RECV_WORKQ_SYS */
|
||||
if (err < 0) {
|
||||
BT_ERR("Could not submit rx_work: %d", err);
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_BT_RECV_BLOCKING */
|
||||
|
||||
int bt_recv(struct net_buf *buf)
|
||||
{
|
||||
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
||||
|
@ -3422,16 +3442,16 @@ int bt_recv(struct net_buf *buf)
|
|||
switch (bt_buf_get_type(buf)) {
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
case BT_BUF_ACL_IN:
|
||||
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if defined(CONFIG_BT_RECV_BLOCKING)
|
||||
hci_acl(buf);
|
||||
#else
|
||||
net_buf_put(&bt_dev.rx_queue, buf);
|
||||
rx_queue_put(buf);
|
||||
#endif
|
||||
return 0;
|
||||
#endif /* BT_CONN */
|
||||
case BT_BUF_EVT:
|
||||
{
|
||||
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if defined(CONFIG_BT_RECV_BLOCKING)
|
||||
hci_event(buf);
|
||||
#else
|
||||
struct bt_hci_evt_hdr *hdr = (void *)buf->data;
|
||||
|
@ -3442,7 +3462,7 @@ int bt_recv(struct net_buf *buf)
|
|||
}
|
||||
|
||||
if (evt_flags & BT_HCI_EVT_FLAG_RECV) {
|
||||
net_buf_put(&bt_dev.rx_queue, buf);
|
||||
rx_queue_put(buf);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -3450,10 +3470,10 @@ int bt_recv(struct net_buf *buf)
|
|||
}
|
||||
#if defined(CONFIG_BT_ISO)
|
||||
case BT_BUF_ISO_IN:
|
||||
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if defined(CONFIG_BT_RECV_BLOCKING)
|
||||
hci_iso(buf);
|
||||
#else
|
||||
net_buf_put(&bt_dev.rx_queue, buf);
|
||||
rx_queue_put(buf);
|
||||
#endif
|
||||
return 0;
|
||||
#endif /* CONFIG_BT_ISO */
|
||||
|
@ -3464,7 +3484,6 @@ int bt_recv(struct net_buf *buf)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
int bt_recv_prio(struct net_buf *buf)
|
||||
{
|
||||
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
|
||||
|
@ -3475,7 +3494,6 @@ int bt_recv_prio(struct net_buf *buf)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(CONFIG_BT_RECV_IS_RX_THREAD) */
|
||||
|
||||
int bt_hci_driver_register(const struct bt_hci_driver *drv)
|
||||
{
|
||||
|
@ -3554,16 +3572,18 @@ static void init_work(struct k_work *work)
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
static void hci_rx_thread(void)
|
||||
#if !defined(CONFIG_BT_RECV_BLOCKING)
|
||||
static void rx_work_handler(struct k_work *work)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct net_buf *buf;
|
||||
|
||||
BT_DBG("started");
|
||||
|
||||
while (1) {
|
||||
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_NO_WAIT);
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf),
|
||||
buf->len);
|
||||
|
@ -3591,10 +3611,20 @@ static void hci_rx_thread(void)
|
|||
/* Make sure we don't hog the CPU if the rx_queue never
|
||||
* gets empty.
|
||||
*/
|
||||
k_yield();
|
||||
if (k_fifo_is_empty(&bt_dev.rx_queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_RECV_WORKQ_SYS)
|
||||
err = k_work_submit(&rx_work);
|
||||
#elif defined(CONFIG_BT_RECV_WORKQ_BT)
|
||||
err = k_work_submit_to_queue(&bt_workq, &rx_work);
|
||||
#endif
|
||||
if (err < 0) {
|
||||
BT_ERR("Could not submit rx_work: %d", err);
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_BT_RECV_IS_RX_THREAD */
|
||||
#endif /* !CONFIG_BT_RECV_BLOCKING */
|
||||
|
||||
int bt_enable(bt_ready_cb_t cb)
|
||||
{
|
||||
|
@ -3633,14 +3663,12 @@ int bt_enable(bt_ready_cb_t cb)
|
|||
0, K_NO_WAIT);
|
||||
k_thread_name_set(&tx_thread_data, "BT TX");
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if defined(CONFIG_BT_RECV_WORKQ_BT)
|
||||
/* RX thread */
|
||||
k_thread_create(&rx_thread_data, rx_thread_stack,
|
||||
K_KERNEL_STACK_SIZEOF(rx_thread_stack),
|
||||
(k_thread_entry_t)hci_rx_thread, NULL, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_BT_RX_PRIO),
|
||||
0, K_NO_WAIT);
|
||||
k_thread_name_set(&rx_thread_data, "BT RX");
|
||||
k_work_queue_start(&bt_workq, rx_thread_stack,
|
||||
CONFIG_BT_RX_STACK_SIZE,
|
||||
K_PRIO_COOP(CONFIG_BT_RX_PRIO), NULL);
|
||||
k_thread_name_set(&bt_workq.thread, "BT RX");
|
||||
#endif
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
|
||||
|
@ -3699,9 +3727,9 @@ int bt_disable(void)
|
|||
/* Abort TX thread */
|
||||
k_thread_abort(&tx_thread_data);
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if defined(CONFIG_BT_RECV_WORKQ_BT)
|
||||
/* Abort RX thread */
|
||||
k_thread_abort(&rx_thread_data);
|
||||
k_thread_abort(&bt_workq.thread);
|
||||
#endif
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
|
||||
|
|
|
@ -341,7 +341,7 @@ struct bt_dev {
|
|||
/* Last sent HCI command */
|
||||
struct net_buf *sent_cmd;
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
#if !defined(CONFIG_BT_RECV_BLOCKING)
|
||||
/* Queue for incoming HCI events & ACL data */
|
||||
struct k_fifo rx_queue;
|
||||
#endif
|
||||
|
|
|
@ -91,7 +91,7 @@ static void send_cmd_status(uint16_t opcode, uint8_t status)
|
|||
evt->opcode = sys_cpu_to_le16(opcode);
|
||||
evt->status = status;
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_RECV_IS_RX_THREAD)) {
|
||||
if (IS_ENABLED(CONFIG_BT_RECV_BLOCKING)) {
|
||||
bt_recv_prio(buf);
|
||||
} else {
|
||||
bt_recv(buf);
|
||||
|
|
|
@ -4,7 +4,7 @@ CONFIG_ZTEST=y
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_CTLR=n
|
||||
CONFIG_BT_NO_DRIVER=y
|
||||
CONFIG_BT_RECV_IS_RX_THREAD=y
|
||||
CONFIG_BT_RECV_BLOCKING=y
|
||||
|
||||
CONFIG_BT_HCI_VS_EVT_USER=y
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ CONFIG_BT_HCI=n
|
|||
CONFIG_BT_HCI_RAW=n
|
||||
CONFIG_BT_OBSERVER=y
|
||||
CONFIG_BT_NO_DRIVER=y
|
||||
CONFIG_BT_RECV_IS_RX_THREAD=y
|
||||
CONFIG_BT_RECV_BLOCKING=y
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
|
||||
CONFIG_BT_DEBUG_LOG=y
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue