diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index bb33dfb8047e..4e32b4d04b6d 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -33,7 +33,7 @@ config BLUETOOTH_H4 bool "H:4 UART" select UART_INTERRUPT_DRIVEN select BLUETOOTH_UART - select BLUETOOTH_HOST_BUFFERS + select BLUETOOTH_RECV_IS_RX_THREAD depends on SERIAL help Bluetooth H:4 UART driver. Requires hardware flow control @@ -43,7 +43,6 @@ config BLUETOOTH_H5 bool "H:5 UART [EXPERIMENTAL]" select UART_INTERRUPT_DRIVEN select BLUETOOTH_UART - select BLUETOOTH_HOST_BUFFERS depends on SERIAL help Bluetooth three-wire (H:5) UART driver. Implementation of HCI @@ -59,17 +58,6 @@ endchoice endif # !BLUETOOTH_CONTROLLER -config BLUETOOTH_HOST_BUFFERS - bool "Host managed incoming data buffers" - help - Enable this to have the host stack manage incoming ACL data - and HCI event buffers. This makes sense for all HCI drivers - that talk to a controller running on a different CPU. - - If the controller resides in the same address space it may - make sense to have the lower layers manage these buffers, in - which case this option can be left disabled. - config BLUETOOTH_DEBUG_HCI_DRIVER bool "Bluetooth HCI driver debug" depends on BLUETOOTH_DEBUG diff --git a/drivers/bluetooth/hci/h4.c b/drivers/bluetooth/hci/h4.c index 7bc53a22f398..0a7293b62b9c 100644 --- a/drivers/bluetooth/hci/h4.c +++ b/drivers/bluetooth/hci/h4.c @@ -44,37 +44,150 @@ #include "../nrf51_pm.h" #endif -#define H4_CMD 0x01 -#define H4_ACL 0x02 -#define H4_SCO 0x03 -#define H4_EVT 0x04 +#define H4_NONE 0x00 +#define H4_CMD 0x01 +#define H4_ACL 0x02 +#define H4_SCO 0x03 +#define H4_EVT 0x04 + +static BT_STACK_NOINIT(rx_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE); + +static struct { + struct net_buf *buf; + struct k_fifo fifo; + + uint16_t remaining; + uint16_t discard; + + bool have_hdr; + + uint8_t type; + union { + struct bt_hci_evt_hdr evt; + struct bt_hci_acl_hdr acl; + }; +} rx = { + .fifo = K_FIFO_INITIALIZER(rx.fifo), +}; + +static struct { + uint8_t type; + struct net_buf *buf; + struct k_fifo fifo; +} tx = { + .fifo = K_FIFO_INITIALIZER(tx.fifo), +}; static struct device *h4_dev; -static int h4_read(struct device *uart, uint8_t *buf, - size_t len, size_t min) +static inline void h4_get_type(void) +{ + /* Get packet type */ + if (uart_fifo_read(h4_dev, &rx.type, 1) != 1) { + BT_WARN("Unable to read H:4 packet type"); + rx.type = H4_NONE; + return; + } + + switch (rx.type) { + case H4_EVT: + rx.remaining = sizeof(rx.evt); + break; + case H4_ACL: + rx.remaining = sizeof(rx.acl); + break; + default: + BT_ERR("Unknown H:4 type 0x%02x", rx.type); + rx.type = H4_NONE; + } +} + +static inline void get_acl_hdr(void) { - int total = 0; + struct bt_hci_acl_hdr *hdr = &rx.acl; + int to_read = sizeof(*hdr) - rx.remaining; + + rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read, + rx.remaining); + if (!rx.remaining) { + rx.remaining = sys_le16_to_cpu(hdr->len); + BT_DBG("Got ACL header. Payload %u bytes", rx.remaining); + rx.have_hdr = true; + } +} + +static inline void get_evt_hdr(void) +{ + struct bt_hci_evt_hdr *hdr = &rx.evt; + int to_read = sizeof(*hdr) - rx.remaining; + + rx.remaining -= uart_fifo_read(h4_dev, (uint8_t *)hdr + to_read, + rx.remaining); + if (!rx.remaining) { + rx.remaining = hdr->len; + BT_DBG("Got event header. Payload %u bytes", rx.remaining); + rx.have_hdr = true; + } +} - while (len) { - int rx; - rx = uart_fifo_read(uart, buf, len); - if (rx == 0) { - BT_DBG("Got zero bytes from UART"); - if (total < min) { - continue; +static inline void copy_hdr(struct net_buf *buf) +{ + if (rx.type == H4_EVT) { + net_buf_add_mem(buf, &rx.evt, sizeof(rx.evt)); + BT_DBG("buf %p EVT len %u", buf, sys_le16_to_cpu(rx.evt.len)); + } else { + net_buf_add_mem(buf, &rx.acl, sizeof(rx.acl)); + BT_DBG("buf %p ACL len %u", buf, sys_le16_to_cpu(rx.acl.len)); + } +} + +static void rx_thread(void *p1, void *p2, void *p3) +{ + struct net_buf *buf; + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + BT_DBG("started"); + + while (1) { + BT_DBG("rx.buf %p", rx.buf); + + if (!rx.buf) { + rx.buf = bt_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) { + copy_hdr(rx.buf); } - break; } - BT_DBG("read %d remaining %zu", rx, len - rx); - len -= rx; - total += rx; - buf += rx; - } + /* Let the ISR continue receiving new packets */ + uart_irq_rx_enable(h4_dev); - return total; + buf = net_buf_get(&rx.fifo, K_FOREVER); + do { + uart_irq_rx_enable(h4_dev); + + BT_DBG("Calling bt_recv(%p)", buf); + bt_recv(buf); + + /* Give other threads a chance to run if the ISR + * is receiving data so fast that rx.fifo never + * or very rarely goes empty. + */ + k_yield(); + + uart_irq_rx_disable(h4_dev); + buf = net_buf_get(&rx.fifo, K_NO_WAIT); + } while (buf); + } } static size_t h4_discard(struct device *uart, size_t len) @@ -84,122 +197,176 @@ static size_t h4_discard(struct device *uart, size_t len) return uart_fifo_read(uart, buf, min(len, sizeof(buf))); } -static struct net_buf *h4_evt_recv(int *remaining) +static inline void read_payload(void) { - struct bt_hci_evt_hdr hdr; struct net_buf *buf; + bool prio; + int read; + + if (!rx.buf) { + rx.buf = bt_buf_get_rx(K_NO_WAIT); + if (!rx.buf) { + BT_DBG("Failed to allocate, deferring to rx_thread"); + uart_irq_rx_disable(h4_dev); + return; + } - /* We can ignore the return value since we pass len == min */ - h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr)); + BT_DBG("Allocated rx.buf %p", rx.buf); - *remaining = hdr.len; + 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; + return; + } - buf = bt_buf_get_evt(hdr.evt, K_NO_WAIT); - if (buf) { - memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); - } else { - BT_ERR("No available event buffers!"); + copy_hdr(rx.buf); } - BT_DBG("len %u", hdr.len); + read = uart_fifo_read(h4_dev, net_buf_tail(rx.buf), rx.remaining); + net_buf_add(rx.buf, read); + rx.remaining -= read; - return buf; -} + BT_DBG("got %d bytes, remaining %u", read, rx.remaining); + BT_DBG("Payload (len %u): %s", rx.buf->len, + bt_hex(rx.buf->data, rx.buf->len)); -static struct net_buf *h4_acl_recv(int *remaining) -{ - struct bt_hci_acl_hdr hdr; - struct net_buf *buf; + if (rx.remaining) { + return; + } - /* We can ignore the return value since we pass len == min */ - h4_read(h4_dev, (void *)&hdr, sizeof(hdr), sizeof(hdr)); + prio = (rx.type == H4_EVT && bt_hci_evt_is_prio(rx.evt.evt)); - buf = bt_buf_get_acl(K_NO_WAIT); - if (buf) { - memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); + buf = rx.buf; + rx.buf = NULL; + + if (rx.type == H4_EVT) { + bt_buf_set_type(buf, BT_BUF_EVT); } else { - BT_ERR("No available ACL buffers!"); + bt_buf_set_type(buf, BT_BUF_ACL_IN); } - *remaining = sys_le16_to_cpu(hdr.len); - - BT_DBG("len %u", *remaining); + rx.type = H4_NONE; + rx.have_hdr = false; - return buf; + if (prio) { + BT_DBG("Calling bt_recv_prio(%p)", buf); + bt_recv_prio(buf); + } else { + BT_DBG("Putting buf %p to rx fifo", buf); + net_buf_put(&rx.fifo, buf); + } } -static void bt_uart_isr(struct device *unused) +static inline void read_header(void) { - static struct net_buf *buf; - static int remaining; + switch (rx.type) { + case H4_NONE: + h4_get_type(); + return; + case H4_EVT: + get_evt_hdr(); + break; + case H4_ACL: + get_acl_hdr(); + break; + default: + CODE_UNREACHABLE; + return; + } - ARG_UNUSED(unused); + if (rx.have_hdr && 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 { + copy_hdr(rx.buf); + } + } +} - while (uart_irq_update(h4_dev) && uart_irq_is_pending(h4_dev)) { - int read; +static inline void process_tx(void) +{ + int bytes; + + if (!tx.buf) { + tx.buf = net_buf_get(&tx.fifo, K_NO_WAIT); + if (!tx.buf) { + BT_ERR("TX interrupt but no pending buffer!"); + uart_irq_tx_disable(h4_dev); + return; + } + } - if (!uart_irq_rx_ready(h4_dev)) { - if (uart_irq_tx_ready(h4_dev)) { - BT_DBG("transmit ready"); - } else { - BT_DBG("spurious interrupt"); - } - /* Only the UART RX path is interrupt-enabled */ + if (!tx.type) { + switch (bt_buf_get_type(tx.buf)) { + case BT_BUF_ACL_OUT: + tx.type = H4_ACL; + break; + case BT_BUF_CMD: + tx.type = H4_CMD; break; + default: + BT_ERR("Unknown buffer type"); + goto done; } - /* Beginning of a new packet */ - if (!remaining) { - uint8_t type; - - /* Get packet type */ - read = h4_read(h4_dev, &type, sizeof(type), 0); - if (read != sizeof(type)) { - BT_WARN("Unable to read H4 packet type"); - continue; - } + bytes = uart_fifo_fill(h4_dev, &tx.type, 1); + if (bytes != 1) { + BT_WARN("Unable to send H:4 type"); + tx.type = H4_NONE; + return; + } + } - switch (type) { - case H4_EVT: - buf = h4_evt_recv(&remaining); - break; - case H4_ACL: - buf = h4_acl_recv(&remaining); - break; - default: - BT_ERR("Unknown H4 type %u", type); - return; - } + bytes = uart_fifo_fill(h4_dev, tx.buf->data, tx.buf->len); + net_buf_pull(tx.buf, bytes); - BT_DBG("need to get %u bytes", remaining); + if (tx.buf->len) { + return; + } - if (buf && remaining > net_buf_tailroom(buf)) { - BT_ERR("Not enough space in buffer"); - net_buf_unref(buf); - buf = NULL; - } - } +done: + tx.type = H4_NONE; + net_buf_unref(tx.buf); + tx.buf = net_buf_get(&tx.fifo, K_NO_WAIT); + if (!tx.buf) { + uart_irq_tx_disable(h4_dev); + } +} - if (!buf) { - read = h4_discard(h4_dev, remaining); - BT_WARN("Discarded %d bytes", read); - remaining -= read; - continue; - } +static inline void process_rx(void) +{ + BT_DBG("remaining %u discard %u have_hdr %u rx.buf %p len %u", + rx.remaining, rx.discard, rx.have_hdr, rx.buf, + rx.buf ? rx.buf->len : 0); - read = h4_read(h4_dev, net_buf_tail(buf), remaining, 0); + if (rx.discard) { + rx.discard -= h4_discard(h4_dev, rx.discard); + return; + } - buf->len += read; - remaining -= read; + if (rx.have_hdr) { + read_payload(); + } else { + read_header(); + } +} - BT_DBG("received %d bytes", read); +static void bt_uart_isr(struct device *unused) +{ + ARG_UNUSED(unused); - if (!remaining) { - BT_DBG("full packet received"); + while (uart_irq_update(h4_dev) && uart_irq_is_pending(h4_dev)) { + if (uart_irq_tx_ready(h4_dev)) { + process_tx(); + } - /* Pass buffer to the stack */ - bt_recv(buf); - buf = NULL; + if (uart_irq_rx_ready(h4_dev)) { + process_rx(); } } } @@ -208,22 +375,8 @@ static int h4_send(struct net_buf *buf) { BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len); - switch (bt_buf_get_type(buf)) { - case BT_BUF_ACL_OUT: - uart_poll_out(h4_dev, H4_ACL); - break; - case BT_BUF_CMD: - uart_poll_out(h4_dev, H4_CMD); - break; - default: - return -EINVAL; - } - - while (buf->len) { - uart_poll_out(h4_dev, net_buf_pull_u8(buf)); - } - - net_buf_unref(buf); + net_buf_put(&tx.fifo, buf); + uart_irq_tx_enable(h4_dev); return 0; } @@ -240,12 +393,13 @@ static int h4_open(void) return -EIO; } #else - bt_uart_drain(h4_dev); + h4_discard(h4_dev, 32); #endif uart_irq_callback_set(h4_dev, bt_uart_isr); - uart_irq_rx_enable(h4_dev); + k_thread_spawn(rx_thread_stack, sizeof(rx_thread_stack), rx_thread, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); return 0; } diff --git a/drivers/bluetooth/hci/h5.c b/drivers/bluetooth/hci/h5.c index a53f1eb9c06e..2b1ef83aa6b4 100644 --- a/drivers/bluetooth/hci/h5.c +++ b/drivers/bluetooth/hci/h5.c @@ -475,23 +475,25 @@ static void bt_uart_isr(struct device *unused) switch (H5_HDR_PKT_TYPE(hdr)) { case HCI_EVENT_PKT: - h5.rx_buf = bt_buf_get_evt(0x00, K_NO_WAIT); + 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); h5.rx_state = PAYLOAD; break; case HCI_ACLDATA_PKT: - h5.rx_buf = bt_buf_get_acl(K_NO_WAIT); + h5.rx_buf = bt_buf_get_rx(K_NO_WAIT); if (!h5.rx_buf) { BT_WARN("No available data buffers"); h5_reset_rx(); continue; } + bt_buf_set_type(h5.rx_buf, BT_BUF_ACL_IN); h5.rx_state = PAYLOAD; break; case HCI_3WIRE_LINK_PKT: @@ -518,8 +520,7 @@ static void bt_uart_isr(struct device *unused) continue; } - memcpy(net_buf_add(h5.rx_buf, sizeof(byte)), &byte, - sizeof(byte)); + net_buf_add_mem(h5.rx_buf, &byte, sizeof(byte)); remaining--; if (!remaining) { h5.rx_state = END; diff --git a/drivers/bluetooth/nble/gatt.c b/drivers/bluetooth/nble/gatt.c index ba2113c2b3b5..f3c8e8226f57 100644 --- a/drivers/bluetooth/nble/gatt.c +++ b/drivers/bluetooth/nble/gatt.c @@ -1429,7 +1429,7 @@ static int32_t prep_write_evt(const struct nble_gatts_write_evt *ev, /* Copy data into the outstanding queue */ memcpy(net_buf_user_data(buf), ev, sizeof(*ev)); - memcpy(net_buf_add(buf, len), data, len); + net_buf_add_mem(buf, data, len); k_fifo_put(&queue, buf); diff --git a/drivers/bluetooth/nble/rpc_serialize.c b/drivers/bluetooth/nble/rpc_serialize.c index a5e9fe00f030..b9765ddd178e 100644 --- a/drivers/bluetooth/nble/rpc_serialize.c +++ b/drivers/bluetooth/nble/rpc_serialize.c @@ -226,7 +226,7 @@ static void serialize_struct(struct net_buf *buf, const uint8_t *struct_data, uint8_t struct_length) { net_buf_add_u8(buf, struct_length); - memcpy(net_buf_add(buf, struct_length), struct_data, struct_length); + net_buf_add_mem(buf, struct_data, struct_length); } static uint16_t encoded_buflen(const uint8_t *buf, uint16_t buflen) @@ -260,14 +260,14 @@ static void serialize_buf(struct net_buf *buf, const uint8_t *data, net_buf_add_u8(buf, (varint >> 7)); } - memcpy(net_buf_add(buf, len), data, len); + net_buf_add_mem(buf, data, len); } static void serialize_p(struct net_buf *buf, void *ptr) { uintptr_t val = (uintptr_t)ptr; - memcpy(net_buf_add(buf, sizeof(val)), &val, sizeof(val)); + net_buf_add_mem(buf, &val, sizeof(val)); } void rpc_serialize_none(uint8_t fn_index) diff --git a/include/bluetooth/buf.h b/include/bluetooth/buf.h index 90e5f43531df..c61c5daa0e84 100644 --- a/include/bluetooth/buf.h +++ b/include/bluetooth/buf.h @@ -47,41 +47,21 @@ enum bt_buf_type { /** Minimum amount of user data size for buffers passed to the stack. */ #define BT_BUF_USER_DATA_MIN 4 -/** Data size neeed for HCI event buffers */ -#define BT_BUF_EVT_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \ - sizeof(struct bt_hci_evt_hdr) + \ - CONFIG_BLUETOOTH_MAX_EVT_LEN) +/** Data size neeed for HCI RX buffers */ +#define BT_BUF_RX_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \ + sizeof(struct bt_hci_acl_hdr) + \ + CONFIG_BLUETOOTH_RX_BUF_LEN) -/** Data size needed for incoming ACL buffers */ -#define BT_BUF_ACL_IN_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \ - sizeof(struct bt_hci_acl_hdr) + \ - 4 /* L2CAP header size */ + \ - CONFIG_BLUETOOTH_L2CAP_IN_MTU) - -/** Allocate a buffer for an HCI event - * - * This will set the BT_BUF_EVT buffer type so bt_buf_set_type() - * doesn't need to be explicitly called. Only available when - * CONFIG_BLUETOOTH_HOST_BUFFERS has been selected. - * - * @param opcode HCI event opcode or 0 if not known - * @param timeout Timeout in milliseconds, or one of the special values - * K_NO_WAIT and K_FOREVER. - * @return A new buffer with the BT_BUF_EVT type. - */ -struct net_buf *bt_buf_get_evt(uint8_t opcode, int32_t timeout); - -/** Allocate a buffer for incoming ACL data +/** Allocate a buffer for incoming data * - * This will set the BT_BUF_ACL_IN buffer type so bt_buf_set_type() - * doesn't need to be explicitly called. Only available when - * CONFIG_BLUETOOTH_HOST_BUFFERS has been selected. + * This will not set the buffer type so bt_buf_set_type() needs to be called + * before bt_recv(). * * @param timeout Timeout in milliseconds, or one of the special values * K_NO_WAIT and K_FOREVER. - * @return A new buffer with the BT_BUF_ACL_IN type. + * @return A new buffer. */ -struct net_buf *bt_buf_get_acl(int32_t timeout); +struct net_buf *bt_buf_get_rx(int32_t timeout); /** Set the buffer type * diff --git a/include/bluetooth/hci.h b/include/bluetooth/hci.h index e804a3561429..604722113b54 100644 --- a/include/bluetooth/hci.h +++ b/include/bluetooth/hci.h @@ -104,6 +104,7 @@ static inline bool bt_addr_le_is_identity(const bt_addr_le_t *addr) #define BT_HCI_ERR_CMD_DISALLOWED 0x0c #define BT_HCI_ERR_INSUFFICIENT_RESOURCES 0x0d #define BT_HCI_ERR_UNSUPP_FEATURE_PARAMS_VAL 0x11 +#define BT_HCI_ERR_INVALID_PARAMS 0x12 #define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13 #define BT_HCI_ERR_PAIRING_NOT_ALLOWED 0x18 #define BT_HCI_ERR_UNSUPP_REMOTE_FEATURE 0x1a diff --git a/include/bluetooth/hfp_hf.h b/include/bluetooth/hfp_hf.h index cbdf4ce96fbe..5bfa15135a0c 100644 --- a/include/bluetooth/hfp_hf.h +++ b/include/bluetooth/hfp_hf.h @@ -33,10 +33,81 @@ extern "C" { #endif -/* HFP profile application callback */ +/** @brief HFP profile application callback */ struct bt_hfp_hf_cb { + /** HF connected callback to application + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param conn Connection object. + */ void (*connected)(struct bt_conn *conn); + /** HF disconnected callback to application + * + * If this callback is provided it will be called whenever the + * connection gets disconnected, including when a connection gets + * rejected or cancelled or any error in SLC establisment. + * + * @param conn Connection object. + */ void (*disconnected)(struct bt_conn *conn); + /** HF indicator Callback + * + * This callback provides service indicator value to the application + * + * @param conn Connection object. + * @param value service indicator value received from the AG. + */ + void (*service)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call indicator value to the application + * + * @param conn Connection object. + * @param value call indicator value received from the AG. + */ + void (*call)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call setup indicator value to the application + * + * @param conn Connection object. + * @param value call setup indicator value received from the AG. + */ + void (*call_setup)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call held indicator value to the application + * + * @param conn Connection object. + * @param value call held indicator value received from the AG. + */ + void (*call_held)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides signal indicator value to the application + * + * @param conn Connection object. + * @param value signal indicator value received from the AG. + */ + void (*signal)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides roaming indicator value to the application + * + * @param conn Connection object. + * @param value roaming indicator value received from the AG. + */ + void (*roam)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback battery service indicator value to the application + * + * @param conn Connection object. + * @param value battery indicator value received from the AG. + */ + void (*battery)(struct bt_conn *conn, uint32_t value); }; /** @brief Register HFP HF profile diff --git a/include/bluetooth/rfcomm.h b/include/bluetooth/rfcomm.h index 3279858f0672..057ba147d253 100644 --- a/include/bluetooth/rfcomm.h +++ b/include/bluetooth/rfcomm.h @@ -84,6 +84,9 @@ typedef enum bt_rfcomm_role { /** @brief RFCOMM DLC structure. */ struct bt_rfcomm_dlc { + /* Response Timeout eXpired (RTX) timer */ + struct k_delayed_work rtx_work; + /* Queue for outgoing data */ struct k_fifo tx_queue; @@ -103,7 +106,7 @@ struct bt_rfcomm_dlc { uint8_t rx_credit; /* Stack for TX fiber */ - BT_STACK(stack, 128); + BT_STACK(stack, 256); }; struct bt_rfcomm_server { diff --git a/include/bluetooth/sdp.h b/include/bluetooth/sdp.h index c346778a5cd7..3bb1f1b17b2b 100644 --- a/include/bluetooth/sdp.h +++ b/include/bluetooth/sdp.h @@ -442,9 +442,11 @@ int bt_sdp_register_service(struct bt_sdp_record *service); /** @brief Generic SDP Client Query Result data holder */ struct bt_sdp_client_result { /* buffer containing unparsed SDP record result for given UUID */ - struct net_buf *resp_buf; + struct net_buf *resp_buf; /* flag pointing that there are more result chunks for given UUID */ - bool next_record_hint; + bool next_record_hint; + /* Reference to UUID object on behalf one discovery was started */ + const struct bt_uuid *uuid; }; /** @brief Helper enum to be used as return value of bt_sdp_discover_func_t. diff --git a/include/drivers/bluetooth/hci_driver.h b/include/drivers/bluetooth/hci_driver.h index 6a63d04ad98b..b6e9a8df1ace 100644 --- a/include/drivers/bluetooth/hci_driver.h +++ b/include/drivers/bluetooth/hci_driver.h @@ -27,6 +27,7 @@ * @{ */ +#include #include #include @@ -34,9 +35,37 @@ extern "C" { #endif +/** 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; + } +} + /* Receive data from the controller/HCI driver */ int bt_recv(struct net_buf *buf); +/* Receive priority event from the controller/HCI driver */ +int bt_recv_prio(struct net_buf *buf); + enum bt_hci_driver_bus { BT_HCI_DRIVER_BUS_VIRTUAL = 0, BT_HCI_DRIVER_BUS_USB = 1, @@ -56,7 +85,10 @@ struct bt_hci_driver { /* Bus of the transport (BT_HCI_DRIVER_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); /* Send HCI buffer to controller */ diff --git a/samples/bluetooth/handsfree/src/main.c b/samples/bluetooth/handsfree/src/main.c index 7845cace719b..91e001cde341 100644 --- a/samples/bluetooth/handsfree/src/main.c +++ b/samples/bluetooth/handsfree/src/main.c @@ -38,9 +38,51 @@ static void disconnected(struct bt_conn *conn) printk("HFP HF Disconnected!\n"); } +static void service(struct bt_conn *conn, uint32_t value) +{ + printk("Service indicator value: %u\n", value); +} + +static void call(struct bt_conn *conn, uint32_t value) +{ + printk("Call indicator value: %u\n", value); +} + +static void call_setup(struct bt_conn *conn, uint32_t value) +{ + printk("Call Setup indicator value: %u\n", value); +} + +static void call_held(struct bt_conn *conn, uint32_t value) +{ + printk("Call Held indicator value: %u\n", value); +} + +static void signal(struct bt_conn *conn, uint32_t value) +{ + printk("Signal indicator value: %u\n", value); +} + +static void roam(struct bt_conn *conn, uint32_t value) +{ + printk("Roaming indicator value: %u\n", value); +} + +static void battery(struct bt_conn *conn, uint32_t value) +{ + printk("Battery indicator value: %u\n", value); +} + static struct bt_hfp_hf_cb hf_cb = { .connected = connected, .disconnected = disconnected, + .service = service, + .call = call, + .call_setup = call_setup, + .call_held = call_held, + .signal = signal, + .roam = roam, + .battery = battery, }; static void bt_ready(int err) diff --git a/samples/bluetooth/hci_uart/nrf5.conf b/samples/bluetooth/hci_uart/nrf5.conf index 75c51ffc6966..3ae001b1c1cb 100644 --- a/samples/bluetooth/hci_uart/nrf5.conf +++ b/samples/bluetooth/hci_uart/nrf5.conf @@ -8,9 +8,9 @@ CONFIG_UART_NRF5_BAUD_RATE=1000000 CONFIG_UART_NRF5_FLOW_CONTROL=y CONFIG_MAIN_STACK_SIZE=512 CONFIG_IDLE_STACK_SIZE=256 -CONFIG_ISR_STACK_SIZE=512 +CONFIG_ISR_STACK_SIZE=640 CONFIG_BLUETOOTH=y CONFIG_BLUETOOTH_HCI_RAW=y CONFIG_BLUETOOTH_MAX_CONN=20 CONFIG_BLUETOOTH_CONTROLLER_ASSERT_HANDLER=y -CONFIG_BLUETOOTH_TINYCRYPT_ECC=y +CONFIG_BLUETOOTH_TINYCRYPT_ECC=n diff --git a/samples/bluetooth/hci_uart/src/main.c b/samples/bluetooth/hci_uart/src/main.c index 39d121a16b3f..3f9b2b3ac3f9 100644 --- a/samples/bluetooth/hci_uart/src/main.c +++ b/samples/bluetooth/hci_uart/src/main.c @@ -38,7 +38,7 @@ #include static struct device *hci_uart_dev; -static BT_STACK_NOINIT(tx_thread_stack, CONFIG_BLUETOOTH_HCI_SEND_STACK); +static BT_STACK_NOINIT(tx_thread_stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE); /* HCI command buffers */ #define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + \ @@ -48,7 +48,7 @@ static BT_STACK_NOINIT(tx_thread_stack, CONFIG_BLUETOOTH_HCI_SEND_STACK); 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 64 +#define BT_L2CAP_MTU 65 /* 64-byte public key + opcode */ /** Data size needed for ACL buffers */ #define BT_BUF_ACL_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \ sizeof(struct bt_hci_acl_hdr) + \ @@ -126,7 +126,7 @@ static struct net_buf *h4_cmd_recv(int *remaining) buf = net_buf_alloc(&cmd_tx_pool, K_NO_WAIT); if (buf) { bt_buf_set_type(buf, BT_BUF_CMD); - memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); + net_buf_add_mem(buf, &hdr, sizeof(hdr)); } else { SYS_LOG_ERR("No available command buffers!"); } @@ -147,7 +147,7 @@ static struct net_buf *h4_acl_recv(int *remaining) buf = net_buf_alloc(&acl_tx_pool, K_NO_WAIT); if (buf) { bt_buf_set_type(buf, BT_BUF_ACL_OUT); - memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); + net_buf_add_mem(buf, &hdr, sizeof(hdr)); } else { SYS_LOG_ERR("No available ACL buffers!"); } diff --git a/samples/bluetooth/hci_usb/src/main.c b/samples/bluetooth/hci_usb/src/main.c index 5970dc7a0a45..ed5d02c50a6f 100644 --- a/samples/bluetooth/hci_usb/src/main.c +++ b/samples/bluetooth/hci_usb/src/main.c @@ -459,11 +459,11 @@ static void btusb_bulk_out(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status) read += 4; if (len > read) { - memcpy(net_buf_add(buf, 4), tmp, 4); + net_buf_add_mem(buf, tmp, 4); } else { uint8_t remains = 4 - (read - len); - memcpy(net_buf_add(buf, remains), tmp, remains); + net_buf_add_mem(buf, tmp, remains); } } @@ -621,7 +621,7 @@ static int btusb_class_handler(struct usb_setup_packet *setup, bt_buf_set_type(buf, BT_BUF_CMD); - memcpy(net_buf_add(buf, *len), *data, *len); + net_buf_add_mem(buf, *data, *len); bt_send(buf); diff --git a/samples/bluetooth/ipsp/prj.conf b/samples/bluetooth/ipsp/prj.conf index 75a6b9f2dd45..859a0b1a3bb4 100644 --- a/samples/bluetooth/ipsp/prj.conf +++ b/samples/bluetooth/ipsp/prj.conf @@ -1,6 +1,30 @@ -CONFIG_NETWORKING=y -CONFIG_NETWORKING_WITH_LOGGING=y -CONFIG_NETWORKING_WITH_BT=y -CONFIG_IP_BUF_RX_SIZE=5 -CONFIG_IP_BUF_TX_SIZE=3 +CONFIG_BLUETOOTH=y CONFIG_BLUETOOTH_DEBUG_LOG=y +CONFIG_BLUETOOTH_SMP=y +CONFIG_BLUETOOTH_PERIPHERAL=y +CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_NETWORKING=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=n +CONFIG_NET_UDP=y +CONFIG_NET_TCP=y +CONFIG_NANO_TIMEOUTS=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_LOG=y +CONFIG_NET_L2_BLUETOOTH=y +CONFIG_NET_DEBUG_L2_BLUETOOTH=y +CONFIG_SYS_LOG_SHOW_COLOR=y +CONFIG_INIT_STACKS=y +CONFIG_NET_STATISTICS=y +CONFIG_NET_NBUF_RX_COUNT=14 +CONFIG_NET_NBUF_TX_COUNT=14 +CONFIG_NET_NBUF_DATA_COUNT=30 +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 +CONFIG_NET_MAX_CONTEXTS=10 + +CONFIG_NET_SHELL=y + +CONFIG_NET_SAMPLES_IP_ADDRESSES=y +CONFIG_NET_SAMPLES_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_SAMPLES_PEER_IPV6_ADDR="2001:db8::2" diff --git a/samples/bluetooth/ipsp/src/main.c b/samples/bluetooth/ipsp/src/main.c index 5c6eb369c5cf..f1f4e6ab876a 100644 --- a/samples/bluetooth/ipsp/src/main.c +++ b/samples/bluetooth/ipsp/src/main.c @@ -17,135 +17,347 @@ */ #include +#include +#include +#include -#include -#include +#include +#include #include -#include +#include +#include #include -#define UDP_PORT 4242 +/* admin-local, dynamically allocated multicast address */ +#define MCAST_IP6ADDR { { { 0xff, 0x84, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x2 } } } -/* The 2001:db8::/32 is the private address space for documentation RFC 3849 */ -#define MY_IPADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0x2 } } } +struct in6_addr in6addr_mcast = MCAST_IP6ADDR; -/* admin-local, dynamically allocated multicast address */ -#define MCAST_IPADDR { { { 0xff, 0x84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0x2 } } } +/* Define my IP address where to expect messages */ +#define MY_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x1 } } } +#define MY_PREFIX_LEN 64 -static inline void reverse(unsigned char *buf, int len) -{ - int i, last = len - 1; +static struct in6_addr in6addr_my = MY_IP6ADDR; - for (i = 0; i < len / 2; i++) { - unsigned char tmp = buf[i]; +#define MY_PORT 4242 - buf[i] = buf[last - i]; - buf[last - i] = tmp; - } +#define STACKSIZE 2000 +char __noinit __stack thread_stack[STACKSIZE]; + +#define MAX_DBG_PRINT 64 + +static struct k_sem quit_lock; + +static inline void quit(void) +{ + k_sem_give(&quit_lock); } -static inline struct net_buf *prepare_reply(const char *type, - struct net_buf *buf) +static inline void init_app(void) { - printk("%s: received %d bytes\n", type, ip_buf_appdatalen(buf)); + printk("Run IPSP sample"); - /* In this test we reverse the received bytes. - * We could just pass the data back as is but - * this way it is possible to see how the app - * can manipulate the received data. - */ - reverse(ip_buf_appdata(buf), ip_buf_appdatalen(buf)); + k_sem_init(&quit_lock, 0, UINT_MAX); - return buf; + if (net_addr_pton(AF_INET6, + CONFIG_NET_SAMPLES_MY_IPV6_ADDR, + (struct sockaddr *)&in6addr_my) < 0) { + printk("Invalid IPv6 address %s", + CONFIG_NET_SAMPLES_MY_IPV6_ADDR); + } + + do { + struct net_if_addr *ifaddr; + + ifaddr = net_if_ipv6_addr_add(net_if_get_default(), + &in6addr_my, NET_ADDR_MANUAL, 0); + } while (0); + + net_if_ipv6_maddr_add(net_if_get_default(), &in6addr_mcast); } -static inline bool get_context(struct net_context **recv, - struct net_context **mcast_recv) +static inline bool get_context(struct net_context **udp_recv6, + struct net_context **tcp_recv6, + struct net_context **mcast_recv6) { - static struct net_addr mcast_addr; - static struct net_addr any_addr; - static struct net_addr my_addr; - static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; - static const struct in6_addr in6addr_mcast = MCAST_IPADDR; - static struct in6_addr in6addr_my = MY_IPADDR; - - mcast_addr.in6_addr = in6addr_mcast; - mcast_addr.family = AF_INET6; - - any_addr.in6_addr = in6addr_any; - any_addr.family = AF_INET6; - - my_addr.in6_addr = in6addr_my; - my_addr.family = AF_INET6; - - *recv = net_context_get(IPPROTO_UDP, - &any_addr, 0, - &my_addr, UDP_PORT); - if (!*recv) { - printk("%s: Cannot get network context\n", __func__); - return NULL; - } - - *mcast_recv = net_context_get(IPPROTO_UDP, - &any_addr, 0, - &mcast_addr, UDP_PORT); - if (!*mcast_recv) { - printk("%s: Cannot get receiving mcast network context\n", - __func__); + int ret; + struct sockaddr_in6 mcast_addr6 = { 0 }; + struct sockaddr_in6 my_addr6 = { 0 }; + + net_ipaddr_copy(&mcast_addr6.sin6_addr, &in6addr_mcast); + mcast_addr6.sin6_family = AF_INET6; + + my_addr6.sin6_family = AF_INET6; + my_addr6.sin6_port = htons(MY_PORT); + + ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, udp_recv6); + if (ret < 0) { + printk("Cannot get network context for IPv6 UDP (%d)", + ret); + return false; + } + + ret = net_context_bind(*udp_recv6, (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + printk("Cannot bind IPv6 UDP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } + + ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, mcast_recv6); + if (ret < 0) { + printk("Cannot get receiving IPv6 mcast " + "network context (%d)", ret); + return false; + } + + ret = net_context_bind(*mcast_recv6, (struct sockaddr *)&mcast_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + printk("Cannot bind IPv6 mcast (%d)", ret); + return false; + } + + ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, tcp_recv6); + if (ret < 0) { + printk("Cannot get network context for IPv6 TCP (%d)", ret); + return false; + } + + ret = net_context_bind(*tcp_recv6, (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + printk("Cannot bind IPv6 TCP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } + + ret = net_context_listen(*tcp_recv6, 0); + if (ret < 0) { + printk("Cannot listen IPv6 TCP (%d)", ret); return false; } return true; } -static inline void receive_and_reply(struct net_context *recv, - struct net_context *mcast_recv) +static struct net_buf *build_reply_buf(const char *name, + struct net_context *context, + struct net_buf *buf) { - struct net_buf *buf; + struct net_buf *reply_buf, *frag, *tmp; + int header_len, recv_len, reply_len; + + printk("%s received %d bytes", name, + net_nbuf_appdatalen(buf)); + + reply_buf = net_nbuf_get_tx(context); - buf = net_receive(recv, K_FOREVER); - if (buf) { - prepare_reply("unicast ", buf); + recv_len = net_buf_frags_len(buf->frags); + + tmp = buf->frags; + + /* First fragment will contain IP header so move the data + * down in order to get rid of it. + */ + header_len = net_nbuf_appdata(buf) - tmp->data; - if (net_reply(recv, buf)) { - ip_buf_unref(buf); + /* After this pull, the tmp->data points directly to application + * data. + */ + net_buf_pull(tmp, header_len); + + while (tmp) { + frag = net_nbuf_get_data(context); + + if (!net_buf_headroom(tmp)) { + /* If there is no link layer headers in the + * received fragment, then get rid of that also + * in the sending fragment. We end up here + * if MTU is larger than fragment size, this + * is typical for ethernet. + */ + net_buf_push(frag, net_buf_headroom(frag)); + + frag->len = 0; /* to make fragment empty */ + + /* Make sure to set the reserve so that + * in sending side we add the link layer + * header if needed. + */ + net_nbuf_set_ll_reserve(reply_buf, 0); } - return; + + net_buf_add_mem(frag, tmp->data, tmp->len); + + net_buf_frag_add(reply_buf, frag); + + net_buf_frag_del(buf, tmp); + + tmp = buf->frags; } - buf = net_receive(mcast_recv, K_FOREVER); - if (buf) { - prepare_reply("multicast ", buf); + reply_len = net_buf_frags_len(reply_buf->frags); - if (net_reply(mcast_recv, buf)) { - ip_buf_unref(buf); - } - return; + printk("Received %d bytes, sending %d bytes", recv_len - header_len, + reply_len); + + return reply_buf; +} + +static inline void pkt_sent(struct net_context *context, + int status, + void *token, + void *user_data) +{ + if (!status) { + printk("Sent %d bytes", POINTER_TO_UINT(token)); + } +} + +static inline void set_dst_addr(sa_family_t family, + struct net_buf *buf, + struct sockaddr *dst_addr) +{ + net_ipaddr_copy(&net_sin6(dst_addr)->sin6_addr, + &NET_IPV6_BUF(buf)->src); + net_sin6(dst_addr)->sin6_family = AF_INET6; + net_sin6(dst_addr)->sin6_port = NET_UDP_BUF(buf)->src_port; +} + +static void udp_received(struct net_context *context, + struct net_buf *buf, + int status, + void *user_data) +{ + struct net_buf *reply_buf; + struct sockaddr dst_addr; + sa_family_t family = net_nbuf_family(buf); + static char dbg[MAX_DBG_PRINT + 1]; + int ret; + + snprintf(dbg, MAX_DBG_PRINT, "UDP IPv%c", + family == AF_INET6 ? '6' : '4'); + + set_dst_addr(family, buf, &dst_addr); + + reply_buf = build_reply_buf(dbg, context, buf); + + net_nbuf_unref(buf); + + ret = net_context_sendto(reply_buf, &dst_addr, + family == AF_INET6 ? + sizeof(struct sockaddr_in6) : + sizeof(struct sockaddr_in), + pkt_sent, 0, + UINT_TO_POINTER(net_buf_frags_len(reply_buf)), + user_data); + if (ret < 0) { + printk("Cannot send data to peer (%d)", ret); + net_nbuf_unref(reply_buf); + } +} + +static void setup_udp_recv(struct net_context *udp_recv6) +{ + int ret; + + ret = net_context_recv(udp_recv6, udp_received, 0, NULL); + if (ret < 0) { + printk("Cannot receive IPv6 UDP packets"); + } +} + +static void tcp_received(struct net_context *context, + struct net_buf *buf, + int status, + void *user_data) +{ + static char dbg[MAX_DBG_PRINT + 1]; + sa_family_t family = net_nbuf_family(buf); + struct net_buf *reply_buf; + int ret; + + snprintf(dbg, MAX_DBG_PRINT, "TCP IPv%c", + family == AF_INET6 ? '6' : '4'); + + reply_buf = build_reply_buf(dbg, context, buf); + + net_buf_unref(buf); + + ret = net_context_send(reply_buf, pkt_sent, K_NO_WAIT, + UINT_TO_POINTER(net_buf_frags_len(reply_buf)), + NULL); + if (ret < 0) { + printk("Cannot send data to peer (%d)", ret); + net_nbuf_unref(reply_buf); + + quit(); + } +} + +static void tcp_accepted(struct net_context *context, + struct sockaddr *addr, + socklen_t addrlen, + int error, + void *user_data) +{ + int ret; + + NET_DBG("Accept called, context %p error %d", context, error); + + ret = net_context_recv(context, tcp_received, 0, NULL); + if (ret < 0) { + printk("Cannot receive TCP packet (family %d)", + net_context_get_family(context)); + } +} + +static void setup_tcp_accept(struct net_context *tcp_recv6) +{ + int ret; + + ret = net_context_accept(tcp_recv6, tcp_accepted, 0, NULL); + if (ret < 0) { + printk("Cannot receive IPv6 TCP packets (%d)", ret); } } static void listen(void) { - static struct net_context *recv; - static struct net_context *mcast_recv; + struct net_context *udp_recv6 = { 0 }; + struct net_context *tcp_recv6 = { 0 }; + struct net_context *mcast_recv6 = { 0 }; - if (!get_context(&recv, &mcast_recv)) { - printk("%s: Cannot get network contexts\n", __func__); + if (!get_context(&udp_recv6, &tcp_recv6, &mcast_recv6)) { + printk("Cannot get network contexts"); return; } - while (1) { - receive_and_reply(recv, mcast_recv); - } + printk("Starting to wait"); + + setup_tcp_accept(tcp_recv6); + setup_udp_recv(udp_recv6); + + k_sem_take(&quit_lock, K_FOREVER); + + printk("Stopping..."); + + net_context_put(udp_recv6); + net_context_put(mcast_recv6); + net_context_put(tcp_recv6); } void main(void) { int err; + init_app(); + err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); @@ -154,8 +366,6 @@ void main(void) printk("Bluetooth initialized\n"); - net_init(); - ipss_init(); err = ipss_advertise(); @@ -166,5 +376,7 @@ void main(void) printk("Advertising successfully started\n"); - listen(); + k_thread_spawn(&thread_stack[0], STACKSIZE, + (k_thread_entry_t)listen, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, 0); } diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index ba83e85c3717..5c45b8531f9a 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -47,7 +47,7 @@ if BLUETOOTH_HCI config BLUETOOTH_CONTROLLER bool "Bluetooth Controller" - select BLUETOOTH_HOST_BUFFERS + select BLUETOOTH_RECV_IS_RX_THREAD help Enables support for SoC native controller implementation. diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index 9e1438db9b18..4728ddb27c65 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -2,14 +2,6 @@ if BLUETOOTH_CONTROLLER comment "BLE Controller configuration" -config BLUETOOTH_CONTROLLER_DATA_LENGTH - prompt "Maximum data length supported" - int - default 27 - range 27 251 - help - Set the maximum data length of PDU supported in the stack. - config BLUETOOTH_CONTROLLER_RX_BUFFERS prompt "Number of Rx buffers" int @@ -29,14 +21,66 @@ config BLUETOOTH_CONTROLLER_TX_BUFFERS Set the number of Tx PDUs to be queued for transmission in the controller. -config BLUETOOTH_CONTROLLER_RX_STACK_SIZE - int "Size of the receiving thread stack" - default 256 - range 256 65536 +config BLUETOOTH_CONTROLLER_TX_BUFFER_SIZE + prompt "Tx buffer size" + int + range 27 16384 + default 27 help - Size of the receiving thread stack. This is the context from - which all radio messages are encoded into HCI events or data - before passing it to Bluetooth receiving thread. + Size of the Tx buffers and the value returned in HCI LE Read Buffer + Size command response. If this size if greater than effective PDU size + then controller will perform fragmentation before transmitting on the + the packet on air. + Maximum is set to 16384 due to implementation limitations (use of + uint16_t for size/length variables). + +config BLUETOOTH_CONTROLLER_RX_PRIO_STACK_SIZE + int + default 320 + +comment "BLE Controller features" + +config BLUETOOTH_CONTROLLER_LE_PING + bool "LE Ping" + default y + help + Enable support for Bluetooth v4.1 LE Ping feature in the Controller. + +config BLUETOOTH_CONTROLLER_DATA_LENGTH + bool "Data Length Update" + default y + help + Enable support for Bluetooth v4.2 LE Data Length Update procedure in + the Controller. + +config BLUETOOTH_CONTROLLER_DATA_LENGTH_MAX + prompt "Maximum data length supported" + depends on BLUETOOTH_CONTROLLER_DATA_LENGTH + int + default 27 + range 27 251 + help + Set the maximum data length of PDU supported in the Controller. + +config BLUETOOTH_CONTROLLER_FAST_ENC + bool "Fast Encryption Setup" + help + Enable connection encryption setup in 3 connection intervals. + Peripheral will respond to Encryption Request with Encryption Response + in the same connection interval, and also, will respond with Start + Encryption Response PDU in the 3rd connection interval, hence + completing encryption setup in 3 connection intervals. Encrypted data + would be transmitted as fast as in 3rd connection interval from the + connection establishment. + Maximum CPU time in Radio ISR will increase if this feature is + selected. + +config BLUETOOTH_CONTROLLER_CONN_RSSI + bool "Connection RSSI" + help + Enable connection RSSI measurement. + +comment "BLE Controller debug configuration" config BLUETOOTH_CONTROLLER_ASSERT_HANDLER bool "Bluetooth Controller Assertion Handler" @@ -48,5 +92,12 @@ config BLUETOOTH_CONTROLLER_ASSERT_HANDLER and will be invoked whenever the controller code encounters an unrecoverable error. +config BLUETOOTH_CONTROLLER_PROFILE_ISR + bool "Profile radio ISR" + help + Turn on measurement of radio ISR latency, CPU usage and generation of + controller event with these profiling data. The controller event + contains current, minimum and maximum ISR entry latencies; and + current, minimum and maximum ISR CPU use in micro-seconds. endif # BLUETOOTH_CONTROLLER diff --git a/subsys/bluetooth/controller/Makefile b/subsys/bluetooth/controller/Makefile index 98f4575fb96c..5910cf0f0dfa 100644 --- a/subsys/bluetooth/controller/Makefile +++ b/subsys/bluetooth/controller/Makefile @@ -1,15 +1,16 @@ ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/subsys/bluetooth/controller/util ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/subsys/bluetooth/controller/hal +ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/subsys/bluetooth/controller/ticker ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/subsys/bluetooth/controller/ll obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/mem.o obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/memq.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/work.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/mayfly.o obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/util.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rtc.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rand.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/ecb.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/radio.o -obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ticker.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/nrf5/cntr.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/nrf5/rand.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/nrf5/ecb.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/nrf5/radio.o +obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ticker/ticker.o obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ctrl.o obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ll.o obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hci/hci_driver.o diff --git a/subsys/bluetooth/controller/hal/ccm.h b/subsys/bluetooth/controller/hal/ccm.h index f22bcec95006..3c325ec50612 100644 --- a/subsys/bluetooth/controller/hal/ccm.h +++ b/subsys/bluetooth/controller/hal/ccm.h @@ -18,8 +18,6 @@ #ifndef _CCM_H_ #define _CCM_H_ -#include - struct ccm { uint8_t key[16]; uint64_t counter; diff --git a/subsys/bluetooth/controller/hal/hal_rtc.h b/subsys/bluetooth/controller/hal/cntr.h similarity index 75% rename from subsys/bluetooth/controller/hal/hal_rtc.h rename to subsys/bluetooth/controller/hal/cntr.h index 1cb200bfcffe..a3fe7b1f98fc 100644 --- a/subsys/bluetooth/controller/hal/hal_rtc.h +++ b/subsys/bluetooth/controller/hal/cntr.h @@ -15,13 +15,13 @@ * limitations under the License. */ -#ifndef _RTC_H_ -#define _RTC_H_ +#ifndef _CNTR_H_ +#define _CNTR_H_ -void rtc_init(void); -uint32_t rtc_start(void); -uint32_t rtc_stop(void); -uint32_t rtc_tick_get(void); -uint32_t rtc_compare_set(uint8_t instance, uint32_t value); +void cntr_init(void); +uint32_t cntr_start(void); +uint32_t cntr_stop(void); +uint32_t cntr_cnt_get(void); +void cntr_cmp_set(uint8_t cmp, uint32_t value); -#endif /* _RTC_H_ */ +#endif /* _CNTR_H_ */ diff --git a/subsys/bluetooth/controller/hal/cpu.h b/subsys/bluetooth/controller/hal/cpu.h index e8bd14e1cbb0..7a54c85051c5 100644 --- a/subsys/bluetooth/controller/hal/cpu.h +++ b/subsys/bluetooth/controller/hal/cpu.h @@ -18,8 +18,6 @@ #ifndef _CPU_H_ #define _CPU_H_ -#include "nrf.h" - static inline void cpu_sleep(void) { __WFE(); diff --git a/subsys/bluetooth/controller/hal/debug.h b/subsys/bluetooth/controller/hal/debug.h index 0d6b31905147..2b5f6bb4f77a 100644 --- a/subsys/bluetooth/controller/hal/debug.h +++ b/subsys/bluetooth/controller/hal/debug.h @@ -18,8 +18,6 @@ #ifndef _DEBUG_H_ #define _DEBUG_H_ -#include - #ifdef CONFIG_BLUETOOTH_CONTROLLER_ASSERT_HANDLER void bt_controller_assert_handle(char *file, uint32_t line); #define LL_ASSERT(cond) if (!(cond)) { \ diff --git a/subsys/bluetooth/controller/hal/ecb.h b/subsys/bluetooth/controller/hal/ecb.h index 9dca22674ab4..77d2934ad4e7 100644 --- a/subsys/bluetooth/controller/hal/ecb.h +++ b/subsys/bluetooth/controller/hal/ecb.h @@ -38,7 +38,7 @@ void ecb_encrypt(uint8_t const *const key_le, uint8_t * const cipher_text_le, uint8_t * const cipher_text_be); uint32_t ecb_encrypt_nonblocking(struct ecb *ecb); -void ecb_isr(void); +void isr_ecb(void *param); uint32_t ecb_ut(void); diff --git a/subsys/bluetooth/controller/hal/hal_work.h b/subsys/bluetooth/controller/hal/hal_work.h deleted file mode 100644 index b57c9ce15c87..000000000000 --- a/subsys/bluetooth/controller/hal/hal_work.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2016 Nordic Semiconductor ASA - * Copyright (c) 2016 Vinayak Kariappa Chettimada - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _HAL_WORK_H_ -#define _HAL_WORK_H_ - -#include "nrf.h" - -#define WORK_TICKER_WORKER0_IRQ RTC0_IRQn -#define WORK_TICKER_JOB0_IRQ SWI4_IRQn -#define WORK_TICKER_WORKER1_IRQ SWI5_IRQn -#define WORK_TICKER_JOB1_IRQ SWI5_IRQn - -#define WORK_TICKER_WORKER0_IRQ_PRIORITY 0 -#define WORK_TICKER_JOB0_IRQ_PRIORITY 0 -#define WORK_TICKER_WORKER1_IRQ_PRIORITY 2 -#define WORK_TICKER_JOB1_IRQ_PRIORITY 2 - -#endif /* _HAL_WORK_H_ */ diff --git a/subsys/bluetooth/controller/hal/rtc.c b/subsys/bluetooth/controller/hal/nrf5/cntr.c similarity index 63% rename from subsys/bluetooth/controller/hal/rtc.c rename to subsys/bluetooth/controller/hal/nrf5/cntr.c index ac9dbf06da95..5687ad52a61b 100644 --- a/subsys/bluetooth/controller/hal/rtc.c +++ b/subsys/bluetooth/controller/hal/nrf5/cntr.c @@ -15,27 +15,30 @@ * limitations under the License. */ -#include "nrf.h" +#include +#include "cntr.h" +#include #include "debug.h" -/* RTC instance to use */ +#ifndef NRF_RTC #define NRF_RTC NRF_RTC0 +#endif -static uint8_t _rtc_refcount; +static uint8_t _refcount; -void rtc_init(void) +void cntr_init(void) { NRF_RTC->PRESCALER = 0; - NRF_RTC->EVTENSET = - (RTC_EVTENSET_COMPARE0_Msk | RTC_EVTENSET_COMPARE1_Msk); - NRF_RTC->INTENSET = - (RTC_INTENSET_COMPARE0_Msk | RTC_INTENSET_COMPARE1_Msk); + NRF_RTC->EVTENSET = (RTC_EVTENSET_COMPARE0_Msk | + RTC_EVTENSET_COMPARE1_Msk); + NRF_RTC->INTENSET = (RTC_INTENSET_COMPARE0_Msk | + RTC_INTENSET_COMPARE1_Msk); } -uint32_t rtc_start(void) +uint32_t cntr_start(void) { - if (_rtc_refcount++) { + if (_refcount++) { return 1; } @@ -44,11 +47,11 @@ uint32_t rtc_start(void) return 0; } -uint32_t rtc_stop(void) +uint32_t cntr_stop(void) { - LL_ASSERT(_rtc_refcount); + LL_ASSERT(_refcount); - if (--_rtc_refcount) { + if (--_refcount) { return 1; } @@ -57,12 +60,12 @@ uint32_t rtc_stop(void) return 0; } -uint32_t rtc_tick_get(void) +uint32_t cntr_cnt_get(void) { return NRF_RTC->COUNTER; } -void rtc_compare_set(uint8_t instance, uint32_t value) +void cntr_cmp_set(uint8_t cmp, uint32_t value) { - NRF_RTC->CC[instance] = value; + NRF_RTC->CC[cmp] = value; } diff --git a/subsys/bluetooth/controller/hal/ecb.c b/subsys/bluetooth/controller/hal/nrf5/ecb.c similarity index 98% rename from subsys/bluetooth/controller/hal/ecb.c rename to subsys/bluetooth/controller/hal/nrf5/ecb.c index 1dcbc54c2d3a..5425869ce2c6 100644 --- a/subsys/bluetooth/controller/hal/ecb.c +++ b/subsys/bluetooth/controller/hal/nrf5/ecb.c @@ -19,8 +19,10 @@ #include #include "mem.h" + #include "ecb.h" +#include #include "debug.h" void ecb_encrypt(uint8_t const *const key_le, @@ -102,8 +104,10 @@ static void ecb_cleanup(void) irq_disable(ECB_IRQn); } -void ecb_isr(void) +void isr_ecb(void *param) { + ARG_UNUSED(param); + if (NRF_ECB->EVENTS_ERRORECB) { struct ecb *ecb = (struct ecb *)NRF_ECB->ECBDATAPTR; diff --git a/subsys/bluetooth/controller/hal/radio.c b/subsys/bluetooth/controller/hal/nrf5/radio.c similarity index 97% rename from subsys/bluetooth/controller/hal/radio.c rename to subsys/bluetooth/controller/hal/nrf5/radio.c index bffffa8f5208..fc00d4047cf7 100644 --- a/subsys/bluetooth/controller/hal/radio.c +++ b/subsys/bluetooth/controller/hal/nrf5/radio.c @@ -15,11 +15,10 @@ * limitations under the License. */ #include -#include -#include "hal_work.h" - -#include "defines.h" +#include "util.h" +#include "mem.h" +#include "pdu.h" #include "ccm.h" #include "radio.h" @@ -33,8 +32,10 @@ static radio_isr_fp sfp_radio_isr; -void radio_isr(void) +void isr_radio(void *param) { + ARG_UNUSED(param); + if (sfp_radio_isr) { sfp_radio_isr(); } @@ -215,8 +216,8 @@ uint32_t radio_crc_is_valid(void) return NRF_RADIO->CRCSTATUS; } -static uint8_t ALIGNED(4) _pkt_empty[RADIO_EMPDU_SIZE_MAX]; -static uint8_t ALIGNED(4) _pkt_scratch[ +static uint8_t MALIGN(4) _pkt_empty[RADIO_EMPDU_SIZE_MAX]; +static uint8_t MALIGN(4) _pkt_scratch[ ((RADIO_PDU_LEN_MAX + 3) > RADIO_ACPDU_SIZE_MAX) ? (RADIO_PDU_LEN_MAX + 3) : RADIO_ACPDU_SIZE_MAX]; @@ -431,7 +432,7 @@ uint32_t radio_tmr_sample_get(void) return NRF_TIMER0->CC[3]; } -static uint8_t ALIGNED(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16]; +static uint8_t MALIGN(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16]; void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt) { @@ -527,7 +528,7 @@ uint32_t radio_ccm_mic_is_valid(void) return NRF_CCM->MICSTATUS; } -static uint8_t ALIGNED(4) _aar_scratch[3]; +static uint8_t MALIGN(4) _aar_scratch[3]; void radio_ar_configure(uint32_t nirk, void *irk) { diff --git a/subsys/bluetooth/controller/hal/rand.c b/subsys/bluetooth/controller/hal/nrf5/rand.c similarity index 72% rename from subsys/bluetooth/controller/hal/rand.c rename to subsys/bluetooth/controller/hal/nrf5/rand.c index 467e69a48ca8..510f267e8e05 100644 --- a/subsys/bluetooth/controller/hal/rand.c +++ b/subsys/bluetooth/controller/hal/nrf5/rand.c @@ -19,6 +19,7 @@ #include "rand.h" +#include #include "debug.h" #define RAND_RESERVED (4) @@ -30,19 +31,20 @@ struct rand { uint8_t rand[1]; }; -static struct rand *_rand; +static struct rand *rng; void rand_init(uint8_t *context, uint8_t context_len) { LL_ASSERT(context_len > sizeof(struct rand)); - _rand = (struct rand *)context; - _rand->count = context_len - sizeof(struct rand) + 1; - _rand->first = _rand->last = 0; + rng = (struct rand *)context; + rng->count = context_len - sizeof(struct rand) + 1; + rng->first = rng->last = 0; NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Msk; NRF_RNG->EVENTS_VALRDY = 0; NRF_RNG->INTENSET = RNG_INTENSET_VALRDY_Msk; + NRF_RNG->TASKS_START = 1; } @@ -52,30 +54,30 @@ uint32_t rand_get(uint8_t octets, uint8_t *rand) uint8_t first; while (octets) { - if (_rand->first == _rand->last) { + if (rng->first == rng->last) { break; } - rand[--octets] = _rand->rand[_rand->first]; + rand[--octets] = rng->rand[rng->first]; - first = _rand->first + 1; - if (first == _rand->count) { + first = rng->first + 1; + if (first == rng->count) { first = 0; } - _rand->first = first; + rng->first = first; } reserved = RAND_RESERVED; - first = _rand->first; + first = rng->first; while (reserved--) { - if (first == _rand->last) { + if (first == rng->last) { NRF_RNG->TASKS_START = 1; break; } first++; - if (first == _rand->count) { + if (first == rng->count) { first = 0; } } @@ -83,17 +85,19 @@ uint32_t rand_get(uint8_t octets, uint8_t *rand) return octets; } -void rng_isr(void) +void isr_rand(void *param) { + ARG_UNUSED(param); + if (NRF_RNG->EVENTS_VALRDY) { uint8_t last; - last = _rand->last + 1; - if (last == _rand->count) { + last = rng->last + 1; + if (last == rng->count) { last = 0; } - if (last == _rand->first) { + if (last == rng->first) { /* this condition should not happen * , but due to probable bug in HW * , new value could be generated @@ -105,17 +109,17 @@ void rng_isr(void) return; } - _rand->rand[_rand->last] = NRF_RNG->VALUE; - _rand->last = last; + rng->rand[rng->last] = NRF_RNG->VALUE; + rng->last = last; - last = _rand->last + 1; - if (last == _rand->count) { + last = rng->last + 1; + if (last == rng->count) { last = 0; } NRF_RNG->EVENTS_VALRDY = 0; - if (last == _rand->first) { + if (last == rng->first) { NRF_RNG->TASKS_STOP = 1; } } diff --git a/subsys/bluetooth/controller/hal/radio.h b/subsys/bluetooth/controller/hal/radio.h index 2ce8d4220d27..83ef54e097dd 100644 --- a/subsys/bluetooth/controller/hal/radio.h +++ b/subsys/bluetooth/controller/hal/radio.h @@ -37,7 +37,7 @@ typedef void (*radio_isr_fp) (void); -void radio_isr(void); +void isr_radio(void *param); void radio_isr_set(radio_isr_fp fp_radio_isr); void radio_reset(void); diff --git a/subsys/bluetooth/controller/hal/rand.h b/subsys/bluetooth/controller/hal/rand.h index 4514298fc477..0d7be225a783 100644 --- a/subsys/bluetooth/controller/hal/rand.h +++ b/subsys/bluetooth/controller/hal/rand.h @@ -20,6 +20,6 @@ void rand_init(uint8_t *context, uint8_t context_len); uint32_t rand_get(uint8_t octets, uint8_t *rand); -void rng_isr(void); +void isr_rand(void *param); #endif /* _RAND_H_ */ diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index 622aa519914b..9cef1889716d 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -18,18 +18,20 @@ #include #include #include + +#include #include #include -#include #include #include #include +#include -#include "defines.h" -#include "ticker.h" +#include "util.h" #include "mem.h" -#include "rand.h" +#include "ticker.h" #include "cpu.h" +#include "rand.h" #include "ecb.h" #include "ccm.h" #include "radio.h" @@ -38,6 +40,7 @@ #include "ll.h" #include "hci_internal.h" +#include #include "debug.h" /* opcode of the HCI command currently being processed. The opcode is stored @@ -212,18 +215,25 @@ static void read_supported_commands(struct net_buf *buf, struct net_buf *evt) * LE Read Supported States. */ rp->commands[28] = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3); - /* LE Remote Conn Param Req and Neg Reply, LE Set Data Length, - * and LE Read Suggested Data Length. - */ - rp->commands[33] = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); + /* LE Remote Conn Param Req and Neg Reply */ + rp->commands[33] = (1 << 4) | (1 << 5); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) + /* LE Set Data Length, and LE Read Suggested Data Length. */ + rp->commands[33] |= (1 << 6) | (1 << 7); /* LE Write Suggested Data Length. */ rp->commands[34] = (1 << 0); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + #if defined(CONFIG_BLUETOOTH_HCI_RAW) && defined(CONFIG_BLUETOOTH_TINYCRYPT_ECC) /* LE Read Local P256 Public Key and LE Generate DH Key*/ rp->commands[34] |= (1 << 1) | (1 << 2); #endif + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* LE Read Maximum Data Length. */ rp->commands[35] = (1 << 3); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ } static void read_local_features(struct net_buf *buf, struct net_buf *evt) @@ -293,7 +303,7 @@ static void le_read_buffer_size(struct net_buf *buf, struct net_buf *evt) rp->status = 0x00; - rp->le_max_len = sys_cpu_to_le16(RADIO_LL_LENGTH_OCTETS_RX_MAX); + rp->le_max_len = sys_cpu_to_le16(RADIO_PACKET_TX_DATA_SIZE); rp->le_max_num = RADIO_PACKET_COUNT_TX_MAX; } @@ -660,6 +670,7 @@ static void le_conn_param_req_neg_reply(struct net_buf *buf, rp->handle = cmd->handle; } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static void le_set_data_len(struct net_buf *buf, struct net_buf *evt) { struct bt_hci_cp_le_set_data_len *cmd = (void *)buf->data; @@ -710,6 +721,7 @@ static void le_read_max_data_len(struct net_buf *buf, struct net_buf *evt) &rp->max_rx_octets, &rp->max_rx_time); rp->status = 0x00; } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static int controller_cmd_handle(uint8_t ocf, struct net_buf *cmd, struct net_buf *evt) @@ -827,6 +839,7 @@ static int controller_cmd_handle(uint8_t ocf, struct net_buf *cmd, le_conn_param_req_neg_reply(cmd, evt); break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) case BT_OCF(BT_HCI_OP_LE_SET_DATA_LEN): le_set_data_len(cmd, evt); break; @@ -842,6 +855,7 @@ static int controller_cmd_handle(uint8_t ocf, struct net_buf *cmd, case BT_OCF(BT_HCI_OP_LE_READ_MAX_DATA_LEN): le_read_max_data_len(cmd, evt); break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ default: return -EINVAL; @@ -1055,6 +1069,7 @@ static void enc_refresh_complete(struct pdu_data *pdu_data, uint16_t handle, ep->handle = sys_cpu_to_le16(handle); } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static void auth_payload_timeout_exp(struct pdu_data *pdu_data, uint16_t handle, struct net_buf *buf) { @@ -1065,6 +1080,7 @@ static void auth_payload_timeout_exp(struct pdu_data *pdu_data, uint16_t handle, ep->handle = sys_cpu_to_le16(handle); } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ static void encode_control(struct radio_pdu_node_rx *node_rx, struct pdu_data *pdu_data, struct net_buf *buf) @@ -1095,17 +1111,30 @@ static void encode_control(struct radio_pdu_node_rx *node_rx, enc_refresh_complete(pdu_data, handle, buf); break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case NODE_RX_TYPE_APTO: auth_payload_timeout_exp(pdu_data, handle, buf); break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) case NODE_RX_TYPE_RSSI: - /** @todo */ + BT_INFO("handle: 0x%04x, rssi: -%d dB.", handle, + pdu_data->payload.rssi); return; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) case NODE_RX_TYPE_PROFILE: - /** @todo */ + BT_INFO("l: %d, %d, %d; t: %d, %d, %d.", + pdu_data->payload.profile.lcur, + pdu_data->payload.profile.lmin, + pdu_data->payload.profile.lmax, + pdu_data->payload.profile.cur, + pdu_data->payload.profile.min, + pdu_data->payload.profile.max); return; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ default: LL_ASSERT(0); @@ -1169,7 +1198,8 @@ static void le_unknown_rsp(struct pdu_data *pdu_data, uint16_t handle, break; default: - BT_ASSERT(0); + BT_WARN("type: 0x%02x", + pdu_data->payload.llctrl.ctrldata.unknown_rsp.type); break; } } @@ -1337,3 +1367,13 @@ void hci_num_cmplt_encode(struct net_buf *buf, uint16_t handle, uint8_t num) hc->handle = sys_cpu_to_le16(handle); hc->count = sys_cpu_to_le16(num); } + +bool hci_evt_is_discardable(struct radio_pdu_node_rx *node_rx) +{ + switch (node_rx->hdr.type) { + case NODE_RX_TYPE_REPORT: + return true; + default: + return false; + } +} diff --git a/subsys/bluetooth/controller/hci/hci_driver.c b/subsys/bluetooth/controller/hci/hci_driver.c index 379fb8003be4..4e23c2fff987 100644 --- a/subsys/bluetooth/controller/hci/hci_driver.c +++ b/subsys/bluetooth/controller/hci/hci_driver.c @@ -38,14 +38,18 @@ #include #endif -#include "util/defines.h" -#include "util/work.h" +#include "util/config.h" +#include "util/mayfly.h" +#include "util/util.h" +#include "util/mem.h" #include "hal/rand.h" #include "hal/ccm.h" #include "hal/radio.h" -#include "hal/hal_rtc.h" +#include "hal/cntr.h" #include "hal/cpu.h" -#include "ll/ticker.h" +#include "ticker/ticker.h" +#include "ll/pdu.h" +#include "ll/ctrl.h" #include "ll/ctrl_internal.h" #include "hci_internal.h" @@ -61,16 +65,20 @@ #define HCI_SCO 0x03 #define HCI_EVT 0x04 -static uint8_t ALIGNED(4) _rand_context[3 + 4 + 1]; -static uint8_t ALIGNED(4) _ticker_nodes[RADIO_TICKER_NODES][TICKER_NODE_T_SIZE]; -static uint8_t ALIGNED(4) _ticker_users[RADIO_TICKER_USERS][TICKER_USER_T_SIZE]; -static uint8_t ALIGNED(4) _ticker_user_ops[RADIO_TICKER_USER_OPS] +static uint8_t MALIGN(4) _rand_context[3 + 4 + 1]; +static uint8_t MALIGN(4) _ticker_nodes[RADIO_TICKER_NODES][TICKER_NODE_T_SIZE]; +static uint8_t MALIGN(4) _ticker_users[MAYFLY_CALLER_COUNT] + [TICKER_USER_T_SIZE]; +static uint8_t MALIGN(4) _ticker_user_ops[RADIO_TICKER_USER_OPS] [TICKER_USER_OP_T_SIZE]; -static uint8_t ALIGNED(4) _radio[LL_MEM_TOTAL]; +static uint8_t MALIGN(4) _radio[LL_MEM_TOTAL]; -static K_SEM_DEFINE(sem_recv, 0, UINT_MAX); -static BT_STACK_NOINIT(recv_thread_stack, - CONFIG_BLUETOOTH_CONTROLLER_RX_STACK_SIZE); +static K_SEM_DEFINE(sem_prio_recv, 0, UINT_MAX); +static K_FIFO_DEFINE(recv_fifo); + +static BT_STACK_NOINIT(prio_recv_thread_stack, + CONFIG_BLUETOOTH_CONTROLLER_RX_PRIO_STACK_SIZE); +static BT_STACK_NOINIT(recv_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE); K_MUTEX_DEFINE(mutex_rand); @@ -96,18 +104,75 @@ int bt_rand(void *buf, size_t len) } #endif +void mayfly_enable(uint8_t caller_id, uint8_t callee_id, uint8_t enable) +{ + (void)caller_id; + + LL_ASSERT(callee_id == MAYFLY_CALL_ID_1); + + if (enable) { + irq_enable(SWI4_IRQn); + } else { + irq_disable(SWI4_IRQn); + } +} + +uint32_t mayfly_is_enabled(uint8_t caller_id, uint8_t callee_id) +{ + (void)caller_id; + + if (callee_id == MAYFLY_CALL_ID_0) { + return irq_is_enabled(RTC0_IRQn); + } else if (callee_id == MAYFLY_CALL_ID_1) { + return irq_is_enabled(SWI4_IRQn); + } + + LL_ASSERT(0); + + return 0; +} + +uint32_t mayfly_prio_is_equal(uint8_t caller_id, uint8_t callee_id) +{ + return (caller_id == callee_id) || + ((caller_id == MAYFLY_CALL_ID_0) && + (callee_id == MAYFLY_CALL_ID_1)) || + ((caller_id == MAYFLY_CALL_ID_1) && + (callee_id == MAYFLY_CALL_ID_0)); +} + +void mayfly_pend(uint8_t caller_id, uint8_t callee_id) +{ + (void)caller_id; + + switch (callee_id) { + case MAYFLY_CALL_ID_0: + _NvicIrqPend(RTC0_IRQn); + break; + + case MAYFLY_CALL_ID_1: + _NvicIrqPend(SWI4_IRQn); + break; + + case MAYFLY_CALL_ID_PROGRAM: + default: + LL_ASSERT(0); + break; + } +} + void radio_active_callback(uint8_t active) { } void radio_event_callback(void) { - k_sem_give(&sem_recv); + k_sem_give(&sem_prio_recv); } static void radio_nrf5_isr(void *arg) { - radio_isr(); + isr_radio(arg); } static void rtc0_nrf5_isr(void *arg) @@ -132,94 +197,108 @@ static void rtc0_nrf5_isr(void *arg) ticker_trigger(1); } - work_run(RTC0_IRQn); + mayfly_run(MAYFLY_CALL_ID_0); } static void rng_nrf5_isr(void *arg) { - rng_isr(); + isr_rand(arg); } static void swi4_nrf5_isr(void *arg) { - work_run(NRF5_IRQ_SWI4_IRQn); -} - -static void swi5_nrf5_isr(void *arg) -{ - work_run(NRF5_IRQ_SWI5_IRQn); + mayfly_run(MAYFLY_CALL_ID_1); } -static void recv_thread(void *p1, void *p2, void *p3) +static void prio_recv_thread(void *p1, void *p2, void *p3) { while (1) { struct radio_pdu_node_rx *node_rx; - struct pdu_data *pdu_data; struct net_buf *buf; uint8_t num_cmplt; uint16_t handle; while ((num_cmplt = radio_rx_get(&node_rx, &handle))) { - buf = bt_buf_get_evt(BT_HCI_EVT_NUM_COMPLETED_PACKETS, - K_FOREVER); - if (buf) { - hci_num_cmplt_encode(buf, handle, num_cmplt); - BT_DBG("Num Complete: 0x%04x:%u", handle, - num_cmplt); - bt_recv(buf); - } else { - BT_ERR("Cannot allocate Num Complete"); - } + buf = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(buf, BT_BUF_EVT); + hci_num_cmplt_encode(buf, handle, num_cmplt); + BT_DBG("Num Complete: 0x%04x:%u", handle, num_cmplt); + bt_recv_prio(buf); k_yield(); } if (node_rx) { - pdu_data = (void *)node_rx->pdu_data; - /* Check if we need to generate an HCI event or ACL - * data - */ - if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU || - pdu_data->ll_id == PDU_DATA_LLID_CTRL) { - /* generate a (non-priority) HCI event */ - buf = bt_buf_get_evt(0, K_FOREVER); - if (buf) { - hci_evt_encode(node_rx, buf); - } else { - BT_ERR("Cannot allocate RX event"); - } + radio_rx_dequeue(); + + BT_DBG("RX node enqueue"); + k_fifo_put(&recv_fifo, node_rx); + + continue; + } + + BT_DBG("sem take..."); + k_sem_take(&sem_prio_recv, K_FOREVER); + BT_DBG("sem taken"); + + stack_analyze("prio recv thread stack", prio_recv_thread_stack, + sizeof(prio_recv_thread_stack)); + } +} + +static void recv_thread(void *p1, void *p2, void *p3) +{ + while (1) { + struct radio_pdu_node_rx *node_rx; + struct pdu_data *pdu_data; + struct net_buf *buf; + + BT_DBG("RX node get"); + node_rx = k_fifo_get(&recv_fifo, K_FOREVER); + BT_DBG("RX node dequeued"); + + pdu_data = (void *)node_rx->pdu_data; + /* Check if we need to generate an HCI event or ACL + * data + */ + if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU || + pdu_data->ll_id == PDU_DATA_LLID_CTRL) { + /* generate a (non-priority) HCI event */ + if (hci_evt_is_discardable(node_rx)) { + buf = bt_buf_get_rx(K_NO_WAIT); } else { - /* generate ACL data */ - buf = bt_buf_get_acl(K_FOREVER); - if (buf) { - hci_acl_encode(node_rx, buf); - } else { - BT_ERR("Cannot allocate RX ACL"); - } + buf = bt_buf_get_rx(K_FOREVER); } if (buf) { - if (buf->len) { - BT_DBG("Packet in: type:%u len:%u", - bt_buf_get_type(buf), buf->len); - bt_recv(buf); - } else { - net_buf_unref(buf); - } + bt_buf_set_type(buf, BT_BUF_EVT); + hci_evt_encode(node_rx, buf); } + } else { + /* generate ACL data */ + buf = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(buf, BT_BUF_ACL_IN); + hci_acl_encode(node_rx, buf); + } - radio_rx_dequeue(); - radio_rx_fc_set(node_rx->hdr.handle, 0); - node_rx->hdr.onion.next = 0; - radio_rx_mem_release(&node_rx); + radio_rx_fc_set(node_rx->hdr.handle, 0); + node_rx->hdr.onion.next = 0; + radio_rx_mem_release(&node_rx); - k_yield(); - } else { - k_sem_take(&sem_recv, K_FOREVER); + if (buf) { + if (buf->len) { + BT_DBG("Packet in: type:%u len:%u", + bt_buf_get_type(buf), buf->len); + bt_recv(buf); + } else { + net_buf_unref(buf); + } } + k_yield(); + stack_analyze("recv thread stack", recv_thread_stack, sizeof(recv_thread_stack)); } @@ -236,15 +315,12 @@ static int cmd_handle(struct net_buf *buf) * actual point is to retrieve the event from the priority * queue */ - evt = bt_buf_get_evt(BT_HCI_EVT_CMD_COMPLETE, K_FOREVER); - if (!evt) { - BT_ERR("No available event buffers"); - return -ENOMEM; - } + evt = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(evt, BT_BUF_EVT); err = hci_cmd_handle(buf, evt); if (!err && evt->len) { BT_DBG("Replying with event of %u bytes", evt->len); - bt_recv(evt); + bt_recv_prio(evt); } else { net_buf_unref(evt); } @@ -305,33 +381,30 @@ static int hci_driver_open(void) clock_control_on(clk_k32, (void *)CLOCK_CONTROL_NRF5_K32SRC); /* TODO: bind and use counter driver */ - rtc_init(); + cntr_init(); + + mayfly_init(); - _ticker_users[RADIO_TICKER_USER_ID_WORKER][0] = - RADIO_TICKER_USER_WORKER_OPS; - _ticker_users[RADIO_TICKER_USER_ID_JOB][0] = - RADIO_TICKER_USER_JOB_OPS; - _ticker_users[RADIO_TICKER_USER_ID_APP][0] = - RADIO_TICKER_USER_APP_OPS; + _ticker_users[MAYFLY_CALL_ID_0][0] = RADIO_TICKER_USER_WORKER_OPS; + _ticker_users[MAYFLY_CALL_ID_1][0] = RADIO_TICKER_USER_JOB_OPS; + _ticker_users[MAYFLY_CALL_ID_2][0] = 0; + _ticker_users[MAYFLY_CALL_ID_PROGRAM][0] = RADIO_TICKER_USER_APP_OPS; ticker_init(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_NODES, - &_ticker_nodes[0] - , RADIO_TICKER_USERS, &_ticker_users[0] - , RADIO_TICKER_USER_OPS, &_ticker_user_ops[0] - ); + &_ticker_nodes[0], MAYFLY_CALLER_COUNT, &_ticker_users[0], + RADIO_TICKER_USER_OPS, &_ticker_user_ops[0]); clk_m16 = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_M16SRC_DRV_NAME); if (!clk_m16) { return -ENODEV; } - err = radio_init(clk_m16, - CLOCK_CONTROL_NRF5_K32SRC_ACCURACY, + err = radio_init(clk_m16, CLOCK_CONTROL_NRF5_K32SRC_ACCURACY, RADIO_CONNECTION_CONTEXT_MAX, RADIO_PACKET_COUNT_RX_MAX, RADIO_PACKET_COUNT_TX_MAX, - RADIO_LL_LENGTH_OCTETS_RX_MAX, &_radio[0], - sizeof(_radio)); + RADIO_LL_LENGTH_OCTETS_RX_MAX, + RADIO_PACKET_TX_DATA_SIZE, &_radio[0], sizeof(_radio)); if (err) { BT_ERR("Required RAM size: %d, supplied: %u.", err, sizeof(_radio)); @@ -342,12 +415,14 @@ static int hci_driver_open(void) IRQ_CONNECT(NRF5_IRQ_RTC0_IRQn, 0, rtc0_nrf5_isr, 0, 0); IRQ_CONNECT(NRF5_IRQ_RNG_IRQn, 1, rng_nrf5_isr, 0, 0); IRQ_CONNECT(NRF5_IRQ_SWI4_IRQn, 0, swi4_nrf5_isr, 0, 0); - IRQ_CONNECT(NRF5_IRQ_SWI5_IRQn, 1, swi5_nrf5_isr, 0, 0); irq_enable(NRF5_IRQ_RADIO_IRQn); irq_enable(NRF5_IRQ_RTC0_IRQn); irq_enable(NRF5_IRQ_RNG_IRQn); irq_enable(NRF5_IRQ_SWI4_IRQn); - irq_enable(NRF5_IRQ_SWI5_IRQn); + + k_thread_spawn(prio_recv_thread_stack, sizeof(prio_recv_thread_stack), + prio_recv_thread, NULL, NULL, NULL, K_PRIO_COOP(6), 0, + K_NO_WAIT); k_thread_spawn(recv_thread_stack, sizeof(recv_thread_stack), recv_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0, diff --git a/subsys/bluetooth/controller/hci/hci_internal.h b/subsys/bluetooth/controller/hci/hci_internal.h index 3529331c54cf..86446dea0f9f 100644 --- a/subsys/bluetooth/controller/hci/hci_internal.h +++ b/subsys/bluetooth/controller/hci/hci_internal.h @@ -24,4 +24,5 @@ void hci_evt_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf); void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf); void hci_num_cmplt_encode(struct net_buf *buf, uint16_t handle, uint8_t num); void hci_le_rand(void *buf, uint8_t len); +bool hci_evt_is_discardable(struct radio_pdu_node_rx *node_rx); #endif /* _HCI_CONTROLLER_H_ */ diff --git a/subsys/bluetooth/controller/ll/ctrl.c b/subsys/bluetooth/controller/ll/ctrl.c index ceda331108ec..ce95a8a9d320 100644 --- a/subsys/bluetooth/controller/ll/ctrl.c +++ b/subsys/bluetooth/controller/ll/ctrl.c @@ -15,30 +15,35 @@ * limitations under the License. */ -#include #include +#include #include -#include +#include #include #include +#include +#include -#include "hal_work.h" - -#include "defines.h" #include "cpu.h" -#include "work.h" #include "rand.h" -#include "ticker.h" -#include "mem.h" -#include "memq.h" -#include "util.h" #include "ecb.h" #include "ccm.h" #include "radio.h" + +#include "mem.h" +#include "memq.h" +#include "mayfly.h" +#include "util.h" +#include "ticker.h" + #include "pdu.h" +#include "ctrl.h" #include "ctrl_internal.h" +#include "config.h" + +#include #include "debug.h" #define RADIO_PREAMBLE_TO_ADDRESS_US 40 @@ -53,12 +58,13 @@ #define RADIO_TICKER_PREEMPT_PART_MIN_US 0 #define RADIO_TICKER_PREEMPT_PART_MAX_US RADIO_TICKER_XTAL_OFFSET_US +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) #define RADIO_RSSI_SAMPLE_COUNT 10 #define RADIO_RSSI_THRESHOLD 4 +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ #define RADIO_IRK_COUNT_MAX 8 -#define FAST_ENC_PROCEDURE 0 #define XTAL_ADVANCED 1 #define SCHED_ADVANCED 1 #define SILENT_CONNECTION 0 @@ -160,9 +166,11 @@ static struct { uint8_t data_channel_count; uint8_t sca; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* DLE global settings */ uint16_t default_tx_octets; uint16_t default_tx_time; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ /** @todo below members to be made role specific and quota managed for * Rx-es. @@ -193,7 +201,6 @@ static struct { void *pkt_tx_ctrl_free; void *pkt_tx_data_pool; void *pkt_tx_data_free; - uint16_t packet_tx_data_pool_size; uint16_t packet_tx_data_size; /* Host to Controller Tx, and Controller to Host Num complete queue */ @@ -243,7 +250,11 @@ static void tx_packet_set(struct connection *conn, static void prepare_pdu_data_tx(struct connection *conn, struct pdu_data **pdu_data_tx); static void packet_rx_allocate(uint8_t max); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static uint8_t packet_rx_acquired_count_get(void); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + static struct radio_pdu_node_rx *packet_rx_reserve_get(uint8_t count); static void packet_rx_enqueue(void); static void packet_tx_enqueue(uint8_t max); @@ -269,13 +280,21 @@ static void unknown_rsp_send(struct connection *conn, uint8_t type); static void feature_rsp_send(struct connection *conn); static void pause_enc_rsp_send(struct connection *conn); static void version_ind_send(struct connection *conn); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static void ping_resp_send(struct connection *conn); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + static void reject_ind_ext_send(struct connection *conn, uint8_t reject_opcode, uint8_t error_code); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static void length_resp_send(struct connection *conn, uint16_t eff_rx_octets, uint16_t eff_tx_octets); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + static uint32_t role_disable(uint8_t ticker_id_primary, uint8_t ticker_id_stop); static void rx_fc_lock(uint16_t handle); @@ -285,7 +304,8 @@ static void rx_fc_lock(uint16_t handle); ****************************************************************************/ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max, uint8_t rx_count_max, uint8_t tx_count_max, - uint16_t packet_data_octets_max, uint8_t *mem_radio, + uint16_t packet_data_octets_max, + uint16_t packet_tx_data_size, uint8_t *mem_radio, uint16_t mem_size) { uint32_t retcode; @@ -318,16 +338,16 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max, _radio.link_rx_data_quota = rx_count_max; /* initialise rx queue memory */ - _radio.packet_rx = (struct radio_pdu_node_rx **)mem_radio; + _radio.packet_rx = (void *)mem_radio; mem_radio += (sizeof(struct radio_pdu_node_rx *)*_radio.packet_rx_count); /* initialise tx queue memory */ - _radio.pkt_tx = (struct pdu_data_q_tx *)mem_radio; + _radio.pkt_tx = (void *)mem_radio; mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count); /* initialise tx release queue memory */ - _radio.pkt_release = (struct pdu_data_q_tx *)mem_radio; + _radio.pkt_release = (void *)mem_radio; mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count); /* initialise rx memory size and count */ @@ -336,12 +356,12 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max, (offsetof(struct pdu_data, payload) + _radio.packet_data_octets_max)) { _radio.packet_rx_data_pool_size = - (ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) + + (MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + offsetof(struct pdu_data, payload) + _radio.packet_data_octets_max) * rx_count_max); } else { _radio.packet_rx_data_pool_size = - (ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) + + (MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + (RADIO_ACPDU_SIZE_MAX + 1)) * rx_count_max); } _radio.packet_rx_data_size = PACKET_RX_DATA_SIZE_MIN; @@ -363,15 +383,13 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max, /* initialise tx data memory size and count */ _radio.packet_tx_data_size = - ALIGN4(offsetof(struct radio_pdu_node_tx, pdu_data) + + MROUND(offsetof(struct radio_pdu_node_tx, pdu_data) + offsetof(struct pdu_data, payload) + - _radio.packet_data_octets_max); - _radio.packet_tx_data_pool_size = - (_radio.packet_tx_data_size * tx_count_max); + packet_tx_data_size); /* initialise tx data pool memory */ _radio.pkt_tx_data_pool = mem_radio; - mem_radio += _radio.packet_tx_data_pool_size; + mem_radio += (_radio.packet_tx_data_size * tx_count_max); /* check for sufficient memory allocation for stack * configuration. @@ -480,9 +498,11 @@ static void common_init(void) _radio.data_channel_map[4] = 0x1F; _radio.data_channel_count = 37; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* Initialize the DLE defaults */ _radio.default_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; _radio.default_tx_time = RADIO_LL_LENGTH_TIME_RX_MIN; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ /* allocate the rx queue */ packet_rx_allocate(0xFF); @@ -502,6 +522,9 @@ static inline void isr_radio_state_tx(void) case ROLE_ADV: radio_pkt_rx_set(radio_pkt_scratch_get()); + /* assert if radio packet ptr is not set and radio started rx */ + LL_ASSERT(!radio_is_ready()); + if (_radio.advertiser.filter_policy && _radio.nirk) { radio_ar_configure(_radio.nirk, _radio.irk); } @@ -512,22 +535,38 @@ static inline void isr_radio_state_tx(void) case ROLE_OBS: radio_pkt_rx_set(_radio.packet_rx[_radio.packet_rx_last]-> pdu_data); + + /* assert if radio packet ptr is not set and radio started rx */ + LL_ASSERT(!radio_is_ready()); + radio_rssi_measure(); break; case ROLE_MASTER: + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) if (_radio.packet_counter == 0) { radio_rssi_measure(); } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ + /* fall thru */ case ROLE_SLAVE: rx_packet_set(_radio.conn_curr, (struct pdu_data *)_radio. packet_rx[_radio.packet_rx_last]->pdu_data); + /* assert if radio packet ptr is not set and radio started rx */ + LL_ASSERT(!radio_is_ready()); + radio_tmr_end_capture(); /* Route the tx packet to respective connections */ + /* TODO: use timebox for tx enqueue (instead of 1 packet + * that is routed, which may not be for the current connection) + * try to route as much tx packet in queue into corresponding + * connection's tx list. + */ packet_tx_enqueue(1); break; @@ -536,7 +575,6 @@ static inline void isr_radio_state_tx(void) LL_ASSERT(0); break; } - } static inline uint32_t isr_rx_adv(uint8_t devmatch_ok, uint8_t irkmatch_ok, @@ -557,7 +595,7 @@ static inline uint32_t isr_rx_adv(uint8_t devmatch_ok, uint8_t irkmatch_ok, radio_switch_complete_and_disable(); /* TODO use rssi_ready to generate proprietary scan_req event */ - rssi_ready = rssi_ready; /* unused for now */ + ARG_UNUSED(rssi_ready); /* use the latest scan data, if any */ if (_radio.advertiser.scan_data.first != _radio. @@ -631,12 +669,15 @@ static inline uint32_t isr_rx_adv(uint8_t devmatch_ok, uint8_t irkmatch_ok, * 10 * 1000), conn_interval_us); conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); conn->appto_reload = (conn->apto_reload > (conn->latency + 2)) ? (conn->apto_reload - (conn->latency + 2)) : conn->apto_reload; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ /* Prepare the rx packet structure */ radio_pdu_node_rx->hdr.handle = conn->handle; @@ -853,6 +894,7 @@ static inline uint32_t isr_rx_obs(uint8_t irkmatch_id, uint8_t rssi_ready) radio_pkt_tx_set(pdu_adv_tx); + /* assert if radio packet ptr is not set and radio started tx */ LL_ASSERT(!radio_is_ready()); radio_tmr_end_capture(); @@ -1030,6 +1072,9 @@ static inline uint8_t isr_rx_conn_pkt_ack(struct pdu_data *pdu_data_tx, _radio.state = STATE_CLOSE; radio_disable(); + /* assert if radio packet ptr is not set and radio started tx */ + LL_ASSERT(!radio_is_ready()); + terminate_ind_rx_enqueue(_radio.conn_curr, (pdu_data_tx->payload.llctrl.ctrldata.terminate_ind. error_code == 0x13) ? 0x16 : @@ -1109,6 +1154,7 @@ static inline uint8_t isr_rx_conn_pkt_ack(struct pdu_data *pdu_data_tx, _radio.conn_curr->procedure_expire = 0; break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ: if ((_radio.conn_curr->llcp_length.req != _radio.conn_curr->llcp_length.ack) && @@ -1122,6 +1168,7 @@ static inline uint8_t isr_rx_conn_pkt_ack(struct pdu_data *pdu_data_tx, LLCP_LENGTH_STATE_RSP_WAIT; } break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ default: /* Do nothing for other ctrl packet ack */ @@ -1131,9 +1178,9 @@ static inline uint8_t isr_rx_conn_pkt_ack(struct pdu_data *pdu_data_tx, return terminate; } -static inline void isr_rx_conn_pkt_release(struct radio_pdu_node_tx *node_tx) +static inline struct radio_pdu_node_tx * +isr_rx_conn_pkt_release(struct radio_pdu_node_tx *node_tx) { - _radio.conn_curr->packet_tx_head_len = 0; _radio.conn_curr->packet_tx_head_offset = 0; @@ -1160,13 +1207,15 @@ static inline void isr_rx_conn_pkt_release(struct radio_pdu_node_tx *node_tx) _radio.conn_curr->pkt_tx_head = _radio.conn_curr->pkt_tx_head->next; - pdu_node_tx_release(_radio.conn_curr->handle, node_tx); + return node_tx; } + + return NULL; } static inline void isr_rx_conn_pkt_ctrl_rej(struct radio_pdu_node_rx *radio_pdu_node_rx, - struct pdu_data *pdu_data_rx, uint8_t *rx_enqueue) + uint8_t *rx_enqueue) { /* reset ctrl procedure */ _radio.conn_curr->llcp_ack = _radio.conn_curr->llcp_req; @@ -1192,10 +1241,13 @@ isr_rx_conn_pkt_ctrl_rej(struct radio_pdu_node_rx *radio_pdu_node_rx, if (!_radio.conn_curr->llcp.connection_update.is_internal) { struct radio_le_conn_update_cmplt *radio_le_conn_update_cmplt; + struct pdu_data *pdu_data_rx; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONN_UPDATE; /* prepare connection update complete structure */ + pdu_data_rx = + (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_update_cmplt = (struct radio_le_conn_update_cmplt *) &pdu_data_rx->payload; @@ -1219,6 +1271,7 @@ isr_rx_conn_pkt_ctrl_rej(struct radio_pdu_node_rx *radio_pdu_node_rx, } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static inline uint8_t isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx, uint8_t *rx_enqueue) { @@ -1337,13 +1390,16 @@ static inline uint8_t isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx, return nack; } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static inline uint8_t isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, - struct pdu_data *pdu_data_rx, uint8_t *rx_enqueue) + uint8_t *rx_enqueue) { + struct pdu_data *pdu_data_rx; uint8_t nack = 0; + pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; switch (pdu_data_rx->payload.llctrl.opcode) { case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_REQ: if (conn_update(_radio.conn_curr, pdu_data_rx) == 0) { @@ -1395,12 +1451,12 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, } #endif -#if !!FAST_ENC_PROCEDURE +#if defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) /* TODO BT Spec. text: may finalize the sending of additional * data channel PDUs queued in the controller. */ enc_rsp_send(_radio.conn_curr); -#endif +#endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* enqueue the enc req */ *rx_enqueue = 1; @@ -1430,14 +1486,15 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP: if (_radio.role == ROLE_SLAVE) { -#if !FAST_ENC_PROCEDURE + +#if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) LL_ASSERT(_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack); /* start enc rsp to be scheduled in slave prepare */ _radio.conn_curr->llcp_type = LLCP_ENCRYPTION; _radio.conn_curr->llcp_ack--; -#else +#else /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* enable transmit encryption */ _radio.conn_curr->enc_tx = 1; @@ -1446,7 +1503,8 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; -#endif +#endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ + } else { /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; @@ -1680,8 +1738,7 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, case PDU_DATA_LLCTRL_TYPE_REJECT_IND_EXT: if (_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) { - isr_rx_conn_pkt_ctrl_rej(radio_pdu_node_rx, - pdu_data_rx, rx_enqueue); + isr_rx_conn_pkt_ctrl_rej(radio_pdu_node_rx, rx_enqueue); } else { /* By spec. slave shall not generate a conn update @@ -1691,6 +1748,7 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, } break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case PDU_DATA_LLCTRL_TYPE_PING_REQ: ping_resp_send(_radio.conn_curr); break; @@ -1699,6 +1757,7 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP: if (_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) { @@ -1710,6 +1769,8 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, LL_ASSERT(0); break; } + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) } else if (_radio.conn_curr->llcp_length.req != _radio.conn_curr->llcp_length.ack) { /* Procedure complete */ @@ -1724,18 +1785,40 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, * host */ *rx_enqueue = 1; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + } else { - /* enqueue the error and let HCI handle it */ - *rx_enqueue = 1; + struct pdu_data_llctrl *llctrl; + + llctrl = (struct pdu_data_llctrl *) + &pdu_data_rx->payload.llctrl; + switch (llctrl->ctrldata.unknown_rsp.type) { + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) + case PDU_DATA_LLCTRL_TYPE_PING_REQ: + /* unknown rsp to LE Ping Req completes the + * procedure; nothing to do here. + */ + break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + + default: + /* enqueue the error and let HCI handle it */ + *rx_enqueue = 1; + break; + } + /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; } break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) case PDU_DATA_LLCTRL_TYPE_LENGTH_RSP: case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ: nack = isr_rx_conn_pkt_ctrl_dle(pdu_data_rx, rx_enqueue); break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ default: unknown_rsp_send(_radio.conn_curr, @@ -1748,16 +1831,18 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, static inline uint32_t isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, - struct pdu_data *pdu_data_rx) + struct radio_pdu_node_tx **tx_release, uint8_t *rx_enqueue) { - uint8_t nack = 0; - uint8_t terminate = 0; + struct pdu_data *pdu_data_rx; struct pdu_data *pdu_data_tx; + uint8_t terminate = 0; + uint8_t nack = 0; /* Reset CRC expiry counter */ _radio.crc_expire = 0; /* Ack for transmitted data */ + pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; if (pdu_data_rx->nesn != _radio.conn_curr->sn) { /* Increment serial number */ @@ -1792,7 +1877,7 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, _radio.conn_curr->packet_tx_head_offset += pdu_data_tx_len; if (_radio.conn_curr->packet_tx_head_offset == _radio.conn_curr->packet_tx_head_len) { - isr_rx_conn_pkt_release(node_tx); + *tx_release = isr_rx_conn_pkt_release(node_tx); } } else { _radio.conn_curr->empty = 0; @@ -1827,8 +1912,6 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, uint8_t ccm_rx_increment = 0; if (pdu_data_rx->len != 0) { - uint8_t rx_enqueue = 0; - /* If required, wait for CCM to finish */ if (_radio.conn_curr->enc_rx) { @@ -1848,6 +1931,10 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, _radio.state = STATE_CLOSE; radio_disable(); + /* assert if radio packet ptr is not set and + * radio started tx */ + LL_ASSERT(!radio_is_ready()); + terminate_ind_rx_enqueue(_radio.conn_curr, 0x3d); @@ -1857,35 +1944,22 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, return 1; /* terminated */ } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* stop authenticated payload (pre) timeout */ _radio.conn_curr->appto_expire = 0; _radio.conn_curr->apto_expire = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ switch (pdu_data_rx->ll_id) { case PDU_DATA_LLID_DATA_CONTINUE: case PDU_DATA_LLID_DATA_START: /* enqueue data packet */ - rx_enqueue = 1; + *rx_enqueue = 1; break; case PDU_DATA_LLID_CTRL: - /* Handling control procedure takes more CPU - * time hence check how much CPU time we have - * used up inside tIFS (150us) and decide to - * NACK rx-ed packet if consumed more than half - * the tIFS value. Control Procedure will be - * handled in the next re-transmission of the - * packet by peer. - */ - radio_tmr_sample(); - if ((radio_tmr_sample_get() - - radio_tmr_end_get()) < 75) { - nack = isr_rx_conn_pkt_ctrl( - radio_pdu_node_rx, - pdu_data_rx, &rx_enqueue); - } else { - nack = 1; - } + nack = isr_rx_conn_pkt_ctrl(radio_pdu_node_rx, + rx_enqueue); break; case PDU_DATA_LLID_RESV: default: @@ -1893,17 +1967,7 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, break; } - if (rx_enqueue) { - rx_fc_lock(_radio.conn_curr->handle); - - /* as packet is to be enqueued, store the - * correct handle for it and enqueue it - */ - radio_pdu_node_rx->hdr.handle = - _radio.conn_curr->handle; - - packet_rx_enqueue(); - } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) } else if ((_radio.conn_curr->enc_rx) || (_radio.conn_curr->pause_rx)) { /* start authenticated payload (pre) timeout */ @@ -1913,6 +1977,8 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, _radio.conn_curr->apto_expire = _radio.conn_curr->apto_reload; } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + } if (!nack) { @@ -1930,13 +1996,27 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, uint8_t rssi_ready) { - struct radio_pdu_node_rx *radio_pdu_node_rx; + struct radio_pdu_node_tx *tx_release = NULL; + uint8_t is_empty_pdu_tx_retry; struct pdu_data *pdu_data_rx; struct pdu_data *pdu_data_tx; + uint8_t rx_enqueue = 0; uint8_t crc_close = 0; - uint8_t is_empty_pdu_tx_retry; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + static uint8_t s_lmax; + static uint8_t s_lmin = (uint8_t) -1; + static uint8_t s_lprv; + static uint8_t s_max; + static uint8_t s_min = (uint8_t) -1; + static uint8_t s_prv; + uint32_t sample; + uint8_t latency, elapsed, prv; + uint8_t chg = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) /* Collect RSSI for connection */ if (_radio.packet_counter == 0) { if (rssi_ready) { @@ -1955,6 +2035,7 @@ static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, } } } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* Increment packet counter for this connection event */ _radio.packet_counter++; @@ -1962,14 +2043,14 @@ static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, /* received data packet */ radio_pdu_node_rx = _radio.packet_rx[_radio.packet_rx_last]; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_DC_PDU; - pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; if (crc_ok) { uint32_t terminate; - terminate = isr_rx_conn_pkt(radio_pdu_node_rx, pdu_data_rx); + terminate = isr_rx_conn_pkt(radio_pdu_node_rx, &tx_release, + &rx_enqueue); if (terminate) { - return; + goto isr_rx_conn_exit; } } else { /* Start CRC error countdown, if not already started */ @@ -2009,12 +2090,14 @@ static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, (pdu_data_tx->md == 0)) { _radio.state = STATE_CLOSE; radio_disable(); - return; + + goto isr_rx_conn_exit; } } } /* Decide on event continuation and hence Radio Shorts to use */ + pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; _radio.state = ((_radio.state == STATE_CLOSE) || (crc_close) || ((crc_ok) && (pdu_data_rx->md == 0) && (pdu_data_tx->len == 0)) || @@ -2024,11 +2107,11 @@ static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, if (_radio.state == STATE_CLOSE) { /* Event close for master */ if (_radio.role == ROLE_MASTER) { - radio_disable(); - _radio.conn_curr->empty = is_empty_pdu_tx_retry; - return; + radio_disable(); + + goto isr_rx_conn_exit; } /* Event close for slave */ else { @@ -2044,8 +2127,102 @@ static inline void isr_rx_conn(uint8_t crc_ok, uint8_t trx_done, pdu_data_tx->sn = _radio.conn_curr->sn; pdu_data_tx->nesn = _radio.conn_curr->nesn; - /* Setup the radio tx packet buffer */ + /* setup the radio tx packet buffer */ tx_packet_set(_radio.conn_curr, pdu_data_tx); + +isr_rx_conn_exit: + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + /* get the ISR latency sample */ + sample = radio_tmr_sample_get(); + + /* sample the packet timer again, use it to calculate ISR execution time + * and use it in profiling event + */ + radio_tmr_sample(); + +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + + /* release tx node and generate event for num complete */ + if (tx_release) { + pdu_node_tx_release(_radio.conn_curr->handle, tx_release); + } + + /* enqueue any rx packet/event towards application */ + if (rx_enqueue) { + /* set data flow control lock on currently rx-ed connection */ + rx_fc_lock(_radio.conn_curr->handle); + + /* set the connection handle and enqueue */ + radio_pdu_node_rx->hdr.handle = _radio.conn_curr->handle; + packet_rx_enqueue(); + } + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + /* calculate the elapsed time in us since on-air radio packet end + * to ISR entry + */ + latency = sample - radio_tmr_end_get(); + + /* check changes in min, avg and max of latency */ + if (latency > s_lmax) { + s_lmax = latency; + chg = 1; + } + if (latency < s_lmin) { + s_lmin = latency; + chg = 1; + } + + /* check for +/- 1us change */ + prv = ((uint16_t)s_lprv + latency) >> 1; + if (prv != s_lprv) { + s_lprv = latency; + chg = 1; + } + + /* calculate the elapsed time in us since ISR entry */ + elapsed = radio_tmr_sample_get() - sample; + + /* check changes in min, avg and max */ + if (elapsed > s_max) { + s_max = elapsed; + chg = 1; + } + + if (elapsed < s_min) { + s_min = elapsed; + chg = 1; + } + + /* check for +/- 1us change */ + prv = ((uint16_t)s_prv + elapsed) >> 1; + if (prv != s_prv) { + s_prv = elapsed; + chg = 1; + } + + /* generate event if any change */ + if (chg) { + /* NOTE: enqueue only if rx buffer available, else ignore */ + radio_pdu_node_rx = packet_rx_reserve_get(2); + if (radio_pdu_node_rx) { + radio_pdu_node_rx->hdr.handle = 0xFFFF; + radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_PROFILE; + pdu_data_rx = (struct pdu_data *) + radio_pdu_node_rx->pdu_data; + pdu_data_rx->payload.profile.lcur = latency; + pdu_data_rx->payload.profile.lmin = s_lmin; + pdu_data_rx->payload.profile.lmax = s_lmax; + pdu_data_rx->payload.profile.cur = elapsed; + pdu_data_rx->payload.profile.min = s_min; + pdu_data_rx->payload.profile.max = s_max; + packet_rx_enqueue(); + } + } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + + return; } static inline void isr_radio_state_rx(uint8_t trx_done, uint8_t crc_ok, @@ -2374,6 +2551,7 @@ static inline void isr_close_conn(void) } } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* check apto */ if (_radio.conn_curr->apto_expire != 0) { if (_radio.conn_curr->apto_expire > elapsed_event) { @@ -2410,7 +2588,9 @@ static inline void isr_close_conn(void) } } } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) /* generate RSSI event */ if (_radio.conn_curr->rssi_sample_count == 0) { struct radio_pdu_node_rx *radio_pdu_node_rx; @@ -2438,6 +2618,7 @@ static inline void isr_close_conn(void) packet_rx_enqueue(); } } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* break latency based on ctrl procedure pending */ if ((_radio.conn_curr->llcp_ack != _radio.conn_curr->llcp_req) && @@ -2508,7 +2689,7 @@ static inline void isr_radio_state_close(void) clock_control_off(_radio.hf_clock, NULL); - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1); DEBUG_RADIO_CLOSE(0); } @@ -2527,6 +2708,14 @@ static void isr(void) /* Read radio status and events */ trx_done = radio_is_done(); if (trx_done) { + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + /* sample the packet timer here, use it to calculate ISR latency + * and generate the profiling event at the end of the ISR. + */ + radio_tmr_sample(); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + crc_ok = radio_crc_is_valid(); devmatch_ok = radio_filter_has_match(); irkmatch_ok = radio_ar_has_match(); @@ -2571,20 +2760,18 @@ static void isr(void) break; } - LL_ASSERT(((_radio.state != STATE_RX) && (_radio.state != STATE_TX)) || - (!radio_is_ready())); - DEBUG_RADIO_ISR(0); } -#if (WORK_TICKER_WORKER0_IRQ_PRIORITY == WORK_TICKER_JOB0_IRQ_PRIORITY) +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) static void ticker_job_disable(uint32_t status, void *op_context) { ARG_UNUSED(status); ARG_UNUSED(op_context); if (_radio.state != STATE_NONE) { - work_disable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_JOB, 0); } } #endif @@ -2601,7 +2788,7 @@ static void ticker_success_assert(uint32_t status, void *params) LL_ASSERT(status == TICKER_STATUS_SUCCESS); } -static void work_radio_active(void *params) +static void mayfly_radio_active(void *params) { static uint8_t s_active; @@ -2629,9 +2816,9 @@ static void work_radio_active(void *params) static void event_active(uint32_t ticks_at_expire, uint32_t remainder, uint16_t lazy, void *context) { - static struct work s_work_radio_active = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_active, - (void *)1}; + static void *s_link[2]; + static struct mayfly s_mfy_radio_active = {0, 0, s_link, (void *)1, + mayfly_radio_active}; uint32_t retval; ARG_UNUSED(ticks_at_expire); @@ -2639,15 +2826,17 @@ static void event_active(uint32_t ticks_at_expire, uint32_t remainder, ARG_UNUSED(lazy); ARG_UNUSED(context); - retval = work_schedule(&s_work_radio_active, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_active); LL_ASSERT(!retval); } -static void work_radio_inactive(void *params) +static void mayfly_radio_inactive(void *params) { ARG_UNUSED(params); - work_radio_active(0); + mayfly_radio_active(0); DEBUG_RADIO_CLOSE(0); } @@ -2655,8 +2844,9 @@ static void work_radio_inactive(void *params) static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder, uint16_t lazy, void *context) { - static struct work s_work_radio_inactive = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_inactive, 0}; + static void *s_link[2]; + static struct mayfly s_mfy_radio_inactive = {0, 0, s_link, 0, + mayfly_radio_inactive}; uint32_t retval; ARG_UNUSED(ticks_at_expire); @@ -2664,11 +2854,13 @@ static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder, ARG_UNUSED(lazy); ARG_UNUSED(context); - retval = work_schedule(&s_work_radio_inactive, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_inactive); LL_ASSERT(!retval); } -static void work_xtal_start(void *params) +static void mayfly_xtal_start(void *params) { ARG_UNUSED(params); @@ -2679,8 +2871,9 @@ static void work_xtal_start(void *params) static void event_xtal(uint32_t ticks_at_expire, uint32_t remainder, uint16_t lazy, void *context) { - static struct work s_work_xtal_start = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_xtal_start, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_xtal_start = {0, 0, s_link, 0, + mayfly_xtal_start}; uint32_t retval; ARG_UNUSED(ticks_at_expire); @@ -2688,11 +2881,13 @@ static void event_xtal(uint32_t ticks_at_expire, uint32_t remainder, ARG_UNUSED(lazy); ARG_UNUSED(context); - retval = work_schedule(&s_work_xtal_start, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_xtal_start); LL_ASSERT(!retval); } -static void work_xtal_stop(void *params) +static void mayfly_xtal_stop(void *params) { ARG_UNUSED(params); @@ -2702,32 +2897,36 @@ static void work_xtal_stop(void *params) } #if XTAL_ADVANCED -static void work_xtal_retain(uint8_t retain) +static void mayfly_xtal_retain(uint8_t retain) { static uint8_t s_xtal_retained; if (retain) { if (!s_xtal_retained) { - static struct work s_work_xtal_start = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, - (work_fp) work_xtal_start, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_xtal_start = {0, 0, s_link, + 0, mayfly_xtal_start}; uint32_t retval; s_xtal_retained = 1; - retval = work_schedule(&s_work_xtal_start, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_xtal_start); LL_ASSERT(!retval); } } else { if (s_xtal_retained) { - static struct work s_work_xtal_stop = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, - (work_fp) work_xtal_stop, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_xtal_stop = {0, 0, s_link, + 0, mayfly_xtal_stop}; uint32_t retval; s_xtal_retained = 0; - retval = work_schedule(&s_work_xtal_stop, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_xtal_stop); LL_ASSERT(!retval); } } @@ -2793,7 +2992,7 @@ static uint32_t preempt_calc(struct shdr *hdr, uint8_t ticker_id, diff += 3; if (diff > TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US)) { - work_xtal_retain(0); + mayfly_xtal_retain(0); prepare_normal_set(hdr, RADIO_TICKER_USER_ID_WORKER, ticker_id); @@ -2818,7 +3017,7 @@ static uint32_t preempt_calc(struct shdr *hdr, uint8_t ticker_id, * * @todo Detect drift for overlapping tickers. */ -static void work_xtal_stop_calc(void *params) +static void mayfly_xtal_stop_calc(void *params) { uint32_t volatile ticker_status; uint8_t ticker_id; @@ -2834,14 +3033,15 @@ static void work_xtal_stop_calc(void *params) ticker_if_done, (void *)&ticker_status); while (ticker_status == TICKER_STATUS_BUSY) { - ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO); + ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, + RADIO_TICKER_USER_ID_JOB); } LL_ASSERT(ticker_status == TICKER_STATUS_SUCCESS); if ((ticker_id != 0xff) && (ticks_to_expire < TICKER_US_TO_TICKS(10000))) { - work_xtal_retain(1); + mayfly_xtal_retain(1); if (ticker_id >= RADIO_TICKER_ID_ADV) { #if SCHED_ADVANCED @@ -2979,7 +3179,7 @@ static void work_xtal_stop_calc(void *params) #endif /* SCHED_ADVANCED */ } } else { - work_xtal_retain(0); + mayfly_xtal_retain(0); if ((ticker_id != 0xff) && (ticker_id >= RADIO_TICKER_ID_ADV)) { struct shdr *hdr = NULL; @@ -3010,10 +3210,10 @@ static void work_xtal_stop_calc(void *params) #endif /* XTAL_ADVANCED */ #if SCHED_ADVANCED -static void sched_after_master_free_slot_get(uint8_t user_id, - uint32_t ticks_slot_abs, - uint32_t *ticks_anchor, - uint32_t *us_offset) +static void sched_after_mstr_free_slot_get(uint8_t user_id, + uint32_t ticks_slot_abs, + uint32_t *ticks_anchor, + uint32_t *us_offset) { uint8_t ticker_id; uint8_t ticker_id_prev; @@ -3035,7 +3235,8 @@ static void sched_after_master_free_slot_get(uint8_t user_id, (void *)&ticker_status); while (ticker_status == TICKER_STATUS_BUSY) { - ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO); + ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, + user_id); } LL_ASSERT(ticker_status == TICKER_STATUS_SUCCESS); @@ -3089,17 +3290,18 @@ static void sched_after_master_free_slot_get(uint8_t user_id, } } -static void sched_after_master_free_offset_get(uint16_t conn_interval, - uint32_t ticks_slot, - uint32_t ticks_anchor, - uint32_t *win_offset_us) +static void sched_after_mstr_free_offset_get(uint16_t conn_interval, + uint32_t ticks_slot, + uint32_t ticks_anchor, + uint32_t *win_offset_us) { uint32_t ticks_anchor_offset = ticks_anchor; - sched_after_master_free_slot_get(RADIO_TICKER_USER_ID_JOB, - (TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US) + - ticks_slot), &ticks_anchor_offset, - win_offset_us); + sched_after_mstr_free_slot_get(RADIO_TICKER_USER_ID_JOB, + (TICKER_US_TO_TICKS( + RADIO_TICKER_XTAL_OFFSET_US) + + ticks_slot), &ticks_anchor_offset, + win_offset_us); if (ticks_anchor_offset != ticks_anchor) { *win_offset_us += @@ -3116,23 +3318,23 @@ static void sched_after_master_free_offset_get(uint16_t conn_interval, } } -static void work_sched_after_master_free_offset_get(void *params) +static void mayfly_sched_after_mstr_free_offset_get(void *params) { - sched_after_master_free_offset_get(_radio.observer.conn_interval, - _radio.observer.ticks_conn_slot, - (uint32_t)params, - &_radio.observer.win_offset_us); + sched_after_mstr_free_offset_get(_radio.observer.conn_interval, + _radio.observer.ticks_conn_slot, + (uint32_t)params, + &_radio.observer.win_offset_us); } -static void work_sched_win_offset_use(void *params) +static void mayfly_sched_win_offset_use(void *params) { struct connection *conn = (struct connection *)params; uint16_t win_offset; - sched_after_master_free_offset_get(conn->conn_interval, - conn->hdr.ticks_slot, - conn->llcp.connection_update.ticks_ref, - &conn->llcp.connection_update.win_offset_us); + sched_after_mstr_free_offset_get(conn->conn_interval, + conn->hdr.ticks_slot, + conn->llcp.connection_update.ticks_ref, + &conn->llcp.connection_update.win_offset_us); win_offset = conn->llcp.connection_update.win_offset_us / 1250; memcpy(conn->llcp.connection_update.pdu_win_offset, &win_offset, @@ -3187,7 +3389,8 @@ static void sched_free_win_offset_calc(struct connection *conn_curr, (void *)&ticker_status); while (ticker_status == TICKER_STATUS_BUSY) { - ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO); + ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, + RADIO_TICKER_USER_ID_JOB); } LL_ASSERT(ticker_status == TICKER_STATUS_SUCCESS); @@ -3316,7 +3519,7 @@ static void sched_free_win_offset_calc(struct connection *conn_curr, *offset_max = offset_index; } -static void work_sched_free_win_offset_calc(void *params) +static void mayfly_sched_free_win_offset_calc(void *params) { struct connection *conn = (struct connection *)params; uint32_t ticks_to_offset_default = 0; @@ -3339,7 +3542,7 @@ static void work_sched_free_win_offset_calc(void *params) (uint8_t *)conn->llcp.connection_update.pdu_win_offset); } -static void work_sched_win_offset_select(void *params) +static void mayfly_sched_win_offset_select(void *params) { #define OFFSET_S_MAX 6 #define OFFSET_M_MAX 6 @@ -3413,7 +3616,7 @@ static void work_sched_win_offset_select(void *params) } #endif /* SCHED_ADVANCED */ -static void work_radio_stop(void *params) +static void mayfly_radio_stop(void *params) { enum state state = (enum state)((uint32_t)params & 0xff); uint32_t radio_used; @@ -3436,8 +3639,9 @@ static void work_radio_stop(void *params) static void event_stop(uint32_t ticks_at_expire, uint32_t remainder, uint16_t lazy, void *context) { - static struct work s_work_radio_stop = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_stop, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_radio_stop = {0, 0, s_link, 0, + mayfly_radio_stop}; uint32_t retval; ARG_UNUSED(ticks_at_expire); @@ -3447,10 +3651,12 @@ static void event_stop(uint32_t ticks_at_expire, uint32_t remainder, /* Radio state requested (stop or abort) stored in context is supplied * in params. */ - s_work_radio_stop.params = context; + s_mfy_radio_stop.param = context; /* Stop Radio Tx/Rx */ - retval = work_schedule(&s_work_radio_stop, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_stop); LL_ASSERT(!retval); } @@ -3597,14 +3803,16 @@ static void event_common_prepare(uint32_t ticks_at_expire, /* calc whether xtal needs to be retained after this event */ #if XTAL_ADVANCED { - static struct work s_work_xtal_stop_calc = { - 0, 0, 0, WORK_TICKER_JOB0_IRQ, - (work_fp) work_xtal_stop_calc, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_xtal_stop_calc = {0, 0, s_link, 0, + mayfly_xtal_stop_calc}; uint32_t retval; - s_work_xtal_stop_calc.params = (void *)(uint32_t)ticker_id; + s_mfy_xtal_stop_calc.param = (void *)(uint32_t)ticker_id; - retval = work_schedule(&s_work_xtal_stop_calc, 1); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 1, + &s_mfy_xtal_stop_calc); LL_ASSERT(!retval); } #endif @@ -3885,8 +4093,9 @@ static void event_adv(uint32_t ticks_at_expire, uint32_t remainder, radio_disable(); } else #endif - /* Ticker Job Silence */ -#if (WORK_TICKER_WORKER0_IRQ_PRIORITY == WORK_TICKER_JOB0_IRQ_PRIORITY) + + /* Ticker Job Silence */ +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) { uint32_t ticker_status; @@ -3979,9 +4188,10 @@ static void event_obs_prepare(uint32_t ticks_at_expire, uint32_t remainder, * to be placed */ if (_radio.observer.conn) { - static struct work _work_sched_after_master_free_offset_get = { - 0, 0, 0, WORK_TICKER_JOB0_IRQ, - (work_fp) work_sched_after_master_free_offset_get, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_sched_after_mstr_free_offset_get = { + 0, 0, s_link, 0, + mayfly_sched_after_mstr_free_offset_get}; uint32_t ticks_at_expire_normal = ticks_at_expire; uint32_t retval; @@ -3998,11 +4208,12 @@ static void event_obs_prepare(uint32_t ticks_at_expire, uint32_t remainder, ticks_prepare_to_start); } - _work_sched_after_master_free_offset_get.params = + s_mfy_sched_after_mstr_free_offset_get.param = (void *)ticks_at_expire_normal; - retval = work_schedule(&_work_sched_after_master_free_offset_get, - 1); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 1, + &s_mfy_sched_after_mstr_free_offset_get); LL_ASSERT(!retval); } #endif @@ -4088,7 +4299,7 @@ static void event_obs(uint32_t ticks_at_expire, uint32_t remainder, (ticker_status == TICKER_STATUS_BUSY)); /* Ticker Job Silence */ -#if (WORK_TICKER_WORKER0_IRQ_PRIORITY == WORK_TICKER_JOB0_IRQ_PRIORITY) +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) { uint32_t ticker_status; @@ -4106,12 +4317,13 @@ static void event_obs(uint32_t ticks_at_expire, uint32_t remainder, DEBUG_RADIO_START_O(0); } -static inline void event_conn_update_st_init(struct connection *conn, - uint16_t event_counter, - struct pdu_data *pdu_ctrl_tx, - uint32_t ticks_at_expire, - struct work *work_sched_offset, - work_fp fp_work_select_or_use) +static inline void +event_conn_update_st_init(struct connection *conn, + uint16_t event_counter, + struct pdu_data *pdu_ctrl_tx, + uint32_t ticks_at_expire, + struct mayfly *mayfly_sched_offset, + void (*fp_mayfly_select_or_use)(void *)) { /* move to in progress */ conn->llcp.connection_update.state = LLCP_CONN_STATE_INPROG; @@ -4163,16 +4375,18 @@ static inline void event_conn_update_st_init(struct connection *conn, conn->llcp.connection_update.pdu_win_offset = (uint16_t *) &pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_req.win_offset; - work_sched_offset->fp = fp_work_select_or_use; - work_sched_offset->params = (void *)conn; + mayfly_sched_offset->fp = fp_mayfly_select_or_use; + mayfly_sched_offset->param = (void *)conn; - retval = work_schedule(work_sched_offset, 1); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 1, + mayfly_sched_offset); LL_ASSERT(!retval); } #else ARG_UNUSED(ticks_at_expire); - ARG_UNUSED(work_sched_offset); - ARG_UNUSED(fp_work_select_or_use); + ARG_UNUSED(mayfly_sched_offset); + ARG_UNUSED(fp_mayfly_select_or_use); #endif } @@ -4180,7 +4394,7 @@ static inline void event_conn_update_st_req(struct connection *conn, uint16_t event_counter, struct pdu_data *pdu_ctrl_tx, uint32_t ticks_at_expire, - struct work *work_sched_offset) + struct mayfly *mayfly_sched_offset) { /* move to wait for conn_update/rsp/rej */ conn->llcp.connection_update.state = LLCP_CONN_STATE_RSP_WAIT; @@ -4232,15 +4446,17 @@ static inline void event_conn_update_st_req(struct connection *conn, conn->llcp.connection_update.pdu_win_offset = (uint16_t *) &pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset0; - work_sched_offset->fp = work_sched_free_win_offset_calc; - work_sched_offset->params = (void *)conn; + mayfly_sched_offset->fp = mayfly_sched_free_win_offset_calc; + mayfly_sched_offset->param = (void *)conn; - retval = work_schedule(work_sched_offset, 1); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 1, + mayfly_sched_offset); LL_ASSERT(!retval); } #else ARG_UNUSED(ticks_at_expire); - ARG_UNUSED(work_sched_offset); + ARG_UNUSED(mayfly_sched_offset); #endif } @@ -4311,9 +4527,10 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, (conn->llcp.connection_update.state != LLCP_CONN_STATE_RSP_WAIT)) { #if SCHED_ADVANCED - static struct work gs_work_sched_offset = { - 0, 0, 0, WORK_TICKER_JOB0_IRQ, 0, 0 }; - work_fp fp_work_select_or_use; + static void *s_link[2]; + static struct mayfly s_mfy_sched_offset = {0, 0, + s_link, 0, 0 }; + void (*fp_mayfly_select_or_use)(void *); #endif struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; @@ -4327,15 +4544,15 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; #if SCHED_ADVANCED - fp_work_select_or_use = work_sched_win_offset_use; + fp_mayfly_select_or_use = mayfly_sched_win_offset_use; #endif state = conn->llcp.connection_update.state; if ((state == LLCP_CONN_STATE_RSP) && (conn->role.master.role == 0)) { state = LLCP_CONN_STATE_INITIATE; #if SCHED_ADVANCED - fp_work_select_or_use = - work_sched_win_offset_select; + fp_mayfly_select_or_use = + mayfly_sched_win_offset_select; #endif } @@ -4344,18 +4561,18 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, if (conn->role.master.role == 0) { #if SCHED_ADVANCED event_conn_update_st_init(conn, - event_counter, - pdu_ctrl_tx, - ticks_at_expire, - &gs_work_sched_offset, - fp_work_select_or_use); + event_counter, + pdu_ctrl_tx, + ticks_at_expire, + &s_mfy_sched_offset, + fp_mayfly_select_or_use); #else event_conn_update_st_init(conn, - event_counter, - pdu_ctrl_tx, - ticks_at_expire, - NULL, - NULL); + event_counter, + pdu_ctrl_tx, + ticks_at_expire, + NULL, + NULL); #endif break; } @@ -4367,7 +4584,7 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, event_counter, pdu_ctrl_tx, ticks_at_expire, - &gs_work_sched_offset); + &s_mfy_sched_offset); #else event_conn_update_st_req(conn, event_counter, @@ -4400,7 +4617,7 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, uint16_t conn_interval_old; uint16_t conn_interval_new; uint16_t latency; - uint32_t work_was_enabled; + uint32_t mayfly_was_enabled; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; @@ -4532,12 +4749,16 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, * 10 * 1000), conn_interval_us); conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); conn->appto_reload = (conn->apto_reload > (conn->latency + 2)) ? (conn->apto_reload - (conn->latency + 2)) : conn->apto_reload; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + if (!conn->llcp.connection_update.is_internal) { conn->supervision_expire = 0; } @@ -4545,8 +4766,11 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, /* disable ticker job, in order to chain stop and start * to avoid RTC being stopped if no tickers active. */ - work_was_enabled = work_is_enabled(WORK_TICKER_JOB0_IRQ); - work_disable(WORK_TICKER_JOB0_IRQ); + mayfly_was_enabled = + mayfly_is_enabled(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB); + mayfly_enable(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 0); /* start slave/master with new timings */ ticker_status = @@ -4575,8 +4799,9 @@ static inline uint32_t event_conn_update_prep(struct connection *conn, (ticker_status == TICKER_STATUS_BUSY)); /* enable ticker job, if disabled in this function */ - if (work_was_enabled) { - work_enable(WORK_TICKER_JOB0_IRQ); + if (mayfly_was_enabled) { + mayfly_enable(RADIO_TICKER_USER_ID_WORKER, + RADIO_TICKER_USER_ID_JOB, 1); } return 0; @@ -4702,13 +4927,14 @@ static inline void event_enc_prep(struct connection *conn) } /* place the start enc req packet as next in tx queue */ else { -#if !FAST_ENC_PROCEDURE + +#if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) /* TODO BT Spec. text: may finalize the sending * of additional data channel PDUs queued in the * controller. */ enc_rsp_send(conn); -#endif +#endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* calc the Session Key */ ecb_encrypt(&conn->llcp.encryption.ltk[0], @@ -4748,7 +4974,8 @@ static inline void event_enc_prep(struct connection *conn) PDU_DATA_LLCTRL_TYPE_START_ENC_REQ; } } else { -#if !FAST_ENC_PROCEDURE + +#if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) /* enable transmit encryption */ _radio.conn_curr->enc_tx = 1; @@ -4757,13 +4984,14 @@ static inline void event_enc_prep(struct connection *conn) /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; -#else +#else /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* Fast Enc implementation shall have enqueued the * start enc rsp in the radio ISR itself, we should * not get here. */ LL_ASSERT(0); -#endif +#endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ + } ctrl_tx_enqueue(conn, node_tx); @@ -4885,6 +5113,7 @@ static inline void event_vex_prep(struct connection *conn) } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static inline void event_ping_prep(struct connection *conn) { struct radio_pdu_node_tx *node_tx; @@ -4912,7 +5141,9 @@ static inline void event_ping_prep(struct connection *conn) } } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static inline void event_len_prep(struct connection *conn) { switch (conn->llcp_length.state) { @@ -5015,7 +5246,7 @@ static inline void event_len_prep(struct connection *conn) if (_radio.observer.conn) { free_count_conn++; } - packet_rx_data_size = ALIGN4(offsetof(struct radio_pdu_node_rx, + packet_rx_data_size = MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + offsetof(struct pdu_data, payload) + @@ -5058,9 +5289,9 @@ static inline void event_len_prep(struct connection *conn) /* calculate the new rx node size and new count */ if (conn->max_rx_octets < (RADIO_ACPDU_SIZE_MAX + 1)) { _radio.packet_rx_data_size = - ALIGN4(offsetof(struct radio_pdu_node_rx, - pdu_data) + - (RADIO_ACPDU_SIZE_MAX + 1)); + MROUND(offsetof(struct radio_pdu_node_rx, + pdu_data) + + (RADIO_ACPDU_SIZE_MAX + 1)); } else { _radio.packet_rx_data_size = packet_rx_data_size; @@ -5119,7 +5350,7 @@ static inline void event_len_prep(struct connection *conn) break; } } - +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static void event_connection_prepare(uint32_t ticks_at_expire, uint32_t remainder, uint16_t lazy, @@ -5177,9 +5408,11 @@ static void event_connection_prepare(uint32_t ticks_at_expire, event_vex_prep(conn); break; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case LLCP_PING: event_ping_prep(conn); break; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ default: LL_ASSERT(0); @@ -5223,6 +5456,7 @@ static void event_connection_prepare(uint32_t ticks_at_expire, } } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* check if procedure is requested */ if (conn->llcp_length.ack != conn->llcp_length.req) { /* Stop previous event, to avoid Radio DMA corrupting the @@ -5233,6 +5467,7 @@ static void event_connection_prepare(uint32_t ticks_at_expire, /* handle DLU state machine */ event_len_prep(conn); } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ /* Setup XTAL startup and radio active events */ event_common_prepare(ticks_at_expire, remainder, @@ -5308,7 +5543,10 @@ static void event_slave(uint32_t ticks_at_expire, uint32_t remainder, _radio.packet_rx[_radio.packet_rx_last]->pdu_data); radio_switch_complete_and_tx(); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) radio_rssi_measure(); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* Setup Radio Channel */ data_channel_use = channel_calc(&conn->data_channel_use, @@ -5356,8 +5594,8 @@ static void event_slave(uint32_t ticks_at_expire, uint32_t remainder, } else #endif - /* Ticker Job Silence */ -#if (WORK_TICKER_WORKER0_IRQ_PRIORITY == WORK_TICKER_JOB0_IRQ_PRIORITY) + /* Ticker Job Silence */ +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) { uint32_t ticker_status; @@ -5494,7 +5732,7 @@ static void event_master(uint32_t ticks_at_expire, uint32_t remainder, #endif /* Ticker Job Silence */ -#if (WORK_TICKER_WORKER0_IRQ_PRIORITY == WORK_TICKER_JOB0_IRQ_PRIORITY) +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) { uint32_t ticker_status; @@ -5513,15 +5751,22 @@ static void event_master(uint32_t ticks_at_expire, uint32_t remainder, static void rx_packet_set(struct connection *conn, struct pdu_data *pdu_data_rx) { uint8_t phy; + uint16_t max_rx_octets; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) + max_rx_octets = conn->max_rx_octets; +#else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; +#endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ phy = RADIO_PHY_CONN; if (conn->enc_rx) { - radio_pkt_configure(phy, 8, (conn->max_rx_octets + 4)); + radio_pkt_configure(phy, 8, (max_rx_octets + 4)); radio_pkt_rx_set(radio_ccm_rx_pkt_set(&conn->ccm_rx, pdu_data_rx)); } else { - radio_pkt_configure(phy, 8, conn->max_rx_octets); + radio_pkt_configure(phy, 8, max_rx_octets); radio_pkt_rx_set(pdu_data_rx); } @@ -5531,15 +5776,22 @@ static void tx_packet_set(struct connection *conn, struct pdu_data *pdu_data_tx) { uint8_t phy; + uint16_t max_tx_octets; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) + max_tx_octets = conn->max_tx_octets; +#else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; +#endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ phy = RADIO_PHY_CONN; if (conn->enc_tx) { - radio_pkt_configure(phy, 8, (conn->max_tx_octets + 4)); + radio_pkt_configure(phy, 8, (max_tx_octets + 4)); radio_pkt_tx_set(radio_ccm_tx_pkt_set(&conn->ccm_tx, pdu_data_tx)); } else { - radio_pkt_configure(phy, 8, conn->max_tx_octets); + radio_pkt_configure(phy, 8, max_tx_octets); radio_pkt_tx_set(pdu_data_tx); } @@ -5590,6 +5842,8 @@ static void prepare_pdu_data_tx(struct connection *conn, (_pdu_data_tx->payload.llctrl.opcode != PDU_DATA_LLCTRL_TYPE_REJECT_IND_EXT))))))) { _pdu_data_tx = empty_tx_enqueue(conn); } else { + uint16_t max_tx_octets; + _pdu_data_tx = (struct pdu_data *)(conn->pkt_tx_head->pdu_data + conn->packet_tx_head_offset); @@ -5606,8 +5860,14 @@ static void prepare_pdu_data_tx(struct connection *conn, conn->packet_tx_head_offset; _pdu_data_tx->md = 0; - if (_pdu_data_tx->len > conn->max_tx_octets) { - _pdu_data_tx->len = conn->max_tx_octets; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) + max_tx_octets = conn->max_tx_octets; +#else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; +#endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + + if (_pdu_data_tx->len > max_tx_octets) { + _pdu_data_tx->len = max_tx_octets; _pdu_data_tx->md = 1; } @@ -5664,6 +5924,7 @@ static void packet_rx_allocate(uint8_t max) } } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static uint8_t packet_rx_acquired_count_get(void) { if (_radio.packet_rx_acquire >= @@ -5676,6 +5937,7 @@ static uint8_t packet_rx_acquired_count_get(void) _radio.packet_rx_acquire); } } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static struct radio_pdu_node_rx *packet_rx_reserve_get(uint8_t count) { @@ -5700,12 +5962,22 @@ static struct radio_pdu_node_rx *packet_rx_reserve_get(uint8_t count) return radio_pdu_node_rx; } -static void event_callback(void) +static void packet_rx_callback(void) { - static struct work work_event_callback = { 0, 0, 0, - WORK_TICKER_JOB0_IRQ, (work_fp) radio_event_callback, 0 }; + /* Inline call of callback. If JOB configured as lower priority then + * callback will tailchain at end of every radio ISR. If JOB configured + * as same then call inline so as to have callback for every radio ISR. + */ +#if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) + radio_event_callback(); +#else + static void *s_link[2]; + static struct mayfly s_mfy_callback = {0, 0, s_link, 0, + (void *)radio_event_callback}; - work_schedule(&work_event_callback, 1); + mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, + &s_mfy_callback); +#endif } static void packet_rx_enqueue(void) @@ -5739,7 +6011,7 @@ static void packet_rx_enqueue(void) LL_ASSERT(link); /* callback to trigger application action */ - event_callback(); + packet_rx_callback(); } static void packet_tx_enqueue(uint8_t max) @@ -5892,7 +6164,7 @@ static void pdu_node_tx_release(uint16_t handle, _radio.packet_release_last = last; /* callback to trigger application action */ - event_callback(); + packet_rx_callback(); } static void connection_release(struct connection *conn) @@ -5902,7 +6174,7 @@ static void connection_release(struct connection *conn) /* Enable Ticker Job, we are in a radio event which disabled it if * worker0 and job0 priority where same. */ - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1); /** @todo correctly stop tickers ensuring crystal and radio active are * placed in right states @@ -6023,7 +6295,7 @@ static void terminate_ind_rx_enqueue(struct connection *conn, uint8_t reason) LL_ASSERT(link); /* callback to trigger application action */ - event_callback(); + packet_rx_callback(); } static uint32_t conn_update(struct connection *conn, @@ -6295,6 +6567,7 @@ static void version_ind_send(struct connection *conn) empty_tx_enqueue(conn); } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static void ping_resp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; @@ -6311,6 +6584,7 @@ static void ping_resp_send(struct connection *conn) ctrl_tx_enqueue(conn, node_tx); } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ static void reject_ind_ext_send(struct connection *conn, uint8_t reject_opcode, uint8_t error_code) @@ -6336,6 +6610,7 @@ static void reject_ind_ext_send(struct connection *conn, ctrl_tx_enqueue(conn, node_tx); } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static void length_resp_send(struct connection *conn, uint16_t eff_rx_octets, uint16_t eff_tx_octets) { @@ -6362,6 +6637,7 @@ static void length_resp_send(struct connection *conn, uint16_t eff_rx_octets, ctrl_tx_enqueue(conn, node_tx); } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ void radio_ticks_active_to_start_set(uint32_t ticks_active_to_start) { @@ -6463,8 +6739,9 @@ static inline void role_active_disable(uint8_t ticker_id_stop, uint32_t ticks_xtal_to_start, uint32_t ticks_active_to_start) { - static struct work s_work_radio_inactive = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_inactive, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_radio_inactive = {0, 0, s_link, 0, + mayfly_radio_inactive}; uint32_t volatile ticker_status_event; /* Step 2: Is caller before Event? Stop Event */ @@ -6474,14 +6751,16 @@ static inline void role_active_disable(uint8_t ticker_id_stop, ticker_if_done, (void *)&ticker_status_event); if (ticker_status_event == TICKER_STATUS_BUSY) { - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ticker_status_event != TICKER_STATUS_BUSY); } if (ticker_status_event == TICKER_STATUS_SUCCESS) { - static struct work s_work_xtal_stop = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, (work_fp) work_xtal_stop, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_xtal_stop = {0, 0, s_link, 0, + mayfly_xtal_stop}; uint32_t volatile ticker_status_pre_event; /* Step 2.1: Is caller between Primary and Marker0? @@ -6495,7 +6774,8 @@ static inline void role_active_disable(uint8_t ticker_id_stop, (void *)&ticker_status_pre_event); if (ticker_status_pre_event == TICKER_STATUS_BUSY) { - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ticker_status_event != TICKER_STATUS_BUSY); } @@ -6510,14 +6790,19 @@ static inline void role_active_disable(uint8_t ticker_id_stop, /* radio active asserted, handle deasserting * here */ - retval = work_schedule(&s_work_radio_inactive, - 0); + retval = mayfly_enqueue( + RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_inactive); LL_ASSERT(!retval); } else { uint32_t retval; /* XTAL started, handle XTAL stop here */ - retval = work_schedule(&s_work_xtal_stop, 0); + retval = mayfly_enqueue( + RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_xtal_stop); LL_ASSERT(!retval); } } else if (ticker_status_pre_event == TICKER_STATUS_FAILURE) { @@ -6526,11 +6811,15 @@ static inline void role_active_disable(uint8_t ticker_id_stop, /* Step 2.1.2: Deassert Radio Active and XTAL start */ /* radio active asserted, handle deasserting here */ - retval = work_schedule(&s_work_radio_inactive, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_inactive); LL_ASSERT(!retval); /* XTAL started, handle XTAL stop here */ - retval = work_schedule(&s_work_xtal_stop, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_xtal_stop); LL_ASSERT(!retval); } else { LL_ASSERT(0); @@ -6551,7 +6840,8 @@ static inline void role_active_disable(uint8_t ticker_id_stop, (void *)&ticker_status_stop); if (ticker_status_stop == TICKER_STATUS_BUSY) { - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ticker_status_event != TICKER_STATUS_BUSY); } @@ -6560,16 +6850,18 @@ static inline void role_active_disable(uint8_t ticker_id_stop, (ticker_status_stop == TICKER_STATUS_FAILURE)); if (_radio.role != ROLE_NONE) { - static struct work s_work_radio_stop = { 0, 0, 0, - WORK_TICKER_WORKER0_IRQ, - (work_fp) work_radio_stop, 0 }; + static void *s_link[2]; + static struct mayfly s_mfy_radio_stop = {0, 0, s_link, + 0, mayfly_radio_stop}; uint32_t retval; /* Radio state STOP is supplied in params */ - s_work_radio_stop.params = (void *)STATE_STOP; + s_mfy_radio_stop.param = (void *)STATE_STOP; /* Stop Radio Tx/Rx */ - retval = work_schedule(&s_work_radio_stop, 0); + retval = mayfly_enqueue(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_WORKER, 0, + &s_mfy_radio_stop); LL_ASSERT(!retval); /* wait for radio ISR to exit */ @@ -6636,7 +6928,8 @@ static uint32_t role_disable(uint8_t ticker_id_primary, if (ticker_status == TICKER_STATUS_BUSY) { /* if inside our event, enable Job. */ if (_radio.ticker_id_event == ticker_id_primary) { - work_enable(WORK_TICKER_JOB0_IRQ); + mayfly_enable(RADIO_TICKER_USER_ID_JOB, + RADIO_TICKER_USER_ID_JOB, 1); } /** @todo design to avoid this wait */ @@ -6697,9 +6990,13 @@ uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map, conn->event_counter = 0; conn->latency_prepare = 0; conn->latency_event = 0; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->default_tx_octets = _radio.default_tx_octets; conn->max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; conn->max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + conn->role.slave.role = 1; conn->role.slave.latency_cancel = 0; conn->role.slave.window_widening_prepare_us = 0; @@ -6707,8 +7004,12 @@ uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map, conn->role.slave.ticks_to_offset = 0; conn->supervision_expire = 6; conn->procedure_expire = 0; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) conn->apto_expire = 0; conn->appto_expire = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + conn->llcp_req = 0; conn->llcp_ack = 0; conn->llcp_version.tx = 0; @@ -6717,8 +7018,12 @@ uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map, conn->llcp_terminate.ack = 0; conn->llcp_terminate.reason_peer = 0; conn->llcp_terminate.radio_pdu_node_rx.hdr.onion.link = link; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->llcp_length.req = 0; conn->llcp_length.ack = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + conn->sn = 0; conn->nesn = 0; conn->pause_rx = 0; @@ -6733,9 +7038,12 @@ uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map, conn->pkt_tx_last = NULL; conn->packet_tx_head_len = 0; conn->packet_tx_head_offset = 0; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) conn->rssi_latest = 0x7F; conn->rssi_reported = 0x7F; conn->rssi_sample_count = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ _radio.advertiser.conn = conn; } else { @@ -6882,7 +7190,7 @@ uint32_t radio_scan_enable(uint8_t scan_type, uint8_t init_addr_type, _radio.filter_addr_type_bitmask; memcpy(&_radio.observer.filter_bdaddr[0][0], &_radio.filter_bdaddr[0][0], - sizeof(_radio.advertiser.filter_bdaddr)); + sizeof(_radio.observer.filter_bdaddr)); _radio.observer.filter_enable_bitmask = _radio.filter_enable_bitmask; } @@ -6917,10 +7225,10 @@ uint32_t radio_scan_enable(uint8_t scan_type, uint8_t init_addr_type, } #if SCHED_ADVANCED else { - sched_after_master_free_slot_get(RADIO_TICKER_USER_ID_APP, - (ticks_slot_offset + - _radio.observer.hdr.ticks_slot), - &ticks_anchor, &us_offset); + sched_after_mstr_free_slot_get(RADIO_TICKER_USER_ID_APP, + (ticks_slot_offset + + _radio.observer.hdr.ticks_slot), + &ticks_anchor, &us_offset); } #endif @@ -7018,9 +7326,13 @@ uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr, conn->latency_prepare = 0; conn->latency_event = 0; conn->latency = _radio.observer.conn_latency; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->default_tx_octets = _radio.default_tx_octets; conn->max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; conn->max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + conn->role.master.role = 0; conn->role.master.connect_expire = 6; conn_interval_us = @@ -7032,12 +7344,16 @@ uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr, conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); conn->procedure_expire = 0; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); conn->apto_expire = 0; conn->appto_reload = (conn->apto_reload > (conn->latency + 2)) ? (conn->apto_reload - (conn->latency + 2)) : conn->apto_reload; conn->appto_expire = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + conn->llcp_req = 0; conn->llcp_ack = 0; conn->llcp_version.tx = 0; @@ -7046,8 +7362,12 @@ uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr, conn->llcp_terminate.ack = 0; conn->llcp_terminate.reason_peer = 0; conn->llcp_terminate.radio_pdu_node_rx.hdr.onion.link = link; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->llcp_length.req = 0; conn->llcp_length.ack = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + conn->sn = 0; conn->nesn = 0; conn->pause_rx = 0; @@ -7062,9 +7382,12 @@ uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr, conn->pkt_tx_last = NULL; conn->packet_tx_head_len = 0; conn->packet_tx_head_offset = 0; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) conn->rssi_latest = 0x7F; conn->rssi_reported = 0x7F; conn->rssi_sample_count = 0; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ _radio.observer.conn = conn; @@ -7339,6 +7662,7 @@ uint32_t radio_terminate_ind_send(uint16_t handle, uint8_t reason) return 0; } +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) uint32_t radio_length_req_send(uint16_t handle, uint16_t tx_octets) { struct connection *conn; @@ -7349,6 +7673,8 @@ uint32_t radio_length_req_send(uint16_t handle, uint16_t tx_octets) return 1; } + /* TODO: parameter check tx_octets */ + conn->llcp_length.state = LLCP_LENGTH_STATE_REQ; conn->llcp_length.tx_octets = tx_octets; conn->llcp_length.req++; @@ -7364,11 +7690,7 @@ void radio_length_default_get(uint16_t *max_tx_octets, uint16_t *max_tx_time) uint32_t radio_length_default_set(uint16_t max_tx_octets, uint16_t max_tx_time) { - if (max_tx_octets > RADIO_LL_LENGTH_OCTETS_RX_MAX || - max_tx_time > RADIO_LL_LENGTH_TIME_RX_MAX) { - - return 1; - } + /* TODO: parameter check (for BT 5.0 compliance) */ _radio.default_tx_octets = max_tx_octets; _radio.default_tx_time = max_tx_time; @@ -7384,6 +7706,7 @@ void radio_length_max_get(uint16_t *max_tx_octets, uint16_t *max_tx_time, *max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MAX; *max_rx_time = RADIO_LL_LENGTH_TIME_RX_MAX; } +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static uint8_t tx_cmplt_get(uint16_t *handle, uint8_t *first, uint8_t last) { @@ -7496,13 +7819,23 @@ void radio_rx_dequeue(void) switch (radio_pdu_node_rx->hdr.type) { case NODE_RX_TYPE_DC_PDU: - case NODE_RX_TYPE_PROFILE: case NODE_RX_TYPE_REPORT: case NODE_RX_TYPE_CONNECTION: case NODE_RX_TYPE_CONN_UPDATE: case NODE_RX_TYPE_ENC_REFRESH: + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case NODE_RX_TYPE_APTO: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) case NODE_RX_TYPE_RSSI: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + case NODE_RX_TYPE_PROFILE: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + /* release data link credit quota */ LL_ASSERT(_radio.link_rx_data_quota < (_radio.packet_rx_count - 1)); @@ -7534,13 +7867,23 @@ void radio_rx_mem_release(struct radio_pdu_node_rx **radio_pdu_node_rx) switch (_radio_pdu_node_rx_free->hdr.type) { case NODE_RX_TYPE_DC_PDU: - case NODE_RX_TYPE_PROFILE: case NODE_RX_TYPE_REPORT: case NODE_RX_TYPE_CONNECTION: case NODE_RX_TYPE_CONN_UPDATE: case NODE_RX_TYPE_ENC_REFRESH: + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case NODE_RX_TYPE_APTO: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) case NODE_RX_TYPE_RSSI: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + case NODE_RX_TYPE_PROFILE: +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ + mem_release(_radio_pdu_node_rx_free, &_radio.pkt_rx_data_free); break; @@ -7683,11 +8026,15 @@ uint32_t radio_tx_mem_enqueue(uint16_t handle, pdu_data = (struct pdu_data *)node_tx->pdu_data; conn = connection_get(handle); - if ((last == _radio.packet_tx_first) || (conn == 0) || - (pdu_data->len > _radio.packet_data_octets_max)) { + if (!conn || (last == _radio.packet_tx_first)) { return 1; } + LL_ASSERT(pdu_data->len <= (_radio.packet_tx_data_size - + offsetof(struct radio_pdu_node_tx, + pdu_data) - + offsetof(struct pdu_data, payload))); + _radio.pkt_tx[_radio.packet_tx_last].handle = handle; _radio.pkt_tx[_radio.packet_tx_last]. node_tx = node_tx; _radio.packet_tx_last = last; diff --git a/subsys/bluetooth/controller/ll/ctrl.h b/subsys/bluetooth/controller/ll/ctrl.h index b435d32b6164..339c06cb28da 100644 --- a/subsys/bluetooth/controller/ll/ctrl.h +++ b/subsys/bluetooth/controller/ll/ctrl.h @@ -18,8 +18,6 @@ #ifndef _CTRL_H_ #define _CTRL_H_ -#include - /***************************************************************************** * Zephyr Kconfig defined ****************************************************************************/ @@ -29,11 +27,6 @@ #define RADIO_CONNECTION_CONTEXT_MAX 0 #endif -#ifdef CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH -#define RADIO_LL_LENGTH_OCTETS_RX_MAX \ - CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH -#endif - #ifdef CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS #define RADIO_PACKET_COUNT_RX_MAX \ CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS @@ -44,6 +37,26 @@ CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS #endif +#ifdef CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFER_SIZE +#define RADIO_PACKET_TX_DATA_SIZE \ + CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFER_SIZE +#endif + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) +#define RADIO_BLE_FEATURES_BIT_PING BIT(BT_LE_FEAT_BIT_PING) +#else /* !CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ +#define RADIO_BLE_FEATURES_BIT_PING 0 +#endif /* !CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH_MAX) +#define RADIO_BLE_FEATURES_BIT_DLE BIT(BT_LE_FEAT_BIT_DLE) +#define RADIO_LL_LENGTH_OCTETS_RX_MAX \ + CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH_MAX +#else +#define RADIO_BLE_FEATURES_BIT_DLE 0 +#define RADIO_LL_LENGTH_OCTETS_RX_MAX 27 +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH_MAX */ + /***************************************************************************** * Timer Resources (Controller defined) ****************************************************************************/ @@ -61,9 +74,12 @@ #define RADIO_TICKER_USERS 3 -#define RADIO_TICKER_USER_ID_WORKER 0 -#define RADIO_TICKER_USER_ID_JOB 1 -#define RADIO_TICKER_USER_ID_APP 2 +#define RADIO_TICKER_USER_ID_WORKER TICKER_MAYFLY_CALL_ID_WORKER0 +#define RADIO_TICKER_USER_ID_JOB TICKER_MAYFLY_CALL_ID_JOB0 +#define RADIO_TICKER_USER_ID_APP TICKER_MAYFLY_CALL_ID_PROGRAM + +#define RADIO_TICKER_USER_ID_WORKER_PRIO TICKER_MAYFLY_CALL_ID_WORKER0_PRIO +#define RADIO_TICKER_USER_ID_JOB_PRIO TICKER_MAYFLY_CALL_ID_JOB0_PRIO #define RADIO_TICKER_USER_WORKER_OPS (7 + 1) #define RADIO_TICKER_USER_JOB_OPS (2 + 1) @@ -88,8 +104,8 @@ BIT(BT_LE_FEAT_BIT_CONN_PARAM_REQ) | \ BIT(BT_LE_FEAT_BIT_EXT_REJ_IND) | \ BIT(BT_LE_FEAT_BIT_SLAVE_FEAT_REQ) | \ - BIT(BT_LE_FEAT_BIT_PING) | \ - BIT(BT_LE_FEAT_BIT_DLE)) + RADIO_BLE_FEATURES_BIT_PING | \ + RADIO_BLE_FEATURES_BIT_DLE) /***************************************************************************** * Controller Reference Defines (compile time override-able) @@ -142,6 +158,12 @@ #define RADIO_PACKET_COUNT_APP_TX_MAX (RADIO_PACKET_COUNT_TX_MAX) #endif +/* Tx Data Size */ +#if !defined(RADIO_PACKET_TX_DATA_SIZE) || \ + (RADIO_PACKET_TX_DATA_SIZE < RADIO_LL_LENGTH_OCTETS_RX_MIN) +#define RADIO_PACKET_TX_DATA_SIZE RADIO_LL_LENGTH_OCTETS_RX_MIN +#endif + /***************************************************************************** * Controller Interface Structures ****************************************************************************/ @@ -180,24 +202,34 @@ struct radio_pdu_node_tx { enum radio_pdu_node_rx_type { NODE_RX_TYPE_NONE, NODE_RX_TYPE_DC_PDU, - NODE_RX_TYPE_PROFILE, NODE_RX_TYPE_REPORT, NODE_RX_TYPE_CONNECTION, NODE_RX_TYPE_TERMINATE, NODE_RX_TYPE_CONN_UPDATE, NODE_RX_TYPE_ENC_REFRESH, + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) NODE_RX_TYPE_APTO, +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) NODE_RX_TYPE_RSSI, +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) + NODE_RX_TYPE_PROFILE, +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ }; struct radio_pdu_node_rx_hdr { - enum radio_pdu_node_rx_type type; - uint16_t handle; union { - void *next; + void *next; /* used also by k_fifo once pulled */ void *link; uint8_t packet_release_last; } onion; + + enum radio_pdu_node_rx_type type; + uint16_t handle; }; struct radio_pdu_node_rx { @@ -210,7 +242,8 @@ struct radio_pdu_node_rx { ****************************************************************************/ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max, uint8_t rx_count_max, uint8_t tx_count_max, - uint16_t data_octets_max, uint8_t *mem_radio, + uint16_t packet_data_octets_max, + uint16_t packet_tx_data_size, uint8_t *mem_radio, uint16_t mem_size); void ctrl_reset(void); void radio_ticks_active_to_start_set(uint32_t ticks_active_to_start); @@ -244,16 +277,21 @@ uint32_t radio_start_enc_req_send(uint16_t handle, uint8_t err_code, uint32_t radio_feature_req_send(uint16_t handle); uint32_t radio_version_ind_send(uint16_t handle); uint32_t radio_terminate_ind_send(uint16_t handle, uint8_t reason); + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) uint32_t radio_length_req_send(uint16_t handle, uint16_t tx_octets); void radio_length_default_get(uint16_t *max_tx_octets, uint16_t *max_tx_time); uint32_t radio_length_default_set(uint16_t max_tx_octets, uint16_t max_tx_time); void radio_length_max_get(uint16_t *max_tx_octets, uint16_t *max_tx_time, uint16_t *max_rx_octets, uint16_t *max_rx_time); +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + uint8_t radio_rx_get(struct radio_pdu_node_rx **radio_pdu_node_rx, uint16_t *handle); void radio_rx_dequeue(void); void radio_rx_mem_release(struct radio_pdu_node_rx **radio_pdu_node_rx); uint8_t radio_rx_fc_set(uint16_t handle, uint8_t fc); +uint8_t radio_rx_fc_get(uint16_t *handle); struct radio_pdu_node_tx *radio_tx_mem_acquire(void); void radio_tx_mem_release(struct radio_pdu_node_tx *pdu_data_node_tx); uint32_t radio_tx_mem_enqueue(uint16_t handle, diff --git a/subsys/bluetooth/controller/ll/ctrl_internal.h b/subsys/bluetooth/controller/ll/ctrl_internal.h index fa55e5d5b0d5..3a41c4a110d8 100644 --- a/subsys/bluetooth/controller/ll/ctrl_internal.h +++ b/subsys/bluetooth/controller/ll/ctrl_internal.h @@ -15,11 +15,6 @@ * limitations under the License. */ -#include -#include "util/defines.h" -#include "pdu.h" -#include "ctrl.h" - enum llcp { LLCP_NONE, LLCP_CONNECTION_UPDATE, @@ -28,8 +23,10 @@ enum llcp { LLCP_FEATURE_EXCHANGE, LLCP_VERSION_EXCHANGE, /* LLCP_TERMINATE, */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) LLCP_PING, - /* LLCP_LENGTH, */ +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ }; @@ -55,17 +52,24 @@ struct connection { uint16_t latency; uint16_t latency_prepare; uint16_t latency_event; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) uint16_t default_tx_octets; uint16_t max_tx_octets; uint16_t max_rx_octets; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ + uint16_t supervision_reload; uint16_t supervision_expire; uint16_t procedure_reload; uint16_t procedure_expire; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) uint16_t appto_reload; uint16_t appto_expire; uint16_t apto_reload; uint16_t apto_expire; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ union { struct { @@ -156,6 +160,7 @@ struct connection { } radio_pdu_node_rx; } llcp_terminate; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) struct { uint8_t req; uint8_t ack; @@ -167,6 +172,7 @@ struct connection { uint16_t rx_octets; uint16_t tx_octets; } llcp_length; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ uint8_t sn:1; uint8_t nesn:1; @@ -187,11 +193,13 @@ struct connection { uint8_t packet_tx_head_len; uint8_t packet_tx_head_offset; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) uint8_t rssi_latest; uint8_t rssi_reported; uint8_t rssi_sample_count; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ }; -#define CONNECTION_T_SIZE ALIGN4(sizeof(struct connection)) +#define CONNECTION_T_SIZE MROUND(sizeof(struct connection)) struct pdu_data_q_tx { uint16_t handle; @@ -200,12 +208,12 @@ struct pdu_data_q_tx { /* Minimum Rx Data allocation size */ #define PACKET_RX_DATA_SIZE_MIN \ - ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) + \ + MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + \ (RADIO_ACPDU_SIZE_MAX + 1)) /* Minimum Tx Ctrl allocation size */ #define PACKET_TX_CTRL_SIZE_MIN \ - ALIGN4(offsetof(struct radio_pdu_node_tx, pdu_data) + \ + MROUND(offsetof(struct radio_pdu_node_tx, pdu_data) + \ offsetof(struct pdu_data, payload) + 27) /** @todo fix starvation when ctrl rx in radio ISR @@ -219,7 +227,7 @@ struct pdu_data_q_tx { #define LL_MEM_TXQ (sizeof(struct pdu_data_q_tx) * \ (RADIO_PACKET_COUNT_TX_MAX + 2)) -#define LL_MEM_RX_POOL_SZ (ALIGN4(offsetof(struct radio_pdu_node_rx,\ +#define LL_MEM_RX_POOL_SZ (MROUND(offsetof(struct radio_pdu_node_rx,\ pdu_data) + ((\ (RADIO_ACPDU_SIZE_MAX + 1) < \ (offsetof(struct pdu_data, payload) + \ @@ -234,10 +242,10 @@ struct pdu_data_q_tx { 4) + RADIO_CONNECTION_CONTEXT_MAX)) #define LL_MEM_TX_CTRL_POOL (PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL) -#define LL_MEM_TX_DATA_POOL ((ALIGN4(offsetof( \ +#define LL_MEM_TX_DATA_POOL ((MROUND(offsetof( \ struct radio_pdu_node_tx, pdu_data) + \ offsetof(struct pdu_data, payload) + \ - RADIO_LL_LENGTH_OCTETS_RX_MAX)) \ + RADIO_PACKET_TX_DATA_SIZE)) \ * (RADIO_PACKET_COUNT_TX_MAX + 1)) #define LL_MEM_TOTAL (LL_MEM_CONN + LL_MEM_RXQ + (LL_MEM_TXQ * 2) + \ diff --git a/subsys/bluetooth/controller/ll/ll.c b/subsys/bluetooth/controller/ll/ll.c index 444783bb7967..5b335c05fe49 100644 --- a/subsys/bluetooth/controller/ll/ll.c +++ b/subsys/bluetooth/controller/ll/ll.c @@ -17,14 +17,18 @@ #include #include -#include "defines.h" -#include "mem.h" -#include "ticker.h" + +#include + #include "ccm.h" #include "radio.h" + +#include "mem.h" +#include "util.h" +#include "ticker.h" + #include "pdu.h" #include "ctrl.h" - #include "ll.h" #include "debug.h" diff --git a/subsys/bluetooth/controller/ll/pdu.h b/subsys/bluetooth/controller/ll/pdu.h index c44599a3c0e2..3e8ea8e86a37 100644 --- a/subsys/bluetooth/controller/ll/pdu.h +++ b/subsys/bluetooth/controller/ll/pdu.h @@ -18,7 +18,7 @@ #ifndef _PDU_H_ #define _PDU_H_ -#include +#define BDADDR_SIZE 6 struct pdu_adv_payload_adv_ind { uint8_t addr[BDADDR_SIZE]; @@ -232,11 +232,16 @@ struct pdu_data_llctrl { } __packed ctrldata; } __packed; +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) struct profile { - uint32_t min; - uint32_t avg; - uint32_t max; + uint8_t lcur; + uint8_t lmin; + uint8_t lmax; + uint8_t cur; + uint8_t min; + uint8_t max; } __packed; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ struct pdu_data { uint8_t ll_id:2; @@ -252,8 +257,14 @@ struct pdu_data { union { uint8_t lldata[1]; struct pdu_data_llctrl llctrl; + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) uint8_t rssi; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ + +#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) struct profile profile; +#endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ } __packed payload; } __packed; diff --git a/subsys/bluetooth/controller/ll/ticker.c b/subsys/bluetooth/controller/ticker/ticker.c similarity index 81% rename from subsys/bluetooth/controller/ll/ticker.c rename to subsys/bluetooth/controller/ticker/ticker.c index 2ba98ef7e29c..bcceb694a581 100644 --- a/subsys/bluetooth/controller/ll/ticker.c +++ b/subsys/bluetooth/controller/ticker/ticker.c @@ -17,26 +17,29 @@ #include -#include "hal_rtc.h" -#include "hal_work.h" -#include "work.h" - +#include "cntr.h" #include "ticker.h" +#include "config.h" + +#include +#include #include "debug.h" /***************************************************************************** * Defines ****************************************************************************/ -#define TICKER_DOUBLE_BUFFER_SIZE 2 -#define TICKER_RTC_CC_OFFSET_MIN 3 +#define DOUBLE_BUFFER_SIZE 2 +#define COUNTER_CMP_OFFSET_MIN 3 + +#define CALL_ID_TRIGGER 0 +#define CALL_ID_WORKER 1 +#define CALL_ID_JOB 2 +#define CALL_ID_USER 3 /***************************************************************************** * Types ****************************************************************************/ -typedef void (*ticker_fp_sched) (uint8_t chain); -typedef void (*ticker_fp_compare_set) (uint32_t cc); - struct ticker_node { uint8_t next; @@ -119,16 +122,16 @@ struct ticker_instance { uint8_t count_user; uint8_t ticks_elapsed_first; uint8_t ticks_elapsed_last; - uint32_t ticks_elapsed[TICKER_DOUBLE_BUFFER_SIZE]; + uint32_t ticks_elapsed[DOUBLE_BUFFER_SIZE]; uint32_t ticks_current; uint8_t ticker_id_head; uint8_t ticker_id_slot_previous; uint16_t ticks_slot_previous; uint8_t job_guard; uint8_t worker_trigger; - ticker_fp_sched fp_worker_sched; - ticker_fp_sched fp_job_sched; - ticker_fp_compare_set fp_compare_set; + uint8_t (*fp_caller_id_get)(uint8_t user_id); + void (*fp_sched)(uint8_t caller_id, uint8_t callee_id, uint8_t chain); + void (*fp_cmp_set)(uint32_t value); }; /***************************************************************************** @@ -356,8 +359,8 @@ static inline void ticker_worker(struct ticker_instance *instance) } /* ticks_elapsed is collected here, job will use it */ - ticks_elapsed = - ticker_ticks_diff_get(rtc_tick_get(), instance->ticks_current); + ticks_elapsed = ticker_ticks_diff_get(cntr_cnt_get(), + instance->ticks_current); /* initialise actual elapsed ticks being consumed */ ticks_expired = 0; @@ -413,7 +416,7 @@ static inline void ticker_worker(struct ticker_instance *instance) uint8_t last; last = instance->ticks_elapsed_last + 1; - if (last == TICKER_DOUBLE_BUFFER_SIZE) { + if (last == DOUBLE_BUFFER_SIZE) { last = 0; } instance->ticks_elapsed_last = last; @@ -423,7 +426,7 @@ static inline void ticker_worker(struct ticker_instance *instance) instance->worker_trigger = 0; - instance->fp_job_sched(1); + instance->fp_sched(CALL_ID_WORKER, CALL_ID_JOB, 1); } static void prepare_ticks_to_expire(struct ticker_node *ticker, @@ -498,7 +501,7 @@ static inline void ticker_job_node_update(struct ticker_node *ticker, uint32_t ticks_now; uint32_t ticks_to_expire = ticker->ticks_to_expire; - ticks_now = rtc_tick_get(); + ticks_now = cntr_cnt_get(); ticks_elapsed += ticker_ticks_diff_get(ticks_now, ticks_current); if (ticks_to_expire > ticks_elapsed) { ticks_to_expire -= ticks_elapsed; @@ -685,7 +688,8 @@ static inline uint8_t ticker_job_list_manage( */ /* sched job to run after worker bottom half. */ - instance->fp_job_sched(1); + instance->fp_sched(CALL_ID_JOB, + CALL_ID_JOB, 1); /* Update the index upto which management is * complete. @@ -1023,7 +1027,7 @@ static inline void ticker_job_compare_update( uint32_t i; if (instance->ticker_id_head == TICKER_NULL) { - if (rtc_stop() == 0) { + if (cntr_stop() == 0) { instance->ticks_slot_previous = 0; } @@ -1033,9 +1037,9 @@ static inline void ticker_job_compare_update( if (ticker_id_old_head == TICKER_NULL) { uint32_t ticks_current; - ticks_current = rtc_tick_get(); + ticks_current = cntr_cnt_get(); - if (rtc_start() == 0) { + if (cntr_start() == 0) { instance->ticks_current = ticks_current; } } @@ -1056,19 +1060,19 @@ static inline void ticker_job_compare_update( LL_ASSERT(i); i--; - ctr = rtc_tick_get(); + ctr = cntr_cnt_get(); cc = instance->ticks_current; ticks_elapsed = ticker_ticks_diff_get(ctr, cc) + - TICKER_RTC_CC_OFFSET_MIN; + COUNTER_CMP_OFFSET_MIN; cc += ((ticks_elapsed < ticks_to_expire) ? ticks_to_expire : ticks_elapsed); cc &= 0x00FFFFFF; - instance->fp_compare_set(cc); + instance->fp_cmp_set(cc); - ctr_post = rtc_tick_get(); + ctr_post = cntr_cnt_get(); } while ((ticker_ticks_diff_get(ctr_post, ctr) + - TICKER_RTC_CC_OFFSET_MIN) > ticker_ticks_diff_get(cc, ctr)); + COUNTER_CMP_OFFSET_MIN) > ticker_ticks_diff_get(cc, ctr)); } static inline void ticker_job(struct ticker_instance *instance) @@ -1099,7 +1103,7 @@ static inline void ticker_job(struct ticker_instance *instance) uint8_t first; first = instance->ticks_elapsed_first + 1; - if (first == TICKER_DOUBLE_BUFFER_SIZE) { + if (first == DOUBLE_BUFFER_SIZE) { first = 0; } instance->ticks_elapsed_first = first; @@ -1172,79 +1176,341 @@ static inline void ticker_job(struct ticker_instance *instance) /* trigger worker if deferred */ if (instance->worker_trigger) { - instance->fp_worker_sched(1); + instance->fp_sched(CALL_ID_JOB, CALL_ID_WORKER, 1); } DEBUG_TICKER_JOB(0); } /***************************************************************************** - * Instances Work Helpers + * Instances Helpers + * + * TODO: decouple it from using work/mayfly in this file and dynamically + * import it. ****************************************************************************/ -static void ticker_instance0_worker_sched(uint8_t chain) +#include "mayfly.h" +#include "config.h" + +static uint8_t ticker_instance0_caller_id_get(uint8_t user_id) { - static struct work instance0_worker_irq = { - 0, 0, 0, WORK_TICKER_WORKER0_IRQ, (work_fp) ticker_worker, - &_instance[0] - }; + if (user_id == TICKER_MAYFLY_CALL_ID_PROGRAM) { + return CALL_ID_USER; + } else if (user_id == TICKER_MAYFLY_CALL_ID_JOB0) { + return CALL_ID_JOB; + } else if (user_id == TICKER_MAYFLY_CALL_ID_WORKER0) { + return CALL_ID_WORKER; + } else if (user_id == TICKER_MAYFLY_CALL_ID_TRIGGER) { + return CALL_ID_TRIGGER; + } - /* return value not checked as we allow multiple calls to schedule - * before being actually needing the work to complete before new - * schedule. - */ - work_schedule(&instance0_worker_irq, chain); + LL_ASSERT(0); + + return 0; } -static void ticker_instance0_job_sched(uint8_t chain) +static uint8_t ticker_instance1_caller_id_get(uint8_t user_id) { - static struct work instance0_job_irq = { - 0, 0, 0, WORK_TICKER_JOB0_IRQ, (work_fp) ticker_job, - &_instance[0] - }; + if (user_id == TICKER_MAYFLY_CALL_ID_PROGRAM) { + return CALL_ID_USER; + } else if (user_id == TICKER_MAYFLY_CALL_ID_JOB1) { + return CALL_ID_JOB; + } else if (user_id == TICKER_MAYFLY_CALL_ID_WORKER1) { + return CALL_ID_WORKER; + } else if (user_id == TICKER_MAYFLY_CALL_ID_TRIGGER) { + return CALL_ID_TRIGGER; + } - /* return value not checked as we allow multiple calls to schedule - * before being actually needing the work to complete before new - * schedule. - */ - work_schedule(&instance0_job_irq, chain); -} + LL_ASSERT(0); -static void ticker_instance0_rtc_compare_set(uint32_t value) -{ - rtc_compare_set(0, value); + return 0; } -static void ticker_instance1_worker_sched(uint8_t chain) +static void ticker_instance0_sched(uint8_t caller_id, uint8_t callee_id, + uint8_t chain) { - static struct work instance1_worker_irq = { - 0, 0, 0, WORK_TICKER_WORKER1_IRQ, (work_fp) ticker_worker, - &_instance[1] - }; - /* return value not checked as we allow multiple calls to schedule * before being actually needing the work to complete before new * schedule. */ - work_schedule(&instance1_worker_irq, chain); + switch (caller_id) { + case CALL_ID_TRIGGER: + switch (callee_id) { + case CALL_ID_WORKER: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + &_instance[0], + (void *)ticker_worker + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_TRIGGER, + TICKER_MAYFLY_CALL_ID_WORKER0, + chain, + &m); + } + break; + + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[0], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_TRIGGER, + TICKER_MAYFLY_CALL_ID_JOB0, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + case CALL_ID_WORKER: + switch (callee_id) { + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[0], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_WORKER0, + TICKER_MAYFLY_CALL_ID_JOB0, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + case CALL_ID_JOB: + switch (callee_id) { + case CALL_ID_WORKER: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[0], + (void *)ticker_worker + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_JOB0, + TICKER_MAYFLY_CALL_ID_WORKER0, + chain, + &m); + } + break; + + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[0], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_JOB0, + TICKER_MAYFLY_CALL_ID_JOB0, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + default: + switch (callee_id) { + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[0], + (void *)ticker_job + }; + + /* TODO: scheduler lock, if OS used */ + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_PROGRAM, + TICKER_MAYFLY_CALL_ID_JOB0, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + } } -static void ticker_instance1_job_sched(uint8_t chain) +static void ticker_instance1_sched(uint8_t caller_id, uint8_t callee_id, + uint8_t chain) { - static struct work instance1_job_irq = { - 0, 0, 0, WORK_TICKER_JOB1_IRQ, (work_fp) ticker_job, - &_instance[1] - }; - /* return value not checked as we allow multiple calls to schedule * before being actually needing the work to complete before new * schedule. */ - work_schedule(&instance1_job_irq, chain); + switch (caller_id) { + case CALL_ID_TRIGGER: + switch (callee_id) { + case CALL_ID_WORKER: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + &_instance[1], + (void *)ticker_worker + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_TRIGGER, + TICKER_MAYFLY_CALL_ID_WORKER1, + chain, + &m); + } + break; + + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[1], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_TRIGGER, + TICKER_MAYFLY_CALL_ID_JOB1, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + case CALL_ID_WORKER: + switch (callee_id) { + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[1], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_WORKER1, + TICKER_MAYFLY_CALL_ID_JOB1, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + case CALL_ID_JOB: + switch (callee_id) { + case CALL_ID_WORKER: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[1], + (void *)ticker_worker + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_JOB1, + TICKER_MAYFLY_CALL_ID_WORKER1, + chain, + &m); + } + break; + + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[1], + (void *)ticker_job + }; + + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_JOB1, + TICKER_MAYFLY_CALL_ID_JOB1, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + + default: + switch (callee_id) { + case CALL_ID_JOB: + { + static void *link[2]; + static struct mayfly m = { + 0, 0, link, + (void *)&_instance[1], + (void *)ticker_job + }; + + /* TODO: scheduler lock, if OS used */ + mayfly_enqueue(TICKER_MAYFLY_CALL_ID_PROGRAM, + TICKER_MAYFLY_CALL_ID_JOB1, + chain, + &m); + } + break; + + default: + LL_ASSERT(0); + break; + } + break; + } +} + +static void ticker_instance0_cmp_set(uint32_t value) +{ + cntr_cmp_set(0, value); } -static void ticker_instance1_rtc_compare_set(uint32_t value) +static void ticker_instance1_cmp_set(uint32_t value) { - rtc_compare_set(1, value); + cntr_cmp_set(1, value); } /***************************************************************************** @@ -1268,21 +1534,15 @@ uint32_t ticker_init(uint8_t instance_index, uint8_t count_node, void *node, switch (instance_index) { case 0: - instance->fp_worker_sched = - ticker_instance0_worker_sched; - instance->fp_job_sched = - ticker_instance0_job_sched; - instance->fp_compare_set = - ticker_instance0_rtc_compare_set; + instance->fp_caller_id_get = ticker_instance0_caller_id_get; + instance->fp_sched = ticker_instance0_sched; + instance->fp_cmp_set = ticker_instance0_cmp_set; break; case 1: - instance->fp_worker_sched = - ticker_instance1_worker_sched; - instance->fp_job_sched = - ticker_instance1_job_sched; - instance->fp_compare_set = - ticker_instance1_rtc_compare_set; + instance->fp_caller_id_get = ticker_instance1_caller_id_get; + instance->fp_sched = ticker_instance1_sched; + instance->fp_cmp_set = ticker_instance1_cmp_set; break; default: @@ -1320,9 +1580,14 @@ uint32_t ticker_init(uint8_t instance_index, uint8_t count_node, void *node, void ticker_trigger(uint8_t instance_index) { - if (_instance[instance_index].fp_worker_sched) { - _instance[instance_index].fp_worker_sched(1); + DEBUG_TICKER_ISR(1); + + if (_instance[instance_index].fp_sched) { + _instance[instance_index].fp_sched(CALL_ID_TRIGGER, + CALL_ID_WORKER, 1); } + + DEBUG_TICKER_ISR(0); } uint32_t ticker_start(uint8_t instance_index, uint8_t user_id, @@ -1341,7 +1606,7 @@ uint32_t ticker_start(uint8_t instance_index, uint8_t user_id, user = &instance->user[user_id]; last = user->last + 1; - if (last == user->count_user_op) { + if (last >= user->count_user_op) { last = 0; } @@ -1366,7 +1631,7 @@ uint32_t ticker_start(uint8_t instance_index, uint8_t user_id, user->last = last; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); return user_op->status; } @@ -1385,7 +1650,7 @@ uint32_t ticker_update(uint8_t instance_index, uint8_t user_id, user = &instance->user[user_id]; last = user->last + 1; - if (last == user->count_user_op) { + if (last >= user->count_user_op) { last = 0; } @@ -1408,7 +1673,7 @@ uint32_t ticker_update(uint8_t instance_index, uint8_t user_id, user->last = last; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); return user_op->status; } @@ -1425,7 +1690,7 @@ uint32_t ticker_stop(uint8_t instance_index, uint8_t user_id, user = &instance->user[user_id]; last = user->last + 1; - if (last == user->count_user_op) { + if (last >= user->count_user_op) { last = 0; } @@ -1442,7 +1707,7 @@ uint32_t ticker_stop(uint8_t instance_index, uint8_t user_id, user->last = last; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); return user_op->status; } @@ -1461,7 +1726,7 @@ uint32_t ticker_next_slot_get(uint8_t instance_index, uint8_t user_id, user = &instance->user[user_id]; last = user->last + 1; - if (last == user->count_user_op) { + if (last >= user->count_user_op) { last = 0; } @@ -1481,7 +1746,7 @@ uint32_t ticker_next_slot_get(uint8_t instance_index, uint8_t user_id, user->last = last; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); return user_op->status; } @@ -1497,7 +1762,7 @@ uint32_t ticker_job_idle_get(uint8_t instance_index, uint8_t user_id, user = &instance->user[user_id]; last = user->last + 1; - if (last == user->count_user_op) { + if (last >= user->count_user_op) { last = 0; } @@ -1514,21 +1779,21 @@ uint32_t ticker_job_idle_get(uint8_t instance_index, uint8_t user_id, user->last = last; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); return user_op->status; } -void ticker_job_sched(uint8_t instance_index) +void ticker_job_sched(uint8_t instance_index, uint8_t user_id) { struct ticker_instance *instance = &_instance[instance_index]; - instance->fp_job_sched(0); + instance->fp_sched(instance->fp_caller_id_get(user_id), CALL_ID_JOB, 0); } uint32_t ticker_ticks_now_get(void) { - return rtc_tick_get(); + return cntr_cnt_get(); } uint32_t ticker_ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old) diff --git a/subsys/bluetooth/controller/ll/ticker.h b/subsys/bluetooth/controller/ticker/ticker.h similarity index 98% rename from subsys/bluetooth/controller/ll/ticker.h rename to subsys/bluetooth/controller/ticker/ticker.h index f5ce557a5ef0..9283f7394d00 100644 --- a/subsys/bluetooth/controller/ll/ticker.h +++ b/subsys/bluetooth/controller/ticker/ticker.h @@ -131,7 +131,7 @@ uint32_t ticker_next_slot_get(uint8_t instance_index, uint8_t user_id, ticker_op_func fp_op_func, void *op_context); uint32_t ticker_job_idle_get(uint8_t instance_index, uint8_t user_id, ticker_op_func fp_op_func, void *op_context); -void ticker_job_sched(uint8_t instance_index); +void ticker_job_sched(uint8_t instance_index, uint8_t user_id); uint32_t ticker_ticks_now_get(void); uint32_t ticker_ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old); diff --git a/subsys/bluetooth/controller/util/config.h b/subsys/bluetooth/controller/util/config.h new file mode 100644 index 000000000000..c305726b818f --- /dev/null +++ b/subsys/bluetooth/controller/util/config.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define MAYFLY_CALL_ID_0 0 +#define MAYFLY_CALL_ID_1 1 +#define MAYFLY_CALL_ID_2 2 +#define MAYFLY_CALL_ID_PROGRAM 3 +#define MAYFLY_CALLER_COUNT 4 +#define MAYFLY_CALLEE_COUNT 4 + +#define TICKER_MAYFLY_CALL_ID_TRIGGER MAYFLY_CALL_ID_0 +#define TICKER_MAYFLY_CALL_ID_WORKER0 MAYFLY_CALL_ID_0 +#define TICKER_MAYFLY_CALL_ID_WORKER1 MAYFLY_CALL_ID_2 +#define TICKER_MAYFLY_CALL_ID_JOB0 MAYFLY_CALL_ID_1 +#define TICKER_MAYFLY_CALL_ID_JOB1 MAYFLY_CALL_ID_2 +#define TICKER_MAYFLY_CALL_ID_PROGRAM MAYFLY_CALL_ID_PROGRAM + +#define TICKER_MAYFLY_CALL_ID_WORKER0_PRIO 0 +#define TICKER_MAYFLY_CALL_ID_WORKER1_PRIO 1 +#define TICKER_MAYFLY_CALL_ID_JOB0_PRIO 0 +#define TICKER_MAYFLY_CALL_ID_JOB1_PRIO 1 + +#endif /* _CONFIG_H_ */ diff --git a/subsys/bluetooth/controller/util/defines.h b/subsys/bluetooth/controller/util/defines.h deleted file mode 100644 index afdab465821f..000000000000 --- a/subsys/bluetooth/controller/util/defines.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 Nordic Semiconductor ASA - * Copyright (c) 2016 Vinayak Kariappa Chettimada - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _DEFINES_H_ -#define _DEFINES_H_ - -#include - -#if !defined(ALIGNED) -#define ALIGNED(x) __aligned(x) -#endif - -#define ALIGN4(x) (((uint32_t)(x)+3) & (~((uint32_t)3))) - -#define DOUBLE_BUFFER_SIZE 2 -#define TRIPLE_BUFFER_SIZE 3 - -#define BDADDR_SIZE 6 - -#endif /* _DEFINES_H_ */ diff --git a/subsys/bluetooth/controller/util/mayfly.c b/subsys/bluetooth/controller/util/mayfly.c new file mode 100644 index 000000000000..e78648d28bcd --- /dev/null +++ b/subsys/bluetooth/controller/util/mayfly.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "memq.h" +#include "mayfly.h" + +#include "config.h" + +static struct { + void *head; + void *tail; +} mft[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT]; + +static void *mfl[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT][2]; + +void mayfly_init(void) +{ + uint8_t callee_id; + + callee_id = MAYFLY_CALLEE_COUNT; + while (callee_id--) { + uint8_t caller_id; + + caller_id = MAYFLY_CALLER_COUNT; + while (caller_id--) { + memq_init(mfl[callee_id][caller_id], + &mft[callee_id][caller_id].head, + &mft[callee_id][caller_id].tail); + } + } +} + +uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, uint8_t chain, + struct mayfly *m) +{ + uint8_t state; + uint8_t ack; + + chain = chain || !mayfly_prio_is_equal(caller_id, callee_id) || + !mayfly_is_enabled(caller_id, callee_id); + + /* shadow the ack */ + ack = m->_ack; + + /* already in queue */ + state = (m->_req - ack) & 0x03; + if (state != 0) { + if (chain) { + if (state != 1) { + /* mark as ready in queue */ + m->_req = ack + 1; + + /* pend the callee for execution */ + mayfly_pend(caller_id, callee_id); + + return 0; + } + + /* already ready */ + return 1; + } + + /* mark as done in queue, and fall thru */ + m->_req = ack + 2; + } + + /* handle mayfly(s) that can be inline */ + if (!chain) { + /* call fp */ + m->fp(m->param); + + return 0; + } + + /* new, add as ready in the queue */ + m->_req = ack + 1; + memq_enqueue(m, m->_link, &mft[callee_id][caller_id].tail); + + /* pend the callee for execution */ + mayfly_pend(caller_id, callee_id); + + return 0; +} + +void mayfly_run(uint8_t callee_id) +{ + uint8_t caller_id; + + /* iterate through each caller queue to this callee_id */ + caller_id = MAYFLY_CALLER_COUNT; + while (caller_id--) { + void *link; + struct mayfly *m = 0; + + /* fetch mayfly in callee queue, if any */ + link = memq_peek(mft[callee_id][caller_id].tail, + mft[callee_id][caller_id].head, + (void **)&m); + while (link) { + uint8_t state; + uint8_t req; + + /* execute work if ready */ + req = m->_req; + state = (req - m->_ack) & 0x03; + if (state == 1) { + /* mark mayfly as ran */ + m->_ack--; + + /* call the mayfly function */ + m->fp(m->param); + } + + /* dequeue if not re-pended */ + req = m->_req; + if (((req - m->_ack) & 0x03) != 1) { + memq_dequeue(mft[callee_id][caller_id].tail, + &mft[callee_id][caller_id].head, + 0); + + /* release link into dequeued mayfly struct */ + m->_link = link; + + /* reset mayfly state to idle */ + m->_ack = req; + } + + /* fetch next mayfly in callee queue, if any */ + link = memq_peek(mft[callee_id][caller_id].tail, + mft[callee_id][caller_id].head, + (void **)&m); + + /* yield out of mayfly_run if a mayfly function was + * called. + */ + if (state == 1) { + /* pend callee (tailchain) if mayfly queue is + * not empty. + */ + if (link) { + mayfly_pend(callee_id, callee_id); + } + + return; + } + } + } +} diff --git a/subsys/bluetooth/controller/util/work.h b/subsys/bluetooth/controller/util/mayfly.h similarity index 52% rename from subsys/bluetooth/controller/util/work.h rename to subsys/bluetooth/controller/util/mayfly.h index 9baf8619efc4..c7779241a535 100644 --- a/subsys/bluetooth/controller/util/work.h +++ b/subsys/bluetooth/controller/util/mayfly.h @@ -15,24 +15,25 @@ * limitations under the License. */ -#ifndef _WORK_H_ -#define _WORK_H_ +#ifndef _MAYFLY_H_ +#define _MAYFLY_H_ -typedef void (*work_fp) (void *params); - -struct work { - void *next; - uint8_t req; - uint8_t ack; - uint8_t group; - work_fp fp; - void *params; +struct mayfly { + uint8_t volatile _req; + uint8_t _ack; + void *_link; + void *param; + void (*fp)(void *); }; -void work_enable(uint8_t group); -void work_disable(uint8_t group); -uint32_t work_is_enabled(uint8_t group); -uint32_t work_schedule(struct work *w, uint8_t chain); -void work_run(uint8_t group); +void mayfly_init(void); +uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, uint8_t chain, + struct mayfly *m); +void mayfly_run(uint8_t callee_id); + +extern void mayfly_enable(uint8_t caller_id, uint8_t callee_id, uint8_t enable); +extern uint32_t mayfly_is_enabled(uint8_t caller_id, uint8_t callee_id); +extern uint32_t mayfly_prio_is_equal(uint8_t caller_id, uint8_t callee_id); +extern void mayfly_pend(uint8_t caller_id, uint8_t callee_id); -#endif /* _WORK_H_ */ +#endif /* _MAYFLY_H_ */ diff --git a/subsys/bluetooth/controller/util/mem.c b/subsys/bluetooth/controller/util/mem.c index 855222ea806e..c770abb8bae7 100644 --- a/subsys/bluetooth/controller/util/mem.c +++ b/subsys/bluetooth/controller/util/mem.c @@ -18,7 +18,7 @@ #include #include -#include "defines.h" +#include "util.h" #include "mem.h" @@ -27,15 +27,17 @@ void mem_init(void *mem_pool, uint16_t mem_size, uint16_t mem_count, { *mem_head = mem_pool; - /* Store free mem_count after the list's next pointer */ - memcpy(((uint8_t *)mem_pool + sizeof(mem_pool)), - (uint8_t *)&mem_count, sizeof(mem_count)); + /* Store free mem_count after the list's next pointer at an aligned + * memory location to ensure atomic read/write (in ARM for now). + */ + *((uint16_t *)MROUND((uint8_t *)mem_pool + sizeof(mem_pool))) = + mem_count; /* Initialize next pointers to form a free list, * next pointer is stored in the first 32-bit of each block */ memset(((uint8_t *)mem_pool + (mem_size * (--mem_count))), 0, - sizeof(mem_pool)); + sizeof(mem_pool)); while (mem_count--) { uint32_t next; @@ -50,23 +52,24 @@ void *mem_acquire(void **mem_head) { if (*mem_head) { uint16_t free_count; + void *head; void *mem; /* Get the free count from the list and decrement it */ - memcpy((void *)&free_count, - ((uint8_t *)*mem_head + sizeof(mem_head)), - sizeof(free_count)); + free_count = *((uint16_t *)MROUND((uint8_t *)*mem_head + + sizeof(mem_head))); free_count--; mem = *mem_head; - memcpy(mem_head, mem, sizeof(*mem_head)); + memcpy(&head, mem, sizeof(head)); /* Store free mem_count after the list's next pointer */ - if (*mem_head) { - memcpy(((uint8_t *)*mem_head + sizeof(mem_head)), - (uint8_t *)&free_count, sizeof(free_count)); + if (head) { + *((uint16_t *)MROUND((uint8_t *)head + sizeof(head))) = + free_count; } + *mem_head = head; return mem; } @@ -79,17 +82,17 @@ void mem_release(void *mem, void **mem_head) /* Get the free count from the list and increment it */ if (*mem_head) { - memcpy(&free_count, ((uint8_t *)*mem_head + sizeof(mem_head)), - sizeof(free_count)); + free_count = *((uint16_t *)MROUND((uint8_t *)*mem_head + + sizeof(mem_head))); } free_count++; memcpy(mem, mem_head, sizeof(mem)); - *mem_head = mem; /* Store free mem_count after the list's next pointer */ - memcpy(((uint8_t *)*mem_head + sizeof(mem_head)), - (uint8_t *)&free_count, sizeof(free_count)); + *((uint16_t *)MROUND((uint8_t *)mem + sizeof(mem))) = free_count; + + *mem_head = mem; } uint16_t mem_free_count_get(void *mem_head) @@ -98,8 +101,8 @@ uint16_t mem_free_count_get(void *mem_head) /* Get the free count from the list */ if (mem_head) { - memcpy(&free_count, ((uint8_t *)mem_head + sizeof(mem_head)), - sizeof(free_count)); + free_count = *((uint16_t *)MROUND((uint8_t *)mem_head + + sizeof(mem_head))); } return free_count; @@ -137,9 +140,9 @@ uint8_t mem_is_zero(uint8_t *src, uint16_t len) uint32_t mem_ut(void) { -#define BLOCK_SIZE ALIGN4(10) +#define BLOCK_SIZE MROUND(10) #define BLOCK_COUNT 10 - uint8_t ALIGNED(4) pool[BLOCK_COUNT][BLOCK_SIZE]; + uint8_t MALIGN(4) pool[BLOCK_COUNT][BLOCK_SIZE]; void *mem_free; void *mem_used; uint16_t mem_free_count; diff --git a/subsys/bluetooth/controller/util/mem.h b/subsys/bluetooth/controller/util/mem.h index f75f15e55feb..14eee24c44c2 100644 --- a/subsys/bluetooth/controller/util/mem.h +++ b/subsys/bluetooth/controller/util/mem.h @@ -18,6 +18,14 @@ #ifndef _MEM_H_ #define _MEM_H_ +#ifndef MALIGN +#define MALIGN(x) __attribute__((aligned(x))) +#endif + +#ifndef MROUND +#define MROUND(x) (((uint32_t)(x)+3) & (~((uint32_t)3))) +#endif + void mem_init(void *mem_pool, uint16_t mem_size, uint16_t mem_count, void **mem_head); void *mem_acquire(void **mem_head); diff --git a/subsys/bluetooth/controller/util/memq.c b/subsys/bluetooth/controller/util/memq.c index 94166fda9001..b241831051d5 100644 --- a/subsys/bluetooth/controller/util/memq.c +++ b/subsys/bluetooth/controller/util/memq.c @@ -17,6 +17,8 @@ #include +inline void *memq_peek(void *tail, void *head, void **mem); + void *memq_init(void *link, void **head, void **tail) { /* head and tail pointer to the initial link node */ @@ -39,23 +41,33 @@ void *memq_enqueue(void *mem, void *link, void **tail) return link; } -void *memq_dequeue(void *tail, void **head, void **mem) +void *memq_peek(void *tail, void *head, void **mem) { void *link; /* if head and tail are equal, then queue empty */ - if (*head == tail) { + if (head == tail) { return 0; } /* pick the head link node */ - link = *head; + link = head; /* extract the element node */ if (mem) { *mem = *((void **)link + 1); } + return link; +} + +void *memq_dequeue(void *tail, void **head, void **mem) +{ + void *link; + + /* use memq peek to get the link and mem */ + link = memq_peek(tail, *head, mem); + /* increment the head to next link node */ *head = *((void **)link); diff --git a/subsys/bluetooth/controller/util/memq.h b/subsys/bluetooth/controller/util/memq.h index 59b205341c85..6c4a36141b03 100644 --- a/subsys/bluetooth/controller/util/memq.h +++ b/subsys/bluetooth/controller/util/memq.h @@ -20,6 +20,7 @@ void *memq_init(void *link, void **head, void **tail); void *memq_enqueue(void *mem, void *link, void **tail); +void *memq_peek(void *tail, void *head, void **mem); void *memq_dequeue(void *tail, void **head, void **mem); #endif diff --git a/subsys/bluetooth/controller/util/util.h b/subsys/bluetooth/controller/util/util.h index e61bace1c46f..ebdc7ce0ee58 100644 --- a/subsys/bluetooth/controller/util/util.h +++ b/subsys/bluetooth/controller/util/util.h @@ -18,6 +18,14 @@ #ifndef _UTIL_H_ #define _UTIL_H_ +#ifndef DOUBLE_BUFFER_SIZE +#define DOUBLE_BUFFER_SIZE 2 +#endif + +#ifndef TRIPLE_BUFFER_SIZE +#define TRIPLE_BUFFER_SIZE 3 +#endif + uint8_t util_ones_count_get(uint8_t *octets, uint8_t octets_len); #endif diff --git a/subsys/bluetooth/controller/util/work.c b/subsys/bluetooth/controller/util/work.c deleted file mode 100644 index 435f1d9e99b0..000000000000 --- a/subsys/bluetooth/controller/util/work.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2016 Nordic Semiconductor ASA - * Copyright (c) 2016 Vinayak Kariappa Chettimada - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "work.h" - -static struct work *_work_head; - -static int _irq_is_priority_equal(unsigned int irq) -{ - unsigned int curr_ctx; - int curr_prio; - - curr_ctx = _ScbActiveVectorGet(); - if (curr_ctx > 15) { - /* Interrupts */ - curr_prio = _NvicIrqPrioGet(curr_ctx - 16); - } else if (curr_ctx > 3) { - /* Execeptions */ - curr_prio = _ScbExcPrioGet(curr_ctx); - } else if (curr_ctx > 0) { - /* Fixed Priority Exceptions: -3, -2, -1 priority */ - curr_prio = curr_ctx - 4; - } else { - /* Thread mode */ - curr_prio = 256; - } - - return (_NvicIrqPrioGet(irq) == curr_prio); -} - -void work_enable(uint8_t group) -{ - irq_enable(group); -} - -void work_disable(uint8_t group) -{ - irq_disable(group); -} - -uint32_t work_is_enabled(uint8_t group) -{ - return irq_is_enabled(group); -} - -uint32_t work_schedule(struct work *w, uint8_t chain) -{ - uint32_t imask = irq_lock(); - struct work *prev; - struct work *curr; - - /* Dequeue expired work at head */ - while ((_work_head) - && (_work_head->ack == _work_head->req) - ) { - _work_head = _work_head->next; - } - - /* Dequeue expired in between list and find last node */ - curr = _work_head; - prev = curr; - while (curr) { - /* delete expired work */ - if (curr->ack == curr->req) { - prev->next = curr->next; - } else { - prev = curr; - } - - curr = curr->next; - } - - /* chain, if explicitly requested, or if work not at current level */ - chain = chain || (!_irq_is_priority_equal(w->group)) - || (!irq_is_enabled(w->group)); - - /* Already in List */ - curr = _work_head; - while (curr) { - if (curr == w) { - if (!chain) { - break; - } - - irq_unlock(imask); - - return 1; - } - - curr = curr->next; - } - - /* handle work(s) that can be inline */ - if (!chain) { - w->req = w->ack; - - irq_unlock(imask); - - if (w->fp) { - w->fp(w->params); - } - - return 0; - } - - /* New, add to the list */ - w->req = w->ack + 1; - w->next = 0; - if (prev == curr) { - _work_head = w; - } else { - prev->next = w; - } - - _NvicIrqPend(w->group); - - irq_unlock(imask); - - return 0; -} - -void work_run(uint8_t group) -{ - uint32_t imask = irq_lock(); - struct work *curr = _work_head; - - while (curr) { - if ((curr->group == group) && (curr->ack != curr->req)) { - curr->ack = curr->req; - - if (curr->fp) { - if (curr->next) { - _NvicIrqPend(group); - } - - irq_unlock(imask); - - curr->fp(curr->params); - - return; - } - } - - curr = curr->next; - } - - irq_unlock(imask); -} diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index b991dda4d19c..293679afc5db 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -19,14 +19,14 @@ comment "Host Stack Configuration" # Stack size needed for executing bt_send with specified driver -config BLUETOOTH_HCI_SEND_STACK +config BLUETOOTH_HCI_TX_STACK_SIZE int # Even if no driver is selected the following default is still # needed e.g. for unit tests. default 256 default 256 if BLUETOOTH_H4 default 256 if BLUETOOTH_H5 - default 512 if BLUETOOTH_CONTROLLER + default 640 if BLUETOOTH_CONTROLLER config BLUETOOTH_HCI_RAW bool "RAW HCI access" @@ -44,6 +44,14 @@ config BLUETOOTH_HCI_HOST select TINYCRYPT_SHA256_HMAC 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_HCI_CMD_COUNT int "Number of HCI command buffers" default 2 @@ -60,27 +68,22 @@ config BLUETOOTH_MAX_CMD_LEN help Maximum length of each HCI command. -config BLUETOOTH_HCI_EVT_COUNT - int "Number of HCI event buffers" - default 4 - default 8 if BLUETOOTH_CONN - range 2 64 +config BLUETOOTH_RX_BUF_COUNT + int "Number of HCI RX buffers" + default 10 + default 3 if BLUETOOTH_RECV_IS_RX_THREAD + range 2 255 help - Number of buffers available for HCI events. This number should - ideally be at least as large as the available outgoing buffers - to make sure we've got enough buffers to handle bursts of - Number of Completed Packets HCI events. + Number of buffers available for incoming ACL packets or HCI events + from the controller. -config BLUETOOTH_MAX_EVT_LEN - int "Maximum supported HCI event length" - default 68 - default 255 if BLUETOOTH_BREDR - range 68 255 +config BLUETOOTH_RX_BUF_LEN + int "Maximum supported HCI RX buffer length" + default 70 + default 253 if BLUETOOTH_BREDR + range 70 2000 help - Maximum size of each HCI event buffer. E.g. one big event - for LE is the Command Complete for Read Local Supported - Commands. It is a 3 byte Command Complete header + 65 byte - return parameters = 68 bytes in total. + Maximum data size for each HCI RX buffer. config BLUETOOTH_UART_TO_HOST_DEV_NAME string "Device Name of UART Device to an external Bluetooth Host" @@ -91,40 +94,13 @@ config BLUETOOTH_UART_TO_HOST_DEV_NAME to connect to an external Bluetooth Host when Zephyr is acting as a Bluetooth Controller. -if BLUETOOTH_CONN || BLUETOOTH_HCI_RAW -config BLUETOOTH_ACL_IN_COUNT - int "Number of incoming ACL data buffers" - default 6 - default BLUETOOTH_CONTROLLER_RX_BUFFERS if BLUETOOTH_CONTROLLER - range 2 64 - help - Number of buffers available for incoming ACL data. - -config BLUETOOTH_L2CAP_IN_MTU - int "Maximum supported L2CAP MTU for incoming data" - default 23 - default 65 if BLUETOOTH_SMP - default 200 if BLUETOOTH_BREDR - range 23 1300 - range 65 1300 if BLUETOOTH_SMP - help - Maximum size of each incoming L2CAP PDU. -endif # BLUETOOTH_CONN || BLUETOOTH_HCI_RAW - -if BLUETOOTH_HCI_HOST -config BLUETOOTH_INTERNAL_STORAGE - bool "Use an internal persistent storage handler" - depends on FILE_SYSTEM - depends on PRINTK - help - When selected the application doesn't need to register its own - persistent storage handlers through the bt_storage API, rather - an internal default handler is used for this. - config BLUETOOTH_RX_STACK_SIZE int "Size of the receiving thread stack" + depends on BLUETOOTH_HCI_HOST || BLUETOOTH_RECV_IS_RX_THREAD default 1024 + default 512 if CONFIG_BLUETOOTH_HCI_RAW range 1024 65536 + range 512 65536 if CONFIG_BLUETOOTH_HCI_RAW help Size of the receiving thread stack. This is the context from which all event callbacks to the application occur. The @@ -133,6 +109,16 @@ config BLUETOOTH_RX_STACK_SIZE require extra stack space, this value can be increased to accommodate for that. +if BLUETOOTH_HCI_HOST +config BLUETOOTH_INTERNAL_STORAGE + bool "Use an internal persistent storage handler" + depends on FILE_SYSTEM + depends on PRINTK + help + When selected the application doesn't need to register its own + persistent storage handlers through the bt_storage API, rather + an internal default handler is used for this. + config BLUETOOTH_PERIPHERAL bool "Peripheral Role support" select BLUETOOTH_CONN @@ -154,10 +140,10 @@ config BLUETOOTH_ATT_MTU default 23 default 50 if BLUETOOTH_SMP # BLUETOOTH_L2CAP_IN_MTU is big enough # for two complete ACL packets - range 23 BLUETOOTH_L2CAP_IN_MTU + range 23 BLUETOOTH_RX_BUF_LEN help The MTU for the ATT channel. The minimum and default is 23, - whereas the maximum is limited by CONFIG_BLUETOOTH_L2CAP_IN_MTU. + whereas the maximum is limited by CONFIG_BLUETOOTH_RX_BUF_LEN. config BLUETOOTH_ATT_PREPARE_COUNT int "Number of ATT prepare write buffers" @@ -458,9 +444,9 @@ config BLUETOOTH_RFCOMM config BLUETOOTH_RFCOMM_L2CAP_MTU int "L2CAP MTU for RFCOMM frames" - default BLUETOOTH_L2CAP_IN_MTU + default BLUETOOTH_RX_BUF_LEN depends on BLUETOOTH_RFCOMM - range BLUETOOTH_L2CAP_IN_MTU 32767 + range BLUETOOTH_RX_BUF_LEN 32767 help Maximum size of L2CAP PDU for RFCOMM frames. diff --git a/subsys/bluetooth/host/a2dp.c b/subsys/bluetooth/host/a2dp.c index 88006c89023c..4d7091e983f3 100644 --- a/subsys/bluetooth/host/a2dp.c +++ b/subsys/bluetooth/host/a2dp.c @@ -49,11 +49,6 @@ struct bt_a2dp { /* Connections */ static struct bt_a2dp connection[CONFIG_BLUETOOTH_MAX_CONN]; -/* Callback for action confirmation */ -static struct bt_avdtp_cfm_cb cb_cfm = { - /*TODO*/ -}; - /* Callback for incoming requests */ static struct bt_avdtp_ind_cb cb_ind = { /*TODO*/ @@ -62,7 +57,6 @@ static struct bt_avdtp_ind_cb cb_ind = { /* The above callback structures need to be packed and passed to AVDTP */ static struct bt_avdtp_event_cb avdtp_cb = { .ind = &cb_ind, - .cfm = &cb_cfm }; int bt_a2dp_init(void) diff --git a/subsys/bluetooth/host/at.c b/subsys/bluetooth/host/at.c index 3baad1c0236a..c0345fe62bdc 100644 --- a/subsys/bluetooth/host/at.c +++ b/subsys/bluetooth/host/at.c @@ -27,7 +27,7 @@ #include "at.h" -static void next_stream(struct at_client *at) +static void next_list(struct at_client *at) { if (at->buf[at->pos] == ',') { at->pos++; @@ -46,7 +46,7 @@ int at_check_byte(struct net_buf *buf, char check_byte) return 0; } -static void skip_whitespace(struct at_client *at) +static void skip_space(struct at_client *at) { while (at->buf[at->pos] == ' ') { at->pos++; @@ -57,7 +57,7 @@ int at_get_number(struct at_client *at, uint32_t *val) { uint32_t i; - skip_whitespace(at); + skip_space(at); for (i = 0, *val = 0; isdigit(at->buf[at->pos]); at->pos++, i++) { *val = *val * 10 + at->buf[at->pos] - '0'; @@ -67,7 +67,7 @@ int at_get_number(struct at_client *at, uint32_t *val) return -ENODATA; } - next_stream(at); + next_list(at); return 0; } @@ -233,6 +233,10 @@ static int at_state_process_result(struct at_client *at, struct net_buf *buf) } } + /* Reset the state to process unsolicited response */ + at->cmd_state = CMD_START; + at->state = AT_STATE_START; + return 0; } @@ -347,16 +351,16 @@ int at_parse_cmd_input(struct at_client *at, struct net_buf *buf, return 0; } -int at_has_next_stream(struct at_client *at) +int at_has_next_list(struct at_client *at) { return at->buf[at->pos] != '\0'; } -int at_open_stream(struct at_client *at) +int at_open_list(struct at_client *at) { - skip_whitespace(at); + skip_space(at); - /* The stream shall start with '(' open parenthesis */ + /* The list shall start with '(' open parenthesis */ if (at->buf[at->pos] != '(') { return -ENODATA; } @@ -365,25 +369,25 @@ int at_open_stream(struct at_client *at) return 0; } -int at_close_stream(struct at_client *at) +int at_close_list(struct at_client *at) { - skip_whitespace(at); + skip_space(at); if (at->buf[at->pos] != ')') { return -ENODATA; } at->pos++; - next_stream(at); + next_list(at); return 0; } -int at_stream_get_string(struct at_client *at, char *name, uint8_t len) +int at_list_get_string(struct at_client *at, char *name, uint8_t len) { int i = 0; - skip_whitespace(at); + skip_space(at); if (at->buf[at->pos] != '"') { return -ENODATA; @@ -408,13 +412,13 @@ int at_stream_get_string(struct at_client *at, char *name, uint8_t len) } at->pos++; - skip_whitespace(at); - next_stream(at); + skip_space(at); + next_list(at); return 0; } -int at_stream_get_range(struct at_client *at, uint32_t *min, uint32_t *max) +int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max) { uint32_t low, high; int ret; @@ -441,7 +445,7 @@ int at_stream_get_range(struct at_client *at, uint32_t *min, uint32_t *max) *min = low; *max = high; - next_stream(at); + next_list(at); return 0; } diff --git a/subsys/bluetooth/host/at.h b/subsys/bluetooth/host/at.h index b1dff110549d..eb74aa1a6ff6 100644 --- a/subsys/bluetooth/host/at.h +++ b/subsys/bluetooth/host/at.h @@ -80,8 +80,8 @@ int at_parse_input(struct at_client *at, struct net_buf *buf); int at_parse_cmd_input(struct at_client *at, struct net_buf *buf, const char *prefix, parse_val_t func); int at_check_byte(struct net_buf *buf, char check_byte); -int at_stream_get_range(struct at_client *at, uint32_t *min, uint32_t *max); -int at_stream_get_string(struct at_client *at, char *name, uint8_t len); -int at_close_stream(struct at_client *at); -int at_open_stream(struct at_client *at); -int at_has_next_stream(struct at_client *at); +int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max); +int at_list_get_string(struct at_client *at, char *name, uint8_t len); +int at_close_list(struct at_client *at); +int at_open_list(struct at_client *at); +int at_has_next_list(struct at_client *at); diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index ed64df756de0..cb915854299b 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -1181,13 +1181,9 @@ static uint8_t att_write_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp, static uint8_t att_write_req(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.chan.conn; - struct bt_att_write_req *req; uint16_t handle; - req = (void *)buf->data; - - handle = sys_le16_to_cpu(req->handle); - net_buf_pull(buf, sizeof(*req)); + handle = net_buf_pull_le16(buf); BT_DBG("handle 0x%04x", handle); @@ -1243,7 +1239,7 @@ static uint8_t prep_write_cb(const struct bt_gatt_attr *attr, void *user_data) attr_data->handle = attr->handle; attr_data->offset = data->offset; - memcpy(net_buf_add(data->buf, data->len), data->value, data->len); + net_buf_add_mem(data->buf, data->value, data->len); data->err = 0; @@ -1279,8 +1275,10 @@ static uint8_t att_prep_write_rsp(struct bt_att *att, uint16_t handle, return 0; } + BT_DBG("buf %p handle 0x%04x offset %u", data.buf, handle, offset); + /* Store buffer in the outstanding queue */ - k_fifo_put(&att->prep_queue, data.buf); + net_buf_put(&att->prep_queue, data.buf); /* Generate response */ data.buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_RSP, 0); @@ -1327,9 +1325,12 @@ static uint8_t att_exec_write_rsp(struct bt_att *att, uint8_t flags) struct net_buf *buf; uint8_t err = 0; - while ((buf = k_fifo_get(&att->prep_queue, K_NO_WAIT))) { + while ((buf = net_buf_get(&att->prep_queue, K_NO_WAIT))) { struct bt_attr_data *data = net_buf_user_data(buf); + BT_DBG("buf %p handle 0x%04x offset %u", buf, data->handle, + data->offset); + /* Just discard the data if an error was set */ if (!err && flags == BT_ATT_FLAG_EXEC) { err = att_write_rsp(conn, BT_ATT_OP_EXEC_WRITE_REQ, 0, @@ -1380,17 +1381,9 @@ static uint8_t att_exec_write_req(struct bt_att *att, struct net_buf *buf) static uint8_t att_write_cmd(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.chan.conn; - struct bt_att_write_cmd *req; uint16_t handle; - if (buf->len < sizeof(*req)) { - /* Commands don't have any response */ - return 0; - } - - req = (void *)buf->data; - - handle = sys_le16_to_cpu(req->handle); + handle = net_buf_pull_le16(buf); BT_DBG("handle 0x%04x", handle); diff --git a/subsys/bluetooth/host/avdtp.c b/subsys/bluetooth/host/avdtp.c index c5ceefbccd9f..aea3fb8c849b 100644 --- a/subsys/bluetooth/host/avdtp.c +++ b/subsys/bluetooth/host/avdtp.c @@ -47,8 +47,6 @@ NET_BUF_POOL_DEFINE(avdtp_sig_pool, CONFIG_BLUETOOTH_AVDTP_CONN, BT_BUF_USER_DATA_MIN, NULL); */ -static struct bt_avdtp bt_avdtp_pool[CONFIG_BLUETOOTH_AVDTP_CONN]; - static struct bt_avdtp_event_cb *event_cb; static struct bt_avdtp_seid_lsep *lseps; @@ -81,7 +79,6 @@ void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan) session = AVDTP_CHAN(chan); BT_DBG("chan %p session %p", chan, session); - /* Init the timer */ k_delayed_work_init(&session->req.timeout_work, avdtp_timeout); @@ -89,12 +86,11 @@ void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan) void bt_avdtp_l2cap_disconnected(struct bt_l2cap_chan *chan) { - if (!chan) { - BT_ERR("Invalid AVDTP chan"); - return; - } + struct bt_avdtp *session = AVDTP_CHAN(chan); - BT_DBG("chan %p session %p", chan, AVDTP_CHAN(chan)); + BT_DBG("chan %p session %p", chan, session); + session->br_chan.chan.conn = NULL; + /* Clear the Pending req if set*/ } void bt_avdtp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status) @@ -141,7 +137,8 @@ int bt_avdtp_disconnect(struct bt_avdtp *session) int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) { - int i; + struct bt_avdtp *session = NULL; + int result; static struct bt_l2cap_chan_ops ops = { .connected = bt_avdtp_l2cap_connected, .disconnected = bt_avdtp_l2cap_disconnected, @@ -149,21 +146,15 @@ int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) }; BT_DBG("conn %p", conn); - - for (i = 0; i < ARRAY_SIZE(bt_avdtp_pool); i++) { - struct bt_avdtp *avdtp = &bt_avdtp_pool[i]; - - if (avdtp->br_chan.chan.conn) { - continue; - } - - avdtp->br_chan.chan.ops = &ops; - avdtp->br_chan.rx.mtu = BT_AVDTP_MAX_MTU; - *chan = &avdtp->br_chan.chan; - return 0; + /* Get the AVDTP session from upper layer */ + result = event_cb->accept(conn, &session); + if (result < 0) { + return result; } - - return -ENOMEM; + session->br_chan.chan.ops = &ops; + session->br_chan.rx.mtu = BT_AVDTP_MAX_MTU; + *chan = &session->br_chan.chan; + return 0; } /* Application will register its callback */ diff --git a/subsys/bluetooth/host/avdtp_internal.h b/subsys/bluetooth/host/avdtp_internal.h index c967aff67be3..45582fd2613e 100644 --- a/subsys/bluetooth/host/avdtp_internal.h +++ b/subsys/bluetooth/host/avdtp_internal.h @@ -55,16 +55,12 @@ #define BT_AVDTP_GET_ALL_CAPABILITIES 0x0c #define BT_AVDTP_DELAYREPORT 0x0d -/* @brief AVDTP STATE */ -#define BT_AVDTP_STATE_IDLE 0x01 -#define BT_AVDTP_STATE_CONFIGURED 0x02 -#define BT_AVDTP_STATE_OPEN 0x03 -#define BT_AVDTP_STATE_STREAMING 0x04 -#define BT_AVDTP_STATE_CLOSING 0x05 -#define BT_AVDTP_STATE_ABORT 0x06 -#define BT_AVDTP_STATE_SIG_CONNECTED 0x07 -#define BT_AVDTP_STATE_SIG_DISCONNECTED 0x08 -#define BT_AVDTP_STATE_INVALID 0x00 +/* @brief AVDTP STREAM STATE */ +#define BT_AVDTP_STREAM_STATE_IDLE 0x01 +#define BT_AVDTP_STREAM_STATE_CONFIGURED 0x02 +#define BT_AVDTP_STREAM_STATE_OPEN 0x03 +#define BT_AVDTP_STREAM_STATE_STREAMING 0x04 +#define BT_AVDTP_STREAM_STATE_CLOSING 0x05 /* @brief AVDTP Media TYPE */ #define BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT 0x01 @@ -97,7 +93,7 @@ #define BT_AVDTP_ERR_BAD_STATE 0x31 #define BT_AVDTP_MIN_MTU 48 -#define BT_AVDTP_MAX_MTU CONFIG_BLUETOOTH_L2CAP_IN_MTU +#define BT_AVDTP_MAX_MTU CONFIG_BLUETOOTH_RX_BUF_LEN #define BT_AVDTP_MIN_SEID 0x01 #define BT_AVDTP_MAX_SEID 0x3E @@ -115,18 +111,6 @@ struct bt_avdtp_single_sig_hdr { #define BT_AVDTP_SIG_HDR_LEN sizeof(struct bt_avdtp_single_sig_hdr) -struct bt_avdtp_cfm_cb { - /* - * Discovery_cfm; - * get_capabilities_cfm; - * set_configuration_cfm; - * open_cfm; - * start_cfm; - * suspend_cfm; - * close_cfm; - */ -}; - struct bt_avdtp_ind_cb { /* * discovery_ind; @@ -139,11 +123,6 @@ struct bt_avdtp_ind_cb { */ }; -struct bt_avdtp_event_cb { - struct bt_avdtp_ind_cb *ind; - struct bt_avdtp_cfm_cb *cfm; -}; - struct bt_pending_req { uint8_t signal_id; uint8_t transaction_id; @@ -157,6 +136,11 @@ struct bt_avdtp { struct bt_pending_req req; }; +struct bt_avdtp_event_cb { + struct bt_avdtp_ind_cb *ind; + int (*accept)(struct bt_conn *conn, struct bt_avdtp **session); +}; + /* Initialize AVDTP layer*/ int bt_avdtp_init(void); diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 6217f7046a26..cc12c5400512 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -838,7 +838,7 @@ void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) return; } - memcpy(net_buf_add(conn->rx, buf->len), buf->data, buf->len); + net_buf_add_mem(conn->rx, buf->data, buf->len); conn->rx_len -= buf->len; net_buf_unref(buf); @@ -957,7 +957,7 @@ static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) frag_len = min(conn_mtu(conn), net_buf_tailroom(frag)); - memcpy(net_buf_add(frag, frag_len), buf->data, frag_len); + net_buf_add_mem(frag, buf->data, frag_len); net_buf_pull(buf, frag_len); return frag; diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 199290cd4522..5a15b21e525e 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -120,7 +120,7 @@ struct bt_conn { * Since these threads don't overlap, one stack can be used by * both of them. */ - BT_STACK(stack, CONFIG_BLUETOOTH_HCI_SEND_STACK); + BT_STACK(stack, CONFIG_BLUETOOTH_HCI_TX_STACK_SIZE); }; /* Process incoming data for a connection */ diff --git a/subsys/bluetooth/host/gatt.c b/subsys/bluetooth/host/gatt.c index 1e376e5827d0..c89610a36144 100644 --- a/subsys/bluetooth/host/gatt.c +++ b/subsys/bluetooth/host/gatt.c @@ -909,8 +909,7 @@ static int gatt_find_type(struct bt_conn *conn, net_buf_add_le16(buf, BT_UUID_16(params->uuid)->val); break; case BT_UUID_TYPE_128: - memcpy(net_buf_add(buf, 16), - BT_UUID_128(params->uuid)->val, 16); + net_buf_add_mem(buf, BT_UUID_128(params->uuid)->val, 16); break; default: BT_ERR("Unknown UUID type %u", params->uuid->type); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index ae2a3004df13..b1db433a8904 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -58,10 +58,15 @@ #define RPA_TIMEOUT K_SECONDS(CONFIG_BLUETOOTH_RPA_TIMEOUT) /* 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(cmd_tx_thread_stack, CONFIG_BLUETOOTH_HCI_SEND_STACK); +#endif +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 = { + .init = K_WORK_INITIALIZER(init_work), /* Give cmd_sem allowing to send first HCI_Reset cmd, the only * exception is if the controller requests to wait for an * 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), #endif .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), +#endif }; +static bt_ready_cb_t ready_cb; + const struct bt_storage *bt_storage; static bt_le_scan_cb_t *scan_dev_found_cb; @@ -122,69 +131,11 @@ struct acl_data { NET_BUF_POOL_DEFINE(hci_cmd_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT, CMD_BUF_SIZE, sizeof(struct cmd_data), NULL); -#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS) -/* HCI event buffers */ -NET_BUF_POOL_DEFINE(hci_evt_pool, CONFIG_BLUETOOTH_HCI_EVT_COUNT, - BT_BUF_EVT_SIZE, BT_BUF_USER_DATA_MIN, NULL); -/* - * This priority pool is to handle HCI events that must not be dropped - * (currently this is Command Status, Command Complete and Number of - * Complete Packets) if running low on buffers. Buffers from this pool are not - * allowed to be passed to RX thread and must be returned from bt_recv(). Since - * the HCI ECC emulation is able to also allocate command status events - * we need to reserve one extra buffer for it. - */ -#if defined(CONFIG_BLUETOOTH_TINYCRYPT_ECC) -NET_BUF_POOL_DEFINE(hci_evt_prio_pool, 2, BT_BUF_EVT_SIZE, - BT_BUF_USER_DATA_MIN, NULL); -#else -NET_BUF_POOL_DEFINE(hci_evt_prio_pool, 1, BT_BUF_EVT_SIZE, - BT_BUF_USER_DATA_MIN, NULL); -#endif - -#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */ +NET_BUF_POOL_DEFINE(hci_rx_pool, CONFIG_BLUETOOTH_RX_BUF_COUNT, + BT_BUF_RX_SIZE, BT_BUF_USER_DATA_MIN, NULL); static struct tc_hmac_prng_struct prng; -#if defined(CONFIG_BLUETOOTH_CONN) && defined(CONFIG_BLUETOOTH_HOST_BUFFERS) -static void report_completed_packet(struct net_buf *buf) -{ - - struct bt_hci_cp_host_num_completed_packets *cp; - uint16_t handle = acl(buf)->handle; - struct bt_hci_handle_count *hc; - - net_buf_destroy(buf); - - /* Do nothing if controller to host flow control is not supported */ - if (!(bt_dev.supported_commands[10] & 0x20)) { - return; - } - - BT_DBG("Reporting completed packet for handle %u", handle); - - buf = bt_hci_cmd_create(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, - sizeof(*cp) + sizeof(*hc)); - if (!buf) { - BT_ERR("Unable to allocate new HCI command"); - return; - } - - cp = net_buf_add(buf, sizeof(*cp)); - cp->num_handles = sys_cpu_to_le16(1); - - hc = net_buf_add(buf, sizeof(*hc)); - hc->handle = sys_cpu_to_le16(handle); - hc->count = sys_cpu_to_le16(1); - - bt_hci_cmd_send(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, buf); -} - -NET_BUF_POOL_DEFINE(acl_in_pool, CONFIG_BLUETOOTH_ACL_IN_COUNT, - BT_BUF_ACL_IN_SIZE, sizeof(struct acl_data), - report_completed_packet); -#endif /* CONFIG_BLUETOOTH_CONN && CONFIG_BLUETOOTH_HOST_BUFFERS */ - #if defined(CONFIG_BLUETOOTH_DEBUG) const char *bt_addr_str(const bt_addr_t *addr) { @@ -408,7 +359,7 @@ static int set_random_address(const bt_addr_t *addr) return -ENOBUFS; } - memcpy(net_buf_add(buf, sizeof(*addr)), addr, sizeof(*addr)); + net_buf_add_mem(buf, addr, sizeof(*addr)); err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, buf, NULL); if (err) { @@ -615,7 +566,9 @@ static void hci_disconn_complete(struct net_buf *buf) conn->err = evt->reason; /* 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)); +#endif stack_analyze("cmd tx stack", cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack)); stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack)); @@ -1012,44 +965,6 @@ static void check_pending_conn(const bt_addr_le_t *id_addr, bt_conn_unref(conn); bt_le_scan_update(false); } - -static int set_flow_control(void) -{ - struct bt_hci_cp_host_buffer_size *hbs; - struct net_buf *buf; - int err; - - /* Check if host flow control is actually supported */ - if (!(bt_dev.supported_commands[10] & 0x20)) { - BT_WARN("Controller to host flow control not supported"); - return 0; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, - sizeof(*hbs)); - if (!buf) { - return -ENOBUFS; - } - - hbs = net_buf_add(buf, sizeof(*hbs)); - memset(hbs, 0, sizeof(*hbs)); - hbs->acl_mtu = sys_cpu_to_le16(CONFIG_BLUETOOTH_L2CAP_IN_MTU + - sizeof(struct bt_l2cap_hdr)); - hbs->acl_pkts = sys_cpu_to_le16(CONFIG_BLUETOOTH_ACL_IN_COUNT); - - err = bt_hci_cmd_send_sync(BT_HCI_OP_HOST_BUFFER_SIZE, buf, NULL); - if (err) { - return err; - } - - buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1); - if (!buf) { - return -ENOBUFS; - } - - net_buf_add_u8(buf, BT_HCI_CTL_TO_HOST_FLOW_ENABLE); - return bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL); -} #endif /* CONFIG_BLUETOOTH_CONN */ #if defined(CONFIG_BLUETOOTH_BREDR) @@ -2690,6 +2605,8 @@ static void hci_event(struct net_buf *buf) BT_DBG("event 0x%02x", hdr->evt); + BT_ASSERT(!bt_hci_evt_is_prio(hdr->evt)); + net_buf_pull(buf, sizeof(*hdr)); switch (hdr->evt) { @@ -2994,13 +2911,6 @@ static int common_init(void) read_supported_commands_complete(rsp); net_buf_unref(rsp); -#if defined(CONFIG_BLUETOOTH_CONN) - err = set_flow_control(); - if (err) { - return err; - } -#endif /* CONFIG_BLUETOOTH_CONN */ - return 0; } @@ -3504,12 +3414,8 @@ int bt_send(struct net_buf *buf) return bt_dev.drv->send(buf); } -/* Interface to HCI driver layer */ - int bt_recv(struct net_buf *buf) { - struct bt_hci_evt_hdr *hdr; - bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); BT_DBG("buf %p len %u", buf, buf->len); @@ -3520,51 +3426,62 @@ int bt_recv(struct net_buf *buf) return -EINVAL; } - if (bt_buf_get_type(buf) == BT_BUF_ACL_IN) { + switch (bt_buf_get_type(buf)) { +#if defined(CONFIG_BLUETOOTH_CONN) + 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); +#endif return 0; - } - - if (bt_buf_get_type(buf) != BT_BUF_EVT) { +#endif /* BLUETOOTH_CONN */ + case BT_BUF_EVT: +#if defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) + hci_event(buf); +#else + net_buf_put(&bt_dev.rx_queue, buf); +#endif + return 0; + default: BT_ERR("Invalid buf type %u", bt_buf_get_type(buf)); net_buf_unref(buf); return -EINVAL; } +} - hdr = (void *)buf->data; +int bt_recv_prio(struct net_buf *buf) +{ + struct bt_hci_evt_hdr *hdr = (void *)buf->data; + + bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); + + BT_ASSERT(bt_buf_get_type(buf) == BT_BUF_EVT); + BT_ASSERT(buf->len >= sizeof(*hdr)); + BT_ASSERT(bt_hci_evt_is_prio(hdr->evt)); + + net_buf_pull(buf, sizeof(*hdr)); switch (hdr->evt) { case BT_HCI_EVT_CMD_COMPLETE: - net_buf_pull(buf, sizeof(*hdr)); hci_cmd_complete(buf); break; case BT_HCI_EVT_CMD_STATUS: - net_buf_pull(buf, sizeof(*hdr)); hci_cmd_status(buf); break; #if defined(CONFIG_BLUETOOTH_CONN) case BT_HCI_EVT_NUM_COMPLETED_PACKETS: - net_buf_pull(buf, sizeof(*hdr)); hci_num_completed_packets(buf); break; #endif /* CONFIG_BLUETOOTH_CONN */ default: -#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS) - /* - * If buffer used is from priority pool we are running low on - * buffers and those needs to be kept for 'critical' events - * handled directly from bt_recv(). - */ - if (buf->pool == &hci_evt_prio_pool) { - break; - } -#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */ - - net_buf_put(&bt_dev.rx_queue, net_buf_ref(buf)); - break; + net_buf_unref(buf); + BT_ASSERT(0); + return -EINVAL; } net_buf_unref(buf); + return 0; } @@ -3629,17 +3546,8 @@ static int irk_init(void) static int bt_init(void) { - struct bt_hci_driver *drv = bt_dev.drv; int err; - bt_hci_ecc_init(); - - err = drv->open(); - if (err) { - BT_ERR("HCI driver open failed (%d)", err); - return err; - } - err = hci_init(); if (err) { return err; @@ -3668,15 +3576,22 @@ static int bt_init(void) return 0; } -static void hci_rx_thread(bt_ready_cb_t ready_cb) +static void init_work(struct k_work *work) { - struct net_buf *buf; - - BT_DBG("started"); + int err; + err = bt_init(); if (ready_cb) { - ready_cb(bt_init()); + ready_cb(err); } +} + +#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) +static void hci_rx_thread(void) +{ + struct net_buf *buf; + + BT_DBG("started"); while (1) { BT_DBG("calling fifo_get_wait"); @@ -3706,9 +3621,12 @@ static void hci_rx_thread(bt_ready_cb_t ready_cb) k_yield(); } } +#endif /* !CONFIG_BLUETOOTH_RECV_IS_RX_THREAD */ int bt_enable(bt_ready_cb_t cb) { + int err; + if (!bt_dev.drv) { BT_ERR("No HCI driver registered"); return -ENODEV; @@ -3718,20 +3636,33 @@ int bt_enable(bt_ready_cb_t cb) return -EALREADY; } + ready_cb = cb; + /* TX thread */ k_thread_spawn(cmd_tx_thread_stack, sizeof(cmd_tx_thread_stack), (k_thread_entry_t)hci_cmd_tx_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); +#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) /* RX thread */ 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); +#endif + + bt_hci_ecc_init(); + + err = bt_dev.drv->open(); + if (err) { + BT_ERR("HCI driver open failed (%d)", err); + return err; + } if (!cb) { return bt_init(); } + k_work_submit(&bt_dev.init); return 0; } @@ -4023,51 +3954,18 @@ int bt_le_scan_stop(void) return bt_le_scan_update(false); } -#if defined(CONFIG_BLUETOOTH_HOST_BUFFERS) -struct net_buf *bt_buf_get_evt(uint8_t opcode, int32_t timeout) +struct net_buf *bt_buf_get_rx(int32_t timeout) { struct net_buf *buf; - switch (opcode) { - case BT_HCI_EVT_CMD_COMPLETE: - case BT_HCI_EVT_CMD_STATUS: - case BT_HCI_EVT_NUM_COMPLETED_PACKETS: - buf = net_buf_alloc(&hci_evt_prio_pool, timeout); - break; - default: - buf = net_buf_alloc(&hci_evt_pool, K_NO_WAIT); - if (!buf && opcode == 0x00) { - buf = net_buf_alloc(&hci_evt_prio_pool, timeout); - } - break; - } - + buf = net_buf_alloc(&hci_rx_pool, timeout); if (buf) { net_buf_reserve(buf, CONFIG_BLUETOOTH_HCI_RECV_RESERVE); - bt_buf_set_type(buf, BT_BUF_EVT); } return buf; } -struct net_buf *bt_buf_get_acl(int32_t timeout) -{ -#if defined(CONFIG_BLUETOOTH_CONN) - struct net_buf *buf; - - buf = net_buf_alloc(&acl_in_pool, timeout); - if (buf) { - net_buf_reserve(buf, CONFIG_BLUETOOTH_HCI_RECV_RESERVE); - bt_buf_set_type(buf, BT_BUF_ACL_IN); - } - - return buf; -#else - return NULL; -#endif /* CONFIG_BLUETOOTH_CONN */ -} -#endif /* CONFIG_BLUETOOTH_HOST_BUFFERS */ - #if defined(CONFIG_BLUETOOTH_BREDR) static int br_start_inquiry(const struct bt_br_discovery_param *param) { @@ -4349,19 +4247,20 @@ int bt_pub_key_gen(struct bt_pub_key_cb *new_cb) new_cb->_next = pub_key_cb; pub_key_cb = new_cb; - if (atomic_test_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { return 0; } + atomic_clear_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_P256_PUBLIC_KEY, NULL, NULL); if (err) { + BT_ERR("Sending LE P256 Public Key command failed"); + atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); pub_key_cb = NULL; return err; } - atomic_set_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); - atomic_clear_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); - for (cb = pub_key_cb; cb; cb = cb->_next) { if (cb != new_cb) { cb->func(NULL); diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 1cf4a6cd0060..15264fcc3d15 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -92,6 +92,8 @@ struct bt_dev { /* Supported commands */ uint8_t supported_commands[64]; + struct k_work init; + ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS); /* LE controller specific features */ @@ -108,8 +110,10 @@ struct bt_dev { /* Last sent HCI command */ struct net_buf *sent_cmd; +#if !defined(CONFIG_BLUETOOTH_RECV_IS_RX_THREAD) /* Queue for incoming HCI events & ACL data */ struct k_fifo rx_queue; +#endif /* Queue for high priority HCI events which may unlock waiters * in other threads. Such events include Number of Completed diff --git a/subsys/bluetooth/host/hci_ecc.c b/subsys/bluetooth/host/hci_ecc.c index 72869de395e6..eea18e98d2f2 100644 --- a/subsys/bluetooth/host/hci_ecc.c +++ b/subsys/bluetooth/host/hci_ecc.c @@ -75,11 +75,8 @@ static void send_cmd_status(uint16_t opcode, uint8_t status) BT_DBG("opcode %x status %x", opcode, status); - buf = bt_buf_get_evt(BT_HCI_EVT_CMD_STATUS, K_FOREVER); - if (!buf) { - BT_ERR("No available event buffers!"); - return; - } + buf = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(buf, BT_BUF_EVT); hdr = net_buf_add(buf, sizeof(*hdr)); hdr->evt = BT_HCI_EVT_CMD_STATUS; @@ -90,36 +87,31 @@ static void send_cmd_status(uint16_t opcode, uint8_t status) evt->opcode = sys_cpu_to_le16(opcode); evt->status = status; - bt_recv(buf); + bt_recv_prio(buf); } -static uint8_t generate_keys(uint8_t public_key[64], uint32_t private_key[32]) +static uint8_t generate_keys(EccPoint *pkey, uint32_t private_key[8]) { #if !defined(CONFIG_BLUETOOTH_USE_DEBUG_KEYS) - EccPoint pkey; - do { uint32_t random[8]; int rc; if (bt_rand((uint8_t *)random, sizeof(random))) { BT_ERR("Failed to get random bytes for ECC keys"); - return 0x1f; /* unspecified error */ + return BT_HCI_ERR_UNSPECIFIED; } - rc = ecc_make_key(&pkey, private_key, random); + rc = ecc_make_key(pkey, private_key, random); if (rc == TC_CRYPTO_FAIL) { BT_ERR("Failed to create ECC public/private pair"); - return 0x1f; /* unspecified error */ + return BT_HCI_ERR_UNSPECIFIED; } /* make sure generated key isn't debug key */ } while (memcmp(private_key, debug_private_key, 32) == 0); - - memcpy(public_key, pkey.x, 32); - memcpy(&public_key[32], pkey.y, 32); #else - memcpy(public_key, debug_public_key, 64); + memcpy(pkey, debug_public_key, 64); memcpy(private_key, debug_private_key, 32); #endif return 0; @@ -130,18 +122,19 @@ static void emulate_le_p256_public_key_cmd(struct net_buf *buf) struct bt_hci_evt_le_p256_public_key_complete *evt; struct bt_hci_evt_le_meta_event *meta; struct bt_hci_evt_hdr *hdr; + uint8_t status; + EccPoint pkey; - BT_DBG(); + BT_DBG(""); net_buf_unref(buf); send_cmd_status(BT_HCI_OP_LE_P256_PUBLIC_KEY, 0); - buf = bt_buf_get_evt(BT_HCI_EVT_LE_META_EVENT, K_FOREVER); - if (!buf) { - BT_ERR("No available event buffers!"); - return; - } + status = generate_keys(&pkey, private_key); + + buf = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(buf, BT_BUF_EVT); hdr = net_buf_add(buf, sizeof(*hdr)); hdr->evt = BT_HCI_EVT_LE_META_EVENT; @@ -151,10 +144,13 @@ static void emulate_le_p256_public_key_cmd(struct net_buf *buf) meta->subevent = BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE; evt = net_buf_add(buf, sizeof(*evt)); + evt->status = status; - evt->status = generate_keys(evt->key, private_key); - if (evt->status) { + if (status) { memset(evt->key, 0, sizeof(evt->key)); + } else { + memcpy(evt->key, pkey.x, 32); + memcpy(&evt->key[32], pkey.y, 32); } bt_recv(buf); @@ -166,24 +162,39 @@ static void emulate_le_generate_dhkey(struct net_buf *buf) struct bt_hci_cp_le_generate_dhkey *cmd; struct bt_hci_evt_le_meta_event *meta; struct bt_hci_evt_hdr *hdr; - EccPoint pk; - - cmd = (void *)buf->data + sizeof(struct bt_hci_cmd_hdr); + int32_t ret; + /* The following large stack variables are never needed at the same + * time, so we save some stack space by putting them in a union. + */ + union { + EccPoint pk; + uint32_t dhkey[8]; + } ecc; + + if (buf->len < sizeof(*cmd)) { + send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, + BT_HCI_ERR_INVALID_PARAMS); + return; + } - /* TODO verify cmd parameters? */ send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, 0); - memcpy(pk.x, cmd->key, 32); - memcpy(pk.y, &cmd->key[32], 32); + 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); net_buf_unref(buf); - buf = bt_buf_get_evt(BT_HCI_EVT_LE_META_EVENT, K_FOREVER); - if (!buf) { - BT_ERR("No available event buffers!"); - return; + if (ecc_valid_public_key(&ecc.pk) < 0) { + ret = TC_CRYPTO_FAIL; + } else { + ret = ecdh_shared_secret(ecc.dhkey, &ecc.pk, private_key); } + buf = bt_buf_get_rx(K_FOREVER); + bt_buf_set_type(buf, BT_BUF_EVT); + hdr = net_buf_add(buf, sizeof(*hdr)); hdr->evt = BT_HCI_EVT_LE_META_EVENT; hdr->len = sizeof(*meta) + sizeof(*evt); @@ -192,19 +203,13 @@ static void emulate_le_generate_dhkey(struct net_buf *buf) meta->subevent = BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE; evt = net_buf_add(buf, sizeof(*evt)); - evt->status = 0; - - if (ecc_valid_public_key(&pk) < 0) { - evt->status = 0x1f; /* unspecified error */ - memset(evt->dhkey, 0, sizeof(evt->dhkey)); - bt_recv(buf); - return; - } - if (ecdh_shared_secret((uint32_t *)evt->dhkey, &pk, private_key) - == TC_CRYPTO_FAIL) { - evt->status = 0x1f; /* unspecified error */ + if (ret == TC_CRYPTO_FAIL) { + evt->status = BT_HCI_ERR_UNSPECIFIED; memset(evt->dhkey, 0, sizeof(evt->dhkey)); + } else { + evt->status = 0; + memcpy(evt->dhkey, ecc.dhkey, sizeof(ecc.dhkey)); } bt_recv(buf); diff --git a/subsys/bluetooth/host/hci_raw.c b/subsys/bluetooth/host/hci_raw.c index 5a6d4fb4b81b..28bc85a250a7 100644 --- a/subsys/bluetooth/host/hci_raw.c +++ b/subsys/bluetooth/host/hci_raw.c @@ -29,13 +29,8 @@ static struct k_fifo *raw_rx; -/* ACL incoming buffers */ -NET_BUF_POOL_DEFINE(acl_in_pool, CONFIG_BLUETOOTH_ACL_IN_COUNT, - BT_BUF_ACL_IN_SIZE, sizeof(uint8_t), NULL); - -/* HCI event buffers */ -NET_BUF_POOL_DEFINE(hci_evt_pool, CONFIG_BLUETOOTH_HCI_EVT_COUNT, - BT_BUF_EVT_SIZE, sizeof(uint8_t), NULL); +NET_BUF_POOL_DEFINE(hci_rx_pool, CONFIG_BLUETOOTH_RX_BUF_COUNT, + BT_BUF_RX_SIZE, BT_BUF_USER_DATA_MIN, NULL); struct bt_dev_raw bt_dev; @@ -64,11 +59,16 @@ void bt_hci_driver_unregister(struct bt_hci_driver *drv) bt_dev.drv = NULL; } +struct net_buf *bt_buf_get_rx(int timeout) +{ + return net_buf_alloc(&hci_rx_pool, timeout); +} + struct net_buf *bt_buf_get_evt(uint8_t opcode, int timeout) { struct net_buf *buf; - buf = net_buf_alloc(&hci_evt_pool, timeout); + buf = net_buf_alloc(&hci_rx_pool, timeout); if (buf) { bt_buf_set_type(buf, BT_BUF_EVT); } @@ -80,7 +80,7 @@ struct net_buf *bt_buf_get_acl(int32_t timeout) { struct net_buf *buf; - buf = net_buf_alloc(&acl_in_pool, timeout); + buf = net_buf_alloc(&hci_rx_pool, timeout); if (buf) { bt_buf_set_type(buf, BT_BUF_ACL_IN); } @@ -100,6 +100,11 @@ int bt_recv(struct net_buf *buf) return 0; } +int bt_recv_prio(struct net_buf *buf) +{ + return bt_recv(buf); +} + int bt_send(struct net_buf *buf) { BT_DBG("buf %p len %u", buf, buf->len); diff --git a/subsys/bluetooth/host/hfp_hf.c b/subsys/bluetooth/host/hfp_hf.c index 0876f117611c..e6df2311ea49 100644 --- a/subsys/bluetooth/host/hfp_hf.c +++ b/subsys/bluetooth/host/hfp_hf.c @@ -37,6 +37,8 @@ #define BT_DBG(fmt, ...) #endif +#define MAX_IND_STR_LEN 17 + struct bt_hfp_hf_cb *bt_hf; NET_BUF_POOL_DEFINE(hf_pool, CONFIG_BLUETOOTH_MAX_CONN + 1, @@ -45,6 +47,21 @@ NET_BUF_POOL_DEFINE(hf_pool, CONFIG_BLUETOOTH_MAX_CONN + 1, static struct bt_hfp_hf bt_hfp_hf_pool[CONFIG_BLUETOOTH_MAX_CONN]; +/* The order should follow the enum hfp_hf_ag_indicators */ +static const struct { + char *name; + uint32_t min; + uint32_t max; +} ag_ind[] = { + {"service", 0, 1}, /* HF_SERVICE_IND */ + {"call", 0, 1}, /* HF_CALL_IND */ + {"callsetup", 0, 3}, /* HF_CALL_SETUP_IND */ + {"callheld", 0, 2}, /* HF_CALL_HELD_IND */ + {"signal", 0, 5}, /* HF_SINGNAL_IND */ + {"roam", 0, 1}, /* HF_ROAM_IND */ + {"battchg", 0, 5} /* HF_BATTERY_IND */ +}; + void hf_slc_error(struct at_client *hf_at) { BT_ERR("SLC error: disconnecting"); @@ -121,16 +138,260 @@ int brsf_resp(struct at_client *hf_at, struct net_buf *buf) return 0; } +static void cind_handle_values(struct at_client *hf_at, uint32_t index, + char *name, uint32_t min, uint32_t max) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int i; + + BT_DBG("index: %u, name: %s, min: %u, max:%u", index, name, min, max); + + for (i = 0; i < ARRAY_SIZE(ag_ind); i++) { + if (strcmp(name, ag_ind[i].name) != 0) { + continue; + } + if (min != ag_ind[i].min || max != ag_ind[i].max) { + BT_ERR("%s indicator min/max value not matching", name); + } + + hf->ind_table[index] = i; + break; + } +} + +int cind_handle(struct at_client *hf_at) +{ + uint32_t index = 0; + + /* Parsing Example: CIND: ("call",(0,1)) etc.. */ + while (at_has_next_list(hf_at)) { + char name[MAX_IND_STR_LEN]; + uint32_t min, max; + + if (at_open_list(hf_at) < 0) { + BT_ERR("Could not get open list"); + goto error; + } + + if (at_list_get_string(hf_at, name, sizeof(name)) < 0) { + BT_ERR("Could not get string"); + goto error; + } + + if (at_open_list(hf_at) < 0) { + BT_ERR("Could not get open list"); + goto error; + } + + if (at_list_get_range(hf_at, &min, &max) < 0) { + BT_ERR("Could not get range"); + goto error; + } + + if (at_close_list(hf_at) < 0) { + BT_ERR("Could not get close list"); + goto error; + } + + if (at_close_list(hf_at) < 0) { + BT_ERR("Could not get close list"); + goto error; + } + + cind_handle_values(hf_at, index, name, min, max); + index++; + } + + return 0; +error: + BT_ERR("Error on CIND response"); + hf_slc_error(hf_at); + return -EINVAL; +} + +int cind_resp(struct at_client *hf_at, struct net_buf *buf) +{ + int err; + + err = at_parse_cmd_input(hf_at, buf, "CIND", cind_handle); + if (err < 0) { + BT_ERR("Error parsing CMD input"); + hf_slc_error(hf_at); + } + + return 0; +} + +void ag_indicator_handle_values(struct at_client *hf_at, uint32_t index, + uint32_t value) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn; + + BT_DBG("Index :%u, Value :%u", index, value); + + if (index >= ARRAY_SIZE(ag_ind)) { + BT_ERR("Max only %lu indicators are supported", + ARRAY_SIZE(ag_ind)); + return; + } + + if (value > ag_ind[hf->ind_table[index]].max || + value < ag_ind[hf->ind_table[index]].min) { + BT_ERR("Indicators out of range - value: %u", value); + return; + } + + switch (hf->ind_table[index]) { + case HF_SERVICE_IND: + if (bt_hf->service) { + bt_hf->service(conn, value); + } + break; + case HF_CALL_IND: + if (bt_hf->call) { + bt_hf->call(conn, value); + } + break; + case HF_CALL_SETUP_IND: + if (bt_hf->call_setup) { + bt_hf->call_setup(conn, value); + } + break; + case HF_CALL_HELD_IND: + if (bt_hf->call_held) { + bt_hf->call_held(conn, value); + } + break; + case HF_SINGNAL_IND: + if (bt_hf->signal) { + bt_hf->signal(conn, value); + } + break; + case HF_ROAM_IND: + if (bt_hf->roam) { + bt_hf->roam(conn, value); + } + break; + case HF_BATTERY_IND: + if (bt_hf->battery) { + bt_hf->battery(conn, value); + } + break; + default: + BT_ERR("Unknown AG indicator"); + break; + } +} + +int cind_status_handle(struct at_client *hf_at) +{ + uint32_t index = 0; + + while (at_has_next_list(hf_at)) { + uint32_t value; + int ret; + + ret = at_get_number(hf_at, &value); + if (ret < 0) { + BT_ERR("could not get the value"); + return ret; + } + + ag_indicator_handle_values(hf_at, index, value); + + index++; + } + + return 0; +} + +int cind_status_resp(struct at_client *hf_at, struct net_buf *buf) +{ + int err; + + err = at_parse_cmd_input(hf_at, buf, "CIND", cind_status_handle); + if (err < 0) { + BT_ERR("Error parsing CMD input"); + hf_slc_error(hf_at); + } + + return 0; +} + +int cmer_finish(struct at_client *hf_at, struct net_buf *buf, + enum at_result result) +{ + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + return 0; +} + +int cind_status_finish(struct at_client *hf_at, struct net_buf *buf, + enum at_result result) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + err = hfp_hf_send_cmd(hf, NULL, cmer_finish, "AT+CMER=3,0,0,1"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + +int cind_finish(struct at_client *hf_at, struct net_buf *buf, + enum at_result result) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + err = hfp_hf_send_cmd(hf, cind_status_resp, cind_status_finish, + "AT+CIND?"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + int brsf_finish(struct at_client *hf_at, struct net_buf *buf, enum at_result result) { + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + if (result != AT_RESULT_OK) { BT_ERR("SLC Connection ERROR in response"); hf_slc_error(hf_at); return -EINVAL; } - /* Continue with SLC creation */ + err = hfp_hf_send_cmd(hf, cind_resp, cind_finish, "AT+CIND=?"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + return 0; } @@ -187,6 +448,7 @@ static int bt_hfp_hf_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc) for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) { struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i]; + int j; if (hf->rfcomm_dlc.session) { continue; @@ -203,6 +465,10 @@ static int bt_hfp_hf_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc) /* Set the supported features*/ hf->hf_features = BT_HFP_HF_SUPPORTED_FEATURES; + for (j = 0; j < HF_MAX_AG_INDICATORS; j++) { + hf->ind_table[j] = -1; + } + return 0; } diff --git a/subsys/bluetooth/host/hfp_internal.h b/subsys/bluetooth/host/hfp_internal.h index caaa7f78c27f..1f911e3f1152 100644 --- a/subsys/bluetooth/host/hfp_internal.h +++ b/subsys/bluetooth/host/hfp_internal.h @@ -19,7 +19,7 @@ */ #define BLUETOOTH_HFP_MAX_MTU 140 -#define BLUETOOTH_HF_CLIENT_MAX_PDU 20 +#define BLUETOOTH_HF_CLIENT_MAX_PDU BLUETOOTH_HFP_MAX_MTU /* HFP AG Features */ #define BT_HFP_AG_FEATURE_3WAY_CALL 0x00000001 /* Three-way calling */ @@ -51,7 +51,8 @@ #define BT_HFP_HF_SUPPORTED_FEATURES (BT_HFP_HF_FEATURE_CLI | \ BT_HFP_HF_FEATURE_VOLUME) -#define HF_MAX_BUF_LEN 20 +#define HF_MAX_BUF_LEN BLUETOOTH_HF_CLIENT_MAX_PDU +#define HF_MAX_AG_INDICATORS 20 struct bt_hfp_hf { struct bt_rfcomm_dlc rfcomm_dlc; @@ -59,4 +60,15 @@ struct bt_hfp_hf { struct at_client at; uint32_t hf_features; uint32_t ag_features; + int8_t ind_table[HF_MAX_AG_INDICATORS]; +}; + +enum hfp_hf_ag_indicators { + HF_SERVICE_IND, + HF_CALL_IND, + HF_CALL_SETUP_IND, + HF_CALL_HELD_IND, + HF_SINGNAL_IND, + HF_ROAM_IND, + HF_BATTERY_IND }; diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index f3c9d6c2dc7f..7cf2074d631d 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -41,7 +41,7 @@ #define LE_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, chan.rtx_work) #define L2CAP_LE_MIN_MTU 23 -#define L2CAP_LE_MAX_CREDITS (CONFIG_BLUETOOTH_ACL_IN_COUNT - 1) +#define L2CAP_LE_MAX_CREDITS (CONFIG_BLUETOOTH_RX_BUF_COUNT - 1) #define L2CAP_LE_CREDITS_THRESHOLD (L2CAP_LE_MAX_CREDITS / 2) #define L2CAP_LE_CID_DYN_START 0x0040 @@ -58,7 +58,7 @@ /* Size of MTU is based on the maximum amount of data the buffer can hold * excluding ACL and driver headers. */ -#define BT_L2CAP_MAX_LE_MPS CONFIG_BLUETOOTH_L2CAP_IN_MTU +#define BT_L2CAP_MAX_LE_MPS CONFIG_BLUETOOTH_RX_BUF_LEN /* For now use MPS - SDU length to disable segmentation */ #define BT_L2CAP_MAX_LE_MTU (BT_L2CAP_MAX_LE_MPS - 2) @@ -535,7 +535,7 @@ static void l2cap_send_reject(struct bt_conn *conn, uint8_t ident, rej->reason = sys_cpu_to_le16(reason); if (data) { - memcpy(net_buf_add(buf, data_len), data, data_len); + net_buf_add_mem(buf, data, data_len); } bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); @@ -1225,7 +1225,7 @@ static void l2cap_chan_le_recv_sdu(struct bt_l2cap_le_chan *chan, } len = min(net_buf_tailroom(frag), buf->len); - memcpy(net_buf_add(frag, len), buf->data, len); + net_buf_add_mem(frag, buf->data, len); net_buf_pull(buf, len); BT_DBG("frag %p len %u", frag, frag->len); @@ -1587,7 +1587,7 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, len = min(net_buf_tailroom(seg), ch->tx.mps - sdu_hdr_len); /* Limit if original buffer is smaller than the segment */ len = min(buf->len, len); - memcpy(net_buf_add(seg, len), buf->data, len); + net_buf_add_mem(seg, buf->data, len); net_buf_pull(buf, len); BT_DBG("ch %p seg %p len %u", ch, seg, seg->len); diff --git a/subsys/bluetooth/host/l2cap_br.c b/subsys/bluetooth/host/l2cap_br.c index 2ddc78ad4eb1..cb9696295793 100644 --- a/subsys/bluetooth/host/l2cap_br.c +++ b/subsys/bluetooth/host/l2cap_br.c @@ -68,7 +68,7 @@ /* Size of MTU is based on the maximum amount of data the buffer can hold * excluding ACL and driver headers. */ -#define L2CAP_BR_MAX_MTU CONFIG_BLUETOOTH_L2CAP_IN_MTU +#define L2CAP_BR_MAX_MTU CONFIG_BLUETOOTH_RX_BUF_LEN /* * L2CAP extended feature mask: @@ -967,7 +967,7 @@ static void l2cap_br_send_reject(struct bt_conn *conn, uint8_t ident, * table 4.4] */ if (data) { - memcpy(net_buf_add(buf, data_len), data, data_len); + net_buf_add_mem(buf, data, data_len); } bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf); diff --git a/subsys/bluetooth/host/rfcomm.c b/subsys/bluetooth/host/rfcomm.c index 25154676c05d..1b9984f7eb11 100644 --- a/subsys/bluetooth/host/rfcomm.c +++ b/subsys/bluetooth/host/rfcomm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -47,10 +48,18 @@ #define RFCOMM_MIN_MTU BT_RFCOMM_SIG_MIN_MTU #define RFCOMM_DEFAULT_MTU 127 -#define RFCOMM_MAX_CREDITS (CONFIG_BLUETOOTH_ACL_IN_COUNT - 1) +#define RFCOMM_MAX_CREDITS (CONFIG_BLUETOOTH_RX_BUF_COUNT - 1) #define RFCOMM_CREDITS_THRESHOLD (RFCOMM_MAX_CREDITS / 2) #define RFCOMM_DEFAULT_CREDIT RFCOMM_MAX_CREDITS +#define RFCOMM_CONN_TIMEOUT K_SECONDS(60) +#define RFCOMM_DISC_TIMEOUT K_SECONDS(20) +#define RFCOMM_IDLE_TIMEOUT K_SECONDS(2) + +#define DLC_RTX(_w) CONTAINER_OF(_w, struct bt_rfcomm_dlc, rtx_work) + +#define SESSION_RTX(_w) CONTAINER_OF(_w, struct bt_rfcomm_session, rtx_work) + static struct bt_rfcomm_server *servers; /* Pool for outgoing RFCOMM control packets, min MTU is 23 */ @@ -242,8 +251,20 @@ static void rfcomm_dlc_destroy(struct bt_rfcomm_dlc *dlc) { BT_DBG("dlc %p", dlc); + if (!dlc->session->dlcs) { + /* Start an idle timer. Incase we initiate session disconnect + * then this will be restarted as DISC timer + */ + k_delayed_work_submit(&dlc->session->rtx_work, + RFCOMM_IDLE_TIMEOUT); + } + + k_delayed_work_cancel(&dlc->rtx_work); dlc->state = BT_RFCOMM_STATE_IDLE; dlc->session = NULL; + + stack_analyze("dlc stack", dlc->stack, sizeof(dlc->stack)); + if (dlc->ops && dlc->ops->disconnected) { dlc->ops->disconnected(dlc); } @@ -322,7 +343,7 @@ static int rfcomm_send_sabm(struct bt_rfcomm_session *session, uint8_t dlci) struct net_buf *buf; uint8_t cr, fcs; - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); hdr = net_buf_add(buf, sizeof(*hdr)); cr = BT_RFCOMM_CMD_CR(session->role); @@ -336,6 +357,38 @@ static int rfcomm_send_sabm(struct bt_rfcomm_session *session, uint8_t dlci) return bt_l2cap_chan_send(&session->br_chan.chan, buf); } +static int rfcomm_send_disc(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t fcs, cr; + + BT_DBG("dlci %d", dlci); + + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_RESP_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_DISC, BT_RFCOMM_PF_NON_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static void rfcomm_session_disconnect(struct bt_rfcomm_session *session) +{ + if (session->dlcs) { + return; + } + + session->state = BT_RFCOMM_STATE_DISCONNECTING; + rfcomm_send_disc(session, 0); + k_delayed_work_submit(&session->rtx_work, RFCOMM_DISC_TIMEOUT); +} + static struct net_buf *rfcomm_make_uih_msg(struct bt_rfcomm_session *session, uint8_t cr, uint8_t type, uint8_t len) @@ -345,7 +398,7 @@ static struct net_buf *rfcomm_make_uih_msg(struct bt_rfcomm_session *session, struct net_buf *buf; uint8_t hdr_cr; - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); hdr = net_buf_add(buf, sizeof(*hdr)); hdr_cr = BT_RFCOMM_UIH_CR(session->role); @@ -382,10 +435,23 @@ static void rfcomm_disconnected(struct bt_l2cap_chan *chan) BT_DBG("Session %p", session); + k_delayed_work_cancel(&session->rtx_work); rfcomm_session_disconnected(session); session->state = BT_RFCOMM_STATE_IDLE; } +static void rfcomm_dlc_rtx_timeout(struct k_work *work) +{ + struct bt_rfcomm_dlc *dlc = DLC_RTX(work); + struct bt_rfcomm_session *session = dlc->session; + + BT_WARN("dlc %p state %d timeout", dlc, dlc->state); + + rfcomm_dlcs_remove_dlci(session->dlcs, dlc->dlci); + rfcomm_dlc_disconnect(dlc); + rfcomm_session_disconnect(session); +} + static void rfcomm_dlc_init(struct bt_rfcomm_dlc *dlc, struct bt_rfcomm_session *session, uint8_t dlci, @@ -399,6 +465,10 @@ static void rfcomm_dlc_init(struct bt_rfcomm_dlc *dlc, dlc->state = BT_RFCOMM_STATE_INIT; dlc->role = role; k_sem_init(&dlc->tx_credits, 0, UINT32_MAX); + k_delayed_work_init(&dlc->rtx_work, rfcomm_dlc_rtx_timeout); + + /* Start a conn timer which includes auth as well */ + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_CONN_TIMEOUT); dlc->_next = session->dlcs; session->dlcs = dlc; @@ -442,7 +512,7 @@ static int rfcomm_send_dm(struct bt_rfcomm_session *session, uint8_t dlci) BT_DBG("dlci %d", dlci); - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); hdr = net_buf_add(buf, sizeof(*hdr)); cr = BT_RFCOMM_RESP_CR(session->role); @@ -456,27 +526,6 @@ static int rfcomm_send_dm(struct bt_rfcomm_session *session, uint8_t dlci) return bt_l2cap_chan_send(&session->br_chan.chan, buf); } -static int rfcomm_send_disc(struct bt_rfcomm_session *session, uint8_t dlci) -{ - struct bt_rfcomm_hdr *hdr; - struct net_buf *buf; - uint8_t fcs, cr; - - BT_DBG("dlci %d", dlci); - - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); - - hdr = net_buf_add(buf, sizeof(*hdr)); - cr = BT_RFCOMM_RESP_CR(session->role); - hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); - hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_DISC, BT_RFCOMM_PF_NON_UIH); - hdr->length = BT_RFCOMM_SET_LEN_8(0); - fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); - net_buf_add_u8(buf, fcs); - - return bt_l2cap_chan_send(&session->br_chan.chan, buf); -} - static void rfcomm_dlc_tx_thread(void *p1, void *p2, void *p3) { struct bt_rfcomm_dlc *dlc = p1; @@ -534,6 +583,7 @@ static void rfcomm_dlc_tx_thread(void *p1, void *p2, void *p3) if (dlc->state == BT_RFCOMM_STATE_DISCONNECTING) { rfcomm_send_disc(dlc->session, dlc->dlci); + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); } else { rfcomm_dlc_destroy(dlc); } @@ -547,7 +597,7 @@ static int rfcomm_send_ua(struct bt_rfcomm_session *session, uint8_t dlci) struct net_buf *buf; uint8_t cr, fcs; - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); hdr = net_buf_add(buf, sizeof(*hdr)); cr = BT_RFCOMM_RESP_CR(session->role); @@ -610,7 +660,39 @@ static int rfcomm_send_rpn(struct bt_rfcomm_session *session, uint8_t cr, buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_RPN, sizeof(*rpn)); - memcpy(net_buf_add(buf, sizeof(*rpn)), rpn, sizeof(*rpn)); + net_buf_add_mem(buf, rpn, sizeof(*rpn)); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_test(struct bt_rfcomm_session *session, uint8_t cr, + uint8_t *pattern, uint8_t len) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_TEST, len); + + net_buf_add_mem(buf, pattern, len); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_nsc(struct bt_rfcomm_session *session, uint8_t cmd_type) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, BT_RFCOMM_MSG_RESP_CR, + BT_RFCOMM_NSC, sizeof(cmd_type)); + + net_buf_add_u8(buf, cmd_type); fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); net_buf_add_u8(buf, fcs); @@ -624,6 +706,9 @@ static void rfcomm_dlc_connected(struct bt_rfcomm_dlc *dlc) rfcomm_send_msc(dlc, BT_RFCOMM_MSG_CMD_CR); + /* Cancel conn timer */ + k_delayed_work_cancel(&dlc->rtx_work); + k_fifo_init(&dlc->tx_queue); k_thread_spawn(dlc->stack, sizeof(dlc->stack), rfcomm_dlc_tx_thread, dlc, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); @@ -688,6 +773,7 @@ static int rfcomm_dlc_close(struct bt_rfcomm_dlc *dlc) case BT_RFCOMM_STATE_CONFIG: dlc->state = BT_RFCOMM_STATE_DISCONNECTING; rfcomm_send_disc(dlc->session, dlc->dlci); + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); break; case BT_RFCOMM_STATE_CONNECTED: dlc->state = BT_RFCOMM_STATE_DISCONNECTING; @@ -739,7 +825,6 @@ static void rfcomm_handle_sabm(struct bt_rfcomm_session *session, uint8_t dlci) switch (result) { case RFCOMM_SECURITY_PENDING: dlc->state = BT_RFCOMM_STATE_SECURITY_PENDING; - /* TODO: Start an auth timer */ return; case RFCOMM_SECURITY_PASSED: break; @@ -754,6 +839,9 @@ static void rfcomm_handle_sabm(struct bt_rfcomm_session *session, uint8_t dlci) return; } + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); + rfcomm_dlc_connected(dlc); } } @@ -797,7 +885,7 @@ static int rfcomm_send_credit(struct bt_rfcomm_dlc *dlc, uint8_t credits) BT_DBG("Dlc %p credits %d", dlc, credits); - buf = bt_l2cap_create_pdu(&rfcomm_session_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&rfcomm_session_pool, 0); hdr = net_buf_add(buf, sizeof(*hdr)); cr = BT_RFCOMM_UIH_CR(dlc->session->role); @@ -836,16 +924,6 @@ static int rfcomm_dlc_start(struct bt_rfcomm_dlc *dlc) return 0; } -static void rfcomm_session_disconnect(struct bt_rfcomm_session *session) -{ - if (session->dlcs) { - return; - } - - session->state = BT_RFCOMM_STATE_DISCONNECTING; - rfcomm_send_disc(session, 0); -} - static void rfcomm_handle_ua(struct bt_rfcomm_session *session, uint8_t dlci) { struct bt_rfcomm_dlc *dlc, *next; @@ -869,6 +947,8 @@ static void rfcomm_handle_ua(struct bt_rfcomm_session *session, uint8_t dlci) break; case BT_RFCOMM_STATE_DISCONNECTING: session->state = BT_RFCOMM_STATE_DISCONNECTED; + /* Cancel disc timer */ + k_delayed_work_cancel(&session->rtx_work); err = bt_l2cap_chan_disconnect(&session->br_chan.chan); if (err < 0) { session->state = BT_RFCOMM_STATE_IDLE; @@ -1031,6 +1111,8 @@ static void rfcomm_handle_pn(struct bt_rfcomm_session *session, rfcomm_dlc_tx_give_credits(dlc, pn->credits); dlc->state = BT_RFCOMM_STATE_CONFIG; rfcomm_send_pn(dlc, BT_RFCOMM_MSG_RESP_CR); + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); } else { /* If its a command */ if (cr) { @@ -1070,6 +1152,8 @@ static void rfcomm_handle_disc(struct bt_rfcomm_session *session, uint8_t dlci) rfcomm_send_ua(session, dlci); rfcomm_dlc_disconnect(dlc); } else { + /* Cancel idle timer */ + k_delayed_work_cancel(&session->rtx_work); rfcomm_send_ua(session, 0); rfcomm_session_disconnected(session); } @@ -1102,8 +1186,16 @@ static void rfcomm_handle_msg(struct bt_rfcomm_session *session, case BT_RFCOMM_RPN: rfcomm_handle_rpn(session, buf, cr); break; + case BT_RFCOMM_TEST: + if (!cr) { + break; + } + rfcomm_send_test(session, BT_RFCOMM_MSG_RESP_CR, buf->data, + buf->len - 1); + break; default: BT_WARN("Unknown/Unsupported RFCOMM Msg type 0x%02x", msg_type); + rfcomm_send_nsc(session, hdr->type); break; } } @@ -1307,6 +1399,25 @@ static void rfcomm_encrypt_change(struct bt_l2cap_chan *chan, } } +static void rfcomm_session_rtx_timeout(struct k_work *work) +{ + struct bt_rfcomm_session *session = SESSION_RTX(work); + + BT_WARN("session %p state %d timeout", session, session->state); + + switch (session->state) { + case BT_RFCOMM_STATE_CONNECTED: + rfcomm_session_disconnect(session); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + session->state = BT_RFCOMM_STATE_DISCONNECTED; + if (bt_l2cap_chan_disconnect(&session->br_chan.chan) < 0) { + session->state = BT_RFCOMM_STATE_IDLE; + } + break; + } +} + static struct bt_rfcomm_session *rfcomm_session_new(bt_rfcomm_role_t role) { int i; @@ -1330,6 +1441,8 @@ static struct bt_rfcomm_session *rfcomm_session_new(bt_rfcomm_role_t role) session->br_chan.rx.mtu = CONFIG_BLUETOOTH_RFCOMM_L2CAP_MTU; session->state = BT_RFCOMM_STATE_INIT; session->role = role; + k_delayed_work_init(&session->rtx_work, + rfcomm_session_rtx_timeout); return session; } @@ -1401,6 +1514,8 @@ int bt_rfcomm_dlc_connect(struct bt_conn *conn, struct bt_rfcomm_dlc *dlc, if (ret < 0) { goto fail; } + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); break; default: BT_ERR("Invalid session state %d", session->state); @@ -1435,6 +1550,8 @@ int bt_rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc) net_buf_put(&dlc->tx_queue, net_buf_alloc(&dummy_pool, K_NO_WAIT)); + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); + return 0; } diff --git a/subsys/bluetooth/host/rfcomm_internal.h b/subsys/bluetooth/host/rfcomm_internal.h index 8c4a247b7c1e..c59eae25f4bb 100644 --- a/subsys/bluetooth/host/rfcomm_internal.h +++ b/subsys/bluetooth/host/rfcomm_internal.h @@ -24,6 +24,8 @@ struct bt_rfcomm_session { /* L2CAP channel this context is associated with */ struct bt_l2cap_br_chan br_chan; + /* Response Timeout eXpired (RTX) timer */ + struct k_delayed_work rtx_work; struct bt_rfcomm_dlc *dlcs; uint16_t mtu; uint8_t state; @@ -94,6 +96,9 @@ struct bt_rfcomm_rpn { uint16_t param_mask; } __packed; +#define BT_RFCOMM_TEST 0x08 +#define BT_RFCOMM_NSC 0x04 + /* Default RPN Settings */ #define BT_RFCOMM_RPN_BAUD_RATE_9600 0x03 #define BT_RFCOMM_RPN_DATA_BITS_8 0x03 diff --git a/subsys/bluetooth/host/sdp.c b/subsys/bluetooth/host/sdp.c index 657035dcdae1..1f49410f272f 100644 --- a/subsys/bluetooth/host/sdp.c +++ b/subsys/bluetooth/host/sdp.c @@ -62,9 +62,17 @@ NET_BUF_POOL_DEFINE(sdp_pool, CONFIG_BLUETOOTH_MAX_CONN, #define SDP_CLIENT_MTU 64 struct bt_sdp_client { - struct bt_l2cap_br_chan chan; + struct bt_l2cap_br_chan chan; /* list of waiting to be resolved UUID params */ - sys_slist_t reqs; + sys_slist_t reqs; + /* required SDP transaction ID */ + uint16_t tid; + /* UUID params holder being now resolved */ + const struct bt_sdp_discover_params *param; + /* PDU continuation state object */ + struct bt_sdp_pdu_cstate cstate; + /* buffer for collecting record data */ + struct net_buf *rec_buf; }; static struct bt_sdp_client bt_sdp_client_pool[CONFIG_BLUETOOTH_MAX_CONN]; @@ -126,7 +134,7 @@ struct net_buf *bt_sdp_create_pdu(void) { struct net_buf *buf; - buf = bt_l2cap_create_pdu(&sdp_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&sdp_pool, 0); /* NULL is not a possible return due to K_FOREVER */ net_buf_reserve(buf, sizeof(struct bt_sdp_hdr)); @@ -319,13 +327,273 @@ int bt_sdp_register_service(struct bt_sdp_record *service) return 0; } +#define GET_PARAM(__node) \ + CONTAINER_OF(__node, struct bt_sdp_discover_params, _node) + +/* ServiceSearchAttribute PDU, ref to BT Core 4.2, Vol 3, part B, 4.7.1 */ +static int sdp_client_ssa_search(struct bt_sdp_client *session) +{ + const struct bt_sdp_discover_params *param; + struct bt_sdp_hdr *hdr; + struct net_buf *buf; + + /* + * Select proper user params, if session->param is invalid it means + * getting new UUID from top of to be resolved params list. Otherwise + * the context is in a middle of partial SDP PDU responses and cached + * value from context can be used. + */ + if (!session->param) { + param = GET_PARAM(sys_slist_peek_head(&session->reqs)); + } else { + param = session->param; + } + + if (!param) { + BT_WARN("No UUIDs to be resolved on remote"); + return -EINVAL; + } + + buf = bt_l2cap_create_pdu(&sdp_pool, 0); + if (!buf) { + BT_ERR("No bufs for PDU"); + return -ENOMEM; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + + hdr->op_code = BT_SDP_SVC_SEARCH_ATTR_REQ; + /* BT_SDP_SEQ8 means length of sequence is on additional next byte */ + net_buf_add_u8(buf, BT_SDP_SEQ8); + + switch (param->uuid->type) { + case BT_UUID_TYPE_16: + /* Seq length */ + net_buf_add_u8(buf, 0x03); + /* Seq type */ + net_buf_add_u8(buf, BT_SDP_UUID16); + /* Seq value */ + net_buf_add_be16(buf, BT_UUID_16(param->uuid)->val); + break; + case BT_UUID_TYPE_32: + net_buf_add_u8(buf, 0x05); + net_buf_add_u8(buf, BT_SDP_UUID32); + net_buf_add_be32(buf, BT_UUID_32(param->uuid)->val); + break; + case BT_UUID_TYPE_128: + net_buf_add_u8(buf, 0x11); + net_buf_add_u8(buf, BT_SDP_UUID128); + net_buf_add_mem(buf, BT_UUID_128(param->uuid)->val, + ARRAY_SIZE(BT_UUID_128(param->uuid)->val)); + break; + default: + BT_ERR("Unknown UUID type %u", param->uuid->type); + return -EINVAL; + } + + /* Set attribute max bytes count to be returned from server */ + net_buf_add_be16(buf, BT_SDP_MAX_ATTR_LEN); + /* + * Sequence definition where data is sequence of elements and where + * additional next byte points the size of elements within + */ + net_buf_add_u8(buf, BT_SDP_SEQ8); + net_buf_add_u8(buf, 0x05); + /* Data element definition for two following 16bits range elements */ + net_buf_add_u8(buf, BT_SDP_UINT32); + /* Get all attributes. It enables filter out wanted only attributes */ + net_buf_add_be16(buf, 0x0000); + net_buf_add_be16(buf, 0xffff); + + /* + * Update and validate PDU ContinuationState. Initial SSA Request has + * zero length continuation state since no interaction has place with + * server so far, otherwise use the original state taken from remote's + * last response PDU that is cached by SDP client context. + */ + if (session->cstate.length == 0) { + net_buf_add_u8(buf, 0x00); + } else { + net_buf_add_u8(buf, session->cstate.length); + net_buf_add_mem(buf, session->cstate.data, + session->cstate.length); + } + + /* set overall PDU length */ + hdr->param_len = sys_cpu_to_be16(buf->len - sizeof(*hdr)); + + /* Update context param to the one being resolving now */ + session->param = param; + session->tid++; + hdr->tid = sys_cpu_to_be16(session->tid); + + return bt_l2cap_chan_send(&session->chan.chan, buf); +} + +static void sdp_client_params_iterator(struct bt_sdp_client *session) +{ + struct bt_l2cap_chan *chan = &session->chan.chan; + const struct bt_sdp_discover_params *param; + sys_snode_t *node, *node_s; + + SYS_SLIST_FOR_EACH_NODE_SAFE(&session->reqs, node, node_s) { + param = GET_PARAM(node); + if (param != session->param) { + continue; + } + + BT_DBG(""); + + /* Remove already checked UUID node */ + sys_slist_remove(&session->reqs, NULL, node); + /* Invalidate cached param in context */ + session->param = NULL; + /* Reset continuation state in current context */ + memset(&session->cstate, 0, sizeof(session->cstate)); + + /* Check if there's valid next UUID */ + if (!sys_slist_is_empty(&session->reqs)) { + sdp_client_ssa_search(session); + return; + } + + /* No UUID items, disconnect channel */ + bt_l2cap_chan_disconnect(chan); + break; + } +} + +static uint16_t sdp_client_get_total(struct bt_sdp_client *session, + struct net_buf *buf, uint16_t *total) +{ + uint16_t pulled; + uint8_t seq; + + /* + * Pull value of total octets of all attributes available to be + * collected when response gets completed for given UUID. Such info can + * be get from the very first response frame after initial SSA request + * was sent. For subsequent calls related to the same SSA request input + * buf and in/out function parameters stays neutral. + */ + if (session->cstate.length == 0) { + seq = net_buf_pull_u8(buf); + pulled = 1; + switch (seq) { + case BT_SDP_SEQ8: + *total = net_buf_pull_u8(buf); + pulled += 1; + break; + case BT_SDP_SEQ16: + *total = net_buf_pull_be16(buf); + pulled += 2; + break; + default: + BT_WARN("Sequence type 0x%02x not handled", seq); + *total = 0; + break; + } + + BT_DBG("Total %u octets of all attributes", *total); + } else { + pulled = 0; + *total = 0; + } + + return pulled; +} + +static uint16_t get_record_len(struct net_buf *buf) +{ + uint16_t len; + uint8_t seq; + + seq = net_buf_pull_u8(buf); + + switch (seq) { + case BT_SDP_SEQ8: + len = net_buf_pull_u8(buf); + break; + case BT_SDP_SEQ16: + len = net_buf_pull_be16(buf); + break; + default: + BT_WARN("Sequence type 0x%02x not handled", seq); + len = 0; + break; + } + + BT_DBG("Record len %u", len); + + return len; +} + +enum uuid_state { + UUID_NOT_RESOLVED, + UUID_RESOLVED, +}; + +static void sdp_client_notify_result(struct bt_sdp_client *session, + enum uuid_state state) +{ + struct bt_conn *conn = session->chan.chan.conn; + struct bt_sdp_client_result result; + uint16_t rec_len; + uint8_t user_ret; + + result.uuid = session->param->uuid; + + if (state == UUID_NOT_RESOLVED) { + result.resp_buf = NULL; + result.next_record_hint = false; + session->param->func(conn, &result); + return; + } + + while (session->rec_buf->len) { + struct net_buf_simple_state buf_state; + + rec_len = get_record_len(session->rec_buf); + /* tell the user about multi record resolution */ + if (session->rec_buf->len > rec_len) { + result.next_record_hint = true; + } else { + result.next_record_hint = false; + } + + /* save the original session buffer */ + net_buf_simple_save(&session->rec_buf->b, &buf_state); + /* initialize internal result buffer instead of memcpy */ + result.resp_buf = session->rec_buf; + /* + * Set user internal result buffer length as same as record + * length to fake user. User will see the individual record + * length as rec_len insted of whole session rec_buf length. + */ + result.resp_buf->len = rec_len; + + user_ret = session->param->func(conn, &result); + + /* restore original session buffer */ + net_buf_simple_restore(&session->rec_buf->b, &buf_state); + /* + * sync session buffer data length with next record chunk not + * send to user so far + */ + net_buf_pull(session->rec_buf, rec_len); + if (user_ret == BT_SDP_DISCOVER_UUID_STOP) { + break; + } + } +} + static void sdp_client_receive(struct bt_l2cap_chan *chan, struct net_buf *buf) { struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); struct bt_sdp_hdr *hdr = (void *)buf->data; - uint16_t len, tid; - - ARG_UNUSED(session); + struct bt_sdp_pdu_cstate *cstate; + uint16_t len, tid, frame_len; + uint16_t total; BT_DBG("session %p buf %p", session, buf); @@ -349,6 +617,91 @@ static void sdp_client_receive(struct bt_l2cap_chan *chan, struct net_buf *buf) BT_ERR("SDP PDU length mismatch (%u != %u)", buf->len, len); return; } + + if (tid != session->tid) { + BT_ERR("Mismatch transaction ID value in SDP PDU"); + return; + } + + switch (hdr->op_code) { + case BT_SDP_SVC_SEARCH_ATTR_RSP: + /* Get number of attributes in this frame. */ + frame_len = net_buf_pull_be16(buf); + /* Check valid range of attributes length */ + if (frame_len < 2) { + BT_ERR("Invalid attributes data length"); + return; + } + + /* Get PDU continuation state */ + cstate = (struct bt_sdp_pdu_cstate *)(buf->data + frame_len); + + if (cstate->length > BT_SDP_MAX_PDU_CSTATE_LEN) { + BT_ERR("Invalid SDP PDU Continuation State length %u", + cstate->length); + return; + } + + if ((frame_len + cstate->length) > len) { + BT_ERR("Invalid frame payload length"); + return; + } + + /* + * No record found for given UUID. The check catches case when + * current response frame has Continuation State shortest and + * valid and this is the first response frame as well. + */ + if (frame_len == 2 && cstate->length == 0 && + session->cstate.length == 0) { + BT_DBG("record for UUID 0x%s not found", + bt_uuid_str(session->param->uuid)); + /* Call user UUID handler */ + sdp_client_notify_result(session, UUID_NOT_RESOLVED); + net_buf_pull(buf, frame_len + sizeof(cstate->length)); + goto iterate; + } + + /* Get total value of all attributes to be collected */ + frame_len -= sdp_client_get_total(session, buf, &total); + + if (total > net_buf_tailroom(session->rec_buf)) { + BT_WARN("Not enough room for getting records data"); + goto iterate; + } + + net_buf_add_mem(session->rec_buf, buf->data, frame_len); + net_buf_pull(buf, frame_len); + + /* + * check if current response says there's next portion to be + * fetched + */ + if (cstate->length) { + /* Cache original Continuation State in context */ + memcpy(&session->cstate, cstate, + sizeof(struct bt_sdp_pdu_cstate)); + + net_buf_pull(buf, cstate->length + + sizeof(cstate->length)); + + /* Request for next portion of attributes data */ + sdp_client_ssa_search(session); + break; + } + + net_buf_pull(buf, sizeof(cstate->length)); + + BT_DBG("UUID 0x%s resolved", bt_uuid_str(session->param->uuid)); + sdp_client_notify_result(session, UUID_RESOLVED); +iterate: + /* Get next UUID and start resolving it */ + sdp_client_params_iterator(session); + break; + default: + BT_DBG("PDU 0x%0x response not handled", hdr->op_code); + break; + } } static int sdp_client_chan_connect(struct bt_sdp_client *session) @@ -357,13 +710,26 @@ static int sdp_client_chan_connect(struct bt_sdp_client *session) &session->chan.chan, SDP_PSM); } +static struct net_buf *sdp_client_alloc_buf(struct bt_l2cap_chan *chan) +{ + struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); + + BT_DBG("session %p chan %p", session, chan); + + session->param = GET_PARAM(sys_slist_peek_head(&session->reqs)); + + return net_buf_alloc(session->param->pool, K_FOREVER); +} + static void sdp_client_connected(struct bt_l2cap_chan *chan) { struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); BT_DBG("session %p chan %p connected", session, chan); - ARG_UNUSED(session); + session->rec_buf = chan->ops->alloc_buf(chan); + + sdp_client_ssa_search(session); } static void sdp_client_disconnected(struct bt_l2cap_chan *chan) @@ -372,6 +738,8 @@ static void sdp_client_disconnected(struct bt_l2cap_chan *chan) BT_DBG("session %p chan %p disconnected", session, chan); + net_buf_unref(session->rec_buf); + /* * Reset session excluding L2CAP channel member. Let's the channel * resets autonomous. @@ -383,6 +751,7 @@ static struct bt_l2cap_chan_ops sdp_client_chan_ops = { .connected = sdp_client_connected, .disconnected = sdp_client_disconnected, .recv = sdp_client_receive, + .alloc_buf = sdp_client_alloc_buf, }; static struct bt_sdp_client *sdp_client_new_session(struct bt_conn *conn) diff --git a/subsys/bluetooth/host/sdp_internal.h b/subsys/bluetooth/host/sdp_internal.h index 77a2f38ae4b1..f6356838c6e5 100644 --- a/subsys/bluetooth/host/sdp_internal.h +++ b/subsys/bluetooth/host/sdp_internal.h @@ -53,4 +53,16 @@ struct bt_sdp_hdr { uint16_t param_len; } __packed; +/* Allowed attributes length in SSA Request PDU to be taken from server */ +#define BT_SDP_MAX_ATTR_LEN 0xffff + +/* Max allowed length of PDU Continuation State */ +#define BT_SDP_MAX_PDU_CSTATE_LEN 16 + +/* Type mapping SDP PDU Continuation State */ +struct bt_sdp_pdu_cstate { + uint8_t length; + uint8_t data[BT_SDP_MAX_PDU_CSTATE_LEN]; +} __packed; + void bt_sdp_init(void); diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index d3df92f4f24c..46afa126458d 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -336,7 +336,7 @@ static struct net_buf *smp_create_pdu(struct bt_conn *conn, uint8_t op, struct bt_smp_hdr *hdr; struct net_buf *buf; - buf = bt_l2cap_create_pdu(&smp_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&smp_pool, 0); /* NULL is not a possible return due to K_FOREVER */ hdr = net_buf_add(buf, sizeof(*hdr)); diff --git a/subsys/bluetooth/host/smp_null.c b/subsys/bluetooth/host/smp_null.c index 9f4eab5390a0..68406a1b2e31 100644 --- a/subsys/bluetooth/host/smp_null.c +++ b/subsys/bluetooth/host/smp_null.c @@ -62,7 +62,7 @@ static void bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) * Core Specification Vol. 3, Part H, 3.3 */ - buf = bt_l2cap_create_pdu(&smp_pool, K_FOREVER); + buf = bt_l2cap_create_pdu(&smp_pool, 0); /* NULL is not a possible return due to K_FOREVER */ hdr = net_buf_add(buf, sizeof(*hdr)); diff --git a/tests/bluetooth/init/prj_controller_4_0.conf b/tests/bluetooth/init/prj_controller_4_0.conf new file mode 100644 index 000000000000..bc0ebe5089ba --- /dev/null +++ b/tests/bluetooth/init/prj_controller_4_0.conf @@ -0,0 +1,14 @@ +CONFIG_BLUETOOTH=y +CONFIG_BLUETOOTH_CONTROLLER=y +CONFIG_BLUETOOTH_CONTROLLER_LE_PING=n +CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH=n +CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI=n +CONFIG_BLUETOOTH_PERIPHERAL=y +CONFIG_BLUETOOTH_CENTRAL=y +CONFIG_BLUETOOTH_SMP=y +CONFIG_BLUETOOTH_SIGNING=y +CONFIG_BLUETOOTH_SMP_SC_ONLY=y +CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL=y +CONFIG_BLUETOOTH_GATT_DYNAMIC_DB=y +CONFIG_BLUETOOTH_GATT_CLIENT=y +CONFIG_BLUETOOTH_BREDR=n diff --git a/tests/bluetooth/init/prj_controller_dbg.conf b/tests/bluetooth/init/prj_controller_dbg.conf index b90f28f003c3..586c495d613e 100644 --- a/tests/bluetooth/init/prj_controller_dbg.conf +++ b/tests/bluetooth/init/prj_controller_dbg.conf @@ -1,5 +1,7 @@ CONFIG_BLUETOOTH=y CONFIG_BLUETOOTH_CONTROLLER=y +CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI=y +CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR=y CONFIG_BLUETOOTH_PERIPHERAL=y CONFIG_BLUETOOTH_CENTRAL=y CONFIG_BLUETOOTH_SMP=y diff --git a/tests/bluetooth/init/testcase.ini b/tests/bluetooth/init/testcase.ini index e483f00583b1..a68d0c458a2b 100644 --- a/tests/bluetooth/init/testcase.ini +++ b/tests/bluetooth/init/testcase.ini @@ -8,14 +8,21 @@ tags = bluetooth build_only = true extra_args = CONF_FILE=prj_controller.conf # Only run on nRF5x, the only supported radio -platform_whitelist = nrf52_pca10040 nrf51_pca10028 96b_nitrogen +platform_whitelist = nrf52840_pca10056 nrf52_pca10040 nrf51_pca10028 arduino_101_ble 96b_nitrogen + +[test_controller_4_0] +tags = bluetooth +build_only = true +extra_args = CONF_FILE=prj_controller_4_0.conf +# Only run on nRF5x, the only supported radio +platform_whitelist = nrf52840_pca10056 nrf52_pca10040 nrf51_pca10028 arduino_101_ble [test_controller_dbg] tags = bluetooth build_only = true extra_args = CONF_FILE=prj_controller_dbg.conf # Only run on nRF5x, the only supported radio -platform_whitelist = nrf52_pca10040 nrf51_pca10028 96b_nitrogen +platform_whitelist = nrf52840_pca10056 nrf52_pca10040 nrf51_pca10028 arduino_101_ble 96b_nitrogen [test_h5] tags = bluetooth diff --git a/tests/bluetooth/shell/src/main.c b/tests/bluetooth/shell/src/main.c index 12eedba68806..e2e505c07365 100644 --- a/tests/bluetooth/shell/src/main.c +++ b/tests/bluetooth/shell/src/main.c @@ -1929,7 +1929,7 @@ static int cmd_l2cap_send(int argc, char *argv[]) buf = net_buf_alloc(&data_pool, K_FOREVER); net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE); - memcpy(net_buf_add(buf, len), buf_data, len); + net_buf_add_mem(buf, buf_data, len); ret = bt_l2cap_chan_send(&l2cap_chan.chan, buf); if (ret < 0) { printk("Unable to send: %d\n", -ret); @@ -2130,7 +2130,7 @@ static int cmd_rfcomm_send(int argc, char *argv[]) /* Should reserve one byte in tail for FCS */ len = min(rfcomm_dlc.mtu, net_buf_tailroom(buf) - 1); - memcpy(net_buf_add(buf, len), buf_data, len); + net_buf_add_mem(buf, buf_data, len); ret = bt_rfcomm_dlc_send(&rfcomm_dlc, buf); if (ret < 0) { printk("Unable to send: %d\n", -ret); diff --git a/tests/bluetooth/tester/src/l2cap.c b/tests/bluetooth/tester/src/l2cap.c index d1a2b945f544..c4936c136da8 100644 --- a/tests/bluetooth/tester/src/l2cap.c +++ b/tests/bluetooth/tester/src/l2cap.c @@ -218,7 +218,7 @@ static void send_data(uint8_t *data, uint16_t len) buf = net_buf_alloc(&data_pool, K_FOREVER); net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE); - memcpy(net_buf_add(buf, data_len), cmd->data, data_len); + net_buf_add_mem(buf, cmd->data, data_len); ret = bt_l2cap_chan_send(&chan->le.chan, buf); if (ret < 0) { SYS_LOG_ERR("Unable to send data: %d", -ret);