blob: 713c96562109ddca554b0a439096f64bec3ce286 [file] [log] [blame]
/*
* 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"
#define STR_NULL_TERMINATOR 0x00
/* 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;
#if defined(CONFIG_BT_HCI_VS_EVT)
static uint64_t vs_events_mask = DEFAULT_VS_EVT_MASK;
#endif /* CONFIG_BT_HCI_VS_EVT */
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_VS_EVT)
static void *vs_event(struct net_buf *buf, uint8_t subevt, uint8_t evt_len)
{
struct bt_hci_evt_vs *evt;
hci_evt_create(buf, BT_HCI_EVT_VENDOR, sizeof(*evt) + evt_len);
evt = net_buf_add(buf, sizeof(*evt));
evt->subevent = subevt;
return net_buf_add(buf, evt_len);
}
#endif /* CONFIG_BT_HCI_VS_EVT */
#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);
}
#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
hci_recv_fifo_reset();
}
#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_HCI_CODEC_AND_DELAY_INFO)
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_HCI_CODEC_AND_DELAY_INFO */
#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_HCI_CODEC_AND_DELAY_INFO)
case BT_OCF(BT_HCI_OP_CONFIGURE_DATA_PATH):
configure_data_path(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_HCI_CODEC_AND_DELAY_INFO */
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);
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_TX)
/* LE Set Connection CTE Transmit Parameters */
rp->commands[40] |= BIT(1);
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_TX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
/* LE Set Connection CTE Receive Parameters */
rp->commands[40] |= BIT(0);
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/* LE Connection CTE Request Enable */
rp->commands[40] |= BIT(2);
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
/* LE Connection CTE Response Enable */
rp->commands[40] |= BIT(3);
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
#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, Configure Data Path */
rp->commands[45] |= BIT(2) | BIT(3) | BIT(4) | BIT(5);
#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);
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) || \
defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
static int8_t iq_convert_12_to_8_bits(int16_t data)
{
if (data == IQ_SAMPLE_SATURATED_16_BIT) {
return IQ_SAMPLE_SATURATED_8_BIT;
}
#if defined(CONFIG_BT_CTLR_DF_IQ_SAMPLES_CONVERT_USE_8_LSB)
return (data > INT8_MAX || data < INT8_MIN) ? IQ_SAMPLE_SATURATED_8_BIT
: IQ_SAMPLE_CONVERT_12_TO_8_BIT(data);
#else /* !CONFIG_BT_CTLR_DF_IQ_SAMPLES_CONVERT_USE_8_LSB */
int16_t data_conv = IQ_SAMPLE_CONVERT_12_TO_8_BIT(data);
return (data_conv > INT8_MAX || data_conv < INT8_MIN) ? IQ_SAMPLE_SATURATED_8_BIT
: (int8_t)data_conv;
#endif /* CONFIG_BT_CTLR_DF_IQ_SAMPLES_CONVERT_USE_8_LSB */
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX || CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT
* || CONFIG_BT_CTLR_DF_CONN_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 packet status does not indicate insufficient resources for IQ samples and for
* some reason sample_count is zero, inform Host about lack of valid IQ samples by
* storing single I_sample and Q_sample with BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE value.
*/
if (iq_report->packet_status == BT_HCI_LE_CTE_INSUFFICIENT_RESOURCES) {
samples_cnt = 0U;
} else {
samples_cnt = MAX(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) {
if (iq_report->sample_count == 0U) {
sep->sample[0].i = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
sep->sample[0].q = BT_HCI_LE_CTE_REPORT_NO_VALID_SAMPLE;
} else {
for (uint8_t idx = 0U; idx < samples_cnt; ++idx) {
sep->sample[idx].i =
iq_convert_12_to_8_bits(iq_report->sample[idx].i);
sep->sample[idx].q =
iq_convert_12_to_8_bits(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