diff --git a/include/bluetooth/direction.h b/include/bluetooth/direction.h index 7e55e94b8e4..d5a847e32f9 100644 --- a/include/bluetooth/direction.h +++ b/include/bluetooth/direction.h @@ -109,6 +109,17 @@ struct bt_df_per_adv_sync_iq_samples_report { struct bt_hci_le_iq_sample const *sample; }; +struct bt_df_conn_cte_rx_param { + /* Bitmap with allowed CTE types (@ref bt_df_cte_type). */ + uint8_t cte_type; + /** Antenna switching slots (@ref bt_df_antenna_switching_slot). */ + uint8_t slot_durations; + /** Length of antenna switch pattern. */ + uint8_t num_ant_ids; + /** Antenna switch pattern. */ + const uint8_t *ant_ids; +}; + /** * @brief Set or update the Constant Tone Extension parameters for periodic advertising set. * @@ -165,4 +176,23 @@ int bt_df_per_adv_sync_cte_rx_enable(struct bt_le_per_adv_sync *sync, */ int bt_df_per_adv_sync_cte_rx_disable(struct bt_le_per_adv_sync *sync); +/** + * @brief Enable receive and sampling of Constant Tone Extension for the connection object. + * + * @param conn Connection object. + * @param params CTE receive and sampling parameters. + * + * @return Zero in case of success, other value in case of failure. + */ +int bt_df_conn_cte_rx_enable(struct bt_conn *conn, const struct bt_df_conn_cte_rx_param *params); + +/** + * @brief Disable receive and sampling of Constant Tone Extension for the connection object. + * + * @param conn Connection object. + * + * @return Zero in case of success, other value in case of failure. + */ +int bt_df_conn_cte_rx_disable(struct bt_conn *conn); + #endif /* ZEPHYR_INCLUDE_BLUETOOTH_DF_H_ */ diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index ecdb37fcb6b..1a0271ed958 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -43,6 +43,8 @@ enum { * is only needed for controllers with BT_QUIRK_NO_AUTO_DLE. */ BT_CONN_AUTO_DATA_LEN_COMPLETE, + BT_CONN_CTE_RX_ENABLED, /* CTE receive and sampling is enabled */ + /* Total number of flags - must be at the end of the enum */ BT_CONN_NUM_FLAGS, }; @@ -162,6 +164,11 @@ struct bt_conn { uint8_t encrypt; #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ +#if defined(CONFIG_BT_DF_CONNECTION_CTE_RX) + /** Accepted CTE type */ + uint8_t cte_type; +#endif /* CONFIG_BT_DF_CONNECTION_CTE_RX */ + /* Connection error or reason for disconnect */ uint8_t err; diff --git a/subsys/bluetooth/host/direction.c b/subsys/bluetooth/host/direction.c index 569f98be06b..6bef50b645a 100644 --- a/subsys/bluetooth/host/direction.c +++ b/subsys/bluetooth/host/direction.c @@ -36,9 +36,9 @@ struct bt_le_df_ant_info { }; static struct bt_le_df_ant_info df_ant_info; -#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) +#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) || defined(CONFIG_BT_DF_CONNECTION_CTE_RX) const static uint8_t df_dummy_switch_pattern[BT_HCI_LE_SWITCH_PATTERN_LEN_MIN] = { 0, 0 }; -#endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX */ +#endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX || CONFIG_BT_DF_CONNECTION_CTE_RX */ #define DF_AOD_TX_1US_SUPPORT(supp) (supp & BT_HCI_LE_1US_AOD_TX) #define DF_AOD_RX_1US_SUPPORT(supp) (supp & BT_HCI_LE_1US_AOD_RX) @@ -46,15 +46,29 @@ const static uint8_t df_dummy_switch_pattern[BT_HCI_LE_SWITCH_PATTERN_LEN_MIN] = #define DF_SAMPLING_ANTENNA_NUMBER_MIN 0x2 +#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) || defined(CONFIG_BT_DF_CONNECTION_CTE_RX) +static bool validate_cte_rx_common_params(uint8_t cte_type, uint8_t slot_durations, + uint8_t num_ant_ids, const uint8_t *ant_ids); +#endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX || CONFIG_BT_DF_CONNECTION_CTE_RX */ + #if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) -static int validate_cte_rx_params(const struct bt_df_per_adv_sync_cte_rx_param *params); -static void prepare_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_per_adv_sync *sync, - const struct bt_df_per_adv_sync_cte_rx_param *params, - bool enable); +static bool validate_cl_cte_rx_params(const struct bt_df_per_adv_sync_cte_rx_param *params); +static void +prepare_cl_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_per_adv_sync *sync, + const struct bt_df_per_adv_sync_cte_rx_param *params, + bool enable); static int hci_df_set_cl_cte_rx_enable(struct bt_le_per_adv_sync *sync, bool enable, const struct bt_df_per_adv_sync_cte_rx_param *params); #endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX */ +#if defined(CONFIG_BT_DF_CONNECTION_CTE_RX) +static void prepare_conn_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_conn *conn, + const struct bt_df_conn_cte_rx_param *params, + bool enable); +static int hci_df_set_conn_cte_rx_enable(struct bt_conn *conn, bool enable, + const struct bt_df_conn_cte_rx_param *params); +#endif /* CONFIG_BT_DF_CONNECTION_CTE_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) { @@ -200,38 +214,55 @@ static int hci_df_set_adv_cte_tx_enable(struct bt_le_ext_adv *adv, buf, NULL); } -#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) -static int validate_cte_rx_params(const struct bt_df_per_adv_sync_cte_rx_param *params) +#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) || defined(CONFIG_BT_DF_CONNECTION_CTE_RX) +static bool validate_cte_rx_common_params(uint8_t cte_type, uint8_t slot_durations, + uint8_t num_ant_ids, const uint8_t *ant_ids) { - if (!(params->cte_type & - (BT_DF_CTE_TYPE_AOA | BT_DF_CTE_TYPE_AOD_1US | BT_DF_CTE_TYPE_AOD_2US))) { - return -EINVAL; + if (!(cte_type & (BT_DF_CTE_TYPE_AOA | BT_DF_CTE_TYPE_AOD_1US | BT_DF_CTE_TYPE_AOD_2US))) { + return false; + } + + if (cte_type & BT_DF_CTE_TYPE_AOA) { + if (df_ant_info.num_ant < DF_SAMPLING_ANTENNA_NUMBER_MIN || + !BT_FEAT_LE_ANT_SWITCH_RX_AOA(bt_dev.le.features)) { + return false; + } + + if (!(slot_durations == BT_HCI_LE_ANTENNA_SWITCHING_SLOT_2US || + (slot_durations == BT_HCI_LE_ANTENNA_SWITCHING_SLOT_1US && + DF_AOA_RX_1US_SUPPORT(df_ant_info.switch_sample_rates)))) { + return false; + } + + if (num_ant_ids < BT_HCI_LE_SWITCH_PATTERN_LEN_MIN || + num_ant_ids > df_ant_info.max_switch_pattern_len || !ant_ids) { + return false; + } + } + + return true; +} +#endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX || CONFIG_BT_DF_CONNECTION_CTE_RX */ + +#if defined(CONFIG_BT_DF_CONNECTIONLESS_CTE_RX) +static bool validate_cl_cte_rx_params(const struct bt_df_per_adv_sync_cte_rx_param *params) +{ + if (params->max_cte_count > BT_HCI_LE_SAMPLE_CTE_COUNT_MAX) { + return false; } if (params->cte_type & BT_DF_CTE_TYPE_AOA) { - if (df_ant_info.num_ant < DF_SAMPLING_ANTENNA_NUMBER_MIN || - !BT_FEAT_LE_ANT_SWITCH_RX_AOA(bt_dev.le.features)) { - return -EINVAL; - } - - if (!(params->slot_durations == BT_HCI_LE_ANTENNA_SWITCHING_SLOT_2US || - (params->slot_durations == BT_HCI_LE_ANTENNA_SWITCHING_SLOT_1US && - DF_AOA_RX_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 > df_ant_info.max_switch_pattern_len || !params->ant_ids) { - return -EINVAL; - } + return validate_cte_rx_common_params(params->cte_type, params->slot_durations, + params->num_ant_ids, params->ant_ids); } - return 0; + return true; } -static void prepare_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_per_adv_sync *sync, - const struct bt_df_per_adv_sync_cte_rx_param *params, - bool enable) +static void +prepare_cl_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_per_adv_sync *sync, + const struct bt_df_per_adv_sync_cte_rx_param *params, + bool enable) { struct bt_hci_cp_le_set_cl_cte_sampling_enable *cp; const uint8_t *ant_ids; @@ -253,7 +284,7 @@ static void prepare_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_p ant_ids = params->ant_ids; } else { /* Those values are put here due to constraints from HCI command - * specification: Bluetooth Core Spec. Vol 4,Part E, sec 7.8.82. + * specification: Bluetooth Core Spec. 5.3 Vol 4,Part E, sec 7.8.82. * There is no right way to successfully send the command to enable CTE * receive for AoD mode (e.g. device equipped with single antenna). */ @@ -263,7 +294,7 @@ static void prepare_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_le_p } dest_ant_ids = net_buf_add(buf, params->num_ant_ids); - memcpy(dest_ant_ids, params->ant_ids, params->num_ant_ids); + memcpy(dest_ant_ids, ant_ids, cp->switch_pattern_len); } } @@ -276,9 +307,8 @@ static int hci_df_set_cl_cte_rx_enable(struct bt_le_per_adv_sync *sync, bool ena int err; if (enable) { - err = validate_cte_rx_params(params); - if (err != 0) { - return err; + if (!validate_cl_cte_rx_params(params)) { + return -EINVAL; } } @@ -292,7 +322,7 @@ static int hci_df_set_cl_cte_rx_enable(struct bt_le_per_adv_sync *sync, bool ena return -ENOBUFS; } - prepare_cte_rx_enable_cmd_params(buf, sync, params, enable); + prepare_cl_cte_rx_enable_cmd_params(buf, sync, params, enable); bt_hci_cmd_state_set_init(buf, &state, sync->flags, BT_PER_ADV_SYNC_CTE_ENABLED, enable); @@ -430,6 +460,90 @@ static int hci_df_set_conn_cte_tx_param(struct bt_conn *conn, uint8_t cte_types, } #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ +#if defined(CONFIG_BT_DF_CONNECTION_CTE_RX) +static void prepare_conn_cte_rx_enable_cmd_params(struct net_buf *buf, struct bt_conn *conn, + const struct bt_df_conn_cte_rx_param *params, + bool enable) +{ + struct bt_hci_cp_le_set_conn_cte_rx_params *cp; + const uint8_t *ant_ids; + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(conn->handle); + cp->sampling_enable = enable ? 1 : 0; + + if (enable) { + uint8_t *dest_ant_ids; + + if (params->cte_type & BT_DF_CTE_TYPE_AOA) { + cp->slot_durations = params->slot_durations; + cp->switch_pattern_len = params->num_ant_ids; + ant_ids = params->ant_ids; + } else { + /* Those values are put here due to constraints from HCI command + * specification: Bluetooth Core Spec. 5.3 Vol 4,Part E, sec 7.8.85. + * There is no right way to successfully send the command to enable CTE + * receive for AoD mode (e.g. device equipped with single antenna). + */ + cp->slot_durations = BT_HCI_LE_ANTENNA_SWITCHING_SLOT_2US; + cp->switch_pattern_len = ARRAY_SIZE(df_dummy_switch_pattern); + ant_ids = &df_dummy_switch_pattern[0]; + } + + dest_ant_ids = net_buf_add(buf, params->num_ant_ids); + (void)memcpy(dest_ant_ids, ant_ids, cp->switch_pattern_len); + } +} + +static int hci_df_set_conn_cte_rx_enable(struct bt_conn *conn, bool enable, + const struct bt_df_conn_cte_rx_param *params) +{ + struct bt_hci_rp_le_set_conn_cte_rx_params *rp; + struct bt_hci_cmd_state_set state; + struct net_buf *buf, *rsp; + int err; + + if (enable) { + if (!validate_cte_rx_common_params(params->cte_type, params->slot_durations, + params->num_ant_ids, params->ant_ids)) { + return -EINVAL; + } + } + + /* If CTE Rx is enabled, command parameters total length must include + * antenna ids, so command size if extended by num_and_ids. + */ + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CONN_CTE_RX_PARAMS, + (sizeof(struct bt_hci_rp_le_set_conn_cte_rx_params) + + (enable ? params->num_ant_ids : 0))); + if (!buf) { + return -ENOBUFS; + } + + prepare_conn_cte_rx_enable_cmd_params(buf, conn, params, enable); + + bt_hci_cmd_state_set_init(buf, &state, conn->flags, BT_CONN_CTE_RX_ENABLED, enable); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CONN_CTE_RX_PARAMS, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + if (conn->handle != sys_le16_to_cpu(rp->handle)) { + err = -EIO; + } else { + conn->cte_type = (enable ? params->cte_type : 0); + } + + net_buf_unref(rsp); + + return err; +} +#endif /* CONFIG_BT_DF_CONNECTION_CTE_RX */ + /* @brief Function initializes Direction Finding in Host * * @return Zero in case of success, other value in case of failure. @@ -562,3 +676,45 @@ int bt_df_per_adv_sync_cte_rx_disable(struct bt_le_per_adv_sync *sync) return bt_df_set_per_adv_sync_cte_rx_enable(sync, false, NULL); } #endif /* CONFIG_BT_DF_CONNECTIONLESS_CTE_RX */ + +#if defined(CONFIG_BT_DF_CONNECTION_CTE_RX) +static int bt_df_set_conn_cte_rx_enable(struct bt_conn *conn, bool enable, + const struct bt_df_conn_cte_rx_param *params) +{ + if (!BT_FEAT_LE_RX_CTE(bt_dev.le.features)) { + BT_WARN("Receiving Constant Tone Extensions is not supported"); + return -ENOTSUP; + } + + if (conn->state != BT_CONN_CONNECTED) { + BT_ERR("not connected!"); + return -ENOTCONN; + } + + return hci_df_set_conn_cte_rx_enable(conn, enable, params); +} + +int bt_df_conn_cte_rx_enable(struct bt_conn *conn, const struct bt_df_conn_cte_rx_param *params) +{ + CHECKIF(!conn) + { + return -EINVAL; + } + CHECKIF(!params) + { + return -EINVAL; + } + + return bt_df_set_conn_cte_rx_enable(conn, true, params); +} + +int bt_df_conn_cte_rx_disable(struct bt_conn *conn) +{ + CHECKIF(!conn) + { + return -EINVAL; + } + + return bt_df_set_conn_cte_rx_enable(conn, false, NULL); +} +#endif /* CONFIG_BT_DF_CONNECTION_CTE_RX */ diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 609c87d1a72..25ee6fcbf45 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2964,7 +2964,8 @@ static int le_init(void) #if IS_ENABLED(CONFIG_BT_DF) if (BT_FEAT_LE_CONNECTIONLESS_CTE_TX(bt_dev.le.features) || - BT_FEAT_LE_CONNECTIONLESS_CTE_RX(bt_dev.le.features)) { + BT_FEAT_LE_CONNECTIONLESS_CTE_RX(bt_dev.le.features) || + BT_FEAT_LE_RX_CTE(bt_dev.le.features)) { err = le_df_init(); if (err) { return err;