diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 55bb48c6934..23dd9fb89f8 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -548,7 +548,6 @@ enum bt_conn_le_cs_procedure_done_status { /** Subevent done status */ enum bt_conn_le_cs_subevent_done_status { BT_CONN_LE_CS_SUBEVENT_COMPLETE = BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_COMPLETE, - BT_CONN_LE_CS_SUBEVENT_INCOMPLETE = BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL, BT_CONN_LE_CS_SUBEVENT_ABORTED = BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_ABORTED, }; @@ -618,7 +617,19 @@ struct bt_conn_le_cs_subevent_result { int8_t reference_power_level; /** Procedure status. */ enum bt_conn_le_cs_procedure_done_status procedure_done_status; - /** Subevent status. */ + /** Subevent status + * + * For aborted subevents, this will be set to @ref BT_CONN_LE_CS_SUBEVENT_ABORTED + * and abort_step will contain the step number on which the subevent was aborted. + * Consider the following example: + * + * subevent_done_status = @ref BT_CONN_LE_CS_SUBEVENT_ABORTED + * num_steps_reported = 160 + * abort_step = 100 + * + * this would mean that steps from 0 to 99 are complete and steps from 100 to 159 + * are aborted. + */ enum bt_conn_le_cs_subevent_done_status subevent_done_status; /** Abort reason. * @@ -640,6 +651,11 @@ struct bt_conn_le_cs_subevent_result { /** Number of CS steps in the subevent. */ uint8_t num_steps_reported; + /** Step number, on which the subevent was aborted + * if subevent_done_status is @ref BT_CONN_LE_CS_SUBEVENT_COMPLETE + * then abort_step will be unused and set to 255 + */ + uint8_t abort_step; } header; struct net_buf_simple *step_data_buf; }; @@ -1502,6 +1518,86 @@ enum bt_security_err { BT_SECURITY_ERR_UNSPECIFIED, }; +enum bt_conn_le_cs_procedure_enable_state { + BT_CONN_LE_CS_PROCEDURES_DISABLED = BT_HCI_OP_LE_CS_PROCEDURES_DISABLED, + BT_CONN_LE_CS_PROCEDURES_ENABLED = BT_HCI_OP_LE_CS_PROCEDURES_ENABLED, +}; + +/** CS Test Tone Antennna Config Selection. + * + * These enum values are indices in the following table, where N_AP is the maximum + * number of antenna paths (in the range [1, 4]). + * + * +--------------+-------------+-------------------+-------------------+--------+ + * | Config Index | Total Paths | Dev A: # Antennas | Dev B: # Antennas | Config | + * +--------------+-------------+-------------------+-------------------+--------+ + * | 0 | 1 | 1 | 1 | 1:1 | + * | 1 | 2 | 2 | 1 | N_AP:1 | + * | 2 | 3 | 3 | 1 | N_AP:1 | + * | 3 | 4 | 4 | 1 | N_AP:1 | + * | 4 | 2 | 1 | 2 | 1:N_AP | + * | 5 | 3 | 1 | 3 | 1:N_AP | + * | 6 | 4 | 1 | 4 | 1:N_AP | + * | 7 | 4 | 2 | 2 | 2:2 | + * +--------------+-------------+-------------------+-------------------+--------+ + * + * There are therefore four groups of possible antenna configurations: + * + * - 1:1 configuration, where both A and B support 1 antenna each + * - 1:N_AP configuration, where A supports 1 antenna, B supports N_AP antennas, and + * N_AP is a value in the range [2, 4] + * - N_AP:1 configuration, where A supports N_AP antennas, B supports 1 antenna, and + * N_AP is a value in the range [2, 4] + * - 2:2 configuration, where both A and B support 2 antennas and N_AP = 4 + */ +enum bt_conn_le_cs_tone_antenna_config_selection { + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_ONE = BT_HCI_OP_LE_CS_ACI_0, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_TWO = BT_HCI_OP_LE_CS_ACI_1, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_THREE = BT_HCI_OP_LE_CS_ACI_2, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_FOUR = BT_HCI_OP_LE_CS_ACI_3, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_FIVE = BT_HCI_OP_LE_CS_ACI_4, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_SIX = BT_HCI_OP_LE_CS_ACI_5, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_SEVEN = BT_HCI_OP_LE_CS_ACI_6, + BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_EIGHT = BT_HCI_OP_LE_CS_ACI_7, +}; + +struct bt_conn_le_cs_procedure_enable_complete { + /* The ID associated with the desired configuration (0 to 3) */ + uint8_t config_id; + + /* State of the CS procedure */ + enum bt_conn_le_cs_procedure_enable_state state; + + /* Antenna configuration index */ + enum bt_conn_le_cs_tone_antenna_config_selection tone_antenna_config_selection; + + /* Transmit power level used for CS procedures (-127 to 20 dB; 0x7F if unavailable) */ + int8_t selected_tx_power; + + /* Duration of each CS subevent in microseconds (1250 us to 4 s) */ + uint32_t subevent_len; + + /* Number of CS subevents anchored off the same ACL connection event (0x01 to 0x20) */ + uint8_t subevents_per_event; + + /* Time between consecutive CS subevents anchored off the same ACL connection event in + * units of 0.625 ms + */ + uint16_t subevent_interval; + + /* Number of ACL connection events between consecutive CS event anchor points */ + uint16_t event_interval; + + /* Number of ACL connection events between consecutive CS procedure anchor points */ + uint16_t procedure_interval; + + /* Number of CS procedures to be scheduled (0 if procedures to continue until disabled) */ + uint16_t procedure_count; + + /* Maximum duration for each procedure in units of 0.625 ms (0x0001 to 0xFFFF) */ + uint16_t max_procedure_len; +}; + /** @brief Connection callback structure. * * This structure is used for tracking the state of a connection. @@ -1794,6 +1890,27 @@ struct bt_conn_cb { */ void (*le_cs_subevent_data_available)(struct bt_conn *conn, struct bt_conn_le_cs_subevent_result *result); + + /** @brief LE CS Security Enabled. + * + * This callback notifies the application that a Channel Sounding + * Security Enable procedure has completed + * + * @param conn Connection object. + */ + void (*le_cs_security_enabled)(struct bt_conn *conn); + + /** @brief LE CS Procedure Enabled. + * + * This callback notifies the application that a Channel Sounding + * Procedure Enable procedure has completed + * + * @param conn Connection object. + * @param params CS Procedure Enable parameters + */ + void (*le_cs_procedure_enabled)( + struct bt_conn *conn, struct bt_conn_le_cs_procedure_enable_complete *params); + #endif /** @internal Internally used field for list handling */ diff --git a/include/zephyr/bluetooth/cs.h b/include/zephyr/bluetooth/cs.h index fa362083180..d5d34039d27 100644 --- a/include/zephyr/bluetooth/cs.h +++ b/include/zephyr/bluetooth/cs.h @@ -88,62 +88,24 @@ enum bt_le_cs_test_cs_sync_antenna_selection { BT_LE_CS_TEST_CS_SYNC_ANTENNA_SELECTION_FOUR = BT_HCI_OP_LE_CS_ANTENNA_SEL_FOUR, }; -/** CS Test Tone Antennna Config Selection. - * - * These enum values are indices in the following table, where N_AP is the maximum - * number of antenna paths (in the range [1, 4]). - * - * +--------------+-------------+-------------------+-------------------+--------+ - * | Config Index | Total Paths | Dev A: # Antennas | Dev B: # Antennas | Config | - * +--------------+-------------+-------------------+-------------------+--------+ - * | 0 | 1 | 1 | 1 | 1:1 | - * | 1 | 2 | 2 | 1 | N_AP:1 | - * | 2 | 3 | 3 | 1 | N_AP:1 | - * | 3 | 4 | 4 | 1 | N_AP:1 | - * | 4 | 2 | 1 | 2 | 1:N_AP | - * | 5 | 3 | 1 | 3 | 1:N_AP | - * | 6 | 4 | 1 | 4 | 1:N_AP | - * | 7 | 4 | 2 | 2 | 2:2 | - * +--------------+-------------+-------------------+-------------------+--------+ - * - * There are therefore four groups of possible antenna configurations: - * - * - 1:1 configuration, where both A and B support 1 antenna each - * - 1:N_AP configuration, where A supports 1 antenna, B supports N_AP antennas, and - * N_AP is a value in the range [2, 4] - * - N_AP:1 configuration, where A supports N_AP antennas, B supports 1 antenna, and - * N_AP is a value in the range [2, 4] - * - 2:2 configuration, where both A and B support 2 antennas and N_AP = 4 - */ -enum bt_le_cs_test_tone_antenna_config_selection { - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_ONE = BT_HCI_OP_LE_CS_TEST_ACI_0, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_TWO = BT_HCI_OP_LE_CS_TEST_ACI_1, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_THREE = BT_HCI_OP_LE_CS_TEST_ACI_2, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_FOUR = BT_HCI_OP_LE_CS_TEST_ACI_3, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_FIVE = BT_HCI_OP_LE_CS_TEST_ACI_4, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_SIX = BT_HCI_OP_LE_CS_TEST_ACI_5, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_SEVEN = BT_HCI_OP_LE_CS_TEST_ACI_6, - BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_EIGHT = BT_HCI_OP_LE_CS_TEST_ACI_7, -}; - /** CS Test Initiator SNR control options */ -enum bt_le_cs_test_initiator_snr_control { - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_18dB = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_18, - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_21dB = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_21, - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_24dB = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_24, - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_27dB = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_27, - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_30dB = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_30, - BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_NOT_USED = BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_NOT_USED, +enum bt_le_cs_initiator_snr_control { + BT_LE_CS_INITIATOR_SNR_CONTROL_18dB = BT_HCI_OP_LE_CS_INITIATOR_SNR_18, + BT_LE_CS_INITIATOR_SNR_CONTROL_21dB = BT_HCI_OP_LE_CS_INITIATOR_SNR_21, + BT_LE_CS_INITIATOR_SNR_CONTROL_24dB = BT_HCI_OP_LE_CS_INITIATOR_SNR_24, + BT_LE_CS_INITIATOR_SNR_CONTROL_27dB = BT_HCI_OP_LE_CS_INITIATOR_SNR_27, + BT_LE_CS_INITIATOR_SNR_CONTROL_30dB = BT_HCI_OP_LE_CS_INITIATOR_SNR_30, + BT_LE_CS_INITIATOR_SNR_CONTROL_NOT_USED = BT_HCI_OP_LE_CS_INITIATOR_SNR_NOT_USED, }; /** CS Test Reflector SNR control options */ -enum bt_le_cs_test_reflector_snr_control { - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_18dB = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_18, - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_21dB = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_21, - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_24dB = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_24, - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_27dB = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_27, - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_30dB = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_30, - BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_NOT_USED = BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_NOT_USED, +enum bt_le_cs_reflector_snr_control { + BT_LE_CS_REFLECTOR_SNR_CONTROL_18dB = BT_HCI_OP_LE_CS_REFLECTOR_SNR_18, + BT_LE_CS_REFLECTOR_SNR_CONTROL_21dB = BT_HCI_OP_LE_CS_REFLECTOR_SNR_21, + BT_LE_CS_REFLECTOR_SNR_CONTROL_24dB = BT_HCI_OP_LE_CS_REFLECTOR_SNR_24, + BT_LE_CS_REFLECTOR_SNR_CONTROL_27dB = BT_HCI_OP_LE_CS_REFLECTOR_SNR_27, + BT_LE_CS_REFLECTOR_SNR_CONTROL_30dB = BT_HCI_OP_LE_CS_REFLECTOR_SNR_30, + BT_LE_CS_REFLECTOR_SNR_CONTROL_NOT_USED = BT_HCI_OP_LE_CS_REFLECTOR_SNR_NOT_USED, }; /** CS Test Override 3 T_PM Tone Extension */ @@ -338,24 +300,73 @@ struct bt_le_cs_test_param { * it should use as low a transmit power as possible */ uint8_t transmit_power_level; - /** Interlude time in microseconds between the RTT packets. */ + /** Interlude time in microseconds between the RTT packets. + * + * Valid options are: + * - 10 us + * - 20 us + * - 30 us + * - 40 us + * - 50 us + * - 60 us + * - 80 us + * - 145 us + */ uint8_t t_ip1_time; - /** Interlude time in microseconds between the CS tones. */ + /** Interlude time in microseconds between the CS tones. + * + * Valid options are: + * - 10 us + * - 20 us + * - 30 us + * - 40 us + * - 50 us + * - 60 us + * - 80 us + * - 145 us + */ uint8_t t_ip2_time; - /** Time in microseconds for frequency changes. */ + /** Time in microseconds for frequency changes. + * + * Valid options are: + * - 15 us + * - 20 us + * - 30 us + * - 40 us + * - 50 us + * - 60 us + * - 80 us + * - 100 us + * - 120 us + * - 150 us + */ uint8_t t_fcs_time; - /** Time in microseconds for the phase measurement period of the CS tones. */ + /** Time in microseconds for the phase measurement period of the CS tones. + * + * Valid options are: + * - 10 us + * - 20 us + * - 40 us + */ uint8_t t_pm_time; - /** Time in microseconds for the antenna switch period of the CS tones. */ + /** Time in microseconds for the antenna switch period of the CS tones. + * + * Valid options are: + * - 0 us + * - 1 us + * - 2 us + * - 4 us + * - 10 us + */ uint8_t t_sw_time; /** Antenna Configuration Index used during antenna switching during * the tone phases of CS steps. */ - enum bt_le_cs_test_tone_antenna_config_selection tone_antenna_config_selection; + enum bt_conn_le_cs_tone_antenna_config_selection tone_antenna_config_selection; /** Initiator SNR control options */ - enum bt_le_cs_test_initiator_snr_control initiator_snr_control; + enum bt_le_cs_initiator_snr_control initiator_snr_control; /** Reflector SNR control options */ - enum bt_le_cs_test_reflector_snr_control reflector_snr_control; + enum bt_le_cs_reflector_snr_control reflector_snr_control; /** Determines octets 14 and 15 of the initial value of the DRBG nonce. */ uint16_t drbg_nonce; @@ -669,7 +680,7 @@ int bt_le_cs_remove_config(struct bt_conn *conn, uint8_t config_id); */ int bt_le_cs_stop_test(void); -/** @brief Parse CS Test Subevent Results +/** @brief Parse CS Subevent Step Data * * A helper for parsing HCI-formatted step data found in channel sounding subevent results. * @@ -687,6 +698,178 @@ void bt_le_cs_step_data_parse(struct net_buf_simple *step_data_buf, bool (*func)(struct bt_le_cs_subevent_step *step, void *user_data), void *user_data); +/** @brief CS Security Enable + * + * This commmand is used to start or restart the Channel Sounding Security + * Start procedure in the local Controller for the ACL connection identified + * in the conn parameter. + * + * @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_le_cs_security_enable(struct bt_conn *conn); + +struct bt_le_cs_procedure_enable_param { + uint8_t config_id; + enum bt_conn_le_cs_procedure_enable_state enable; +}; + +/** @brief CS Procedure Enable + * + * This command is used to enable or disable the scheduling of CS procedures + * by the local Controller, with the remote device identified in the conn + * parameter. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * @param params Parameters for the CS Procedure Enable command. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_procedure_enable(struct bt_conn *conn, + const struct bt_le_cs_procedure_enable_param *params); + +enum bt_le_cs_procedure_phy { + BT_LE_CS_PROCEDURE_PHY_1M = BT_HCI_OP_LE_CS_PROCEDURE_PHY_1M, + BT_LE_CS_PROCEUDRE_PHY_2M = BT_HCI_OP_LE_CS_PROCEDURE_PHY_2M, + BT_LE_CS_PROCEDURE_PHY_CODED_S8 = BT_HCI_OP_LE_CS_PROCEDURE_PHY_CODED_S8, + BT_LE_CS_PROCEDURE_PHY_CODED_S2 = BT_HCI_OP_LE_CS_PROCEDURE_PHY_CODED_S2, +}; + +#define BT_LE_CS_PROCEDURE_PREFERRED_PEER_ANTENNA_1 BIT(0) +#define BT_LE_CS_PROCEDURE_PREFERRED_PEER_ANTENNA_2 BIT(1) +#define BT_LE_CS_PROCEDURE_PREFERRED_PEER_ANTENNA_3 BIT(2) +#define BT_LE_CS_PROCEDURE_PREFERRED_PEER_ANTENNA_4 BIT(3) + +struct bt_le_cs_set_procedure_parameters_param { + /* The ID associated with the desired configuration (0 to 3) */ + uint8_t config_id; + + /* Max. duration for each CS procedure, where T = N * 0.625 ms (0x0001 to 0xFFFF) */ + uint16_t max_procedure_len; + + /* Min. number of connection events between consecutive CS procedures (0x0001 to 0xFFFF) */ + uint16_t min_procedure_interval; + + /* Max. number of connection events between consecutive CS procedures (0x0001 to 0xFFFF) */ + uint16_t max_procedure_interval; + + /* Max. number of procedures to be scheduled (0x0000 for no limit; otherwise 0x0001 + * to 0xFFFF) + */ + uint16_t max_procedure_count; + + /* Min. suggested duration for each CS subevent in microseconds (1250 us to 4 s) */ + uint32_t min_subevent_len; + + /* Max. suggested duration for each CS subevent in microseconds (1250 us to 4 s) */ + uint32_t max_subevent_len; + + /* Antenna configuration index */ + enum bt_conn_le_cs_tone_antenna_config_selection tone_antenna_config_selection; + + /* Phy */ + enum bt_le_cs_procedure_phy phy; + + /* Transmit power delta, in signed dB, to indicate the recommended difference between the + * remote device's power level for the CS tones and RTT packets and the existing power + * level for the Phy indicated by the Phy parameter (0x80 for no recommendation) + */ + int8_t tx_power_delta; + + /* Preferred peer antenna (Bitmask of BT_LE_CS_PROCEDURE_PREFERRED_PEER_ANTENNA_*) */ + uint8_t preferred_peer_antenna; + + /* Initiator SNR control adjustment */ + enum bt_le_cs_initiator_snr_control snr_control_initiator; + + /* Reflector SNR control adjustment */ + enum bt_le_cs_reflector_snr_control snr_control_reflector; +}; + +/** @brief CS Set Procedure Parameters + * + * This command is used to set the parameters for the scheduling of one + * or more CS procedures by the local controller. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * @param params Parameters for the CS Set Procedure Parameters command. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_set_procedure_parameters(struct bt_conn *conn, + const struct bt_le_cs_set_procedure_parameters_param *params); + +/** @brief CS Set Channel Classification + * + * This command is used to update the channel classification based on + * its local information. + * + * The nth bitfield (in the range 0 to 78) contains the value for the CS + * channel index n. Channel Enabled = 1; Channel Disabled = 0. + * + * Channels n = 0, 1, 23, 24, 25, 77, and 78 shall be reserved for future + * use and shall be set to zero. At least 15 channels shall be enabled. + * + * The most significant bit (bit 79) is reserved for future use. + * + * @note To use this API, @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param channel_classification Bit fields + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_set_channel_classification(uint8_t channel_classification[10]); + +/** @brief CS Read Local Supported Capabilities + * + * This command is used to read the CS capabilities that are supported + * by the local Controller. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param ret Return values for the CS Procedure Enable command. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_read_local_supported_capabilities(struct bt_conn_le_cs_capabilities *ret); + +/** @brief CS Write Cached Remote Supported Capabilities + * + * This command is used to write the cached copy of the CS capabilities + * that are supported by the remote Controller for the connection + * identified. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * @param params Parameters for the CS Write Cached Remote Supported Capabilities command. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_write_cached_remote_supported_capabilities( + struct bt_conn *conn, const struct bt_conn_le_cs_capabilities *params); + +/** @brief CS Write Cached Remote FAE Table + * + * This command is used to write a cached copy of the per-channel mode-0 + * Frequency Actuation Error table of the remote device in the local Controller. + * + * @note To use this API @kconfig{CONFIG_BT_CHANNEL_SOUNDING} must be set. + * + * @param conn Connection Object. + * @param remote_fae_table Per-channel mode-0 FAE table of the local Controller + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_le_cs_write_cached_remote_fae_table(struct bt_conn *conn, uint8_t remote_fae_table[72]); + #ifdef __cplusplus } #endif diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 131b81134c1..1b22e172fcc 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -2402,12 +2402,71 @@ struct bt_hci_cp_le_tx_test_v4_tx_power { int8_t tx_power; } __packed; +#define BT_HCI_OP_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES BT_OP(BT_OGF_LE, 0x0089) /* 0x2089 */ + +struct bt_hci_rp_le_read_local_supported_capabilities { + uint8_t status; + 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_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_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES \ + BT_OP(BT_OGF_LE, 0x008B) /* 0x208B */ + +struct bt_hci_cp_le_write_cached_remote_supported_capabilities { + uint16_t 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_OP_LE_CS_SECURITY_ENABLE BT_OP(BT_OGF_LE, 0x008C) /* 0x208C */ + +struct bt_hci_cp_le_security_enable { + 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) @@ -2436,6 +2495,49 @@ struct bt_hci_cp_le_read_remote_fae_table { uint16_t handle; } __packed; +#define BT_HCI_OP_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE BT_OP(BT_OGF_LE, 0x008F) /* 0x208F */ + +struct bt_hci_cp_le_write_cached_remote_fae_table { + uint16_t handle; + uint8_t remote_fae_table[72]; +} __packed; + +#define BT_HCI_OP_LE_CS_SET_CHANNEL_CLASSIFICATION BT_OP(BT_OGF_LE, 0x0092) /* 0x2092 */ + +#define BT_HCI_OP_LE_CS_SET_PROCEDURE_PARAMETERS BT_OP(BT_OGF_LE, 0x0093) /* 0x2093 */ + +#define BT_HCI_OP_LE_CS_PROCEDURE_PHY_1M 0x01 +#define BT_HCI_OP_LE_CS_PROCEDURE_PHY_2M 0x02 +#define BT_HCI_OP_LE_CS_PROCEDURE_PHY_CODED_S8 0x03 +#define BT_HCI_OP_LE_CS_PROCEDURE_PHY_CODED_S2 0x04 + +struct bt_hci_cp_le_set_procedure_parameters { + uint16_t handle; + uint8_t config_id; + uint16_t max_procedure_len; + uint16_t min_procedure_interval; + uint16_t max_procedure_interval; + uint16_t max_procedure_count; + uint8_t min_subevent_len[3]; + uint8_t max_subevent_len[3]; + uint8_t tone_antenna_config_selection; + uint8_t phy; + uint8_t tx_power_delta; + uint8_t preferred_peer_antenna; + uint8_t snr_control_initiator; + uint8_t snr_control_reflector; +} __packed; + +#define BT_HCI_OP_LE_CS_PROCEDURE_ENABLE BT_OP(BT_OGF_LE, 0x0094) /* 0x2094 */ + +#define BT_HCI_OP_LE_CS_PROCEDURES_DISABLED 0x00 +#define BT_HCI_OP_LE_CS_PROCEDURES_ENABLED 0x01 + +struct bt_hci_cp_le_procedure_enable { + uint16_t handle; + uint8_t config_id; + uint8_t enable; +} __packed; #define BT_HCI_OP_LE_CS_TEST BT_OP(BT_OGF_LE, 0x0095) /* 0x2095 */ @@ -2463,31 +2565,31 @@ struct bt_hci_cp_le_read_remote_fae_table { #define BT_HCI_OP_LE_CS_CS_SYNC_2M 0x2 #define BT_HCI_OP_LE_CS_CS_SYNC_2M_2BT 0x3 -#define BT_HCI_OP_LE_CS_TEST_MAXIMIZE_TX_POWER 0x7E -#define BT_HCI_OP_LE_CS_TEST_MINIMIZE_TX_POWER 0x7F - -#define BT_HCI_OP_LE_CS_TEST_ACI_0 0x0 -#define BT_HCI_OP_LE_CS_TEST_ACI_1 0x1 -#define BT_HCI_OP_LE_CS_TEST_ACI_2 0x2 -#define BT_HCI_OP_LE_CS_TEST_ACI_3 0x3 -#define BT_HCI_OP_LE_CS_TEST_ACI_4 0x4 -#define BT_HCI_OP_LE_CS_TEST_ACI_5 0x5 -#define BT_HCI_OP_LE_CS_TEST_ACI_6 0x6 -#define BT_HCI_OP_LE_CS_TEST_ACI_7 0x7 - -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_18 0x0 -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_21 0x1 -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_24 0x2 -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_27 0x3 -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_30 0x4 -#define BT_HCI_OP_LE_CS_TEST_INITIATOR_SNR_NOT_USED 0xFF - -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_18 0x0 -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_21 0x1 -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_24 0x2 -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_27 0x3 -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_30 0x4 -#define BT_HCI_OP_LE_CS_TEST_REFLECTOR_SNR_NOT_USED 0xFF +#define BT_HCI_OP_LE_CS_TEST_MINIMIZE_TX_POWER 0x7E +#define BT_HCI_OP_LE_CS_TEST_MAXIMIZE_TX_POWER 0x7F + +#define BT_HCI_OP_LE_CS_ACI_0 0x0 +#define BT_HCI_OP_LE_CS_ACI_1 0x1 +#define BT_HCI_OP_LE_CS_ACI_2 0x2 +#define BT_HCI_OP_LE_CS_ACI_3 0x3 +#define BT_HCI_OP_LE_CS_ACI_4 0x4 +#define BT_HCI_OP_LE_CS_ACI_5 0x5 +#define BT_HCI_OP_LE_CS_ACI_6 0x6 +#define BT_HCI_OP_LE_CS_ACI_7 0x7 + +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_18 0x0 +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_21 0x1 +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_24 0x2 +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_27 0x3 +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_30 0x4 +#define BT_HCI_OP_LE_CS_INITIATOR_SNR_NOT_USED 0xFF + +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_18 0x0 +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_21 0x1 +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_24 0x2 +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_27 0x3 +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_30 0x4 +#define BT_HCI_OP_LE_CS_REFLECTOR_SNR_NOT_USED 0xFF #define BT_HCI_OP_LE_CS_TEST_OVERRIDE_CONFIG_0_MASK BIT(0) #define BT_HCI_OP_LE_CS_TEST_OVERRIDE_CONFIG_2_MASK BIT(2) @@ -3438,6 +3540,12 @@ struct bt_hci_evt_le_cs_read_remote_fae_table_complete { #define BT_HCI_LE_CS_CONFIG_ACTION_REMOVED 0x00 #define BT_HCI_LE_CS_CONFIG_ACTION_CREATED 0x01 +#define BT_HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE 0x2E +struct bt_hci_evt_le_cs_security_enable_complete { + uint8_t status; + uint16_t handle; +} __packed; + #define BT_HCI_EVT_LE_CS_CONFIG_COMPLETE 0x2F struct bt_hci_evt_le_cs_config_complete { uint8_t status; @@ -3521,11 +3629,22 @@ struct bt_hci_evt_le_cs_config_complete { #define BT_HCI_LE_CS_PACKET_NADM_ATTACK_EXT_LIKELY 0x06 #define BT_HCI_LE_CS_PACKET_NADM_UNKNOWN 0xFF +#define BT_HCI_LE_CS_PACKET_QUALITY_AA_CHECK_SUCCESSFUL 0x0 +#define BT_HCI_LE_CS_PACKET_QUALITY_AA_CHECK_BIT_ERRORS_FOUND 0x1 +#define BT_HCI_LE_CS_PACKET_QUALITY_AA_CHECK_AA_NOT_FOUND 0x2 + +#define BT_HCI_LE_CS_PACKET_RSSI_NOT_AVAILABLE 0x7F + #define BT_HCI_EVT_LE_CS_SUBEVENT_RESULT 0x31 /** Subevent result step data format: Mode 0 Initiator */ struct bt_hci_le_cs_step_data_mode_0_initiator { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_rssi; uint8_t packet_antenna; uint16_t measured_freq_offset; @@ -3533,34 +3652,49 @@ struct bt_hci_le_cs_step_data_mode_0_initiator { /** Subevent result step data format: Mode 0 Reflector */ struct bt_hci_le_cs_step_data_mode_0_reflector { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_rssi; uint8_t packet_antenna; } __packed; /** Subevent result step data format: Mode 1 */ struct bt_hci_le_cs_step_data_mode_1 { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_nadm; uint8_t packet_rssi; union { - uint16_t toa_tod_initiator; - uint16_t tod_toa_reflector; + int16_t toa_tod_initiator; + int16_t tod_toa_reflector; }; uint8_t packet_antenna; } __packed; /** Subevent result step data format: Mode 1 with sounding sequence RTT support */ struct bt_hci_le_cs_step_data_mode_1_ss_rtt { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_nadm; uint8_t packet_rssi; union { - uint16_t toa_tod_initiator; - uint16_t tod_toa_reflector; + int16_t toa_tod_initiator; + int16_t tod_toa_reflector; }; uint8_t packet_antenna; uint8_t packet_pct1[4]; @@ -3571,8 +3705,13 @@ struct bt_hci_le_cs_step_data_mode_1_ss_rtt { /** Format for per-antenna path step data in modes 2 and 3 */ struct bt_hci_le_cs_step_data_tone_info { uint8_t phase_correction_term[3]; +#ifdef CONFIG_LITTLE_ENDIAN uint8_t quality_indicator: 4; uint8_t extension_indicator: 4; +#else + uint8_t extension_indicator: 4; + uint8_t quality_indicator: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ } __packed; /** Subevent result step data format: Mode 2 */ @@ -3583,13 +3722,18 @@ struct bt_hci_le_cs_step_data_mode_2 { /** Subevent result step data format: Mode 3 */ struct bt_hci_le_cs_step_data_mode_3 { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_nadm; uint8_t packet_rssi; union { - uint16_t toa_tod_initiator; - uint16_t tod_toa_reflector; + int16_t toa_tod_initiator; + int16_t tod_toa_reflector; }; uint8_t packet_antenna; uint8_t antenna_permutation_index; @@ -3598,13 +3742,18 @@ struct bt_hci_le_cs_step_data_mode_3 { /** Subevent result step data format: Mode 3 with sounding sequence RTT support */ struct bt_hci_le_cs_step_data_mode_3_ss_rtt { +#ifdef CONFIG_LITTLE_ENDIAN uint8_t packet_quality_aa_check: 4; uint8_t packet_quality_bit_errors: 4; +#else + uint8_t packet_quality_bit_errors: 4; + uint8_t packet_quality_aa_check: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t packet_nadm; uint8_t packet_rssi; union { - uint16_t toa_tod_initiator; - uint16_t tod_toa_reflector; + int16_t toa_tod_initiator; + int16_t tod_toa_reflector; }; uint8_t packet_antenna; uint8_t packet_pct1[4]; @@ -3629,8 +3778,32 @@ struct bt_hci_evt_le_cs_subevent_result { uint8_t reference_power_level; uint8_t procedure_done_status; uint8_t subevent_done_status; +#ifdef CONFIG_LITTLE_ENDIAN uint8_t procedure_abort_reason: 4; uint8_t subevent_abort_reason: 4; +#else + uint8_t subevent_abort_reason: 4; + uint8_t procedure_abort_reason: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ + uint8_t num_antenna_paths; + uint8_t num_steps_reported; + uint8_t steps[]; +} __packed; + +#define BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE 0x32 + +struct bt_hci_evt_le_cs_subevent_result_continue { + uint16_t conn_handle; + uint8_t config_id; + uint8_t procedure_done_status; + uint8_t subevent_done_status; +#ifdef CONFIG_LITTLE_ENDIAN + uint8_t procedure_abort_reason: 4; + uint8_t subevent_abort_reason: 4; +#else + uint8_t subevent_abort_reason: 4; + uint8_t procedure_abort_reason: 4; +#endif /* CONFIG_LITTLE_ENDIAN */ uint8_t num_antenna_paths; uint8_t num_steps_reported; uint8_t steps[]; @@ -3641,6 +3814,23 @@ struct bt_hci_evt_le_cs_test_end_complete { uint8_t status; } __packed; +#define BT_HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE 0x30 +struct bt_hci_evt_le_cs_procedure_enable_complete { + uint8_t status; + uint16_t handle; + uint8_t config_id; + uint8_t state; + uint8_t tone_antenna_config_selection; + uint8_t selected_tx_power; + uint8_t subevent_len[3]; + uint8_t subevents_per_event; + uint16_t subevent_interval; + uint16_t event_interval; + uint16_t procedure_interval; + uint16_t procedure_count; + uint16_t max_procedure_len; +} __packed; + /* Event mask bits */ #define BT_EVT_BIT(n) (1ULL << (n)) @@ -3732,8 +3922,11 @@ struct bt_hci_evt_le_cs_test_end_complete { #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) +#define BT_EVT_MASK_LE_CS_SECURITY_ENABLE_COMPLETE BT_EVT_BIT(45) #define BT_EVT_MASK_LE_CS_CONFIG_COMPLETE BT_EVT_BIT(46) +#define BT_EVT_MASK_LE_CS_PROCEDURE_ENABLE_COMPLETE BT_EVT_BIT(47) #define BT_EVT_MASK_LE_CS_SUBEVENT_RESULT BT_EVT_BIT(48) +#define BT_EVT_MASK_LE_CS_SUBEVENT_RESULT_CONTINUE BT_EVT_BIT(49) #define BT_EVT_MASK_LE_CS_TEST_END_COMPLETE BT_EVT_BIT(50) /** HCI Error Codes, BT Core Spec v5.4 [Vol 1, Part F]. */ diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index bf6feff37eb..dbb8214a136 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -203,6 +203,26 @@ config BT_CHANNEL_SOUNDING_TEST help Enable support for Channel Sounding test mode. +config BT_CHANNEL_SOUNDING_REASSEMBLY_BUFFER_SIZE + int "Subevent result reassembly buffer size" + depends on BT_CHANNEL_SOUNDING + range 239 5600 + default 5600 + help + When the results for a CS subevent cannot fit into a single HCI event, + it will be split up into multiple events and consequently, reassembled into a + full CS subevent. This config sets the size of the reassembly buffer. + +config BT_CHANNEL_SOUNDING_REASSEMBLY_BUFFER_CNT + int "Subevent result reassembly buffer count" + depends on BT_CHANNEL_SOUNDING + range 1 BT_MAX_CONN + default 1 + help + Controls the number of the reassembly buffers for CS subevent + results. Each running CS procedure is allocated one buffer and the + number of concurrent CS procedures is limited by this value. + endif # BT_CONN rsource "Kconfig.iso" diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 47014a5c076..5e853d57be1 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -3410,6 +3410,41 @@ void notify_cs_config_removed(struct bt_conn *conn, uint8_t config_id) } } +void notify_cs_security_enable_available(struct bt_conn *conn) +{ + struct bt_conn_cb *callback; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) { + if (callback->le_cs_security_enabled) { + callback->le_cs_security_enabled(conn); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { + if (cb->le_cs_security_enabled) { + cb->le_cs_security_enabled(conn); + } + } +} + +void notify_cs_procedure_enable_available(struct bt_conn *conn, + struct bt_conn_le_cs_procedure_enable_complete *params) +{ + struct bt_conn_cb *callback; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) { + if (callback->le_cs_procedure_enabled) { + callback->le_cs_procedure_enabled(conn, params); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { + if (cb->le_cs_procedure_enabled) { + cb->le_cs_procedure_enabled(conn, params); + } + } +} + void notify_cs_subevent_result(struct bt_conn *conn, struct bt_conn_le_cs_subevent_result *result) { struct bt_conn_cb *callback; diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index ac2141c1aad..6b6c97b1902 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -506,6 +506,11 @@ void notify_cs_config_removed(struct bt_conn *conn, uint8_t config_id); void notify_cs_subevent_result(struct bt_conn *conn, struct bt_conn_le_cs_subevent_result *result); +void notify_cs_security_enable_available(struct bt_conn *conn); + +void notify_cs_procedure_enable_available(struct bt_conn *conn, + struct bt_conn_le_cs_procedure_enable_complete *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 75d5f21cf2c..b22877d31b8 100644 --- a/subsys/bluetooth/host/cs.c +++ b/subsys/bluetooth/host/cs.c @@ -22,6 +22,224 @@ LOG_MODULE_REGISTER(bt_cs); static struct bt_le_cs_test_cb cs_test_callbacks; #endif +struct reassembly_buf_meta_data { + uint16_t conn_handle; +}; + +static void clear_on_disconnect(struct bt_conn *conn, uint8_t reason); + +NET_BUF_POOL_FIXED_DEFINE(reassembly_buf_pool, CONFIG_BT_CHANNEL_SOUNDING_REASSEMBLY_BUFFER_CNT, + CONFIG_BT_CHANNEL_SOUNDING_REASSEMBLY_BUFFER_SIZE, + sizeof(struct reassembly_buf_meta_data), NULL); + +static sys_slist_t reassembly_bufs = SYS_SLIST_STATIC_INIT(&reassembly_bufs); + +struct bt_conn_le_cs_subevent_result reassembled_result; + +BT_CONN_CB_DEFINE(cs_conn_callbacks) = { + .disconnected = clear_on_disconnect, +}; + +/** @brief Allocates new reassembly buffer identified by the connection handle + * + * @param conn_handle Connection handle + * @return struct net_buf* Reassembly buffer, NULL if allocation fails + */ +static struct net_buf *alloc_reassembly_buf(uint16_t conn_handle) +{ + struct net_buf *buf = net_buf_alloc(&reassembly_buf_pool, K_NO_WAIT); + + if (!buf) { + LOG_ERR("Failed to allocate new reassembly buffer"); + return NULL; + } + + struct reassembly_buf_meta_data *buf_meta_data = + (struct reassembly_buf_meta_data *)buf->user_data; + + buf_meta_data->conn_handle = conn_handle; + net_buf_slist_put(&reassembly_bufs, buf); + + LOG_DBG("Allocated new reassembly buffer for conn handle %d", conn_handle); + return buf; +} + +/** @brief Frees a reassembly buffer + * + * @note Takes the ownership of the pointer and sets it to NULL + * + * @param buf Double pointer to reassembly buffer + */ +static void free_reassembly_buf(struct net_buf **buf) +{ + if (!buf) { + LOG_ERR("NULL double pointer was passed when attempting to free reassembly buffer"); + return; + } + + if (!(*buf)) { + LOG_WRN("Attempted double free on reassembly buffer"); + return; + } + + struct reassembly_buf_meta_data *buf_meta_data = + (struct reassembly_buf_meta_data *)((*buf)->user_data); + + LOG_DBG("De-allocating reassembly buffer for conn handle %d", buf_meta_data->conn_handle); + if (!sys_slist_find_and_remove(&reassembly_bufs, &(*buf)->node)) { + LOG_WRN("The buffer was not in the list"); + } + + net_buf_unref(*buf); + *buf = NULL; +} + +/** @brief Gets the reassembly buffer identified by the connection handle + * + * @param conn_handle Connection handle + * @param allocate Allocates a new reassembly buffer if it's not allocated already + * @return struct net_buf* Reassembly buffer, NULL if it doesn't exist or failed when allocating new + */ +static struct net_buf *get_reassembly_buf(uint16_t conn_handle, bool allocate) +{ + sys_snode_t *node; + + SYS_SLIST_FOR_EACH_NODE(&reassembly_bufs, node) { + struct net_buf *buf = CONTAINER_OF(node, struct net_buf, node); + struct reassembly_buf_meta_data *buf_meta_data = + (struct reassembly_buf_meta_data *)(buf->user_data); + + if (buf_meta_data->conn_handle == conn_handle) { + return buf; + } + } + + return allocate ? alloc_reassembly_buf(conn_handle) : NULL; +} + +/** @brief Adds step data to a reassembly buffer + * + * @param reassembly_buf Reassembly buffer + * @param data Step data + * @param data_len Step data length + * @return true if successful, false if there is insufficient space + */ +static bool add_reassembly_data(struct net_buf *reassembly_buf, const uint8_t *data, + uint16_t data_len) +{ + if (data_len > net_buf_tailroom(reassembly_buf)) { + LOG_ERR("Not enough reassembly buffer space for subevent result"); + return false; + } + + net_buf_add_mem(reassembly_buf, data, data_len); + return true; +} + +/** @brief Initializes a reassembly buffer from partial step data + * + * @note Upon first call, this function also registers the disconnection callback + * to ensure any dangling reassembly buffer is freed + * + * @param conn_handle Connection handle + * @param steps Step data + * @param step_data_len Step data length + * @return struct net_buf* Pointer to reassembly buffer, NULL if fails to allocate or insert data + */ +static struct net_buf *start_reassembly(uint16_t conn_handle, const uint8_t *steps, + uint16_t step_data_len) +{ + struct net_buf *reassembly_buf = get_reassembly_buf(conn_handle, true); + + if (!reassembly_buf) { + LOG_ERR("No buffer allocated for the result reassembly"); + return NULL; + } + + if (reassembly_buf->len) { + LOG_WRN("Over-written incomplete CS subevent results"); + } + + net_buf_reset(reassembly_buf); + + bool success = add_reassembly_data(reassembly_buf, steps, step_data_len); + + return success ? reassembly_buf : NULL; +} + +/** @brief Adds more step data to reassembly buffer identified by the connection handle + * + * @param conn_handle Connection handle + * @param steps Step data + * @param step_data_len Step data length + * @return struct net_buf* Pointer to reassembly buffer, NULL if fails to insert data + */ +static struct net_buf *continue_reassembly(uint16_t conn_handle, const uint8_t *steps, + uint16_t step_data_len) +{ + struct net_buf *reassembly_buf = get_reassembly_buf(conn_handle, false); + + if (!reassembly_buf) { + LOG_ERR("No reassembly buffer was allocated for this CS procedure, possibly due to " + "an out-of-order subevent result continue event"); + return NULL; + } + + if (!reassembly_buf->len) { + LOG_WRN("Discarded out-of-order partial CS subevent results"); + return NULL; + } + + if (!step_data_len) { + return reassembly_buf; + } + + bool success = add_reassembly_data(reassembly_buf, steps, step_data_len); + + return success ? reassembly_buf : NULL; +} + +/** + * @brief Disconnect callback to clear any dangling reassembly buffer + * + * @param conn Connection + * @param reason Reason + */ +static void clear_on_disconnect(struct bt_conn *conn, uint8_t reason) +{ + struct net_buf *buf = get_reassembly_buf(conn->handle, false); + + if (buf) { + free_reassembly_buf(&buf); + } +} + +/** @brief Invokes user callback for new subevent results + * + * @param conn Connection context, NULL for CS Test subevent results + * @param p_result Pointer to subevent results + */ +static void invoke_subevent_result_callback(struct bt_conn *conn, + struct bt_conn_le_cs_subevent_result *p_result) +{ +#if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) + if (!conn) { + cs_test_callbacks.le_cs_test_subevent_data_available(p_result); + } else +#endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */ + { + notify_cs_subevent_result(conn, p_result); + } +} + +/** @brief Resets reassembly results + * + */ +static void reset_reassembly_results(void) +{ + memset(&reassembled_result, 0, sizeof(struct bt_conn_le_cs_subevent_result)); +} + void bt_le_cs_set_valid_chmap_bits(uint8_t channel_map[10]) { memset(channel_map, 0xFF, 10); @@ -360,7 +578,9 @@ void bt_hci_le_cs_subevent_result(struct net_buf *buf) struct bt_conn *conn = NULL; struct bt_hci_evt_le_cs_subevent_result *evt; struct bt_conn_le_cs_subevent_result result; + struct bt_conn_le_cs_subevent_result *p_result = &result; struct net_buf_simple step_data_buf; + struct net_buf *reassembly_buf = NULL; if (buf->len < sizeof(*evt)) { LOG_ERR("Unexpected end of buffer"); @@ -368,14 +588,10 @@ void bt_hci_le_cs_subevent_result(struct net_buf *buf) } evt = net_buf_pull_mem(buf, sizeof(*evt)); - - if (evt->subevent_done_status == BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { - LOG_WRN("Discarded incomplete CS subevent results."); - return; - } + uint16_t conn_handle = sys_le16_to_cpu(evt->conn_handle); #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) - if (sys_le16_to_cpu(evt->conn_handle) == BT_HCI_LE_CS_TEST_CONN_HANDLE) { + if (conn_handle == BT_HCI_LE_CS_TEST_CONN_HANDLE) { if (!cs_test_callbacks.le_cs_test_subevent_data_available) { LOG_WRN("No callback registered. Discarded subevent results from CS Test."); return; @@ -383,49 +599,177 @@ void bt_hci_le_cs_subevent_result(struct net_buf *buf) } else #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */ { - conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->conn_handle), BT_CONN_TYPE_LE); + conn = bt_conn_lookup_handle(conn_handle, BT_CONN_TYPE_LE); if (!conn) { LOG_ERR("Unknown connection handle when processing subevent results"); return; } } - result.header.procedure_counter = sys_le16_to_cpu(evt->procedure_counter); - result.header.frequency_compensation = sys_le16_to_cpu(evt->frequency_compensation); - result.header.procedure_done_status = evt->procedure_done_status; - result.header.subevent_done_status = evt->subevent_done_status; - result.header.procedure_abort_reason = evt->procedure_abort_reason; - result.header.subevent_abort_reason = evt->subevent_abort_reason; - result.header.reference_power_level = evt->reference_power_level; - result.header.num_antenna_paths = evt->num_antenna_paths; - result.header.num_steps_reported = evt->num_steps_reported; - - if (evt->num_steps_reported) { - net_buf_simple_init_with_data(&step_data_buf, - evt->steps, - buf->len); - result.step_data_buf = &step_data_buf; + if (evt->subevent_done_status != BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { + p_result->step_data_buf = NULL; + if (evt->num_steps_reported) { + net_buf_simple_init_with_data(&step_data_buf, evt->steps, buf->len); + p_result->step_data_buf = &step_data_buf; + } } else { - result.step_data_buf = NULL; + if (evt->procedure_done_status != BT_HCI_LE_CS_PROCEDURE_DONE_STATUS_PARTIAL) { + LOG_WRN("Procedure status is inconsistent with subevent status. Discarding " + "subevent results"); + goto abort; + } + + if (!evt->num_steps_reported) { + LOG_WRN("Discarding partial results without step data"); + goto abort; + } + + reassembly_buf = start_reassembly(conn_handle, evt->steps, buf->len); + if (!reassembly_buf) { + goto abort; + } + + p_result = &reassembled_result; + p_result->step_data_buf = (struct net_buf_simple *)&reassembly_buf->data; } -#if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) - if (sys_le16_to_cpu(evt->conn_handle) == BT_HCI_LE_CS_TEST_CONN_HANDLE) { - result.header.config_id = 0; - result.header.start_acl_conn_event = 0; + p_result->header.procedure_counter = sys_le16_to_cpu(evt->procedure_counter); + p_result->header.frequency_compensation = sys_le16_to_cpu(evt->frequency_compensation); + p_result->header.procedure_done_status = evt->procedure_done_status; + p_result->header.subevent_done_status = evt->subevent_done_status; + p_result->header.procedure_abort_reason = evt->procedure_abort_reason; + p_result->header.subevent_abort_reason = evt->subevent_abort_reason; + p_result->header.reference_power_level = evt->reference_power_level; + p_result->header.num_antenna_paths = evt->num_antenna_paths; + p_result->header.num_steps_reported = evt->num_steps_reported; + p_result->header.abort_step = + evt->subevent_done_status == BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_ABORTED ? 0 : 255; + + p_result->header.config_id = 0; + p_result->header.start_acl_conn_event = 0; + if (conn) { + p_result->header.config_id = evt->config_id; + p_result->header.start_acl_conn_event = + sys_le16_to_cpu(evt->start_acl_conn_event_counter); + } - cs_test_callbacks.le_cs_test_subevent_data_available(&result); + if (evt->subevent_done_status != BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { + invoke_subevent_result_callback(conn, p_result); + } + + if (evt->procedure_done_status != BT_CONN_LE_CS_PROCEDURE_INCOMPLETE) { + /* We can now clear the any reassembly buffer allocated for this procedure, + * to avoid code duplication, we're using the abort label to do so + */ + goto abort; + } + + if (conn) { + bt_conn_unref(conn); + conn = NULL; + } + + return; + +abort: + if (conn) { + bt_conn_unref(conn); + conn = NULL; + } + + reassembly_buf = get_reassembly_buf(conn_handle, false); + if (reassembly_buf) { + free_reassembly_buf(&reassembly_buf); + } +} + +void bt_hci_le_cs_subevent_result_continue(struct net_buf *buf) +{ + struct bt_conn *conn = NULL; + struct bt_hci_evt_le_cs_subevent_result_continue *evt; + struct net_buf *reassembly_buf = NULL; + uint16_t conn_handle; + + if (buf->len < sizeof(*evt)) { + LOG_ERR("Unexpected end of buffer"); + return; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + conn_handle = sys_le16_to_cpu(evt->conn_handle); +#if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) + if (conn_handle == BT_HCI_LE_CS_TEST_CONN_HANDLE) { + if (!cs_test_callbacks.le_cs_test_subevent_data_available) { + LOG_WRN("No callback registered. Discarded subevent results from CS Test."); + return; + } } else #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */ { - result.header.config_id = evt->config_id; - result.header.start_acl_conn_event = - sys_le16_to_cpu(evt->start_acl_conn_event_counter); + conn = bt_conn_lookup_handle(conn_handle, BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Unknown connection handle when processing subevent results"); + return; + } + } + + uint16_t step_data_len = evt->num_steps_reported ? buf->len : 0; - notify_cs_subevent_result(conn, &result); + reassembly_buf = continue_reassembly(conn_handle, evt->steps, step_data_len); + if (!reassembly_buf) { + goto abort; + } + + reassembled_result.header.procedure_done_status = evt->procedure_done_status; + reassembled_result.header.subevent_done_status = evt->subevent_done_status; + reassembled_result.header.procedure_abort_reason = evt->procedure_abort_reason; + reassembled_result.header.subevent_abort_reason = evt->subevent_abort_reason; + + if (evt->num_antenna_paths != reassembled_result.header.num_antenna_paths) { + LOG_WRN("Received inconsistent number of antenna paths from the controller: %d, " + "previous number was: %d", + evt->num_antenna_paths, reassembled_result.header.num_antenna_paths); + } + + if (evt->subevent_done_status == BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_ABORTED && + reassembled_result.header.num_steps_reported < reassembled_result.header.abort_step) { + reassembled_result.header.abort_step = reassembled_result.header.num_steps_reported; + } + + reassembled_result.header.num_steps_reported += evt->num_steps_reported; + + if (evt->subevent_done_status != BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { + invoke_subevent_result_callback(conn, &reassembled_result); + net_buf_reset(reassembly_buf); + reset_reassembly_results(); + } + + if (evt->procedure_done_status != BT_HCI_LE_CS_PROCEDURE_DONE_STATUS_PARTIAL) { + if (evt->subevent_done_status == BT_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { + LOG_WRN("Procedure status is inconsistent with subevent status. Discarding " + "subevent results"); + goto abort; + } + free_reassembly_buf(&reassembly_buf); + } + + if (conn) { + bt_conn_unref(conn); + conn = NULL; + } + + return; + +abort: + if (conn) { bt_conn_unref(conn); + conn = NULL; + } + + if (reassembly_buf) { + free_reassembly_buf(&reassembly_buf); } } @@ -533,6 +877,378 @@ int bt_le_cs_remove_config(struct bt_conn *conn, uint8_t config_id) return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_REMOVE_CONFIG, buf, NULL); } +int bt_le_cs_security_enable(struct bt_conn *conn) +{ + struct bt_hci_cp_le_security_enable *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_SECURITY_ENABLE, 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_SECURITY_ENABLE, buf, NULL); +} + +int bt_le_cs_procedure_enable(struct bt_conn *conn, + const struct bt_le_cs_procedure_enable_param *params) +{ + struct bt_hci_cp_le_procedure_enable *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_PROCEDURE_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->config_id = params->config_id; + cp->enable = params->enable; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_PROCEDURE_ENABLE, buf, NULL); +} + +int bt_le_cs_set_procedure_parameters(struct bt_conn *conn, + const struct bt_le_cs_set_procedure_parameters_param *params) +{ + struct bt_hci_cp_le_set_procedure_parameters *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_SET_PROCEDURE_PARAMETERS, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->config_id = params->config_id; + cp->max_procedure_len = sys_cpu_to_le16(params->max_procedure_len); + cp->min_procedure_interval = sys_cpu_to_le16(params->min_procedure_interval); + cp->max_procedure_interval = sys_cpu_to_le16(params->max_procedure_interval); + cp->max_procedure_count = sys_cpu_to_le16(params->max_procedure_count); + sys_put_le24(params->min_subevent_len, cp->min_subevent_len); + sys_put_le24(params->max_subevent_len, cp->max_subevent_len); + cp->tone_antenna_config_selection = params->tone_antenna_config_selection; + cp->phy = params->phy; + cp->tx_power_delta = params->tx_power_delta; + cp->preferred_peer_antenna = params->preferred_peer_antenna; + cp->snr_control_initiator = params->snr_control_initiator; + cp->snr_control_reflector = params->snr_control_reflector; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_SET_PROCEDURE_PARAMETERS, buf, NULL); +} + +int bt_le_cs_set_channel_classification(uint8_t channel_classification[10]) +{ + uint8_t *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_SET_CHANNEL_CLASSIFICATION, 10); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, 10); + memcpy(cp, channel_classification, 10); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_SET_CHANNEL_CLASSIFICATION, buf, NULL); +} + +int bt_le_cs_read_local_supported_capabilities(struct bt_conn_le_cs_capabilities *ret) +{ + struct bt_hci_rp_le_read_local_supported_capabilities *rp; + struct net_buf *rsp; + + int err = + bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES, NULL, &rsp); + + if (err) { + return err; + } + + rp = (void *)rsp->data; + + uint8_t status = rp->status; + + ret->num_config_supported = rp->num_config_supported; + ret->max_consecutive_procedures_supported = + sys_le16_to_cpu(rp->max_consecutive_procedures_supported); + ret->num_antennas_supported = rp->num_antennas_supported; + ret->max_antenna_paths_supported = rp->max_antenna_paths_supported; + + ret->initiator_supported = rp->roles_supported & BT_HCI_LE_CS_INITIATOR_ROLE_MASK; + ret->reflector_supported = rp->roles_supported & BT_HCI_LE_CS_REFLECTOR_ROLE_MASK; + ret->mode_3_supported = rp->modes_supported & BT_HCI_LE_CS_MODES_SUPPORTED_MODE_3_MASK; + + ret->rtt_aa_only_n = rp->rtt_aa_only_n; + ret->rtt_sounding_n = rp->rtt_sounding_n; + ret->rtt_random_payload_n = rp->rtt_random_payload_n; + + if (rp->rtt_aa_only_n) { + if (rp->rtt_capability & BT_HCI_LE_CS_RTT_AA_ONLY_N_10NS_MASK) { + ret->rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_10NS; + } else { + ret->rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_150NS; + } + } else { + ret->rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_NOT_SUPP; + } + + if (rp->rtt_sounding_n) { + if (rp->rtt_capability & BT_HCI_LE_CS_RTT_SOUNDING_N_10NS_MASK) { + ret->rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_10NS; + } else { + ret->rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_150NS; + } + } else { + ret->rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_NOT_SUPP; + } + + if (rp->rtt_random_payload_n) { + if (rp->rtt_capability & BT_HCI_LE_CS_RTT_RANDOM_PAYLOAD_N_10NS_MASK) { + ret->rtt_random_payload_precision = BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS; + } else { + ret->rtt_random_payload_precision = BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_150NS; + } + } else { + ret->rtt_random_payload_precision = BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_NOT_SUPP; + } + + ret->phase_based_nadm_sounding_supported = + sys_le16_to_cpu(rp->nadm_sounding_capability) & + BT_HCI_LE_CS_NADM_SOUNDING_CAPABILITY_PHASE_BASED_MASK; + + ret->phase_based_nadm_random_supported = + sys_le16_to_cpu(rp->nadm_random_capability) & + BT_HCI_LE_CS_NADM_RANDOM_CAPABILITY_PHASE_BASED_MASK; + + ret->cs_sync_2m_phy_supported = rp->cs_sync_phys_supported & BT_HCI_LE_CS_SYNC_PHYS_2M_MASK; + + ret->cs_sync_2m_2bt_phy_supported = + rp->cs_sync_phys_supported & BT_HCI_LE_CS_SYNC_PHYS_2M_2BT_MASK; + + ret->cs_without_fae_supported = + sys_le16_to_cpu(rp->subfeatures_supported) & BT_HCI_LE_CS_SUBFEATURE_NO_TX_FAE_MASK; + + ret->chsel_alg_3c_supported = sys_le16_to_cpu(rp->subfeatures_supported) & + BT_HCI_LE_CS_SUBFEATURE_CHSEL_ALG_3C_MASK; + + ret->pbr_from_rtt_sounding_seq_supported = + sys_le16_to_cpu(rp->subfeatures_supported) & + BT_HCI_LE_CS_SUBFEATURE_PBR_FROM_RTT_SOUNDING_SEQ_MASK; + + ret->t_ip1_times_supported = sys_le16_to_cpu(rp->t_ip1_times_supported); + ret->t_ip2_times_supported = sys_le16_to_cpu(rp->t_ip2_times_supported); + ret->t_fcs_times_supported = sys_le16_to_cpu(rp->t_fcs_times_supported); + ret->t_pm_times_supported = sys_le16_to_cpu(rp->t_pm_times_supported); + + ret->t_sw_time = rp->t_sw_time_supported; + ret->tx_snr_capability = rp->tx_snr_capability; + + net_buf_unref(rsp); + return status; +} + +int bt_le_cs_write_cached_remote_supported_capabilities( + struct bt_conn *conn, const struct bt_conn_le_cs_capabilities *params) +{ + struct bt_hci_cp_le_write_cached_remote_supported_capabilities *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES, + sizeof(*cp)); + + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(conn->handle); + + cp->num_config_supported = params->num_config_supported; + + cp->max_consecutive_procedures_supported = + sys_cpu_to_le16(params->max_consecutive_procedures_supported); + + cp->num_antennas_supported = params->num_antennas_supported; + cp->max_antenna_paths_supported = params->max_antenna_paths_supported; + + cp->roles_supported = 0; + if (params->initiator_supported) { + cp->roles_supported |= BT_HCI_LE_CS_INITIATOR_ROLE_MASK; + } + if (params->reflector_supported) { + cp->roles_supported |= BT_HCI_LE_CS_REFLECTOR_ROLE_MASK; + } + + cp->modes_supported = 0; + if (params->mode_3_supported) { + cp->modes_supported |= BT_HCI_LE_CS_MODES_SUPPORTED_MODE_3_MASK; + } + + cp->rtt_aa_only_n = params->rtt_aa_only_n; + cp->rtt_sounding_n = params->rtt_sounding_n; + cp->rtt_random_payload_n = params->rtt_random_payload_n; + + cp->rtt_capability = 0; + if (params->rtt_aa_only_precision == BT_CONN_LE_CS_RTT_AA_ONLY_10NS) { + cp->rtt_capability |= BT_HCI_LE_CS_RTT_AA_ONLY_N_10NS_MASK; + } + + if (params->rtt_sounding_precision == BT_CONN_LE_CS_RTT_SOUNDING_10NS) { + cp->rtt_capability |= BT_HCI_LE_CS_RTT_SOUNDING_N_10NS_MASK; + } + + if (params->rtt_random_payload_precision == BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS) { + cp->rtt_capability |= BT_HCI_LE_CS_RTT_RANDOM_PAYLOAD_N_10NS_MASK; + } + + cp->nadm_sounding_capability = 0; + if (params->phase_based_nadm_sounding_supported) { + cp->nadm_sounding_capability |= + sys_cpu_to_le16(BT_HCI_LE_CS_NADM_SOUNDING_CAPABILITY_PHASE_BASED_MASK); + } + + cp->nadm_random_capability = 0; + if (params->phase_based_nadm_random_supported) { + cp->nadm_random_capability |= + sys_cpu_to_le16(BT_HCI_LE_CS_NADM_RANDOM_CAPABILITY_PHASE_BASED_MASK); + } + + cp->cs_sync_phys_supported = 0; + if (params->cs_sync_2m_phy_supported) { + cp->cs_sync_phys_supported |= BT_HCI_LE_CS_SYNC_PHYS_2M_MASK; + } + if (params->cs_sync_2m_2bt_phy_supported) { + cp->cs_sync_phys_supported |= BT_HCI_LE_CS_SYNC_PHYS_2M_2BT_MASK; + } + + cp->subfeatures_supported = 0; + if (params->cs_without_fae_supported) { + cp->subfeatures_supported |= + sys_cpu_to_le16(BT_HCI_LE_CS_SUBFEATURE_NO_TX_FAE_MASK); + } + if (params->chsel_alg_3c_supported) { + cp->subfeatures_supported |= + sys_cpu_to_le16(BT_HCI_LE_CS_SUBFEATURE_CHSEL_ALG_3C_MASK); + } + if (params->pbr_from_rtt_sounding_seq_supported) { + cp->subfeatures_supported |= + sys_cpu_to_le16(BT_HCI_LE_CS_SUBFEATURE_PBR_FROM_RTT_SOUNDING_SEQ_MASK); + } + + cp->t_ip1_times_supported = sys_cpu_to_le16(params->t_ip1_times_supported); + cp->t_ip2_times_supported = sys_cpu_to_le16(params->t_ip2_times_supported); + cp->t_fcs_times_supported = sys_cpu_to_le16(params->t_fcs_times_supported); + cp->t_pm_times_supported = sys_cpu_to_le16(params->t_pm_times_supported); + cp->t_sw_time_supported = params->t_sw_time; + cp->tx_snr_capability = params->tx_snr_capability; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES, buf, + NULL); +} + +int bt_le_cs_write_cached_remote_fae_table(struct bt_conn *conn, uint8_t remote_fae_table[72]) +{ + struct bt_hci_cp_le_write_cached_remote_fae_table *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(conn->handle); + memcpy(cp->remote_fae_table, remote_fae_table, sizeof(cp->remote_fae_table)); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE, buf, NULL); +} + +void bt_hci_le_cs_security_enable_complete(struct net_buf *buf) +{ + struct bt_conn *conn; + + struct bt_hci_evt_le_cs_security_enable_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("Security Enable failed with status 0x%02X", evt->status); + return; + } + + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Can't lookup conn handle when reading Security Enable Complete event"); + return; + } + + notify_cs_security_enable_available(conn); + + bt_conn_unref(conn); +} + +void bt_hci_le_cs_procedure_enable_complete(struct net_buf *buf) +{ + struct bt_conn *conn; + + struct bt_hci_evt_le_cs_procedure_enable_complete *evt; + struct bt_conn_le_cs_procedure_enable_complete params; + + 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("Procedure Enable failed with status 0x%02X", evt->status); + return; + } + + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Can't lookup conn handle when reading Procedure Enable Complete event"); + return; + } + + if (evt->state == BT_HCI_OP_LE_CS_PROCEDURES_DISABLED) { + struct net_buf *reassembly_buf = get_reassembly_buf(conn->handle, false); + + if (reassembly_buf) { + LOG_WRN("De-allocating a dangling reassembly buffer"); + free_reassembly_buf(&reassembly_buf); + } + } + + params.config_id = evt->config_id; + params.state = evt->state; + params.tone_antenna_config_selection = evt->tone_antenna_config_selection; + params.selected_tx_power = evt->selected_tx_power; + params.subevent_len = sys_get_le24(evt->subevent_len); + params.subevents_per_event = evt->subevents_per_event; + params.subevent_interval = sys_le16_to_cpu(evt->subevent_interval); + params.event_interval = sys_le16_to_cpu(evt->event_interval); + params.procedure_interval = sys_le16_to_cpu(evt->procedure_interval); + params.procedure_count = sys_le16_to_cpu(evt->procedure_count); + params.max_procedure_len = sys_le16_to_cpu(evt->max_procedure_len); + + notify_cs_procedure_enable_available(conn, ¶ms); + + bt_conn_unref(conn); +} + #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) int bt_le_cs_stop_test(void) { @@ -561,6 +1277,13 @@ void bt_hci_le_cs_test_end_complete(struct net_buf *buf) return; } + struct net_buf *reassembly_buf = get_reassembly_buf(BT_HCI_LE_CS_TEST_CONN_HANDLE, false); + + if (reassembly_buf) { + LOG_WRN("De-allocating a dangling reassembly buffer"); + free_reassembly_buf(&reassembly_buf); + } + if (cs_test_callbacks.le_cs_test_end_complete) { cs_test_callbacks.le_cs_test_end_complete(); } diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 6d0670502c1..2db727fb6eb 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2830,9 +2830,18 @@ static const struct event_handler meta_events[] = { sizeof(struct bt_hci_evt_le_cs_read_remote_fae_table_complete)), EVENT_HANDLER(BT_HCI_EVT_LE_CS_CONFIG_COMPLETE, bt_hci_le_cs_config_complete_event, sizeof(struct bt_hci_evt_le_cs_config_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE, + bt_hci_le_cs_security_enable_complete, + sizeof(struct bt_hci_evt_le_cs_security_enable_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE, + bt_hci_le_cs_procedure_enable_complete, + sizeof(struct bt_hci_evt_le_cs_procedure_enable_complete)), EVENT_HANDLER(BT_HCI_EVT_LE_CS_SUBEVENT_RESULT, bt_hci_le_cs_subevent_result, sizeof(struct bt_hci_evt_le_cs_subevent_result)), + EVENT_HANDLER(BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE, + bt_hci_le_cs_subevent_result_continue, + sizeof(struct bt_hci_evt_le_cs_subevent_result_continue)), #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST) EVENT_HANDLER(BT_HCI_EVT_LE_CS_TEST_END_COMPLETE, bt_hci_le_cs_test_end_complete, @@ -3415,7 +3424,10 @@ static int le_set_event_mask(void) mask |= BT_EVT_MASK_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE; mask |= BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE; mask |= BT_EVT_MASK_LE_CS_CONFIG_COMPLETE; + mask |= BT_EVT_MASK_LE_CS_SECURITY_ENABLE_COMPLETE; + mask |= BT_EVT_MASK_LE_CS_PROCEDURE_ENABLE_COMPLETE; mask |= BT_EVT_MASK_LE_CS_SUBEVENT_RESULT; + mask |= BT_EVT_MASK_LE_CS_SUBEVENT_RESULT_CONTINUE; mask |= BT_EVT_MASK_LE_CS_TEST_END_COMPLETE; } diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 27387c9c53f..fb7a69539fd 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -542,7 +542,10 @@ void bt_hci_le_past_received_v2(struct net_buf *buf); 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); void bt_hci_le_cs_config_complete_event(struct net_buf *buf); +void bt_hci_le_cs_security_enable_complete(struct net_buf *buf); +void bt_hci_le_cs_procedure_enable_complete(struct net_buf *buf); void bt_hci_le_cs_subevent_result(struct net_buf *buf); +void bt_hci_le_cs_subevent_result_continue(struct net_buf *buf); void bt_hci_le_cs_test_end_complete(struct net_buf *buf); /* Adv HCI event handlers */ diff --git a/subsys/bluetooth/shell/cs.c b/subsys/bluetooth/shell/cs.c index fbc55623b14..b1f6f18aceb 100644 --- a/subsys/bluetooth/shell/cs.c +++ b/subsys/bluetooth/shell/cs.c @@ -219,9 +219,9 @@ static int cmd_cs_test_simple(const struct shell *sh, size_t argc, char *argv[]) params.t_fcs_time = 120; params.t_pm_time = 20; params.t_sw_time = 0; - params.tone_antenna_config_selection = BT_LE_CS_TEST_TONE_ANTENNA_CONFIGURATION_INDEX_ONE; - params.initiator_snr_control = BT_LE_CS_TEST_INITIATOR_SNR_CONTROL_NOT_USED; - params.reflector_snr_control = BT_LE_CS_TEST_REFLECTOR_SNR_CONTROL_NOT_USED; + params.tone_antenna_config_selection = BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_ONE; + params.initiator_snr_control = BT_LE_CS_INITIATOR_SNR_CONTROL_NOT_USED; + params.reflector_snr_control = BT_LE_CS_REFLECTOR_SNR_CONTROL_NOT_USED; params.drbg_nonce = 0x1234; params.override_config = 0; params.override_config_0.channel_map_repetition = 1; @@ -442,6 +442,255 @@ static int cmd_cs_stop_test(const struct shell *sh, size_t argc, char *argv[]) return 0; } +static int cmd_read_local_supported_capabilities(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + struct bt_conn_le_cs_capabilities params; + + err = bt_le_cs_read_local_supported_capabilities(¶ms); + + if (err) { + shell_error(sh, "bt_le_cs_read_local_supported_capabilities returned error %d", + err); + + return -ENOEXEC; + } + + shell_print( + sh, + "Local channel sounding supported 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); + + return 0; +} + +static int cmd_write_cached_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; + } + + struct bt_conn_le_cs_capabilities params; + + params.num_config_supported = 1; + params.max_consecutive_procedures_supported = 0; + params.num_antennas_supported = 1; + params.max_antenna_paths_supported = 1; + params.initiator_supported = true; + params.reflector_supported = true; + params.mode_3_supported = true; + params.rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_10NS; + params.rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_10NS; + params.rtt_random_payload_precision = BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS; + params.rtt_aa_only_n = 5; + params.rtt_sounding_n = 6; + params.rtt_random_payload_n = 7; + params.phase_based_nadm_sounding_supported = true; + params.phase_based_nadm_random_supported = true; + params.cs_sync_2m_phy_supported = true; + params.cs_sync_2m_2bt_phy_supported = true; + params.chsel_alg_3c_supported = true; + params.cs_without_fae_supported = true; + params.pbr_from_rtt_sounding_seq_supported = false; + params.t_ip1_times_supported = BT_HCI_LE_CS_T_IP1_TIME_10US_MASK; + params.t_ip2_times_supported = BT_HCI_LE_CS_T_IP2_TIME_10US_MASK; + params.t_fcs_times_supported = BT_HCI_LE_CS_T_FCS_TIME_100US_MASK; + params.t_sw_time = 0x04; + params.tx_snr_capability = BT_HCI_LE_CS_TX_SNR_CAPABILITY_18DB_MASK; + + err = bt_le_cs_write_cached_remote_supported_capabilities(default_conn, ¶ms); + + if (err) { + shell_error(sh, "bt_le_cs_set_channel_classification returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + +static int cmd_security_enable(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_le_cs_security_enable(default_conn); + + if (err) { + shell_error(sh, "bt_le_cs_security_enable returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + +static int cmd_set_channel_classification(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; + } + + uint8_t channel_classification[10]; + + for (int i = 0; i < 10; i++) { + channel_classification[i] = shell_strtoul(argv[1 + i], 16, &err); + + if (err) { + shell_help(sh); + shell_error(sh, "Could not parse input %d, Channel Classification[%d]", i, + i); + + return SHELL_CMD_HELP_PRINTED; + } + } + + err = bt_le_cs_set_channel_classification(channel_classification); + + if (err) { + shell_error(sh, "bt_le_cs_set_channel_classification returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + +static int cmd_set_procedure_parameters(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; + } + + struct bt_le_cs_set_procedure_parameters_param params; + + params.config_id = 0; + params.max_procedure_len = 1000; + params.min_procedure_interval = 5; + params.max_procedure_interval = 5000; + params.max_procedure_count = 1; + params.min_subevent_len = 5000; + params.max_subevent_len = 4000000; + params.tone_antenna_config_selection = BT_LE_CS_TONE_ANTENNA_CONFIGURATION_INDEX_ONE; + params.phy = 0x01; + params.tx_power_delta = 0x80; + params.preferred_peer_antenna = 1; + params.snr_control_initiator = BT_LE_CS_INITIATOR_SNR_CONTROL_18dB; + params.snr_control_reflector = BT_HCI_OP_LE_CS_REFLECTOR_SNR_18; + + err = bt_le_cs_set_procedure_parameters(default_conn, ¶ms); + + if (err) { + shell_error(sh, "bt_le_cs_set_procedure_parameters returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + +static int cmd_procedure_enable(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; + } + + struct bt_le_cs_procedure_enable_param params; + + params.config_id = shell_strtoul(argv[1], 16, &err); + + if (err) { + shell_help(sh); + shell_error(sh, "Could not parse input 1, Config ID"); + return SHELL_CMD_HELP_PRINTED; + } + + params.enable = shell_strtoul(argv[2], 16, &err); + + if (err) { + shell_help(sh); + shell_error(sh, "Could not parse input 2, Enable"); + return SHELL_CMD_HELP_PRINTED; + } + + err = bt_le_cs_procedure_enable(default_conn, ¶ms); + + if (err) { + shell_error(sh, "bt_le_cs_procedure_enable returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + SHELL_STATIC_SUBCMD_SET_CREATE( cs_cmds, SHELL_CMD_ARG(read_remote_supported_capabilities, NULL, "", @@ -463,7 +712,20 @@ SHELL_STATIC_SUBCMD_SET_CREATE( "128b-rand] [phy-1m, phy-2m, phy-2m-2b] [chmap-rep ] [hat-shape, x-shape] " "[ch3c-jump ] [chmap ] (78-0) [chsel-3b, chsel-3c]", cmd_create_config, 4, 15), - SHELL_CMD_ARG(remove_config, NULL, "", cmd_remove_config, 2, 0), SHELL_SUBCMD_SET_END); + SHELL_CMD_ARG(remove_config, NULL, "", cmd_remove_config, 2, 0), + SHELL_CMD_ARG(read_local_supported_capabilities, NULL, "", + cmd_read_local_supported_capabilities, 1, 0), + SHELL_CMD_ARG(write_cached_remote_supported_capabilities, NULL, "", + cmd_write_cached_remote_supported_capabilities, 1, 0), + SHELL_CMD_ARG(security_enable, NULL, "", cmd_security_enable, 1, 0), + SHELL_CMD_ARG(set_channel_classification, NULL, + " " + " ", + cmd_set_channel_classification, 11, 0), + SHELL_CMD_ARG(set_procedure_parameters, NULL, "", cmd_set_procedure_parameters, 1, 0), + SHELL_CMD_ARG(procedure_enable, NULL, " ", + cmd_procedure_enable, 3, 0), + SHELL_SUBCMD_SET_END); static int cmd_cs(const struct shell *sh, size_t argc, char **argv) {