Bluetooth: controller: Controller to Host flow control

The Bluetooth Specification allows for optional Controller to Host flow
control based on the same credit-based mechanism as the Host to
Controller one. This is particularly useful in 2-chip solutions where
the Host and the Controller are connected via a physical link (UART, SPI
or similar) where the Host is sometimes required to ask the Controller
to throttle its data traffic while still making sure that relevant
events get through the line.

This implementation is based on a simple queue of pending events and
data that is populated whenever the Controller detects that the Host is
out of buffers and then emptied whenever the Host notifies the
Controller that is ready to receive data again. Events relevant to the
connections are also queued to preserve the order of arrival.

At this point the Controller ignores the connection handle sent by the
Host and treats all connections equally, and it also queues events even
for connections that have no data pending in the queue. Both this items
can be improved if necessity arises.

Note that Number of Completed Packets will still flow freely from the
Controller to the Host regardless of the pending ACL data packets, which
might lead to inconsistencies in the sequential order of certain
operations that include bi-directional data transfer.

Jira: ZEP-1735

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Carles Cufi 2017-04-07 09:53:02 +02:00 committed by Johan Hedberg
commit 7ebe7da736
7 changed files with 453 additions and 45 deletions

View file

@ -491,8 +491,12 @@ struct bt_hci_write_local_name {
#define BT_BREDR_SCAN_INQUIRY 0x01
#define BT_BREDR_SCAN_PAGE 0x02
#define BT_HCI_CTL_TO_HOST_FLOW_DISABLE 0x00
#define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01
#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031)
struct bt_hci_cp_set_ctl_to_host_flow {
u8_t flow_enable;
} __packed;
#define BT_HCI_OP_HOST_BUFFER_SIZE BT_OP(BT_OGF_BASEBAND, 0x0033)
struct bt_hci_cp_host_buffer_size {

View file

@ -30,6 +30,15 @@ endchoice
comment "BLE Controller configuration"
config BLUETOOTH_CONTROLLER_TO_HOST_FC
bool "Controller to Host Flow Control"
depends on BLUETOOTH_CONN
default n
default y if BLUETOOTH_HCI_RAW
select POLL
help
Enable Controller to Host flow control.
config BLUETOOTH_CONTROLLER_DUP_FILTER_LEN
prompt "Number of addresses in the scan duplicate filter"
int

View file

@ -12,6 +12,7 @@
#include <soc.h>
#include <toolchain.h>
#include <errno.h>
#include <atomic.h>
#include <bluetooth/hci.h>
#include <bluetooth/buf.h>
#include <bluetooth/bluetooth.h>
@ -46,6 +47,18 @@ static s32_t dup_count;
static u32_t dup_curr;
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
s32_t hci_hbuf_total;
u32_t hci_hbuf_sent;
u32_t hci_hbuf_acked;
atomic_t hci_state_mask;
static struct k_poll_signal *hbuf_signal;
#endif
#if defined(CONFIG_BLUETOOTH_CONN)
static u32_t conn_count;
#endif
#define DEFAULT_EVENT_MASK 0x1fffffffffff
#define DEFAULT_LE_EVENT_MASK 0x1f
@ -159,8 +172,6 @@ static void reset(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_evt_cc_status *ccst;
ll_reset();
#if CONFIG_BLUETOOTH_CONTROLLER_DUP_FILTER_LEN > 0
dup_count = -1;
#endif
@ -168,10 +179,113 @@ static void reset(struct net_buf *buf, struct net_buf **evt)
event_mask = DEFAULT_EVENT_MASK;
le_event_mask = DEFAULT_LE_EVENT_MASK;
if (buf) {
ll_reset();
ccst = cmd_complete(evt, sizeof(*ccst));
ccst->status = 0x00;
}
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
hci_hbuf_total = 0;
hci_hbuf_sent = 0;
hci_hbuf_acked = 0;
conn_count = 0;
atomic_set_bit(&hci_state_mask, HCI_STATE_BIT_RESET);
k_poll_signal(hbuf_signal, 0x0);
#endif
}
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
static void set_ctl_to_host_flow(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_set_ctl_to_host_flow *cmd = (void *)buf->data;
struct bt_hci_evt_cc_status *ccst;
ccst = cmd_complete(evt, sizeof(*ccst));
/* require host buffer size before enabling flow control, and
* disallow if any connections are up
*/
if (!hci_hbuf_total || conn_count) {
ccst->status = BT_HCI_ERR_CMD_DISALLOWED;
return;
} else {
ccst->status = 0x00;
}
switch (cmd->flow_enable) {
case BT_HCI_CTL_TO_HOST_FLOW_DISABLE:
if (hci_hbuf_total < 0) {
/* already disabled */
return;
}
break;
case BT_HCI_CTL_TO_HOST_FLOW_ENABLE:
if (hci_hbuf_total > 0) {
/* already enabled */
return;
}
break;
default:
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
hci_hbuf_sent = 0;
hci_hbuf_acked = 0;
hci_hbuf_total = -hci_hbuf_total;
}
static void host_buffer_size(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_host_buffer_size *cmd = (void *)buf->data;
struct bt_hci_evt_cc_status *ccst;
ccst = cmd_complete(evt, sizeof(*ccst));
if (hci_hbuf_total) {
ccst->status = BT_HCI_ERR_CMD_DISALLOWED;
return;
}
/* fragmentation from controller to host not supported, require
* ACL MTU to be at least the LL MTU
*/
if (cmd->acl_mtu < RADIO_LL_LENGTH_OCTETS_RX_MAX) {
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
hci_hbuf_total = -cmd->acl_pkts;
}
static void host_num_completed_packets(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_host_num_completed_packets *cmd = (void *)buf->data;
struct bt_hci_evt_cc_status *ccst;
u32_t count = 0;
int i;
/* special case, no event returned except for error conditions */
if (hci_hbuf_total <= 0) {
ccst = cmd_complete(evt, sizeof(*ccst));
ccst->status = BT_HCI_ERR_CMD_DISALLOWED;
return;
} else if (!conn_count) {
ccst = cmd_complete(evt, sizeof(*ccst));
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
/* leave *evt == NULL so no event is generated */
for (i = 0; i < cmd->num_handles; i++) {
count += cmd->h[i].count;
}
hci_hbuf_acked += count;
k_poll_signal(hbuf_signal, 0x0);
}
#endif
static int ctrl_bb_cmd_handle(u8_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
@ -184,6 +298,19 @@ static int ctrl_bb_cmd_handle(u8_t ocf, struct net_buf *cmd,
reset(cmd, evt);
break;
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
case BT_OCF(BT_HCI_OP_SET_CTL_TO_HOST_FLOW):
set_ctl_to_host_flow(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_HOST_BUFFER_SIZE):
host_buffer_size(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS):
host_num_completed_packets(cmd, evt);
break;
#endif
default:
return -EINVAL;
}
@ -1082,6 +1209,7 @@ fill_report:
}
#if defined(CONFIG_BLUETOOTH_CONN)
static void le_conn_complete(struct pdu_data *pdu_data, u16_t handle,
struct net_buf *buf)
{
@ -1106,6 +1234,8 @@ static void le_conn_complete(struct pdu_data *pdu_data, u16_t handle,
sep->latency = sys_cpu_to_le16(radio_cc->latency);
sep->supv_timeout = sys_cpu_to_le16(radio_cc->timeout);
sep->clock_accuracy = radio_cc->mca;
conn_count++;
}
static void disconn_complete(struct pdu_data *pdu_data, u16_t handle,
@ -1123,6 +1253,8 @@ static void disconn_complete(struct pdu_data *pdu_data, u16_t handle,
ep->status = 0x00;
ep->handle = sys_cpu_to_le16(handle);
ep->reason = *((u8_t *)pdu_data);
conn_count--;
}
static void le_conn_update_complete(struct pdu_data *pdu_data, u16_t handle,
@ -1163,6 +1295,7 @@ static void enc_refresh_complete(struct pdu_data *pdu_data, u16_t handle,
ep->status = 0x00;
ep->handle = sys_cpu_to_le16(handle);
}
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING)
static void auth_payload_timeout_exp(struct pdu_data *pdu_data, u16_t handle,
@ -1212,6 +1345,7 @@ static void encode_control(struct radio_pdu_node_rx *node_rx,
le_advertising_report(pdu_data, b, buf);
break;
#if defined(CONFIG_BLUETOOTH_CONN)
case NODE_RX_TYPE_CONNECTION:
le_conn_complete(pdu_data, handle, buf);
break;
@ -1227,6 +1361,7 @@ static void encode_control(struct radio_pdu_node_rx *node_rx,
case NODE_RX_TYPE_ENC_REFRESH:
enc_refresh_complete(pdu_data, handle, buf);
break;
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING)
case NODE_RX_TYPE_APTO:
@ -1460,6 +1595,7 @@ static void encode_data_ctrl(struct radio_pdu_node_rx *node_rx,
}
}
#if defined(CONFIG_BLUETOOTH_CONN)
void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf)
{
struct bt_hci_acl_hdr *acl;
@ -1484,6 +1620,13 @@ void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf)
acl->len = sys_cpu_to_le16(pdu_data->len);
data = (void *)net_buf_add(buf, pdu_data->len);
memcpy(data, &pdu_data->payload.lldata[0], pdu_data->len);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
if (hci_hbuf_total > 0) {
LL_ASSERT((hci_hbuf_sent - hci_hbuf_acked) <
hci_hbuf_total);
hci_hbuf_sent++;
}
#endif
break;
default:
@ -1492,6 +1635,7 @@ void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf)
}
}
#endif
void hci_evt_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf)
{
@ -1534,3 +1678,54 @@ bool hci_evt_is_discardable(struct radio_pdu_node_rx *node_rx)
return false;
}
}
s8_t hci_get_class(struct radio_pdu_node_rx *node_rx)
{
struct pdu_data *pdu_data;
pdu_data = (struct pdu_data *)node_rx->pdu_data;
if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU) {
switch (node_rx->hdr.type) {
case NODE_RX_TYPE_REPORT:
#if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION)
case NODE_RX_TYPE_ADV_INDICATION:
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR)
case NODE_RX_TYPE_PROFILE:
#endif
return HCI_CLASS_EVT_DISCARDABLE;
case NODE_RX_TYPE_CONNECTION:
return HCI_CLASS_EVT_REQUIRED;
case NODE_RX_TYPE_TERMINATE:
case NODE_RX_TYPE_CONN_UPDATE:
case NODE_RX_TYPE_ENC_REFRESH:
#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI)
case NODE_RX_TYPE_RSSI:
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING)
case NODE_RX_TYPE_APTO:
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)
case NODE_RX_TYPE_CHAN_SEL_ALGO:
#endif
return HCI_CLASS_EVT_CONNECTION;
default:
return -1;
}
} else if (pdu_data->ll_id == PDU_DATA_LLID_CTRL) {
return HCI_CLASS_EVT_CONNECTION;
} else {
return HCI_CLASS_ACL_DATA;
}
}
void hci_init(struct k_poll_signal *signal_host_buf)
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
hbuf_signal = signal_host_buf;
#endif
reset(NULL, NULL);
}

