zephyr/subsys/bluetooth/controller/hci/hci.c

8303 lines
222 KiB
C
Raw Normal View History

/*
* Copyright (c) 2016-2018 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <version.h>
#include <errno.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/drivers/bluetooth/hci_driver.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/hci_vs.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/bluetooth.h>
#include "../host/hci_ecc.h"
#include "util/util.h"
#include "util/memq.h"
#include "util/mem.h"
#include "util/dbuf.h"
#include "hal/ecb.h"
#include "hal/ccm.h"
#include "hal/ticker.h"
#include "ticker/ticker.h"
#include "ll_sw/pdu.h"
#include "ll_sw/lll.h"
#include "lll/lll_adv_types.h"
#include "ll_sw/lll_adv.h"
#include "lll/lll_adv_pdu.h"
#include "ll_sw/lll_scan.h"
#include "lll/lll_df_types.h"
#include "ll_sw/lll_sync.h"
#include "ll_sw/lll_sync_iso.h"
#include "ll_sw/lll_conn.h"
#include "ll_sw/lll_conn_iso.h"
#include "ll_sw/lll_iso_tx.h"
#include "ll_sw/isoal.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ll_sw/ull_adv_types.h"
#include "ll_sw/ull_scan_types.h"
#include "ll_sw/ull_sync_types.h"
#include "ll_sw/ull_conn_types.h"
#include "ll_sw/ull_iso_types.h"
#include "ll_sw/ull_conn_iso_types.h"
#include "ll_sw/ull_conn_iso_internal.h"
#include "ll_sw/ull_df_types.h"
#include "ll_sw/ull_adv_internal.h"
#include "ll_sw/ull_sync_internal.h"
#include "ll_sw/ull_conn_internal.h"
#include "ll_sw/ull_sync_iso_internal.h"
#include "ll_sw/ull_df_internal.h"
#include "ll.h"
#include "ll_feat.h"
#include "ll_settings.h"
#include "hci_internal.h"
#include "hci_vendor.h"
#if defined(CONFIG_BT_HCI_MESH_EXT)
#include "ll_sw/ll_mesh.h"
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if defined(CONFIG_BT_CTLR_DTM_HCI)
#include "ll_sw/ll_test.h"
#endif /* CONFIG_BT_CTLR_DTM_HCI */
#if defined(CONFIG_BT_CTLR_USER_EXT)
#include "hci_user_ext.h"
#endif /* CONFIG_BT_CTLR_USER_EXT */
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_hci
#include "common/log.h"
#include "hal/debug.h"
/* opcode of the HCI command currently being processed. The opcode is stored
* by hci_cmd_handle() and then used during the creation of cmd complete and
* cmd status events to avoid passing it up the call chain.
*/
static uint16_t _opcode;
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* NOTE: Duplicate filter uses two LS bits value of standard advertising modes:
* 0 - Non-Connectable Non-Scannable advertising report
* 1 - Connectable Non-Scannable advertising report
* 2 - Non-Connectable Scannable advertisig report
* 3 - Connectable Scannable advertising report
*
* FIXME: Duplicate filtering of Connectable Directed low and high duty
* cycle. If advertiser changes between Connectable Non-Scannable,
* Connectable Directed low, and high duty cycle without changing
* SID and DID, then such reports will be filtered out by the
* implementation. Needs enhancement to current implementation.
*
* Define a custom duplicate filter mode for periodic advertising:
* 4 - Periodic Advertising report
*/
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
#define DUP_EXT_ADV_MODE_MAX 5
#define DUP_EXT_ADV_MODE_PERIODIC BIT(2)
#else /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
#define DUP_EXT_ADV_MODE_MAX 4
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
#define DUP_EXT_ADV_MODE_COUNT 4
/* Duplicate filter entries, one per Bluetooth address */
static struct dup_entry {
bt_addr_le_t addr;
/* Mask to accumulate advertising PDU type as bitmask */
uint8_t mask;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
struct dup_ext_adv_mode {
uint16_t set_count:5;
uint16_t set_curr:5;
struct dup_ext_adv_set {
uint8_t data_cmplt:1;
struct pdu_adv_adi adi;
} set[CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX];
} adv_mode[DUP_EXT_ADV_MODE_MAX];
#endif
} dup_filter[CONFIG_BT_CTLR_DUP_FILTER_LEN];
/* Duplicate filtering is disabled if count value is set to negative integer */
#define DUP_FILTER_DISABLED (-1)
/* Duplicate filtering array entry count, filtering disabled if negative */
static int32_t dup_count;
/* Duplicate filtering current free entry, overwrites entries after rollover */
static uint32_t dup_curr;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
/* Helper function to reset non-periodic advertising entries in filter table */
static void dup_ext_adv_reset(void);
/* Flag for advertising reports be filtered for duplicates. */
static bool dup_scan;
#else /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
/* Set constant true so that (dup_count >= 0) decides if advertising duplicate
* filter is enabled when Periodic Advertising ADI support is disabled.
*/
static const bool dup_scan = true;
#endif /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
#if defined(CONFIG_BT_HCI_MESH_EXT)
struct scan_filter {
uint8_t count;
uint8_t lengths[CONFIG_BT_CTLR_MESH_SF_PATTERNS];
uint8_t patterns[CONFIG_BT_CTLR_MESH_SF_PATTERNS]
[BT_HCI_MESH_PATTERN_LEN_MAX];
};
static struct scan_filter scan_filters[CONFIG_BT_CTLR_MESH_SCAN_FILTERS];
static uint8_t sf_curr;
#endif
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
int32_t hci_hbuf_total;
uint32_t hci_hbuf_sent;
uint32_t hci_hbuf_acked;
uint16_t hci_hbuf_pend[CONFIG_BT_MAX_CONN];
atomic_t hci_state_mask;
static struct k_poll_signal *hbuf_signal;
#endif
#if defined(CONFIG_BT_CONN)
static uint32_t conn_count;
#endif
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
static uint32_t cis_pending_count;
#endif
/* In HCI event PHY indices start at 1 compare to 0 indexed in aux_ptr field in
* the Common Extended Payload Format in the PDUs.
*/
#define HCI_AUX_PHY_TO_HCI_PHY(aux_phy) ((aux_phy) + 1)
#define DEFAULT_EVENT_MASK 0x1fffffffffff
#define DEFAULT_EVENT_MASK_PAGE_2 0x0
#define DEFAULT_LE_EVENT_MASK 0x1f
static uint64_t event_mask = DEFAULT_EVENT_MASK;
static uint64_t event_mask_page_2 = DEFAULT_EVENT_MASK_PAGE_2;
static uint64_t le_event_mask = DEFAULT_LE_EVENT_MASK;
static struct net_buf *cmd_complete_status(uint8_t status);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#define BUF_GET_TIMEOUT K_SECONDS(10)
#if defined(CONFIG_BT_HCI_RAW)
static uint8_t ll_adv_cmds;
__weak int ll_adv_cmds_set(uint8_t adv_cmds)
{
if (!ll_adv_cmds) {
ll_adv_cmds = adv_cmds;
}
if (ll_adv_cmds != adv_cmds) {
return -EINVAL;
}
return 0;
}
__weak int ll_adv_cmds_is_ext(void)
{
return ll_adv_cmds == LL_ADV_CMDS_EXT;
}
#else /* !CONFIG_BT_HCI_RAW */
__weak int ll_adv_cmds_is_ext(void)
{
return 1;
}
#endif /* !CONFIG_BT_HCI_RAW */
static int adv_cmds_legacy_check(struct net_buf **cc_evt)
{
int err;
#if defined(CONFIG_BT_HCI_RAW)
err = ll_adv_cmds_set(LL_ADV_CMDS_LEGACY);
if (err && cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
#else
if (cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
err = -EINVAL;
#endif /* CONFIG_BT_HCI_RAW */
return err;
}
static int adv_cmds_ext_check(struct net_buf **cc_evt)
{
int err;
#if defined(CONFIG_BT_HCI_RAW)
err = ll_adv_cmds_set(LL_ADV_CMDS_EXT);
if (err && cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
#else
err = 0;
#endif /* CONFIG_BT_HCI_RAW */
return err;
}
#else
static inline int adv_cmds_legacy_check(struct net_buf **cc_evt)
{
return 0;
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CONN)
static void le_conn_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf);
#endif /* CONFIG_BT_CONN */
static void hci_evt_create(struct net_buf *buf, uint8_t evt, uint8_t len)
{
struct bt_hci_evt_hdr *hdr;
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->evt = evt;
hdr->len = len;
}
void *hci_cmd_complete(struct net_buf **buf, uint8_t plen)
{
*buf = bt_hci_cmd_complete_create(_opcode, plen);
return net_buf_add(*buf, plen);
}
static struct net_buf *cmd_status(uint8_t status)
{
return bt_hci_cmd_status_create(_opcode, status);
}
static struct net_buf *cmd_complete_status(uint8_t status)
{
struct net_buf *buf;
struct bt_hci_evt_cc_status *ccst;
buf = bt_hci_cmd_complete_create(_opcode, sizeof(*ccst));
ccst = net_buf_add(buf, sizeof(*ccst));
ccst->status = status;
return buf;
}
static void *meta_evt(struct net_buf *buf, uint8_t subevt, uint8_t melen)
{
struct bt_hci_evt_le_meta_event *me;
hci_evt_create(buf, BT_HCI_EVT_LE_META_EVENT, sizeof(*me) + melen);
me = net_buf_add(buf, sizeof(*me));
me->subevent = subevt;
return net_buf_add(buf, melen);
}
#if defined(CONFIG_BT_HCI_MESH_EXT)
static void *mesh_evt(struct net_buf *buf, uint8_t subevt, uint8_t melen)
{
struct bt_hci_evt_mesh *me;
hci_evt_create(buf, BT_HCI_EVT_VENDOR, sizeof(*me) + melen);
me = net_buf_add(buf, sizeof(*me));
me->prefix = BT_HCI_MESH_EVT_PREFIX;
me->subevent = subevt;
return net_buf_add(buf, melen);
}
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if defined(CONFIG_BT_CONN)
static void disconnect(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_disconnect *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_terminate_ind_send(handle, cmd->reason);
*evt = cmd_status(status);
}
static void read_remote_ver_info(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_remote_version_info *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_version_ind_send(handle);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CONN */
static int link_control_cmd_handle(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
switch (ocf) {
#if defined(CONFIG_BT_CONN)
case BT_OCF(BT_HCI_OP_DISCONNECT):
disconnect(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_REMOTE_VERSION_INFO):
read_remote_ver_info(cmd, evt);
break;
#endif /* CONFIG_BT_CONN */
default:
return -EINVAL;
}
return 0;
}
static void set_event_mask(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_set_event_mask *cmd = (void *)buf->data;
event_mask = sys_get_le64(cmd->events);
*evt = cmd_complete_status(0x00);
}
static void set_event_mask_page_2(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_set_event_mask_page_2 *cmd = (void *)buf->data;
event_mask_page_2 = sys_get_le64(cmd->events_page_2);
*evt = cmd_complete_status(0x00);
}
static void reset(struct net_buf *buf, struct net_buf **evt)
{
#if defined(CONFIG_BT_HCI_MESH_EXT)
int i;
for (i = 0; i < ARRAY_SIZE(scan_filters); i++) {
scan_filters[i].count = 0U;
}
sf_curr = 0xFF;
#endif
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
dup_count = DUP_FILTER_DISABLED;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
dup_scan = false;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
/* reset event masks */
event_mask = DEFAULT_EVENT_MASK;
event_mask_page_2 = DEFAULT_EVENT_MASK_PAGE_2;
le_event_mask = DEFAULT_LE_EVENT_MASK;
if (buf) {
ll_reset();
*evt = cmd_complete_status(0x00);
}
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
#if defined(CONFIG_BT_CONN)
conn_count = 0U;
#endif
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
cis_pending_count = 0U;
#endif
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
hci_hbuf_total = 0;
hci_hbuf_sent = 0U;
hci_hbuf_acked = 0U;
(void)memset(hci_hbuf_pend, 0, sizeof(hci_hbuf_pend));
if (buf) {
atomic_set_bit(&hci_state_mask, HCI_STATE_BIT_RESET);
k_poll_signal_raise(hbuf_signal, 0x0);
}
#endif
}
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
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;
uint8_t flow_enable = cmd->flow_enable;
struct bt_hci_evt_cc_status *ccst;
ccst = hci_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 (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 = 0U;
hci_hbuf_acked = 0U;
(void)memset(hci_hbuf_pend, 0, sizeof(hci_hbuf_pend));
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;
uint16_t acl_pkts = sys_le16_to_cpu(cmd->acl_pkts);
uint16_t acl_mtu = sys_le16_to_cpu(cmd->acl_mtu);
struct bt_hci_evt_cc_status *ccst;
ccst = hci_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 (acl_mtu < LL_LENGTH_OCTETS_RX_MAX) {
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
BT_DBG("FC: host buf size: %d", acl_pkts);
hci_hbuf_total = -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;
uint32_t count = 0U;
/* special case, no event returned except for error conditions */
if (hci_hbuf_total <= 0) {
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = BT_HCI_ERR_CMD_DISALLOWED;
return;
} else if (!conn_count) {
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
/* leave *evt == NULL so no event is generated */
for (uint8_t i = 0; i < cmd->num_handles; i++) {
uint16_t h = sys_le16_to_cpu(cmd->h[i].handle);
uint16_t c = sys_le16_to_cpu(cmd->h[i].count);
if ((h >= ARRAY_SIZE(hci_hbuf_pend)) ||
(c > hci_hbuf_pend[h])) {
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = BT_HCI_ERR_INVALID_PARAM;
return;
}
hci_hbuf_pend[h] -= c;
count += c;
}
BT_DBG("FC: acked: %d", count);
hci_hbuf_acked += count;
k_poll_signal_raise(hbuf_signal, 0x0);
}
#endif
#if defined(CONFIG_BT_CTLR_LE_PING)
static void read_auth_payload_timeout(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_auth_payload_timeout *cmd = (void *)buf->data;
struct bt_hci_rp_read_auth_payload_timeout *rp;
uint16_t auth_payload_timeout;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_apto_get(handle, &auth_payload_timeout);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
rp->auth_payload_timeout = sys_cpu_to_le16(auth_payload_timeout);
}
static void write_auth_payload_timeout(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_write_auth_payload_timeout *cmd = (void *)buf->data;
struct bt_hci_rp_write_auth_payload_timeout *rp;
uint16_t auth_payload_timeout;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
auth_payload_timeout = sys_le16_to_cpu(cmd->auth_payload_timeout);
status = ll_apto_set(handle, auth_payload_timeout);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_ISO)
static void configure_data_path(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_configure_data_path *cmd = (void *)buf->data;
struct bt_hci_rp_configure_data_path *rp;
uint8_t *vs_config;
uint8_t status;
vs_config = &cmd->vs_config[0];
status = ll_configure_data_path(cmd->data_path_dir,
cmd->data_path_id,
cmd->vs_config_len,
vs_config);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
}
#endif /* CONFIG_BT_CTLR_ISO */
#if defined(CONFIG_BT_CONN)
static void read_tx_power_level(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_tx_power_level *cmd = (void *)buf->data;
struct bt_hci_rp_read_tx_power_level *rp;
uint16_t handle;
uint8_t status;
uint8_t type;
handle = sys_le16_to_cpu(cmd->handle);
type = cmd->type;
rp = hci_cmd_complete(evt, sizeof(*rp));
status = ll_tx_pwr_lvl_get(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
handle, type, &rp->tx_power_level);
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CONN */
static int ctrl_bb_cmd_handle(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
switch (ocf) {
case BT_OCF(BT_HCI_OP_SET_EVENT_MASK):
set_event_mask(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_RESET):
reset(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_SET_EVENT_MASK_PAGE_2):
set_event_mask_page_2(cmd, evt);
break;
#if defined(CONFIG_BT_CONN)
case BT_OCF(BT_HCI_OP_READ_TX_POWER_LEVEL):
read_tx_power_level(cmd, evt);
break;
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
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
#if defined(CONFIG_BT_CTLR_LE_PING)
case BT_OCF(BT_HCI_OP_READ_AUTH_PAYLOAD_TIMEOUT):
read_auth_payload_timeout(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_WRITE_AUTH_PAYLOAD_TIMEOUT):
write_auth_payload_timeout(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_ISO)
case BT_OCF(BT_HCI_OP_CONFIGURE_DATA_PATH):
configure_data_path(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ISO */
default:
return -EINVAL;
}
return 0;
}
static void read_local_version_info(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_local_version_info *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->hci_version = LL_VERSION_NUMBER;
rp->hci_revision = sys_cpu_to_le16(0);
rp->lmp_version = LL_VERSION_NUMBER;
rp->manufacturer = sys_cpu_to_le16(ll_settings_company_id());
rp->lmp_subversion = sys_cpu_to_le16(ll_settings_subversion_number());
}
static void read_supported_commands(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_supported_commands *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)memset(&rp->commands[0], 0, sizeof(rp->commands));
#if defined(CONFIG_BT_REMOTE_VERSION)
/* Read Remote Version Info. */
rp->commands[2] |= BIT(7);
#endif
/* Set Event Mask, and Reset. */
rp->commands[5] |= BIT(6) | BIT(7);
/* Read TX Power Level. */
rp->commands[10] |= BIT(2);
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
/* Set FC, Host Buffer Size and Host Num Completed */
rp->commands[10] |= BIT(5) | BIT(6) | BIT(7);
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
/* Read Local Version Info, Read Local Supported Features. */
rp->commands[14] |= BIT(3) | BIT(5);
/* Read BD ADDR. */
rp->commands[15] |= BIT(1);
#if defined(CONFIG_BT_CTLR_CONN_RSSI)
/* Read RSSI. */
rp->commands[15] |= BIT(5);
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
/* Set Event Mask Page 2 */
rp->commands[22] |= BIT(2);
/* LE Set Event Mask, LE Read Buffer Size, LE Read Local Supp Feats,
* Set Random Addr
*/
rp->commands[25] |= BIT(0) | BIT(1) | BIT(2) | BIT(4);
#if defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
/* LE Read FAL Size, LE Clear FAL */
rp->commands[26] |= BIT(6) | BIT(7);
/* LE Add Dev to FAL, LE Remove Dev from FAL */
rp->commands[27] |= BIT(0) | BIT(1);
#endif /* CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
/* LE Encrypt, LE Rand */
rp->commands[27] |= BIT(6) | BIT(7);
/* LE Read Supported States */
rp->commands[28] |= BIT(3);
#if defined(CONFIG_BT_BROADCASTER)
/* LE Set Adv Params, LE Read Adv Channel TX Power, LE Set Adv Data */
rp->commands[25] |= BIT(5) | BIT(6) | BIT(7);
/* LE Set Scan Response Data, LE Set Adv Enable */
rp->commands[26] |= BIT(0) | BIT(1);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
/* LE Set Adv Set Random Addr, LE Set Ext Adv Params, LE Set Ext Adv
* Data, LE Set Ext Adv Scan Rsp Data, LE Set Ext Adv Enable, LE Read
* Max Adv Data Len, LE Read Num Supp Adv Sets
*/
rp->commands[36] |= BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7);
/* LE Remove Adv Set, LE Clear Adv Sets */
rp->commands[37] |= BIT(0) | BIT(1);
#if defined(CONFIG_BT_CTLR_ADV_PERIODIC)
/* LE Set PA Params, LE Set PA Data, LE Set PA Enable */
rp->commands[37] |= BIT(2) | BIT(3) | BIT(4);
#endif /* CONFIG_BT_CTLR_ADV_PERIODIC */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
/* LE Set Scan Params, LE Set Scan Enable */
rp->commands[26] |= BIT(2) | BIT(3);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
/* LE Set Extended Scan Params, LE Set Extended Scan Enable */
rp->commands[37] |= BIT(5) | BIT(6);
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
/* LE PA Create Sync, LE PA Create Sync Cancel, LE PA Terminate Sync */
rp->commands[38] |= BIT(0) | BIT(1) | BIT(2);
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST)
/* LE PA Add Device to Periodic Advertiser List,
* LE PA Remove Device from Periodic Advertiser List,
* LE Clear Periodic Advertiser List,
* LE Read Periodic Adveritiser List Size
*/
rp->commands[38] |= BIT(3) | BIT(4) | BIT(5) | BIT(6);
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST */
/* LE Set PA Receive Enable */
rp->commands[40] |= BIT(5);
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CONN)
#if defined(CONFIG_BT_CENTRAL)
/* LE Create Connection, LE Create Connection Cancel */
rp->commands[26] |= BIT(4) | BIT(5);
/* Set Host Channel Classification */
rp->commands[27] |= BIT(3);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
/* LE Extended Create Connection */
rp->commands[37] |= BIT(7);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CTLR_LE_ENC)
/* LE Start Encryption */
rp->commands[28] |= BIT(0);
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
/* LE Set CIG Parameters */
rp->commands[41] |= BIT(7);
/* LE Set CIG Parameters Test, LE Create CIS, LE Remove CIS */
rp->commands[42] |= BIT(0) | BIT(1) | BIT(2);
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
#if defined(CONFIG_BT_CTLR_LE_ENC)
/* LE LTK Request Reply, LE LTK Request Negative Reply */
rp->commands[28] |= BIT(1) | BIT(2);
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
/* LE Accept CIS Request, LE Reject CIS Request */
rp->commands[42] |= BIT(3) | BIT(4);
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#endif /* CONFIG_BT_PERIPHERAL */
/* Disconnect. */
rp->commands[0] |= BIT(5);
/* LE Connection Update, LE Read Channel Map, LE Read Remote Features */
rp->commands[27] |= BIT(2) | BIT(4) | BIT(5);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
/* LE Remote Conn Param Req and Neg Reply */
rp->commands[33] |= BIT(4) | BIT(5);
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CTLR_LE_PING)
/* Read and Write authenticated payload timeout */
rp->commands[32] |= BIT(4) | BIT(5);
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
/* LE Set Data Length, and LE Read Suggested Data Length. */
rp->commands[33] |= BIT(6) | BIT(7);
/* LE Write Suggested Data Length. */
rp->commands[34] |= BIT(0);
/* LE Read Maximum Data Length. */
rp->commands[35] |= BIT(3);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
/* LE Read PHY Command. */
rp->commands[35] |= BIT(4);
/* LE Set Default PHY Command. */
rp->commands[35] |= BIT(5);
/* LE Set PHY Command. */
rp->commands[35] |= BIT(6);
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_DTM_HCI)
/* LE RX Test, LE TX Test, LE Test End */
rp->commands[28] |= BIT(4) | BIT(5) | BIT(6);
/* LE Enhanced RX Test. */
rp->commands[35] |= BIT(7);
/* LE Enhanced TX Test. */
rp->commands[36] |= BIT(0);
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
rp->commands[39] |= BIT(3);
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
rp->commands[39] |= BIT(4);
#endif
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
rp->commands[45] |= BIT(0);
#endif
#endif /* CONFIG_BT_CTLR_DTM_HCI */
#if defined(CONFIG_BT_CTLR_PRIVACY)
/* LE resolving list commands, LE Read Peer RPA */
rp->commands[34] |= BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7);
/* LE Read Local RPA, LE Set AR Enable, Set RPA Timeout */
rp->commands[35] |= BIT(0) | BIT(1) | BIT(2);
/* LE Set Privacy Mode */
rp->commands[39] |= BIT(2);
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_DF)
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
/* LE Set Connectionless CTE Transmit Parameters,
* LE Set Connectionless CTE Transmit Enable
*/
rp->commands[39] |= BIT(5) | BIT(6);
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
/* LE Set Connectionless IQ Sampling Enable */
rp->commands[39] |= BIT(7);
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
/* LE Read Antenna Information */
rp->commands[40] |= BIT(4);
#endif /* CONFIG_BT_CTLR_DF */
#if defined(CONFIG_BT_HCI_RAW) && defined(CONFIG_BT_TINYCRYPT_ECC)
bt_hci_ecc_supported_commands(rp->commands);
#endif /* CONFIG_BT_HCI_RAW && CONFIG_BT_TINYCRYPT_ECC */
/* LE Read TX Power. */
rp->commands[38] |= BIT(7);
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
/* LE Read Buffer Size v2, LE Read ISO TX Sync */
rp->commands[41] |= BIT(5) | BIT(6);
/* LE ISO Transmit Test */
rp->commands[43] |= BIT(5);
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
/* LE ISO Receive Test, LE ISO Read Test Counters */
rp->commands[43] |= BIT(6) | BIT(7);
#if defined(CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY)
/* LE Read ISO Link Quality */
rp->commands[44] |= BIT(2);
#endif /* CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY */
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_ISO)
/* LE Setup ISO Data Path, LE Remove ISO Data Path */
rp->commands[43] |= BIT(3) | BIT(4);
/* LE ISO Test End */
rp->commands[44] |= BIT(0);
#endif /* CONFIG_BT_CTLR_ISO */
#if defined(CONFIG_BT_CTLR_SET_HOST_FEATURE)
/* LE Set Host Feature */
rp->commands[44] |= BIT(1);
#endif /* CONFIG_BT_CTLR_SET_HOST_FEATURE */
#if defined(CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO)
/* Read Supported Codecs */
rp->commands[29] |= BIT(5);
/* Read Supported Codecs [v2], Codec Capabilities, Controller Delay */
rp->commands[45] |= BIT(2) | BIT(3) | BIT(4);
#endif /* CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO */
}
static void read_local_features(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_local_features *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)memset(&rp->features[0], 0x00, sizeof(rp->features));
/* BR/EDR not supported and LE supported */
rp->features[4] = (1 << 5) | (1 << 6);
}
static void read_bd_addr(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_bd_addr *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)ll_addr_read(0, &rp->bdaddr.val[0]);
}
#if defined(CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO)
uint8_t __weak hci_vendor_read_std_codecs(
const struct bt_hci_std_codec_info_v2 **codecs)
{
ARG_UNUSED(codecs);
/* return number of supported codecs */
return 0;
}
uint8_t __weak hci_vendor_read_vs_codecs(
const struct bt_hci_vs_codec_info_v2 **codecs)
{
ARG_UNUSED(codecs);
/* return number of supported codecs */
return 0;
}
static void read_codecs(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_codecs *rp;
const struct bt_hci_std_codec_info_v2 *std_codec_info;
const struct bt_hci_vs_codec_info_v2 *vs_codec_info;
struct bt_hci_std_codecs *std_codecs;
struct bt_hci_vs_codecs *vs_codecs;
size_t std_codecs_bytes;
size_t vs_codecs_bytes;
uint8_t num_std_codecs;
uint8_t num_vs_codecs;
uint8_t i;
/* read standard codec information */
num_std_codecs = hci_vendor_read_std_codecs(&std_codec_info);
std_codecs_bytes = sizeof(struct bt_hci_std_codecs) +
num_std_codecs * sizeof(struct bt_hci_std_codec_info);
/* read vendor-specific codec information */
num_vs_codecs = hci_vendor_read_vs_codecs(&vs_codec_info);
vs_codecs_bytes = sizeof(struct bt_hci_vs_codecs) +
num_vs_codecs * sizeof(struct bt_hci_vs_codec_info);
/* allocate response packet */
rp = hci_cmd_complete(evt, sizeof(*rp) +
std_codecs_bytes +
vs_codecs_bytes);
rp->status = 0x00;
/* copy standard codec information */
std_codecs = (struct bt_hci_std_codecs *)&rp->codecs[0];
std_codecs->num_codecs = num_std_codecs;
for (i = 0; i < num_std_codecs; i++) {
struct bt_hci_std_codec_info *codec;
codec = &std_codecs->codec_info[i];
codec->codec_id = std_codec_info[i].codec_id;
}
/* copy vendor specific codec information */
vs_codecs = (struct bt_hci_vs_codecs *)&rp->codecs[std_codecs_bytes];
vs_codecs->num_codecs = num_vs_codecs;
for (i = 0; i < num_std_codecs; i++) {
struct bt_hci_vs_codec_info *codec;
codec = &vs_codecs->codec_info[i];
codec->company_id =
sys_cpu_to_le16(vs_codec_info[i].company_id);
codec->codec_id = sys_cpu_to_le16(vs_codec_info[i].codec_id);
}
}
static void read_codecs_v2(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_read_codecs_v2 *rp;
const struct bt_hci_std_codec_info_v2 *std_codec_info;
const struct bt_hci_vs_codec_info_v2 *vs_codec_info;
struct bt_hci_std_codecs_v2 *std_codecs;
struct bt_hci_vs_codecs_v2 *vs_codecs;
size_t std_codecs_bytes;
size_t vs_codecs_bytes;
uint8_t num_std_codecs;
uint8_t num_vs_codecs;
uint8_t i;
/* read standard codec information */
num_std_codecs = hci_vendor_read_std_codecs(&std_codec_info);
std_codecs_bytes = sizeof(struct bt_hci_std_codecs_v2) +
num_std_codecs * sizeof(struct bt_hci_std_codec_info_v2);
/* read vendor-specific codec information */
num_vs_codecs = hci_vendor_read_vs_codecs(&vs_codec_info);
vs_codecs_bytes = sizeof(struct bt_hci_vs_codecs_v2) +
num_vs_codecs * sizeof(struct bt_hci_vs_codec_info_v2);
/* allocate response packet */
rp = hci_cmd_complete(evt, sizeof(*rp) +
std_codecs_bytes +
vs_codecs_bytes);
rp->status = 0x00;
/* copy standard codec information */
std_codecs = (struct bt_hci_std_codecs_v2 *)&rp->codecs[0];
std_codecs->num_codecs = num_std_codecs;
for (i = 0; i < num_std_codecs; i++) {
struct bt_hci_std_codec_info_v2 *codec;
codec = &std_codecs->codec_info[i];
codec->codec_id = std_codec_info[i].codec_id;
codec->transports = std_codec_info[i].transports;
}
/* copy vendor specific codec information */
vs_codecs = (struct bt_hci_vs_codecs_v2 *)&rp->codecs[std_codecs_bytes];
vs_codecs->num_codecs = num_vs_codecs;
for (i = 0; i < num_std_codecs; i++) {
struct bt_hci_vs_codec_info_v2 *codec;
codec = &vs_codecs->codec_info[i];
codec->company_id =
sys_cpu_to_le16(vs_codec_info[i].company_id);
codec->codec_id = sys_cpu_to_le16(vs_codec_info[i].codec_id);
codec->transports = vs_codec_info[i].transports;
}
}
uint8_t __weak hci_vendor_read_codec_capabilities(uint8_t coding_format,
uint16_t company_id,
uint16_t vs_codec_id,
uint8_t transport,
uint8_t direction,
uint8_t *num_capabilities,
size_t *capabilities_bytes,
const uint8_t **capabilities)
{
ARG_UNUSED(coding_format);
ARG_UNUSED(company_id);
ARG_UNUSED(vs_codec_id);
ARG_UNUSED(transport);
ARG_UNUSED(direction);
ARG_UNUSED(capabilities);
*num_capabilities = 0;
*capabilities_bytes = 0;
/* return status */
return 0x00;
}
static void read_codec_capabilities(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_codec_capabilities *cmd = (void *)buf->data;
struct bt_hci_rp_read_codec_capabilities *rp;
const uint8_t *capabilities;
size_t capabilities_bytes;
uint8_t num_capabilities;
uint16_t vs_codec_id;
uint16_t company_id;
uint8_t status;
company_id = sys_le16_to_cpu(cmd->codec_id.company_id);
vs_codec_id = sys_le16_to_cpu(cmd->codec_id.vs_codec_id);
/* read codec capabilities */
status = hci_vendor_read_codec_capabilities(cmd->codec_id.coding_format,
company_id,
vs_codec_id,
cmd->transport,
cmd->direction,
&num_capabilities,
&capabilities_bytes,
&capabilities);
/* allocate response packet */
rp = hci_cmd_complete(evt, sizeof(*rp) + capabilities_bytes);
rp->status = status;
/* copy codec capabilities information */
rp->num_capabilities = num_capabilities;
memcpy(&rp->capabilities, capabilities, capabilities_bytes);
}
uint8_t __weak hci_vendor_read_ctlr_delay(uint8_t coding_format,
uint16_t company_id,
uint16_t vs_codec_id,
uint8_t transport,
uint8_t direction,
uint8_t codec_config_len,
const uint8_t *codec_config,
uint32_t *min_delay,
uint32_t *max_delay)
{
ARG_UNUSED(coding_format);
ARG_UNUSED(company_id);
ARG_UNUSED(vs_codec_id);
ARG_UNUSED(transport);
ARG_UNUSED(direction);
ARG_UNUSED(codec_config_len);
ARG_UNUSED(codec_config);
*min_delay = 0;
*max_delay = 0x3D0900; /* 4 seconds, maximum value allowed by spec */
/* return status */
return 0x00;
}
static void read_ctlr_delay(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_ctlr_delay *cmd = (void *)buf->data;
struct bt_hci_rp_read_ctlr_delay *rp;
uint16_t vs_codec_id;
uint16_t company_id;
uint32_t min_delay;
uint32_t max_delay;
uint8_t status;
company_id = sys_le16_to_cpu(cmd->codec_id.company_id);
vs_codec_id = sys_le16_to_cpu(cmd->codec_id.vs_codec_id);
status = hci_vendor_read_ctlr_delay(cmd->codec_id.coding_format,
company_id,
vs_codec_id,
cmd->transport,
cmd->direction,
cmd->codec_config_len,
cmd->codec_config,
&min_delay,
&max_delay);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
sys_put_le24(min_delay, rp->min_ctlr_delay);
sys_put_le24(max_delay, rp->max_ctlr_delay);
}
#endif /* CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO */
static int info_cmd_handle(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
switch (ocf) {
case BT_OCF(BT_HCI_OP_READ_LOCAL_VERSION_INFO):
read_local_version_info(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_SUPPORTED_COMMANDS):
read_supported_commands(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_LOCAL_FEATURES):
read_local_features(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_BD_ADDR):
read_bd_addr(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO)
case BT_OCF(BT_HCI_OP_READ_CODECS):
read_codecs(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_CODECS_V2):
read_codecs_v2(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_CODEC_CAPABILITIES):
read_codec_capabilities(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_READ_CTLR_DELAY):
read_ctlr_delay(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO */
default:
return -EINVAL;
}
return 0;
}
#if defined(CONFIG_BT_CTLR_CONN_RSSI)
static void read_rssi(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_read_rssi *cmd = (void *)buf->data;
struct bt_hci_rp_read_rssi *rp;
uint16_t handle;
handle = sys_le16_to_cpu(cmd->handle);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = ll_rssi_get(handle, &rp->rssi);
rp->handle = sys_cpu_to_le16(handle);
/* The Link Layer currently returns RSSI as an absolute value */
rp->rssi = (!rp->status) ? -rp->rssi : 127;
}
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
static int status_cmd_handle(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
switch (ocf) {
#if defined(CONFIG_BT_CTLR_CONN_RSSI)
case BT_OCF(BT_HCI_OP_READ_RSSI):
read_rssi(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
default:
return -EINVAL;
}
return 0;
}
static void le_set_event_mask(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_set_event_mask *cmd = (void *)buf->data;
le_event_mask = sys_get_le64(cmd->events);
*evt = cmd_complete_status(0x00);
}
static void le_read_buffer_size(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_buffer_size *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->le_max_len = sys_cpu_to_le16(LL_LENGTH_OCTETS_TX_MAX);
Bluetooth: Refactor bluetooth buffer configuration for simplification Refactor and simplify the bluetooth buffer configurations to improve the easy of configurations and eliminate invalid ones. By moving configurations out of host and controller specific configurations and into a common one it becomes easier to configure the host and controller separately as the same configurations can be used as would be for a combined build. All HCI configurations are now given exluding the matching HCI header, which eases the configuration as the application don't have to know the different header sizes. The BT_RX_BUF_LEN is split into ACL and Event, as well as the suprising use of Command size. BT_L2CAP_RX_MTU is removed as the stack does not support reassembling of HCI ACL data to larger L2CAP PDUs. The application will have to set ACL RX size and account for the L2CAP PDU header itself. BT_EATT_RX_MTU was removed as it is only used for setting a different default value for another option which leads to the stuck kconfig symbol problem. The configurations can be updated according to the table below: ** New configuration | ** Old configuration All configurations BT_BUF_ACL_RX_SIZE | BT_L2CAP_RX_MTU + 4 BT_BUF_ACL_RX_SIZE | BT_RX_BUF_LEN - 4 BT_BUF_EVT_RX_SIZE | BT_RX_BUF_LEN - 2 BT_BUF_CMD_TX_SIZE | BT_RX_BUF_LEN - 3 BT_BUF_CMD_TX_COUNT | BT_HCI_CMD_COUNT BT_BUF_EVT_RX_COUNT | BT_RX_BUF_COUNT BT_BUF_ACL_RX_COUNT | BT_RX_BUF_COUNT BT_BUF_ACL_RX_COUNT | BT_ACL_RX_COUNT BT_BUF_EVT_DISCARDABLE_SIZE | BT_DISCARDABLE_BUF_SIZE - 2 BT_BUF_EVT_DISCARDABLE_COUNT | BT_DISCARDABLE_BUF_COUNT Controller-build BT_BUF_ACL_TX_SIZE | BT_CTLR_TX_BUFFERS_SIZE BT_BUF_ACL_TX_COUNT | BT_CTLR_TX_BUFFER HCI-bridge BT_BUF_ACL_TX_SIZE | BT_HCI_ACL_DATA_SIZE BT_BUF_ACL_TX_COUNT | 6 Fixed invalid configurations setting either BT_L2CAP_RX_MTU or BT_CTLR_DATA_LENGTH_MAX larger than BT_RX_BUF_LEN could lead to buffer overruns. Fix advertising report max data length calculation. This always used the BT_DISCARDABLE_BUF_SIZE macro but this feature can be turned off and advertising reports will be allocated from the RX buffer in that case. Also controller-build does not have this buffer (in hci_raw.c). Also the wrong HCI header was used in the calculation, HCI event header should have been used instead of HCI ACL header. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2021-04-27 10:38:23 +02:00
rp->le_max_num = CONFIG_BT_BUF_ACL_TX_COUNT;
}
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
static void le_read_buffer_size_v2(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_buffer_size_v2 *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->acl_max_len = sys_cpu_to_le16(LL_LENGTH_OCTETS_TX_MAX);
rp->acl_max_num = CONFIG_BT_BUF_ACL_TX_COUNT;
rp->iso_max_len = sys_cpu_to_le16(CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE);
rp->iso_max_num = CONFIG_BT_CTLR_ISO_TX_BUFFERS;
}
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
static void le_read_local_features(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_local_features *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)memset(&rp->features[0], 0x00, sizeof(rp->features));
sys_put_le64(ll_feat_get(), rp->features);
}
static void le_set_random_address(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_random_address *cmd = (void *)buf->data;
uint8_t status;
status = ll_addr_set(1, &cmd->bdaddr.val[0]);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
static void le_read_fal_size(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_fal_size *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->fal_size = ll_fal_size_get();
}
static void le_clear_fal(struct net_buf *buf, struct net_buf **evt)
{
uint8_t status;
status = ll_fal_clear();
*evt = cmd_complete_status(status);
}
static void le_add_dev_to_fal(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_add_dev_to_fal *cmd = (void *)buf->data;
uint8_t status;
status = ll_fal_add(&cmd->addr);
*evt = cmd_complete_status(status);
}
static void le_rem_dev_from_fal(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rem_dev_from_fal *cmd = (void *)buf->data;
uint8_t status;
status = ll_fal_remove(&cmd->addr);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
static void le_encrypt(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_encrypt *cmd = (void *)buf->data;
struct bt_hci_rp_le_encrypt *rp;
uint8_t enc_data[16];
ecb_encrypt(cmd->key, cmd->plaintext, enc_data, NULL);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
memcpy(rp->enc_data, enc_data, 16);
}
static void le_rand(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_rand *rp;
uint8_t count = sizeof(rp->rand);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
lll_csrand_get(rp->rand, count);
}
static void le_read_supp_states(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_supp_states *rp;
uint64_t states = 0U;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
#define ST_ADV (BIT64(0) | BIT64(1) | BIT64(8) | BIT64(9) | BIT64(12) | \
BIT64(13) | BIT64(16) | BIT64(17) | BIT64(18) | BIT64(19) | \
BIT64(20) | BIT64(21))
#define ST_SCA (BIT64(4) | BIT64(5) | BIT64(8) | BIT64(9) | BIT64(10) | \
BIT64(11) | BIT64(12) | BIT64(13) | BIT64(14) | BIT64(15) | \
BIT64(22) | BIT64(23) | BIT64(24) | BIT64(25) | BIT64(26) | \
BIT64(27) | BIT64(30) | BIT64(31))
#define ST_PER (BIT64(2) | BIT64(3) | BIT64(7) | BIT64(10) | BIT64(11) | \
BIT64(14) | BIT64(15) | BIT64(20) | BIT64(21) | BIT64(26) | \
BIT64(27) | BIT64(29) | BIT64(30) | BIT64(31) | BIT64(32) | \
BIT64(33) | BIT64(34) | BIT64(35) | BIT64(36) | BIT64(37) | \
BIT64(38) | BIT64(39) | BIT64(40) | BIT64(41))
#define ST_CEN (BIT64(6) | BIT64(16) | BIT64(17) | BIT64(18) | BIT64(19) | \
BIT64(22) | BIT64(23) | BIT64(24) | BIT64(25) | BIT64(28) | \
BIT64(32) | BIT64(33) | BIT64(34) | BIT64(35) | BIT64(36) | \
BIT64(37) | BIT64(41))
#if defined(CONFIG_BT_BROADCASTER)
states |= ST_ADV;
#else
states &= ~ST_ADV;
#endif
#if defined(CONFIG_BT_OBSERVER)
states |= ST_SCA;
#else
states &= ~ST_SCA;
#endif
#if defined(CONFIG_BT_PERIPHERAL)
states |= ST_PER;
#else
states &= ~ST_PER;
#endif
#if defined(CONFIG_BT_CENTRAL)
states |= ST_CEN;
#else
states &= ~ST_CEN;
#endif
/* All states and combinations supported except:
* Initiating State + Passive Scanning
* Initiating State + Active Scanning
*/
states &= ~(BIT64(22) | BIT64(23));
BT_DBG("states: 0x%08x%08x", (uint32_t)(states >> 32),
(uint32_t)(states & 0xffffffff));
sys_put_le64(states, rp->le_states);
}
#if defined(CONFIG_BT_BROADCASTER)
static void le_set_adv_param(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_adv_param *cmd = (void *)buf->data;
uint16_t min_interval;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
min_interval = sys_le16_to_cpu(cmd->min_interval);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
(cmd->type != BT_HCI_ADV_DIRECT_IND)) {
uint16_t max_interval = sys_le16_to_cpu(cmd->max_interval);
if ((min_interval > max_interval) ||
(min_interval < 0x0020) ||
(max_interval > 0x4000)) {
*evt = cmd_complete_status(BT_HCI_ERR_INVALID_PARAM);
return;
}
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_adv_params_set(0, 0, min_interval, cmd->type,
cmd->own_addr_type, cmd->direct_addr.type,
&cmd->direct_addr.a.val[0], cmd->channel_map,
cmd->filter_policy, 0, 0, 0, 0, 0, 0);
#else /* !CONFIG_BT_CTLR_ADV_EXT */
status = ll_adv_params_set(min_interval, cmd->type,
cmd->own_addr_type, cmd->direct_addr.type,
&cmd->direct_addr.a.val[0], cmd->channel_map,
cmd->filter_policy);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
*evt = cmd_complete_status(status);
}
static void le_read_adv_chan_tx_power(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_chan_tx_power *rp;
if (adv_cmds_legacy_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->tx_power_level = 0;
}
static void le_set_adv_data(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_adv_data *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_adv_data_set(0, cmd->len, &cmd->data[0]);
#else /* !CONFIG_BT_CTLR_ADV_EXT */
status = ll_adv_data_set(cmd->len, &cmd->data[0]);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
*evt = cmd_complete_status(status);
}
static void le_set_scan_rsp_data(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_scan_rsp_data *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_adv_scan_rsp_set(0, cmd->len, &cmd->data[0]);
#else /* !CONFIG_BT_CTLR_ADV_EXT */
status = ll_adv_scan_rsp_set(cmd->len, &cmd->data[0]);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
*evt = cmd_complete_status(status);
}
static void le_set_adv_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_adv_enable *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT) || defined(CONFIG_BT_HCI_MESH_EXT)
#if defined(CONFIG_BT_HCI_MESH_EXT)
status = ll_adv_enable(0, cmd->enable, 0, 0, 0, 0, 0);
#else /* !CONFIG_BT_HCI_MESH_EXT */
status = ll_adv_enable(0, cmd->enable, 0, 0);
#endif /* !CONFIG_BT_HCI_MESH_EXT */
#else /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_HCI_MESH_EXT */
status = ll_adv_enable(cmd->enable);
#endif /* !CONFIG_BT_CTLR_ADV_EXT || !CONFIG_BT_HCI_MESH_EXT */
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_ADV_ISO)
static void le_create_big(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_create_big *cmd = (void *)buf->data;
uint32_t sdu_interval;
uint16_t max_latency;
uint8_t big_handle;
uint8_t adv_handle;
uint16_t max_sdu;
uint8_t status;
status = ll_adv_iso_by_hci_handle_new(cmd->big_handle, &big_handle);
if (status) {
*evt = cmd_status(status);
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->adv_handle, &adv_handle);
if (status) {
*evt = cmd_status(status);
return;
}
sdu_interval = sys_get_le24(cmd->sdu_interval);
max_sdu = sys_le16_to_cpu(cmd->max_sdu);
max_latency = sys_le16_to_cpu(cmd->max_latency);
status = ll_big_create(big_handle, adv_handle, cmd->num_bis,
sdu_interval, max_sdu, max_latency, cmd->rtn,
cmd->phy, cmd->packing, cmd->framing,
cmd->encryption, cmd->bcode);
*evt = cmd_status(status);
}
static void le_create_big_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_create_big_test *cmd = (void *)buf->data;
uint32_t sdu_interval;
uint16_t iso_interval;
uint16_t max_sdu;
uint16_t max_pdu;
uint8_t status;
sdu_interval = sys_get_le24(cmd->sdu_interval);
iso_interval = sys_le16_to_cpu(cmd->iso_interval);
max_sdu = sys_le16_to_cpu(cmd->max_sdu);
max_pdu = sys_le16_to_cpu(cmd->max_pdu);
status = ll_big_test_create(cmd->big_handle, cmd->adv_handle,
cmd->num_bis, sdu_interval, iso_interval,
cmd->nse, max_sdu, max_pdu, cmd->phy,
cmd->packing, cmd->framing, cmd->bn,
cmd->irc, cmd->pto, cmd->encryption,
cmd->bcode);
*evt = cmd_status(status);
}
static void le_terminate_big(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_terminate_big *cmd = (void *)buf->data;
uint8_t status;
status = ll_big_terminate(cmd->big_handle, cmd->reason);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CTLR_ADV_ISO */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
static void le_set_scan_param(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_scan_param *cmd = (void *)buf->data;
uint16_t interval;
uint16_t window;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
interval = sys_le16_to_cpu(cmd->interval);
window = sys_le16_to_cpu(cmd->window);
status = ll_scan_params_set(cmd->scan_type, interval, window,
cmd->addr_type, cmd->filter_policy);
*evt = cmd_complete_status(status);
}
static void le_set_scan_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_scan_enable *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* Initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) {
if (0) {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
} else if (dup_count == DUP_FILTER_DISABLED) {
dup_scan = true;
/* All entries reset */
dup_count = 0;
dup_curr = 0U;
} else if (!dup_scan) {
dup_scan = true;
dup_ext_adv_reset();
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
} else {
/* All entries reset */
dup_count = 0;
dup_curr = 0U;
}
} else {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
dup_scan = false;
#else /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
dup_count = DUP_FILTER_DISABLED;
#endif /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_scan_enable(cmd->enable, 0, 0);
#else /* !CONFIG_BT_CTLR_ADV_EXT */
status = ll_scan_enable(cmd->enable);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
/* NOTE: As filter duplicates is implemented here in HCI source code,
* enabling of already enabled scanning shall succeed after
* updates to filter duplicates is handled in the above
* statements. Refer to BT Spec v5.0 Vol 2 Part E Section 7.8.11.
*/
if (!IS_ENABLED(CONFIG_BT_CTLR_SCAN_ENABLE_STRICT) &&
(status == BT_HCI_ERR_CMD_DISALLOWED)) {
status = BT_HCI_ERR_SUCCESS;
}
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
static void le_big_create_sync(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_big_create_sync *cmd = (void *)buf->data;
uint8_t status;
uint16_t sync_handle;
uint16_t sync_timeout;
sync_handle = sys_le16_to_cpu(cmd->sync_handle);
sync_timeout = sys_le16_to_cpu(cmd->sync_timeout);
status = ll_big_sync_create(cmd->big_handle, sync_handle,
cmd->encryption, cmd->bcode, cmd->mse,
sync_timeout, cmd->num_bis, cmd->bis);
*evt = cmd_status(status);
}
static void le_big_terminate_sync(struct net_buf *buf, struct net_buf **evt,
void **node_rx)
{
struct bt_hci_cp_le_big_terminate_sync *cmd = (void *)buf->data;
struct bt_hci_rp_le_big_terminate_sync *rp;
uint8_t big_handle;
uint8_t status;
big_handle = cmd->big_handle;
status = ll_big_sync_terminate(big_handle, node_rx);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->big_handle = big_handle;
}
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CENTRAL)
static uint8_t check_cconn_params(bool ext, uint16_t scan_interval,
uint16_t scan_window,
uint16_t conn_interval_max,
uint16_t conn_latency,
uint16_t supervision_timeout)
{
if (scan_interval < 0x0004 || scan_window < 0x0004 ||
(!ext && (scan_interval > 0x4000 || scan_window > 0x4000))) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (conn_interval_max < 0x0006 || conn_interval_max > 0x0C80) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (conn_latency > 0x01F3) {
return BT_HCI_ERR_INVALID_PARAM;
}
if (supervision_timeout < 0x000A || supervision_timeout > 0x0C80) {
return BT_HCI_ERR_INVALID_PARAM;
}
/* sto * 10ms > (1 + lat) * ci * 1.25ms * 2
* sto * 10 > (1 + lat) * ci * 2.5
* sto * 2 > (1 + lat) * ci * 0.5
* sto * 4 > (1 + lat) * ci
*/
if ((supervision_timeout << 2) <= ((1 + conn_latency) *
conn_interval_max)) {
return BT_HCI_ERR_INVALID_PARAM;
}
return 0;
}
static void le_create_connection(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_create_conn *cmd = (void *)buf->data;
uint16_t supervision_timeout;
uint16_t conn_interval_max;
uint16_t scan_interval;
uint16_t conn_latency;
uint16_t scan_window;
uint8_t status;
if (adv_cmds_legacy_check(NULL)) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
scan_interval = sys_le16_to_cpu(cmd->scan_interval);
scan_window = sys_le16_to_cpu(cmd->scan_window);
conn_interval_max = sys_le16_to_cpu(cmd->conn_interval_max);
conn_latency = sys_le16_to_cpu(cmd->conn_latency);
supervision_timeout = sys_le16_to_cpu(cmd->supervision_timeout);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK)) {
status = check_cconn_params(false, scan_interval,
scan_window,
conn_interval_max,
conn_latency,
supervision_timeout);
if (status) {
*evt = cmd_status(status);
return;
}
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_create_connection(scan_interval, scan_window,
cmd->filter_policy,
cmd->peer_addr.type,
&cmd->peer_addr.a.val[0],
cmd->own_addr_type, conn_interval_max,
conn_latency, supervision_timeout,
PHY_LEGACY);
if (status) {
*evt = cmd_status(status);
return;
}
status = ll_connect_enable(0U);
#else /* !CONFIG_BT_CTLR_ADV_EXT */
status = ll_create_connection(scan_interval, scan_window,
cmd->filter_policy,
cmd->peer_addr.type,
&cmd->peer_addr.a.val[0],
cmd->own_addr_type, conn_interval_max,
conn_latency, supervision_timeout);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
*evt = cmd_status(status);
}
static void le_create_conn_cancel(struct net_buf *buf, struct net_buf **evt,
void **node_rx)
{
uint8_t status;
status = ll_connect_disable(node_rx);
*evt = cmd_complete_status(status);
}
static void le_set_host_chan_classif(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_host_chan_classif *cmd = (void *)buf->data;
uint8_t status;
status = ll_chm_update(&cmd->ch_map[0]);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_LE_ENC)
static void le_start_encryption(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_start_encryption *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_enc_req_send(handle,
(uint8_t *)&cmd->rand,
(uint8_t *)&cmd->ediv,
&cmd->ltk[0]);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
static void le_set_cig_parameters(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_cig_params *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_cig_params *rp;
uint32_t c_interval;
uint32_t p_interval;
uint16_t c_latency;
uint16_t p_latency;
uint8_t status;
uint8_t i;
c_interval = sys_get_le24(cmd->c_interval);
p_interval = sys_get_le24(cmd->p_interval);
c_latency = sys_le16_to_cpu(cmd->c_latency);
p_latency = sys_le16_to_cpu(cmd->p_latency);
/* Create CIG or start modifying existing CIG */
status = ll_cig_parameters_open(cmd->cig_id, c_interval, p_interval,
cmd->sca, cmd->packing, cmd->framing,
c_latency, p_latency, cmd->num_cis);
rp = hci_cmd_complete(evt, sizeof(*rp) +
cmd->num_cis * sizeof(uint16_t));
rp->cig_id = cmd->cig_id;
rp->num_handles = cmd->num_cis;
/* Configure individual CISes */
for (i = 0; !status && i < cmd->num_cis; i++) {
struct bt_hci_cis_params *params = cmd->cis;
uint16_t handle;
uint16_t c_sdu;
uint16_t p_sdu;
c_sdu = sys_le16_to_cpu(params->c_sdu);
p_sdu = sys_le16_to_cpu(params->p_sdu);
status = ll_cis_parameters_set(params->cis_id, c_sdu, p_sdu,
params->c_phy, params->p_phy,
params->c_rtn, params->p_rtn,
&handle);
rp->handle[i] = sys_cpu_to_le16(handle);
}
/* Only apply parameters if all went well */
if (!status) {
status = ll_cig_parameters_commit(cmd->cig_id);
}
rp->status = status;
}
static void le_set_cig_params_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_cig_params_test *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_cig_params_test *rp;
uint32_t c_interval;
uint32_t p_interval;
uint16_t iso_interval;
uint8_t status;
uint8_t i;
c_interval = sys_get_le24(cmd->c_interval);
p_interval = sys_get_le24(cmd->p_interval);
iso_interval = sys_le16_to_cpu(cmd->iso_interval);
/* Create CIG or start modifying existing CIG */
status = ll_cig_parameters_test_open(cmd->cig_id, c_interval,
p_interval, cmd->c_ft,
cmd->p_ft, iso_interval,
cmd->sca, cmd->packing,
cmd->framing,
cmd->num_cis);
rp = hci_cmd_complete(evt, sizeof(*rp) +
cmd->num_cis * sizeof(uint16_t));
rp->cig_id = cmd->cig_id;
rp->num_handles = cmd->num_cis;
/* Configure individual CISes */
for (i = 0; !status && i < cmd->num_cis; i++) {
struct bt_hci_cis_params_test *params = cmd->cis;
uint16_t handle;
uint16_t c_sdu;
uint16_t p_sdu;
uint16_t c_pdu;
uint16_t p_pdu;
c_sdu = sys_le16_to_cpu(params->c_sdu);
p_sdu = sys_le16_to_cpu(params->p_sdu);
c_pdu = sys_le16_to_cpu(params->c_pdu);
p_pdu = sys_le16_to_cpu(params->p_pdu);
status = ll_cis_parameters_test_set(params->cis_id,
c_sdu, p_sdu,
c_pdu, p_pdu,
params->c_phy,
params->p_phy,
params->c_bn,
params->p_bn,
&handle);
rp->handle[i] = sys_cpu_to_le16(handle);
}
/* Only apply parameters if all went well */
if (!status) {
status = ll_cig_parameters_commit(cmd->cig_id);
}
rp->status = status;
}
static void le_create_cis(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_create_cis *cmd = (void *)buf->data;
uint8_t status;
uint8_t i;
/*
* Creating new CISes is disallowed until all previous CIS
* established events have been generated
*/
if (cis_pending_count) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
/* Check all handles before actually starting to create CISes */
status = 0x00;
for (i = 0; !status && i < cmd->num_cis; i++) {
uint16_t cis_handle;
uint16_t acl_handle;
cis_handle = sys_le16_to_cpu(cmd->cis[i].cis_handle);
acl_handle = sys_le16_to_cpu(cmd->cis[i].acl_handle);
status = ll_cis_create_check(cis_handle, acl_handle);
}
*evt = cmd_status(status);
if (!status) {
return;
}
/*
* Actually create CISes, any errors are to be reported
* through CIS established events
*/
cis_pending_count = cmd->num_cis;
for (i = 0; i < cmd->num_cis; i++) {
uint16_t cis_handle;
uint16_t acl_handle;
cis_handle = sys_le16_to_cpu(cmd->cis[i].cis_handle);
acl_handle = sys_le16_to_cpu(cmd->cis[i].acl_handle);
ll_cis_create(cis_handle, acl_handle);
}
}
static void le_remove_cig(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_remove_cig *cmd = (void *)buf->data;
struct bt_hci_rp_le_remove_cig *rp;
uint8_t status;
status = ll_cig_remove(cmd->cig_id);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->cig_id = cmd->cig_id;
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
static void le_iso_transmit_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_iso_transmit_test *cmd = (void *)buf->data;
struct bt_hci_rp_le_iso_transmit_test *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_iso_transmit_test(handle, cmd->payload_type);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_read_iso_tx_sync(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_iso_tx_sync *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_iso_tx_sync *rp;
uint16_t handle_le16;
uint32_t timestamp;
uint32_t offset;
uint16_t handle;
uint8_t status;
uint16_t seq;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_read_iso_tx_sync(handle, &seq, &timestamp, &offset);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
rp->seq = sys_cpu_to_le16(seq);
rp->timestamp = sys_cpu_to_le32(timestamp);
sys_put_le24(offset, rp->offset);
}
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
static void le_iso_receive_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_iso_receive_test *cmd = (void *)buf->data;
struct bt_hci_rp_le_iso_receive_test *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_iso_receive_test(handle, cmd->payload_type);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_iso_read_test_counters(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_test_counters *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_test_counters *rp;
uint32_t received_cnt;
uint32_t missed_cnt;
uint32_t failed_cnt;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_iso_read_test_counters(handle, &received_cnt,
&missed_cnt, &failed_cnt);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
rp->received_cnt = sys_cpu_to_le32(received_cnt);
rp->missed_cnt = sys_cpu_to_le32(missed_cnt);
rp->failed_cnt = sys_cpu_to_le32(failed_cnt);
}
#if defined(CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY)
static void le_read_iso_link_quality(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_iso_link_quality *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_iso_link_quality *rp;
uint32_t tx_last_subevent_packets;
uint32_t retransmitted_packets;
uint32_t rx_unreceived_packets;
uint32_t tx_unacked_packets;
uint32_t tx_flushed_packets;
uint32_t crc_error_packets;
uint32_t duplicate_packets;
uint16_t handle_le16;
uint16_t handle;
uint8_t status;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_read_iso_link_quality(handle, &tx_unacked_packets,
&tx_flushed_packets,
&tx_last_subevent_packets,
&retransmitted_packets,
&crc_error_packets,
&rx_unreceived_packets,
&duplicate_packets);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
rp->tx_unacked_packets = sys_cpu_to_le32(tx_unacked_packets);
rp->tx_flushed_packets = sys_cpu_to_le32(tx_flushed_packets);
rp->tx_last_subevent_packets =
sys_cpu_to_le32(tx_last_subevent_packets);
rp->retransmitted_packets = sys_cpu_to_le32(retransmitted_packets);
rp->crc_error_packets = sys_cpu_to_le32(crc_error_packets);
rp->rx_unreceived_packets = sys_cpu_to_le32(rx_unreceived_packets);
rp->duplicate_packets = sys_cpu_to_le32(duplicate_packets);
}
#endif /* CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY */
#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_ISO)
static void le_setup_iso_path(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_setup_iso_path *cmd = (void *)buf->data;
struct bt_hci_rp_le_setup_iso_path *rp;
uint32_t controller_delay;
uint8_t *codec_config;
uint8_t coding_format;
uint16_t vs_codec_id;
uint16_t company_id;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
coding_format = cmd->codec_id.coding_format;
company_id = sys_le16_to_cpu(cmd->codec_id.company_id);
vs_codec_id = sys_le16_to_cpu(cmd->codec_id.vs_codec_id);
controller_delay = sys_get_le24(cmd->controller_delay);
codec_config = &cmd->codec_config[0];
status = ll_setup_iso_path(handle, cmd->path_dir, cmd->path_id,
coding_format, company_id, vs_codec_id,
controller_delay, cmd->codec_config_len,
codec_config);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_remove_iso_path(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_remove_iso_path *cmd = (void *)buf->data;
struct bt_hci_rp_le_remove_iso_path *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_remove_iso_path(handle, cmd->path_dir);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_iso_test_end(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_iso_test_end *cmd = (void *)buf->data;
struct bt_hci_rp_le_iso_test_end *rp;
uint32_t received_cnt;
uint32_t missed_cnt;
uint32_t failed_cnt;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_iso_test_end(handle, &received_cnt, &missed_cnt,
&failed_cnt);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
rp->received_cnt = sys_cpu_to_le32(received_cnt);
rp->missed_cnt = sys_cpu_to_le32(missed_cnt);
rp->failed_cnt = sys_cpu_to_le32(failed_cnt);
}
#endif /* CONFIG_BT_CTLR_ISO */
#if defined(CONFIG_BT_CTLR_SET_HOST_FEATURE)
static void le_set_host_feature(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_host_feature *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_host_feature *rp;
uint8_t status;
status = ll_set_host_feature(cmd->bit_number, cmd->bit_value);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
}
#endif /* CONFIG_BT_CTLR_SET_HOST_FEATURE */
#if defined(CONFIG_BT_PERIPHERAL)
#if defined(CONFIG_BT_CTLR_LE_ENC)
static void le_ltk_req_reply(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_ltk_req_reply *cmd = (void *)buf->data;
struct bt_hci_rp_le_ltk_req_reply *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_start_enc_req_send(handle, 0x00, &cmd->ltk[0]);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_ltk_req_neg_reply(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_ltk_req_neg_reply *cmd = (void *)buf->data;
struct bt_hci_rp_le_ltk_req_neg_reply *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_start_enc_req_send(handle, BT_HCI_ERR_PIN_OR_KEY_MISSING,
NULL);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_le16_to_cpu(handle);
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
static void le_accept_cis(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_accept_cis *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_cis_accept(handle);
*evt = cmd_status(status);
}
static void le_reject_cis(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_reject_cis *cmd = (void *)buf->data;
struct bt_hci_rp_le_reject_cis *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_cis_reject(handle, cmd->reason);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CONN)
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
static void le_read_remote_features(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_remote_features *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_feature_req_send(handle);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CENTRAL || CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG */
static void le_read_chan_map(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_chan_map *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_chan_map *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
rp = hci_cmd_complete(evt, sizeof(*rp));
status = ll_chm_get(handle, rp->ch_map);
rp->status = status;
rp->handle = sys_le16_to_cpu(handle);
}
static void le_conn_update(struct net_buf *buf, struct net_buf **evt)
{
struct hci_cp_le_conn_update *cmd = (void *)buf->data;
uint16_t supervision_timeout;
uint16_t conn_interval_min;
uint16_t conn_interval_max;
uint16_t conn_latency;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
conn_interval_min = sys_le16_to_cpu(cmd->conn_interval_min);
conn_interval_max = sys_le16_to_cpu(cmd->conn_interval_max);
conn_latency = sys_le16_to_cpu(cmd->conn_latency);
supervision_timeout = sys_le16_to_cpu(cmd->supervision_timeout);
status = ll_conn_update(handle, 0, 0, conn_interval_min,
conn_interval_max, conn_latency,
supervision_timeout);
*evt = cmd_status(status);
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void le_conn_param_req_reply(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_conn_param_req_reply *cmd = (void *)buf->data;
struct bt_hci_rp_le_conn_param_req_reply *rp;
uint16_t interval_min;
uint16_t interval_max;
uint16_t latency;
uint16_t timeout;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
interval_min = sys_le16_to_cpu(cmd->interval_min);
interval_max = sys_le16_to_cpu(cmd->interval_max);
latency = sys_le16_to_cpu(cmd->latency);
timeout = sys_le16_to_cpu(cmd->timeout);
status = ll_conn_update(handle, 2, 0, interval_min, interval_max,
latency, timeout);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_conn_param_req_neg_reply(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_le_conn_param_req_neg_reply *cmd = (void *)buf->data;
struct bt_hci_rp_le_conn_param_req_neg_reply *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_conn_update(handle, 2, cmd->reason, 0, 0, 0, 0);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CTLR_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;
struct bt_hci_rp_le_set_data_len *rp;
uint16_t tx_octets;
uint16_t tx_time;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
tx_octets = sys_le16_to_cpu(cmd->tx_octets);
tx_time = sys_le16_to_cpu(cmd->tx_time);
status = ll_length_req_send(handle, tx_octets, tx_time);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
}
static void le_read_default_data_len(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_default_data_len *rp;
uint16_t max_tx_octets;
uint16_t max_tx_time;
rp = hci_cmd_complete(evt, sizeof(*rp));
ll_length_default_get(&max_tx_octets, &max_tx_time);
rp->max_tx_octets = sys_cpu_to_le16(max_tx_octets);
rp->max_tx_time = sys_cpu_to_le16(max_tx_time);
rp->status = 0x00;
}
static void le_write_default_data_len(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_le_write_default_data_len *cmd = (void *)buf->data;
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint8_t status;
max_tx_octets = sys_le16_to_cpu(cmd->max_tx_octets);
max_tx_time = sys_le16_to_cpu(cmd->max_tx_time);
status = ll_length_default_set(max_tx_octets, max_tx_time);
*evt = cmd_complete_status(status);
}
static void le_read_max_data_len(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_max_data_len *rp;
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint16_t max_rx_octets;
uint16_t max_rx_time;
rp = hci_cmd_complete(evt, sizeof(*rp));
ll_length_max_get(&max_tx_octets, &max_tx_time,
&max_rx_octets, &max_rx_time);
rp->max_tx_octets = sys_cpu_to_le16(max_tx_octets);
rp->max_tx_time = sys_cpu_to_le16(max_tx_time);
rp->max_rx_octets = sys_cpu_to_le16(max_rx_octets);
rp->max_rx_time = sys_cpu_to_le16(max_rx_time);
rp->status = 0x00;
}
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
static void le_read_phy(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_phy *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_phy *rp;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
rp = hci_cmd_complete(evt, sizeof(*rp));
status = ll_phy_get(handle, &rp->tx_phy, &rp->rx_phy);
rp->status = status;
rp->handle = sys_cpu_to_le16(handle);
rp->tx_phy = find_lsb_set(rp->tx_phy);
rp->rx_phy = find_lsb_set(rp->rx_phy);
}
static void le_set_default_phy(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_default_phy *cmd = (void *)buf->data;
uint8_t status;
if (cmd->all_phys & BT_HCI_LE_PHY_TX_ANY) {
cmd->tx_phys = 0x07;
}
if (cmd->all_phys & BT_HCI_LE_PHY_RX_ANY) {
cmd->rx_phys = 0x07;
}
status = ll_phy_default_set(cmd->tx_phys, cmd->rx_phys);
*evt = cmd_complete_status(status);
}
static void le_set_phy(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_phy *cmd = (void *)buf->data;
uint16_t phy_opts;
uint8_t mask_phys;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
phy_opts = sys_le16_to_cpu(cmd->phy_opts);
mask_phys = BT_HCI_LE_PHY_PREFER_1M;
if (IS_ENABLED(CONFIG_BT_CTLR_PHY_2M)) {
mask_phys |= BT_HCI_LE_PHY_PREFER_2M;
}
if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) {
mask_phys |= BT_HCI_LE_PHY_PREFER_CODED;
}
if (cmd->all_phys & BT_HCI_LE_PHY_TX_ANY) {
cmd->tx_phys |= mask_phys;
}
if (cmd->all_phys & BT_HCI_LE_PHY_RX_ANY) {
cmd->rx_phys |= mask_phys;
}
if ((cmd->tx_phys | cmd->rx_phys) & ~mask_phys) {
*evt = cmd_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
if (!(cmd->tx_phys & 0x07) ||
!(cmd->rx_phys & 0x07)) {
*evt = cmd_status(BT_HCI_ERR_INVALID_PARAM);
return;
}
if (phy_opts & 0x03) {
phy_opts -= 1U;
phy_opts &= 1;
} else {
phy_opts = 0U;
}
status = ll_phy_req_send(handle, cmd->tx_phys, phy_opts,
cmd->rx_phys);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_PRIVACY)
static void le_add_dev_to_rl(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_add_dev_to_rl *cmd = (void *)buf->data;
uint8_t status;
status = ll_rl_add(&cmd->peer_id_addr, cmd->peer_irk, cmd->local_irk);
*evt = cmd_complete_status(status);
}
static void le_rem_dev_from_rl(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rem_dev_from_rl *cmd = (void *)buf->data;
uint8_t status;
status = ll_rl_remove(&cmd->peer_id_addr);
*evt = cmd_complete_status(status);
}
static void le_clear_rl(struct net_buf *buf, struct net_buf **evt)
{
uint8_t status;
status = ll_rl_clear();
*evt = cmd_complete_status(status);
}
static void le_read_rl_size(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_rl_size *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->rl_size = ll_rl_size_get();
rp->status = 0x00;
}
static void le_read_peer_rpa(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_peer_rpa *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_peer_rpa *rp;
bt_addr_le_t peer_id_addr;
bt_addr_le_copy(&peer_id_addr, &cmd->peer_id_addr);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = ll_rl_crpa_get(&peer_id_addr, &rp->peer_rpa);
}
static void le_read_local_rpa(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_local_rpa *cmd = (void *)buf->data;
struct bt_hci_rp_le_read_local_rpa *rp;
bt_addr_le_t peer_id_addr;
bt_addr_le_copy(&peer_id_addr, &cmd->peer_id_addr);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = ll_rl_lrpa_get(&peer_id_addr, &rp->local_rpa);
}
static void le_set_addr_res_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_addr_res_enable *cmd = (void *)buf->data;
uint8_t status;
status = ll_rl_enable(cmd->enable);
*evt = cmd_complete_status(status);
}
static void le_set_rpa_timeout(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_rpa_timeout *cmd = (void *)buf->data;
uint16_t timeout = sys_le16_to_cpu(cmd->rpa_timeout);
ll_rl_timeout_set(timeout);
*evt = cmd_complete_status(0x00);
}
static void le_set_privacy_mode(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_privacy_mode *cmd = (void *)buf->data;
uint8_t status;
status = ll_priv_mode_set(&cmd->id_addr, cmd->mode);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_PRIVACY */
static void le_read_tx_power(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_tx_power *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
ll_tx_pwr_get(&rp->min_tx_power, &rp->max_tx_power);
}
#if defined(CONFIG_BT_CTLR_DF)
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
static void le_df_set_cl_cte_tx_params(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_le_set_cl_cte_tx_params *cmd = (void *)buf->data;
uint8_t adv_handle;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &adv_handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_df_set_cl_cte_tx_params(adv_handle, cmd->cte_len,
cmd->cte_type, cmd->cte_count,
cmd->switch_pattern_len,
cmd->ant_ids);
*evt = cmd_complete_status(status);
}
static void le_df_set_cl_cte_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_cl_cte_tx_enable *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_df_set_cl_cte_tx_enable(handle, cmd->cte_enable);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
static void le_df_set_cl_iq_sampling_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_cl_cte_sampling_enable *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_cl_cte_sampling_enable *rp;
uint16_t sync_handle;
uint8_t status;
sync_handle = sys_le16_to_cpu(cmd->sync_handle);
status = ll_df_set_cl_iq_sampling_enable(sync_handle,
cmd->sampling_enable,
cmd->slot_durations,
cmd->max_sampled_cte,
cmd->switch_pattern_len,
cmd->ant_ids);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->sync_handle = sys_cpu_to_le16(sync_handle);
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
static void le_df_connectionless_iq_report(struct pdu_data *pdu_rx,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_connectionless_iq_report *sep;
struct node_rx_iq_report *iq_report;
struct lll_sync *lll;
uint8_t samples_cnt;
int16_t rssi;
uint16_t sync_handle;
uint16_t per_evt_counter;
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
struct ll_sync_set *sync = NULL;
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
iq_report = (struct node_rx_iq_report *)node_rx;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CONNECTIONLESS_IQ_REPORT)) {
return;
}
lll = iq_report->hdr.rx_ftr.param;
/* If there is not LLL context and CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT is enabled
* the controller is in the Direct Test Mode and may generate
* the Connectionless IQ Report.
*/
if (!lll && IS_ENABLED(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)) {
/* Set sync_handle to 0x0FFF according to the BT Core 5.3 specification
* Vol 4 7.7.65.21
*/
sync_handle = 0x0FFF;
/* Set periodic event counter to 0 since there is not periodic advertising train. */
per_evt_counter = 0;
}
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
else {
sync = HDR_LLL2ULL(lll);
/* TX LL thread has higher priority than RX thread. It may happen that
* host successfully disables CTE sampling in the meantime.
* It should be verified here, to avoid reporting IQ samples after
* the functionality was disabled or if sync was lost.
*/
if (ull_df_sync_cfg_is_not_enabled(&lll->df_cfg) ||
!sync->timeout_reload) {
/* Drop further processing of the event. */
return;
}
/* Get the sync handle corresponding to the LLL context passed in the
* node rx footer field.
*/
sync_handle = ull_sync_handle_get(sync);
per_evt_counter = iq_report->event_counter;
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
/* If there are no IQ samples due to insufficient resources
* HCI event should inform about it by storing single octet with
* special I_sample and Q_sample data.
*/
samples_cnt = (!iq_report->sample_count ? 1 : iq_report->sample_count);
sep = meta_evt(buf, BT_HCI_EVT_LE_CONNECTIONLESS_IQ_REPORT,
(sizeof(*sep) +
(samples_cnt * sizeof(struct bt_hci_le_iq_sample))));
rssi = RSSI_DBM_TO_DECI_DBM(iq_report->hdr.rx_ftr.rssi);
sep->sync_handle = sys_cpu_to_le16(sync_handle);
sep->rssi = sys_cpu_to_le16(rssi);
sep->rssi_ant_id = iq_report->rssi_ant_id;
sep->cte_type = iq_report->cte_info.type;
sep->chan_idx = iq_report->chan_idx;
sep->per_evt_counter = sys_cpu_to_le16(per_evt_counter);
if (sep->cte_type == BT_HCI_LE_AOA_CTE) {
sep->slot_durations = iq_report->local_slot_durations;
} else if (sep->cte_type == BT_HCI_LE_AOD_CTE_1US) {
sep->slot_durations = BT_HCI_LE_ANTENNA_SWITCHING_SLOT_1US;
} else {
sep->slot_durations = BT_HCI_LE_ANTENNA_SWITCHING_SLOT_2US;
}
sep->packet_status = iq_report->packet_status;
if (iq_report->packet_status == BT_HCI_LE_CTE_INSUFFICIENT_RESOURCES) {
sep->sample[0].i = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
sep->sample[0].q = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
sep->sample_count = 0U;
} else {
for (uint8_t idx = 0U; idx < samples_cnt; ++idx) {
sep->sample[idx].i = IQ_CONVERT_12_TO_8_BIT(iq_report->sample[idx].i);
sep->sample[idx].q = IQ_CONVERT_12_TO_8_BIT(iq_report->sample[idx].q);
}
sep->sample_count = samples_cnt;
}
}
#endif /* defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT) */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_TX)
static void le_df_set_conn_cte_tx_params(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_le_set_conn_cte_tx_params *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_conn_cte_tx_params *rp;
uint16_t handle, handle_le16;
uint8_t status;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_df_set_conn_cte_tx_params(handle, cmd->cte_types,
cmd->switch_pattern_len,
cmd->ant_ids);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
static void le_df_set_conn_cte_rx_params(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_conn_cte_rx_params *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_conn_cte_rx_params *rp;
uint16_t handle, handle_le16;
uint8_t status;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_df_set_conn_cte_rx_params(handle, cmd->sampling_enable, cmd->slot_durations,
cmd->switch_pattern_len, cmd->ant_ids);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
}
static void le_df_connection_iq_report(struct node_rx_pdu *node_rx, struct net_buf *buf)
{
struct bt_hci_evt_le_connection_iq_report *sep;
struct node_rx_iq_report *iq_report;
struct lll_conn *lll;
uint8_t samples_cnt;
uint8_t phy_rx;
int16_t rssi;
iq_report = (struct node_rx_iq_report *)node_rx;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CONNECTION_IQ_REPORT)) {
return;
}
lll = iq_report->hdr.rx_ftr.param;
#if defined(CONFIG_BT_CTLR_PHY)
phy_rx = lll->phy_rx;
/* Make sure the report is generated for connection on PHY UNCODED */
LL_ASSERT(phy_rx != PHY_CODED);
#else
phy_rx = PHY_1M;
#endif /* CONFIG_BT_CTLR_PHY */
/* TX LL thread has higher priority than RX thread. It may happen that host succefully
* disables CTE sampling in the meantime. It should be verified here, to avoid reporing
* IQ samples after the functionality was disabled.
*/
if (ull_df_conn_cfg_is_not_enabled(&lll->df_rx_cfg)) {
/* Dropp further processing of the event. */
return;
}
/* If there are no IQ samples due to insufficient resources HCI event should inform about
* it by store single octet with special I_sample and Q_sample data.
*/
samples_cnt = MAX(1, iq_report->sample_count);
sep = meta_evt(buf, BT_HCI_EVT_LE_CONNECTION_IQ_REPORT,
(sizeof(*sep) + (samples_cnt * sizeof(struct bt_hci_le_iq_sample))));
rssi = RSSI_DBM_TO_DECI_DBM(iq_report->hdr.rx_ftr.rssi);
sep->conn_handle = sys_cpu_to_le16(iq_report->hdr.handle);
sep->rx_phy = phy_rx;
sep->rssi = sys_cpu_to_le16(rssi);
sep->rssi_ant_id = iq_report->rssi_ant_id;
sep->cte_type = iq_report->cte_info.type;
sep->data_chan_idx = iq_report->chan_idx;
sep->conn_evt_counter = sys_cpu_to_le16(iq_report->event_counter);
if (sep->cte_type == BT_HCI_LE_AOA_CTE) {
sep->slot_durations = iq_report->local_slot_durations;
} else if (sep->cte_type == BT_HCI_LE_AOD_CTE_1US) {
sep->slot_durations = BT_HCI_LE_ANTENNA_SWITCHING_SLOT_1US;
} else {
sep->slot_durations = BT_HCI_LE_ANTENNA_SWITCHING_SLOT_2US;
}
sep->packet_status = iq_report->packet_status;
if (iq_report->packet_status == BT_HCI_LE_CTE_INSUFFICIENT_RESOURCES) {
sep->sample[0].i = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
sep->sample[0].q = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
sep->sample_count = 0U;
} else {
for (uint8_t idx = 0U; idx < samples_cnt; ++idx) {
sep->sample[idx].i = IQ_CONVERT_12_TO_8_BIT(iq_report->sample[idx].i);
sep->sample[idx].q = IQ_CONVERT_12_TO_8_BIT(iq_report->sample[idx].q);
}
sep->sample_count = samples_cnt;
}
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
static void le_df_set_conn_cte_req_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_conn_cte_req_enable *cmd = (void *)buf->data;
struct bt_hci_rp_le_conn_cte_req_enable *rp;
uint16_t handle, handle_le16;
uint8_t status;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_df_set_conn_cte_req_enable(handle, cmd->enable,
sys_le16_to_cpu(cmd->cte_request_interval),
cmd->requested_cte_length, cmd->requested_cte_type);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
}
static void le_df_cte_req_failed(uint8_t error_code, uint16_t handle, struct net_buf *buf)
{
struct bt_hci_evt_le_cte_req_failed *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CTE_REQUEST_FAILED)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_CTE_REQUEST_FAILED, sizeof(*sep));
sep->status = error_code;
sep->conn_handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
static void le_df_set_conn_cte_rsp_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_conn_cte_rsp_enable *cmd = (void *)buf->data;
struct bt_hci_rp_le_conn_cte_rsp_enable *rp;
uint16_t handle, handle_le16;
uint8_t status;
handle_le16 = cmd->handle;
handle = sys_le16_to_cpu(handle_le16);
status = ll_df_set_conn_cte_rsp_enable(handle, cmd->enable);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->handle = handle_le16;
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
static void le_df_read_ant_inf(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_ant_info *rp;
uint8_t max_switch_pattern_len;
uint8_t switch_sample_rates;
uint8_t max_cte_len;
uint8_t num_ant;
ll_df_read_ant_inf(&switch_sample_rates, &num_ant,
&max_switch_pattern_len, &max_cte_len);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->max_switch_pattern_len = max_switch_pattern_len;
rp->switch_sample_rates = switch_sample_rates;
rp->max_cte_len = max_cte_len;
rp->num_ant = num_ant;
rp->status = 0x00;
}
#endif /* CONFIG_BT_CTLR_DF */
#if defined(CONFIG_BT_CTLR_DTM_HCI)
static void le_rx_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, BT_HCI_LE_RX_PHY_1M, BT_HCI_LE_MOD_INDEX_STANDARD,
BT_HCI_LE_TEST_CTE_DISABLED, BT_HCI_LE_TEST_CTE_TYPE_ANY,
BT_HCI_LE_TEST_SLOT_DURATION_ANY, BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY,
NULL);
*evt = cmd_complete_status(status);
}
static void le_tx_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_tx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload,
BT_HCI_LE_TX_PHY_1M, BT_HCI_LE_TEST_CTE_DISABLED,
BT_HCI_LE_TEST_CTE_TYPE_ANY, BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY,
NULL, BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
static void le_test_end(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_test_end *rp;
uint16_t rx_pkt_count;
uint8_t status;
status = ll_test_end(&rx_pkt_count);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->rx_pkt_count = sys_cpu_to_le16(rx_pkt_count);
}
static void le_enh_rx_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_enh_rx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, cmd->phy, cmd->mod_index, BT_HCI_LE_TEST_CTE_DISABLED,
BT_HCI_LE_TEST_CTE_TYPE_ANY, BT_HCI_LE_TEST_SLOT_DURATION_ANY,
BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY, NULL);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
static void le_rx_test_v3(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rx_test_v3 *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, cmd->phy, cmd->mod_index, cmd->expected_cte_len,
cmd->expected_cte_type, cmd->slot_durations, cmd->switch_pattern_len,
cmd->ant_ids);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
static void le_enh_tx_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_enh_tx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
BT_HCI_LE_TEST_CTE_DISABLED, BT_HCI_LE_TEST_CTE_TYPE_ANY,
BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY, NULL,
BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
static void le_tx_test_v3(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_tx_test_v3 *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
cmd->cte_len, cmd->cte_type, cmd->switch_pattern_len, cmd->ant_ids,
BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
static void le_tx_test_v4(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_tx_test_v4 *cmd = (void *)buf->data;
struct bt_hci_cp_le_tx_test_v4_tx_power *tx_power = (void *)(buf->data +
sizeof(struct bt_hci_cp_le_tx_test_v4) + cmd->switch_pattern_len);
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
cmd->cte_len, cmd->cte_type, cmd->switch_pattern_len, cmd->ant_ids,
tx_power->tx_power);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V4 */
#endif /* CONFIG_BT_CTLR_DTM_HCI */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_BROADCASTER)
static void le_set_adv_set_random_addr(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_cp_le_set_adv_set_random_addr *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_aux_random_addr_set(handle, &cmd->bdaddr.val[0]);
*evt = cmd_complete_status(status);
}
static void le_set_ext_adv_param(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_adv_param *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_ext_adv_param *rp;
uint32_t min_interval;
uint16_t evt_prop;
uint8_t tx_pwr;
uint8_t status;
uint8_t phy_p;
uint8_t phy_s;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
if (cmd->handle > BT_HCI_LE_ADV_HANDLE_MAX) {
*evt = cmd_complete_status(BT_HCI_ERR_INVALID_PARAM);
return;
}
min_interval = sys_get_le24(cmd->prim_min_interval);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK)) {
const uint32_t max_interval =
sys_get_le24(cmd->prim_max_interval);
/* Compare advertising interval maximum with implementation
* supported advertising interval maximum value defined in the
* Kconfig CONFIG_BT_CTLR_ADV_INTERVAL_MAX.
*/
if ((min_interval > max_interval) ||
(min_interval < BT_HCI_LE_PRIM_ADV_INTERVAL_MIN) ||
(max_interval > CONFIG_BT_CTLR_ADV_INTERVAL_MAX)) {
*evt = cmd_complete_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
}
status = ll_adv_set_by_hci_handle_get_or_new(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
evt_prop = sys_le16_to_cpu(cmd->props);
tx_pwr = cmd->tx_power;
phy_p = BIT(cmd->prim_adv_phy - 1);
phy_s = BIT(cmd->sec_adv_phy - 1);
status = ll_adv_params_set(handle, evt_prop, min_interval,
PDU_ADV_TYPE_EXT_IND, cmd->own_addr_type,
cmd->peer_addr.type, cmd->peer_addr.a.val,
cmd->prim_channel_map, cmd->filter_policy,
&tx_pwr, phy_p, cmd->sec_adv_max_skip, phy_s,
cmd->sid, cmd->scan_req_notify_enable);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->tx_power = tx_pwr;
}
static void le_set_ext_adv_data(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_adv_data *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_aux_ad_data_set(handle, cmd->op, cmd->frag_pref,
cmd->len, cmd->data);
*evt = cmd_complete_status(status);
}
static void le_set_ext_scan_rsp_data(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_scan_rsp_data *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_aux_sr_data_set(handle, cmd->op, cmd->frag_pref,
cmd->len, cmd->data);
*evt = cmd_complete_status(status);
}
static void le_set_ext_adv_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_adv_enable *cmd = (void *)buf->data;
struct bt_hci_ext_adv_set *s;
uint8_t set_num;
uint8_t enable;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
set_num = cmd->set_num;
if (!set_num) {
if (cmd->enable) {
*evt = cmd_complete_status(BT_HCI_ERR_INVALID_PARAM);
return;
}
/* FIXME: Implement disable of all advertising sets */
*evt = cmd_complete_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
s = (void *) cmd->s;
enable = cmd->enable;
do {
status = ll_adv_set_by_hci_handle_get(s->handle, &handle);
if (status) {
break;
}
/* TODO: duration and events parameter use. */
#if defined(CONFIG_BT_HCI_MESH_EXT)
status = ll_adv_enable(handle, cmd->enable, 0, 0, 0, 0, 0);
#else /* !CONFIG_BT_HCI_MESH_EXT */
status = ll_adv_enable(handle, cmd->enable,
s->duration, s->max_ext_adv_evts);
#endif /* !CONFIG_BT_HCI_MESH_EXT */
if (status) {
/* TODO: how to handle succeeded ones before this
* error.
*/
break;
}
s++;
} while (--set_num);
*evt = cmd_complete_status(status);
}
static void le_read_max_adv_data_len(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_max_adv_data_len *rp;
uint16_t max_adv_data_len;
if (adv_cmds_ext_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
max_adv_data_len = ll_adv_aux_max_data_length_get();
rp->max_adv_data_len = sys_cpu_to_le16(max_adv_data_len);
rp->status = 0x00;
}
static void le_read_num_adv_sets(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_num_adv_sets *rp;
if (adv_cmds_ext_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->num_sets = ll_adv_aux_set_count_get();
rp->status = 0x00;
}
static void le_remove_adv_set(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_remove_adv_set *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_aux_set_remove(handle);
*evt = cmd_complete_status(status);
}
static void le_clear_adv_sets(struct net_buf *buf, struct net_buf **evt)
{
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_set_clear();
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_ADV_PERIODIC)
static void le_set_per_adv_param(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_per_adv_param *cmd = (void *)buf->data;
uint16_t max_interval;
uint16_t flags;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
max_interval = sys_le16_to_cpu(cmd->max_interval);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK)) {
const uint32_t min_interval =
sys_le16_to_cpu(cmd->min_interval);
/* Compare periodic advertising interval maximum with
* implementation supported periodic advertising interval
* maximum value defined in the Kconfig
* CONFIG_BT_CTLR_ADV_PERIODIC_INTERVAL_MAX.
*/
if ((min_interval > max_interval) ||
(min_interval < BT_HCI_LE_PER_ADV_INTERVAL_MIN) ||
(max_interval > CONFIG_BT_CTLR_ADV_PERIODIC_INTERVAL_MAX)) {
*evt = cmd_complete_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
flags = sys_le16_to_cpu(cmd->props);
status = ll_adv_sync_param_set(handle, max_interval, flags);
*evt = cmd_complete_status(status);
}
static void le_set_per_adv_data(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_per_adv_data *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_sync_ad_data_set(handle, cmd->op, cmd->len,
cmd->data);
*evt = cmd_complete_status(status);
}
static void le_set_per_adv_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_per_adv_enable *cmd = (void *)buf->data;
uint8_t status;
uint8_t handle;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_set_by_hci_handle_get(cmd->handle, &handle);
if (status) {
*evt = cmd_complete_status(status);
return;
}
status = ll_adv_sync_enable(handle, cmd->enable);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_ADV_PERIODIC */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
static void le_set_ext_scan_param(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_scan_param *cmd = (void *)buf->data;
struct bt_hci_ext_scan_phy *p;
uint8_t own_addr_type;
uint8_t filter_policy;
uint8_t phys_bitmask;
uint8_t status;
uint8_t phys;
if (adv_cmds_ext_check(evt)) {
return;
}
/* Number of bits set indicate scan sets to be configured by calling
* ll_scan_params_set function.
*/
phys_bitmask = BT_HCI_LE_EXT_SCAN_PHY_1M;
if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) {
phys_bitmask |= BT_HCI_LE_EXT_SCAN_PHY_CODED;
}
phys = cmd->phys;
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
(phys > phys_bitmask)) {
*evt = cmd_complete_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
own_addr_type = cmd->own_addr_type;
filter_policy = cmd->filter_policy;
p = cmd->p;
/* Irrespective of enabled PHYs to scan for, ll_scan_params_set needs
* to be called to initialise the scan sets.
* Passing interval and window as 0, disable the particular scan set
* from being enabled.
*/
do {
uint16_t interval;
uint16_t window;
uint8_t type;
uint8_t phy;
/* Get single PHY bit from the loop bitmask */
phy = BIT(find_lsb_set(phys_bitmask) - 1);
/* Pass the PHY (1M or Coded) of scan set in MSbits of type
* parameter
*/
type = (phy << 1);
/* If current PHY is one of the PHY in the Scanning_PHYs,
* pick the supplied scan type, interval and window.
*/
if (phys & phy) {
type |= (p->type & 0x01);
interval = sys_le16_to_cpu(p->interval);
window = sys_le16_to_cpu(p->window);
p++;
} else {
interval = 0U;
window = 0U;
}
status = ll_scan_params_set(type, interval, window,
own_addr_type, filter_policy);
if (status) {
break;
}
phys_bitmask &= (phys_bitmask - 1);
} while (phys_bitmask);
*evt = cmd_complete_status(status);
}
static void le_set_ext_scan_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_ext_scan_enable *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* Initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) {
if (0) {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
} else if (dup_count == DUP_FILTER_DISABLED) {
dup_scan = true;
/* All entries reset */
dup_count = 0;
dup_curr = 0U;
} else if (!dup_scan) {
dup_scan = true;
dup_ext_adv_reset();
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
} else {
/* All entries reset */
dup_count = 0;
dup_curr = 0U;
}
} else {
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
dup_scan = false;
#else /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
dup_count = DUP_FILTER_DISABLED;
#endif /* !CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
status = ll_scan_enable(cmd->enable, cmd->duration, cmd->period);
/* NOTE: As filter duplicates is implemented here in HCI source code,
* enabling of already enabled scanning shall succeed after
* updates to filter duplicates is handled in the above
* statements. Refer to BT Spec v5.0 Vol 2 Part E Section 7.8.11.
*/
if (!IS_ENABLED(CONFIG_BT_CTLR_SCAN_ENABLE_STRICT) &&
(status == BT_HCI_ERR_CMD_DISALLOWED)) {
status = BT_HCI_ERR_SUCCESS;
}
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
static void le_per_adv_create_sync(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_per_adv_create_sync *cmd = (void *)buf->data;
uint16_t sync_timeout;
uint8_t status;
uint16_t skip;
if (adv_cmds_ext_check(NULL)) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
if (!IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST) &&
(cmd->options & BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST)) {
*evt = cmd_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
if (!IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) &&
(cmd->options &
(BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED |
BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE)) ==
BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE) {
*evt = cmd_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
/* FIXME: Check for HCI LE Set Periodic Advertising Receive Enable
* command support and if reporting is initially disabled then
* return error code Connection Failed to be Established /
* Synchronization Timeout (0x3E).
*/
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* Initialize duplicate filtering */
if (cmd->options & BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_FILTER_DUPLICATE) {
if (!dup_scan || (dup_count == DUP_FILTER_DISABLED)) {
dup_count = 0;
dup_curr = 0U;
} else {
/* NOTE: Invalidate dup_ext_adv_mode array entries is
* done when sync is established.
*/
}
} else if (!dup_scan) {
dup_count = DUP_FILTER_DISABLED;
}
#endif
skip = sys_le16_to_cpu(cmd->skip);
sync_timeout = sys_le16_to_cpu(cmd->sync_timeout);
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING)
if ((cmd->cte_type & BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_INVALID_VALUE) != 0) {
status = BT_HCI_ERR_CMD_DISALLOWED;
#else
if (cmd->cte_type != BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_FILTERING) {
status = BT_HCI_ERR_INVALID_PARAM;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_CTE_TYPE_FILTERING */
} else {
status = ll_sync_create(cmd->options, cmd->sid, cmd->addr.type, cmd->addr.a.val,
skip, sync_timeout, cmd->cte_type);
}
*evt = cmd_status(status);
}
static void le_per_adv_create_sync_cancel(struct net_buf *buf,
struct net_buf **evt, void **node_rx)
{
struct bt_hci_evt_cc_status *ccst;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_sync_create_cancel(node_rx);
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = status;
}
static void le_per_adv_terminate_sync(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_per_adv_terminate_sync *cmd = (void *)buf->data;
struct bt_hci_evt_cc_status *ccst;
uint16_t handle;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
handle = sys_le16_to_cpu(cmd->handle);
status = ll_sync_terminate(handle);
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = status;
}
static void le_per_adv_recv_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_per_adv_recv_enable *cmd = (void *)buf->data;
struct bt_hci_evt_cc_status *ccst;
uint16_t handle;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
handle = sys_le16_to_cpu(cmd->handle);
status = ll_sync_recv_enable(handle, cmd->enable);
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
if (!status) {
if (cmd->enable &
BT_HCI_LE_SET_PER_ADV_RECV_ENABLE_FILTER_DUPLICATE) {
if (!dup_scan || (dup_count == DUP_FILTER_DISABLED)) {
dup_count = 0;
dup_curr = 0U;
} else {
/* NOTE: Invalidate dup_ext_adv_mode array
* entries is done when sync is
* established.
*/
}
} else if (!dup_scan) {
dup_count = DUP_FILTER_DISABLED;
}
}
#endif
ccst = hci_cmd_complete(evt, sizeof(*ccst));
ccst->status = status;
}
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST)
static void le_add_dev_to_pal(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_add_dev_to_per_adv_list *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_pal_add(&cmd->addr, cmd->sid);
*evt = cmd_complete_status(status);
}
static void le_rem_dev_from_pal(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rem_dev_from_per_adv_list *cmd = (void *)buf->data;
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_pal_remove(&cmd->addr, cmd->sid);
*evt = cmd_complete_status(status);
}
static void le_clear_pal(struct net_buf *buf, struct net_buf **evt)
{
uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_pal_clear();
*evt = cmd_complete_status(status);
}
static void le_read_pal_size(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_read_per_adv_list_size *rp;
if (adv_cmds_ext_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->list_size = ll_pal_size_get();
}
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST */
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CENTRAL)
static void le_ext_create_connection(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_ext_create_conn *cmd = (void *)buf->data;
struct bt_hci_ext_conn_phy *p;
uint8_t peer_addr_type;
uint8_t own_addr_type;
uint8_t filter_policy;
uint8_t phys_bitmask;
uint8_t *peer_addr;
uint8_t status;
uint8_t phys;
if (adv_cmds_ext_check(NULL)) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
/* Number of bits set indicate scan sets to be configured by calling
* ll_create_connection function.
*/
phys_bitmask = BT_HCI_LE_EXT_SCAN_PHY_1M;
if (IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED)) {
phys_bitmask |= BT_HCI_LE_EXT_SCAN_PHY_CODED;
}
phys = cmd->phys;
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
(phys > phys_bitmask)) {
*evt = cmd_status(BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
return;
}
filter_policy = cmd->filter_policy;
own_addr_type = cmd->own_addr_type;
peer_addr_type = cmd->peer_addr.type;
peer_addr = cmd->peer_addr.a.val;
p = cmd->p;
do {
uint16_t supervision_timeout;
uint16_t conn_interval_max;
uint16_t scan_interval;
uint16_t conn_latency;
uint16_t scan_window;
uint8_t phy;
phy = BIT(find_lsb_set(phys_bitmask) - 1);
if (phys & phy) {
scan_interval = sys_le16_to_cpu(p->scan_interval);
scan_window = sys_le16_to_cpu(p->scan_window);
conn_interval_max =
sys_le16_to_cpu(p->conn_interval_max);
conn_latency = sys_le16_to_cpu(p->conn_latency);
supervision_timeout =
sys_le16_to_cpu(p->supervision_timeout);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK)) {
status = check_cconn_params(true, scan_interval,
scan_window,
conn_interval_max,
conn_latency,
supervision_timeout);
if (status) {
*evt = cmd_status(status);
return;
}
}
status = ll_create_connection(scan_interval,
scan_window,
filter_policy,
peer_addr_type,
peer_addr,
own_addr_type,
conn_interval_max,
conn_latency,
supervision_timeout,
phy);
p++;
} else {
uint8_t type;
type = (phy << 1);
/* NOTE: Pass invalid interval value to not start
* scanning using this scan instance.
*/
status = ll_scan_params_set(type, 0, 0, 0, 0);
}
if (status) {
*evt = cmd_status(status);
return;
}
phys_bitmask &= (phys_bitmask - 1);
} while (phys_bitmask);
status = ll_connect_enable(phys & BT_HCI_LE_EXT_SCAN_PHY_CODED);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CENTRAL */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
static void le_cis_request(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_cis_req *sep;
struct node_rx_conn_iso_req *req;
void *node;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CIS_REQ)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_CIS_REQ, sizeof(*sep));
sep->acl_handle = sys_cpu_to_le16(node_rx->hdr.handle);
/* Check for pdu field being aligned before accessing CIS established
* event.
*/
node = pdu_data;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_conn_iso_estab));
req = node;
sep->cis_handle = sys_cpu_to_le16(req->cis_handle);
sep->cig_id = req->cig_id;
sep->cis_id = req->cis_id;
}
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_CONN_ISO)
static void le_cis_established(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct lll_conn_iso_stream_rxtx *lll_cis_c;
struct lll_conn_iso_stream_rxtx *lll_cis_p;
struct bt_hci_evt_le_cis_established *sep;
struct lll_conn_iso_stream *lll_cis;
struct node_rx_conn_iso_estab *est;
struct ll_conn_iso_stream *cis;
struct ll_conn_iso_group *cig;
bool is_central;
void *node;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CIS_ESTABLISHED)) {
return;
}
cis = node_rx->hdr.rx_ftr.param;
cig = cis->group;
lll_cis = &cis->lll;
is_central = cig->lll.role == BT_CONN_ROLE_CENTRAL;
lll_cis_c = is_central ? &lll_cis->tx : &lll_cis->rx;
lll_cis_p = is_central ? &lll_cis->rx : &lll_cis->tx;
sep = meta_evt(buf, BT_HCI_EVT_LE_CIS_ESTABLISHED, sizeof(*sep));
/* Check for pdu field being aligned before accessing CIS established
* event.
*/
node = pdu_data;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_conn_iso_estab));
est = node;
sep->status = est->status;
sep->conn_handle = sys_cpu_to_le16(est->cis_handle);
sys_put_le24(cig->sync_delay, sep->cig_sync_delay);
sys_put_le24(cis->sync_delay, sep->cis_sync_delay);
sys_put_le24(cig->c_latency, sep->c_latency);
sys_put_le24(cig->p_latency, sep->p_latency);
sep->c_phy = lll_cis_c->phy;
sep->p_phy = lll_cis_p->phy;
sep->nse = lll_cis->num_subevents;
sep->c_bn = lll_cis_c->burst_number;
sep->p_bn = lll_cis_p->burst_number;
sep->c_ft = lll_cis_c->flush_timeout;
sep->p_ft = lll_cis_p->flush_timeout;
sep->c_max_pdu = sys_cpu_to_le16(lll_cis_c->max_octets);
sep->p_max_pdu = sys_cpu_to_le16(lll_cis_p->max_octets);
sep->interval = sys_cpu_to_le16(cig->iso_interval);
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
if (is_central) {
cis_pending_count--;
}
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
}
#endif /* CONFIG_BT_CTLR_CONN_ISO */
static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt, void **node_rx)
{
switch (ocf) {
case BT_OCF(BT_HCI_OP_LE_SET_EVENT_MASK):
le_set_event_mask(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_BUFFER_SIZE):
le_read_buffer_size(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
case BT_OCF(BT_HCI_OP_LE_READ_BUFFER_SIZE_V2):
le_read_buffer_size_v2(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
case BT_OCF(BT_HCI_OP_LE_READ_LOCAL_FEATURES):
le_read_local_features(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_RANDOM_ADDRESS):
le_set_random_address(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
case BT_OCF(BT_HCI_OP_LE_READ_FAL_SIZE):
le_read_fal_size(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CLEAR_FAL):
le_clear_fal(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_ADD_DEV_TO_FAL):
le_add_dev_to_fal(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REM_DEV_FROM_FAL):
le_rem_dev_from_fal(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
case BT_OCF(BT_HCI_OP_LE_ENCRYPT):
le_encrypt(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_RAND):
le_rand(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_SUPP_STATES):
le_read_supp_states(cmd, evt);
break;
#if defined(CONFIG_BT_BROADCASTER)
case BT_OCF(BT_HCI_OP_LE_SET_ADV_PARAM):
le_set_adv_param(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_ADV_CHAN_TX_POWER):
le_read_adv_chan_tx_power(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_ADV_DATA):
le_set_adv_data(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_SCAN_RSP_DATA):
le_set_scan_rsp_data(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_ADV_ENABLE):
le_set_adv_enable(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_ADV_ISO)
case BT_OCF(BT_HCI_OP_LE_CREATE_BIG):
le_create_big(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CREATE_BIG_TEST):
le_create_big_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_TERMINATE_BIG):
le_terminate_big(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ADV_ISO */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
case BT_OCF(BT_HCI_OP_LE_SET_SCAN_PARAM):
le_set_scan_param(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_SCAN_ENABLE):
le_set_scan_enable(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
case BT_OCF(BT_HCI_OP_LE_BIG_CREATE_SYNC):
le_big_create_sync(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_BIG_TERMINATE_SYNC):
le_big_terminate_sync(cmd, evt, node_rx);
break;
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CENTRAL)
case BT_OCF(BT_HCI_OP_LE_CREATE_CONN):
le_create_connection(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CREATE_CONN_CANCEL):
le_create_conn_cancel(cmd, evt, node_rx);
break;
case BT_OCF(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF):
le_set_host_chan_classif(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC)
case BT_OCF(BT_HCI_OP_LE_START_ENCRYPTION):
le_start_encryption(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO)
case BT_OCF(BT_HCI_OP_LE_SET_CIG_PARAMS):
le_set_cig_parameters(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_CIG_PARAMS_TEST):
le_set_cig_params_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CREATE_CIS):
le_create_cis(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REMOVE_CIG):
le_remove_cig(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
#if defined(CONFIG_BT_CTLR_LE_ENC)
case BT_OCF(BT_HCI_OP_LE_LTK_REQ_REPLY):
le_ltk_req_reply(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY):
le_ltk_req_neg_reply(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case BT_OCF(BT_HCI_OP_LE_ACCEPT_CIS):
le_accept_cis(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REJECT_CIS):
le_reject_cis(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CTLR_ISO)
case BT_OCF(BT_HCI_OP_LE_SETUP_ISO_PATH):
le_setup_iso_path(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REMOVE_ISO_PATH):
le_remove_iso_path(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_ISO_TEST_END):
le_iso_test_end(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ISO */
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
case BT_OCF(BT_HCI_OP_LE_ISO_TRANSMIT_TEST):
le_iso_transmit_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_ISO_TX_SYNC):
le_read_iso_tx_sync(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
case BT_OCF(BT_HCI_OP_LE_ISO_RECEIVE_TEST):
le_iso_receive_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_ISO_READ_TEST_COUNTERS):
le_iso_read_test_counters(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY)
case BT_OCF(BT_HCI_OP_LE_READ_ISO_LINK_QUALITY):
le_read_iso_link_quality(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_READ_ISO_LINK_QUALITY */
#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SET_HOST_FEATURE)
case BT_OCF(BT_HCI_OP_LE_SET_HOST_FEATURE):
le_set_host_feature(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_SET_HOST_FEATURE */
#if defined(CONFIG_BT_CONN)
case BT_OCF(BT_HCI_OP_LE_READ_CHAN_MAP):
le_read_chan_map(cmd, evt);
break;
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
case BT_OCF(BT_HCI_OP_LE_READ_REMOTE_FEATURES):
le_read_remote_features(cmd, evt);
break;
#endif /* CONFIG_BT_CENTRAL || CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG */
case BT_OCF(BT_HCI_OP_LE_CONN_UPDATE):
le_conn_update(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case BT_OCF(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY):
le_conn_param_req_reply(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY):
le_conn_param_req_neg_reply(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case BT_OCF(BT_HCI_OP_LE_SET_DATA_LEN):
le_set_data_len(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_DEFAULT_DATA_LEN):
le_read_default_data_len(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN):
le_write_default_data_len(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_MAX_DATA_LEN):
le_read_max_data_len(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
case BT_OCF(BT_HCI_OP_LE_READ_PHY):
le_read_phy(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_DEFAULT_PHY):
le_set_default_phy(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_PHY):
le_set_phy(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_BROADCASTER)
case BT_OCF(BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR):
le_set_adv_set_random_addr(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_EXT_ADV_PARAM):
le_set_ext_adv_param(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_EXT_ADV_DATA):
le_set_ext_adv_data(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA):
le_set_ext_scan_rsp_data(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE):
le_set_ext_adv_enable(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_MAX_ADV_DATA_LEN):
le_read_max_adv_data_len(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_NUM_ADV_SETS):
le_read_num_adv_sets(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REMOVE_ADV_SET):
le_remove_adv_set(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_CLEAR_ADV_SETS):
le_clear_adv_sets(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_ADV_PERIODIC)
case BT_OCF(BT_HCI_OP_LE_SET_PER_ADV_PARAM):
le_set_per_adv_param(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_PER_ADV_DATA):
le_set_per_adv_data(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_PER_ADV_ENABLE):
le_set_per_adv_enable(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_ADV_PERIODIC */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
case BT_OCF(BT_HCI_OP_LE_SET_EXT_SCAN_PARAM):
le_set_ext_scan_param(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE):
le_set_ext_scan_enable(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case BT_OCF(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC):
le_per_adv_create_sync(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL):
le_per_adv_create_sync_cancel(cmd, evt, node_rx);
break;
case BT_OCF(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC):
le_per_adv_terminate_sync(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE):
le_per_adv_recv_enable(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST)
case BT_OCF(BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST):
le_add_dev_to_pal(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST):
le_rem_dev_from_pal(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CLEAR_PER_ADV_LIST):
le_clear_pal(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_PER_ADV_LIST_SIZE):
le_read_pal_size(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST */
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CONN)
#if defined(CONFIG_BT_CENTRAL)
case BT_OCF(BT_HCI_OP_LE_EXT_CREATE_CONN):
le_ext_create_connection(cmd, evt);
break;
#endif /* CONFIG_BT_CENTRAL */
#endif /* CONFIG_BT_CONN */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CTLR_PRIVACY)
case BT_OCF(BT_HCI_OP_LE_ADD_DEV_TO_RL):
le_add_dev_to_rl(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_REM_DEV_FROM_RL):
le_rem_dev_from_rl(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_CLEAR_RL):
le_clear_rl(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_RL_SIZE):
le_read_rl_size(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_PEER_RPA):
le_read_peer_rpa(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_READ_LOCAL_RPA):
le_read_local_rpa(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_ADDR_RES_ENABLE):
le_set_addr_res_enable(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_RPA_TIMEOUT):
le_set_rpa_timeout(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_PRIVACY_MODE):
le_set_privacy_mode(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_PRIVACY */
case BT_OCF(BT_HCI_OP_LE_READ_TX_POWER):
le_read_tx_power(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DF)
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
case BT_OCF(BT_HCI_OP_LE_SET_CL_CTE_TX_PARAMS):
le_df_set_cl_cte_tx_params(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_SET_CL_CTE_TX_ENABLE):
le_df_set_cl_cte_enable(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
case BT_OCF(BT_HCI_OP_LE_SET_CL_CTE_SAMPLING_ENABLE):
le_df_set_cl_iq_sampling_enable(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
case BT_OCF(BT_HCI_OP_LE_READ_ANT_INFO):
le_df_read_ant_inf(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_TX)
case BT_OCF(BT_HCI_OP_LE_SET_CONN_CTE_TX_PARAMS):
le_df_set_conn_cte_tx_params(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
case BT_OCF(BT_HCI_OP_LE_SET_CONN_CTE_RX_PARAMS):
le_df_set_conn_cte_rx_params(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case BT_OCF(BT_HCI_OP_LE_CONN_CTE_REQ_ENABLE):
le_df_set_conn_cte_req_enable(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
case BT_OCF(BT_HCI_OP_LE_CONN_CTE_RSP_ENABLE):
le_df_set_conn_cte_rsp_enable(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
#endif /* CONFIG_BT_CTLR_DF */
#if defined(CONFIG_BT_CTLR_DTM_HCI)
case BT_OCF(BT_HCI_OP_LE_RX_TEST):
le_rx_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_TX_TEST):
le_tx_test(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_TEST_END):
le_test_end(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_LE_ENH_RX_TEST):
le_enh_rx_test(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
case BT_OCF(BT_HCI_OP_LE_RX_TEST_V3):
le_rx_test_v3(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
case BT_OCF(BT_HCI_OP_LE_ENH_TX_TEST):
le_enh_tx_test(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
case BT_OCF(BT_HCI_OP_LE_TX_TEST_V3):
le_tx_test_v3(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
case BT_OCF(BT_HCI_OP_LE_TX_TEST_V4):
le_tx_test_v4(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V4 */
#endif /* CONFIG_BT_CTLR_DTM_HCI */
default:
return -EINVAL;
}
return 0;
}
#if defined(CONFIG_BT_HCI_VS)
static void vs_read_version_info(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_vs_read_version_info *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->hw_platform = sys_cpu_to_le16(BT_HCI_VS_HW_PLAT);
rp->hw_variant = sys_cpu_to_le16(BT_HCI_VS_HW_VAR);
rp->fw_variant = 0U;
rp->fw_version = (KERNEL_VERSION_MAJOR & 0xff);
rp->fw_revision = sys_cpu_to_le16(KERNEL_VERSION_MINOR);
rp->fw_build = sys_cpu_to_le32(KERNEL_PATCHLEVEL & 0xffff);
}
static void vs_read_supported_commands(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_rp_vs_read_supported_commands *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)memset(&rp->commands[0], 0, sizeof(rp->commands));
/* Set Version Information, Supported Commands, Supported Features. */
rp->commands[0] |= BIT(0) | BIT(1) | BIT(2);
#if defined(CONFIG_BT_HCI_VS_EXT)
/* Write BD_ADDR, Read Build Info */
rp->commands[0] |= BIT(5) | BIT(7);
/* Read Static Addresses, Read Key Hierarchy Roots */
rp->commands[1] |= BIT(0) | BIT(1);
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
/* Write Tx Power, Read Tx Power */
rp->commands[1] |= BIT(5) | BIT(6);
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
#if defined(CONFIG_USB_DEVICE_BLUETOOTH_VS_H4)
/* Read Supported USB Transport Modes */
rp->commands[1] |= BIT(7);
/* Set USB Transport Mode */
rp->commands[2] |= BIT(0);
#endif /* USB_DEVICE_BLUETOOTH_VS_H4 */
#endif /* CONFIG_BT_HCI_VS_EXT */
}
static void vs_read_supported_features(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_rp_vs_read_supported_features *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
(void)memset(&rp->features[0], 0x00, sizeof(rp->features));
}
uint8_t __weak hci_vendor_read_static_addr(struct bt_hci_vs_static_addr addrs[],
uint8_t size)
{
ARG_UNUSED(addrs);
ARG_UNUSED(size);
return 0;
}
/* If Zephyr VS HCI commands are not enabled provide this functionality directly
*/
#if !defined(CONFIG_BT_HCI_VS_EXT)
uint8_t bt_read_static_addr(struct bt_hci_vs_static_addr addrs[], uint8_t size)
{
return hci_vendor_read_static_addr(addrs, size);
}
#endif /* !defined(CONFIG_BT_HCI_VS_EXT) */
#if defined(CONFIG_BT_HCI_VS_EXT)
static void vs_write_bd_addr(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_vs_write_bd_addr *cmd = (void *)buf->data;
ll_addr_set(0, &cmd->bdaddr.val[0]);
*evt = cmd_complete_status(0x00);
}
static void vs_read_build_info(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_vs_read_build_info *rp;
#define HCI_VS_BUILD_INFO "Zephyr OS v" \
KERNEL_VERSION_STRING CONFIG_BT_CTLR_HCI_VS_BUILD_INFO
const char build_info[] = HCI_VS_BUILD_INFO;
#define BUILD_INFO_EVT_LEN (sizeof(struct bt_hci_evt_hdr) + \
sizeof(struct bt_hci_evt_cmd_complete) + \
sizeof(struct bt_hci_rp_vs_read_build_info) + \
sizeof(build_info))
Bluetooth: Refactor bluetooth buffer configuration for simplification Refactor and simplify the bluetooth buffer configurations to improve the easy of configurations and eliminate invalid ones. By moving configurations out of host and controller specific configurations and into a common one it becomes easier to configure the host and controller separately as the same configurations can be used as would be for a combined build. All HCI configurations are now given exluding the matching HCI header, which eases the configuration as the application don't have to know the different header sizes. The BT_RX_BUF_LEN is split into ACL and Event, as well as the suprising use of Command size. BT_L2CAP_RX_MTU is removed as the stack does not support reassembling of HCI ACL data to larger L2CAP PDUs. The application will have to set ACL RX size and account for the L2CAP PDU header itself. BT_EATT_RX_MTU was removed as it is only used for setting a different default value for another option which leads to the stuck kconfig symbol problem. The configurations can be updated according to the table below: ** New configuration | ** Old configuration All configurations BT_BUF_ACL_RX_SIZE | BT_L2CAP_RX_MTU + 4 BT_BUF_ACL_RX_SIZE | BT_RX_BUF_LEN - 4 BT_BUF_EVT_RX_SIZE | BT_RX_BUF_LEN - 2 BT_BUF_CMD_TX_SIZE | BT_RX_BUF_LEN - 3 BT_BUF_CMD_TX_COUNT | BT_HCI_CMD_COUNT BT_BUF_EVT_RX_COUNT | BT_RX_BUF_COUNT BT_BUF_ACL_RX_COUNT | BT_RX_BUF_COUNT BT_BUF_ACL_RX_COUNT | BT_ACL_RX_COUNT BT_BUF_EVT_DISCARDABLE_SIZE | BT_DISCARDABLE_BUF_SIZE - 2 BT_BUF_EVT_DISCARDABLE_COUNT | BT_DISCARDABLE_BUF_COUNT Controller-build BT_BUF_ACL_TX_SIZE | BT_CTLR_TX_BUFFERS_SIZE BT_BUF_ACL_TX_COUNT | BT_CTLR_TX_BUFFER HCI-bridge BT_BUF_ACL_TX_SIZE | BT_HCI_ACL_DATA_SIZE BT_BUF_ACL_TX_COUNT | 6 Fixed invalid configurations setting either BT_L2CAP_RX_MTU or BT_CTLR_DATA_LENGTH_MAX larger than BT_RX_BUF_LEN could lead to buffer overruns. Fix advertising report max data length calculation. This always used the BT_DISCARDABLE_BUF_SIZE macro but this feature can be turned off and advertising reports will be allocated from the RX buffer in that case. Also controller-build does not have this buffer (in hci_raw.c). Also the wrong HCI header was used in the calculation, HCI event header should have been used instead of HCI ACL header. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2021-04-27 10:38:23 +02:00
BUILD_ASSERT(CONFIG_BT_BUF_EVT_RX_SIZE >= BUILD_INFO_EVT_LEN);
rp = hci_cmd_complete(evt, sizeof(*rp) + sizeof(build_info));
rp->status = 0x00;
memcpy(rp->info, build_info, sizeof(build_info));
}
void __weak hci_vendor_read_key_hierarchy_roots(uint8_t ir[16], uint8_t er[16])
{
/* Mark IR as invalid */
(void)memset(ir, 0x00, 16);
/* Mark ER as invalid */
(void)memset(er, 0x00, 16);
}
static void vs_read_static_addrs(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_vs_read_static_addrs *rp;
rp = hci_cmd_complete(evt, sizeof(*rp) +
sizeof(struct bt_hci_vs_static_addr));
rp->status = 0x00;
rp->num_addrs = hci_vendor_read_static_addr(rp->a, 1);
}
static void vs_read_key_hierarchy_roots(struct net_buf *buf,
struct net_buf **evt)
{
struct bt_hci_rp_vs_read_key_hierarchy_roots *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
hci_vendor_read_key_hierarchy_roots(rp->ir, rp->er);
}
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) && defined(CONFIG_BT_PERIPHERAL)
static void vs_set_min_used_chans(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_vs_set_min_num_used_chans *cmd = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(cmd->handle);
uint8_t status;
status = ll_set_min_used_chans(handle, cmd->phys, cmd->min_used_chans);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_PERIPHERAL */
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
static void vs_write_tx_power_level(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_vs_write_tx_power_level *cmd = (void *)buf->data;
struct bt_hci_rp_vs_write_tx_power_level *rp;
uint8_t handle_type;
uint16_t handle;
uint8_t status;
handle_type = cmd->handle_type;
handle = sys_le16_to_cpu(cmd->handle);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->selected_tx_power = cmd->tx_power_level;
status = ll_tx_pwr_lvl_set(handle_type, handle, &rp->selected_tx_power);
rp->status = status;
rp->handle_type = handle_type;
rp->handle = sys_cpu_to_le16(handle);
}
static void vs_read_tx_power_level(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_vs_read_tx_power_level *cmd = (void *)buf->data;
struct bt_hci_rp_vs_read_tx_power_level *rp;
uint8_t handle_type;
uint16_t handle;
uint8_t status;
handle_type = cmd->handle_type;
handle = sys_le16_to_cpu(cmd->handle);
rp = hci_cmd_complete(evt, sizeof(*rp));
status = ll_tx_pwr_lvl_get(handle_type, handle, 0, &rp->tx_power_level);
rp->status = status;
rp->handle_type = handle_type;
rp->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
#endif /* CONFIG_BT_HCI_VS_EXT */
#if defined(CONFIG_BT_HCI_MESH_EXT)
static void mesh_get_opts(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_mesh_get_opts *rp;
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->opcode = BT_HCI_OC_MESH_GET_OPTS;
rp->revision = BT_HCI_MESH_REVISION;
rp->ch_map = 0x7;
/*@todo: nRF51 only */
rp->min_tx_power = -30;
/*@todo: nRF51 only */
rp->max_tx_power = 4;
rp->max_scan_filter = CONFIG_BT_CTLR_MESH_SCAN_FILTERS;
rp->max_filter_pattern = CONFIG_BT_CTLR_MESH_SF_PATTERNS;
rp->max_adv_slot = 1U;
rp->evt_prefix_len = 0x01;
rp->evt_prefix = BT_HCI_MESH_EVT_PREFIX;
}
static void mesh_set_scan_filter(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_mesh_set_scan_filter *cmd = (void *)buf->data;
struct bt_hci_rp_mesh_set_scan_filter *rp;
uint8_t filter = cmd->scan_filter - 1;
struct scan_filter *f;
uint8_t status = 0x00;
uint8_t i;
if (filter > ARRAY_SIZE(scan_filters) ||
cmd->num_patterns > CONFIG_BT_CTLR_MESH_SF_PATTERNS) {
status = BT_HCI_ERR_INVALID_PARAM;
goto exit;
}
if (filter == sf_curr) {
status = BT_HCI_ERR_CMD_DISALLOWED;
goto exit;
}
/* duplicate filtering not supported yet */
if (cmd->filter_dup) {
status = BT_HCI_ERR_INVALID_PARAM;
goto exit;
}
f = &scan_filters[filter];
for (i = 0U; i < cmd->num_patterns; i++) {
if (!cmd->patterns[i].pattern_len ||
cmd->patterns[i].pattern_len >
BT_HCI_MESH_PATTERN_LEN_MAX) {
status = BT_HCI_ERR_INVALID_PARAM;
goto exit;
}
f->lengths[i] = cmd->patterns[i].pattern_len;
memcpy(f->patterns[i], cmd->patterns[i].pattern, f->lengths[i]);
}
f->count = cmd->num_patterns;
exit:
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->opcode = BT_HCI_OC_MESH_SET_SCAN_FILTER;
rp->scan_filter = filter + 1;
}
static void mesh_advertise(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_mesh_advertise *cmd = (void *)buf->data;
struct bt_hci_rp_mesh_advertise *rp;
uint8_t adv_slot = cmd->adv_slot;
uint8_t status;
status = ll_mesh_advertise(adv_slot,
cmd->own_addr_type, cmd->random_addr.val,
cmd->ch_map, cmd->tx_power,
cmd->min_tx_delay, cmd->max_tx_delay,
cmd->retx_count, cmd->retx_interval,
cmd->scan_duration, cmd->scan_delay,
cmd->scan_filter, cmd->data_len, cmd->data);
if (!status) {
/* Yields 0xFF if no scan filter selected */
sf_curr = cmd->scan_filter - 1;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->opcode = BT_HCI_OC_MESH_ADVERTISE;
rp->adv_slot = adv_slot;
}
static void mesh_advertise_cancel(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_mesh_advertise_cancel *cmd = (void *)buf->data;
struct bt_hci_rp_mesh_advertise_cancel *rp;
uint8_t adv_slot = cmd->adv_slot;
uint8_t status;
status = ll_mesh_advertise_cancel(adv_slot);
if (!status) {
/* Yields 0xFF if no scan filter selected */
sf_curr = 0xFF;
}
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = status;
rp->opcode = BT_HCI_OC_MESH_ADVERTISE_CANCEL;
rp->adv_slot = adv_slot;
}
static int mesh_cmd_handle(struct net_buf *cmd, struct net_buf **evt)
{
struct bt_hci_cp_mesh *cp_mesh;
uint8_t mesh_op;
if (cmd->len < sizeof(*cp_mesh)) {
BT_ERR("No HCI VSD Command header");
return -EINVAL;
}
cp_mesh = net_buf_pull_mem(cmd, sizeof(*cp_mesh));
mesh_op = cp_mesh->opcode;
switch (mesh_op) {
case BT_HCI_OC_MESH_GET_OPTS:
mesh_get_opts(cmd, evt);
break;
case BT_HCI_OC_MESH_SET_SCAN_FILTER:
mesh_set_scan_filter(cmd, evt);
break;
case BT_HCI_OC_MESH_ADVERTISE:
mesh_advertise(cmd, evt);
break;
case BT_HCI_OC_MESH_ADVERTISE_CANCEL:
mesh_advertise_cancel(cmd, evt);
break;
default:
return -EINVAL;
}
return 0;
}
#endif /* CONFIG_BT_HCI_MESH_EXT */
int hci_vendor_cmd_handle_common(uint16_t ocf, struct net_buf *cmd,
struct net_buf **evt)
{
switch (ocf) {
case BT_OCF(BT_HCI_OP_VS_READ_VERSION_INFO):
vs_read_version_info(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_READ_SUPPORTED_COMMANDS):
vs_read_supported_commands(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_READ_SUPPORTED_FEATURES):
vs_read_supported_features(cmd, evt);
break;
#if defined(CONFIG_USB_DEVICE_BLUETOOTH_VS_H4)
case BT_OCF(BT_HCI_OP_VS_READ_USB_TRANSPORT_MODE):
break;
case BT_OCF(BT_HCI_OP_VS_SET_USB_TRANSPORT_MODE):
reset(cmd, evt);
break;
#endif /* CONFIG_USB_DEVICE_BLUETOOTH_VS_H4 */
#if defined(CONFIG_BT_HCI_VS_EXT)
case BT_OCF(BT_HCI_OP_VS_READ_BUILD_INFO):
vs_read_build_info(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_WRITE_BD_ADDR):
vs_write_bd_addr(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_READ_STATIC_ADDRS):
vs_read_static_addrs(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_READ_KEY_HIERARCHY_ROOTS):
vs_read_key_hierarchy_roots(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
case BT_OCF(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL):
vs_write_tx_power_level(cmd, evt);
break;
case BT_OCF(BT_HCI_OP_VS_READ_TX_POWER_LEVEL):
vs_read_tx_power_level(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
#endif /* CONFIG_BT_HCI_VS_EXT */
#if defined(CONFIG_BT_HCI_MESH_EXT)
case BT_OCF(BT_HCI_OP_VS_MESH):
mesh_cmd_handle(cmd, evt);
break;
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) && defined(CONFIG_BT_PERIPHERAL)
case BT_OCF(BT_HCI_OP_VS_SET_MIN_NUM_USED_CHANS):
vs_set_min_used_chans(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_PERIPHERAL */
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
default:
return -EINVAL;
}
return 0;
}
#endif
struct net_buf *hci_cmd_handle(struct net_buf *cmd, void **node_rx)
{
struct bt_hci_cmd_hdr *chdr;
struct net_buf *evt = NULL;
uint16_t ocf;
int err;
if (cmd->len < sizeof(*chdr)) {
BT_ERR("No HCI Command header");
return NULL;
}
chdr = net_buf_pull_mem(cmd, sizeof(*chdr));
if (cmd->len < chdr->param_len) {
BT_ERR("Invalid HCI CMD packet length");
return NULL;
}
/* store in a global for later CC/CS event creation */
_opcode = sys_le16_to_cpu(chdr->opcode);
ocf = BT_OCF(_opcode);
switch (BT_OGF(_opcode)) {
case BT_OGF_LINK_CTRL:
err = link_control_cmd_handle(ocf, cmd, &evt);
break;
case BT_OGF_BASEBAND:
err = ctrl_bb_cmd_handle(ocf, cmd, &evt);
break;
case BT_OGF_INFO:
err = info_cmd_handle(ocf, cmd, &evt);
break;
case BT_OGF_STATUS:
err = status_cmd_handle(ocf, cmd, &evt);
break;
case BT_OGF_LE:
err = controller_cmd_handle(ocf, cmd, &evt, node_rx);
break;
#if defined(CONFIG_BT_HCI_VS)
case BT_OGF_VS:
err = hci_vendor_cmd_handle(ocf, cmd, &evt);
break;
#endif
default:
err = -EINVAL;
break;
}
if (err == -EINVAL) {
evt = cmd_status(BT_HCI_ERR_UNKNOWN_CMD);
}
return evt;
}
#if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) || \
defined(CONFIG_BT_CTLR_CONN_ISO)
static void data_buf_overflow(struct net_buf **buf, uint8_t link_type)
{
struct bt_hci_evt_data_buf_overflow *ep;
if (!(event_mask & BT_EVT_MASK_DATA_BUFFER_OVERFLOW)) {
return;
}
*buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
hci_evt_create(*buf, BT_HCI_EVT_DATA_BUF_OVERFLOW, sizeof(*ep));
ep = net_buf_add(*buf, sizeof(*ep));
ep->link_type = link_type;
}
#endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_SYNC_ISO ||
* CONFIG_BT_CTLR_CONN_ISO
*/
#if defined(CONFIG_BT_CONN)
int hci_acl_handle(struct net_buf *buf, struct net_buf **evt)
{
struct node_tx *node_tx;
struct bt_hci_acl_hdr *acl;
struct pdu_data *pdu_data;
uint16_t handle;
uint8_t flags;
uint16_t len;
*evt = NULL;
if (buf->len < sizeof(*acl)) {
BT_ERR("No HCI ACL header");
return -EINVAL;
}
acl = net_buf_pull_mem(buf, sizeof(*acl));
len = sys_le16_to_cpu(acl->len);
handle = sys_le16_to_cpu(acl->handle);
if (buf->len < len) {
BT_ERR("Invalid HCI ACL packet length");
return -EINVAL;
}
if (len > LL_LENGTH_OCTETS_TX_MAX) {
BT_ERR("Invalid HCI ACL Data length");
return -EINVAL;
}
/* assigning flags first because handle will be overwritten */
flags = bt_acl_flags(handle);
handle = bt_acl_handle(handle);
node_tx = ll_tx_mem_acquire();
if (!node_tx) {
BT_ERR("Tx Buffer Overflow");
data_buf_overflow(evt, BT_OVERFLOW_LINK_ACL);
return -ENOBUFS;
}
pdu_data = (void *)node_tx->pdu;
if (bt_acl_flags_bc(flags) != BT_ACL_POINT_TO_POINT) {
return -EINVAL;
}
switch (bt_acl_flags_pb(flags)) {
case BT_ACL_START_NO_FLUSH:
pdu_data->ll_id = PDU_DATA_LLID_DATA_START;
break;
case BT_ACL_CONT:
pdu_data->ll_id = PDU_DATA_LLID_DATA_CONTINUE;
break;
default:
/* BT_ACL_START and BT_ACL_COMPLETE not allowed on LE-U
* from Host to Controller
*/
return -EINVAL;
}
pdu_data->len = len;
memcpy(&pdu_data->lldata[0], buf->data, len);
if (ll_tx_mem_enqueue(handle, node_tx)) {
BT_ERR("Invalid Tx Enqueue");
ll_tx_mem_release(node_tx);
return -EINVAL;
}
return 0;
}
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_ADV_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
int hci_iso_handle(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_iso_data_hdr *iso_data_hdr;
struct isoal_sdu_tx sdu_frag_tx;
struct bt_hci_iso_hdr *iso_hdr;
struct ll_iso_datapath *dp_in;
struct ll_iso_stream_hdr *hdr;
uint32_t *time_stamp;
uint16_t handle;
uint8_t pb_flag;
uint8_t ts_flag;
uint8_t flags;
uint16_t len;
iso_data_hdr = NULL;
*evt = NULL;
hdr = NULL;
dp_in = NULL;
if (buf->len < sizeof(*iso_hdr)) {
BT_ERR("No HCI ISO header");
return -EINVAL;
}
iso_hdr = net_buf_pull_mem(buf, sizeof(*iso_hdr));
handle = sys_le16_to_cpu(iso_hdr->handle);
len = sys_le16_to_cpu(iso_hdr->len);
if (buf->len < len) {
BT_ERR("Invalid HCI ISO packet length");
return -EINVAL;
}
/* Assigning flags first because handle will be overwritten */
flags = bt_iso_flags(handle);
pb_flag = bt_iso_flags_pb(flags);
ts_flag = bt_iso_flags_ts(flags);
handle = bt_iso_handle(handle);
/* Extract time stamp */
/* Set default to current time
* BT Core V5.3 : Vol 6 Low Energy Controller : Part G IS0-AL:
* 3.1 Time_Offset in framed PDUs :
* The Controller transmitting a SDU may use any of the following
* methods to determine the value of the SDU reference time:
* -- A captured time stamp of the SDU
* -- A time stamp provided by the higher layer
* -- A computed time stamp based on a sequence counter provided by the
* higher layer (Not implemented)
* -- Any other method of determining Time_Offset (Not implemented)
*/
if (ts_flag) {
/* Overwrite time stamp with HCI provided time stamp */
time_stamp = net_buf_pull_mem(buf, sizeof(*time_stamp));
len -= sizeof(*time_stamp);
sdu_frag_tx.time_stamp = *time_stamp;
} else {
sdu_frag_tx.time_stamp =
HAL_TICKER_TICKS_TO_US(ticker_ticks_now_get());
}
/* Extract ISO data header if included (PB_Flag 0b00 or 0b10) */
if ((pb_flag & 0x01) == 0) {
iso_data_hdr = net_buf_pull_mem(buf, sizeof(*iso_data_hdr));
len -= sizeof(*iso_data_hdr);
sdu_frag_tx.packet_sn = iso_data_hdr->sn;
sdu_frag_tx.iso_sdu_length = iso_data_hdr->slen;
} else {
sdu_frag_tx.packet_sn = 0;
sdu_frag_tx.iso_sdu_length = 0;
}
/* Packet boudary flags should be bitwise identical to the SDU state
* 0b00 BT_ISO_START
* 0b01 BT_ISO_CONT
* 0b10 BT_ISO_SINGLE
* 0b11 BT_ISO_END
*/
sdu_frag_tx.sdu_state = pb_flag;
/* Fill in SDU buffer fields */
sdu_frag_tx.dbuf = buf->data;
sdu_frag_tx.size = len;
#if defined(CONFIG_BT_CTLR_CONN_ISO)
/* Extract source handle from CIS or BIS handle by way of header and
* data path
*/
if (IS_CIS_HANDLE(handle)) {
struct ll_conn_iso_stream *cis =
ll_iso_stream_connected_get(handle);
if (!cis) {
return -EINVAL;
}
struct ll_conn_iso_group *cig = cis->group;
hdr = &(cis->hdr);
/* Set target event as the current event. This might cause some
* misalignment between SDU interval and ISO interval in the
* case of a burst from the application or late release. However
* according to the specifications:
* BT Core V5.3 : Vol 6 Low Energy Controller : Part B LL Spec:
* 4.5.13.3 Connected Isochronous Data:
* This burst is associated with the corresponding CIS event but
* the payloads may be transmitted in later events as well.
* If flush timeout is greater than one, use the current event,
* otherwise postpone to the next.
*
* TODO: Calculate the best possible target event based on CIS
* reference, FT and event_count.
*/
sdu_frag_tx.target_event = cis->lll.event_count +
(cis->lll.tx.flush_timeout > 1 ? 0 : 1);
sdu_frag_tx.cig_ref_point = cig->cig_ref_point;
/* Get controller's input data path for CIS */
dp_in = hdr->datapath_in;
if (!dp_in || dp_in->path_id != BT_HCI_DATAPATH_ID_HCI) {
BT_ERR("Input data path not set for HCI");
return -EINVAL;
}
/* Get input data path's source handle */
isoal_source_handle_t source = dp_in->source_hdl;
/* Start Fragmentation */
if (isoal_tx_sdu_fragment(source, &sdu_frag_tx)) {
return -EINVAL;
}
/* TODO: Assign *evt if an immediate response is required */
return 0;
}
#endif /* CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_ADV_ISO)
if (IS_ADV_ISO_HANDLE(handle)) {
/* FIXME: Use ISOAL */
struct node_tx_iso *tx;
uint16_t stream_handle;
uint16_t slen;
/* FIXME: Code only expects header present */
slen = iso_data_hdr ? iso_data_hdr->slen : 0;
/* Check invalid BIS PDU length */
if (slen > LL_BIS_OCTETS_TX_MAX) {
BT_ERR("Invalid HCI ISO Data length");
return -EINVAL;
}
/* Get BIS stream handle and stream context */
handle = bt_iso_handle(handle);
if (handle < BT_CTLR_ADV_ISO_STREAM_HANDLE_BASE) {
return -EINVAL;
}
stream_handle = handle - BT_CTLR_ADV_ISO_STREAM_HANDLE_BASE;
struct lll_adv_iso_stream *stream;
stream = ull_adv_iso_stream_get(stream_handle);
if (!stream) {
BT_ERR("Invalid BIS stream");
return -EINVAL;
}
struct ll_adv_iso_set *adv_iso;
adv_iso = ull_adv_iso_by_stream_get(stream_handle);
if (!adv_iso) {
BT_ERR("No BIG associated with stream handle");
return -EINVAL;
}
/* Get free node tx */
tx = ll_iso_tx_mem_acquire();
if (!tx) {
BT_ERR("ISO Tx Buffer Overflow");
data_buf_overflow(evt, BT_OVERFLOW_LINK_ISO);
return -ENOBUFS;
}
struct pdu_bis *pdu = (void *)tx->pdu;
/* FIXME: Update to use correct LLID for BIS and CIS */
switch (bt_iso_flags_pb(flags)) {
case BT_ISO_SINGLE:
pdu->ll_id = PDU_BIS_LLID_COMPLETE_END;
break;
default:
ll_iso_tx_mem_release(tx);
return -EINVAL;
}
pdu->len = slen;
memcpy(pdu->payload, buf->data, slen);
struct lll_adv_iso *lll_iso;
lll_iso = &adv_iso->lll;
uint64_t pkt_seq_num;
pkt_seq_num = lll_iso->payload_count / lll_iso->bn;
if (((pkt_seq_num - stream->pkt_seq_num) & BIT64_MASK(39)) <=
BIT64_MASK(38)) {
stream->pkt_seq_num = pkt_seq_num;
} else {
pkt_seq_num = stream->pkt_seq_num;
}
tx->payload_count = pkt_seq_num * lll_iso->bn;
stream->pkt_seq_num++;
if (ll_iso_tx_mem_enqueue(stream_handle, tx, NULL)) {
BT_ERR("Invalid ISO Tx Enqueue");
ll_iso_tx_mem_release(tx);
return -EINVAL;
}
return 0;
}
#endif /* CONFIG_BT_CTLR_ADV_ISO */
return -EINVAL;
}
#endif /* CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
#if defined(CONFIG_BT_CTLR_ADV_EXT)
static void dup_ext_adv_adi_store(struct dup_ext_adv_mode *dup_mode,
const struct pdu_adv_adi *adi,
uint8_t data_status)
{
struct dup_ext_adv_set *adv_set;
adv_set = &dup_mode->set[dup_mode->set_curr];
adv_set->data_cmplt = (data_status ==
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE) ?
1U : 0U;
if (adi) {
(void)memcpy(&adv_set->adi, adi, sizeof(*adi));
} else {
(void)memset(&adv_set->adi, 0U, sizeof(*adi));
}
if (dup_mode->set_count < CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX) {
dup_mode->set_count++;
dup_mode->set_curr = dup_mode->set_count;
} else {
dup_mode->set_curr++;
}
if (dup_mode->set_curr == CONFIG_BT_CTLR_DUP_FILTER_ADV_SET_MAX) {
dup_mode->set_curr = 0U;
}
}
static void dup_ext_adv_mode_reset(struct dup_ext_adv_mode *dup_adv_mode)
{
uint8_t adv_mode;
for (adv_mode = 0U; adv_mode < DUP_EXT_ADV_MODE_COUNT;
adv_mode++) {
struct dup_ext_adv_mode *dup_mode;
dup_mode = &dup_adv_mode[adv_mode];
dup_mode->set_count = 0U;
dup_mode->set_curr = 0U;
}
}
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
static void dup_ext_adv_reset(void)
{
for (int32_t i = 0; i < dup_count; i++) {
struct dup_entry *dup;
dup = &dup_filter[i];
dup->mask = 0U;
dup_ext_adv_mode_reset(dup->adv_mode);
}
}
static void dup_periodic_adv_reset(uint8_t addr_type, const uint8_t *addr,
uint8_t sid)
{
for (int32_t addr_idx = 0; addr_idx < dup_count; addr_idx++) {
struct dup_ext_adv_mode *dup_mode;
struct dup_entry *dup;
dup = &dup_filter[addr_idx];
if (memcmp(addr, dup->addr.a.val, sizeof(bt_addr_t)) ||
(addr_type != dup->addr.type)) {
continue;
}
dup_mode = &dup->adv_mode[DUP_EXT_ADV_MODE_PERIODIC];
for (uint16_t set_idx = 0; set_idx < dup_mode->set_count;
set_idx++) {
struct dup_ext_adv_set *adv_set;
adv_set = &dup_mode->set[set_idx];
if (adv_set->adi.sid != sid) {
continue;
}
/* reset data complete state */
adv_set->data_cmplt = 0U;
return;
}
return;
}
}
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
static inline bool is_dup_or_update(struct dup_entry *dup, uint8_t adv_type,
uint8_t adv_mode,
const struct pdu_adv_adi *adi,
uint8_t data_status)
{
if (!(dup->mask & BIT(adv_type))) {
/* report different adv types */
dup->mask |= BIT(adv_type);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
data_status);
return false;
} else if (adv_type != PDU_ADV_TYPE_EXT_IND) {
/* drop duplicate legacy advertising */
return true;
} else if (dup->adv_mode[adv_mode].set_count == 0U) {
/* report different extended adv mode */
dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
data_status);
return false;
} else if (adi) {
struct dup_ext_adv_mode *dup_mode;
uint8_t j;
dup_mode = &dup->adv_mode[adv_mode];
for (j = 0; j < dup_mode->set_count; j++) {
struct dup_ext_adv_set *adv_set;
adv_set = &dup_mode->set[j];
if (adv_set->adi.sid != adi->sid) {
continue;
}
if (adv_set->adi.did != adi->did) {
/* report different DID */
adv_set->adi.did = adi->did;
/* set new data status */
if (data_status == BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE) {
adv_set->data_cmplt = 1U;
} else {
adv_set->data_cmplt = 0U;
}
return false;
} else if (!adv_set->data_cmplt &&
(data_status ==
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE)) {
/* report data complete */
adv_set->data_cmplt = 1U;
return false;
} else if (!adv_set->data_cmplt) {
/* report partial and incomplete data */
return false;
}
return true;
}
dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
data_status);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
return false;
}
return true;
}
static bool dup_found(uint8_t adv_type, uint8_t addr_type, const uint8_t *addr,
uint8_t adv_mode, const struct pdu_adv_adi *adi,
uint8_t data_status)
{
/* check for duplicate filtering */
if (dup_count >= 0) {
struct dup_entry *dup;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
__ASSERT((adv_mode < ARRAY_SIZE(dup_filter[0].adv_mode)),
"adv_mode index out-of-bound");
#endif /* CONFIG_BT_CTLR_ADV_EXT */
/* find for existing entry and update if changed */
for (int32_t i = 0; i < dup_count; i++) {
dup = &dup_filter[i];
if (memcmp(addr, &dup->addr.a.val[0],
sizeof(bt_addr_t)) ||
(addr_type != dup->addr.type)) {
continue;
}
/* still duplicate or update entry with change */
return is_dup_or_update(dup, adv_type, adv_mode, adi,
data_status);
}
/* insert into the duplicate filter */
dup = &dup_filter[dup_curr];
(void)memcpy(&dup->addr.a.val[0], addr, sizeof(bt_addr_t));
dup->addr.type = addr_type;
dup->mask = BIT(adv_type);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
dup_ext_adv_mode_reset(dup->adv_mode);
dup_ext_adv_adi_store(&dup->adv_mode[adv_mode], adi,
data_status);
#endif /* CONFIG_BT_CTLR_ADV_EXT */
if (dup_count < CONFIG_BT_CTLR_DUP_FILTER_LEN) {
dup_count++;
dup_curr = dup_count;
} else {
dup_curr++;
}
if (dup_curr == CONFIG_BT_CTLR_DUP_FILTER_LEN) {
dup_curr = 0U;
}
}
return false;
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
static inline void le_dir_adv_report(struct pdu_adv *adv, struct net_buf *buf,
int8_t rssi, uint8_t rl_idx)
{
struct bt_hci_evt_le_direct_adv_report *drp;
struct bt_hci_evt_le_direct_adv_info *dir_info;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_DIRECT_ADV_REPORT)) {
return;
}
LL_ASSERT(adv->type == PDU_ADV_TYPE_DIRECT_IND);
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
if (dup_scan &&
dup_found(adv->type, adv->tx_addr, adv->adv_ind.addr, 0, NULL, 0)) {
return;
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
drp = meta_evt(buf, BT_HCI_EVT_LE_DIRECT_ADV_REPORT,
sizeof(*drp) + sizeof(*dir_info));
drp->num_reports = 1U;
dir_info = (void *)(((uint8_t *)drp) + sizeof(*drp));
/* Directed Advertising */
dir_info->evt_type = BT_HCI_ADV_DIRECT_IND;
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (rl_idx < ll_rl_size_get()) {
/* Store identity address */
ll_rl_id_addr_get(rl_idx, &dir_info->addr.type,
&dir_info->addr.a.val[0]);
/* Mark it as identity address from RPA (0x02, 0x03) */
dir_info->addr.type += 2U;
} else {
#else
if (1) {
#endif /* CONFIG_BT_CTLR_PRIVACY */
dir_info->addr.type = adv->tx_addr;
memcpy(&dir_info->addr.a.val[0], &adv->direct_ind.adv_addr[0],
sizeof(bt_addr_t));
}
dir_info->dir_addr.type = adv->rx_addr;
memcpy(&dir_info->dir_addr.a.val[0],
&adv->direct_ind.tgt_addr[0], sizeof(bt_addr_t));
dir_info->rssi = rssi;
}
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_OBSERVER)
#if defined(CONFIG_BT_HCI_MESH_EXT)
static inline bool scan_filter_apply(uint8_t filter, uint8_t *data, uint8_t len)
{
struct scan_filter *f = &scan_filters[filter];
/* No patterns means filter out all advertising packets */
for (uint8_t i = 0; i < f->count; i++) {
/* Require at least the length of the pattern */
if (len >= f->lengths[i] &&
!memcmp(data, f->patterns[i], f->lengths[i])) {
return true;
}
}
return false;
}
static inline void le_mesh_scan_report(struct pdu_adv *adv,
struct node_rx_pdu *node_rx,
struct net_buf *buf, int8_t rssi)
{
uint8_t data_len = (adv->len - BDADDR_SIZE);
struct bt_hci_evt_mesh_scanning_report *mep;
struct bt_hci_evt_mesh_scan_report *sr;
uint32_t instant;
uint8_t chan;
LL_ASSERT(adv->type == PDU_ADV_TYPE_NONCONN_IND);
/* Filter based on currently active Scan Filter */
if (sf_curr < ARRAY_SIZE(scan_filters) &&
!scan_filter_apply(sf_curr, &adv->adv_ind.data[0], data_len)) {
/* Drop the report */
return;
}
chan = node_rx->hdr.rx_ftr.chan;
instant = node_rx->hdr.rx_ftr.anchor_ticks;
mep = mesh_evt(buf, BT_HCI_EVT_MESH_SCANNING_REPORT,
sizeof(*mep) + sizeof(*sr));
mep->num_reports = 1U;
sr = (void *)(((uint8_t *)mep) + sizeof(*mep));
sr->addr.type = adv->tx_addr;
memcpy(&sr->addr.a.val[0], &adv->adv_ind.addr[0], sizeof(bt_addr_t));
sr->chan = chan;
sr->rssi = rssi;
sys_put_le32(instant, (uint8_t *)&sr->instant);
sr->data_len = data_len;
memcpy(&sr->data[0], &adv->adv_ind.data[0], data_len);
}
#endif /* CONFIG_BT_HCI_MESH_EXT */
static void le_advertising_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
const uint8_t c_adv_type[] = { 0x00, 0x01, 0x03, 0xff, 0x04,
0xff, 0x02 };
struct bt_hci_evt_le_advertising_report *sep;
struct pdu_adv *adv = (void *)pdu_data;
struct bt_hci_evt_le_advertising_info *adv_info;
uint8_t data_len;
uint8_t info_len;
int8_t rssi;
#if defined(CONFIG_BT_CTLR_PRIVACY)
uint8_t rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
uint8_t direct_report;
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
int8_t *prssi;
rssi = -(node_rx->hdr.rx_ftr.rssi);
#if defined(CONFIG_BT_CTLR_PRIVACY)
rl_idx = node_rx->hdr.rx_ftr.rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
direct_report = node_rx->hdr.rx_ftr.direct;
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (adv->tx_addr) {
/* Update current RPA */
ll_rl_crpa_set(0x00, NULL, rl_idx, &adv->adv_ind.addr[0]);
}
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
if (direct_report) {
#if defined(CONFIG_BT_CTLR_PRIVACY)
le_dir_adv_report(adv, buf, rssi, rl_idx);
#else
le_dir_adv_report(adv, buf, rssi, 0xFF);
#endif /* CONFIG_BT_CTLR_PRIVACY */
return;
}
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_HCI_MESH_EXT)
if (node_rx->hdr.type == NODE_RX_TYPE_MESH_REPORT) {
le_mesh_scan_report(adv, node_rx, buf, rssi);
return;
}
#endif /* CONFIG_BT_HCI_MESH_EXT */
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_ADVERTISING_REPORT)) {
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
if (dup_scan &&
dup_found(adv->type, adv->tx_addr, adv->adv_ind.addr, 0, NULL, 0)) {
return;
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
if (adv->type != PDU_ADV_TYPE_DIRECT_IND) {
data_len = (adv->len - BDADDR_SIZE);
} else {
data_len = 0U;
}
info_len = sizeof(struct bt_hci_evt_le_advertising_info) + data_len +
sizeof(*prssi);
sep = meta_evt(buf, BT_HCI_EVT_LE_ADVERTISING_REPORT,
sizeof(*sep) + info_len);
sep->num_reports = 1U;
adv_info = (void *)(((uint8_t *)sep) + sizeof(*sep));
adv_info->evt_type = c_adv_type[adv->type];
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (rl_idx < ll_rl_size_get()) {
/* Store identity address */
ll_rl_id_addr_get(rl_idx, &adv_info->addr.type,
&adv_info->addr.a.val[0]);
/* Mark it as identity address from RPA (0x02, 0x03) */
adv_info->addr.type += 2U;
} else {
#else
if (1) {
#endif /* CONFIG_BT_CTLR_PRIVACY */
adv_info->addr.type = adv->tx_addr;
memcpy(&adv_info->addr.a.val[0], &adv->adv_ind.addr[0],
sizeof(bt_addr_t));
}
adv_info->length = data_len;
memcpy(&adv_info->data[0], &adv->adv_ind.data[0], data_len);
/* RSSI */
prssi = &adv_info->data[0] + data_len;
*prssi = rssi;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT)
static void le_ext_adv_legacy_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
/* Lookup event type based on pdu_adv_type set by LLL */
const uint8_t evt_type_lookup[] = {
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY | BT_HCI_LE_ADV_EVT_TYPE_SCAN |
BT_HCI_LE_ADV_EVT_TYPE_CONN), /* ADV_IND */
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY | BT_HCI_LE_ADV_EVT_TYPE_DIRECT |
BT_HCI_LE_ADV_EVT_TYPE_CONN), /* DIRECT_IND */
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY), /* NONCONN_IND */
0xff, /* Invalid index lookup */
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY |
BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP |
BT_HCI_LE_ADV_EVT_TYPE_SCAN), /* SCAN_RSP to an ADV_SCAN_IND
*/
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY |
BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP |
BT_HCI_LE_ADV_EVT_TYPE_SCAN |
BT_HCI_LE_ADV_EVT_TYPE_CONN), /* SCAN_RSP to an ADV_IND,
* NOTE: LLL explicitly sets
* adv_type to
* PDU_ADV_TYPE_ADV_IND_SCAN_RSP
*/
(BT_HCI_LE_ADV_EVT_TYPE_LEGACY |
BT_HCI_LE_ADV_EVT_TYPE_SCAN) /* SCAN_IND */
};
struct bt_hci_evt_le_ext_advertising_info *adv_info;
struct bt_hci_evt_le_ext_advertising_report *sep;
struct pdu_adv *adv = (void *)pdu_data;
uint8_t data_len;
uint8_t info_len;
int8_t rssi;
#if defined(CONFIG_BT_CTLR_PRIVACY)
uint8_t rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT)) {
return;
}
/* The Link Layer currently returns RSSI as an absolute value */
rssi = -(node_rx->hdr.rx_ftr.rssi);
#if defined(CONFIG_BT_CTLR_PRIVACY)
rl_idx = node_rx->hdr.rx_ftr.rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (adv->tx_addr) {
/* Update current RPA */
ll_rl_crpa_set(0x00, NULL, rl_idx, &adv->adv_ind.addr[0]);
}
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
if (dup_scan &&
dup_found(adv->type, adv->tx_addr, adv->adv_ind.addr, 0, NULL, 0)) {
return;
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
if (adv->type != PDU_ADV_TYPE_DIRECT_IND) {
data_len = (adv->len - BDADDR_SIZE);
} else {
data_len = 0U;
}
info_len = sizeof(struct bt_hci_evt_le_ext_advertising_info) +
data_len;
sep = meta_evt(buf, BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT,
sizeof(*sep) + info_len);
sep->num_reports = 1U;
adv_info = (void *)(((uint8_t *)sep) + sizeof(*sep));
adv_info->evt_type = evt_type_lookup[adv->type];
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (rl_idx < ll_rl_size_get()) {
/* Store identity address */
ll_rl_id_addr_get(rl_idx, &adv_info->addr.type,
&adv_info->addr.a.val[0]);
/* Mark it as identity address from RPA (0x02, 0x03) */
adv_info->addr.type += 2U;
} else
#endif /* CONFIG_BT_CTLR_PRIVACY */
{
adv_info->addr.type = adv->tx_addr;
memcpy(&adv_info->addr.a.val[0], &adv->adv_ind.addr[0],
sizeof(bt_addr_t));
}
adv_info->prim_phy = BT_HCI_LE_EXT_SCAN_PHY_1M;
adv_info->sec_phy = 0U;
adv_info->sid = 0xff;
adv_info->tx_power = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
adv_info->rssi = rssi;
adv_info->interval = 0U;
if (adv->type == PDU_ADV_TYPE_DIRECT_IND) {
adv_info->direct_addr.type = adv->rx_addr;
bt_addr_copy(&adv_info->direct_addr.a,
(void *)adv->direct_ind.tgt_addr);
} else {
adv_info->direct_addr.type = 0U;
(void)memset(adv_info->direct_addr.a.val, 0U,
sizeof(adv_info->direct_addr.a.val));
}
adv_info->length = data_len;
memcpy(&adv_info->data[0], &adv->adv_ind.data[0], data_len);
}
static uint8_t ext_adv_direct_addr_type(struct lll_scan *lll,
bool peer_resolved, bool direct_report,
uint8_t rx_addr_type,
const uint8_t *const rx_addr)
{
/* The directed address is resolvable private address, but Controller
* could not resolve it.
*/
if (direct_report) {
return BT_ADDR_LE_UNRESOLVED;
}
if (0) {
#if defined(CONFIG_BT_CTLR_PRIVACY)
/* Peer directed advertiser's address was resolved */
} else if (peer_resolved) {
struct ll_scan_set *scan;
scan = HDR_LLL2ULL(lll);
if ((rx_addr_type == lll->init_addr_type) &&
!memcmp(lll->init_addr, rx_addr, BDADDR_SIZE)) {
/* Peer directed advertiser used local scanner's
* initiator address.
*/
return scan->own_addr_type;
}
/* Peer directed advertiser used directed resolvable
* private address generated from the local scanner's
* Identity Resolution Key.
*/
return scan->own_addr_type | BIT(1);
#endif /* CONFIG_BT_CTLR_PRIVACY */
} else {
struct ll_scan_set *scan;
scan = HDR_LLL2ULL(lll);
/* Peer directed advertiser used local scanner's
* initiator address.
*/
return scan->own_addr_type;
}
}
static uint8_t ext_adv_data_get(const struct node_rx_pdu *node_rx_data,
uint8_t *const sec_phy, int8_t *const tx_pwr,
const uint8_t **const data)
{
const struct pdu_adv *adv = (void *)node_rx_data->pdu;
const struct pdu_adv_com_ext_adv *p;
const struct pdu_adv_ext_hdr *h;
uint8_t hdr_buf_len;
const uint8_t *ptr;
uint8_t hdr_len;
*tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
p = (void *)&adv->adv_ext_ind;
h = (void *)p->ext_hdr_adv_data;
ptr = (void *)h;
if (!p->ext_hdr_len) {
hdr_len = PDU_AC_EXT_HEADER_SIZE_MIN;
goto no_ext_hdr;
}
ptr = h->data;
if (h->adv_addr) {
ptr += BDADDR_SIZE;
}
if (h->tgt_addr) {
ptr += BDADDR_SIZE;
}
if (h->adi) {
ptr += sizeof(struct pdu_adv_adi);
}
if (h->aux_ptr) {
struct pdu_adv_aux_ptr *aux_ptr;
aux_ptr = (void *)ptr;
ptr += sizeof(*aux_ptr);
*sec_phy = HCI_AUX_PHY_TO_HCI_PHY(aux_ptr->phy);
}
if (h->sync_info) {
ptr += sizeof(struct pdu_adv_sync_info);
}
if (h->tx_pwr) {
*tx_pwr = *(int8_t *)ptr;
ptr++;
}
hdr_len = ptr - (uint8_t *)p;
hdr_buf_len = PDU_AC_EXT_HEADER_SIZE_MIN + p->ext_hdr_len;
if (hdr_len < hdr_buf_len) {
uint8_t acad_len = hdr_buf_len - hdr_len;
ptr += acad_len;
hdr_len += acad_len;
}
no_ext_hdr:
if (hdr_len < adv->len) {
*data = ptr;
return adv->len - hdr_len;
}
return 0;
}
static void node_rx_extra_list_release(struct node_rx_pdu *node_rx_extra)
{
while (node_rx_extra) {
struct node_rx_pdu *node_rx_curr;
node_rx_curr = node_rx_extra;
node_rx_extra = node_rx_curr->hdr.rx_ftr.extra;
node_rx_curr->hdr.next = NULL;
ll_rx_mem_release((void **)&node_rx_curr);
}
}
static void ext_adv_info_fill(uint8_t evt_type, uint8_t phy, uint8_t sec_phy,
uint8_t adv_addr_type, const uint8_t *adv_addr,
uint8_t direct_addr_type,
const uint8_t *direct_addr, uint8_t rl_idx,
int8_t tx_pwr, int8_t rssi,
uint16_t interval_le16,
const struct pdu_adv_adi *adi, uint8_t data_len,
const uint8_t *data, struct net_buf *buf)
{
struct bt_hci_evt_le_ext_advertising_info *adv_info;
struct bt_hci_evt_le_ext_advertising_report *sep;
uint8_t info_len;
info_len = sizeof(struct bt_hci_evt_le_ext_advertising_info) +
data_len;
sep = meta_evt(buf, BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT,
sizeof(*sep) + info_len);
sep->num_reports = 1U;
adv_info = (void *)(((uint8_t *)sep) + sizeof(*sep));
adv_info->evt_type = evt_type;
if (0) {
#if defined(CONFIG_BT_CTLR_PRIVACY)
} else if (rl_idx < ll_rl_size_get()) {
/* Store identity address */
ll_rl_id_addr_get(rl_idx, &adv_info->addr.type,
adv_info->addr.a.val);
/* Mark it as identity address from RPA (0x02, 0x03) */
adv_info->addr.type += 2U;
#else /* !CONFIG_BT_CTLR_PRIVACY */
ARG_UNUSED(rl_idx);
#endif /* !CONFIG_BT_CTLR_PRIVACY */
} else if (adv_addr) {
adv_info->addr.type = adv_addr_type;
(void)memcpy(adv_info->addr.a.val, adv_addr, sizeof(bt_addr_t));
} else {
adv_info->addr.type = 0U;
(void)memset(adv_info->addr.a.val, 0, sizeof(bt_addr_t));
}
adv_info->prim_phy = find_lsb_set(phy);
adv_info->sec_phy = sec_phy;
adv_info->sid = (adi) ? adi->sid : BT_HCI_LE_EXT_ADV_SID_INVALID;
adv_info->tx_power = tx_pwr;
adv_info->rssi = rssi;
adv_info->interval = interval_le16;
if (evt_type & BT_HCI_LE_ADV_EVT_TYPE_DIRECT) {
adv_info->direct_addr.type = direct_addr_type;
(void)memcpy(adv_info->direct_addr.a.val, direct_addr,
sizeof(bt_addr_t));
} else {
adv_info->direct_addr.type = 0U;
(void)memset(adv_info->direct_addr.a.val, 0, sizeof(bt_addr_t));
}
adv_info->length = data_len;
(void)memcpy(adv_info->data, data, data_len);
}
static void ext_adv_pdu_frag(uint8_t evt_type, uint8_t phy, uint8_t sec_phy,
uint8_t adv_addr_type, const uint8_t *adv_addr,
uint8_t direct_addr_type,
const uint8_t *direct_addr, uint8_t rl_idx,
int8_t tx_pwr, int8_t rssi, uint16_t interval_le16,
const struct pdu_adv_adi *adi,
uint8_t data_len_max,
uint16_t *const data_len_total,
uint8_t *const data_len,
const uint8_t **const data, struct net_buf *buf,
struct net_buf **const evt_buf)
{
const uint8_t data_len_frag = MIN(*data_len, data_len_max);
do {
/* Prepare a fragment of PDU data in a HCI event */
ext_adv_info_fill(evt_type, phy, sec_phy, adv_addr_type,
adv_addr, direct_addr_type, direct_addr,
rl_idx, tx_pwr, rssi, interval_le16, adi,
data_len_frag, *data, *evt_buf);
*data += data_len_frag;
*data_len -= data_len_frag;
*data_len_total -= data_len_frag;
*evt_buf = bt_buf_get_rx(BT_BUF_EVT, BUF_GET_TIMEOUT);
LL_ASSERT(*evt_buf);
net_buf_frag_add(buf, *evt_buf);
/* Continue to fragment until last partial PDU data fragment,
* remainder PDU data's HCI event will be prepare by caller.
*/
} while (*data_len > data_len_max);
}
static void ext_adv_data_frag(const struct node_rx_pdu *node_rx_data,
uint8_t evt_type, uint8_t phy,
uint8_t *const sec_phy, uint8_t adv_addr_type,
const uint8_t *adv_addr, uint8_t direct_addr_type,
const uint8_t *direct_addr, uint8_t rl_idx,
int8_t *const tx_pwr, int8_t rssi,
uint16_t interval_le16,
const struct pdu_adv_adi *adi,
uint8_t data_len_max, uint16_t data_len_total,
uint8_t *const data_len,
const uint8_t **const data, struct net_buf *buf,
struct net_buf **const evt_buf)
{
evt_type |= (BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL << 5);
do {
/* Fragment the PDU data */
ext_adv_pdu_frag(evt_type, phy, *sec_phy, adv_addr_type,
adv_addr, direct_addr_type, direct_addr,
rl_idx, *tx_pwr, rssi, interval_le16, adi,
data_len_max, &data_len_total, data_len,
data, buf, evt_buf);
/* Check if more PDUs in the list */
node_rx_data = node_rx_data->hdr.rx_ftr.extra;
if (node_rx_data) {
if (*data_len >= data_len_total) {
/* Last fragment restricted to maximum scan
* data length, caller will prepare the last
* HCI fragment event.
*/
break;
} else if (*data_len) {
/* Last fragment of current PDU data */
ext_adv_pdu_frag(evt_type, phy, *sec_phy,
adv_addr_type, adv_addr,
direct_addr_type, direct_addr,
rl_idx, *tx_pwr, rssi,
interval_le16, adi,
data_len_max, &data_len_total,
data_len, data, buf, evt_buf);
}
/* Get next PDU data in list */
*data_len = ext_adv_data_get(node_rx_data, sec_phy,
tx_pwr, data);
/* Restrict PDU data to maximum scan data length */
if (*data_len > data_len_total) {
*data_len = data_len_total;
}
}
/* Continue to fragment if current PDU data length less than
* total data length or current PDU data length greater than
* HCI event max length.
*/
} while ((*data_len < data_len_total) || (*data_len > data_len_max));
}
static void le_ext_adv_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf, uint8_t phy)
{
int8_t scan_rsp_tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
int8_t tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
struct node_rx_pdu *node_rx_scan_data = NULL;
struct node_rx_pdu *node_rx_data = NULL;
const struct pdu_adv_adi *adi = NULL;
uint16_t scan_data_len_total = 0U;
struct node_rx_pdu *node_rx_curr;
struct node_rx_pdu *node_rx_next;
const uint8_t *scan_data = NULL;
uint8_t scan_data_status = 0U;
uint8_t direct_addr_type = 0U;
uint16_t data_len_total = 0U;
uint8_t *direct_addr = NULL;
uint16_t interval_le16 = 0U;
const uint8_t *data = NULL;
uint8_t scan_data_len = 0U;
uint8_t adv_addr_type = 0U;
uint8_t sec_phy_scan = 0U;
uint8_t *adv_addr = NULL;
uint8_t data_status = 0U;
struct net_buf *evt_buf;
bool devmatch = false;
uint8_t data_len = 0U;
uint8_t evt_type = 0U;
uint8_t sec_phy = 0U;
uint8_t data_len_max;
uint8_t rl_idx = 0U;
struct pdu_adv *adv;
int8_t rssi;
/* NOTE: This function uses a lot of initializers before the check and
* return below, as an exception to initializing close to their locality
* of reference. This is acceptable as the return is unlikely in typical
* Controller use.
*/
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT)) {
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
return;
}
#if defined(CONFIG_BT_CTLR_PRIVACY)
rl_idx = ll_rl_size_get();
#endif /* CONFIG_BT_CTLR_PRIVACY */
adv = (void *)pdu_data;
node_rx_curr = node_rx;
node_rx_next = node_rx_curr->hdr.rx_ftr.extra;
do {
int8_t tx_pwr_curr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
struct pdu_adv_adi *adi_curr = NULL;
uint8_t direct_addr_type_curr = 0U;
bool direct_resolved_curr = false;
uint8_t *direct_addr_curr = NULL;
uint8_t adv_addr_type_curr = 0U;
struct pdu_adv_com_ext_adv *p;
uint8_t *adv_addr_curr = NULL;
uint8_t data_len_curr = 0U;
uint8_t *data_curr = NULL;
struct pdu_adv_ext_hdr *h;
uint8_t sec_phy_curr = 0U;
uint8_t evt_type_curr;
uint8_t hdr_buf_len;
uint8_t hdr_len;
uint8_t *ptr;
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
bool direct_report_curr = node_rx_curr->hdr.rx_ftr.direct;
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_CTLR_PRIVACY)
uint8_t rl_idx_curr = node_rx_curr->hdr.rx_ftr.rl_idx;
direct_resolved_curr = node_rx_curr->hdr.rx_ftr.direct_resolved;
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) && \
defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
const bool devmatch_curr = node_rx_curr->hdr.rx_ftr.devmatch;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC && CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
/* The Link Layer currently returns RSSI as an absolute value */
rssi = -(node_rx_curr->hdr.rx_ftr.rssi);
BT_DBG("phy= 0x%x, type= 0x%x, len= %u, tat= %u, rat= %u,"
" rssi=%d dB", phy, adv->type, adv->len, adv->tx_addr,
adv->rx_addr, rssi);
p = (void *)&adv->adv_ext_ind;
h = (void *)p->ext_hdr_adv_data;
ptr = (void *)h;
BT_DBG(" Ext. adv mode= 0x%x, hdr len= %u", p->adv_mode,
p->ext_hdr_len);
evt_type_curr = p->adv_mode;
if (!p->ext_hdr_len) {
hdr_len = PDU_AC_EXT_HEADER_SIZE_MIN;
goto no_ext_hdr;
}
ptr = h->data;
if (h->adv_addr) {
bt_addr_le_t addr;
adv_addr_type_curr = adv->tx_addr;
adv_addr_curr = ptr;
addr.type = adv->tx_addr;
(void)memcpy(addr.a.val, ptr, sizeof(bt_addr_t));
ptr += BDADDR_SIZE;
BT_DBG(" AdvA: %s", bt_addr_le_str(&addr));
}
if (h->tgt_addr) {
struct lll_scan *lll;
bt_addr_le_t addr;
lll = node_rx->hdr.rx_ftr.param;
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
direct_addr_type_curr =
ext_adv_direct_addr_type(lll,
direct_resolved_curr,
direct_report_curr,
adv->rx_addr, ptr);
#else /* !CONFIG_BT_CTLR_EXT_SCAN_FP */
direct_addr_type_curr =
ext_adv_direct_addr_type(lll,
direct_resolved_curr,
false, adv->rx_addr,
ptr);
#endif /* !CONFIG_BT_CTLR_EXT_SCAN_FP */
direct_addr_curr = ptr;
ptr += BDADDR_SIZE;
addr.type = adv->rx_addr;
(void)memcpy(addr.a.val, direct_addr_curr,
sizeof(bt_addr_t));
BT_DBG(" TgtA: %s", bt_addr_le_str(&addr));
}
if (h->adi) {
adi_curr = (void *)ptr;
ptr += sizeof(*adi);
BT_DBG(" AdvDataInfo DID = 0x%x, SID = 0x%x",
adi_curr->did, adi_curr->sid);
}
if (h->aux_ptr) {
struct pdu_adv_aux_ptr *aux_ptr;
uint8_t aux_phy;
aux_ptr = (void *)ptr;
if (aux_ptr->phy > EXT_ADV_AUX_PHY_LE_CODED) {
struct node_rx_ftr *ftr;
ftr = &node_rx->hdr.rx_ftr;
node_rx_extra_list_release(ftr->extra);
return;
}
ptr += sizeof(*aux_ptr);
sec_phy_curr = HCI_AUX_PHY_TO_HCI_PHY(aux_ptr->phy);
aux_phy = BIT(aux_ptr->phy);
BT_DBG(" AuxPtr chan_idx = %u, ca = %u, offs_units "
"= %u offs = 0x%x, phy = 0x%x",
aux_ptr->chan_idx, aux_ptr->ca,
aux_ptr->offs_units, aux_ptr->offs, aux_phy);
}
if (h->sync_info) {
struct pdu_adv_sync_info *si;
si = (void *)ptr;
ptr += sizeof(*si);
interval_le16 = si->interval;
BT_DBG(" SyncInfo offs = %u, offs_unit = 0x%x, "
"interval = 0x%x, sca = 0x%x, "
"chan map = 0x%x 0x%x 0x%x 0x%x 0x%x, "
"AA = 0x%x, CRC = 0x%x 0x%x 0x%x, "
"evt cntr = 0x%x",
sys_le16_to_cpu(si->offs),
si->offs_units,
sys_le16_to_cpu(si->interval),
((si->sca_chm[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] &
PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK) >>
PDU_SYNC_INFO_SCA_CHM_SCA_BIT_POS),
si->sca_chm[0], si->sca_chm[1], si->sca_chm[2],
si->sca_chm[3],
(si->sca_chm[PDU_SYNC_INFO_SCA_CHM_SCA_BYTE_OFFSET] &
~PDU_SYNC_INFO_SCA_CHM_SCA_BIT_MASK),
sys_le32_to_cpu(si->aa),
si->crc_init[0], si->crc_init[1],
si->crc_init[2], sys_le16_to_cpu(si->evt_cntr));
}
if (h->tx_pwr) {
tx_pwr_curr = *(int8_t *)ptr;
ptr++;
BT_DBG(" Tx pwr= %d dB", tx_pwr_curr);
}
hdr_len = ptr - (uint8_t *)p;
hdr_buf_len = PDU_AC_EXT_HEADER_SIZE_MIN + p->ext_hdr_len;
if (hdr_len > hdr_buf_len) {
BT_WARN(" Header length %u/%u, INVALID.", hdr_len,
p->ext_hdr_len);
} else {
uint8_t acad_len = hdr_buf_len - hdr_len;
if (acad_len) {
ptr += acad_len;
hdr_len += acad_len;
}
}
no_ext_hdr:
if (hdr_len < adv->len) {
data_len_curr = adv->len - hdr_len;
data_curr = ptr;
BT_DBG(" AD Data (%u): <todo>", data_len);
}
if (node_rx_curr == node_rx) {
evt_type = evt_type_curr;
adv_addr_type = adv_addr_type_curr;
adv_addr = adv_addr_curr;
direct_addr_type = direct_addr_type_curr;
direct_addr = direct_addr_curr;
adi = adi_curr;
sec_phy = sec_phy_curr;
node_rx_data = node_rx_curr;
data_len = data_len_curr;
data_len_total = data_len;
data = data_curr;
scan_data_len_total = 0U;
tx_pwr = tx_pwr_curr;
#if defined(CONFIG_BT_CTLR_PRIVACY)
rl_idx = rl_idx_curr;
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) && \
defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
devmatch = devmatch_curr;
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC && CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
} else {
/* TODO: Validate current value with previous */
/* Detect the scan response in the list of node_rx */
if (node_rx_curr->hdr.rx_ftr.scan_rsp) {
node_rx_scan_data = node_rx_curr;
if (sec_phy_curr) {
sec_phy_scan = sec_phy_curr;
} else {
sec_phy_scan = sec_phy;
}
scan_data_len = data_len_curr;
scan_data = data_curr;
scan_rsp_tx_pwr = tx_pwr_curr;
}
if (!adv_addr) {
adv_addr_type = adv_addr_type_curr;
adv_addr = adv_addr_curr;
}
if (!direct_addr) {
direct_addr_type = direct_addr_type_curr;
direct_addr = direct_addr_curr;
}
if (scan_data) {
scan_data_len_total += data_len_curr;
} else if (!data) {
node_rx_data = node_rx_curr;
data_len = data_len_curr;
data_len_total = data_len;
data = data_curr;
tx_pwr = tx_pwr_curr;
} else {
data_len_total += data_len_curr;
}
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (rl_idx >= ll_rl_size_get()) {
rl_idx = rl_idx_curr;
}
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC) && \
defined(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST)
if (!devmatch) {
devmatch = devmatch_curr;
}
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC && CONFIG_BT_CTLR_FILTER_ACCEPT_LIST */
}
if (!node_rx_next) {
bool has_aux_ptr = !!sec_phy_curr;
if (scan_data) {
if (has_aux_ptr) {
scan_data_status =
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
} else if (has_aux_ptr) {
data_status =
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
break;
}
node_rx_curr = node_rx_next;
node_rx_next = node_rx_curr->hdr.rx_ftr.extra;
adv = (void *)node_rx_curr->pdu;
} while (1);
if (IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC) &&
IS_ENABLED(CONFIG_BT_CTLR_FILTER_ACCEPT_LIST) &&
!devmatch) {
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
if (adv_addr) {
if (dup_scan &&
dup_found(PDU_ADV_TYPE_EXT_IND, adv_addr_type, adv_addr,
(evt_type & BIT_MASK(2)), adi, data_status)) {
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
return;
}
}
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 */
/* If data incomplete */
if (data_status) {
/* Data incomplete and no more to come */
if (!(adv_addr ||
(adi && ((tx_pwr != BT_HCI_LE_ADV_TX_POWER_NO_PREF) ||
data)))) {
/* No device address and no valid AD data parsed or
* Tx Power present for this PDU chain that has ADI,
* skip HCI event generation.
* In other terms, generate HCI event if device address
* is present or if Tx pwr and/or data is present from
* anonymous device.
*/
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
return;
}
}
/* Restrict data length to maximum scan data length */
if (data_len_total > CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX) {
data_len_total = CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX;
if (data_len > data_len_total) {
data_len = data_len_total;
}
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
/* Set directed advertising bit */
if (direct_addr) {
evt_type |= BT_HCI_LE_ADV_EVT_TYPE_DIRECT;
}
/* HCI fragment */
evt_buf = buf;
data_len_max = CONFIG_BT_BUF_EVT_RX_SIZE -
sizeof(struct bt_hci_evt_le_meta_event) -
sizeof(struct bt_hci_evt_le_ext_advertising_report) -
sizeof(struct bt_hci_evt_le_ext_advertising_info);
/* If PDU data length less than total data length or PDU data length
* greater than maximum HCI event data length, then fragment.
*/
if ((data_len < data_len_total) || (data_len > data_len_max)) {
ext_adv_data_frag(node_rx_data, evt_type, phy, &sec_phy,
adv_addr_type, adv_addr, direct_addr_type,
direct_addr, rl_idx, &tx_pwr, rssi,
interval_le16, adi, data_len_max,
data_len_total, &data_len, &data, buf,
&evt_buf);
}
/* Set data status bits */
evt_type |= (data_status << 5);
/* Start constructing the adv event for remainder of the PDU data */
ext_adv_info_fill(evt_type, phy, sec_phy, adv_addr_type, adv_addr,
direct_addr_type, direct_addr, rl_idx, tx_pwr, rssi,
interval_le16, adi, data_len, data, evt_buf);
/* If scan response event to be constructed */
if (!scan_data) {
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
return;
}
/* Restrict scan response data length to maximum scan data length */
if (scan_data_len_total > CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX) {
scan_data_len_total = CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX;
if (scan_data_len > scan_data_len_total) {
scan_data_len = scan_data_len_total;
}
scan_data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
/* Set scan response bit */
evt_type |= BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP;
/* Clear the data status bits */
evt_type &= ~(BIT_MASK(2) << 5);
/* Allocate, append as buf fragement and construct the scan response
* event.
*/
evt_buf = bt_buf_get_rx(BT_BUF_EVT, BUF_GET_TIMEOUT);
LL_ASSERT(evt_buf);
net_buf_frag_add(buf, evt_buf);
/* If PDU data length less than total data length or PDU data length
* greater than maximum HCI event data length, then fragment.
*/
if ((scan_data_len < scan_data_len_total) ||
(scan_data_len > data_len_max)) {
ext_adv_data_frag(node_rx_scan_data, evt_type, phy,
&sec_phy_scan, adv_addr_type, adv_addr,
direct_addr_type, direct_addr, rl_idx,
&scan_rsp_tx_pwr, rssi, interval_le16, adi,
data_len_max, scan_data_len_total,
&scan_data_len, &scan_data, buf, &evt_buf);
}
/* set scan data status bits */
evt_type |= (scan_data_status << 5);
/* Start constructing the event for remainder of the PDU data */
ext_adv_info_fill(evt_type, phy, sec_phy_scan, adv_addr_type, adv_addr,
direct_addr_type, direct_addr, rl_idx,
scan_rsp_tx_pwr, rssi, interval_le16, adi,
scan_data_len, scan_data, evt_buf);
node_rx_extra_list_release(node_rx->hdr.rx_ftr.extra);
}
static void le_adv_ext_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf, uint8_t phy)
{
struct pdu_adv *adv = (void *)pdu_data;
if ((adv->type == PDU_ADV_TYPE_EXT_IND) && adv->len) {
le_ext_adv_report(pdu_data, node_rx, buf, phy);
} else {
le_ext_adv_legacy_report(pdu_data, node_rx, buf);
}
}
static void le_adv_ext_1M_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
le_adv_ext_report(pdu_data, node_rx, buf, BT_HCI_LE_EXT_SCAN_PHY_1M);
}
static void le_adv_ext_2M_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
le_adv_ext_report(pdu_data, node_rx, buf, BT_HCI_LE_EXT_SCAN_PHY_2M);
}
static void le_adv_ext_coded_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
le_adv_ext_report(pdu_data, node_rx, buf, BT_HCI_LE_EXT_SCAN_PHY_CODED);
}
static void le_scan_timeout(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx, struct net_buf *buf)
{
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_SCAN_TIMEOUT)) {
return;
}
meta_evt(buf, BT_HCI_EVT_LE_SCAN_TIMEOUT, 0U);
}
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
static void le_per_adv_sync_established(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_per_adv_sync_established *sep;
struct ll_scan_set *scan;
struct node_rx_sync *se;
void *node;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED,
sizeof(*sep));
/* Check for pdu field being aligned before accessing sync established
* event.
*/
node = pdu_data;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_sync));
se = node;
sep->status = se->status;
if (se->status == BT_HCI_ERR_OP_CANCELLED_BY_HOST) {
return;
}
scan = node_rx->hdr.rx_ftr.param;
#if (CONFIG_BT_CTLR_DUP_FILTER_LEN > 0) && \
defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
dup_periodic_adv_reset(scan->periodic.adv_addr_type,
scan->periodic.adv_addr,
scan->periodic.sid);
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 &&
* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT
*/
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
/* Resolved address, if private, has been populated in ULL */
sep->adv_addr.type = scan->periodic.adv_addr_type;
(void)memcpy(sep->adv_addr.a.val, scan->periodic.adv_addr, BDADDR_SIZE);
sep->sid = scan->periodic.sid;
sep->phy = find_lsb_set(se->phy);
sep->interval = sys_cpu_to_le16(se->interval);
sep->clock_accuracy = se->sca;
}
static void le_per_adv_sync_report(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct node_rx_ftr *ftr = &node_rx->hdr.rx_ftr;
int8_t tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
struct pdu_adv *adv = (void *)pdu_data;
struct pdu_adv_aux_ptr *aux_ptr = NULL;
const struct pdu_adv_adi *adi = NULL;
uint8_t cte_type = BT_HCI_LE_NO_CTE;
const struct ll_sync_set *sync;
struct pdu_adv_com_ext_adv *p;
struct pdu_adv_ext_hdr *h;
uint16_t data_len_total;
struct net_buf *evt_buf;
uint8_t data_len = 0U;
uint8_t acad_len = 0U;
uint8_t *data = NULL;
uint8_t data_len_max;
uint8_t *acad = NULL;
uint8_t hdr_buf_len;
uint8_t hdr_len;
uint8_t *ptr;
int8_t rssi;
bool accept;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
(!(le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT) &&
!(le_event_mask & BT_EVT_MASK_LE_BIGINFO_ADV_REPORT))) {
return;
}
/* NOTE: The timeout_reload field in the sync context is checked under
* race condition between HCI Tx and Rx thread wherein a sync
* terminate was performed which resets the timeout_reload field
* before releasing the sync context back into its memory pool.
* It is important that timeout_reload field is at safe offset
* inside the sync context such that it is not corrupt while being
* in the memory pool.
*
* This check ensures reports are not sent out after sync
* terminate.
*/
sync = HDR_LLL2ULL(ftr->param);
if (unlikely(!sync->timeout_reload)) {
return;
}
if ((le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT) &&
node_rx->hdr.rx_ftr.aux_failed) {
struct bt_hci_evt_le_per_advertising_report *sep;
sep = meta_evt(buf,
BT_HCI_EVT_LE_PER_ADVERTISING_REPORT,
sizeof(*sep));
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->tx_power = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
sep->rssi = BT_HCI_LE_RSSI_NOT_AVAILABLE;
sep->cte_type = BT_HCI_LE_NO_CTE;
sep->data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
sep->length = 0;
return;
}
/* The Link Layer currently returns RSSI as an absolute value */
rssi = -(node_rx->hdr.rx_ftr.rssi);
BT_DBG("len = %u, rssi = %d", adv->len, rssi);
p = (void *)&adv->adv_ext_ind;
h = (void *)p->ext_hdr_adv_data;
ptr = (void *)h;
BT_DBG(" Per. adv mode= 0x%x, hdr len= %u", p->adv_mode,
p->ext_hdr_len);
if (!p->ext_hdr_len) {
hdr_len = PDU_AC_EXT_HEADER_SIZE_MIN;
goto no_ext_hdr;
}
ptr = h->data;
if (h->adv_addr) {
ptr += BDADDR_SIZE;
}
if (h->tgt_addr) {
ptr += BDADDR_SIZE;
}
if (h->cte_info) {
struct pdu_cte_info *cte_info;
cte_info = (void *)ptr;
cte_type = cte_info->type;
ptr++;
BT_DBG(" CTE type= %d", cte_type);
}
if (h->adi) {
adi = (void *)ptr;
ptr += sizeof(struct pdu_adv_adi);
}
/* AuxPtr */
if (h->aux_ptr) {
uint8_t aux_phy;
aux_ptr = (void *)ptr;
if (aux_ptr->phy > EXT_ADV_AUX_PHY_LE_CODED) {
return;
}
ptr += sizeof(*aux_ptr);
aux_phy = BIT(aux_ptr->phy);
BT_DBG(" AuxPtr chan_idx = %u, ca = %u, offs_units "
"= %u offs = 0x%x, phy = 0x%x",
aux_ptr->chan_idx, aux_ptr->ca,
aux_ptr->offs_units, aux_ptr->offs, aux_phy);
}
/* No SyncInfo */
if (h->sync_info) {
ptr += sizeof(struct pdu_adv_sync_info);
}
/* Tx Power */
if (h->tx_pwr) {
tx_pwr = *(int8_t *)ptr;
ptr++;
BT_DBG(" Tx pwr= %d dB", tx_pwr);
}
hdr_len = ptr - (uint8_t *)p;
hdr_buf_len = PDU_AC_EXT_HEADER_SIZE_MIN + p->ext_hdr_len;
if (hdr_len > hdr_buf_len) {
BT_WARN(" Header length %u/%u, INVALID.", hdr_len,
p->ext_hdr_len);
} else {
acad_len = hdr_buf_len - hdr_len;
if (acad_len) {
acad = ptr;
ptr += acad_len;
hdr_len += acad_len;
}
}
no_ext_hdr:
if (hdr_len < adv->len) {
data_len = adv->len - hdr_len;
data = ptr;
BT_DBG(" AD Data (%u): <todo>", data_len);
}
if (0) {
#if (CONFIG_BT_CTLR_DUP_FILTER_LEN > 0) && \
defined(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT)
} else if (IS_ENABLED(CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT) &&
adi) {
uint8_t data_status;
data_status = (aux_ptr) ?
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL :
BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE;
accept = sync->rx_enable && ftr->sync_rx_enabled &&
(!sync->nodups ||
!dup_found(PDU_ADV_TYPE_EXT_IND,
sync->peer_id_addr_type,
sync->peer_id_addr,
DUP_EXT_ADV_MODE_PERIODIC,
adi, data_status));
#endif /* CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 &&
* CONFIG_BT_CTLR_SYNC_PERIODIC_ADI_SUPPORT
*/
} else {
accept = sync->rx_enable && ftr->sync_rx_enabled;
}
data_len_max = CONFIG_BT_BUF_EVT_RX_SIZE -
sizeof(struct bt_hci_evt_le_meta_event) -
sizeof(struct bt_hci_evt_le_per_advertising_report);
data_len_total = node_rx->hdr.rx_ftr.aux_data_len;
evt_buf = buf;
if ((le_event_mask & BT_EVT_MASK_LE_PER_ADVERTISING_REPORT) && accept &&
((data_len_total - data_len) < CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
/* Pass verdict in LL.TS.p19 section 4.2.3.6 Extended Scanning,
* Passive, Periodic Advertising Report, RSSI and TX_Power
* states:
* TX_Power is set to value of the TxPower field for the
* AUX_SYNC_IND received, and RSSI set to a valid value.
* Subsequent reports with data and the status set to
* "Incomplete, more data to come" or "complete" can have the
* TX_Power field set to 0x7F.
*
* In the implementation data_len_total is the running total
* AD data length so far, data_len is the current PDU's AD data
* length. For AUX_SYNC_IND received, data_len_total ==
* data_len.
*/
if (data_len_total > data_len) {
/* Subsequent reports */
tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
}
data_len = MIN(data_len, (CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX +
data_len - data_len_total));
do {
struct bt_hci_evt_le_per_advertising_report *sep;
uint8_t data_len_frag;
uint8_t data_status;
data_len_frag = MIN(data_len, data_len_max);
/* Start constructing periodic advertising report */
sep = meta_evt(evt_buf,
BT_HCI_EVT_LE_PER_ADVERTISING_REPORT,
sizeof(*sep) + data_len_frag);
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->tx_power = tx_pwr;
sep->rssi = rssi;
sep->cte_type = cte_type;
sep->length = data_len_frag;
memcpy(&sep->data[0], data, data_len_frag);
data += data_len_frag;
data_len -= data_len_frag;
if (data_len > 0) {
/* Some data left in PDU, mark as partial data. */
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL;
evt_buf = bt_buf_get_rx(BT_BUF_EVT, BUF_GET_TIMEOUT);
LL_ASSERT(evt_buf);
net_buf_frag_add(buf, evt_buf);
tx_pwr = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
} else if (!aux_ptr &&
(data_len_total <= CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
/* No data left, no AuxPtr, mark as complete data. */
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE;
} else if (ftr->aux_sched &&
(data_len_total < CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX)) {
/* No data left, but have AuxPtr and scheduled aux scan,
* mark as partial data.
*/
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL;
} else {
/* No data left, have AuxPtr but not aux scan scheduled,
* mark as incomplete data.
*/
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE;
}
sep->data_status = data_status;
} while (data_len > 0);
evt_buf = NULL;
}
if ((le_event_mask & BT_EVT_MASK_LE_BIGINFO_ADV_REPORT) && acad &&
(acad_len >= (PDU_BIG_INFO_CLEARTEXT_SIZE +
PDU_ADV_DATA_HEADER_SIZE))) {
struct bt_hci_evt_le_biginfo_adv_report *sep;
struct pdu_big_info *bi;
uint8_t bi_size;
/* FIXME: Parse and find the BIGInfo */
if (acad[PDU_ADV_DATA_HEADER_TYPE_OFFSET] != BT_DATA_BIG_INFO) {
return;
}
bi_size = acad[PDU_ADV_DATA_HEADER_LEN_OFFSET];
bi = (void *)&acad[PDU_ADV_DATA_HEADER_DATA_OFFSET];
/* Allocate new event buffer if periodic advertising report was
* constructed with the caller supplied buffer.
*/
if (!evt_buf) {
evt_buf = bt_buf_get_rx(BT_BUF_EVT, BUF_GET_TIMEOUT);
LL_ASSERT(evt_buf);
net_buf_frag_add(buf, evt_buf);
}
/* Start constructing BIGInfo advertising report */
sep = meta_evt(evt_buf, BT_HCI_EVT_LE_BIGINFO_ADV_REPORT,
sizeof(*sep));
sep->sync_handle = sys_cpu_to_le16(node_rx->hdr.handle);
/* NOTE: both sep and bi struct store little-endian values,
* explicit endian-ness conversion not required.
*/
sep->num_bis = bi->num_bis;
sep->nse = bi->nse;
sep->iso_interval = bi->iso_interval;
sep->bn = bi->bn;
sep->pto = bi->pto;
sep->irc = bi->irc;
sep->max_pdu = bi->max_pdu;
sys_put_le24(sys_le24_to_cpu(bi->sdu_interval),
sep->sdu_interval);
sep->max_sdu = bi->max_sdu;
sep->phy = HCI_AUX_PHY_TO_HCI_PHY(bi->chm_phy[4] >> 5);
sep->framing = (bi->payload_count_framing[4] >> 7) & 0x01;
if (bi_size == (PDU_BIG_INFO_ENCRYPTED_SIZE + 1)) {
sep->encryption = 1U;
} else {
sep->encryption = 0U;
}
}
}
static void le_per_adv_sync_lost(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_per_adv_sync_lost *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_PER_ADV_SYNC_LOST)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_PER_ADV_SYNC_LOST, sizeof(*sep));
sep->handle = sys_cpu_to_le16(node_rx->hdr.handle);
}
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
static void le_big_sync_established(struct pdu_data *pdu,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_big_sync_established *sep;
struct ll_sync_iso_set *sync_iso;
struct node_rx_sync_iso *se;
struct lll_sync_iso *lll;
size_t evt_size;
void *node;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_BIG_SYNC_ESTABLISHED)) {
return;
}
sync_iso = node_rx->hdr.rx_ftr.param;
lll = &sync_iso->lll;
evt_size = sizeof(*sep) + (lll->num_bis * sizeof(uint16_t));
sep = meta_evt(buf, BT_HCI_EVT_LE_BIG_SYNC_ESTABLISHED, evt_size);
sep->big_handle = sys_cpu_to_le16(node_rx->hdr.handle);
/* Check for pdu field being aligned before accessing ISO sync
* established event.
*/
node = pdu;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_sync_iso));
se = node;
sep->status = se->status;
if (sep->status) {
return;
}
/* FIXME: Fill latency */
sys_put_le24(0, sep->latency);
sep->nse = lll->nse;
sep->bn = lll->bn;
sep->pto = lll->pto;
sep->irc = lll->irc;
sep->max_pdu = sys_cpu_to_le16(lll->max_pdu);
sep->iso_interval = sys_cpu_to_le16(lll->iso_interval);
sep->num_bis = lll->stream_count;
/* Connection handle list of all BISes synchronized in the BIG */
for (uint8_t i = 0U; i < lll->stream_count; i++) {
uint16_t handle;
handle = BT_CTLR_SYNC_ISO_STREAM_HANDLE_BASE +
lll->stream_handle[i];
sep->handle[i] = sys_cpu_to_le16(handle);
}
}
static void le_big_sync_lost(struct pdu_data *pdu,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_big_sync_lost *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_BIG_SYNC_LOST)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_BIG_SYNC_LOST, sizeof(*sep));
sep->big_handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->reason = *((uint8_t *)pdu);
}
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_BROADCASTER)
#if defined(CONFIG_BT_CTLR_ADV_EXT)
static void le_adv_ext_terminate(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_adv_set_terminated *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_ADV_SET_TERMINATED)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_ADV_SET_TERMINATED, sizeof(*sep));
sep->status = node_rx->hdr.rx_ftr.param_adv_term.status;
sep->adv_handle = ll_adv_set_hci_handle_get(node_rx->hdr.handle & 0xff);
sep->conn_handle =
sys_cpu_to_le16(node_rx->hdr.rx_ftr.param_adv_term.conn_handle);
sep->num_completed_ext_adv_evts =
node_rx->hdr.rx_ftr.param_adv_term.num_events;
}
#if defined(CONFIG_BT_CTLR_ADV_ISO)
static void le_big_complete(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_big_complete *sep;
struct ll_adv_iso_set *adv_iso;
struct lll_adv_iso *lll;
size_t evt_size;
adv_iso = node_rx->hdr.rx_ftr.param;
lll = &adv_iso->lll;
evt_size = sizeof(*sep) + (lll->num_bis * sizeof(uint16_t));
sep = meta_evt(buf, BT_HCI_EVT_LE_BIG_COMPLETE, evt_size);
sep->status = BT_HCI_ERR_SUCCESS;
sep->big_handle = sys_cpu_to_le16(node_rx->hdr.handle);
if (sep->status) {
return;
}
/* FIXME: Fill sync delay and latency */
sys_put_le24(0, sep->sync_delay);
sys_put_le24(0, sep->latency);
sep->phy = find_lsb_set(lll->phy);
sep->nse = lll->nse;
sep->bn = lll->bn;
sep->pto = lll->pto;
sep->irc = lll->irc;
sep->max_pdu = sys_cpu_to_le16(lll->max_pdu);
sep->num_bis = lll->num_bis;
/* Connection handle list of all BISes in the BIG */
for (uint8_t i = 0U; i < lll->num_bis; i++) {
uint16_t handle;
handle = BT_CTLR_ADV_ISO_STREAM_HANDLE_BASE +
lll->stream_handle[i];
sep->handle[i] = sys_cpu_to_le16(handle);
}
}
static void le_big_terminate(struct pdu_data *pdu,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_big_terminate *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_BIG_TERMINATED)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_BIG_TERMINATE, sizeof(*sep));
sep->big_handle = sys_cpu_to_le16(node_rx->hdr.handle);
sep->reason = *((uint8_t *)pdu);
}
#endif /* CONFIG_BT_CTLR_ADV_ISO */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY)
static void le_scan_req_received(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct pdu_adv *adv = (void *)pdu_data;
struct bt_hci_evt_le_scan_req_received *sep;
#if defined(CONFIG_BT_CTLR_PRIVACY)
uint8_t rl_idx;
#endif
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_SCAN_REQ_RECEIVED)) {
bt_addr_le_t addr;
uint8_t handle;
int8_t rssi;
handle = ll_adv_set_hci_handle_get(node_rx->hdr.handle & 0xff);
addr.type = adv->tx_addr;
memcpy(&addr.a.val[0], &adv->scan_req.scan_addr[0],
sizeof(bt_addr_t));
/* The Link Layer currently returns RSSI as an absolute value */
rssi = -(node_rx->hdr.rx_ftr.rssi);
BT_DBG("handle: %d, addr: %s, rssi: %d dB.",
handle, bt_addr_le_str(&addr), rssi);
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_SCAN_REQ_RECEIVED, sizeof(*sep));
sep->handle = ll_adv_set_hci_handle_get(node_rx->hdr.handle & 0xff);
sep->addr.type = adv->tx_addr;
memcpy(&sep->addr.a.val[0], &adv->scan_req.scan_addr[0],
sizeof(bt_addr_t));
#if defined(CONFIG_BT_CTLR_PRIVACY)
rl_idx = node_rx->hdr.rx_ftr.rl_idx;
if (rl_idx < ll_rl_size_get()) {
/* Store identity address */
ll_rl_id_addr_get(rl_idx, &sep->addr.type,
&sep->addr.a.val[0]);
/* Mark it as identity address from RPA (0x02, 0x03) */
sep->addr.type += 2U;
} else {
#else
if (1) {
#endif
sep->addr.type = adv->tx_addr;
memcpy(&sep->addr.a.val[0], &adv->adv_ind.addr[0],
sizeof(bt_addr_t));
}
}
#endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */
#if defined(CONFIG_BT_CONN)
static void le_conn_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_conn_complete *lecc;
struct node_rx_cc *cc;
uint8_t status;
void *node;
/* Check for pdu field being aligned before accessing connection
* complete event.
*/
node = pdu_data;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_cc));
cc = node;
status = cc->status;
#if defined(CONFIG_BT_CTLR_PRIVACY)
if (!status) {
/* Update current RPA */
ll_rl_crpa_set(cc->peer_addr_type,
&cc->peer_addr[0], 0xff,
&cc->peer_rpa[0]);
}
#endif
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
(!(le_event_mask & BT_EVT_MASK_LE_CONN_COMPLETE) &&
#if defined(CONFIG_BT_CTLR_PRIVACY) || defined(CONFIG_BT_CTLR_ADV_EXT)
!(le_event_mask & BT_EVT_MASK_LE_ENH_CONN_COMPLETE))) {
#else
1)) {
#endif /* CONFIG_BT_CTLR_PRIVACY || CONFIG_BT_CTLR_ADV_EXT */
return;
}
if (!status) {
conn_count++;
}
#if defined(CONFIG_BT_CTLR_PRIVACY) || defined(CONFIG_BT_CTLR_ADV_EXT)
if (le_event_mask & BT_EVT_MASK_LE_ENH_CONN_COMPLETE) {
struct bt_hci_evt_le_enh_conn_complete *leecc;
leecc = meta_evt(buf, BT_HCI_EVT_LE_ENH_CONN_COMPLETE,
sizeof(*leecc));
if (status) {
(void)memset(leecc, 0x00, sizeof(*leecc));
leecc->status = status;
return;
}
leecc->status = 0x00;
leecc->handle = sys_cpu_to_le16(handle);
leecc->role = cc->role;
leecc->peer_addr.type = cc->peer_addr_type;
memcpy(&leecc->peer_addr.a.val[0], &cc->peer_addr[0],
BDADDR_SIZE);
#if defined(CONFIG_BT_CTLR_PRIVACY)
memcpy(&leecc->local_rpa.val[0], &cc->local_rpa[0],
BDADDR_SIZE);
memcpy(&leecc->peer_rpa.val[0], &cc->peer_rpa[0],
BDADDR_SIZE);
#else /* !CONFIG_BT_CTLR_PRIVACY */
memset(&leecc->local_rpa.val[0], 0, BDADDR_SIZE);
memset(&leecc->peer_rpa.val[0], 0, BDADDR_SIZE);
#endif /* !CONFIG_BT_CTLR_PRIVACY */
leecc->interval = sys_cpu_to_le16(cc->interval);
leecc->latency = sys_cpu_to_le16(cc->latency);
leecc->supv_timeout = sys_cpu_to_le16(cc->timeout);
leecc->clock_accuracy = cc->sca;
return;
}
#endif /* CONFIG_BT_CTLR_PRIVACY || CONFIG_BT_CTLR_ADV_EXT */
lecc = meta_evt(buf, BT_HCI_EVT_LE_CONN_COMPLETE, sizeof(*lecc));
if (status) {
(void)memset(lecc, 0x00, sizeof(*lecc));
lecc->status = status;
return;
}
lecc->status = 0x00;
lecc->handle = sys_cpu_to_le16(handle);
lecc->role = cc->role;
lecc->peer_addr.type = cc->peer_addr_type & 0x1;
memcpy(&lecc->peer_addr.a.val[0], &cc->peer_addr[0], BDADDR_SIZE);
lecc->interval = sys_cpu_to_le16(cc->interval);
lecc->latency = sys_cpu_to_le16(cc->latency);
lecc->supv_timeout = sys_cpu_to_le16(cc->timeout);
lecc->clock_accuracy = cc->sca;
}
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
void hci_disconn_complete_encode(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_disconn_complete *ep;
if (!(event_mask & BT_EVT_MASK_DISCONN_COMPLETE)) {
return;
}
hci_evt_create(buf, BT_HCI_EVT_DISCONN_COMPLETE, sizeof(*ep));
ep = net_buf_add(buf, sizeof(*ep));
ep->status = 0x00;
ep->handle = sys_cpu_to_le16(handle);
ep->reason = *((uint8_t *)pdu_data);
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
}
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
void hci_disconn_complete_process(uint16_t handle)
{
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
/* Clear any pending packets upon disconnection */
/* Note: This requires linear handle values starting from 0 */
LL_ASSERT(handle < ARRAY_SIZE(hci_hbuf_pend));
hci_hbuf_acked += hci_hbuf_pend[handle];
hci_hbuf_pend[handle] = 0U;
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
conn_count--;
}
static void le_conn_update_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_conn_update_complete *sep;
struct node_rx_cu *cu;
void *node;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, sizeof(*sep));
/* Check for pdu field being aligned before accessing connection
* update complete event.
*/
node = pdu_data;
LL_ASSERT(IS_PTR_ALIGNED(node, struct node_rx_cu));
cu = node;
sep->status = cu->status;
sep->handle = sys_cpu_to_le16(handle);
sep->interval = sys_cpu_to_le16(cu->interval);
sep->latency = sys_cpu_to_le16(cu->latency);
sep->supv_timeout = sys_cpu_to_le16(cu->timeout);
}
#if defined(CONFIG_BT_CTLR_LE_ENC)
static void enc_refresh_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_encrypt_key_refresh_complete *ep;
if (!(event_mask & BT_EVT_MASK_ENCRYPT_KEY_REFRESH_COMPLETE)) {
return;
}
hci_evt_create(buf, BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE,
sizeof(*ep));
ep = net_buf_add(buf, sizeof(*ep));
ep->status = 0x00;
ep->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_LE_PING)
static void auth_payload_timeout_exp(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_auth_payload_timeout_exp *ep;
if (!(event_mask_page_2 & BT_EVT_MASK_AUTH_PAYLOAD_TIMEOUT_EXP)) {
return;
}
hci_evt_create(buf, BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXP, sizeof(*ep));
ep = net_buf_add(buf, sizeof(*ep));
ep->handle = sys_cpu_to_le16(handle);
}
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_CHAN_SEL_2)
static void le_chan_sel_algo(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_chan_sel_algo *sep;
struct node_rx_cs *cs;
cs = (void *)pdu_data;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CHAN_SEL_ALGO)) {
BT_DBG("handle: 0x%04x, CSA: %x.", handle, cs->csa);
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_CHAN_SEL_ALGO, sizeof(*sep));
sep->handle = sys_cpu_to_le16(handle);
sep->chan_sel_algo = cs->csa;
}
#endif /* CONFIG_BT_CTLR_CHAN_SEL_2 */
#if defined(CONFIG_BT_CTLR_PHY)
static void le_phy_upd_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_phy_update_complete *sep;
struct node_rx_pu *pu;
pu = (void *)pdu_data;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE)) {
BT_WARN("handle: 0x%04x, status: %x, tx: %x, rx: %x.", handle,
pu->status,
find_lsb_set(pu->tx),
find_lsb_set(pu->rx));
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE, sizeof(*sep));
sep->status = pu->status;
sep->handle = sys_cpu_to_le16(handle);
sep->tx_phy = find_lsb_set(pu->tx);
sep->rx_phy = find_lsb_set(pu->rx);
}
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_HCI_MESH_EXT)
static void mesh_adv_cplt(struct pdu_data *pdu_data,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_mesh_adv_complete *mep;
mep = mesh_evt(buf, BT_HCI_EVT_MESH_ADV_COMPLETE, sizeof(*mep));
mep->adv_slot = ((uint8_t *)pdu_data)[0];
}
#endif /* CONFIG_BT_HCI_MESH_EXT */
/**
* @brief Encode a control-PDU into an HCI buffer
* @details Execution context: Host thread
*
* @param node_rx_pdu[in] RX node containing header and PDU
* @param pdu_data[in] PDU. Same as node_rx_pdu->pdu, but more convenient
* @param net_buf[out] Upwards-going HCI buffer to fill
*/
static void encode_control(struct node_rx_pdu *node_rx,
struct pdu_data *pdu_data, struct net_buf *buf)
{
uint16_t handle;
handle = node_rx->hdr.handle;
switch (node_rx->hdr.type) {
#if defined(CONFIG_BT_OBSERVER)
case NODE_RX_TYPE_REPORT:
le_advertising_report(pdu_data, node_rx, buf);
break;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
case NODE_RX_TYPE_EXT_1M_REPORT:
le_adv_ext_1M_report(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_EXT_2M_REPORT:
le_adv_ext_2M_report(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_EXT_CODED_REPORT:
le_adv_ext_coded_report(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_EXT_SCAN_TERMINATE:
le_scan_timeout(pdu_data, node_rx, buf);
break;
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC:
le_per_adv_sync_established(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_SYNC_REPORT:
le_per_adv_sync_report(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_SYNC_LOST:
le_per_adv_sync_lost(pdu_data, node_rx, buf);
break;
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT:
le_df_connectionless_iq_report(pdu_data, node_rx, buf);
break;
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
case NODE_RX_TYPE_SYNC_ISO:
le_big_sync_established(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_SYNC_ISO_LOST:
le_big_sync_lost(pdu_data, node_rx, buf);
break;
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_BROADCASTER)
#if defined(CONFIG_BT_CTLR_ADV_EXT)
case NODE_RX_TYPE_EXT_ADV_TERMINATE:
le_adv_ext_terminate(pdu_data, node_rx, buf);
break;
#if defined(CONFIG_BT_CTLR_ADV_ISO)
case NODE_RX_TYPE_BIG_COMPLETE:
le_big_complete(pdu_data, node_rx, buf);
break;
case NODE_RX_TYPE_BIG_TERMINATE:
le_big_terminate(pdu_data, node_rx, buf);
break;
#endif /* CONFIG_BT_CTLR_ADV_ISO */
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY)
case NODE_RX_TYPE_SCAN_REQ:
le_scan_req_received(pdu_data, node_rx, buf);
break;
#endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */
#if defined(CONFIG_BT_CONN)
case NODE_RX_TYPE_CONNECTION:
le_conn_complete(pdu_data, handle, buf);
break;
case NODE_RX_TYPE_TERMINATE:
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
hci_disconn_complete_encode(pdu_data, handle, buf);
break;
case NODE_RX_TYPE_CONN_UPDATE:
le_conn_update_complete(pdu_data, handle, buf);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC)
case NODE_RX_TYPE_ENC_REFRESH:
enc_refresh_complete(pdu_data, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_LE_PING)
case NODE_RX_TYPE_APTO:
auth_payload_timeout_exp(pdu_data, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_CHAN_SEL_2)
case NODE_RX_TYPE_CHAN_SEL_ALGO:
le_chan_sel_algo(pdu_data, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_CHAN_SEL_2 */
#if defined(CONFIG_BT_CTLR_PHY)
case NODE_RX_TYPE_PHY_UPDATE:
le_phy_upd_complete(pdu_data, handle, buf);
return;
#endif /* CONFIG_BT_CTLR_PHY */
#if defined(CONFIG_BT_CTLR_CONN_RSSI_EVENT)
case NODE_RX_TYPE_RSSI:
BT_INFO("handle: 0x%04x, rssi: -%d dB.", handle,
pdu_data->rssi);
return;
#endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case NODE_RX_TYPE_CIS_REQUEST:
le_cis_request(pdu_data, node_rx, buf);
return;
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_CONN_ISO)
case NODE_RX_TYPE_CIS_ESTABLISHED:
le_cis_established(pdu_data, node_rx, buf);
return;
#endif /* CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT:
le_df_connection_iq_report(node_rx, buf);
return;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_ADV_INDICATION)
case NODE_RX_TYPE_ADV_INDICATION:
BT_INFO("Advertised.");
return;
#endif /* CONFIG_BT_CTLR_ADV_INDICATION */
#if defined(CONFIG_BT_CTLR_SCAN_INDICATION)
case NODE_RX_TYPE_SCAN_INDICATION:
BT_INFO("Scanned.");
return;
#endif /* CONFIG_BT_CTLR_SCAN_INDICATION */
#if defined(CONFIG_BT_CTLR_PROFILE_ISR)
case NODE_RX_TYPE_PROFILE:
BT_INFO("l: %u, %u, %u; t: %u, %u, %u; cpu: %u, %u, %u, %u.",
pdu_data->profile.lcur,
pdu_data->profile.lmin,
pdu_data->profile.lmax,
pdu_data->profile.cur,
pdu_data->profile.min,
pdu_data->profile.max,
pdu_data->profile.radio,
pdu_data->profile.lll,
pdu_data->profile.ull_high,
pdu_data->profile.ull_low);
return;
#endif /* CONFIG_BT_CTLR_PROFILE_ISR */
#if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT:
le_df_connectionless_iq_report(pdu_data, node_rx, buf);
return;
#endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */
#if defined(CONFIG_BT_HCI_MESH_EXT)
case NODE_RX_TYPE_MESH_ADV_CPLT:
mesh_adv_cplt(pdu_data, node_rx, buf);
return;
case NODE_RX_TYPE_MESH_REPORT:
le_advertising_report(pdu_data, node_rx, buf);
return;
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if CONFIG_BT_CTLR_USER_EVT_RANGE > 0
case NODE_RX_TYPE_USER_START ... NODE_RX_TYPE_USER_END - 1:
hci_user_ext_encode_control(node_rx, pdu_data, buf);
return;
#endif /* CONFIG_BT_CTLR_USER_EVT_RANGE > 0 */
default:
LL_ASSERT(0);
return;
}
}
#if defined(CONFIG_BT_CTLR_LE_ENC)
static void le_ltk_request(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_ltk_request *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_LTK_REQUEST)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_LTK_REQUEST, sizeof(*sep));
sep->handle = sys_cpu_to_le16(handle);
memcpy(&sep->rand, pdu_data->llctrl.enc_req.rand, sizeof(uint64_t));
memcpy(&sep->ediv, pdu_data->llctrl.enc_req.ediv, sizeof(uint16_t));
}
static void encrypt_change(uint8_t err, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_encrypt_change *ep;
if (!(event_mask & BT_EVT_MASK_ENCRYPT_CHANGE)) {
return;
}
hci_evt_create(buf, BT_HCI_EVT_ENCRYPT_CHANGE, sizeof(*ep));
ep = net_buf_add(buf, sizeof(*ep));
ep->status = err;
ep->handle = sys_cpu_to_le16(handle);
ep->encrypt = !err ? 1 : 0;
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
static void le_remote_feat_complete(uint8_t status, struct pdu_data *pdu_data,
uint16_t handle, struct net_buf *buf)
{
struct bt_hci_evt_le_remote_feat_complete *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_REMOTE_FEAT_COMPLETE, sizeof(*sep));
sep->status = status;
sep->handle = sys_cpu_to_le16(handle);
if (!status) {
memcpy(&sep->features[0],
&pdu_data->llctrl.feature_rsp.features[0],
sizeof(sep->features));
} else {
(void)memset(&sep->features[0], 0x00, sizeof(sep->features));
}
}
static void le_unknown_rsp(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
switch (pdu_data->llctrl.unknown_rsp.type) {
case PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG:
le_remote_feat_complete(BT_HCI_ERR_UNSUPP_REMOTE_FEATURE,
NULL, handle, buf);
break;
default:
BT_WARN("type: 0x%02x", pdu_data->llctrl.unknown_rsp.type);
break;
}
}
static void le_reject_ext_ind(struct pdu_data *pdu, uint16_t handle, struct net_buf *buf)
{
switch (pdu->llctrl.reject_ext_ind.reject_opcode) {
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PDU_DATA_LLCTRL_TYPE_CTE_REQ:
le_df_cte_req_failed(pdu->llctrl.reject_ext_ind.error_code, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
default:
BT_WARN("reject opcode: 0x%02x", pdu->llctrl.reject_ext_ind.reject_opcode);
break;
}
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void le_conn_param_req(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_conn_param_req *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_CONN_PARAM_REQ)) {
/* event masked, reject the conn param req */
ll_conn_update(handle, 2, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE, 0,
0, 0, 0);
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_CONN_PARAM_REQ, sizeof(*sep));
sep->handle = sys_cpu_to_le16(handle);
sep->interval_min = pdu_data->llctrl.conn_param_req.interval_min;
sep->interval_max = pdu_data->llctrl.conn_param_req.interval_max;
sep->latency = pdu_data->llctrl.conn_param_req.latency;
sep->timeout = pdu_data->llctrl.conn_param_req.timeout;
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
static void le_data_len_change(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_data_len_change *sep;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_DATA_LEN_CHANGE)) {
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_DATA_LEN_CHANGE, sizeof(*sep));
sep->handle = sys_cpu_to_le16(handle);
sep->max_tx_octets = pdu_data->llctrl.length_rsp.max_tx_octets;
sep->max_tx_time = pdu_data->llctrl.length_rsp.max_tx_time;
sep->max_rx_octets = pdu_data->llctrl.length_rsp.max_rx_octets;
sep->max_rx_time = pdu_data->llctrl.length_rsp.max_rx_time;
}
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_REMOTE_VERSION)
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
static void remote_version_info_encode(struct pdu_data *pdu_data,
uint16_t handle, struct net_buf *buf)
{
struct pdu_data_llctrl_version_ind *ver_ind;
struct bt_hci_evt_remote_version_info *ep;
if (!(event_mask & BT_EVT_MASK_REMOTE_VERSION_INFO)) {
return;
}
hci_evt_create(buf, BT_HCI_EVT_REMOTE_VERSION_INFO, sizeof(*ep));
ep = net_buf_add(buf, sizeof(*ep));
ver_ind = &pdu_data->llctrl.version_ind;
ep->status = 0x00;
ep->handle = sys_cpu_to_le16(handle);
ep->version = ver_ind->version_number;
ep->manufacturer = ver_ind->company_id;
ep->subversion = ver_ind->sub_version_number;
}
#endif /* CONFIG_BT_REMOTE_VERSION */
static void encode_data_ctrl(struct node_rx_pdu *node_rx,
struct pdu_data *pdu_data, struct net_buf *buf)
{
uint16_t handle = node_rx->hdr.handle;
switch (pdu_data->llctrl.opcode) {
#if defined(CONFIG_BT_CTLR_LE_ENC)
case PDU_DATA_LLCTRL_TYPE_ENC_REQ:
le_ltk_request(pdu_data, handle, buf);
break;
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
encrypt_change(0x00, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_REMOTE_VERSION)
case PDU_DATA_LLCTRL_TYPE_VERSION_IND:
Bluetooth: Fix host RX thread deadlock Fix host RX thread being deadlocked. The deadlock occurs because the RX thread is stuck waiting in conn_tx_alloc with K_FOREVER but if the connection is disconnected only the RX thread can unblock it in the handling of the disconnect event. This commit fixes this deadlock by splitting the processing of the disconnected event into two parts. The part needed to unblock the RX is to release resources held by unack'ed TX packets and mark the connection state as not connected anymore. The RX thread waiting for free_tx fifo and the TX thread waiting for the bt_dev.le.pkts semaphore will both check the connected state after having acquired them and will abort if disconnected. The rest of the processing will be handled at normal RX thread priority like normal. Move the bt_recv_prio handling to the Bluetooth host when the host has defined its own RX thread (CONFIG_BT_RECV_IS_RX_THREAD=n). If the HCI driver has the RX thread (CONFIG_BT_RECV_IS_RX_THREAD=y), then the responsibility to call bt_recv and bt_recv_prio correctly falls to the HCI driver. The helper function bt_hci_evt_is_prio() is replaced with bt_hci_evt_get_flags() so that the HCI driver can do this correctly. This decision to replace was made so that existing HCI drivers maintained out-of-tree will fail at compile time with the new system. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Bluetooth: host: Move bt_recv_prio to host when RX thread is defined Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
2020-06-03 14:48:04 +02:00
remote_version_info_encode(pdu_data, handle, buf);
break;
#endif /* defined(CONFIG_BT_REMOTE_VERSION) */
case PDU_DATA_LLCTRL_TYPE_FEATURE_RSP:
le_remote_feat_complete(0x00, pdu_data, handle, buf);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC)
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
encrypt_change(pdu_data->llctrl.reject_ind.error_code, handle,
buf);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ:
le_conn_param_req(pdu_data, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
case PDU_DATA_LLCTRL_TYPE_LENGTH_RSP:
le_data_len_change(pdu_data, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PDU_DATA_LLCTRL_TYPE_CTE_RSP:
le_df_cte_req_failed(BT_HCI_CTE_REQ_STATUS_RSP_WITHOUT_CTE, handle, buf);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP:
le_unknown_rsp(pdu_data, handle, buf);
break;
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
le_reject_ext_ind(pdu_data, handle, buf);
break;
default:
LL_ASSERT(0);
return;
}
}
#if defined(CONFIG_BT_CONN)
void hci_acl_encode(struct node_rx_pdu *node_rx, struct net_buf *buf)
{
struct pdu_data *pdu_data = (void *)node_rx->pdu;
struct bt_hci_acl_hdr *acl;
uint16_t handle_flags;
uint16_t handle;
uint8_t *data;
handle = node_rx->hdr.handle;
switch (pdu_data->ll_id) {
case PDU_DATA_LLID_DATA_CONTINUE:
case PDU_DATA_LLID_DATA_START:
acl = (void *)net_buf_add(buf, sizeof(*acl));
if (pdu_data->ll_id == PDU_DATA_LLID_DATA_START) {
handle_flags = bt_acl_handle_pack(handle, BT_ACL_START);
} else {
handle_flags = bt_acl_handle_pack(handle, BT_ACL_CONT);
}
acl->handle = sys_cpu_to_le16(handle_flags);
acl->len = sys_cpu_to_le16(pdu_data->len);
data = (void *)net_buf_add(buf, pdu_data->len);
memcpy(data, pdu_data->lldata, pdu_data->len);
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
if (hci_hbuf_total > 0) {
LL_ASSERT((hci_hbuf_sent - hci_hbuf_acked) <
hci_hbuf_total);
hci_hbuf_sent++;
/* Note: This requires linear handle values starting
* from 0
*/
LL_ASSERT(handle < ARRAY_SIZE(hci_hbuf_pend));
hci_hbuf_pend[handle]++;
}
#endif
break;
default:
LL_ASSERT(0);
break;
}
}
#endif /* CONFIG_BT_CONN */
void hci_evt_encode(struct node_rx_pdu *node_rx, struct net_buf *buf)
{
struct pdu_data *pdu_data = (void *)node_rx->pdu;
if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU) {
encode_control(node_rx, pdu_data, buf);
} else if (IS_ENABLED(CONFIG_BT_CONN)) {
encode_data_ctrl(node_rx, pdu_data, buf);
}
}
#if defined(CONFIG_BT_CONN) || defined(CONFIG_BT_CTLR_ADV_ISO) || \
defined(CONFIG_BT_CTLR_CONN_ISO)
void hci_num_cmplt_encode(struct net_buf *buf, uint16_t handle, uint8_t num)
{
struct bt_hci_evt_num_completed_packets *ep;
struct bt_hci_handle_count *hc;
uint8_t num_handles;
uint8_t len;
num_handles = 1U;
len = (sizeof(*ep) + (sizeof(*hc) * num_handles));
hci_evt_create(buf, BT_HCI_EVT_NUM_COMPLETED_PACKETS, len);
ep = net_buf_add(buf, len);
ep->num_handles = num_handles;
hc = &ep->h[0];
hc->handle = sys_cpu_to_le16(handle);
hc->count = sys_cpu_to_le16(num);
}
#endif /* CONFIG_BT_CONN || CONFIG_BT_CTLR_ADV_ISO || CONFIG_BT_CTLR_CONN_ISO */
uint8_t hci_get_class(struct node_rx_pdu *node_rx)
{
#if defined(CONFIG_BT_CONN)
struct pdu_data *pdu_data = (void *)node_rx->pdu;
#endif
if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU) {
switch (node_rx->hdr.type) {
#if defined(CONFIG_BT_OBSERVER) || \
defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY) || \
defined(CONFIG_BT_CTLR_ADV_INDICATION) || \
defined(CONFIG_BT_CTLR_SCAN_INDICATION) || \
defined(CONFIG_BT_CTLR_PROFILE_ISR)
#if defined(CONFIG_BT_OBSERVER)
case NODE_RX_TYPE_REPORT:
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CTLR_SCAN_REQ_NOTIFY)
case NODE_RX_TYPE_SCAN_REQ:
#endif /* CONFIG_BT_CTLR_SCAN_REQ_NOTIFY */
#if defined(CONFIG_BT_CTLR_ADV_INDICATION)
case NODE_RX_TYPE_ADV_INDICATION:
#endif /* CONFIG_BT_CTLR_ADV_INDICATION */
#if defined(CONFIG_BT_CTLR_SCAN_INDICATION)
case NODE_RX_TYPE_SCAN_INDICATION:
#endif /* CONFIG_BT_CTLR_SCAN_INDICATION */
#if defined(CONFIG_BT_CTLR_PROFILE_ISR)
case NODE_RX_TYPE_PROFILE:
#endif /* CONFIG_BT_CTLR_PROFILE_ISR */
return HCI_CLASS_EVT_DISCARDABLE;
#endif
#if defined(CONFIG_BT_HCI_MESH_EXT)
case NODE_RX_TYPE_MESH_ADV_CPLT:
case NODE_RX_TYPE_MESH_REPORT:
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_BROADCASTER)
case NODE_RX_TYPE_EXT_ADV_TERMINATE:
#if defined(CONFIG_BT_CTLR_ADV_ISO)
case NODE_RX_TYPE_BIG_COMPLETE:
case NODE_RX_TYPE_BIG_TERMINATE:
#endif /* CONFIG_BT_CTLR_ADV_ISO */
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
case NODE_RX_TYPE_EXT_1M_REPORT:
case NODE_RX_TYPE_EXT_2M_REPORT:
case NODE_RX_TYPE_EXT_CODED_REPORT:
case NODE_RX_TYPE_EXT_SCAN_TERMINATE:
#if defined(CONFIG_BT_CTLR_SYNC_PERIODIC)
case NODE_RX_TYPE_SYNC:
case NODE_RX_TYPE_SYNC_REPORT:
case NODE_RX_TYPE_SYNC_LOST:
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
case NODE_RX_TYPE_SYNC_IQ_SAMPLE_REPORT:
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
#if defined(CONFIG_BT_CTLR_SYNC_ISO)
case NODE_RX_TYPE_SYNC_ISO:
case NODE_RX_TYPE_SYNC_ISO_LOST:
#endif /* CONFIG_BT_CTLR_SYNC_ISO */
#endif /* CONFIG_BT_CTLR_SYNC_PERIODIC */
#endif /* CONFIG_BT_OBSERVER */
return HCI_CLASS_EVT_REQUIRED;
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CONN)
case NODE_RX_TYPE_CONNECTION:
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
case NODE_RX_TYPE_CIS_REQUEST:
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_CONN_ISO)
case NODE_RX_TYPE_CIS_ESTABLISHED:
#endif /* CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT:
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
return HCI_CLASS_EVT_REQUIRED;
case NODE_RX_TYPE_TERMINATE:
case NODE_RX_TYPE_CONN_UPDATE:
#if defined(CONFIG_BT_CTLR_LE_ENC)
case NODE_RX_TYPE_ENC_REFRESH:
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_CONN_RSSI_EVENT)
case NODE_RX_TYPE_RSSI:
#endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */
#if defined(CONFIG_BT_CTLR_LE_PING)
case NODE_RX_TYPE_APTO:
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_CHAN_SEL_2)
case NODE_RX_TYPE_CHAN_SEL_ALGO:
#endif /* CONFIG_BT_CTLR_CHAN_SEL_2 */
#if defined(CONFIG_BT_CTLR_PHY)
case NODE_RX_TYPE_PHY_UPDATE:
#endif /* CONFIG_BT_CTLR_PHY */
return HCI_CLASS_EVT_CONNECTION;
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
case NODE_RX_TYPE_ISO_PDU:
return HCI_CLASS_ISO_DATA;
#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT:
return HCI_CLASS_EVT_REQUIRED;
#endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */
#if CONFIG_BT_CTLR_USER_EVT_RANGE > 0
case NODE_RX_TYPE_USER_START ... NODE_RX_TYPE_USER_END - 1:
return hci_user_ext_get_class(node_rx);
#endif /* CONFIG_BT_CTLR_USER_EVT_RANGE > 0 */
default:
return HCI_CLASS_NONE;
}
#if defined(CONFIG_BT_CONN)
} else if (pdu_data->ll_id == PDU_DATA_LLID_CTRL) {
return HCI_CLASS_EVT_LLCP;
} else {
return HCI_CLASS_ACL_DATA;
}
#else
} else {
return HCI_CLASS_NONE;
}
#endif
}
void hci_init(struct k_poll_signal *signal_host_buf)
{
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
hbuf_signal = signal_host_buf;
#endif
reset(NULL, NULL);
}