From d396aab95c635ca249a391105b0f9f6e9b5dd414 Mon Sep 17 00:00:00 2001 From: Burak Gorduk Date: Tue, 15 Oct 2024 10:38:29 +0100 Subject: [PATCH 1/8] [nrf fromtree] bluetooth: host: LE CS subevent result reassembly Adds HCI support for LE CS subevent result continue event and the reassembly logic for the partial results. When subevent results are completed or the subevent is aborted, the user callback is invoked with a buffer pointing to the HCI event buffer, so no copy is done. When subevent results are incomplete, then a reassembly buffer is allocated from a fixed sized pool. This buffer is used for the reassembling of the subevent result containing all of the step data, which is then passed to the user via the callback. kconfigs have been added to set the size and the count of the reassembly buffer. Signed-off-by: Burak Gorduk (cherry picked from commit c5a126cedb2f96239662352ae1662424d261c53e) --- include/zephyr/bluetooth/conn.h | 20 +- include/zephyr/bluetooth/hci_types.h | 20 ++ subsys/bluetooth/Kconfig | 20 ++ subsys/bluetooth/host/cs.c | 415 ++++++++++++++++++++++++--- subsys/bluetooth/host/hci_core.c | 4 + subsys/bluetooth/host/hci_core.h | 1 + 6 files changed, 446 insertions(+), 34 deletions(-) diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 55bb48c69341..f81ca8b3ce8f 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; }; diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 131b81134c1c..cc3ea00afd18 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3636,6 +3636,25 @@ struct bt_hci_evt_le_cs_subevent_result { 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[]; +} __packed; + #define BT_HCI_EVT_LE_CS_TEST_END_COMPLETE 0x33 struct bt_hci_evt_le_cs_test_end_complete { uint8_t status; @@ -3734,6 +3753,7 @@ struct bt_hci_evt_le_cs_test_end_complete { #define BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE BT_EVT_BIT(44) #define BT_EVT_MASK_LE_CS_CONFIG_COMPLETE BT_EVT_BIT(46) #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 bf6feff37ebf..dbb8214a1364 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/cs.c b/subsys/bluetooth/host/cs.c index 75d5f21cf2c6..5a09c0172618 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; + } + + 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); } -#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; + 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; + } - cs_test_callbacks.le_cs_test_subevent_data_available(&result); + 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; + + reassembly_buf = continue_reassembly(conn_handle, evt->steps, step_data_len); + if (!reassembly_buf) { + goto abort; + } - notify_cs_subevent_result(conn, &result); + 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); } } @@ -561,6 +905,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 6d0670502c1c..0d0b5a9c60b0 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2833,6 +2833,9 @@ static const struct event_handler meta_events[] = { 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, @@ -3416,6 +3419,7 @@ static int le_set_event_mask(void) 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_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 27387c9c53fd..458576e74f78 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -543,6 +543,7 @@ void bt_hci_le_cs_read_remote_supported_capabilities_complete(struct net_buf *bu 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_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 */ From c15c6972b5eb7dc68500157f0893e9833f6feb40 Mon Sep 17 00:00:00 2001 From: Burak Gorduk Date: Tue, 15 Oct 2024 10:41:47 +0100 Subject: [PATCH 2/8] [nrf fromtree] bluetooth: host Accommodate platform endianness for CS types Modifies the CS bitfields that are used for accessing HCI event data to take platform endiannes into consideration. Signed-off-by: Burak Gorduk (cherry picked from commit bfe3c4353c7945fdedbdd99698eb228da6897749) --- include/zephyr/bluetooth/hci_types.h | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index cc3ea00afd18..a620097acafb 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3524,8 +3524,13 @@ struct bt_hci_evt_le_cs_config_complete { #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,16 +3538,26 @@ 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 { @@ -3554,8 +3569,13 @@ struct bt_hci_le_cs_step_data_mode_1 { /** 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 { @@ -3571,8 +3591,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,8 +3608,13 @@ 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 { @@ -3598,8 +3628,13 @@ 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 { @@ -3629,8 +3664,13 @@ 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[]; From 8c81aaff670f7a7aacdf859e055fd4f15f5dd1c6 Mon Sep 17 00:00:00 2001 From: Olivier Lesage Date: Tue, 15 Oct 2024 10:44:26 +0200 Subject: [PATCH 3/8] [nrf fromtree] bluetooth: host: Fix values for minimizing/maximizing TX power The values were swapped, leading to the opposite of the intended behavior Signed-off-by: Olivier Lesage (cherry picked from commit 51a0e9484d1f22a2cf9b7c04d8dab3a959bedf25) --- include/zephyr/bluetooth/hci_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index a620097acafb..83956de7d4a9 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -2463,8 +2463,8 @@ 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_MINIMIZE_TX_POWER 0x7E +#define BT_HCI_OP_LE_CS_TEST_MAXIMIZE_TX_POWER 0x7F #define BT_HCI_OP_LE_CS_TEST_ACI_0 0x0 #define BT_HCI_OP_LE_CS_TEST_ACI_1 0x1 From daf1f8f78f52f3020b2700fd2d903930a2aa0ae6 Mon Sep 17 00:00:00 2001 From: Olivier Lesage Date: Tue, 15 Oct 2024 10:45:56 +0200 Subject: [PATCH 4/8] [nrf fromtree] bluetooth: host: Add docs for t_XX times in CS Test These options are in microseconds but not fully configurable, document what options are allowed in 6.0 Signed-off-by: Olivier Lesage (cherry picked from commit 5d0366404ae628762dad95125bdd3816ee6e8290) --- include/zephyr/bluetooth/cs.h | 59 ++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/include/zephyr/bluetooth/cs.h b/include/zephyr/bluetooth/cs.h index fa3620831800..aa145b65f5c9 100644 --- a/include/zephyr/bluetooth/cs.h +++ b/include/zephyr/bluetooth/cs.h @@ -338,15 +338,64 @@ 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. From 8c79bab9c5def79c9ca353b0f11783f9fa09cb35 Mon Sep 17 00:00:00 2001 From: Olivier Lesage Date: Tue, 15 Oct 2024 10:46:44 +0200 Subject: [PATCH 5/8] [nrf fromtree] bluetooth: host: uint16_t -> int16_t mode 1 deltas These are signed 16-bit integers by spec Signed-off-by: Olivier Lesage (cherry picked from commit b1f34a1b19c426794d1ee190566b232d85709e08) --- include/zephyr/bluetooth/hci_types.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 83956de7d4a9..eb05e53b72c9 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3561,8 +3561,8 @@ struct bt_hci_le_cs_step_data_mode_1 { 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; @@ -3579,8 +3579,8 @@ struct bt_hci_le_cs_step_data_mode_1_ss_rtt { 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]; @@ -3618,8 +3618,8 @@ struct bt_hci_le_cs_step_data_mode_3 { 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; @@ -3638,8 +3638,8 @@ struct bt_hci_le_cs_step_data_mode_3_ss_rtt { 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]; From a6cd3e40022d2713e30dc839f4f3374d2f229717 Mon Sep 17 00:00:00 2001 From: Olivier Lesage Date: Tue, 15 Oct 2024 10:52:25 +0200 Subject: [PATCH 6/8] [nrf fromtree] bluetooth: host: Add defines for mode 1 packet quality and RSSI Add some defines for packet quality and rssi for RTT, these are useful for filtering out steps where the RTT exchange was unsuccessful Signed-off-by: Olivier Lesage (cherry picked from commit 6c954782d2c87c89326c1a5870241d070c8f1384) --- include/zephyr/bluetooth/hci_types.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index eb05e53b72c9..61f4747a7a83 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3521,6 +3521,12 @@ 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 { From ebb823643c44e286be221c39d8bddd163739e92d Mon Sep 17 00:00:00 2001 From: Olivier Lesage Date: Thu, 17 Oct 2024 10:29:28 +0200 Subject: [PATCH 7/8] [nrf fromtree] bluetooth: host: minor doc correction for bt_le_cs_step_data_parse() This function can be used to parse HCI-formatted subevent results regardless of whether they come from CS Test or not Signed-off-by: Olivier Lesage (cherry picked from commit 60a2d7d7d2e4ffa424a9cb0967ef5f279f6d54f6) --- include/zephyr/bluetooth/cs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/zephyr/bluetooth/cs.h b/include/zephyr/bluetooth/cs.h index aa145b65f5c9..5ba452667aad 100644 --- a/include/zephyr/bluetooth/cs.h +++ b/include/zephyr/bluetooth/cs.h @@ -718,7 +718,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. * From de8e21c713bb713f75a673f03113d1e9908d3bb2 Mon Sep 17 00:00:00 2001 From: Adam Cavender Date: Thu, 10 Oct 2024 11:10:25 +0100 Subject: [PATCH 8/8] [nrf fromtree] bluetooth: host: CS support for various HCI commands Adds HCI support for: - LE CS Security Enable - LE CS Procedure Enable - LE CS Set Procedure Parameters - LE CS Set Channel Classification - LE CS Read Local Supported Capabilities - LE CS Write Cached Remote Supported Capabilities - LE CS Write Cached Remote FAE Table Signed-off-by: Adam Cavender (cherry picked from commit 18f508144e4ffc50c13f02866ba4bf47335d35ea) --- include/zephyr/bluetooth/conn.h | 101 +++++++ include/zephyr/bluetooth/cs.h | 244 +++++++++++++---- include/zephyr/bluetooth/hci_types.h | 171 ++++++++++-- subsys/bluetooth/host/conn.c | 35 +++ subsys/bluetooth/host/conn_internal.h | 5 + subsys/bluetooth/host/cs.c | 372 ++++++++++++++++++++++++++ subsys/bluetooth/host/hci_core.c | 8 + subsys/bluetooth/host/hci_core.h | 2 + subsys/bluetooth/shell/cs.c | 270 ++++++++++++++++++- 9 files changed, 1127 insertions(+), 81 deletions(-) diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index f81ca8b3ce8f..23dd9fb89f8e 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -1518,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. @@ -1810,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 5ba452667aad..d5d34039d279 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 */ @@ -400,11 +362,11 @@ struct bt_le_cs_test_param { /** 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; @@ -736,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 61f4747a7a83..1b22e172fcca 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 */ @@ -2466,28 +2568,28 @@ struct bt_hci_cp_le_read_remote_fae_table { #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_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_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; @@ -3706,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)) @@ -3797,7 +3922,9 @@ 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) diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 47014a5c076d..5e853d57be16 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 ac2141c1aad8..6b6c97b19026 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 5a09c0172618..b22877d31b8a 100644 --- a/subsys/bluetooth/host/cs.c +++ b/subsys/bluetooth/host/cs.c @@ -877,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) { diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 0d0b5a9c60b0..2db727fb6eb7 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2830,6 +2830,12 @@ 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)), @@ -3418,6 +3424,8 @@ 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 458576e74f78..fb7a69539fd7 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -542,6 +542,8 @@ 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); diff --git a/subsys/bluetooth/shell/cs.c b/subsys/bluetooth/shell/cs.c index fbc55623b147..b1f6f18aceb3 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) {