Bluetooth: Reuse HCI command buffers for the command response

Reduce the pressure on the common RX buffer pool by reusing HCI
command buffers also for the Command Status or Command Complete
response to them. This also implies removing the existing Kconfig
variable for the command buffer sizes since the size is also dependent
on maximum Command Complete event sizes. Instead, reuse the RX buffer
size also for HCI Command buffers.

Change-Id: I006b287d64a0c9ca40de741aa9a424a49a927385
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-02-01 17:37:14 +02:00
commit 50678b03cb
11 changed files with 171 additions and 106 deletions

View file

@ -149,6 +149,27 @@ static inline void copy_hdr(struct net_buf *buf)
net_buf_add_mem(buf, rx.hdr, rx.hdr_len);
}
static void reset_rx(void)
{
rx.type = H4_NONE;
rx.remaining = 0;
rx.have_hdr = false;
rx.hdr_len = 0;
rx.discardable = false;
}
static struct net_buf *get_rx(int timeout)
{
BT_DBG("type 0x%02x, evt 0x%02x", rx.type, rx.evt.evt);
if (rx.type == H4_EVT && (rx.evt.evt == BT_HCI_EVT_CMD_COMPLETE ||
rx.evt.evt == BT_HCI_EVT_CMD_STATUS)) {
return bt_buf_get_cmd_complete(timeout);
}
return bt_buf_get_rx(timeout);
}
static void rx_thread(void *p1, void *p2, void *p3)
{
struct net_buf *buf;
@ -162,15 +183,18 @@ static void rx_thread(void *p1, void *p2, void *p3)
while (1) {
BT_DBG("rx.buf %p", rx.buf);
if (!rx.buf) {
rx.buf = bt_buf_get_rx(K_FOREVER);
/* We can only do the allocation if we know the initial
* header, since Command Complete/Status events must use the
* original command buffer (if available).
*/
if (rx.have_hdr && !rx.buf) {
rx.buf = get_rx(K_FOREVER);
BT_DBG("Got rx.buf %p", rx.buf);
if (rx.remaining > net_buf_tailroom(rx.buf)) {
BT_ERR("Not enough space in buffer");
rx.discard = rx.remaining;
rx.remaining = 0;
rx.have_hdr = false;
} else if (rx.have_hdr) {
reset_rx();
} else {
copy_hdr(rx.buf);
}
}
@ -204,15 +228,6 @@ static size_t h4_discard(struct device *uart, size_t len)
return uart_fifo_read(uart, buf, min(len, sizeof(buf)));
}
static void reset_rx(void)
{
rx.type = H4_NONE;
rx.remaining = 0;
rx.have_hdr = false;
rx.hdr_len = 0;
rx.discardable = false;
}
static inline void read_payload(void)
{
struct net_buf *buf;
@ -220,7 +235,7 @@ static inline void read_payload(void)
int read;
if (!rx.buf) {
rx.buf = bt_buf_get_rx(K_NO_WAIT);
rx.buf = get_rx(K_NO_WAIT);
if (!rx.buf) {
if (rx.discardable) {
BT_WARN("Discarding event 0x%02x", rx.evt.evt);
@ -301,8 +316,7 @@ static inline void read_header(void)
if (rx.remaining > net_buf_tailroom(rx.buf)) {
BT_ERR("Not enough space in buffer");
rx.discard = rx.remaining;
rx.remaining = 0;
rx.have_hdr = false;
reset_rx();
} else {
copy_hdr(rx.buf);
}

View file

@ -402,6 +402,28 @@ static void h5_process_complete_packet(uint8_t *hdr)
}
}
static inline struct net_buf *get_evt_buf(uint8_t evt)
{
struct net_buf *buf;
switch (evt) {
case BT_HCI_EVT_CMD_COMPLETE:
case BT_HCI_EVT_CMD_STATUS:
buf = bt_buf_get_cmd_complete(K_NO_WAIT);
break;
default:
buf = bt_buf_get_rx(K_NO_WAIT);
break;
}
if (buf) {
bt_buf_set_type(h5.rx_buf, BT_BUF_EVT);
net_buf_add_u8(h5.rx_buf, evt);
}
return buf;
}
static void bt_uart_isr(struct device *unused)
{
static int remaining;
@ -461,14 +483,9 @@ static void bt_uart_isr(struct device *unused)
switch (H5_HDR_PKT_TYPE(hdr)) {
case HCI_EVENT_PKT:
h5.rx_buf = bt_buf_get_rx(K_NO_WAIT);
if (!h5.rx_buf) {
BT_WARN("No available event buffers");
h5_reset_rx();
continue;
}
bt_buf_set_type(h5.rx_buf, BT_BUF_EVT);
/* The buffer is allocated only once we know
* the exact event type.
*/
h5.rx_state = PAYLOAD;
break;
case HCI_ACLDATA_PKT:
@ -506,6 +523,18 @@ static void bt_uart_isr(struct device *unused)
continue;
}
/* Allocate HCI event buffer now that we know the
* exact event type.
*/
if (!h5.rx_buf) {
h5.rx_buf = get_evt_buf(byte);
if (!h5.rx_buf) {
BT_WARN("No available event buffers");
h5_reset_rx();
continue;
}
}
net_buf_add_mem(h5.rx_buf, &byte, sizeof(byte));
remaining--;
if (!remaining) {

View file

@ -171,13 +171,20 @@ static void bt_spi_rx_thread(void)
switch (rxmsg[PACKET_TYPE]) {
case HCI_EVT:
/* Vendor events are currently unsupported */
if (rxmsg[EVT_HEADER_EVENT] == BT_HCI_EVT_VENDOR) {
switch (rxmsg[EVT_HEADER_EVENT]) {
case BT_HCI_EVT_VENDOR:
/* Vendor events are currently unsupported */
bt_spi_handle_vendor_evt(rxmsg);
continue;
case BT_HCI_EVT_CMD_COMPLETE:
case BT_HCI_EVT_CMD_STATUS:
buf = bt_buf_get_cmd_complete(K_FOREVER);
break;
default:
buf = bt_buf_get_rx(K_FOREVER);
break;
}
buf = bt_buf_get_rx(K_FOREVER);
bt_buf_set_type(buf, BT_BUF_EVT);
net_buf_add_mem(buf, &rxmsg[1],
rxmsg[EVT_HEADER_SIZE] + 2);

View file

@ -52,6 +52,17 @@ enum bt_buf_type {
*/
struct net_buf *bt_buf_get_rx(int32_t timeout);
/** Allocate a buffer for an HCI Command Complete/Status Event
*
* This will set the buffer type so bt_buf_set_type() does not need to
* be explicitly called before bt_recv_prio().
*
* @param timeout Timeout in milliseconds, or one of the special values
* K_NO_WAIT and K_FOREVER.
* @return A new buffer.
*/
struct net_buf *bt_buf_get_cmd_complete(int32_t timeout);
/** Set the buffer type
*
* @param buf Bluetooth buffer

View file

@ -32,16 +32,13 @@ static struct device *hci_uart_dev;
static BT_STACK_NOINIT(tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE);
/* HCI command buffers */
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_RESERVE + BT_HCI_CMD_HDR_SIZE + \
CONFIG_BLUETOOTH_MAX_CMD_LEN)
#define CMD_BUF_SIZE BT_BUF_RX_SIZE
NET_BUF_POOL_DEFINE(cmd_tx_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT, CMD_BUF_SIZE,
BT_BUF_USER_DATA_MIN, NULL);
#define BT_L2CAP_MTU 65 /* 64-byte public key + opcode */
/** Data size needed for ACL buffers */
#define BT_BUF_ACL_SIZE (CONFIG_BLUETOOTH_HCI_RESERVE + BT_HCI_ACL_HDR_SIZE + \
BT_L2CAP_HDR_SIZE + BT_L2CAP_MTU)
#define BT_BUF_ACL_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_MTU)
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS)
#define TX_BUF_COUNT CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS

View file

@ -75,16 +75,13 @@ static struct device *btusb_dev;
static K_FIFO_DEFINE(rx_queue);
/* HCI command buffers */
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_RESERVE + BT_HCI_CMD_HDR_SIZE + \
CONFIG_BLUETOOTH_MAX_CMD_LEN)
#define CMD_BUF_SIZE BT_BUF_RX_SIZE
NET_BUF_POOL_DEFINE(tx_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT, CMD_BUF_SIZE,
sizeof(uint8_t), NULL);
#define BT_L2CAP_MTU 64
/** Data size needed for ACL buffers */
#define BT_BUF_ACL_SIZE (CONFIG_BLUETOOTH_HCI_RESERVE + BT_HCI_ACL_HDR_SIZE + \
BT_L2CAP_HDR_SIZE + BT_L2CAP_MTU)
#define BT_BUF_ACL_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_MTU)
NET_BUF_POOL_DEFINE(acl_tx_pool, 2, BT_BUF_ACL_SIZE, sizeof(uint8_t), NULL);

View file

@ -54,8 +54,7 @@ static void *cmd_complete(struct net_buf **buf, uint8_t plen)
{
struct bt_hci_evt_cmd_complete *cc;
*buf = bt_buf_get_rx(K_FOREVER);
bt_buf_set_type(*buf, BT_BUF_EVT);
*buf = bt_buf_get_cmd_complete(K_FOREVER);
evt_create(*buf, BT_HCI_EVT_CMD_COMPLETE, sizeof(*cc) + plen);
@ -71,8 +70,7 @@ static struct net_buf *cmd_status(uint8_t status)
struct bt_hci_evt_cmd_status *cs;
struct net_buf *buf;
buf = bt_buf_get_rx(K_FOREVER);
bt_buf_set_type(buf, BT_BUF_EVT);
buf = bt_buf_get_cmd_complete(K_FOREVER);
evt_create(buf, BT_HCI_EVT_CMD_STATUS, sizeof(*cs));
cs = net_buf_add(buf, sizeof(*cs));

View file

@ -50,15 +50,6 @@ config BLUETOOTH_HCI_CMD_COUNT
help
Number of buffers available for HCI commands.
config BLUETOOTH_MAX_CMD_LEN
int "Maximum supported HCI command length"
default 64
default 255 if BLUETOOTH_BREDR
range 64 255
range 255 255 if BLUETOOTH_BREDR
help
Maximum length of each HCI command.
config BLUETOOTH_RX_BUF_COUNT
int "Number of HCI RX buffers"
default 10

View file

@ -84,14 +84,14 @@ struct cmd_data {
/** BT_BUF_CMD */
uint8_t type;
/** HCI status of the command completion */
uint8_t status;
/** The command OpCode that the buffer contains */
uint16_t opcode;
/** Used by bt_hci_cmd_send_sync. Initially contains the waiting
* semaphore, as the semaphore is given back contains the bt_buf
* for the return parameters.
*/
void *sync;
/** Used by bt_hci_cmd_send_sync. */
struct k_sem *sync;
};
struct acl_data {
@ -105,10 +105,10 @@ struct acl_data {
#define cmd(buf) ((struct cmd_data *)net_buf_user_data(buf))
#define acl(buf) ((struct acl_data *)net_buf_user_data(buf))
/* HCI command buffers */
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_RESERVE + \
BT_HCI_CMD_HDR_SIZE + \
CONFIG_BLUETOOTH_MAX_CMD_LEN)
/* HCI command buffers. Derive the needed size from BT_BUF_RX_SIZE since
* the same buffer is also used for the response.
*/
#define CMD_BUF_SIZE BT_BUF_RX_SIZE
NET_BUF_POOL_DEFINE(hci_cmd_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT,
CMD_BUF_SIZE, sizeof(struct cmd_data), NULL);
@ -211,30 +211,32 @@ int bt_hci_cmd_send_sync(uint16_t opcode, struct net_buf *buf,
}
}
BT_DBG("opcode 0x%04x len %u", opcode, buf->len);
BT_DBG("buf %p opcode 0x%04x len %u", buf, opcode, buf->len);
k_sem_init(&sync_sem, 0, 1);
cmd(buf)->sync = &sync_sem;
/* Make sure the buffer stays around until the command completes */
net_buf_ref(buf);
net_buf_put(&bt_dev.cmd_tx_queue, buf);
k_sem_take(&sync_sem, K_FOREVER);
/* Indicate failure if we failed to get the return parameters */
if (!cmd(buf)->sync) {
BT_DBG("opcode 0x%04x status 0x%02x", opcode, cmd(buf)->status);
if (cmd(buf)->status) {
err = -EIO;
net_buf_unref(buf);
} else {
err = 0;
if (rsp) {
*rsp = buf;
} else {
net_buf_unref(buf);
}
}
if (rsp) {
*rsp = cmd(buf)->sync;
} else if (cmd(buf)->sync) {
net_buf_unref(cmd(buf)->sync);
}
net_buf_unref(buf);
return err;
}
@ -2222,39 +2224,17 @@ static void hci_reset_complete(struct net_buf *buf)
static void hci_cmd_done(uint16_t opcode, uint8_t status, struct net_buf *buf)
{
struct net_buf *sent;
int key = irq_lock();
BT_DBG("opcode 0x%04x status 0x%02x buf %p", opcode, status, buf);
sent = bt_dev.sent_cmd;
if (!sent) {
irq_unlock(key);
return;
if (cmd(buf)->opcode != opcode) {
BT_WARN("OpCode 0x%04x completed instead of expected 0x%04x",
opcode, cmd(buf)->opcode);
}
if (cmd(sent)->opcode != opcode) {
BT_ERR("Unexpected completion of opcode 0x%04x expected 0x%04x",
opcode, cmd(sent)->opcode);
irq_unlock(key);
return;
}
bt_dev.sent_cmd = NULL;
irq_unlock(key);
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
if (cmd(sent)->sync) {
struct k_sem *sem = cmd(sent)->sync;
if (status) {
cmd(sent)->sync = NULL;
} else {
cmd(sent)->sync = net_buf_ref(buf);
}
k_sem_give(sem);
} else {
net_buf_unref(sent);
if (buf->pool == &hci_cmd_pool && cmd(buf)->sync) {
cmd(buf)->status = status;
k_sem_give(cmd(buf)->sync);
}
}
@ -2262,7 +2242,7 @@ static void hci_cmd_complete(struct net_buf *buf)
{
struct bt_hci_evt_cmd_complete *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
uint8_t status;
uint8_t status, ncmd = evt->ncmd;
BT_DBG("opcode 0x%04x", opcode);
@ -2276,7 +2256,7 @@ static void hci_cmd_complete(struct net_buf *buf)
hci_cmd_done(opcode, status, buf);
/* Allow next command to be sent */
if (evt->ncmd) {
if (ncmd) {
k_sem_give(&bt_dev.ncmd_sem);
}
}
@ -2285,6 +2265,7 @@ static void hci_cmd_status(struct net_buf *buf)
{
struct bt_hci_evt_cmd_status *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
uint8_t ncmd = evt->ncmd;
BT_DBG("opcode 0x%04x", opcode);
@ -2293,7 +2274,7 @@ static void hci_cmd_status(struct net_buf *buf)
hci_cmd_done(opcode, evt->status, buf);
/* Allow next command to be sent */
if (evt->ncmd) {
if (ncmd) {
k_sem_give(&bt_dev.ncmd_sem);
}
}
@ -3981,6 +3962,34 @@ struct net_buf *bt_buf_get_rx(int32_t timeout)
return buf;
}
struct net_buf *bt_buf_get_cmd_complete(int32_t timeout)
{
struct net_buf *buf;
unsigned int key;
key = irq_lock();
buf = bt_dev.sent_cmd;
bt_dev.sent_cmd = NULL;
irq_unlock(key);
BT_DBG("sent_cmd %p", buf);
if (buf) {
bt_buf_set_type(buf, BT_BUF_EVT);
buf->len = 0;
net_buf_reserve(buf, CONFIG_BLUETOOTH_HCI_RESERVE);
return buf;
}
buf = bt_buf_get_rx(timeout);
if (buf) {
bt_buf_set_type(buf, BT_BUF_EVT);
}
return buf;
}
#if defined(CONFIG_BLUETOOTH_BREDR)
static int br_start_inquiry(const struct bt_br_discovery_param *param)
{

View file

@ -64,7 +64,7 @@ static void send_cmd_status(uint16_t opcode, uint8_t status)
BT_DBG("opcode %x status %x", opcode, status);
buf = bt_buf_get_rx(K_FOREVER);
buf = bt_buf_get_cmd_complete(K_FOREVER);
bt_buf_set_type(buf, BT_BUF_EVT);
hdr = net_buf_add(buf, sizeof(*hdr));
@ -166,13 +166,13 @@ static void emulate_le_generate_dhkey(struct net_buf *buf)
return;
}
send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, 0);
cmd = (void *)buf->data + sizeof(struct bt_hci_cmd_hdr);
memcpy(ecc.pk.x, cmd->key, 32);
memcpy(ecc.pk.y, &cmd->key[32], 32);
send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, 0);
net_buf_unref(buf);
if (ecc_valid_public_key(&ecc.pk) < 0) {

View file

@ -50,6 +50,18 @@ struct net_buf *bt_buf_get_rx(int32_t timeout)
return net_buf_alloc(&hci_rx_pool, timeout);
}
struct net_buf *bt_buf_get_cmd_complete(int32_t timeout)
{
struct net_buf *buf;
buf = net_buf_alloc(&hci_rx_pool, timeout);
if (buf) {
bt_buf_set_type(buf, BT_BUF_EVT);
}
return buf;
}
struct net_buf *bt_buf_get_evt(uint8_t opcode, int timeout)
{
struct net_buf *buf;