* [PATCH BlueZ v1 0/2] Channel Sounding: Aggregate procedure data and expose via D-Bus @ 2026-07-17 7:26 Prathibha Madugonde 2026-07-17 7:26 ` [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API Prathibha Madugonde 2026-07-17 7:26 ` [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding Prathibha Madugonde 0 siblings, 2 replies; 7+ messages in thread From: Prathibha Madugonde @ 2026-07-17 7:26 UTC (permalink / raw) To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> This series adds infrastructure to aggregate Channel Sounding (CS) procedure results from both the local initiator and remote reflector into a single bcs_procedure_data structure, and exposes the completed data over D-Bus for use by distance-calculation applications. Patch overview: 1/2 shared/rap: Add bcs_procedure_data aggregation and procedure data API 2/2 profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding Prathibha Madugonde (2): shared/rap: Add bcs_procedure_data aggregation and procedure data API profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding profiles/ranging/rap.c | 497 ++++++++++++++++++++++++++- profiles/ranging/rap_hci.c | 59 ++++ src/shared/rap.c | 673 +++++++++++++++++++++++++++++++++++-- src/shared/rap.h | 50 +++ 4 files changed, 1242 insertions(+), 37 deletions(-) -- 2.34.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API 2026-07-17 7:26 [PATCH BlueZ v1 0/2] Channel Sounding: Aggregate procedure data and expose via D-Bus Prathibha Madugonde @ 2026-07-17 7:26 ` Prathibha Madugonde 2026-07-17 9:10 ` Channel Sounding: Aggregate procedure data and expose via D-Bus bluez.test.bot 2026-07-17 7:26 ` [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding Prathibha Madugonde 1 sibling, 1 reply; 7+ messages in thread From: Prathibha Madugonde @ 2026-07-17 7:26 UTC (permalink / raw) To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> Define bcs_procedure_data to hold per-procedure CS results from both the local initiator and remote reflector, including subevent step data, selected TX powers, procedure enable config, CS config, sw_time values, and BLE connection interval. Add cs_proc_state with a 16-entry ring buffer in cstracker to track procedures keyed by procedure counter, accumulating results independently from each side until both report all-results-complete. Update parse_mode_{0,1,2,3}, parse_step, and parse_subevent_steps with output pointer parameters to capture remote reflector step data. Introduce parse_cs_local_initiator_data() to store local HCI subevent results, handling both initial and continuation events. Update parse_ras_data_segments() to route decoded RAS subevent data into the matching cs_proc_state as reflector results. Add bt_rap_set_procedure_data_cb() to register a callback fired once both local and remote results are complete for a given procedure. Add bt_rap_set_local_sw_time(), bt_rap_set_remote_sw_time(), and bt_rap_set_conn_interval() so callers such as rap_hci.c can supply the CS capability sw_time and connection interval required by upper-layer distance calculation algorithms. Remove unwanted function rap_detached , fix for rap profile level disconnections --- src/shared/rap.c | 673 ++++++++++++++++++++++++++++++++++++++++++++--- src/shared/rap.h | 50 ++++ 2 files changed, 687 insertions(+), 36 deletions(-) diff --git a/src/shared/rap.c b/src/shared/rap.c index dfb272d3a..eadc41707 100644 --- a/src/shared/rap.c +++ b/src/shared/rap.c @@ -12,6 +12,8 @@ #include <stdbool.h> #include <unistd.h> #include <errno.h> +#include <time.h> +#include <glib.h> #include "bluetooth/bluetooth.h" #include "bluetooth/hci.h" @@ -151,6 +153,16 @@ static inline uint8_t ranging_header_get_antenna_mask( return hdr->antenna_pct & 0x0F; } +static inline uint16_t ranging_header_get_counter( + const struct ranging_header *hdr) +{ + if (!hdr) + return 0; + + return (uint16_t)(hdr->counter_config[0] | + ((hdr->counter_config[1] & 0x0F) << 8)); +} + static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask) { uint8_t count = 0; @@ -206,11 +218,23 @@ enum cs_role { }; #define CS_INVALID_CONFIG_ID 0xFF +#define CS_RANGING_COUNTER_MASK 0x0FFF + /* Minimal enums (align to controller values if needed) */ enum cs_procedure_done_status { CS_PROC_ALL_RESULTS_COMPLETE = 0x00, CS_PROC_PARTIAL_RESULTS = 0x01, - CS_PROC_ABORTED = 0x02 + CS_PROC_ABORTED = 0x0F +}; + +/* Per-procedure tracking — one entry per HCI procedure counter */ +struct cs_proc_state { + uint16_t proc_counter; + struct bcs_procedure_data bcs_data; + enum cs_procedure_done_status local_status; + bool local_has_complete_subevent; + enum cs_procedure_done_status remote_status; + bool remote_has_complete_subevent; }; /* Main cs_procedure_data */ @@ -253,6 +277,12 @@ struct cstracker { struct ranging_header ranging_header_; /* Client - subsequent segments appended using iovec */ struct iovec segment_data; + + /* Session-level config template and per-procedure state */ + struct bcs_procedure_data bcs_proc_data; + struct queue *proc_states; + uint8_t procedure_sequence_after_enable; + uint64_t proc_start_timestamp_nanos; }; /* Ranging Service context */ @@ -300,6 +330,15 @@ struct bt_rap { bt_rap_destroy_func_t debug_destroy; void *debug_data; void *user_data; + + bt_rap_procedure_data_func_t procedure_data_cb; + bt_rap_destroy_func_t procedure_data_destroy; + void *procedure_data_user_data; + + /* CS capabilities sw_time and connection interval for config param */ + uint8_t local_sw_time; + uint8_t remote_sw_time; + uint16_t conn_interval; struct cstracker *resptracker; struct cstracker *reqtracker; }; @@ -542,6 +581,177 @@ static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d, return true; } +/* ---------- bcs_procedure_data helpers ----------------------------------- */ +static uint64_t get_time_nanos(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_REALTIME, &ts); + return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +static void bcs_proc_data_clear(struct bcs_procedure_data *proc) +{ + uint32_t i; + + if (!proc) + return; + + for (i = 0; i < proc->initiator_subevent_count; i++) + free(proc->initiator_subevent_results[i].step_data); + free(proc->initiator_subevent_results); + proc->initiator_subevent_results = NULL; + proc->initiator_subevent_count = 0; + + for (i = 0; i < proc->reflector_subevent_count; i++) + free(proc->reflector_subevent_results[i].step_data); + free(proc->reflector_subevent_results); + proc->reflector_subevent_results = NULL; + proc->reflector_subevent_count = 0; +} + +static bool bcs_proc_data_add_initiator_subevent( + struct bcs_procedure_data *proc, + uint16_t start_acl_evt, + uint16_t freq_comp, + int8_t ref_pwr, + uint8_t num_ant_paths, + uint8_t abort_reason, + uint64_t timestamp_nanos, + struct cs_step_data *steps, + uint32_t num_steps) +{ + struct cs_subevent_result_data *arr; + uint32_t new_count; + + if (!proc) + return false; + + new_count = proc->initiator_subevent_count + 1; + arr = realloc(proc->initiator_subevent_results, + new_count * sizeof(*arr)); + if (!arr) { + free(steps); + return false; + } + + arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt; + arr[new_count - 1].freq_comp = freq_comp; + arr[new_count - 1].ref_pwr_lvl = ref_pwr; + arr[new_count - 1].num_ant_paths = num_ant_paths; + arr[new_count - 1].subevent_abort_reason = abort_reason; + arr[new_count - 1].timestamp_nanos = timestamp_nanos; + arr[new_count - 1].num_steps = num_steps; + arr[new_count - 1].step_data = steps; + + proc->initiator_subevent_results = arr; + proc->initiator_subevent_count = new_count; + return true; +} + +static bool bcs_proc_data_add_reflector_subevent( + struct bcs_procedure_data *proc, + uint16_t start_acl_evt, + uint16_t freq_comp, + int8_t ref_pwr, + uint8_t num_ant_paths, + uint8_t abort_reason, + uint64_t timestamp_nanos, + struct cs_step_data *steps, + uint32_t num_steps) +{ + struct cs_subevent_result_data *arr; + uint32_t new_count; + + if (!proc) + return false; + + new_count = proc->reflector_subevent_count + 1; + arr = realloc(proc->reflector_subevent_results, + new_count * sizeof(*arr)); + if (!arr) { + free(steps); + return false; + } + + arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt; + arr[new_count - 1].freq_comp = freq_comp; + arr[new_count - 1].ref_pwr_lvl = ref_pwr; + arr[new_count - 1].num_ant_paths = num_ant_paths; + arr[new_count - 1].subevent_abort_reason = abort_reason; + arr[new_count - 1].timestamp_nanos = timestamp_nanos; + arr[new_count - 1].num_steps = num_steps; + arr[new_count - 1].step_data = steps; + + proc->reflector_subevent_results = arr; + proc->reflector_subevent_count = new_count; + return true; +} + +static bool match_proc_counter(const void *data, const void *match_data) +{ + const struct cs_proc_state *s = data; + uint16_t proc_counter = PTR_TO_UINT(match_data); + + return s->proc_counter == proc_counter; +} + +static void free_proc_state(void *data) +{ + struct cs_proc_state *s = data; + + bcs_proc_data_clear(&s->bcs_data); + free(s); +} + +static struct cs_proc_state *find_or_create_proc_state(struct cstracker *t, + uint16_t proc_counter) +{ + struct cs_proc_state *s; + + s = queue_find(t->proc_states, match_proc_counter, + UINT_TO_PTR(proc_counter)); + if (s) + return s; + + s = new0(struct cs_proc_state, 1); + s->proc_counter = proc_counter; + s->local_status = CS_PROC_PARTIAL_RESULTS; + s->remote_status = CS_PROC_PARTIAL_RESULTS; + /* Copy session-level config from template */ + s->bcs_data.proc_enable_config = t->bcs_proc_data.proc_enable_config; + s->bcs_data.cs_config = t->bcs_proc_data.cs_config; + s->bcs_data.t_sw_time_us_supported_by_local = + t->bcs_proc_data.t_sw_time_us_supported_by_local; + s->bcs_data.t_sw_time_us_supported_by_remote = + t->bcs_proc_data.t_sw_time_us_supported_by_remote; + s->bcs_data.ble_conn_interval = t->bcs_proc_data.ble_conn_interval; + s->bcs_data.initiator_selected_tx_power = + t->bcs_proc_data.initiator_selected_tx_power; + s->bcs_data.procedure_counter = proc_counter; + + queue_push_tail(t->proc_states, s); + + return s; +} + +static struct cs_proc_state *find_proc_state_for_ras(struct cstracker *t, + uint16_t ranging_counter) +{ + const struct queue_entry *entry; + + for (entry = queue_get_entries(t->proc_states); entry; + entry = entry->next) { + struct cs_proc_state *s = entry->data; + + if ((s->proc_counter & CS_RANGING_COUNTER_MASK) == + ranging_counter) + return s; + } + return NULL; +} +/* ---------- end bcs helpers ---------------------------------------------- */ + static struct ras *rap_get_ras(struct bt_rap *rap) { if (!rap) @@ -556,14 +766,6 @@ static struct ras *rap_get_ras(struct bt_rap *rap) return rap->rrapdb->ras; } -static void rap_detached(void *data, void *user_data) -{ - struct bt_rap_cb *cb = data; - struct bt_rap *rap = user_data; - - cb->detached(rap, cb->user_data); -} - void bt_rap_detach(struct bt_rap *rap) { if (!queue_remove(sessions, rap)) @@ -572,8 +774,6 @@ void bt_rap_detach(struct bt_rap *rap) bt_gatt_client_idle_unregister(rap->client, rap->idle_id); bt_gatt_client_unref(rap->client); rap->client = NULL; - - queue_foreach(bt_rap_cbs, rap_detached, rap); } static void rap_db_free(void *data) @@ -608,15 +808,21 @@ static void rap_free(void *data) rap_db_free(rap->rrapdb); if (rap->resptracker) { + free(rap->resptracker->segment_data.iov_base); free(rap->resptracker); rap->resptracker = NULL; } if (rap->reqtracker) { + queue_destroy(rap->reqtracker->proc_states, free_proc_state); + free(rap->reqtracker->segment_data.iov_base); free(rap->reqtracker); rap->reqtracker = NULL; } + if (rap->procedure_data_destroy) + rap->procedure_data_destroy(rap->procedure_data_user_data); + queue_destroy(rap->notify, free); queue_destroy(rap->pending, NULL); queue_destroy(rap->ready_cbs, rap_ready_free); @@ -702,6 +908,24 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func, return true; } +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap, + bt_rap_procedure_data_func_t cb, + void *user_data, + bt_rap_destroy_func_t destroy) +{ + if (!rap) + return false; + + if (rap->procedure_data_destroy) + rap->procedure_data_destroy(rap->procedure_data_user_data); + + rap->procedure_data_cb = cb; + rap->procedure_data_destroy = destroy; + rap->procedure_data_user_data = user_data; + + return true; +} + static void cs_tracker_init(struct cstracker *t) { if (!t) @@ -716,6 +940,7 @@ static void cs_tracker_init(struct cstracker *t) t->last_start_acl_conn_evt_counter = 0; t->last_freq_comp = 0; t->last_ref_pwr_lvl = 0; + t->proc_states = queue_new(); /* Initialize ranging header using helper functions */ memset(&t->ranging_header_, 0, sizeof(t->ranging_header_)); @@ -1804,12 +2029,180 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap, cont->step_data); } +static void write_procedure_data_to_bcs_algo(struct bt_rap *rap, + struct bcs_procedure_data *bcs) +{ + if (!rap || !bcs) + return; + + if (rap->reqtracker && rap->reqtracker->proc_start_timestamp_nanos) { + uint64_t elapsed_nanos = get_time_nanos() - + rap->reqtracker->proc_start_timestamp_nanos; + uint32_t k; + + DBG(rap, "Procedure elapsed time: %llu nanos", + (unsigned long long)elapsed_nanos); + + for (k = 0; k < bcs->initiator_subevent_count; k++) + bcs->initiator_subevent_results[k].timestamp_nanos = + elapsed_nanos; + for (k = 0; k < bcs->reflector_subevent_count; k++) + bcs->reflector_subevent_results[k].timestamp_nanos = + elapsed_nanos; + } + + DBG(rap, "procedure_counter=%u sequence=%u " + "init_tx_pwr=%d refl_tx_pwr=%d " + "init_abort=%d refl_abort=%d " + "init_subevents=%u refl_subevents=%u", + bcs->procedure_counter, bcs->procedure_sequence, + bcs->initiator_selected_tx_power, + bcs->reflector_selected_tx_power, + bcs->initiator_procedure_abort_reason, + bcs->reflector_procedure_abort_reason, + bcs->initiator_subevent_count, + bcs->reflector_subevent_count); + + if (rap->procedure_data_cb) + rap->procedure_data_cb(rap, bcs, + rap->procedure_data_user_data); + + bcs_proc_data_clear(bcs); +} + +static void check_cs_procedure_complete(struct bt_rap *rap, + struct cstracker *reqtracker, + struct cs_proc_state *state) +{ + struct bcs_procedure_data *bcs = &state->bcs_data; + + if (!rap->procedure_data_cb) + return; + + if (state->local_status != CS_PROC_ALL_RESULTS_COMPLETE || + state->remote_status != CS_PROC_ALL_RESULTS_COMPLETE) { + DBG(rap, "Procedure not complete: local=%d remote=%d", + state->local_status, state->remote_status); + return; + } + + if (!state->local_has_complete_subevent && + !state->remote_has_complete_subevent) { + DBG(rap, "No complete subevent available"); + return; + } + + reqtracker->procedure_sequence_after_enable++; + bcs->procedure_sequence = reqtracker->procedure_sequence_after_enable; + write_procedure_data_to_bcs_algo(rap, bcs); + + queue_remove(reqtracker->proc_states, state); + free(state); +} + +static void parse_cs_local_initiator_data(struct bt_rap *rap, + bool has_header_fields, + uint8_t config_id, + uint8_t num_ant_paths, + uint16_t proc_counter, + uint16_t start_acl_conn_evt_counter, + uint16_t freq_comp, + int8_t ref_pwr_lvl, + uint8_t proc_done_status, + uint8_t subevt_done_status, + uint8_t abort_reason, + uint8_t num_steps_reported, + const struct cs_step_data *hci_steps) +{ + struct cstracker *reqtracker = rap->reqtracker; + uint16_t effective_counter; + struct cs_proc_state *state; + struct bcs_procedure_data *bcs; + + effective_counter = has_header_fields ? proc_counter + : reqtracker->last_proc_counter; + + state = find_or_create_proc_state(reqtracker, effective_counter); + if (!state) + return; + + bcs = &state->bcs_data; + + bcs->initiator_selected_tx_power = reqtracker->selected_tx_power; + bcs->initiator_procedure_abort_reason = abort_reason & 0x0F; + + state->local_status = + (enum cs_procedure_done_status)(proc_done_status & 0x0F); + if ((subevt_done_status & 0x0F) == 0x00) + state->local_has_complete_subevent = true; + + if (has_header_fields) { + struct cs_step_data *steps = NULL; + + reqtracker->proc_start_timestamp_nanos = get_time_nanos(); + + bcs->procedure_counter = proc_counter; + reqtracker->last_proc_counter = proc_counter; + reqtracker->last_start_acl_conn_evt_counter = + start_acl_conn_evt_counter; + reqtracker->last_freq_comp = freq_comp; + reqtracker->last_ref_pwr_lvl = ref_pwr_lvl; + + if (num_steps_reported > 0 && hci_steps) { + steps = calloc(num_steps_reported, sizeof(*steps)); + if (!steps) + return; + memcpy(steps, hci_steps, + num_steps_reported * sizeof(*steps)); + } + + bcs_proc_data_add_initiator_subevent(bcs, + start_acl_conn_evt_counter, + freq_comp, ref_pwr_lvl, + num_ant_paths, + (abort_reason >> 4) & 0x0F, + 0, + steps, num_steps_reported); + } else { + struct cs_subevent_result_data *last; + struct cs_step_data *new_steps; + uint32_t old_count, new_count; + + if (!bcs->initiator_subevent_results || + bcs->initiator_subevent_count == 0) + return; + + last = &bcs->initiator_subevent_results[ + bcs->initiator_subevent_count - 1]; + old_count = last->num_steps; + new_count = old_count + num_steps_reported; + + if (num_steps_reported > 0 && hci_steps) { + new_steps = realloc(last->step_data, + new_count * sizeof(*new_steps)); + if (!new_steps) + return; + + memcpy(&new_steps[old_count], hci_steps, + num_steps_reported * sizeof(*new_steps)); + last->step_data = new_steps; + last->num_steps = new_count; + } + + last->subevent_abort_reason = (abort_reason >> 4) & 0x0F; + } + + check_cs_procedure_complete(rap, reqtracker, state); +} + + static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap, const struct rap_ev_cs_subevent_result_cont *cont, uint16_t length) { size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont, step_data); + struct cstracker *reqtracker; if (!rap || !rap->reqtracker || !cont) return; @@ -1819,6 +2212,22 @@ static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap, DBG(rap, "Received CS subevent result continue subevent: len=%u", length); + + reqtracker = rap->reqtracker; + + parse_cs_local_initiator_data(rap, + false, + cont->config_id, + cont->num_ant_paths, + reqtracker->last_proc_counter, + reqtracker->last_start_acl_conn_evt_counter, + reqtracker->last_freq_comp, + reqtracker->last_ref_pwr_lvl, + cont->proc_done_status, + cont->subevt_done_status, + cont->abort_reason, + cont->num_steps_reported, + cont->step_data); } static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap, @@ -1836,7 +2245,20 @@ static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap, return; DBG(rap, "Received CS subevent result subevent: len=%u", length); - /* TODO: Store initiator subevent result data */ + + parse_cs_local_initiator_data(rap, + true, + data->config_id, + data->num_ant_paths, + data->proc_counter, + data->start_acl_conn_evt_counter, + data->freq_comp, + data->ref_pwr_lvl, + data->proc_done_status, + data->subevt_done_status, + data->abort_reason, + data->num_steps_reported, + data->step_data); } void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length, @@ -1880,6 +2302,7 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length, const struct rap_ev_cs_proc_enable_cmplt *data = param; struct bt_rap *rap = user_data; struct cstracker *resptracker; + struct cstracker *reqtracker; DBG(rap, "Received CS procedure enable complete subevent: len=%u", length); @@ -1895,6 +2318,21 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length, /* Populate responder tracker */ resptracker->config_id = data->config_id; resptracker->selected_tx_power = data->sel_tx_pwr; + + if (!rap->reqtracker) { + reqtracker = new0(struct cstracker, 1); + cs_tracker_init(reqtracker); + rap->reqtracker = reqtracker; + } + + reqtracker = rap->reqtracker; + reqtracker->config_id = data->config_id; + reqtracker->selected_tx_power = data->sel_tx_pwr; + reqtracker->bcs_proc_data.initiator_selected_tx_power = + data->sel_tx_pwr; + + /* Store procedure enable config in the session-level template */ + reqtracker->bcs_proc_data.proc_enable_config = *data; } void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length, @@ -1945,6 +2383,39 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length, reqtracker->config_id = data->config_id; reqtracker->role = data->role; reqtracker->rtt_type = data->rtt_type; + reqtracker->procedure_sequence_after_enable = 0; + + /* Store cs_config in session-level template for procedure data */ + reqtracker->bcs_proc_data.cs_config = *data; + reqtracker->bcs_proc_data.t_sw_time_us_supported_by_local = + rap->local_sw_time; + reqtracker->bcs_proc_data.t_sw_time_us_supported_by_remote = + rap->remote_sw_time; + reqtracker->bcs_proc_data.ble_conn_interval = rap->conn_interval; +} + +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time) +{ + if (!rap) + return; + + rap->local_sw_time = local_sw_time; +} + +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time) +{ + if (!rap) + return; + + rap->remote_sw_time = remote_sw_time; +} + +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval) +{ + if (!rap) + return; + + rap->conn_interval = conn_interval; } struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb) @@ -2122,7 +2593,8 @@ static size_t get_mode_zero_length(enum cs_role remote_role) } static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov, - enum cs_role remote_role) + enum cs_role remote_role, + struct cs_mode_zero_data *out) { uint8_t packet_quality; int8_t packet_rssi_dbm; @@ -2143,7 +2615,12 @@ static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov, } } - /* TODO: Store this data as reflector data */ + if (out) { + out->packet_quality = packet_quality; + out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm; + out->packet_ant = packet_ant; + out->init_measured_freq_offset = init_measured_freq_offset; + } } static size_t get_mode_one_length(bool include_pct) @@ -2154,7 +2631,8 @@ static size_t get_mode_one_length(bool include_pct) } static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov, - enum cs_role remote_role, bool include_pct) + enum cs_role remote_role, bool include_pct, + struct cs_mode_one_data *out) { uint8_t packet_quality; uint8_t packet_nadm; @@ -2178,7 +2656,20 @@ static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov, parse_i_q_sample(mode_iov, &pct2_i, &pct2_q); } - /* TODO: Store this data as reflector data */ + if (out) { + out->packet_quality = packet_quality; + out->packet_nadm = packet_nadm; + out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm; + out->packet_ant = packet_ant; + if (remote_role == CS_ROLE_REFLECTOR) + out->tod_toa_refl = time_value; + else + out->toa_tod_init = time_value; + out->packet_pct1.i_sample = pct1_i; + out->packet_pct1.q_sample = pct1_q; + out->packet_pct2.i_sample = pct2_i; + out->packet_pct2.q_sample = pct2_q; + } } static size_t get_mode_two_length(uint8_t num_antenna_paths) @@ -2189,7 +2680,8 @@ static size_t get_mode_two_length(uint8_t num_antenna_paths) } static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov, - uint8_t num_antenna_paths) + uint8_t num_antenna_paths, + struct cs_mode_two_data *out) { uint8_t ant_perm_index; int16_t tone_pct_i[5]; @@ -2228,7 +2720,14 @@ static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov, DBG(rap, " cs_mode_two_data: ant_perm_idx=%u", ant_perm_index); - /* TODO: Store this data as reflector data */ + if (out) { + out->ant_perm_index = ant_perm_index; + for (k = 0; k < num_paths; k++) { + out->tone_pct[k].i_sample = tone_pct_i[k]; + out->tone_pct[k].q_sample = tone_pct_q[k]; + out->tone_quality_indicator[k] = tone_quality[k]; + } + } } static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct) @@ -2239,13 +2738,17 @@ static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct) static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov, enum cs_role remote_role, bool include_pct, - uint8_t num_antenna_paths) + uint8_t num_antenna_paths, + struct cs_mode_three_data *out) { + struct cs_mode_one_data *out_m1 = out ? &out->mode_one_data : NULL; + struct cs_mode_two_data *out_m2 = out ? &out->mode_two_data : NULL; + /* Mode 3 = Mode 1 + Mode 2 */ - parse_mode_one(rap, mode_iov, remote_role, include_pct); + parse_mode_one(rap, mode_iov, remote_role, include_pct, out_m1); if (mode_iov->iov_len > 0) - parse_mode_two(rap, mode_iov, num_antenna_paths); + parse_mode_two(rap, mode_iov, num_antenna_paths, out_m2); } static bool parse_subevent_header(struct iovec *iov, @@ -2278,7 +2781,8 @@ static bool parse_subevent_header(struct iovec *iov, static bool parse_step(struct bt_rap *rap, struct iovec *iov, struct cstracker *reqtracker, - uint8_t num_antenna_paths, uint8_t step_idx) + uint8_t num_antenna_paths, uint8_t step_idx, + struct cs_step_data *out_step) { uint8_t mode_byte, step_mode; bool include_pct; @@ -2337,20 +2841,42 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov, mode_iov.iov_base = payload; mode_iov.iov_len = step_payload_len; + if (out_step) { + out_step->step_mode = step_mode; + out_step->step_chnl = 0; + out_step->step_data_length = (uint8_t)step_payload_len; + } + switch (step_mode) { - case CS_MODE_ZERO: - parse_mode_zero(rap, &mode_iov, remote_role); + case CS_MODE_ZERO: { + struct cs_mode_zero_data *out = out_step ? + &out_step->step_mode_data.mode_zero_data : NULL; + + parse_mode_zero(rap, &mode_iov, remote_role, out); break; - case CS_MODE_ONE: - parse_mode_one(rap, &mode_iov, remote_role, include_pct); + } + case CS_MODE_ONE: { + struct cs_mode_one_data *out = out_step ? + &out_step->step_mode_data.mode_one_data : NULL; + + parse_mode_one(rap, &mode_iov, remote_role, include_pct, out); break; - case CS_MODE_TWO: - parse_mode_two(rap, &mode_iov, num_antenna_paths); + } + case CS_MODE_TWO: { + struct cs_mode_two_data *out = out_step ? + &out_step->step_mode_data.mode_two_data : NULL; + + parse_mode_two(rap, &mode_iov, num_antenna_paths, out); break; - case CS_MODE_THREE: + } + case CS_MODE_THREE: { + struct cs_mode_three_data *out = out_step ? + &out_step->step_mode_data.mode_three_data : NULL; + parse_mode_three(rap, &mode_iov, remote_role, include_pct, - num_antenna_paths); + num_antenna_paths, out); break; + } default: break; } @@ -2360,12 +2886,16 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov, static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov, struct cstracker *reqtracker, - uint8_t num_antenna_paths, uint8_t num_steps) + uint8_t num_antenna_paths, uint8_t num_steps, + struct cs_step_data *out_steps) { uint8_t i; for (i = 0; i < num_steps; i++) { - if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i)) + struct cs_step_data *out = out_steps ? &out_steps[i] : NULL; + + if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i, + out)) break; } } @@ -2376,6 +2906,10 @@ static void parse_ras_data_segments(struct bt_rap *rap, struct iovec iov; uint8_t antenna_mask; uint8_t num_antenna_paths; + uint16_t ranging_counter; + struct cs_proc_state *state = NULL; + struct bcs_procedure_data *bcs = NULL; + bool is_initiator; if (!rap || !reqtracker) return; @@ -2387,10 +2921,30 @@ static void parse_ras_data_segments(struct bt_rap *rap, ranging_header_get_antenna_mask(&reqtracker->ranging_header_); num_antenna_paths = antenna_mask_count_paths(antenna_mask); + ranging_counter = + ranging_header_get_counter(&reqtracker->ranging_header_); + + /* Find the per-procedure state that matches this RAS counter */ + if (rap->procedure_data_cb) { + state = find_proc_state_for_ras(reqtracker, ranging_counter); + if (state) { + bcs = &state->bcs_data; + bcs->reflector_selected_tx_power = + reqtracker->ranging_header_.selected_tx_power; + } + } + + /* When local role=INITIATOR, remote data goes to + * reflector_subevent_results. When local role=REFLECTOR, + * remote data goes to initiator_subevent_results. + */ + is_initiator = (reqtracker->role == CS_ROLE_INITIATOR); + iov = reqtracker->segment_data; while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) { struct ras_subevent_header hdr; + struct cs_step_data *steps = NULL; if (!parse_subevent_header(&iov, &hdr)) break; @@ -2402,19 +2956,66 @@ static void parse_ras_data_segments(struct bt_rap *rap, hdr.reference_power_level, hdr.num_steps_reported); + if (bcs && hdr.num_steps_reported > 0) + steps = calloc(hdr.num_steps_reported, sizeof(*steps)); + parse_subevent_steps(rap, &iov, reqtracker, num_antenna_paths, - hdr.num_steps_reported); + hdr.num_steps_reported, + steps); + + if (bcs) { + uint32_t stored_steps = + steps ? hdr.num_steps_reported : 0; + + if (is_initiator) + bcs_proc_data_add_reflector_subevent(bcs, + hdr.start_acl_conn_event, + hdr.frequency_compensation, + hdr.reference_power_level, + num_antenna_paths, + hdr.subevent_abort_reason, + 0, + steps, stored_steps); + else + bcs_proc_data_add_initiator_subevent(bcs, + hdr.start_acl_conn_event, + hdr.frequency_compensation, + hdr.reference_power_level, + num_antenna_paths, + hdr.subevent_abort_reason, + 0, + steps, stored_steps); + steps = NULL; /* ownership transferred */ + } else { + free(steps); + steps = NULL; + } - if (hdr.subevent_done_status == - SUBEVENT_DONE_ALL_RESULTS_COMPLETE || - hdr.ranging_done_status == + if (state) { + if (hdr.subevent_done_status == + SUBEVENT_DONE_ALL_RESULTS_COMPLETE) + state->remote_has_complete_subevent = true; + if (hdr.ranging_done_status == + RANGING_DONE_ALL_RESULTS_COMPLETE) + state->remote_status = + CS_PROC_ALL_RESULTS_COMPLETE; + } + + if (bcs && hdr.ranging_abort_reason) + bcs->reflector_procedure_abort_reason = + hdr.ranging_abort_reason & 0x0F; + + if (hdr.ranging_done_status == RANGING_DONE_ALL_RESULTS_COMPLETE) { DBG(rap, "Ranging procedure complete"); break; } } + if (state) + check_cs_procedure_complete(rap, reqtracker, state); + free(reqtracker->segment_data.iov_base); reqtracker->segment_data.iov_base = NULL; reqtracker->segment_data.iov_len = 0; diff --git a/src/shared/rap.h b/src/shared/rap.h index 5582635e6..342b504ea 100644 --- a/src/shared/rap.h +++ b/src/shared/rap.h @@ -157,10 +157,45 @@ struct rap_ev_cs_subevent_result_cont { struct cs_step_data step_data[]; }; +struct cs_subevent_result_data { + uint16_t start_acl_conn_evt_counter; + uint16_t freq_comp; + int8_t ref_pwr_lvl; + uint8_t num_ant_paths; + uint8_t subevent_abort_reason; + uint64_t timestamp_nanos; + uint32_t num_steps; + struct cs_step_data *step_data; +}; + +struct bcs_procedure_data { + uint16_t procedure_counter; + uint16_t procedure_sequence; + + int8_t initiator_selected_tx_power; + int8_t reflector_selected_tx_power; + + struct cs_subevent_result_data *initiator_subevent_results; + uint32_t initiator_subevent_count; + uint8_t initiator_procedure_abort_reason; + + struct cs_subevent_result_data *reflector_subevent_results; + uint32_t reflector_subevent_count; + uint8_t reflector_procedure_abort_reason; + + struct rap_ev_cs_proc_enable_cmplt proc_enable_config; + struct rap_ev_cs_config_cmplt cs_config; + uint8_t t_sw_time_us_supported_by_local; + uint8_t t_sw_time_us_supported_by_remote; + uint16_t ble_conn_interval; +}; typedef void (*bt_rap_debug_func_t)(const char *str, void *user_data); typedef void (*bt_rap_ready_func_t)(struct bt_rap *rap, void *user_data); typedef void (*bt_rap_destroy_func_t)(void *user_data); typedef void (*bt_rap_func_t)(struct bt_rap *rap, void *user_data); +typedef void (*bt_rap_procedure_data_func_t)(struct bt_rap *rap, + struct bcs_procedure_data *data, + void *user_data); struct bt_rap *bt_rap_ref(struct bt_rap *rap); void bt_rap_unref(struct bt_rap *rap); @@ -177,6 +212,10 @@ bool bt_rap_set_user_data(struct bt_rap *rap, void *user_data); bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func, void *user_data, bt_rap_destroy_func_t destroy); +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap, + bt_rap_procedure_data_func_t cb, + void *user_data, + bt_rap_destroy_func_t destroy); /* session related functions */ unsigned int bt_rap_register(bt_rap_func_t attached, bt_rap_func_t detached, void *user_data); @@ -228,6 +267,10 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci, int8_t max_tx_power); void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm); +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm, + bt_rap_procedure_data_func_t cb, + void *user_data, + bt_rap_destroy_func_t destroy); /* Connection handle mapping functions */ bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap, @@ -245,3 +288,10 @@ void bt_rap_set_timeout_cb(void *hci_sm, void (*func)(void *), void bt_rap_set_proc_active_cb(void *hci_sm, void (*func)(bool, void *), void *user_data); + +/* CS capability sw_time and connection interval setters */ +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time); + +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time); + +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval); -- 2.34.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* RE: Channel Sounding: Aggregate procedure data and expose via D-Bus 2026-07-17 7:26 ` [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API Prathibha Madugonde @ 2026-07-17 9:10 ` bluez.test.bot 0 siblings, 0 replies; 7+ messages in thread From: bluez.test.bot @ 2026-07-17 9:10 UTC (permalink / raw) To: linux-bluetooth, prathibha.madugonde [-- Attachment #1: Type: text/plain, Size: 1499 bytes --] This is automated email and please do not reply to this email! Dear submitter, Thank you for submitting the patches to the linux bluetooth mailing list. This is a CI test results with your patch series: PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1129350 ---Test result--- Test Summary: CheckPatch PASS 1.95 seconds GitLint FAIL 0.69 seconds BuildEll PASS 25.67 seconds BluezMake PASS 541.37 seconds MakeCheck PASS 1.04 seconds MakeDistcheck PASS 153.47 seconds CheckValgrind PASS 146.41 seconds CheckSmatch PASS 296.61 seconds bluezmakeextell PASS 95.73 seconds IncrementalBuild PASS 510.76 seconds ScanBuild PASS 893.79 seconds Details ############################## Test: GitLint - FAIL Desc: Run gitlint Output: [BlueZ,v1,1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API 1: T1 Title exceeds max length (84>80): "[BlueZ,v1,1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API" [BlueZ,v1,2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding 1: T1 Title exceeds max length (84>80): "[BlueZ,v1,2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding" https://github.com/bluez/bluez/pull/2325 --- Regards, Linux Bluetooth ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding 2026-07-17 7:26 [PATCH BlueZ v1 0/2] Channel Sounding: Aggregate procedure data and expose via D-Bus Prathibha Madugonde 2026-07-17 7:26 ` [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API Prathibha Madugonde @ 2026-07-17 7:26 ` Prathibha Madugonde 2026-07-17 15:07 ` Luiz Augusto von Dentz 1 sibling, 1 reply; 7+ messages in thread From: Prathibha Madugonde @ 2026-07-17 7:26 UTC (permalink / raw) To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results, and CS/procedure-enable config) into an a{sv} dictionary and emit it as a new ProcedureData signal on the CS D-Bus interface. Register a procedure_data callback on the RAP HCI state machine so rap.c is notified when a procedure completes, and propagate the locally supported T_SW time capability from the read-local-supported- capabilities response into bt_rap. --- profiles/ranging/rap.c | 497 ++++++++++++++++++++++++++++++++++++- profiles/ranging/rap_hci.c | 59 +++++ 2 files changed, 555 insertions(+), 1 deletion(-) diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c index 3ffc0da76..42e1ef2a4 100644 --- a/profiles/ranging/rap.c +++ b/profiles/ranging/rap.c @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc( return NULL; } +static void dict_append_byte(DBusMessageIter *dict, const char *key, + uint8_t val) +{ + DBusMessageIter entry, variant; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y", + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void dict_append_int32(DBusMessageIter *dict, const char *key, + dbus_int32_t val) +{ + DBusMessageIter entry, variant; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void dict_append_uint32(DBusMessageIter *dict, const char *key, + dbus_uint32_t val) +{ + DBusMessageIter entry, variant; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u", + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void dict_append_uint64(DBusMessageIter *dict, const char *key, + dbus_uint64_t val) +{ + DBusMessageIter entry, variant; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t", + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void dict_append_byte_array(DBusMessageIter *dict, const char *key, + const uint8_t *data, int len) +{ + DBusMessageIter entry, variant, arr; + int i; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); + for (i = 0; i < len; i++) + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]); + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void append_mode_zero(DBusMessageIter *dict, + const struct cs_mode_zero_data *m0) +{ + dict_append_byte(dict, "packetQuality", m0->packet_quality); + dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm); + dict_append_byte(dict, "packetAntenna", m0->packet_ant); + dict_append_int32(dict, "initiatorMeasuredFreqOffset", + (dbus_int32_t)m0->init_measured_freq_offset); +} + +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key, + const struct pct_iq_sample *pct) +{ + DBusMessageIter entry, variant, arr; + dbus_int32_t i_s = pct->i_sample; + dbus_int32_t q_s = pct->q_sample; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void append_mode_one(DBusMessageIter *dict, + const struct cs_mode_one_data *m1) +{ + dict_append_byte(dict, "packetQuality", m1->packet_quality); + dict_append_byte(dict, "packetNadm", m1->packet_nadm); + dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm); + dict_append_int32(dict, "toaTodInitiator", + (dbus_int32_t)m1->toa_tod_init); + dict_append_int32(dict, "todToaReflector", + (dbus_int32_t)m1->tod_toa_refl); + dict_append_byte(dict, "packetAntenna", m1->packet_ant); + append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1); + append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2); +} + +static void append_mode_two(DBusMessageIter *dict, + const struct cs_mode_two_data *m2, + uint8_t num_ant_paths) +{ + DBusMessageIter entry, variant, arr; + const char *key; + int j; + int num_paths; + + /* + * num_ant_paths is the HCI "number of antenna paths" value + * (0-indexed), so actual tone sample count = num_ant_paths + 1, + * capped at array size. + */ + num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ? + (num_ant_paths + 1) : CS_MAX_ANT_PATHS; + + dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index); + + /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */ + key = "tonePctIQSamples"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); + for (j = 0; j < num_paths; j++) { + dbus_int32_t i_s = m2->tone_pct[j].i_sample; + dbus_int32_t q_s = m2->tone_pct[j].q_sample; + + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); + } + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); + + /* toneQualityIndicators: "ay" */ + key = "toneQualityIndicators"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); + for (j = 0; j < num_paths; j++) { + uint8_t tqi = m2->tone_quality_indicator[j]; + + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi); + } + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void append_proc_enable_config(DBusMessageIter *outer_dict, + const struct rap_ev_cs_proc_enable_cmplt *cfg) +{ + DBusMessageIter entry, variant, inner; + const char *key = "procedureEnableConfig"; + dbus_uint32_t sub_evt_len_us; + + sub_evt_len_us = cfg->sub_evt_len[0] | + ((uint32_t)cfg->sub_evt_len[1] << 8) | + ((uint32_t)cfg->sub_evt_len[2] << 16); + + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", + &inner); + + dict_append_byte(&inner, "toneAntennaConfigSelection", + cfg->tone_ant_config_sel); + dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us); + dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt); + dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl); + dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl); + dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl); + dict_append_uint32(&inner, "procedureCount", cfg->proc_counter); + dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len); + + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(outer_dict, &entry); +} + +static void append_cs_config_param(DBusMessageIter *outer_dict, + const struct bcs_procedure_data *bcs) +{ + DBusMessageIter entry, variant, inner; + const char *key = "csConfigParam"; + const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config; + + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", + &inner); + + dict_append_byte(&inner, "modeType", cfg->main_mode_type); + dict_append_byte(&inner, "subModeType", cfg->sub_mode_type); + dict_append_byte(&inner, "rttType", cfg->rtt_type); + dict_append_byte_array(&inner, "channelMap", cfg->channel_map, + sizeof(cfg->channel_map)); + dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps); + dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps); + dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep); + dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps); + dict_append_byte(&inner, "role", cfg->role); + dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy); + dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type); + dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape); + dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump); + dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep); + dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time); + dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time); + dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time); + dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time); + dict_append_byte(&inner, "tSwTimeUsSupportedByLocal", + bcs->t_sw_time_us_supported_by_local); + dict_append_byte(&inner, "tSwTimeUsSupportedByRemote", + bcs->t_sw_time_us_supported_by_remote); + dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval); + + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(outer_dict, &entry); +} + +static void append_step_to_dict(DBusMessageIter *dict, + const struct cs_step_data *step, + uint8_t num_ant_paths) +{ + DBusMessageIter entry, variant, inner; + const char *mode_key; + + dict_append_byte(dict, "stepMode", step->step_mode); + dict_append_byte(dict, "stepChannel", step->step_chnl); + + switch (step->step_mode) { + case CS_MODE_ZERO: + mode_key = "modeZeroData"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, + &mode_key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + "a{sv}", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + "{sv}", &inner); + append_mode_zero(&inner, + &step->step_mode_data.mode_zero_data); + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); + break; + + case CS_MODE_ONE: + mode_key = "modeOneData"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, + &mode_key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + "a{sv}", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + "{sv}", &inner); + append_mode_one(&inner, + &step->step_mode_data.mode_one_data); + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); + break; + + case CS_MODE_TWO: + mode_key = "modeTwoData"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, + &mode_key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + "a{sv}", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + "{sv}", &inner); + append_mode_two(&inner, + &step->step_mode_data.mode_two_data, + num_ant_paths); + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); + break; + + case CS_MODE_THREE: + mode_key = "modeThreeData"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, + &mode_key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + "a{sv}", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + "{sv}", &inner); + append_mode_one(&inner, + &step->step_mode_data.mode_three_data.mode_one_data); + append_mode_two(&inner, + &step->step_mode_data.mode_three_data.mode_two_data, + num_ant_paths); + dbus_message_iter_close_container(&variant, &inner); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); + break; + + default: + break; + } +} + +static void append_subevent_to_dict(DBusMessageIter *dict, + const struct cs_subevent_result_data *sub) +{ + DBusMessageIter entry, variant, arr; + const char *key; + uint32_t i; + + dict_append_int32(dict, "startAclConnEvtCounter", + (dbus_int32_t)sub->start_acl_conn_evt_counter); + dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp); + dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl); + dict_append_byte(dict, "numAntPaths", sub->num_ant_paths); + dict_append_byte(dict, "subeventAbortReason", + sub->subevent_abort_reason); + dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos); + dict_append_uint32(dict, "numSteps", sub->num_steps); + + /* stepData: av (each element is a{sv}) */ + key = "stepData"; + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, + &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); + if (sub->step_data) { + for (i = 0; i < sub->num_steps; i++) { + DBusMessageIter inner, step_dict; + + dbus_message_iter_open_container(&arr, + DBUS_TYPE_VARIANT, "a{sv}", &inner); + dbus_message_iter_open_container(&inner, + DBUS_TYPE_ARRAY, "{sv}", &step_dict); + append_step_to_dict(&step_dict, &sub->step_data[i], + sub->num_ant_paths); + dbus_message_iter_close_container(&inner, &step_dict); + dbus_message_iter_close_container(&arr, &inner); + } + } + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(dict, &entry); +} + +static void append_subevent_array(DBusMessageIter *outer_dict, + const char *key, + const struct cs_subevent_result_data *subevents, + uint32_t count) +{ + DBusMessageIter entry, variant, arr; + uint32_t i; + + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", + &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); + for (i = 0; i < count; i++) { + DBusMessageIter inner, sub_dict; + + dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT, + "a{sv}", &inner); + dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY, + "{sv}", &sub_dict); + append_subevent_to_dict(&sub_dict, &subevents[i]); + dbus_message_iter_close_container(&inner, &sub_dict); + dbus_message_iter_close_container(&arr, &inner); + } + dbus_message_iter_close_container(&variant, &arr); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(outer_dict, &entry); +} + +static void rap_emit_procedure_data(struct rap_data *data, + const struct bcs_procedure_data *bcs) +{ + DBusMessage *signal; + DBusMessageIter iter, dict; + + signal = dbus_message_new_signal(device_get_path(data->device), + CS_INTERFACE, "ProcedureData"); + if (!signal) { + error("Failed to allocate ProcedureData signal"); + return; + } + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); + + dict_append_int32(&dict, "procedureCounter", + (dbus_int32_t)bcs->procedure_counter); + dict_append_int32(&dict, "procedureSequence", + (dbus_int32_t)bcs->procedure_sequence); + dict_append_byte(&dict, "initiatorSelectedTxPower", + (uint8_t)bcs->initiator_selected_tx_power); + dict_append_byte(&dict, "reflectorSelectedTxPower", + (uint8_t)bcs->reflector_selected_tx_power); + + dict_append_uint32(&dict, "initiatorSubeventCount", + bcs->initiator_subevent_count); + if (bcs->initiator_subevent_results && + bcs->initiator_subevent_count > 0) + append_subevent_array(&dict, "initiatorSubeventResults", + bcs->initiator_subevent_results, + bcs->initiator_subevent_count); + + dict_append_byte(&dict, "initiatorProcedureAbortReason", + bcs->initiator_procedure_abort_reason); + + dict_append_uint32(&dict, "reflectorSubeventCount", + bcs->reflector_subevent_count); + if (bcs->reflector_subevent_results && + bcs->reflector_subevent_count > 0) + append_subevent_array(&dict, "reflectorSubeventResults", + bcs->reflector_subevent_results, + bcs->reflector_subevent_count); + + dict_append_byte(&dict, "reflectorProcedureAbortReason", + bcs->reflector_procedure_abort_reason); + + append_proc_enable_config(&dict, &bcs->proc_enable_config); + append_cs_config_param(&dict, bcs); + + dbus_message_iter_close_container(&iter, &dict); + + g_dbus_send_message(btd_get_dbus_connection(), signal); +} + +static void rap_procedure_data(struct bt_rap *rap, + struct bcs_procedure_data *bcs, + void *user_data) +{ + struct rap_data *data = user_data; + + DBG("procedure_counter=%u", bcs->procedure_counter); + rap_emit_procedure_data(data, bcs); +} + static DBusMessage *start_measurement(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -534,6 +1020,9 @@ bad_type: data->active_session.cfg = cfg; data->active_session.freq = freq; + bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data, + data, NULL); + return dbus_message_new_method_return(msg); } @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = { { } }; +static const GDBusSignalTable cs_dbus_signals[] = { + { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) }, + { } +}; + static void rap_measurement_timeout_cb(void *user_data) { struct rap_data *data = user_data; @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service) g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(data->device), CS_INTERFACE, cs_dbus_methods, - NULL, cs_dbus_properties, data, NULL); + cs_dbus_signals, cs_dbus_properties, + data, NULL); return 0; } diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c index e57f967a2..3da98972e 100644 --- a/profiles/ranging/rap_hci.c +++ b/profiles/ranging/rap_hci.c @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size, DBG("Sending read remote capabilities for handle 0x%04X", sm->active_conn_handle); bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle); + + bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported); } static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm, @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size, cs_set_state(sm, CS_STATE_INIT); rap_send_hci_def_settings_command(sm, evt); } + + bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported); } static void rap_cs_config_cmplt_evt(const void *data, uint8_t size, @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size, &rap_ev, sm->rap); } +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size, + void *user_data) +{ + struct cs_state_machine *sm = user_data; + const struct bt_hci_evt_le_conn_update_complete *evt; + struct rap_conn_mapping *mapping; + struct bt_rap *rap; + struct iovec iov; + + if (!sm || !data || + size < sizeof(struct bt_hci_evt_le_conn_update_complete)) + return; + + iov.iov_base = (void *) data; + iov.iov_len = size; + + evt = util_iov_pull_mem(&iov, sizeof(*evt)); + if (!evt) { + error("Failed to pull LE conn update complete struct"); + return; + } + + DBG("status=0x%02X handle=0x%04X interval=%u", + evt->status, evt->handle, evt->interval); + + if (evt->status != 0) + return; + + mapping = find_mapping_by_handle(sm, evt->handle); + if (mapping && mapping->rap) { + DBG("Found handle 0x%04X in mapping cache", evt->handle); + rap = mapping->rap; + } else { + error("No mapping found for handle 0x%04X", evt->handle); + return; + } + + bt_rap_set_conn_interval(rap, evt->interval); +} + static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample, int16_t *q_sample) { @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci, rap_cs_subevt_result_evt }, { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE, rap_cs_subevt_result_cont_evt }, + { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, + rap_le_conn_update_complete_evt }, }; struct cs_state_machine *sm; unsigned int i; @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm) false); } +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm, + bt_rap_procedure_data_func_t cb, + void *user_data, + bt_rap_destroy_func_t destroy) +{ + struct cs_state_machine *sm = hci_sm; + + if (!sm || !sm->rap) + return false; + + return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy); +} + bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap, uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type, bool is_central) -- 2.34.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding 2026-07-17 7:26 ` [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding Prathibha Madugonde @ 2026-07-17 15:07 ` Luiz Augusto von Dentz 2026-07-17 15:17 ` Luiz Augusto von Dentz 0 siblings, 1 reply; 7+ messages in thread From: Luiz Augusto von Dentz @ 2026-07-17 15:07 UTC (permalink / raw) To: Prathibha Madugonde Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg Hi Prathibha, On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> wrote: > > From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> > > Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results, > and CS/procedure-enable config) into an a{sv} dictionary and emit it > as a new ProcedureData signal on the CS D-Bus interface. Was this documented though? It doesn't seem to follow the likes of https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst, for instance we don't use camel case for dictionary options. Also, if this is a result of `StartMeasurement`, we should probably signal it as Results or something like that, but I'd start with documentation so we can agree on the overall design. > Register a procedure_data callback on the RAP HCI state machine so > rap.c is notified when a procedure completes, and propagate the > locally supported T_SW time capability from the read-local-supported- > capabilities response into bt_rap. > > --- > profiles/ranging/rap.c | 497 ++++++++++++++++++++++++++++++++++++- > profiles/ranging/rap_hci.c | 59 +++++ > 2 files changed, 555 insertions(+), 1 deletion(-) > > diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c > index 3ffc0da76..42e1ef2a4 100644 > --- a/profiles/ranging/rap.c > +++ b/profiles/ranging/rap.c > @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc( > return NULL; > } > > +static void dict_append_byte(DBusMessageIter *dict, const char *key, > + uint8_t val) > +{ > + DBusMessageIter entry, variant; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y", > + &variant); > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void dict_append_int32(DBusMessageIter *dict, const char *key, > + dbus_int32_t val) > +{ > + DBusMessageIter entry, variant; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", > + &variant); > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void dict_append_uint32(DBusMessageIter *dict, const char *key, > + dbus_uint32_t val) > +{ > + DBusMessageIter entry, variant; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u", > + &variant); > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void dict_append_uint64(DBusMessageIter *dict, const char *key, > + dbus_uint64_t val) > +{ > + DBusMessageIter entry, variant; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t", > + &variant); > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void dict_append_byte_array(DBusMessageIter *dict, const char *key, > + const uint8_t *data, int len) > +{ > + DBusMessageIter entry, variant, arr; > + int i; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > + for (i = 0; i < len; i++) > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]); > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void append_mode_zero(DBusMessageIter *dict, > + const struct cs_mode_zero_data *m0) > +{ > + dict_append_byte(dict, "packetQuality", m0->packet_quality); > + dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm); > + dict_append_byte(dict, "packetAntenna", m0->packet_ant); > + dict_append_int32(dict, "initiatorMeasuredFreqOffset", > + (dbus_int32_t)m0->init_measured_freq_offset); > +} > + > +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key, > + const struct pct_iq_sample *pct) > +{ > + DBusMessageIter entry, variant, arr; > + dbus_int32_t i_s = pct->i_sample; > + dbus_int32_t q_s = pct->q_sample; > + > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void append_mode_one(DBusMessageIter *dict, > + const struct cs_mode_one_data *m1) > +{ > + dict_append_byte(dict, "packetQuality", m1->packet_quality); > + dict_append_byte(dict, "packetNadm", m1->packet_nadm); > + dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm); > + dict_append_int32(dict, "toaTodInitiator", > + (dbus_int32_t)m1->toa_tod_init); > + dict_append_int32(dict, "todToaReflector", > + (dbus_int32_t)m1->tod_toa_refl); > + dict_append_byte(dict, "packetAntenna", m1->packet_ant); > + append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1); > + append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2); > +} > + > +static void append_mode_two(DBusMessageIter *dict, > + const struct cs_mode_two_data *m2, > + uint8_t num_ant_paths) > +{ > + DBusMessageIter entry, variant, arr; > + const char *key; > + int j; > + int num_paths; > + > + /* > + * num_ant_paths is the HCI "number of antenna paths" value > + * (0-indexed), so actual tone sample count = num_ant_paths + 1, > + * capped at array size. > + */ > + num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ? > + (num_ant_paths + 1) : CS_MAX_ANT_PATHS; > + > + dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index); > + > + /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */ > + key = "tonePctIQSamples"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > + for (j = 0; j < num_paths; j++) { > + dbus_int32_t i_s = m2->tone_pct[j].i_sample; > + dbus_int32_t q_s = m2->tone_pct[j].q_sample; > + > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > + } > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > + > + /* toneQualityIndicators: "ay" */ > + key = "toneQualityIndicators"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > + for (j = 0; j < num_paths; j++) { > + uint8_t tqi = m2->tone_quality_indicator[j]; > + > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi); > + } > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void append_proc_enable_config(DBusMessageIter *outer_dict, > + const struct rap_ev_cs_proc_enable_cmplt *cfg) > +{ > + DBusMessageIter entry, variant, inner; > + const char *key = "procedureEnableConfig"; > + dbus_uint32_t sub_evt_len_us; > + > + sub_evt_len_us = cfg->sub_evt_len[0] | > + ((uint32_t)cfg->sub_evt_len[1] << 8) | > + ((uint32_t)cfg->sub_evt_len[2] << 16); > + > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > + &inner); > + > + dict_append_byte(&inner, "toneAntennaConfigSelection", > + cfg->tone_ant_config_sel); > + dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us); > + dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt); > + dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl); > + dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl); > + dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl); > + dict_append_uint32(&inner, "procedureCount", cfg->proc_counter); > + dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len); > + > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(outer_dict, &entry); > +} > + > +static void append_cs_config_param(DBusMessageIter *outer_dict, > + const struct bcs_procedure_data *bcs) > +{ > + DBusMessageIter entry, variant, inner; > + const char *key = "csConfigParam"; > + const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config; > + > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > + &inner); > + > + dict_append_byte(&inner, "modeType", cfg->main_mode_type); > + dict_append_byte(&inner, "subModeType", cfg->sub_mode_type); > + dict_append_byte(&inner, "rttType", cfg->rtt_type); > + dict_append_byte_array(&inner, "channelMap", cfg->channel_map, > + sizeof(cfg->channel_map)); > + dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps); > + dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps); > + dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep); > + dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps); > + dict_append_byte(&inner, "role", cfg->role); > + dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy); > + dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type); > + dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape); > + dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump); > + dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep); > + dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time); > + dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time); > + dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time); > + dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time); > + dict_append_byte(&inner, "tSwTimeUsSupportedByLocal", > + bcs->t_sw_time_us_supported_by_local); > + dict_append_byte(&inner, "tSwTimeUsSupportedByRemote", > + bcs->t_sw_time_us_supported_by_remote); > + dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval); > + > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(outer_dict, &entry); > +} > + > +static void append_step_to_dict(DBusMessageIter *dict, > + const struct cs_step_data *step, > + uint8_t num_ant_paths) > +{ > + DBusMessageIter entry, variant, inner; > + const char *mode_key; > + > + dict_append_byte(dict, "stepMode", step->step_mode); > + dict_append_byte(dict, "stepChannel", step->step_chnl); > + > + switch (step->step_mode) { > + case CS_MODE_ZERO: > + mode_key = "modeZeroData"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > + &mode_key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > + "a{sv}", &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > + "{sv}", &inner); > + append_mode_zero(&inner, > + &step->step_mode_data.mode_zero_data); > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > + break; > + > + case CS_MODE_ONE: > + mode_key = "modeOneData"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > + &mode_key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > + "a{sv}", &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > + "{sv}", &inner); > + append_mode_one(&inner, > + &step->step_mode_data.mode_one_data); > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > + break; > + > + case CS_MODE_TWO: > + mode_key = "modeTwoData"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > + &mode_key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > + "a{sv}", &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > + "{sv}", &inner); > + append_mode_two(&inner, > + &step->step_mode_data.mode_two_data, > + num_ant_paths); > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > + break; > + > + case CS_MODE_THREE: > + mode_key = "modeThreeData"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > + &mode_key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > + "a{sv}", &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > + "{sv}", &inner); > + append_mode_one(&inner, > + &step->step_mode_data.mode_three_data.mode_one_data); > + append_mode_two(&inner, > + &step->step_mode_data.mode_three_data.mode_two_data, > + num_ant_paths); > + dbus_message_iter_close_container(&variant, &inner); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > + break; > + > + default: > + break; > + } > +} > + > +static void append_subevent_to_dict(DBusMessageIter *dict, > + const struct cs_subevent_result_data *sub) > +{ > + DBusMessageIter entry, variant, arr; > + const char *key; > + uint32_t i; > + > + dict_append_int32(dict, "startAclConnEvtCounter", > + (dbus_int32_t)sub->start_acl_conn_evt_counter); > + dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp); > + dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl); > + dict_append_byte(dict, "numAntPaths", sub->num_ant_paths); > + dict_append_byte(dict, "subeventAbortReason", > + sub->subevent_abort_reason); > + dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos); > + dict_append_uint32(dict, "numSteps", sub->num_steps); > + > + /* stepData: av (each element is a{sv}) */ > + key = "stepData"; > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > + &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > + if (sub->step_data) { > + for (i = 0; i < sub->num_steps; i++) { > + DBusMessageIter inner, step_dict; > + > + dbus_message_iter_open_container(&arr, > + DBUS_TYPE_VARIANT, "a{sv}", &inner); > + dbus_message_iter_open_container(&inner, > + DBUS_TYPE_ARRAY, "{sv}", &step_dict); > + append_step_to_dict(&step_dict, &sub->step_data[i], > + sub->num_ant_paths); > + dbus_message_iter_close_container(&inner, &step_dict); > + dbus_message_iter_close_container(&arr, &inner); > + } > + } > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(dict, &entry); > +} > + > +static void append_subevent_array(DBusMessageIter *outer_dict, > + const char *key, > + const struct cs_subevent_result_data *subevents, > + uint32_t count) > +{ > + DBusMessageIter entry, variant, arr; > + uint32_t i; > + > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > + NULL, &entry); > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > + &variant); > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > + for (i = 0; i < count; i++) { > + DBusMessageIter inner, sub_dict; > + > + dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT, > + "a{sv}", &inner); > + dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY, > + "{sv}", &sub_dict); > + append_subevent_to_dict(&sub_dict, &subevents[i]); > + dbus_message_iter_close_container(&inner, &sub_dict); > + dbus_message_iter_close_container(&arr, &inner); > + } > + dbus_message_iter_close_container(&variant, &arr); > + dbus_message_iter_close_container(&entry, &variant); > + dbus_message_iter_close_container(outer_dict, &entry); > +} > + > +static void rap_emit_procedure_data(struct rap_data *data, > + const struct bcs_procedure_data *bcs) > +{ > + DBusMessage *signal; > + DBusMessageIter iter, dict; > + > + signal = dbus_message_new_signal(device_get_path(data->device), > + CS_INTERFACE, "ProcedureData"); > + if (!signal) { > + error("Failed to allocate ProcedureData signal"); > + return; > + } > + > + dbus_message_iter_init_append(signal, &iter); > + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); > + > + dict_append_int32(&dict, "procedureCounter", > + (dbus_int32_t)bcs->procedure_counter); > + dict_append_int32(&dict, "procedureSequence", > + (dbus_int32_t)bcs->procedure_sequence); > + dict_append_byte(&dict, "initiatorSelectedTxPower", > + (uint8_t)bcs->initiator_selected_tx_power); > + dict_append_byte(&dict, "reflectorSelectedTxPower", > + (uint8_t)bcs->reflector_selected_tx_power); > + > + dict_append_uint32(&dict, "initiatorSubeventCount", > + bcs->initiator_subevent_count); > + if (bcs->initiator_subevent_results && > + bcs->initiator_subevent_count > 0) > + append_subevent_array(&dict, "initiatorSubeventResults", > + bcs->initiator_subevent_results, > + bcs->initiator_subevent_count); > + > + dict_append_byte(&dict, "initiatorProcedureAbortReason", > + bcs->initiator_procedure_abort_reason); > + > + dict_append_uint32(&dict, "reflectorSubeventCount", > + bcs->reflector_subevent_count); > + if (bcs->reflector_subevent_results && > + bcs->reflector_subevent_count > 0) > + append_subevent_array(&dict, "reflectorSubeventResults", > + bcs->reflector_subevent_results, > + bcs->reflector_subevent_count); > + > + dict_append_byte(&dict, "reflectorProcedureAbortReason", > + bcs->reflector_procedure_abort_reason); > + > + append_proc_enable_config(&dict, &bcs->proc_enable_config); > + append_cs_config_param(&dict, bcs); > + > + dbus_message_iter_close_container(&iter, &dict); > + > + g_dbus_send_message(btd_get_dbus_connection(), signal); > +} > + > +static void rap_procedure_data(struct bt_rap *rap, > + struct bcs_procedure_data *bcs, > + void *user_data) > +{ > + struct rap_data *data = user_data; > + > + DBG("procedure_counter=%u", bcs->procedure_counter); > + rap_emit_procedure_data(data, bcs); > +} > + > static DBusMessage *start_measurement(DBusConnection *conn, > DBusMessage *msg, void *user_data) > { > @@ -534,6 +1020,9 @@ bad_type: > data->active_session.cfg = cfg; > data->active_session.freq = freq; > > + bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data, > + data, NULL); > + > return dbus_message_new_method_return(msg); > } > > @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = { > { } > }; > > +static const GDBusSignalTable cs_dbus_signals[] = { > + { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) }, > + { } > +}; > + > static void rap_measurement_timeout_cb(void *user_data) > { > struct rap_data *data = user_data; > @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service) > g_dbus_register_interface(btd_get_dbus_connection(), > device_get_path(data->device), > CS_INTERFACE, cs_dbus_methods, > - NULL, cs_dbus_properties, data, NULL); > + cs_dbus_signals, cs_dbus_properties, > + data, NULL); > > return 0; > } > diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c > index e57f967a2..3da98972e 100644 > --- a/profiles/ranging/rap_hci.c > +++ b/profiles/ranging/rap_hci.c > @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size, > DBG("Sending read remote capabilities for handle 0x%04X", > sm->active_conn_handle); > bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle); > + > + bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported); > } > > static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm, > @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size, > cs_set_state(sm, CS_STATE_INIT); > rap_send_hci_def_settings_command(sm, evt); > } > + > + bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported); > } > > static void rap_cs_config_cmplt_evt(const void *data, uint8_t size, > @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size, > &rap_ev, sm->rap); > } > > +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size, > + void *user_data) > +{ > + struct cs_state_machine *sm = user_data; > + const struct bt_hci_evt_le_conn_update_complete *evt; > + struct rap_conn_mapping *mapping; > + struct bt_rap *rap; > + struct iovec iov; > + > + if (!sm || !data || > + size < sizeof(struct bt_hci_evt_le_conn_update_complete)) > + return; > + > + iov.iov_base = (void *) data; > + iov.iov_len = size; > + > + evt = util_iov_pull_mem(&iov, sizeof(*evt)); > + if (!evt) { > + error("Failed to pull LE conn update complete struct"); > + return; > + } > + > + DBG("status=0x%02X handle=0x%04X interval=%u", > + evt->status, evt->handle, evt->interval); > + > + if (evt->status != 0) > + return; > + > + mapping = find_mapping_by_handle(sm, evt->handle); > + if (mapping && mapping->rap) { > + DBG("Found handle 0x%04X in mapping cache", evt->handle); > + rap = mapping->rap; > + } else { > + error("No mapping found for handle 0x%04X", evt->handle); > + return; > + } > + > + bt_rap_set_conn_interval(rap, evt->interval); > +} > + > static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample, > int16_t *q_sample) > { > @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci, > rap_cs_subevt_result_evt }, > { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE, > rap_cs_subevt_result_cont_evt }, > + { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, > + rap_le_conn_update_complete_evt }, > }; > struct cs_state_machine *sm; > unsigned int i; > @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm) > false); > } > > +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm, > + bt_rap_procedure_data_func_t cb, > + void *user_data, > + bt_rap_destroy_func_t destroy) > +{ > + struct cs_state_machine *sm = hci_sm; > + > + if (!sm || !sm->rap) > + return false; > + > + return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy); > +} > + > bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap, > uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type, > bool is_central) > -- > 2.34.1 > -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding 2026-07-17 15:07 ` Luiz Augusto von Dentz @ 2026-07-17 15:17 ` Luiz Augusto von Dentz 2026-07-17 15:44 ` Luiz Augusto von Dentz 0 siblings, 1 reply; 7+ messages in thread From: Luiz Augusto von Dentz @ 2026-07-17 15:17 UTC (permalink / raw) To: Prathibha Madugonde Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg Hi. On Fri, Jul 17, 2026 at 11:07 AM Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote: > > Hi Prathibha, > > On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde > <prathibha.madugonde@oss.qualcomm.com> wrote: > > > > From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> > > > > Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results, > > and CS/procedure-enable config) into an a{sv} dictionary and emit it > > as a new ProcedureData signal on the CS D-Bus interface. > > Was this documented though? It doesn't seem to follow the likes of > https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst, > for instance we don't use camel case for dictionary options. Also, if > this is a result of `StartMeasurement`, we should probably signal it > as Results or something like that, but I'd start with documentation so > we can agree on the overall design. Ok, looks like this does follow the documentation already: https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst#void-proceduredatadict-data That said that will probably need updating since some dictionaries use camel case and others don't, so this needs to converge to a single format. > > Register a procedure_data callback on the RAP HCI state machine so > > rap.c is notified when a procedure completes, and propagate the > > locally supported T_SW time capability from the read-local-supported- > > capabilities response into bt_rap. > > > > --- > > profiles/ranging/rap.c | 497 ++++++++++++++++++++++++++++++++++++- > > profiles/ranging/rap_hci.c | 59 +++++ > > 2 files changed, 555 insertions(+), 1 deletion(-) > > > > diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c > > index 3ffc0da76..42e1ef2a4 100644 > > --- a/profiles/ranging/rap.c > > +++ b/profiles/ranging/rap.c > > @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc( > > return NULL; > > } > > > > +static void dict_append_byte(DBusMessageIter *dict, const char *key, > > + uint8_t val) > > +{ > > + DBusMessageIter entry, variant; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y", > > + &variant); > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void dict_append_int32(DBusMessageIter *dict, const char *key, > > + dbus_int32_t val) > > +{ > > + DBusMessageIter entry, variant; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", > > + &variant); > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void dict_append_uint32(DBusMessageIter *dict, const char *key, > > + dbus_uint32_t val) > > +{ > > + DBusMessageIter entry, variant; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u", > > + &variant); > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void dict_append_uint64(DBusMessageIter *dict, const char *key, > > + dbus_uint64_t val) > > +{ > > + DBusMessageIter entry, variant; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t", > > + &variant); > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void dict_append_byte_array(DBusMessageIter *dict, const char *key, > > + const uint8_t *data, int len) > > +{ > > + DBusMessageIter entry, variant, arr; > > + int i; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > > + for (i = 0; i < len; i++) > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]); > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void append_mode_zero(DBusMessageIter *dict, > > + const struct cs_mode_zero_data *m0) > > +{ > > + dict_append_byte(dict, "packetQuality", m0->packet_quality); > > + dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm); > > + dict_append_byte(dict, "packetAntenna", m0->packet_ant); > > + dict_append_int32(dict, "initiatorMeasuredFreqOffset", > > + (dbus_int32_t)m0->init_measured_freq_offset); > > +} > > + > > +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key, > > + const struct pct_iq_sample *pct) > > +{ > > + DBusMessageIter entry, variant, arr; > > + dbus_int32_t i_s = pct->i_sample; > > + dbus_int32_t q_s = pct->q_sample; > > + > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void append_mode_one(DBusMessageIter *dict, > > + const struct cs_mode_one_data *m1) > > +{ > > + dict_append_byte(dict, "packetQuality", m1->packet_quality); > > + dict_append_byte(dict, "packetNadm", m1->packet_nadm); > > + dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm); > > + dict_append_int32(dict, "toaTodInitiator", > > + (dbus_int32_t)m1->toa_tod_init); > > + dict_append_int32(dict, "todToaReflector", > > + (dbus_int32_t)m1->tod_toa_refl); > > + dict_append_byte(dict, "packetAntenna", m1->packet_ant); > > + append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1); > > + append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2); > > +} > > + > > +static void append_mode_two(DBusMessageIter *dict, > > + const struct cs_mode_two_data *m2, > > + uint8_t num_ant_paths) > > +{ > > + DBusMessageIter entry, variant, arr; > > + const char *key; > > + int j; > > + int num_paths; > > + > > + /* > > + * num_ant_paths is the HCI "number of antenna paths" value > > + * (0-indexed), so actual tone sample count = num_ant_paths + 1, > > + * capped at array size. > > + */ > > + num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ? > > + (num_ant_paths + 1) : CS_MAX_ANT_PATHS; > > + > > + dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index); > > + > > + /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */ > > + key = "tonePctIQSamples"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > > + for (j = 0; j < num_paths; j++) { > > + dbus_int32_t i_s = m2->tone_pct[j].i_sample; > > + dbus_int32_t q_s = m2->tone_pct[j].q_sample; > > + > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > > + } > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > + > > + /* toneQualityIndicators: "ay" */ > > + key = "toneQualityIndicators"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > > + for (j = 0; j < num_paths; j++) { > > + uint8_t tqi = m2->tone_quality_indicator[j]; > > + > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi); > > + } > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void append_proc_enable_config(DBusMessageIter *outer_dict, > > + const struct rap_ev_cs_proc_enable_cmplt *cfg) > > +{ > > + DBusMessageIter entry, variant, inner; > > + const char *key = "procedureEnableConfig"; > > + dbus_uint32_t sub_evt_len_us; > > + > > + sub_evt_len_us = cfg->sub_evt_len[0] | > > + ((uint32_t)cfg->sub_evt_len[1] << 8) | > > + ((uint32_t)cfg->sub_evt_len[2] << 16); > > + > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > > + &inner); > > + > > + dict_append_byte(&inner, "toneAntennaConfigSelection", > > + cfg->tone_ant_config_sel); > > + dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us); > > + dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt); > > + dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl); > > + dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl); > > + dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl); > > + dict_append_uint32(&inner, "procedureCount", cfg->proc_counter); > > + dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len); > > + > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(outer_dict, &entry); > > +} > > + > > +static void append_cs_config_param(DBusMessageIter *outer_dict, > > + const struct bcs_procedure_data *bcs) > > +{ > > + DBusMessageIter entry, variant, inner; > > + const char *key = "csConfigParam"; > > + const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config; > > + > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > > + &inner); > > + > > + dict_append_byte(&inner, "modeType", cfg->main_mode_type); > > + dict_append_byte(&inner, "subModeType", cfg->sub_mode_type); > > + dict_append_byte(&inner, "rttType", cfg->rtt_type); > > + dict_append_byte_array(&inner, "channelMap", cfg->channel_map, > > + sizeof(cfg->channel_map)); > > + dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps); > > + dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps); > > + dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep); > > + dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps); > > + dict_append_byte(&inner, "role", cfg->role); > > + dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy); > > + dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type); > > + dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape); > > + dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump); > > + dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep); > > + dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time); > > + dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time); > > + dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time); > > + dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time); > > + dict_append_byte(&inner, "tSwTimeUsSupportedByLocal", > > + bcs->t_sw_time_us_supported_by_local); > > + dict_append_byte(&inner, "tSwTimeUsSupportedByRemote", > > + bcs->t_sw_time_us_supported_by_remote); > > + dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval); > > + > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(outer_dict, &entry); > > +} > > + > > +static void append_step_to_dict(DBusMessageIter *dict, > > + const struct cs_step_data *step, > > + uint8_t num_ant_paths) > > +{ > > + DBusMessageIter entry, variant, inner; > > + const char *mode_key; > > + > > + dict_append_byte(dict, "stepMode", step->step_mode); > > + dict_append_byte(dict, "stepChannel", step->step_chnl); > > + > > + switch (step->step_mode) { > > + case CS_MODE_ZERO: > > + mode_key = "modeZeroData"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > + &mode_key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > + "a{sv}", &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > + "{sv}", &inner); > > + append_mode_zero(&inner, > > + &step->step_mode_data.mode_zero_data); > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > + break; > > + > > + case CS_MODE_ONE: > > + mode_key = "modeOneData"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > + &mode_key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > + "a{sv}", &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > + "{sv}", &inner); > > + append_mode_one(&inner, > > + &step->step_mode_data.mode_one_data); > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > + break; > > + > > + case CS_MODE_TWO: > > + mode_key = "modeTwoData"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > + &mode_key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > + "a{sv}", &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > + "{sv}", &inner); > > + append_mode_two(&inner, > > + &step->step_mode_data.mode_two_data, > > + num_ant_paths); > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > + break; > > + > > + case CS_MODE_THREE: > > + mode_key = "modeThreeData"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > + &mode_key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > + "a{sv}", &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > + "{sv}", &inner); > > + append_mode_one(&inner, > > + &step->step_mode_data.mode_three_data.mode_one_data); > > + append_mode_two(&inner, > > + &step->step_mode_data.mode_three_data.mode_two_data, > > + num_ant_paths); > > + dbus_message_iter_close_container(&variant, &inner); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > + break; > > + > > + default: > > + break; > > + } > > +} > > + > > +static void append_subevent_to_dict(DBusMessageIter *dict, > > + const struct cs_subevent_result_data *sub) > > +{ > > + DBusMessageIter entry, variant, arr; > > + const char *key; > > + uint32_t i; > > + > > + dict_append_int32(dict, "startAclConnEvtCounter", > > + (dbus_int32_t)sub->start_acl_conn_evt_counter); > > + dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp); > > + dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl); > > + dict_append_byte(dict, "numAntPaths", sub->num_ant_paths); > > + dict_append_byte(dict, "subeventAbortReason", > > + sub->subevent_abort_reason); > > + dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos); > > + dict_append_uint32(dict, "numSteps", sub->num_steps); > > + > > + /* stepData: av (each element is a{sv}) */ > > + key = "stepData"; > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > + &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > > + if (sub->step_data) { > > + for (i = 0; i < sub->num_steps; i++) { > > + DBusMessageIter inner, step_dict; > > + > > + dbus_message_iter_open_container(&arr, > > + DBUS_TYPE_VARIANT, "a{sv}", &inner); > > + dbus_message_iter_open_container(&inner, > > + DBUS_TYPE_ARRAY, "{sv}", &step_dict); > > + append_step_to_dict(&step_dict, &sub->step_data[i], > > + sub->num_ant_paths); > > + dbus_message_iter_close_container(&inner, &step_dict); > > + dbus_message_iter_close_container(&arr, &inner); > > + } > > + } > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(dict, &entry); > > +} > > + > > +static void append_subevent_array(DBusMessageIter *outer_dict, > > + const char *key, > > + const struct cs_subevent_result_data *subevents, > > + uint32_t count) > > +{ > > + DBusMessageIter entry, variant, arr; > > + uint32_t i; > > + > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > + NULL, &entry); > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > > + &variant); > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > > + for (i = 0; i < count; i++) { > > + DBusMessageIter inner, sub_dict; > > + > > + dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT, > > + "a{sv}", &inner); > > + dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY, > > + "{sv}", &sub_dict); > > + append_subevent_to_dict(&sub_dict, &subevents[i]); > > + dbus_message_iter_close_container(&inner, &sub_dict); > > + dbus_message_iter_close_container(&arr, &inner); > > + } > > + dbus_message_iter_close_container(&variant, &arr); > > + dbus_message_iter_close_container(&entry, &variant); > > + dbus_message_iter_close_container(outer_dict, &entry); > > +} > > + > > +static void rap_emit_procedure_data(struct rap_data *data, > > + const struct bcs_procedure_data *bcs) > > +{ > > + DBusMessage *signal; > > + DBusMessageIter iter, dict; > > + > > + signal = dbus_message_new_signal(device_get_path(data->device), > > + CS_INTERFACE, "ProcedureData"); > > + if (!signal) { > > + error("Failed to allocate ProcedureData signal"); > > + return; > > + } > > + > > + dbus_message_iter_init_append(signal, &iter); > > + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); > > + > > + dict_append_int32(&dict, "procedureCounter", > > + (dbus_int32_t)bcs->procedure_counter); > > + dict_append_int32(&dict, "procedureSequence", > > + (dbus_int32_t)bcs->procedure_sequence); > > + dict_append_byte(&dict, "initiatorSelectedTxPower", > > + (uint8_t)bcs->initiator_selected_tx_power); > > + dict_append_byte(&dict, "reflectorSelectedTxPower", > > + (uint8_t)bcs->reflector_selected_tx_power); > > + > > + dict_append_uint32(&dict, "initiatorSubeventCount", > > + bcs->initiator_subevent_count); > > + if (bcs->initiator_subevent_results && > > + bcs->initiator_subevent_count > 0) > > + append_subevent_array(&dict, "initiatorSubeventResults", > > + bcs->initiator_subevent_results, > > + bcs->initiator_subevent_count); > > + > > + dict_append_byte(&dict, "initiatorProcedureAbortReason", > > + bcs->initiator_procedure_abort_reason); > > + > > + dict_append_uint32(&dict, "reflectorSubeventCount", > > + bcs->reflector_subevent_count); > > + if (bcs->reflector_subevent_results && > > + bcs->reflector_subevent_count > 0) > > + append_subevent_array(&dict, "reflectorSubeventResults", > > + bcs->reflector_subevent_results, > > + bcs->reflector_subevent_count); > > + > > + dict_append_byte(&dict, "reflectorProcedureAbortReason", > > + bcs->reflector_procedure_abort_reason); > > + > > + append_proc_enable_config(&dict, &bcs->proc_enable_config); > > + append_cs_config_param(&dict, bcs); > > + > > + dbus_message_iter_close_container(&iter, &dict); > > + > > + g_dbus_send_message(btd_get_dbus_connection(), signal); > > +} > > + > > +static void rap_procedure_data(struct bt_rap *rap, > > + struct bcs_procedure_data *bcs, > > + void *user_data) > > +{ > > + struct rap_data *data = user_data; > > + > > + DBG("procedure_counter=%u", bcs->procedure_counter); > > + rap_emit_procedure_data(data, bcs); > > +} > > + > > static DBusMessage *start_measurement(DBusConnection *conn, > > DBusMessage *msg, void *user_data) > > { > > @@ -534,6 +1020,9 @@ bad_type: > > data->active_session.cfg = cfg; > > data->active_session.freq = freq; > > > > + bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data, > > + data, NULL); > > + > > return dbus_message_new_method_return(msg); > > } > > > > @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = { > > { } > > }; > > > > +static const GDBusSignalTable cs_dbus_signals[] = { > > + { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) }, > > + { } > > +}; > > + > > static void rap_measurement_timeout_cb(void *user_data) > > { > > struct rap_data *data = user_data; > > @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service) > > g_dbus_register_interface(btd_get_dbus_connection(), > > device_get_path(data->device), > > CS_INTERFACE, cs_dbus_methods, > > - NULL, cs_dbus_properties, data, NULL); > > + cs_dbus_signals, cs_dbus_properties, > > + data, NULL); > > > > return 0; > > } > > diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c > > index e57f967a2..3da98972e 100644 > > --- a/profiles/ranging/rap_hci.c > > +++ b/profiles/ranging/rap_hci.c > > @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size, > > DBG("Sending read remote capabilities for handle 0x%04X", > > sm->active_conn_handle); > > bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle); > > + > > + bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported); > > } > > > > static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm, > > @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size, > > cs_set_state(sm, CS_STATE_INIT); > > rap_send_hci_def_settings_command(sm, evt); > > } > > + > > + bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported); > > } > > > > static void rap_cs_config_cmplt_evt(const void *data, uint8_t size, > > @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size, > > &rap_ev, sm->rap); > > } > > > > +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size, > > + void *user_data) > > +{ > > + struct cs_state_machine *sm = user_data; > > + const struct bt_hci_evt_le_conn_update_complete *evt; > > + struct rap_conn_mapping *mapping; > > + struct bt_rap *rap; > > + struct iovec iov; > > + > > + if (!sm || !data || > > + size < sizeof(struct bt_hci_evt_le_conn_update_complete)) > > + return; > > + > > + iov.iov_base = (void *) data; > > + iov.iov_len = size; > > + > > + evt = util_iov_pull_mem(&iov, sizeof(*evt)); > > + if (!evt) { > > + error("Failed to pull LE conn update complete struct"); > > + return; > > + } > > + > > + DBG("status=0x%02X handle=0x%04X interval=%u", > > + evt->status, evt->handle, evt->interval); > > + > > + if (evt->status != 0) > > + return; > > + > > + mapping = find_mapping_by_handle(sm, evt->handle); > > + if (mapping && mapping->rap) { > > + DBG("Found handle 0x%04X in mapping cache", evt->handle); > > + rap = mapping->rap; > > + } else { > > + error("No mapping found for handle 0x%04X", evt->handle); > > + return; > > + } > > + > > + bt_rap_set_conn_interval(rap, evt->interval); > > +} > > + > > static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample, > > int16_t *q_sample) > > { > > @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci, > > rap_cs_subevt_result_evt }, > > { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE, > > rap_cs_subevt_result_cont_evt }, > > + { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, > > + rap_le_conn_update_complete_evt }, > > }; > > struct cs_state_machine *sm; > > unsigned int i; > > @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm) > > false); > > } > > > > +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm, > > + bt_rap_procedure_data_func_t cb, > > + void *user_data, > > + bt_rap_destroy_func_t destroy) > > +{ > > + struct cs_state_machine *sm = hci_sm; > > + > > + if (!sm || !sm->rap) > > + return false; > > + > > + return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy); > > +} > > + > > bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap, > > uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type, > > bool is_central) > > -- > > 2.34.1 > > > > > -- > Luiz Augusto von Dentz -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding 2026-07-17 15:17 ` Luiz Augusto von Dentz @ 2026-07-17 15:44 ` Luiz Augusto von Dentz 0 siblings, 0 replies; 7+ messages in thread From: Luiz Augusto von Dentz @ 2026-07-17 15:44 UTC (permalink / raw) To: Prathibha Madugonde Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg Hi, On Fri, Jul 17, 2026 at 11:17 AM Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote: > > Hi. > > On Fri, Jul 17, 2026 at 11:07 AM Luiz Augusto von Dentz > <luiz.dentz@gmail.com> wrote: > > > > Hi Prathibha, > > > > On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde > > <prathibha.madugonde@oss.qualcomm.com> wrote: > > > > > > From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com> > > > > > > Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results, > > > and CS/procedure-enable config) into an a{sv} dictionary and emit it > > > as a new ProcedureData signal on the CS D-Bus interface. > > > > Was this documented though? It doesn't seem to follow the likes of > > https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst, > > for instance we don't use camel case for dictionary options. Also, if > > this is a result of `StartMeasurement`, we should probably signal it > > as Results or something like that, but I'd start with documentation so > > we can agree on the overall design. > > Ok, looks like this does follow the documentation already: > > https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst#void-proceduredatadict-data > > That said that will probably need updating since some dictionaries use > camel case and others don't, so this needs to converge to a single > format. I chatted with Marcel, and we concluded this should probably go as a data blob since the fields are somewhat analog and meaningless individually, packing it into a dictionary just to unpack it later is useless. > > > Register a procedure_data callback on the RAP HCI state machine so > > > rap.c is notified when a procedure completes, and propagate the > > > locally supported T_SW time capability from the read-local-supported- > > > capabilities response into bt_rap. > > > > > > --- > > > profiles/ranging/rap.c | 497 ++++++++++++++++++++++++++++++++++++- > > > profiles/ranging/rap_hci.c | 59 +++++ > > > 2 files changed, 555 insertions(+), 1 deletion(-) > > > > > > diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c > > > index 3ffc0da76..42e1ef2a4 100644 > > > --- a/profiles/ranging/rap.c > > > +++ b/profiles/ranging/rap.c > > > @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc( > > > return NULL; > > > } > > > > > > +static void dict_append_byte(DBusMessageIter *dict, const char *key, > > > + uint8_t val) > > > +{ > > > + DBusMessageIter entry, variant; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y", > > > + &variant); > > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void dict_append_int32(DBusMessageIter *dict, const char *key, > > > + dbus_int32_t val) > > > +{ > > > + DBusMessageIter entry, variant; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", > > > + &variant); > > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void dict_append_uint32(DBusMessageIter *dict, const char *key, > > > + dbus_uint32_t val) > > > +{ > > > + DBusMessageIter entry, variant; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u", > > > + &variant); > > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void dict_append_uint64(DBusMessageIter *dict, const char *key, > > > + dbus_uint64_t val) > > > +{ > > > + DBusMessageIter entry, variant; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t", > > > + &variant); > > > + dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void dict_append_byte_array(DBusMessageIter *dict, const char *key, > > > + const uint8_t *data, int len) > > > +{ > > > + DBusMessageIter entry, variant, arr; > > > + int i; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > > > + for (i = 0; i < len; i++) > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]); > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void append_mode_zero(DBusMessageIter *dict, > > > + const struct cs_mode_zero_data *m0) > > > +{ > > > + dict_append_byte(dict, "packetQuality", m0->packet_quality); > > > + dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm); > > > + dict_append_byte(dict, "packetAntenna", m0->packet_ant); > > > + dict_append_int32(dict, "initiatorMeasuredFreqOffset", > > > + (dbus_int32_t)m0->init_measured_freq_offset); > > > +} > > > + > > > +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key, > > > + const struct pct_iq_sample *pct) > > > +{ > > > + DBusMessageIter entry, variant, arr; > > > + dbus_int32_t i_s = pct->i_sample; > > > + dbus_int32_t q_s = pct->q_sample; > > > + > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void append_mode_one(DBusMessageIter *dict, > > > + const struct cs_mode_one_data *m1) > > > +{ > > > + dict_append_byte(dict, "packetQuality", m1->packet_quality); > > > + dict_append_byte(dict, "packetNadm", m1->packet_nadm); > > > + dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm); > > > + dict_append_int32(dict, "toaTodInitiator", > > > + (dbus_int32_t)m1->toa_tod_init); > > > + dict_append_int32(dict, "todToaReflector", > > > + (dbus_int32_t)m1->tod_toa_refl); > > > + dict_append_byte(dict, "packetAntenna", m1->packet_ant); > > > + append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1); > > > + append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2); > > > +} > > > + > > > +static void append_mode_two(DBusMessageIter *dict, > > > + const struct cs_mode_two_data *m2, > > > + uint8_t num_ant_paths) > > > +{ > > > + DBusMessageIter entry, variant, arr; > > > + const char *key; > > > + int j; > > > + int num_paths; > > > + > > > + /* > > > + * num_ant_paths is the HCI "number of antenna paths" value > > > + * (0-indexed), so actual tone sample count = num_ant_paths + 1, > > > + * capped at array size. > > > + */ > > > + num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ? > > > + (num_ant_paths + 1) : CS_MAX_ANT_PATHS; > > > + > > > + dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index); > > > + > > > + /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */ > > > + key = "tonePctIQSamples"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr); > > > + for (j = 0; j < num_paths; j++) { > > > + dbus_int32_t i_s = m2->tone_pct[j].i_sample; > > > + dbus_int32_t q_s = m2->tone_pct[j].q_sample; > > > + > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s); > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s); > > > + } > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > + > > > + /* toneQualityIndicators: "ay" */ > > > + key = "toneQualityIndicators"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr); > > > + for (j = 0; j < num_paths; j++) { > > > + uint8_t tqi = m2->tone_quality_indicator[j]; > > > + > > > + dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi); > > > + } > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void append_proc_enable_config(DBusMessageIter *outer_dict, > > > + const struct rap_ev_cs_proc_enable_cmplt *cfg) > > > +{ > > > + DBusMessageIter entry, variant, inner; > > > + const char *key = "procedureEnableConfig"; > > > + dbus_uint32_t sub_evt_len_us; > > > + > > > + sub_evt_len_us = cfg->sub_evt_len[0] | > > > + ((uint32_t)cfg->sub_evt_len[1] << 8) | > > > + ((uint32_t)cfg->sub_evt_len[2] << 16); > > > + > > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > > > + &inner); > > > + > > > + dict_append_byte(&inner, "toneAntennaConfigSelection", > > > + cfg->tone_ant_config_sel); > > > + dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us); > > > + dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt); > > > + dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl); > > > + dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl); > > > + dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl); > > > + dict_append_uint32(&inner, "procedureCount", cfg->proc_counter); > > > + dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len); > > > + > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(outer_dict, &entry); > > > +} > > > + > > > +static void append_cs_config_param(DBusMessageIter *outer_dict, > > > + const struct bcs_procedure_data *bcs) > > > +{ > > > + DBusMessageIter entry, variant, inner; > > > + const char *key = "csConfigParam"; > > > + const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config; > > > + > > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}", > > > + &inner); > > > + > > > + dict_append_byte(&inner, "modeType", cfg->main_mode_type); > > > + dict_append_byte(&inner, "subModeType", cfg->sub_mode_type); > > > + dict_append_byte(&inner, "rttType", cfg->rtt_type); > > > + dict_append_byte_array(&inner, "channelMap", cfg->channel_map, > > > + sizeof(cfg->channel_map)); > > > + dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps); > > > + dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps); > > > + dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep); > > > + dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps); > > > + dict_append_byte(&inner, "role", cfg->role); > > > + dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy); > > > + dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type); > > > + dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape); > > > + dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump); > > > + dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep); > > > + dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time); > > > + dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time); > > > + dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time); > > > + dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time); > > > + dict_append_byte(&inner, "tSwTimeUsSupportedByLocal", > > > + bcs->t_sw_time_us_supported_by_local); > > > + dict_append_byte(&inner, "tSwTimeUsSupportedByRemote", > > > + bcs->t_sw_time_us_supported_by_remote); > > > + dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval); > > > + > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(outer_dict, &entry); > > > +} > > > + > > > +static void append_step_to_dict(DBusMessageIter *dict, > > > + const struct cs_step_data *step, > > > + uint8_t num_ant_paths) > > > +{ > > > + DBusMessageIter entry, variant, inner; > > > + const char *mode_key; > > > + > > > + dict_append_byte(dict, "stepMode", step->step_mode); > > > + dict_append_byte(dict, "stepChannel", step->step_chnl); > > > + > > > + switch (step->step_mode) { > > > + case CS_MODE_ZERO: > > > + mode_key = "modeZeroData"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > > + &mode_key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > > + "a{sv}", &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > > + "{sv}", &inner); > > > + append_mode_zero(&inner, > > > + &step->step_mode_data.mode_zero_data); > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > + break; > > > + > > > + case CS_MODE_ONE: > > > + mode_key = "modeOneData"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > > + &mode_key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > > + "a{sv}", &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > > + "{sv}", &inner); > > > + append_mode_one(&inner, > > > + &step->step_mode_data.mode_one_data); > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > + break; > > > + > > > + case CS_MODE_TWO: > > > + mode_key = "modeTwoData"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > > + &mode_key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > > + "a{sv}", &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > > + "{sv}", &inner); > > > + append_mode_two(&inner, > > > + &step->step_mode_data.mode_two_data, > > > + num_ant_paths); > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > + break; > > > + > > > + case CS_MODE_THREE: > > > + mode_key = "modeThreeData"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, > > > + &mode_key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, > > > + "a{sv}", &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, > > > + "{sv}", &inner); > > > + append_mode_one(&inner, > > > + &step->step_mode_data.mode_three_data.mode_one_data); > > > + append_mode_two(&inner, > > > + &step->step_mode_data.mode_three_data.mode_two_data, > > > + num_ant_paths); > > > + dbus_message_iter_close_container(&variant, &inner); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > + break; > > > + > > > + default: > > > + break; > > > + } > > > +} > > > + > > > +static void append_subevent_to_dict(DBusMessageIter *dict, > > > + const struct cs_subevent_result_data *sub) > > > +{ > > > + DBusMessageIter entry, variant, arr; > > > + const char *key; > > > + uint32_t i; > > > + > > > + dict_append_int32(dict, "startAclConnEvtCounter", > > > + (dbus_int32_t)sub->start_acl_conn_evt_counter); > > > + dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp); > > > + dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl); > > > + dict_append_byte(dict, "numAntPaths", sub->num_ant_paths); > > > + dict_append_byte(dict, "subeventAbortReason", > > > + sub->subevent_abort_reason); > > > + dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos); > > > + dict_append_uint32(dict, "numSteps", sub->num_steps); > > > + > > > + /* stepData: av (each element is a{sv}) */ > > > + key = "stepData"; > > > + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, > > > + &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > > > + if (sub->step_data) { > > > + for (i = 0; i < sub->num_steps; i++) { > > > + DBusMessageIter inner, step_dict; > > > + > > > + dbus_message_iter_open_container(&arr, > > > + DBUS_TYPE_VARIANT, "a{sv}", &inner); > > > + dbus_message_iter_open_container(&inner, > > > + DBUS_TYPE_ARRAY, "{sv}", &step_dict); > > > + append_step_to_dict(&step_dict, &sub->step_data[i], > > > + sub->num_ant_paths); > > > + dbus_message_iter_close_container(&inner, &step_dict); > > > + dbus_message_iter_close_container(&arr, &inner); > > > + } > > > + } > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(dict, &entry); > > > +} > > > + > > > +static void append_subevent_array(DBusMessageIter *outer_dict, > > > + const char *key, > > > + const struct cs_subevent_result_data *subevents, > > > + uint32_t count) > > > +{ > > > + DBusMessageIter entry, variant, arr; > > > + uint32_t i; > > > + > > > + dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY, > > > + NULL, &entry); > > > + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > > > + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av", > > > + &variant); > > > + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr); > > > + for (i = 0; i < count; i++) { > > > + DBusMessageIter inner, sub_dict; > > > + > > > + dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT, > > > + "a{sv}", &inner); > > > + dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY, > > > + "{sv}", &sub_dict); > > > + append_subevent_to_dict(&sub_dict, &subevents[i]); > > > + dbus_message_iter_close_container(&inner, &sub_dict); > > > + dbus_message_iter_close_container(&arr, &inner); > > > + } > > > + dbus_message_iter_close_container(&variant, &arr); > > > + dbus_message_iter_close_container(&entry, &variant); > > > + dbus_message_iter_close_container(outer_dict, &entry); > > > +} > > > + > > > +static void rap_emit_procedure_data(struct rap_data *data, > > > + const struct bcs_procedure_data *bcs) > > > +{ > > > + DBusMessage *signal; > > > + DBusMessageIter iter, dict; > > > + > > > + signal = dbus_message_new_signal(device_get_path(data->device), > > > + CS_INTERFACE, "ProcedureData"); > > > + if (!signal) { > > > + error("Failed to allocate ProcedureData signal"); > > > + return; > > > + } > > > + > > > + dbus_message_iter_init_append(signal, &iter); > > > + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); > > > + > > > + dict_append_int32(&dict, "procedureCounter", > > > + (dbus_int32_t)bcs->procedure_counter); > > > + dict_append_int32(&dict, "procedureSequence", > > > + (dbus_int32_t)bcs->procedure_sequence); > > > + dict_append_byte(&dict, "initiatorSelectedTxPower", > > > + (uint8_t)bcs->initiator_selected_tx_power); > > > + dict_append_byte(&dict, "reflectorSelectedTxPower", > > > + (uint8_t)bcs->reflector_selected_tx_power); > > > + > > > + dict_append_uint32(&dict, "initiatorSubeventCount", > > > + bcs->initiator_subevent_count); > > > + if (bcs->initiator_subevent_results && > > > + bcs->initiator_subevent_count > 0) > > > + append_subevent_array(&dict, "initiatorSubeventResults", > > > + bcs->initiator_subevent_results, > > > + bcs->initiator_subevent_count); > > > + > > > + dict_append_byte(&dict, "initiatorProcedureAbortReason", > > > + bcs->initiator_procedure_abort_reason); > > > + > > > + dict_append_uint32(&dict, "reflectorSubeventCount", > > > + bcs->reflector_subevent_count); > > > + if (bcs->reflector_subevent_results && > > > + bcs->reflector_subevent_count > 0) > > > + append_subevent_array(&dict, "reflectorSubeventResults", > > > + bcs->reflector_subevent_results, > > > + bcs->reflector_subevent_count); > > > + > > > + dict_append_byte(&dict, "reflectorProcedureAbortReason", > > > + bcs->reflector_procedure_abort_reason); > > > + > > > + append_proc_enable_config(&dict, &bcs->proc_enable_config); > > > + append_cs_config_param(&dict, bcs); > > > + > > > + dbus_message_iter_close_container(&iter, &dict); > > > + > > > + g_dbus_send_message(btd_get_dbus_connection(), signal); > > > +} > > > + > > > +static void rap_procedure_data(struct bt_rap *rap, > > > + struct bcs_procedure_data *bcs, > > > + void *user_data) > > > +{ > > > + struct rap_data *data = user_data; > > > + > > > + DBG("procedure_counter=%u", bcs->procedure_counter); > > > + rap_emit_procedure_data(data, bcs); > > > +} > > > + > > > static DBusMessage *start_measurement(DBusConnection *conn, > > > DBusMessage *msg, void *user_data) > > > { > > > @@ -534,6 +1020,9 @@ bad_type: > > > data->active_session.cfg = cfg; > > > data->active_session.freq = freq; > > > > > > + bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data, > > > + data, NULL); > > > + > > > return dbus_message_new_method_return(msg); > > > } > > > > > > @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = { > > > { } > > > }; > > > > > > +static const GDBusSignalTable cs_dbus_signals[] = { > > > + { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) }, > > > + { } > > > +}; > > > + > > > static void rap_measurement_timeout_cb(void *user_data) > > > { > > > struct rap_data *data = user_data; > > > @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service) > > > g_dbus_register_interface(btd_get_dbus_connection(), > > > device_get_path(data->device), > > > CS_INTERFACE, cs_dbus_methods, > > > - NULL, cs_dbus_properties, data, NULL); > > > + cs_dbus_signals, cs_dbus_properties, > > > + data, NULL); > > > > > > return 0; > > > } > > > diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c > > > index e57f967a2..3da98972e 100644 > > > --- a/profiles/ranging/rap_hci.c > > > +++ b/profiles/ranging/rap_hci.c > > > @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size, > > > DBG("Sending read remote capabilities for handle 0x%04X", > > > sm->active_conn_handle); > > > bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle); > > > + > > > + bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported); > > > } > > > > > > static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm, > > > @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size, > > > cs_set_state(sm, CS_STATE_INIT); > > > rap_send_hci_def_settings_command(sm, evt); > > > } > > > + > > > + bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported); > > > } > > > > > > static void rap_cs_config_cmplt_evt(const void *data, uint8_t size, > > > @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size, > > > &rap_ev, sm->rap); > > > } > > > > > > +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size, > > > + void *user_data) > > > +{ > > > + struct cs_state_machine *sm = user_data; > > > + const struct bt_hci_evt_le_conn_update_complete *evt; > > > + struct rap_conn_mapping *mapping; > > > + struct bt_rap *rap; > > > + struct iovec iov; > > > + > > > + if (!sm || !data || > > > + size < sizeof(struct bt_hci_evt_le_conn_update_complete)) > > > + return; > > > + > > > + iov.iov_base = (void *) data; > > > + iov.iov_len = size; > > > + > > > + evt = util_iov_pull_mem(&iov, sizeof(*evt)); > > > + if (!evt) { > > > + error("Failed to pull LE conn update complete struct"); > > > + return; > > > + } > > > + > > > + DBG("status=0x%02X handle=0x%04X interval=%u", > > > + evt->status, evt->handle, evt->interval); > > > + > > > + if (evt->status != 0) > > > + return; > > > + > > > + mapping = find_mapping_by_handle(sm, evt->handle); > > > + if (mapping && mapping->rap) { > > > + DBG("Found handle 0x%04X in mapping cache", evt->handle); > > > + rap = mapping->rap; > > > + } else { > > > + error("No mapping found for handle 0x%04X", evt->handle); > > > + return; > > > + } > > > + > > > + bt_rap_set_conn_interval(rap, evt->interval); > > > +} > > > + > > > static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample, > > > int16_t *q_sample) > > > { > > > @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci, > > > rap_cs_subevt_result_evt }, > > > { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE, > > > rap_cs_subevt_result_cont_evt }, > > > + { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, > > > + rap_le_conn_update_complete_evt }, > > > }; > > > struct cs_state_machine *sm; > > > unsigned int i; > > > @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm) > > > false); > > > } > > > > > > +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm, > > > + bt_rap_procedure_data_func_t cb, > > > + void *user_data, > > > + bt_rap_destroy_func_t destroy) > > > +{ > > > + struct cs_state_machine *sm = hci_sm; > > > + > > > + if (!sm || !sm->rap) > > > + return false; > > > + > > > + return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy); > > > +} > > > + > > > bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap, > > > uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type, > > > bool is_central) > > > -- > > > 2.34.1 > > > > > > > > > -- > > Luiz Augusto von Dentz > > > > -- > Luiz Augusto von Dentz -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-17 15:44 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-17 7:26 [PATCH BlueZ v1 0/2] Channel Sounding: Aggregate procedure data and expose via D-Bus Prathibha Madugonde 2026-07-17 7:26 ` [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API Prathibha Madugonde 2026-07-17 9:10 ` Channel Sounding: Aggregate procedure data and expose via D-Bus bluez.test.bot 2026-07-17 7:26 ` [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding Prathibha Madugonde 2026-07-17 15:07 ` Luiz Augusto von Dentz 2026-07-17 15:17 ` Luiz Augusto von Dentz 2026-07-17 15:44 ` Luiz Augusto von Dentz
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox