Add public function to set Constant Tone Extension transmission enabled or disabled for periodic advertising. Signed-off-by: Piotr Pryga <piotr.pryga@nordicsemi.no>
350 lines
9.1 KiB
C
350 lines
9.1 KiB
C
/*
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <sys/byteorder.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/conn.h>
|
|
#include <bluetooth/l2cap.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/direction.h>
|
|
|
|
#include "hci_core.h"
|
|
#include "conn_internal.h"
|
|
#include "direction_internal.h"
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_DF)
|
|
#define LOG_MODULE_NAME bt_df
|
|
#include "common/log.h"
|
|
|
|
/* @brief Antenna information for LE Direction Finding */
|
|
struct bt_le_df_ant_info {
|
|
/* Bitfield holding optional switching and sampling rates */
|
|
uint8_t switch_sample_rates;
|
|
/* Available antennae number */
|
|
uint8_t num_ant;
|
|
/* Maximum supported antennae switching pattern length */
|
|
uint8_t max_switch_pattern_len;
|
|
/* Maximum length of CTE in 8[us] units */
|
|
uint8_t max_cte_len;
|
|
};
|
|
|
|
static struct bt_le_df_ant_info df_ant_info;
|
|
|
|
#define DF_SUPP_TEST(feat, n) ((feat) & BIT((n)))
|
|
|
|
#define DF_AOD_TX_1US_SUPPORT(supp) (DF_SUPP_TEST(supp, \
|
|
BT_HCI_LE_1US_AOD_TX))
|
|
#define DF_AOD_RX_1US_SUPPORT(supp) (DF_SUPP_TEST(supp, \
|
|
BT_HCI_LE_1US_AOD_RX))
|
|
#define DF_AOA_RX_1US_SUPPORT(supp) (DF_SUPP_TEST(supp, \
|
|
BT_HCI_LE_1US_AOA_RX))
|
|
|
|
static int hci_df_set_cl_cte_tx_params(const struct bt_le_ext_adv *adv,
|
|
const struct bt_df_adv_cte_tx_param *params)
|
|
{
|
|
struct bt_hci_cp_le_set_cl_cte_tx_params *cp;
|
|
struct net_buf *buf;
|
|
|
|
/* If AoD is not enabled, ant_ids are ignored by controller:
|
|
* BT Core spec 5.2 Vol 4, Part E sec. 7.8.80.
|
|
*/
|
|
if (params->cte_type == BT_HCI_LE_AOD_CTE_1US ||
|
|
params->cte_type == BT_HCI_LE_AOD_CTE_2US) {
|
|
|
|
if (!BT_FEAT_LE_ANT_SWITCH_TX_AOD(bt_dev.le.features)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (params->cte_type == BT_HCI_LE_AOD_CTE_1US &&
|
|
!DF_AOD_TX_1US_SUPPORT(df_ant_info.switch_sample_rates)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (params->num_ant_ids < BT_HCI_LE_SWITCH_PATTERN_LEN_MIN ||
|
|
params->num_ant_ids > BT_HCI_LE_SWITCH_PATTERN_LEN_MAX ||
|
|
!params->ant_ids) {
|
|
return -EINVAL;
|
|
}
|
|
} else if (params->cte_type != BT_HCI_LE_AOA_CTE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (params->cte_len < BT_HCI_LE_CTE_LEN_MIN ||
|
|
params->cte_len > BT_HCI_LE_CTE_LEN_MAX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (params->cte_count < BT_HCI_LE_CTE_COUNT_MIN ||
|
|
params->cte_count > BT_HCI_LE_CTE_COUNT_MAX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CL_CTE_TX_PARAMS,
|
|
sizeof(*cp) + params->num_ant_ids);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = adv->handle;
|
|
cp->cte_len = params->cte_len;
|
|
cp->cte_type = params->cte_type;
|
|
cp->cte_count = params->cte_count;
|
|
|
|
if (params->num_ant_ids) {
|
|
uint8_t *dest_ant_ids = net_buf_add(buf, params->num_ant_ids);
|
|
|
|
memcpy(dest_ant_ids, params->ant_ids, params->num_ant_ids);
|
|
cp->switch_pattern_len = params->num_ant_ids;
|
|
} else {
|
|
cp->switch_pattern_len = 0;
|
|
}
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CL_CTE_TX_PARAMS,
|
|
buf, NULL);
|
|
}
|
|
|
|
/* @brief Function provides information about DF antennae numer and
|
|
* controller capabilities related with Constant Tone Extension.
|
|
*
|
|
* @param[out] switch_sample_rates Optional switching and sampling rates.
|
|
* @param[out] num_ant Antennae number.
|
|
* @param[out] max_switch_pattern_len Maximum supported antennae switching
|
|
* paterns length.
|
|
* @param[out] max_cte_len Maximum length of CTE in 8[us] units.
|
|
*
|
|
* @return Zero in case of success, other value in case of failure.
|
|
*/
|
|
static int hci_df_read_ant_info(uint8_t *switch_sample_rates,
|
|
uint8_t *num_ant,
|
|
uint8_t *max_switch_pattern_len,
|
|
uint8_t *max_cte_len)
|
|
{
|
|
__ASSERT_NO_MSG(switch_sample_rates);
|
|
__ASSERT_NO_MSG(num_ant);
|
|
__ASSERT_NO_MSG(max_switch_pattern_len);
|
|
__ASSERT_NO_MSG(max_cte_len);
|
|
|
|
struct bt_hci_rp_le_read_ant_info *rp;
|
|
struct net_buf *rsp;
|
|
int err;
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_ANT_INFO, NULL, &rsp);
|
|
if (err) {
|
|
BT_ERR("Failed to read antenna information");
|
|
return err;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
|
|
BT_DBG("DF: sw. sampl rates: %x ant num: %u , max sw. pattern len: %u,"
|
|
"max CTE len %d", rp->switch_sample_rates, rp->num_ant,
|
|
rp->max_switch_pattern_len, rp->max_cte_len);
|
|
|
|
*switch_sample_rates = rp->switch_sample_rates;
|
|
*num_ant = rp->num_ant;
|
|
*max_switch_pattern_len = rp->max_switch_pattern_len;
|
|
*max_cte_len = rp->max_cte_len;
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* @brief Function handles send of HCI commnad to enable or disables CTE
|
|
* transmission for given advertising set.
|
|
*
|
|
* @param[in] adv Pointer to advertising set
|
|
* @param[in] enable Enable or disable CTE TX
|
|
*
|
|
* @return Zero in case of success, other value in case of failure.
|
|
*/
|
|
static int hci_df_set_adv_cte_tx_enable(struct bt_le_ext_adv *adv,
|
|
bool enable)
|
|
{
|
|
struct bt_hci_cp_le_set_cl_cte_tx_enable *cp;
|
|
struct bt_hci_cmd_state_set state;
|
|
struct net_buf *buf;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CL_CTE_TX_ENABLE, sizeof(*cp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
(void)memset(cp, 0, sizeof(*cp));
|
|
|
|
cp->handle = adv->handle;
|
|
cp->cte_enable = enable ? 1 : 0;
|
|
|
|
bt_hci_cmd_state_set_init(&state, adv->flags, BT_PER_ADV_CTE_ENABLED,
|
|
enable);
|
|
bt_hci_cmd_data_state_set(buf, &state);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CL_CTE_TX_ENABLE,
|
|
buf, NULL);
|
|
}
|
|
|
|
/* @brief Function sets CTE parameters for connection object
|
|
*
|
|
* @param[in] cte_types Allowed response CTE types
|
|
* @param[in] num_ant_id Number of available antenna identification
|
|
* patterns in @p ant_id array.
|
|
* @param[in] ant_id Array with antenna identification patterns.
|
|
*
|
|
* @return Zero in case of success, other value in case of failure.
|
|
*/
|
|
static int hci_df_set_conn_cte_tx_param(struct bt_conn *conn, uint8_t cte_types,
|
|
uint8_t num_ant_id, uint8_t *ant_id)
|
|
{
|
|
__ASSERT_NO_MSG(conn);
|
|
__ASSERT_NO_MSG(cte_types != 0);
|
|
|
|
struct bt_hci_cp_le_set_conn_cte_tx_params *cp;
|
|
struct bt_hci_rp_le_set_conn_cte_tx_params *rp;
|
|
struct net_buf *buf, *rsp;
|
|
int err;
|
|
|
|
/* If AoD is not enabled, ant_ids are ignored by controller:
|
|
* BT Core spec 5.2 Vol 4, Part E sec. 7.8.84.
|
|
*/
|
|
if (cte_types & BT_HCI_LE_AOD_CTE_RSP_1US ||
|
|
cte_types & BT_HCI_LE_AOD_CTE_RSP_2US) {
|
|
|
|
if (num_ant_id < BT_HCI_LE_SWITCH_PATTERN_LEN_MIN ||
|
|
num_ant_id > BT_HCI_LE_SWITCH_PATTERN_LEN_MAX ||
|
|
!ant_id) {
|
|
return -EINVAL;
|
|
}
|
|
__ASSERT_NO_MSG((sizeof(*cp) + num_ant_id) < UINT8_MAX);
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CONN_CTE_TX_PARAMS,
|
|
sizeof(*cp) + num_ant_id);
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = net_buf_add(buf, sizeof(*cp));
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
cp->cte_types = cte_types;
|
|
|
|
if (num_ant_id) {
|
|
uint8_t *dest_ant_id = net_buf_add(buf, num_ant_id);
|
|
|
|
memcpy(dest_ant_id, ant_id, num_ant_id);
|
|
cp->switch_pattern_len = num_ant_id;
|
|
} else {
|
|
cp->switch_pattern_len = 0;
|
|
}
|
|
|
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CONN_CTE_TX_PARAMS,
|
|
buf, &rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
rp = (void *)rsp->data;
|
|
if (conn->handle != sys_le16_to_cpu(rp->handle)) {
|
|
err = -EIO;
|
|
}
|
|
|
|
net_buf_unref(rsp);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* @brief Function initializes Direction Finding in Host
|
|
*
|
|
* @return Zero in case of success, other value in case of failure.
|
|
*/
|
|
int le_df_init(void)
|
|
{
|
|
uint8_t max_switch_pattern_len;
|
|
uint8_t switch_sample_rates;
|
|
uint8_t max_cte_len;
|
|
uint8_t num_ant;
|
|
int err;
|
|
|
|
err = hci_df_read_ant_info(&switch_sample_rates, &num_ant,
|
|
&max_switch_pattern_len, &max_cte_len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
df_ant_info.max_switch_pattern_len = max_switch_pattern_len;
|
|
df_ant_info.switch_sample_rates = switch_sample_rates;
|
|
df_ant_info.max_cte_len = max_cte_len;
|
|
df_ant_info.num_ant = num_ant;
|
|
|
|
BT_DBG("DF initialized.");
|
|
return 0;
|
|
}
|
|
|
|
int bt_df_set_adv_cte_tx_param(struct bt_le_ext_adv *adv,
|
|
const struct bt_df_adv_cte_tx_param *params)
|
|
{
|
|
__ASSERT_NO_MSG(adv);
|
|
__ASSERT_NO_MSG(params);
|
|
|
|
int err;
|
|
|
|
if (!BT_FEAT_LE_CONNECTIONLESS_CTE_TX(bt_dev.le.features)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Check if BT_ADV_PARAMS_SET is set, because it implies the set
|
|
* has already been created.
|
|
*/
|
|
if (!atomic_test_bit(adv->flags, BT_ADV_PARAMS_SET)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_test_bit(adv->flags, BT_PER_ADV_CTE_ENABLED)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = hci_df_set_cl_cte_tx_params(adv, params);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(adv->flags, BT_PER_ADV_CTE_PARAMS_SET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bt_df_set_adv_cte_tx_enabled(struct bt_le_ext_adv *adv, bool enable)
|
|
{
|
|
if (!atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_bit(adv->flags, BT_PER_ADV_CTE_PARAMS_SET)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (enable == atomic_test_bit(adv->flags, BT_PER_ADV_CTE_ENABLED)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
return hci_df_set_adv_cte_tx_enable(adv, enable);
|
|
}
|
|
|
|
int bt_df_adv_cte_tx_enable(struct bt_le_ext_adv *adv)
|
|
{
|
|
__ASSERT_NO_MSG(adv);
|
|
return bt_df_set_adv_cte_tx_enabled(adv, true);
|
|
}
|
|
|
|
int bt_df_adv_cte_tx_disable(struct bt_le_ext_adv *adv)
|
|
{
|
|
__ASSERT_NO_MSG(adv);
|
|
return bt_df_set_adv_cte_tx_enabled(adv, false);
|
|
}
|