View file

@ -14,6 +14,7 @@
#include <init.h>
#include <device.h>
#include <clock_control.h>
#include <atomic.h>
#include <misc/util.h>
#include <misc/stack.h>
@ -39,6 +40,9 @@
#include "hal/debug.h"
#define NODE_RX(_node) CONTAINER_OF(_node, struct radio_pdu_node_rx, \
hdr.onion.node)
static K_SEM_DEFINE(sem_prio_recv, 0, UINT_MAX);
static K_FIFO_DEFINE(recv_fifo);
@ -51,23 +55,30 @@ static u32_t prio_ts;
static u32_t rx_ts;
#endif
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
static struct k_poll_signal hbuf_signal = K_POLL_SIGNAL_INITIALIZER();
static sys_slist_t hbuf_pend;
static s32_t hbuf_count;
#endif
static void prio_recv_thread(void *p1, void *p2, void *p3)
{
while (1) {
struct radio_pdu_node_rx *node_rx;
struct net_buf *buf;
u8_t num_cmplt;
u16_t handle;
while ((num_cmplt = radio_rx_get(&node_rx, &handle))) {
#if defined(CONFIG_BLUETOOTH_CONN)
struct net_buf *buf;
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();
#endif
}
if (node_rx) {
@ -95,45 +106,210 @@ static void prio_recv_thread(void *p1, void *p2, void *p3)
}
}
static void recv_thread(void *p1, void *p2, void *p3)
static inline struct net_buf *encode_node(struct radio_pdu_node_rx *node_rx,
s8_t class)
{
while (1) {
struct radio_pdu_node_rx *node_rx;
struct pdu_data *pdu_data;
struct net_buf *buf;
struct net_buf *buf = NULL;
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)) {
/* Check if we need to generate an HCI event or ACL data */
switch (class) {
case HCI_CLASS_EVT_DISCARDABLE:
case HCI_CLASS_EVT_REQUIRED:
case HCI_CLASS_EVT_CONNECTION:
if (class == HCI_CLASS_EVT_DISCARDABLE) {
buf = bt_buf_get_rx(K_NO_WAIT);
} else {
buf = bt_buf_get_rx(K_FOREVER);
}
if (buf) {
bt_buf_set_type(buf, BT_BUF_EVT);
hci_evt_encode(node_rx, buf);
}
} else {
break;
#if defined(CONFIG_BLUETOOTH_CONN)
case HCI_CLASS_ACL_DATA:
/* 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);
break;
#endif
default:
LL_ASSERT(0);
break;
}
radio_rx_fc_set(node_rx->hdr.handle, 0);
node_rx->hdr.onion.next = 0;
radio_rx_mem_release(&node_rx);
return buf;
}
static inline struct net_buf *process_node(struct radio_pdu_node_rx *node_rx)
{
s8_t class = hci_get_class(node_rx);
struct net_buf *buf = NULL;
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
if (hbuf_count != -1) {
bool pend = !sys_slist_is_empty(&hbuf_pend);
/* controller to host flow control enabled */
switch (class) {
case HCI_CLASS_EVT_DISCARDABLE:
case HCI_CLASS_EVT_REQUIRED:
break;
case HCI_CLASS_EVT_CONNECTION:
/* for conn-related events, only pend is relevant */
hbuf_count = 1;
/* fallthrough */
case HCI_CLASS_ACL_DATA:
if (pend || !hbuf_count) {
sys_slist_append(&hbuf_pend,
&node_rx->hdr.onion.node);
return NULL;
}
break;
default:
LL_ASSERT(0);
break;
}
}
#endif
/* process regular node from radio */
buf = encode_node(node_rx, class);
return buf;
}
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
static inline struct net_buf *process_hbuf(void)
{
/* shadow total count in case of preemption */
s32_t hbuf_total = hci_hbuf_total;
struct net_buf *buf = NULL;
int reset;
reset = atomic_test_and_clear_bit(&hci_state_mask, HCI_STATE_BIT_RESET);
if (reset) {
/* flush queue, no need to free, the LL has already done it */
sys_slist_init(&hbuf_pend);
}
if (hbuf_total > 0) {
struct radio_pdu_node_rx *node_rx = NULL;
s8_t class, next_class = -1;
sys_snode_t *node = NULL;
/* available host buffers */
hbuf_count = hbuf_total - (hci_hbuf_sent - hci_hbuf_acked);
/* host acked ACL packets, try to dequeue from hbuf */
node = sys_slist_peek_head(&hbuf_pend);
if (node) {
node_rx = NODE_RX(node);
class = hci_get_class(node_rx);
switch (class) {
case HCI_CLASS_EVT_CONNECTION:
node = sys_slist_get(&hbuf_pend);
break;
case HCI_CLASS_ACL_DATA:
if (hbuf_count) {
node = sys_slist_get(&hbuf_pend);
hbuf_count--;
} else {
/* no buffers, HCI will signal */
node = NULL;
}
break;
case HCI_CLASS_EVT_DISCARDABLE:
case HCI_CLASS_EVT_REQUIRED:
default:
LL_ASSERT(0);
break;
}
if (node) {
struct radio_pdu_node_rx *next;
bool empty = true;
node_rx = NODE_RX(node);
node = sys_slist_peek_head(&hbuf_pend);
if (node) {
next = NODE_RX(node);
next_class = hci_get_class(next);
}
empty = sys_slist_is_empty(&hbuf_pend);
buf = encode_node(node_rx, class);
if (!empty && (class == HCI_CLASS_EVT_CONNECTION ||
(class == HCI_CLASS_ACL_DATA &&
hbuf_count))) {
/* more to process, schedule an
* iteration
*/
k_poll_signal(&hbuf_signal, 0x0);
}
}
}
} else {
hbuf_count = -1;
}
return buf;
}
#endif
static void recv_thread(void *p1, void *p2, void *p3)
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
/* @todo: check if the events structure really needs to be static */
static struct k_poll_event events[2] = {
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&hbuf_signal, 0),
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&recv_fifo, 0),
};
#endif
while (1) {
struct radio_pdu_node_rx *node_rx = NULL;
struct net_buf *buf = NULL;
BT_DBG("blocking");
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
int err;
err = k_poll(events, 2, K_FOREVER);
LL_ASSERT(err == 0);
if (events[0].state == K_POLL_STATE_SIGNALED) {
events[0].signal->signaled = 0;
} else if (events[1].state ==
K_POLL_STATE_FIFO_DATA_AVAILABLE) {
node_rx = k_fifo_get(events[1].fifo, 0);
}
events[0].state = K_POLL_STATE_NOT_READY;
events[1].state = K_POLL_STATE_NOT_READY;
/* process host buffers first if any */
buf = process_hbuf();
#else
node_rx = k_fifo_get(&recv_fifo, K_FOREVER);
#endif
BT_DBG("unblocked");
if (node_rx && !buf) {
/* process regular node from radio */
buf = process_node(node_rx);
}
if (buf) {
if (buf->len) {
BT_DBG("Packet in: type:%u len:%u",
@ -161,12 +337,10 @@ static int cmd_handle(struct net_buf *buf)
struct net_buf *evt;
evt = hci_cmd_handle(buf);
if (!evt) {
return -EINVAL;
}
if (evt) {
BT_DBG("Replying with event of %u bytes", evt->len);
bt_recv_prio(evt);
}
return 0;
}
@ -185,9 +359,11 @@ static int hci_driver_send(struct net_buf *buf)
type = bt_buf_get_type(buf);
switch (type) {
#if defined(CONFIG_BLUETOOTH_CONN)
case BT_BUF_ACL_OUT:
err = hci_acl_handle(buf);
break;
#endif
case BT_BUF_CMD:
err = cmd_handle(buf);
break;
@ -212,12 +388,17 @@ static int hci_driver_open(void)
DEBUG_INIT();
err = ll_init(&sem_prio_recv);
if (err) {
BT_ERR("LL initialization failed: %u", err);
return err;
}
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
hci_init(&hbuf_signal);
#else
hci_init(NULL);
#endif
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);

View file

@ -8,10 +8,27 @@
#ifndef _HCI_CONTROLLER_H_
#define _HCI_CONTROLLER_H_
#if defined(CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC)
extern s32_t hci_hbuf_total;
extern u32_t hci_hbuf_sent;
extern u32_t hci_hbuf_acked;
extern atomic_t hci_state_mask;
#define HCI_STATE_BIT_RESET 0
#endif
#define HCI_CLASS_EVT_REQUIRED 0
#define HCI_CLASS_EVT_DISCARDABLE 1
#define HCI_CLASS_EVT_CONNECTION 2
#define HCI_CLASS_ACL_DATA 3
void hci_init(struct k_poll_signal *signal_host_buf);
struct net_buf *hci_cmd_handle(struct net_buf *cmd);
int hci_acl_handle(struct net_buf *acl);
void hci_evt_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf);
s8_t hci_get_class(struct radio_pdu_node_rx *node_rx);
#if defined(CONFIG_BLUETOOTH_CONN)
int hci_acl_handle(struct net_buf *acl);
void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf);
void hci_num_cmplt_encode(struct net_buf *buf, u16_t handle, u8_t num);
bool hci_evt_is_discardable(struct radio_pdu_node_rx *node_rx);
#endif
#endif /* _HCI_CONTROLLER_H_ */

View file

@ -250,6 +250,7 @@ struct radio_le_chan_sel_algo {
struct radio_pdu_node_rx_hdr {
union {
sys_snode_t node; /* used by slist */
void *next; /* used also by k_fifo once pulled */
void *link;
u8_t packet_release_last;

View file

@ -4,6 +4,7 @@ CONFIG_BLUETOOTH_CONTROLLER_WORKER_PRIO=0
CONFIG_BLUETOOTH_CONTROLLER_JOB_PRIO=0
CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED=y
CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED=y
CONFIG_BLUETOOTH_CONTROLLER_TO_HOST_FC=y
CONFIG_BLUETOOTH_PERIPHERAL=y
CONFIG_BLUETOOTH_CENTRAL=y
CONFIG_BLUETOOTH_SMP=y