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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.