diff --git a/drivers/bluetooth/hci/h4.c b/drivers/bluetooth/hci/h4.c index a4639c9cd1d..a78f2da55e4 100644 --- a/drivers/bluetooth/hci/h4.c +++ b/drivers/bluetooth/hci/h4.c @@ -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); } diff --git a/drivers/bluetooth/hci/h5.c b/drivers/bluetooth/hci/h5.c index ce37209749d..9d73f8b21a7 100644 --- a/drivers/bluetooth/hci/h5.c +++ b/drivers/bluetooth/hci/h5.c @@ -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) { diff --git a/drivers/bluetooth/hci/spi.c b/drivers/bluetooth/hci/spi.c index 4015b916b71..7031b6b77fa 100644 --- a/drivers/bluetooth/hci/spi.c +++ b/drivers/bluetooth/hci/spi.c @@ -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); diff --git a/include/bluetooth/buf.h b/include/bluetooth/buf.h index ea1b32d030e..899c3a6c816 100644 --- a/include/bluetooth/buf.h +++ b/include/bluetooth/buf.h @@ -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 diff --git a/samples/bluetooth/hci_uart/src/main.c b/samples/bluetooth/hci_uart/src/main.c index 3924fd42db2..35a09e5756d 100644 --- a/samples/bluetooth/hci_uart/src/main.c +++ b/samples/bluetooth/hci_uart/src/main.c @@ -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 diff --git a/samples/bluetooth/hci_usb/src/main.c b/samples/bluetooth/hci_usb/src/main.c index 1a4d51da7dd..35b281a21f0 100644 --- a/samples/bluetooth/hci_usb/src/main.c +++ b/samples/bluetooth/hci_usb/src/main.c @@ -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); diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index 05d18ff2804..ba4b48793d2 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -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)); diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 8194831c099..dc5bbc71b2c 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -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 diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 3caa9124db3..c3a3aa481c2 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -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) { diff --git a/subsys/bluetooth/host/hci_ecc.c b/subsys/bluetooth/host/hci_ecc.c index 4cf3c116e8d..e0debf5732b 100644 --- a/subsys/bluetooth/host/hci_ecc.c +++ b/subsys/bluetooth/host/hci_ecc.c @@ -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) { diff --git a/subsys/bluetooth/host/hci_raw.c b/subsys/bluetooth/host/hci_raw.c index 20b48b706df..45fcb67b336 100644 --- a/subsys/bluetooth/host/hci_raw.c +++ b/subsys/bluetooth/host/hci_raw.c @@ -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;