Bluetooth: Merge HCI command and connection TX threads

We can go further with taking advantage of k_poll and merge the
connection TX thread together with the HCI command thread, thereby
saving even more memory.

Change-Id: I1792056fd4621d62c7cd05929094033acca45c74
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-02-02 14:39:28 +02:00
commit dc70415dc4
4 changed files with 121 additions and 95 deletions

View file

@ -44,8 +44,6 @@ const struct bt_conn_auth_cb *bt_auth;
static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN]; static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN];
static struct bt_conn_cb *callback_list; static struct bt_conn_cb *callback_list;
static BT_STACK_NOINIT(conn_tx_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
#if defined(CONFIG_BLUETOOTH_BREDR) #if defined(CONFIG_BLUETOOTH_BREDR)
enum pairing_method { enum pairing_method {
LEGACY, /* Legacy (pre-SSP) pairing */ LEGACY, /* Legacy (pre-SSP) pairing */
@ -1021,7 +1019,7 @@ static bool send_buf(struct bt_conn *conn, struct net_buf *buf)
static struct k_poll_signal conn_change = K_POLL_SIGNAL_INITIALIZER(); static struct k_poll_signal conn_change = K_POLL_SIGNAL_INITIALIZER();
static int prepare_events(struct k_poll_event events[]) int bt_conn_prepare_events(struct k_poll_event events[])
{ {
int i, ev_count = 0; int i, ev_count = 0;
@ -1043,19 +1041,18 @@ static int prepare_events(struct k_poll_event events[])
BT_DBG("Adding conn %p to poll list", conn); BT_DBG("Adding conn %p to poll list", conn);
k_poll_event_init(&events[ev_count++], k_poll_event_init(&events[ev_count],
K_POLL_TYPE_FIFO_DATA_AVAILABLE, K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY, K_POLL_MODE_NOTIFY_ONLY,
&conn->tx_queue); &conn->tx_queue);
events[ev_count++].tag = BT_EVENT_CONN_TX;
} }
return ev_count; return ev_count;
} }
static void process_fifo(struct k_fifo *fifo) void bt_conn_process_tx(struct bt_conn *conn)
{ {
struct bt_conn *conn = CONTAINER_OF(fifo, struct bt_conn,
tx_queue);
struct net_buf *buf; struct net_buf *buf;
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
@ -1077,9 +1074,6 @@ static void process_fifo(struct k_fifo *fifo)
*/ */
bt_conn_unref(conn); bt_conn_unref(conn);
stack_analyze("conn tx stack", conn_tx_stack,
sizeof(conn_tx_stack));
return; return;
} }
@ -1091,53 +1085,6 @@ static void process_fifo(struct k_fifo *fifo)
} }
} }
static void process_events(struct k_poll_event *ev, int count)
{
BT_DBG("count %d", count);
for (; count; ev++, count--) {
BT_DBG("ev->state %u", ev->state);
switch (ev->state) {
case K_POLL_STATE_SIGNALED:
/* prepare_events() will do the right thing */
break;
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
process_fifo(ev->fifo);
break;
case K_POLL_STATE_NOT_READY:
break;
default:
BT_WARN("Unexpected k_poll event state %u", ev->state);
break;
}
}
}
static void conn_tx_thread(void *p1, void *p2, void *p3)
{
/* conn change signal + MAX_CONN */
static struct k_poll_event events[1 + CONFIG_BLUETOOTH_MAX_CONN];
BT_DBG("Started");
while (1) {
int ev_count, err;
ev_count = prepare_events(events);
BT_DBG("Calling k_poll with %d events", ev_count);
err = k_poll(events, ev_count, K_FOREVER);
if (err && err != -EADDRINUSE) {
BT_ERR("k_poll failed with err %d", err);
continue;
}
process_events(events, ev_count);
}
}
struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer) struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer)
{ {
struct bt_conn *conn = conn_new(); struct bt_conn *conn = conn_new();
@ -1804,9 +1751,6 @@ int bt_conn_init(void)
bt_l2cap_init(); bt_l2cap_init();
k_thread_spawn(conn_tx_stack, sizeof(conn_tx_stack), conn_tx_thread,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
/* Initialize background scan */ /* Initialize background scan */
if (IS_ENABLED(CONFIG_BLUETOOTH_CENTRAL)) { if (IS_ENABLED(CONFIG_BLUETOOTH_CENTRAL)) {
int i; int i;

View file

@ -179,3 +179,7 @@ static inline struct k_sem *bt_conn_get_pkts(struct bt_conn *conn)
return &bt_dev.le.pkts; return &bt_dev.le.pkts;
} }
/* k_poll related helpers for the TX thread */
int bt_conn_prepare_events(struct k_poll_event events[]);
void bt_conn_process_tx(struct bt_conn *conn);

View file

@ -42,7 +42,7 @@
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) #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 #endif
static BT_STACK_NOINIT(cmd_tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE); static BT_STACK_NOINIT(tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
static void init_work(struct k_work *work); static void init_work(struct k_work *work);
@ -552,8 +552,8 @@ static void hci_disconn_complete(struct net_buf *buf)
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) #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 #endif
stack_analyze("cmd tx stack", cmd_tx_thread_stack, stack_analyze("tx stack", tx_thread_stack,
sizeof(cmd_tx_thread_stack)); sizeof(tx_thread_stack));
bt_conn_set_state(conn, BT_CONN_DISCONNECTED); bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
conn->handle = 0; conn->handle = 0;
@ -2669,47 +2669,119 @@ static void hci_event(struct net_buf *buf)
net_buf_unref(buf); net_buf_unref(buf);
} }
static void hci_cmd_tx_thread(void) static void send_cmd(void)
{ {
BT_DBG("started"); struct net_buf *buf;
int err;
while (1) { /* Get next command */
struct net_buf *buf; BT_DBG("calling net_buf_get");
int err; buf = net_buf_get(&bt_dev.cmd_tx_queue, K_NO_WAIT);
BT_ASSERT(buf);
/* Wait until ncmd > 0 */ /* Wait until ncmd > 0 */
BT_DBG("calling sem_take_wait"); BT_DBG("calling sem_take_wait");
k_sem_take(&bt_dev.ncmd_sem, K_FOREVER); k_sem_take(&bt_dev.ncmd_sem, K_FOREVER);
/* Get next command - wait if necessary */ /* Clear out any existing sent command */
BT_DBG("calling net_buf_get"); if (bt_dev.sent_cmd) {
buf = net_buf_get(&bt_dev.cmd_tx_queue, K_FOREVER); BT_ERR("Uncleared pending sent_cmd");
net_buf_unref(bt_dev.sent_cmd);
bt_dev.sent_cmd = NULL;
}
/* Clear out any existing sent command */ bt_dev.sent_cmd = net_buf_ref(buf);
if (bt_dev.sent_cmd) {
BT_ERR("Uncleared pending sent_cmd");
net_buf_unref(bt_dev.sent_cmd);
bt_dev.sent_cmd = NULL;
}
bt_dev.sent_cmd = net_buf_ref(buf); BT_DBG("Sending command 0x%04x (buf %p) to driver",
cmd(buf)->opcode, buf);
BT_DBG("Sending command 0x%04x (buf %p) to driver", err = bt_send(buf);
cmd(buf)->opcode, buf); if (err) {
BT_ERR("Unable to send to driver (err %d)", err);
k_sem_give(&bt_dev.ncmd_sem);
hci_cmd_done(cmd(buf)->opcode, BT_HCI_ERR_UNSPECIFIED,
NULL);
net_buf_unref(bt_dev.sent_cmd);
bt_dev.sent_cmd = NULL;
net_buf_unref(buf);
}
}
err = bt_send(buf); static void process_events(struct k_poll_event *ev, int count)
if (err) { {
BT_ERR("Unable to send to driver (err %d)", err); BT_DBG("count %d", count);
k_sem_give(&bt_dev.ncmd_sem);
hci_cmd_done(cmd(buf)->opcode, BT_HCI_ERR_UNSPECIFIED, for (; count; ev++, count--) {
NULL); BT_DBG("ev->state %u", ev->state);
net_buf_unref(bt_dev.sent_cmd);
bt_dev.sent_cmd = NULL; switch (ev->state) {
net_buf_unref(buf); case K_POLL_STATE_SIGNALED:
break;
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
if (ev->tag == BT_EVENT_CMD_TX) {
send_cmd();
} else if (IS_ENABLED(CONFIG_BLUETOOTH_CONN) &&
ev->tag == BT_EVENT_CONN_TX) {
struct bt_conn *conn;
conn = CONTAINER_OF(ev->fifo, struct bt_conn,
tx_queue);
bt_conn_process_tx(conn);
}
break;
case K_POLL_STATE_NOT_READY:
break;
default:
BT_WARN("Unexpected k_poll event state %u", ev->state);
break;
} }
} }
} }
#if defined(CONFIG_BLUETOOTH_CONN)
/* command FIFO + conn_change signal + MAX_CONN */
#define EV_COUNT (2 + CONFIG_BLUETOOTH_MAX_CONN)
#else
/* command FIFO */
#define EV_COUNT 1
#endif
static void hci_tx_thread(void *p1, void *p2, void *p3)
{
static struct k_poll_event events[EV_COUNT] = {
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&bt_dev.cmd_tx_queue,
BT_EVENT_CMD_TX),
};
BT_DBG("Started");
while (1) {
int ev_count, err;
events[0].state = K_POLL_STATE_NOT_READY;
ev_count = 1;
if (IS_ENABLED(CONFIG_BLUETOOTH_CONN)) {
ev_count += bt_conn_prepare_events(&events[1]);
}
BT_DBG("Calling k_poll with %d events", ev_count);
err = k_poll(events, ev_count, K_FOREVER);
BT_ASSERT(err == 0);
process_events(events, ev_count);
/* Make sure we don't hog the CPU if there's all the time
* some ready events.
*/
k_yield();
}
}
static void read_local_ver_complete(struct net_buf *buf) static void read_local_ver_complete(struct net_buf *buf)
{ {
struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data; struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data;
@ -3634,9 +3706,9 @@ int bt_enable(bt_ready_cb_t cb)
ready_cb = cb; ready_cb = cb;
/* TX thread */ /* TX thread */
k_thread_spawn(cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack), k_thread_spawn(tx_thread_stack, sizeof(tx_thread_stack),
(k_thread_entry_t)hci_cmd_tx_thread, NULL, NULL, NULL, hci_tx_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0,
K_PRIO_COOP(7), 0, K_NO_WAIT); K_NO_WAIT);
#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) #if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD)
/* RX thread */ /* RX thread */

View file

@ -16,6 +16,12 @@
#define LMP_FEAT_PAGES_COUNT 1 #define LMP_FEAT_PAGES_COUNT 1
#endif #endif
/* k_poll event tags */
enum {
BT_EVENT_CMD_TX,
BT_EVENT_CONN_TX,
};
/* bt_dev flags: the flags defined here represent BT controller state */ /* bt_dev flags: the flags defined here represent BT controller state */
enum { enum {
BT_DEV_ENABLE, BT_DEV_ENABLE,