From: Cheng Jiang <cheng.jiang@oss.qualcomm.com>
To: linux-bluetooth@vger.kernel.org
Cc: jinwang.li@oss.qualcomm.com, wei.deng@oss.qualcomm.com,
xiuzhuo.shang@oss.qualcomm.com, shuai.zhang@oss.qualcomm.com,
Cheng Jiang <cheng.jiang@oss.qualcomm.com>
Subject: [PATCH 06/11] shared/rap: Add real-time ranging data delivery on the responder
Date: Thu, 1 Oct 2026 21:18:52 +0800 [thread overview]
Message-ID: <20261001131900.707485-7-cheng.jiang@oss.qualcomm.com> (raw)
In-Reply-To: <20261001131900.707485-1-cheng.jiang@oss.qualcomm.com>
Implement the RAS responder side of Real-time Ranging Data. Completed
ranging-data segments are queued in a per-connection FIFO and delivered
on the Real-time Ranging Data characteristic, honoring the transport
selected by the client's CCCD: notifications are paced with a fixed
inter-segment interval, while indications are driven by ATT confirmations
so the next segment is only sent once the previous one is acknowledged.
The pending real-time segment queue is cleared when a new CS procedure
starts, so stale segments from an aborted or superseded procedure are
never delivered to the peer.
Assisted-by: Claude:claude-opus-4.8
---
src/shared/rap.c | 175 +++++++++++++++++++++++++++++++++++------------
1 file changed, 132 insertions(+), 43 deletions(-)
diff --git a/src/shared/rap.c b/src/shared/rap.c
index 7c9a439a5..b1108ad68 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -419,6 +419,22 @@ struct bt_rap_db {
struct ras *ras;
};
+struct ras_realtime_segment {
+ uint8_t *value;
+ uint16_t len;
+};
+
+static void ras_realtime_segment_free(void *data)
+{
+ struct ras_realtime_segment *segment = data;
+
+ if (!segment)
+ return;
+
+ free(segment->value);
+ free(segment);
+}
+
struct bt_rap {
int ref_count;
struct bt_rap_db *lrapdb;
@@ -460,6 +476,11 @@ struct bt_rap {
* centrals cannot evict or receive each other's cached data.
*/
struct ras_ondemand_cache cache;
+
+ /* Serialized real-time segments await transport-paced delivery. */
+ struct queue *realtime_segments;
+ unsigned int realtime_send_id;
+ bool realtime_indicating;
};
static struct queue *rap_db;
@@ -1019,6 +1040,9 @@ static void rap_free(void *data)
rap_db_free(rap->rrapdb);
free(rap->cache.data.iov_base);
+ if (rap->realtime_send_id)
+ timeout_remove(rap->realtime_send_id);
+ queue_destroy(rap->realtime_segments, ras_realtime_segment_free);
if (rap->resptracker) {
free(rap->resptracker->segment_data.iov_base);
@@ -2130,6 +2154,91 @@ static void send_ondemand_segment_data(struct bt_rap *rap, struct ras *ras)
send_ondemand_segment_data_cb(rap);
}
+static bool send_realtime_segment_cb(void *user_data);
+
+static void ras_realtime_segments_clear(struct bt_rap *rap)
+{
+ if (!rap)
+ return;
+
+ if (rap->realtime_send_id) {
+ timeout_remove(rap->realtime_send_id);
+ rap->realtime_send_id = 0;
+ }
+
+ queue_destroy(rap->realtime_segments, ras_realtime_segment_free);
+ rap->realtime_segments = queue_new();
+}
+
+static void realtime_ind_conf_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_rap *rap = user_data;
+
+ if (!rap)
+ return;
+
+ rap->realtime_indicating = false;
+ send_realtime_segment_cb(rap);
+}
+
+static bool send_realtime_segment_cb(void *user_data)
+{
+ struct bt_rap *rap = user_data;
+ struct ras *ras = rap && rap->lrapdb ? rap->lrapdb->ras : NULL;
+ struct ras_realtime_segment *segment;
+
+ if (!rap || !ras || !rap->realtime_segments)
+ return false;
+
+ rap->realtime_send_id = 0;
+ if (rap->realtime_indicating || queue_isempty(rap->realtime_segments))
+ return false;
+
+ segment = queue_pop_head(rap->realtime_segments);
+ if (!segment)
+ return false;
+
+ if (ras_data_ccc_use_indication(ras->realtime_ccc_value)) {
+ uint16_t vhandle = gatt_db_attribute_get_handle(
+ gatt_db_attribute_get_value(ras->realtime_chrc));
+ uint8_t *pdu = malloc(2 + segment->len);
+ unsigned int id = 0;
+
+ if (pdu) {
+ put_le16(vhandle, pdu);
+ memcpy(pdu + 2, segment->value, segment->len);
+ id = bt_att_send(bt_rap_get_att(rap), BT_ATT_OP_HANDLE_IND,
+ pdu, 2 + segment->len, realtime_ind_conf_cb,
+ rap, NULL);
+ free(pdu);
+ }
+
+ ras_realtime_segment_free(segment);
+ if (!id) {
+ DBG(rap, "Failed to send real-time indication");
+ return false;
+ }
+
+ rap->realtime_indicating = true;
+ return false;
+ }
+
+ /* Reached whenever the client enabled notifications, so
+ * gatt_db_attribute_notify() picks the notification transport.
+ */
+ if (!gatt_db_attribute_notify(ras->realtime_chrc, segment->value,
+ segment->len, bt_rap_get_att(rap)))
+ DBG(rap, "Failed to send real-time notification");
+ ras_realtime_segment_free(segment);
+
+ if (!queue_isempty(rap->realtime_segments))
+ rap->realtime_send_id = timeout_add(RAS_SEGMENT_NFY_PACE_MS,
+ send_realtime_segment_cb, rap, NULL);
+
+ return false;
+}
+
static void send_ras_segment_data(struct bt_rap *rap,
struct cs_procedure_data *proc)
{
@@ -2168,7 +2277,6 @@ static void send_ras_segment_data(struct bt_rap *rap,
uint16_t seg_len;
uint8_t *seg;
uint16_t wr = 0;
- bool ok = true;
if (index > total_len)
index = total_len;
@@ -2178,25 +2286,14 @@ static void send_ras_segment_data(struct bt_rap *rap,
if (unsent_data_size == 0)
return;
- /* Set last_segment if procedure complete or fits in segment */
- if ((proc->local_status != CS_PROC_PARTIAL_RESULTS &&
- unsent_data_size <= raw_payload_size) ||
- (proc->contains_complete_subevent_ &&
- unsent_data_size <= raw_payload_size)) {
+ /* Only the final completed procedure segment terminates the stream. */
+ if (proc->local_status != CS_PROC_PARTIAL_RESULTS &&
+ unsent_data_size <= raw_payload_size) {
proc->segmentation_header_.last_segment = 1;
} else {
proc->segmentation_header_.last_segment = 0;
}
- /* Wait for more data if needed and not last segment */
- if (unsent_data_size < raw_payload_size &&
- proc->segmentation_header_.last_segment == 0) {
- DBG(rap, "waiting for more data (unsent=%zu < "
- "payload=%u)", unsent_data_size,
- raw_payload_size);
- return;
- }
-
copy_size = (uint16_t)((unsent_data_size < raw_payload_size) ?
unsent_data_size : raw_payload_size);
seg_len = (uint16_t)(header_len + copy_size);
@@ -2215,37 +2312,25 @@ static void send_ras_segment_data(struct bt_rap *rap,
copy_size);
wr += copy_size;
- /* Try sending to real-time characteristic */
- if (ras->realtime_chrc) {
- if (ras_data_ccc_use_indication(ras->realtime_ccc_value))
- ok = gatt_db_attribute_indicate(
- ras->realtime_chrc, seg, wr,
- bt_rap_get_att(rap));
- else
- ok = gatt_db_attribute_notify(
- ras->realtime_chrc, seg, wr,
- bt_rap_get_att(rap));
- }
+ {
+ struct ras_realtime_segment *segment = new0(
+ struct ras_realtime_segment, 1);
- /* Try sending to on-demand characteristic */
- if (ras->ondemand_chrc && ok) {
- if (ras_data_ccc_use_indication(ras->ondemand_ccc_value))
- ok = gatt_db_attribute_indicate(
- ras->ondemand_chrc, seg, wr,
- bt_rap_get_att(rap));
- else
- ok = gatt_db_attribute_notify(
- ras->ondemand_chrc, seg, wr,
- bt_rap_get_att(rap));
- }
-
- free(seg);
+ if (!segment || !queue_push_tail(rap->realtime_segments,
+ segment)) {
+ free(segment);
+ free(seg);
+ DBG(rap, "Failed to queue real-time RAS segment");
+ return;
+ }
- if (!ok) {
- DBG(rap, "Failed to send RAS notification");
- return;
+ segment->value = seg;
+ segment->len = wr;
}
+ if (!rap->realtime_send_id && !rap->realtime_indicating)
+ send_realtime_segment_cb(rap);
+
/* Advance read cursor and update segmentation state */
proc->ras_raw_data_index_ += copy_size;
proc->segmentation_header_.first_segment = 0;
@@ -2621,7 +2706,10 @@ static void handle_local_subevent_result(struct bt_rap *rap,
struct cs_procedure_data *cur = resptracker->current_proc;
if (has_header_fields && cur->counter != proc_counter) {
- /* Safety: a new procedure; destroy the previous one */
+ /* A new real-time procedure supersedes queued old ranging data. */
+ if (!ras_is_ondemand_mode(rap->lrapdb->ras))
+ ras_realtime_segments_clear(rap);
+
resptracker_reset_current_proc(resptracker);
}
}
@@ -3220,6 +3308,7 @@ struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
rap->pending = queue_new();
rap->ready_cbs = queue_new();
rap->notify = queue_new();
+ rap->realtime_segments = queue_new();
if (!rdb)
goto done;
--
2.43.0
next prev parent reply other threads:[~2026-10-01 13:19 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 13:18 [PATCH 00/11] Ranging Profile and Ranging Service PTS fixes Cheng Jiang
2026-10-01 13:18 ` [PATCH 01/11] shared/gatt-client: Allow registering notify with explicit CCC value Cheng Jiang
2026-10-01 16:03 ` Ranging Profile and Ranging Service PTS fixes bluez.test.bot
2026-10-01 13:18 ` [PATCH 02/11] shared/gatt-db: Add gatt_db_attribute_indicate() Cheng Jiang
2026-10-01 15:48 ` Luiz Augusto von Dentz
2026-10-09 8:45 ` Cheng Jiang
2026-10-01 13:18 ` [PATCH 03/11] shared/rap: Honor CCCD transport for RAS on-demand ranging data Cheng Jiang
2026-10-01 13:18 ` [PATCH 04/11] shared/rap: Add RAS Control Point Abort Operation Cheng Jiang
2026-10-01 13:18 ` [PATCH 05/11] shared/rap: Shorten the requester Control Point response timeout Cheng Jiang
2026-10-01 13:18 ` Cheng Jiang [this message]
2026-10-01 13:18 ` [PATCH 07/11] shared/rap: Add requester ranging data subscription API Cheng Jiang
2026-10-01 13:18 ` [PATCH 08/11] profiles/ranging: Expose requester ranging data mode over D-Bus Cheng Jiang
2026-10-01 13:18 ` [PATCH 09/11] client: Add Channel Sounding ranging data mode control Cheng Jiang
2026-10-01 13:18 ` [PATCH 10/11] doc: Document " Cheng Jiang
2026-10-01 13:18 ` [PATCH 11/11] doc/qualification: Add RAP/RAS PTS qualification results Cheng Jiang
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=20261001131900.707485-7-cheng.jiang@oss.qualcomm.com \
--to=cheng.jiang@oss.qualcomm.com \
--cc=jinwang.li@oss.qualcomm.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=shuai.zhang@oss.qualcomm.com \
--cc=wei.deng@oss.qualcomm.com \
--cc=xiuzhuo.shang@oss.qualcomm.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