diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index fa741715504..6ddc9835ceb 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -262,6 +262,157 @@ enum __packed bt_conn_type { BT_CONN_TYPE_SCO | BT_CONN_TYPE_ISO, }; +/** Supported AA-Only RTT precision. */ +enum bt_conn_le_cs_capability_rtt_aa_only { + /** AA-Only RTT variant is not supported. */ + BT_CONN_LE_CS_RTT_AA_ONLY_NOT_SUPP = 0, + /** 10ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_AA_ONLY_10NS, + /** 150ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_AA_ONLY_150NS, +}; + +/** Supported Sounding Sequence RTT precision. */ +enum bt_conn_le_cs_capability_rtt_sounding { + /** Sounding Sequence RTT variant is not supported. */ + BT_CONN_LE_CS_RTT_SOUNDING_NOT_SUPP = 0, + /** 10ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_SOUNDING_10NS, + /** 150ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_SOUNDING_150NS, +}; + +/** Supported Random Payload RTT precision. */ +enum bt_conn_le_cs_capability_rtt_random_payload { + /** Random Payload RTT variant is not supported. */ + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_NOT_SUPP = 0, + /** 10ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS, + /** 150ns time-of-flight accuracy. */ + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_150NS, +}; + +/** Remote channel sounding capabilities for LE connections supporting CS */ +struct bt_conn_le_cs_capabilities { + /** Number of CS configurations */ + uint8_t num_config_supported; + /** Maximum number of consecutive CS procedures. + * + * When set to zero, indicates support for both fixed and indefinite + * numbers of CS procedures before termination. + */ + uint16_t max_consecutive_procedures_supported; + /** Number of antennas. */ + uint8_t num_antennas_supported; + /** Maximum number of antenna paths. */ + uint8_t max_antenna_paths_supported; + /** Initiator role. */ + bool initiator_supported; + /** Reflector role. */ + bool reflector_supported; + /** Mode-3 */ + bool mode_3_supported; + /** RTT AA-Only */ + enum bt_conn_le_cs_capability_rtt_aa_only rtt_aa_only_precision; + /** RTT Sounding */ + enum bt_conn_le_cs_capability_rtt_sounding rtt_sounding_precision; + /** RTT Random Payload */ + enum bt_conn_le_cs_capability_rtt_random_payload rtt_random_payload_precision; + /** Number of CS steps needed to achieve the + * accuracy requirements for RTT AA Only. + * + * Set to 0 if RTT AA Only isn't supported. + */ + uint8_t rtt_aa_only_n; + /** Number of CS steps needed to achieve the + * accuracy requirements for RTT Sounding. + * + * Set to 0 if RTT Sounding isn't supported + */ + uint8_t rtt_sounding_n; + /** Number of CS steps needed to achieve the + * accuracy requirements for RTT Random Payload. + * + * Set to 0 if RTT Random Payload isn't supported. + */ + uint8_t rtt_random_payload_n; + /** Phase-based normalized attack detector metric + * when a CS_SYNC with sounding sequence is received. + */ + bool phase_based_nadm_sounding_supported; + /** Phase-based normalized attack detector metric + * when a CS_SYNC with random sequence is received. + */ + bool phase_based_nadm_random_supported; + /** CS_SYNC LE 2M PHY. */ + bool cs_sync_2m_phy_supported; + /** CS_SYNC LE 2M 2BT PHY. */ + bool cs_sync_2m_2bt_phy_supported; + /** Subfeature: CS with no Frequency Actuation Error. */ + bool cs_without_fae_supported; + /** Subfeature: Channel Selection Algorithm #3c */ + bool chsel_alg_3c_supported; + /** Subfeature: Phase-based Ranging from RTT sounding sequence. */ + bool pbr_from_rtt_sounding_seq_supported; + /** Optional T_IP1 time durations during CS steps. + * + * - Bit 0: 10 us + * - Bit 1: 20 us + * - Bit 2: 30 us + * - Bit 3: 40 us + * - Bit 4: 50 us + * - Bit 5: 60 us + * - Bit 6: 80 us + */ + uint16_t t_ip1_times_supported; + /** Optional T_IP2 time durations during CS steps. + * + * - Bit 0: 10 us + * - Bit 1: 20 us + * - Bit 2: 30 us + * - Bit 3: 40 us + * - Bit 4: 50 us + * - Bit 5: 60 us + * - Bit 6: 80 us + */ + uint16_t t_ip2_times_supported; + /** Optional T_FCS time durations during CS steps. + * + * - Bit 0: 15 us + * - Bit 1: 20 us + * - Bit 2: 30 us + * - Bit 3: 40 us + * - Bit 4: 50 us + * - Bit 5: 60 us + * - Bit 6: 80 us + * - Bit 7: 100 us + * - Bit 8: 120 us + */ + uint16_t t_fcs_times_supported; + /** Optional T_PM time durations during CS steps. + * + * - Bit 0: 10 us + * - Bit 1: 20 us + */ + uint16_t t_pm_times_supported; + /** Time in microseconds for the antenna switch period of the CS tones. */ + uint8_t t_sw_time; + /** Supported SNR levels used in RTT packets. + * + * - Bit 0: 18dB + * - Bit 1: 21dB + * - Bit 2: 24dB + * - Bit 3: 27dB + * - Bit 4: 30dB + */ + uint8_t tx_snr_capability; +}; + +/** Remote FAE Table for LE connections supporting CS */ +struct bt_conn_le_cs_fae_table { + uint8_t *remote_fae_table; +}; + /** @brief Increment a connection's reference count. * * Increment the reference count of a connection object. @@ -1359,6 +1510,30 @@ struct bt_conn_cb { const struct bt_conn_le_subrate_changed *params); #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_CHANNEL_SOUNDING) + /** @brief LE CS Read Remote Supported Capabilities Complete event. + * + * This callback notifies the application that the remote channel + * sounding capabilities have been received from the peer. + * + * @param conn Connection object. + * @param remote_cs_capabilities Remote Channel Sounding Capabilities. + */ + void (*remote_cs_capabilities_available)(struct bt_conn *conn, + struct bt_conn_le_cs_capabilities *params); + + /** @brief LE CS Read Remote FAE Table Complete event. + * + * This callback notifies the application that the remote mode-0 + * FAE Table has been received from the peer. + * + * @param conn Connection object. + * @param params FAE Table. + */ + void (*remote_cs_fae_table_available)(struct bt_conn *conn, + struct bt_conn_le_cs_fae_table *params); +#endif + /** @internal Internally used field for list handling */ sys_snode_t _node; }; diff --git a/include/zephyr/bluetooth/cs.h b/include/zephyr/bluetooth/cs.h index 903f32eccb0..880cbeeeccc 100644 --- a/include/zephyr/bluetooth/cs.h +++ b/include/zephyr/bluetooth/cs.h @@ -60,6 +60,20 @@ struct bt_cs_set_default_settings_param { int8_t max_tx_power; }; + +/** @brief Read Remote Supported Capabilities + * + * This command is used to query the CS capabilities that are supported + * by the remote controller. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_cs_read_remote_supported_capabilities(struct bt_conn *conn); + /** @brief Set Channel Sounding default settings. * * This command is used to set default Channel Sounding settings for this @@ -75,6 +89,19 @@ struct bt_cs_set_default_settings_param { int bt_cs_set_default_settings(struct bt_conn *conn, const struct bt_cs_set_default_settings_param *params); +/** @brief Read Remote FAE Table + * + * This command is used to read the per-channel mode-0 Frequency Actuation Error + * table of the remote Controller. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_cs_read_remote_fae_table(struct bt_conn *conn); + #ifdef __cplusplus } #endif diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index afdb03f0ce9..48d7196c9e6 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -201,6 +201,9 @@ struct bt_hci_cmd_hdr { #define BT_LE_FEAT_BIT_PAWR_ADVERTISER 43 #define BT_LE_FEAT_BIT_PAWR_SCANNER 44 +#define BT_LE_FEAT_BIT_CHANNEL_SOUNDING 46 +#define BT_LE_FEAT_BIT_CHANNEL_SOUNDING_HOST 47 + #define BT_LE_FEAT_TEST(feat, n) (feat[(n) >> 3] & \ BIT((n) & 7)) @@ -268,6 +271,10 @@ struct bt_hci_cmd_hdr { BT_LE_FEAT_BIT_PAWR_ADVERTISER) #define BT_FEAT_LE_PAWR_SCANNER(feat) BT_LE_FEAT_TEST(feat, \ BT_LE_FEAT_BIT_PAWR_SCANNER) +#define BT_FEAT_LE_CHANNEL_SOUNDING(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CHANNEL_SOUNDING) +#define BT_FEAT_LE_CHANNEL_SOUNDING_HOST(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CHANNEL_SOUNDING_HOST) #define BT_FEAT_LE_CIS(feat) (BT_FEAT_LE_CIS_CENTRAL(feat) | \ BT_FEAT_LE_CIS_PERIPHERAL(feat)) @@ -2395,6 +2402,12 @@ struct bt_hci_cp_le_tx_test_v4_tx_power { int8_t tx_power; } __packed; +#define BT_HCI_OP_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES BT_OP(BT_OGF_LE, 0x008A) /* 0x208A */ + +struct bt_hci_cp_le_read_remote_supported_capabilities { + uint16_t handle; +} __packed; + #define BT_HCI_OP_LE_CS_SET_DEFAULT_SETTINGS BT_OP(BT_OGF_LE, 0x008D) /* 0x208D */ #define BT_HCI_OP_LE_CS_INITIATOR_ROLE_MASK BIT(0) @@ -2417,6 +2430,12 @@ struct bt_hci_cp_le_cs_set_default_settings { int8_t max_tx_power; } __packed; +#define BT_HCI_OP_LE_CS_READ_REMOTE_FAE_TABLE BT_OP(BT_OGF_LE, 0x008E) /* 0x208E */ + +struct bt_hci_cp_le_read_remote_fae_table { + uint16_t handle; +} __packed; + /* Event definitions */ #define BT_HCI_EVT_UNKNOWN 0x00 @@ -3151,6 +3170,93 @@ struct bt_hci_evt_le_subrate_change { uint16_t supervision_timeout; } __packed; +#define BT_HCI_LE_CS_INITIATOR_ROLE_MASK BIT(0) +#define BT_HCI_LE_CS_REFLECTOR_ROLE_MASK BIT(1) + +#define BT_HCI_LE_CS_MODES_SUPPORTED_MODE_3_MASK BIT(0) + +#define BT_HCI_LE_CS_RTT_AA_ONLY_N_10NS_MASK BIT(0) +#define BT_HCI_LE_CS_RTT_SOUNDING_N_10NS_MASK BIT(1) +#define BT_HCI_LE_CS_RTT_RANDOM_PAYLOAD_N_10NS_MASK BIT(2) + +#define BT_HCI_LE_CS_NADM_SOUNDING_CAPABILITY_PHASE_BASED_MASK BIT(0) +#define BT_HCI_LE_CS_NADM_RANDOM_CAPABILITY_PHASE_BASED_MASK BIT(0) + +#define BT_HCI_LE_CS_SYNC_PHYS_2M_MASK BIT(1) +#define BT_HCI_LE_CS_SYNC_PHYS_2M_2BT_MASK BIT(2) + +#define BT_HCI_LE_CS_SUBFEATURE_NO_TX_FAE_MASK BIT(1) +#define BT_HCI_LE_CS_SUBFEATURE_CHSEL_ALG_3C_MASK BIT(2) +#define BT_HCI_LE_CS_SUBFEATURE_PBR_FROM_RTT_SOUNDING_SEQ_MASK BIT(3) + +#define BT_HCI_LE_CS_T_IP1_TIME_10US_MASK BIT(0) +#define BT_HCI_LE_CS_T_IP1_TIME_20US_MASK BIT(1) +#define BT_HCI_LE_CS_T_IP1_TIME_30US_MASK BIT(2) +#define BT_HCI_LE_CS_T_IP1_TIME_40US_MASK BIT(3) +#define BT_HCI_LE_CS_T_IP1_TIME_50US_MASK BIT(4) +#define BT_HCI_LE_CS_T_IP1_TIME_60US_MASK BIT(5) +#define BT_HCI_LE_CS_T_IP1_TIME_80US_MASK BIT(6) + +#define BT_HCI_LE_CS_T_IP2_TIME_10US_MASK BIT(0) +#define BT_HCI_LE_CS_T_IP2_TIME_20US_MASK BIT(1) +#define BT_HCI_LE_CS_T_IP2_TIME_30US_MASK BIT(2) +#define BT_HCI_LE_CS_T_IP2_TIME_40US_MASK BIT(3) +#define BT_HCI_LE_CS_T_IP2_TIME_50US_MASK BIT(4) +#define BT_HCI_LE_CS_T_IP2_TIME_60US_MASK BIT(5) +#define BT_HCI_LE_CS_T_IP2_TIME_80US_MASK BIT(6) + +#define BT_HCI_LE_CS_T_FCS_TIME_15US_MASK BIT(0) +#define BT_HCI_LE_CS_T_FCS_TIME_20US_MASK BIT(1) +#define BT_HCI_LE_CS_T_FCS_TIME_30US_MASK BIT(2) +#define BT_HCI_LE_CS_T_FCS_TIME_40US_MASK BIT(3) +#define BT_HCI_LE_CS_T_FCS_TIME_50US_MASK BIT(4) +#define BT_HCI_LE_CS_T_FCS_TIME_60US_MASK BIT(5) +#define BT_HCI_LE_CS_T_FCS_TIME_80US_MASK BIT(6) +#define BT_HCI_LE_CS_T_FCS_TIME_100US_MASK BIT(7) +#define BT_HCI_LE_CS_T_FCS_TIME_1200US_MASK BIT(8) + +#define BT_HCI_LE_CS_T_PM_TIME_10US_MASK BIT(0) +#define BT_HCI_LE_CS_T_PM_TIME_20US_MASK BIT(1) + +#define BT_HCI_LE_CS_TX_SNR_CAPABILITY_18DB_MASK BIT(0) +#define BT_HCI_LE_CS_TX_SNR_CAPABILITY_21DB_MASK BIT(1) +#define BT_HCI_LE_CS_TX_SNR_CAPABILITY_24DB_MASK BIT(2) +#define BT_HCI_LE_CS_TX_SNR_CAPABILITY_27DB_MASK BIT(3) +#define BT_HCI_LE_CS_TX_SNR_CAPABILITY_30DB_MASK BIT(4) + +#define BT_HCI_EVT_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE 0x2C +struct bt_hci_evt_le_cs_read_remote_supported_capabilities_complete { + uint8_t status; + uint16_t conn_handle; + uint8_t num_config_supported; + uint16_t max_consecutive_procedures_supported; + uint8_t num_antennas_supported; + uint8_t max_antenna_paths_supported; + uint8_t roles_supported; + uint8_t modes_supported; + uint8_t rtt_capability; + uint8_t rtt_aa_only_n; + uint8_t rtt_sounding_n; + uint8_t rtt_random_payload_n; + uint16_t nadm_sounding_capability; + uint16_t nadm_random_capability; + uint8_t cs_sync_phys_supported; + uint16_t subfeatures_supported; + uint16_t t_ip1_times_supported; + uint16_t t_ip2_times_supported; + uint16_t t_fcs_times_supported; + uint16_t t_pm_times_supported; + uint8_t t_sw_time_supported; + uint8_t tx_snr_capability; +} __packed; + +#define BT_HCI_EVT_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE 0x2D +struct bt_hci_evt_le_cs_read_remote_fae_table_complete { + uint8_t status; + uint16_t conn_handle; + uint8_t remote_fae_table[72]; +} __packed; + /* Event mask bits */ #define BT_EVT_BIT(n) (1ULL << (n)) @@ -3240,6 +3346,9 @@ struct bt_hci_evt_le_subrate_change { #define BT_EVT_MASK_LE_PER_ADV_RESPONSE_REPORT BT_EVT_BIT(39) #define BT_EVT_MASK_LE_ENH_CONN_COMPLETE_V2 BT_EVT_BIT(40) +#define BT_EVT_MASK_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE BT_EVT_BIT(43) +#define BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE BT_EVT_BIT(44) + /** HCI Error Codes, BT Core Spec v5.4 [Vol 1, Part F]. */ #define BT_HCI_ERR_SUCCESS 0x00 #define BT_HCI_ERR_UNKNOWN_CMD 0x01 diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 0aa9fb21e87..9f9c0441dff 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -3330,6 +3330,42 @@ int bt_conn_le_subrate_request(struct bt_conn *conn, } #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_CHANNEL_SOUNDING) +void notify_remote_cs_capabilities(struct bt_conn *conn, struct bt_conn_le_cs_capabilities params) +{ + struct bt_conn_cb *callback; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) { + if (callback->remote_cs_capabilities_available) { + callback->remote_cs_capabilities_available(conn, ¶ms); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { + if (cb->remote_cs_capabilities_available) { + cb->remote_cs_capabilities_available(conn, ¶ms); + } + } +} + +void notify_remote_cs_fae_table(struct bt_conn *conn, struct bt_conn_le_cs_fae_table params) +{ + struct bt_conn_cb *callback; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) { + if (callback->remote_cs_fae_table_available) { + callback->remote_cs_fae_table_available(conn, ¶ms); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { + if (cb->remote_cs_fae_table_available) { + cb->remote_cs_fae_table_available(conn, ¶ms); + } + } +} +#endif /* CONFIG_BT_CHANNEL_SOUNDING */ + int bt_conn_le_param_update(struct bt_conn *conn, const struct bt_le_conn_param *param) { diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 58228588d0c..4cc4e70d858 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -492,6 +492,12 @@ void notify_path_loss_threshold_report(struct bt_conn *conn, void notify_subrate_change(struct bt_conn *conn, struct bt_conn_le_subrate_changed params); +void notify_remote_cs_capabilities(struct bt_conn *conn, + struct bt_conn_le_cs_capabilities params); + +void notify_remote_cs_fae_table(struct bt_conn *conn, + struct bt_conn_le_cs_fae_table params); + #if defined(CONFIG_BT_SMP) /* If role specific LTK is present */ bool bt_conn_ltk_present(const struct bt_conn *conn); diff --git a/subsys/bluetooth/host/cs.c b/subsys/bluetooth/host/cs.c index 4ec67fd42ce..78b42dc399e 100644 --- a/subsys/bluetooth/host/cs.c +++ b/subsys/bluetooth/host/cs.c @@ -13,7 +13,143 @@ #include "conn_internal.h" +#define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_cs); + #if defined(CONFIG_BT_CHANNEL_SOUNDING) +int bt_cs_read_remote_supported_capabilities(struct bt_conn *conn) +{ + struct bt_hci_cp_le_read_remote_supported_capabilities *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES, buf, NULL); +} + +void bt_hci_le_cs_read_remote_supported_capabilities_complete(struct net_buf *buf) +{ + struct bt_conn *conn; + struct bt_conn_le_cs_capabilities remote_cs_capabilities; + struct bt_hci_evt_le_cs_read_remote_supported_capabilities_complete *evt; + + if (buf->len < sizeof(*evt)) { + LOG_ERR("Unexpected end of buffer"); + return; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + if (evt->status) { + LOG_INF("Read Remote Supported Capabilities failed (status 0x%02X)", evt->status); + return; + } + + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->conn_handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Could not lookup connection handle when reading remote CS capabilities"); + return; + } + + remote_cs_capabilities.num_config_supported = evt->num_config_supported; + remote_cs_capabilities.max_consecutive_procedures_supported = + sys_le16_to_cpu(evt->max_consecutive_procedures_supported); + remote_cs_capabilities.num_antennas_supported = evt->num_antennas_supported; + remote_cs_capabilities.max_antenna_paths_supported = evt->max_antenna_paths_supported; + + remote_cs_capabilities.initiator_supported = + evt->roles_supported & BT_HCI_LE_CS_INITIATOR_ROLE_MASK; + remote_cs_capabilities.reflector_supported = + evt->roles_supported & BT_HCI_LE_CS_REFLECTOR_ROLE_MASK; + remote_cs_capabilities.mode_3_supported = + evt->modes_supported & BT_HCI_LE_CS_MODES_SUPPORTED_MODE_3_MASK; + + remote_cs_capabilities.rtt_aa_only_n = evt->rtt_aa_only_n; + remote_cs_capabilities.rtt_sounding_n = evt->rtt_sounding_n; + remote_cs_capabilities.rtt_random_payload_n = evt->rtt_random_payload_n; + + if (evt->rtt_aa_only_n) { + if (evt->rtt_capability & BT_HCI_LE_CS_RTT_AA_ONLY_N_10NS_MASK) { + remote_cs_capabilities.rtt_aa_only_precision = + BT_CONN_LE_CS_RTT_AA_ONLY_10NS; + } else { + remote_cs_capabilities.rtt_aa_only_precision = + BT_CONN_LE_CS_RTT_AA_ONLY_150NS; + } + } else { + remote_cs_capabilities.rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_NOT_SUPP; + } + + if (evt->rtt_sounding_n) { + if (evt->rtt_capability & BT_HCI_LE_CS_RTT_SOUNDING_N_10NS_MASK) { + remote_cs_capabilities.rtt_sounding_precision = + BT_CONN_LE_CS_RTT_SOUNDING_10NS; + } else { + remote_cs_capabilities.rtt_sounding_precision = + BT_CONN_LE_CS_RTT_SOUNDING_150NS; + } + } else { + remote_cs_capabilities.rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_NOT_SUPP; + } + + if (evt->rtt_random_payload_n) { + if (evt->rtt_capability & BT_HCI_LE_CS_RTT_RANDOM_PAYLOAD_N_10NS_MASK) { + remote_cs_capabilities.rtt_random_payload_precision = + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS; + } else { + remote_cs_capabilities.rtt_random_payload_precision = + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_150NS; + } + } else { + remote_cs_capabilities.rtt_random_payload_precision = + BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_NOT_SUPP; + } + + remote_cs_capabilities.phase_based_nadm_sounding_supported = + sys_le16_to_cpu(evt->nadm_sounding_capability) & + BT_HCI_LE_CS_NADM_SOUNDING_CAPABILITY_PHASE_BASED_MASK; + + remote_cs_capabilities.phase_based_nadm_random_supported = + sys_le16_to_cpu(evt->nadm_random_capability) & + BT_HCI_LE_CS_NADM_RANDOM_CAPABILITY_PHASE_BASED_MASK; + + remote_cs_capabilities.cs_sync_2m_phy_supported = + evt->cs_sync_phys_supported & BT_HCI_LE_CS_SYNC_PHYS_2M_MASK; + + remote_cs_capabilities.cs_sync_2m_2bt_phy_supported = + evt->cs_sync_phys_supported & BT_HCI_LE_CS_SYNC_PHYS_2M_2BT_MASK; + + remote_cs_capabilities.cs_without_fae_supported = + sys_le16_to_cpu(evt->subfeatures_supported) & + BT_HCI_LE_CS_SUBFEATURE_NO_TX_FAE_MASK; + + remote_cs_capabilities.chsel_alg_3c_supported = + sys_le16_to_cpu(evt->subfeatures_supported) & + BT_HCI_LE_CS_SUBFEATURE_CHSEL_ALG_3C_MASK; + + remote_cs_capabilities.pbr_from_rtt_sounding_seq_supported = + sys_le16_to_cpu(evt->subfeatures_supported) & + BT_HCI_LE_CS_SUBFEATURE_PBR_FROM_RTT_SOUNDING_SEQ_MASK; + + remote_cs_capabilities.t_ip1_times_supported = sys_le16_to_cpu(evt->t_ip1_times_supported); + remote_cs_capabilities.t_ip2_times_supported = sys_le16_to_cpu(evt->t_ip2_times_supported); + remote_cs_capabilities.t_fcs_times_supported = sys_le16_to_cpu(evt->t_fcs_times_supported); + remote_cs_capabilities.t_pm_times_supported = sys_le16_to_cpu(evt->t_pm_times_supported); + + remote_cs_capabilities.t_sw_time = evt->t_sw_time_supported; + remote_cs_capabilities.tx_snr_capability = evt->tx_snr_capability; + + notify_remote_cs_capabilities(conn, remote_cs_capabilities); + + bt_conn_unref(conn); +} + int bt_cs_set_default_settings(struct bt_conn *conn, const struct bt_cs_set_default_settings_param *params) { @@ -41,4 +177,49 @@ int bt_cs_set_default_settings(struct bt_conn *conn, return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_SET_DEFAULT_SETTINGS, buf, NULL); } + +int bt_cs_read_remote_fae_table(struct bt_conn *conn) +{ + struct bt_hci_cp_le_read_remote_fae_table *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_READ_REMOTE_FAE_TABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_READ_REMOTE_FAE_TABLE, buf, NULL); +} + +void bt_hci_le_cs_read_remote_fae_table_complete(struct net_buf *buf) +{ + struct bt_conn *conn; + struct bt_conn_le_cs_fae_table fae_table; + struct bt_hci_evt_le_cs_read_remote_fae_table_complete *evt; + + if (buf->len < sizeof(*evt)) { + LOG_ERR("Unexpected end of buffer"); + return; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + if (evt->status) { + LOG_INF("Read Remote FAE Table failed with status 0x%02X", evt->status); + return; + } + + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->conn_handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Could not lookup connection handle when reading remote FAE Table"); + return; + } + + fae_table.remote_fae_table = evt->remote_fae_table; + notify_remote_cs_fae_table(conn, fae_table); + + bt_conn_unref(conn); +} #endif /* CONFIG_BT_CHANNEL_SOUNDING */ diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index adc215a67ea..b1ab4fbb664 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2821,6 +2821,14 @@ static const struct event_handler meta_events[] = { sizeof(struct bt_hci_evt_le_enh_conn_complete_v2)), #endif /* CONFIG_BT_PER_ADV_RSP || CONFIG_BT_PER_ADV_SYNC_RSP */ #endif /* CONFIG_BT_CONN */ +#if defined(CONFIG_BT_CHANNEL_SOUNDING) + EVENT_HANDLER(BT_HCI_EVT_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE, + bt_hci_le_cs_read_remote_supported_capabilities_complete, + sizeof(struct bt_hci_evt_le_cs_read_remote_supported_capabilities_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE, + bt_hci_le_cs_read_remote_fae_table_complete, + sizeof(struct bt_hci_evt_le_cs_read_remote_fae_table_complete)), +#endif /* CONFIG_BT_CHANNEL_SOUNDING */ }; @@ -3391,6 +3399,13 @@ static int le_set_event_mask(void) mask |= BT_EVT_MASK_LE_ENH_CONN_COMPLETE_V2; } + + if (IS_ENABLED(CONFIG_BT_CHANNEL_SOUNDING) && + BT_FEAT_LE_CHANNEL_SOUNDING(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE; + mask |= BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE; + } + sys_put_le64(mask, cp_mask->events); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL); } @@ -3613,6 +3628,14 @@ static int le_init(void) } } + if (IS_ENABLED(CONFIG_BT_CHANNEL_SOUNDING) && + BT_FEAT_LE_CHANNEL_SOUNDING(bt_dev.le.features)) { + err = le_set_host_feature(BT_LE_FEAT_BIT_CHANNEL_SOUNDING_HOST, 1); + if (err) { + return err; + } + } + return le_set_event_mask(); } diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index b39f219024d..590d85a6c49 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -538,6 +538,10 @@ void bt_hci_le_vs_df_connectionless_iq_report(struct net_buf *buf); void bt_hci_le_past_received(struct net_buf *buf); void bt_hci_le_past_received_v2(struct net_buf *buf); +/* CS HCI event handlers */ +void bt_hci_le_cs_read_remote_supported_capabilities_complete(struct net_buf *buf); +void bt_hci_le_cs_read_remote_fae_table_complete(struct net_buf *buf); + /* Adv HCI event handlers */ void bt_hci_le_adv_set_terminated(struct net_buf *buf); void bt_hci_le_scan_req_received(struct net_buf *buf); diff --git a/subsys/bluetooth/shell/bt.c b/subsys/bluetooth/shell/bt.c index 70b3e69941d..25841fd48d6 100644 --- a/subsys/bluetooth/shell/bt.c +++ b/subsys/bluetooth/shell/bt.c @@ -982,6 +982,78 @@ void subrate_changed(struct bt_conn *conn, } #endif +#if defined(CONFIG_BT_CHANNEL_SOUNDING) +void print_remote_cs_capabilities(struct bt_conn *conn, struct bt_conn_le_cs_capabilities *params) +{ + shell_print( + ctx_shell, + "Received remote channel sounding capabilities:\n" + "- Num CS configurations: %d\n" + "- Max consecutive CS procedures: %d\n" + "- Num antennas supported: %d\n" + "- Max antenna paths supported: %d\n" + "- Initiator role supported: %s\n" + "- Reflector role supported: %s\n" + "- Mode 3 supported: %s\n" + "- RTT AA only supported: %s\n" + "- RTT AA only is 10ns precise: %s\n" + "- RTT AA only N: %d\n" + "- RTT sounding supported: %s\n" + "- RTT sounding is 10ns precise: %s\n" + "- RTT sounding N: %d\n" + "- RTT random payload supported: %s\n" + "- RTT random payload is 10ns precise: %s\n" + "- RTT random payload N: %d\n" + "- Phase-based NADM with sounding sequences supported: %s\n" + "- Phase-based NADM with random sequences supported: %s\n" + "- CS Sync 2M PHY supported: %s\n" + "- CS Sync 2M 2BT PHY supported: %s\n" + "- CS without transmitter FAE supported: %s\n" + "- Channel selection algorithm #3c supported: %s\n" + "- Phase-based ranging from RTT sounding sequence supported: %s\n" + "- T_IP1 times supported: 0x%04x\n" + "- T_IP2 times supported: 0x%04x\n" + "- T_FCS times supported: 0x%04x\n" + "- T_PM times supported: 0x%04x\n" + "- T_SW time supported: %d us\n" + "- TX SNR capability: 0x%02x", + params->num_config_supported, params->max_consecutive_procedures_supported, + params->num_antennas_supported, params->max_antenna_paths_supported, + params->initiator_supported ? "Yes" : "No", + params->reflector_supported ? "Yes" : "No", params->mode_3_supported ? "Yes" : "No", + params->rtt_aa_only_precision == BT_CONN_LE_CS_RTT_AA_ONLY_NOT_SUPP ? "No" : "Yes", + params->rtt_aa_only_precision == BT_CONN_LE_CS_RTT_AA_ONLY_10NS ? "Yes" : "No", + params->rtt_aa_only_n, + params->rtt_sounding_precision == BT_CONN_LE_CS_RTT_SOUNDING_NOT_SUPP ? "No" + : "Yes", + params->rtt_sounding_precision == BT_CONN_LE_CS_RTT_SOUNDING_10NS ? "Yes" : "No", + params->rtt_sounding_n, + params->rtt_random_payload_precision == BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_NOT_SUPP + ? "No" + : "Yes", + params->rtt_random_payload_precision == BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS + ? "Yes" + : "No", + params->rtt_random_payload_n, + params->phase_based_nadm_sounding_supported ? "Yes" : "No", + params->phase_based_nadm_random_supported ? "Yes" : "No", + params->cs_sync_2m_phy_supported ? "Yes" : "No", + params->cs_sync_2m_2bt_phy_supported ? "Yes" : "No", + params->cs_without_fae_supported ? "Yes" : "No", + params->chsel_alg_3c_supported ? "Yes" : "No", + params->pbr_from_rtt_sounding_seq_supported ? "Yes" : "No", + params->t_ip1_times_supported, params->t_ip2_times_supported, + params->t_fcs_times_supported, params->t_pm_times_supported, params->t_sw_time, + params->tx_snr_capability); +} + +void print_remote_cs_fae_table(struct bt_conn *conn, struct bt_conn_le_cs_fae_table *params) +{ + shell_print(ctx_shell, "Received FAE Table: "); + shell_hexdump(ctx_shell, params->remote_fae_table, 72); +} +#endif + static struct bt_conn_cb conn_callbacks = { .connected = connected, .disconnected = disconnected, @@ -1011,6 +1083,10 @@ static struct bt_conn_cb conn_callbacks = { #if defined(CONFIG_BT_SUBRATING) .subrate_changed = subrate_changed, #endif +#if defined(CONFIG_BT_CHANNEL_SOUNDING) + .remote_cs_capabilities_available = print_remote_cs_capabilities, + .remote_cs_fae_table_available = print_remote_cs_fae_table, +#endif }; #endif /* CONFIG_BT_CONN */ diff --git a/subsys/bluetooth/shell/cs.c b/subsys/bluetooth/shell/cs.c index 00a16cb4adb..fbe089155cc 100644 --- a/subsys/bluetooth/shell/cs.c +++ b/subsys/bluetooth/shell/cs.c @@ -39,6 +39,24 @@ static int check_cs_sync_antenna_selection_input(uint16_t input) return 0; } +static int cmd_read_remote_supported_capabilities(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Conn handle error, at least one connection is required."); + return -ENOEXEC; + } + + err = bt_cs_read_remote_supported_capabilities(default_conn); + if (err) { + shell_error(sh, "bt_cs_read_remote_supported_capabilities returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + static int cmd_set_default_settings(const struct shell *sh, size_t argc, char *argv[]) { int err = 0; @@ -98,13 +116,38 @@ static int cmd_set_default_settings(const struct shell *sh, size_t argc, char *a return 0; } +static int cmd_read_remote_fae_table(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Conn handle error, at least one connection is required."); + return -ENOEXEC; + } + + err = bt_cs_read_remote_fae_table(default_conn); + if (err) { + shell_error(sh, "bt_cs_read_remote_fae_table returned error %d", err); + return -ENOEXEC; + } + + return 0; +} SHELL_STATIC_SUBCMD_SET_CREATE( cs_cmds, + SHELL_CMD_ARG( + read_remote_supported_capabilities, NULL, + "", + cmd_read_remote_supported_capabilities, 1, 0), SHELL_CMD_ARG( set_default_settings, NULL, " " " ", cmd_set_default_settings, 5, 0), + SHELL_CMD_ARG( + read_remote_fae_table, NULL, + "", + cmd_read_remote_fae_table, 1, 0), SHELL_SUBCMD_SET_END); static int cmd_cs(const struct shell *sh, size_t argc, char **argv)