From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
To: linux-bluetooth@vger.kernel.org
Cc: luiz.dentz@gmail.com, quic_mohamull@quicinc.com,
quic_hbandi@quicinc.com, quic_anubhavg@quicinc.com
Subject: [PATCH v1] shared/rap: Add bcs_procedure_data aggregation and procedure data API
Date: Fri, 3 Jul 2026 10:50:56 +0530 [thread overview]
Message-ID: <20260703052056.3568571-1-prathm@qti.qualcomm.com> (raw)
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 | 679 ++++++++++++++++++++++++++++++++++++++++++++---
src/shared/rap.h | 47 ++++
2 files changed, 690 insertions(+), 36 deletions(-)
diff --git a/src/shared/rap.c b/src/shared/rap.c
index dfb272d3a..bfcb31ac8 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,25 @@ enum cs_role {
};
#define CS_INVALID_CONFIG_ID 0xFF
+#define CS_PROC_STATE_BUFFER_SIZE 16
+#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 {
+ bool active;
+ 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 +279,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 cs_proc_state proc_states[CS_PROC_STATE_BUFFER_SIZE];
+ uint8_t procedure_sequence_after_enable;
+ uint64_t proc_start_timestamp_nanos;
};
/* Ranging Service context */
@@ -300,6 +332,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 +583,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 struct cs_proc_state *find_or_create_proc_state(struct cstracker *t,
+ uint16_t proc_counter)
+{
+ int i, empty_idx = -1;
+
+ for (i = 0; i < CS_PROC_STATE_BUFFER_SIZE; i++) {
+ if (t->proc_states[i].active &&
+ t->proc_states[i].proc_counter == proc_counter)
+ return &t->proc_states[i];
+ if (empty_idx < 0 && !t->proc_states[i].active)
+ empty_idx = i;
+ }
+
+ if (empty_idx < 0) {
+ /* Buffer full — evict oldest slot */
+ bcs_proc_data_clear(&t->proc_states[0].bcs_data);
+ memmove(&t->proc_states[0], &t->proc_states[1],
+ (CS_PROC_STATE_BUFFER_SIZE - 1) *
+ sizeof(t->proc_states[0]));
+ empty_idx = CS_PROC_STATE_BUFFER_SIZE - 1;
+ }
+
+ {
+ struct cs_proc_state *s = &t->proc_states[empty_idx];
+
+ memset(s, 0, sizeof(*s));
+ s->active = true;
+ 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;
+ return s;
+ }
+}
+
+static struct cs_proc_state *find_proc_state_for_ras(struct cstracker *t,
+ uint16_t ranging_counter)
+{
+ int i;
+
+ for (i = 0; i < CS_PROC_STATE_BUFFER_SIZE; i++) {
+ struct cs_proc_state *s = &t->proc_states[i];
+
+ if (s->active &&
+ (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 +768,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 +776,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 +810,27 @@ 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) {
+ int i;
+
+ for (i = 0; i < CS_PROC_STATE_BUFFER_SIZE; i++) {
+ if (rap->reqtracker->proc_states[i].active)
+ bcs_proc_data_clear(
+ &rap->reqtracker->proc_states[i].bcs_data);
+ }
+ 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 +916,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)
@@ -1804,12 +2036,179 @@ 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);
+
+ state->active = false;
+}
+
+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 +2218,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 +2251,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 +2308,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 +2324,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 +2389,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 +2599,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 +2621,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 +2637,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 +2662,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 +2686,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 +2726,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 +2744,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 +2787,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 +2847,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 +2892,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 +2912,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 +2927,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 +2962,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 (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.subevent_done_status ==
- SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
- hdr.ranging_done_status ==
+ 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 7bc49f03d..e3f1cf994 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);
@@ -211,6 +250,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,
@@ -220,3 +263,7 @@ bool bt_rap_set_conn_hndl(void *hci_sm,
bool is_central);
void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
+/* 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
next reply other threads:[~2026-07-03 5:21 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-03 5:20 Prathibha Madugonde [this message]
2026-07-03 8:41 ` [v1] shared/rap: Add bcs_procedure_data aggregation and procedure data API bluez.test.bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260703052056.3568571-1-prathm@qti.qualcomm.com \
--to=prathibha.madugonde@oss.qualcomm.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=quic_anubhavg@quicinc.com \
--cc=quic_hbandi@quicinc.com \
--cc=quic_mohamull@quicinc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox