* [PATCH BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including:
@ 2026-01-23 11:09 Prathibha Madugonde
2026-01-23 12:08 ` [BlueZ] " bluez.test.bot
2026-01-27 18:00 ` [PATCH BlueZ] " patchwork-bot+bluetooth
0 siblings, 2 replies; 3+ messages in thread
From: Prathibha Madugonde @ 2026-01-23 11:09 UTC (permalink / raw)
To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
- RAS Features characteristic (0x2c14) with feature bitfield decoding
- Real-time and On-demand Ranging Data characteristics (0x2c15, 0x2c16)
with segmentation header, ranging header, and subevent header parsing
- RAS Control Point (0x2c17) with command opcodes:
* Get Ranging Data
* ACK Ranging Data
* Retrieve Lost Ranging Data Segments
* Abort Operation
* Set Filter
- RAS Ranging Data Ready (0x2c18) and Data Overwritten (0x2c19)
notification characteristics
Also add RAS service and characteristic UUIDs to the shared UUID table.
This enables btmon to properly decode and display RAS protocol
operations for debugging and analysis of Bluetooth ranging
implementations.
---
monitor/att.c | 447 +++++++++++++++++++++++++++++++++++++++++++++-
src/shared/util.c | 9 +-
2 files changed, 454 insertions(+), 2 deletions(-)
diff --git a/monitor/att.c b/monitor/att.c
index abcdf15a8..24aaa264c 100644
--- a/monitor/att.c
+++ b/monitor/att.c
@@ -4055,6 +4055,438 @@ static void bgr_features_read(const struct l2cap_frame *frame)
mask);
}
+static const struct bitfield_data ras_features_table[] = {
+ { 0, "Real-time Ranging Data (0x00000001)" },
+ { 1, "Retrieve Lost Ranging Data Segments (0x00000002)" },
+ { 2, "Abort Operation (0x00000004)" },
+ { 3, "Filter Ranging Data (0x00000008)" },
+ { }
+};
+
+static const struct bitfield_data antenna_paths_table[] = {
+ { 0, "Antenna Path 1 (0x01)" },
+ { 1, "Antenna Path 2 (0x02)" },
+ { 2, "Antenna Path 3 (0x04)" },
+ { 3, "Antenna Path 4 (0x08)" },
+ { }
+};
+
+static void ras_features_read(const struct l2cap_frame *frame)
+{
+ uint32_t features;
+ uint32_t mask;
+
+ if (!l2cap_frame_get_le32((void *)frame, &features)) {
+ print_text(COLOR_ERROR, " Features: invalid size");
+ goto done;
+ }
+
+ print_field(" Features: 0x%8.8x", features);
+
+ mask = print_bitfield(6, features, ras_features_table);
+ if (mask)
+ print_text(COLOR_WHITE_BG, " RFU fields (0x%8.8x)",
+ mask);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void print_ras_segmentation_header(uint8_t header)
+{
+ bool first_segment = header & 0x01;
+ bool last_segment = (header >> 1) & 0x01;
+ uint8_t segment_index = (header >> 2) & 0x3F;
+
+ print_field(" Segmentation Header: 0x%2.2x", header);
+ print_field(" First Segment: %s", first_segment ? "True" : "False");
+ print_field(" Last Segment: %s", last_segment ? "True" : "False");
+ print_field(" Segment Index: %u", segment_index);
+}
+
+static void print_ras_ranging_header(const struct l2cap_frame *frame)
+{
+ uint16_t ranging_counter_config;
+ uint8_t selected_tx_power;
+ uint8_t antenna_paths_mask;
+ uint8_t mask;
+
+ if (!l2cap_frame_get_le16((void *)frame, &ranging_counter_config)) {
+ print_text(COLOR_ERROR, " Ranging Header: invalid size");
+ return;
+ }
+
+ /* Lower 12 bits: Ranging Counter, Upper 4 bits: Configuration ID */
+ print_field(" Ranging Counter: 0x%3.3x",
+ ranging_counter_config & 0x0FFF);
+ print_field(" Configuration ID: %u",
+ (ranging_counter_config >> 12) & 0x0F);
+
+ if (!l2cap_frame_get_u8((void *)frame, &selected_tx_power)) {
+ print_text(COLOR_ERROR, " Selected TX Power: invalid size");
+ return;
+ }
+
+ print_field(" Selected TX Power: %d dBm", (int8_t)selected_tx_power);
+
+ if (!l2cap_frame_get_u8((void *)frame, &antenna_paths_mask)) {
+ print_text(COLOR_ERROR, " Antenna Paths Mask: invalid size");
+ return;
+ }
+
+ print_field(" Antenna Paths Mask: 0x%2.2x", antenna_paths_mask);
+
+ mask = print_bitfield(6, antenna_paths_mask,
+ antenna_paths_table);
+
+ if (mask)
+ print_text(COLOR_WHITE_BG, " RFU (0x%2.2x)", mask);
+}
+
+static const char *ras_ranging_done_status_str(uint8_t status)
+{
+ switch (status) {
+ case 0x0:
+ return "All results complete";
+ case 0x1:
+ return "Partial results, more to follow";
+ case 0xF:
+ return "All subsequent procedures aborted";
+ default:
+ return "RFU";
+ }
+}
+
+static const char *ras_subevent_done_status_str(uint8_t status)
+{
+ switch (status) {
+ case 0x0:
+ return "All results complete";
+ case 0xF:
+ return "Current subevent aborted";
+ default:
+ return "RFU";
+ }
+}
+
+static const char *ras_abort_reason_str(uint8_t reason)
+{
+ switch (reason) {
+ case 0x0:
+ return "No abort";
+ case 0x1:
+ return "Local/remote request";
+ case 0x2:
+ return "Filtered channel map < 15 channels";
+ case 0x3:
+ return "Channel map update instant passed";
+ case 0xF:
+ return "Unspecified";
+ default:
+ return "RFU";
+ }
+}
+
+static void print_ras_subevent_header(const struct l2cap_frame *frame)
+{
+ uint16_t start_acl_conn_event;
+ uint16_t freq_compensation;
+ uint8_t status_byte1, status_byte2;
+ uint8_t ranging_done_status, subevent_done_status;
+ uint8_t ranging_abort_reason, subevent_abort_reason;
+ uint8_t ref_power_level;
+ uint8_t num_steps_reported;
+
+ if (!l2cap_frame_get_le16((void *)frame, &start_acl_conn_event)) {
+ print_text(COLOR_ERROR,
+ " Start ACL Connection Event: invalid size");
+ return;
+ }
+
+ print_field(" Start ACL Connection Event: %u",
+ start_acl_conn_event);
+
+ if (!l2cap_frame_get_le16((void *)frame, &freq_compensation)) {
+ print_text(COLOR_ERROR,
+ " Frequency Compensation: invalid size");
+ return;
+ }
+
+ print_field(" Frequency Compensation: %d (0.01 ppm)",
+ (int16_t)freq_compensation);
+
+ if (!l2cap_frame_get_u8((void *)frame, &status_byte1)) {
+ print_text(COLOR_ERROR, " Status: invalid size");
+ return;
+ }
+
+ ranging_done_status = status_byte1 & 0x0F;
+ subevent_done_status = (status_byte1 >> 4) & 0x0F;
+
+ print_field(" Ranging Done Status: %s (0x%x)",
+ ras_ranging_done_status_str(ranging_done_status),
+ ranging_done_status);
+ print_field(" Subevent Done Status: %s (0x%x)",
+ ras_subevent_done_status_str(subevent_done_status),
+ subevent_done_status);
+
+ if (!l2cap_frame_get_u8((void *)frame, &status_byte2)) {
+ print_text(COLOR_ERROR, " Abort Reasons: invalid size");
+ return;
+ }
+
+ ranging_abort_reason = status_byte2 & 0x0F;
+ subevent_abort_reason = (status_byte2 >> 4) & 0x0F;
+
+ print_field(" Ranging Abort Reason: %s (0x%x)",
+ ras_abort_reason_str(ranging_abort_reason),
+ ranging_abort_reason);
+ print_field(" Subevent Abort Reason: %s (0x%x)",
+ ras_abort_reason_str(subevent_abort_reason),
+ subevent_abort_reason);
+
+ if (!l2cap_frame_get_u8((void *)frame, &ref_power_level)) {
+ print_text(COLOR_ERROR,
+ " Reference Power Level: invalid size");
+ return;
+ }
+
+ print_field(" Reference Power Level: %d dBm",
+ (int8_t)ref_power_level);
+
+ if (!l2cap_frame_get_u8((void *)frame, &num_steps_reported)) {
+ print_text(COLOR_ERROR,
+ " Number of Steps Reported: invalid size");
+ return;
+ }
+
+ print_field(" Number of Steps Reported: %u", num_steps_reported);
+}
+
+static void ras_ranging_data_read(const struct l2cap_frame *frame)
+{
+ uint8_t seg_header;
+ bool first_segment;
+
+ if (!l2cap_frame_get_u8((void *)frame, &seg_header)) {
+ print_text(COLOR_ERROR, " Segmentation Header: invalid size");
+ goto done;
+ }
+
+ print_ras_segmentation_header(seg_header);
+
+ first_segment = seg_header & 0x01;
+
+ /* Only try to decode headers if this is the first segment */
+ if (first_segment && frame->size >= 6) {
+ print_field(" Ranging Data Body:");
+ print_ras_ranging_header(frame);
+
+ /* Try to decode subevent header if enough data remains */
+ if (frame->size >= 10) {
+ print_field(" Subevent #0:");
+ print_ras_subevent_header(frame);
+ }
+ }
+
+ if (frame->size > 0) {
+ print_hex_field(" Remaining Ranging Data Segment", frame->data,
+ frame->size);
+ }
+
+done:
+ if (frame->size)
+ print_hex_field(" Remaining Data", frame->data, frame->size);
+}
+
+static void ras_ranging_data_notify(const struct l2cap_frame *frame)
+{
+ ras_ranging_data_read(frame);
+}
+
+static bool ras_get_ranging_data_cmd(const struct l2cap_frame *frame)
+{
+ uint16_t ranging_counter;
+
+ if (!l2cap_frame_get_le16((void *)frame, &ranging_counter)) {
+ print_text(COLOR_ERROR, " Ranging Counter: invalid size");
+ return false;
+ }
+
+ print_field(" Ranging Counter: 0x%4.4x", ranging_counter);
+
+ return true;
+}
+
+static bool ras_ack_ranging_data_cmd(const struct l2cap_frame *frame)
+{
+ uint16_t ranging_counter;
+
+ if (!l2cap_frame_get_le16((void *)frame, &ranging_counter)) {
+ print_text(COLOR_ERROR, " Ranging Counter: invalid size");
+ return false;
+ }
+
+ print_field(" Ranging Counter: 0x%4.4x", ranging_counter);
+
+ return true;
+}
+
+static bool ras_retrieve_lost_segments_cmd(const struct l2cap_frame *frame)
+{
+ uint16_t ranging_counter;
+ uint8_t first_segment, last_segment;
+
+ if (!l2cap_frame_get_le16((void *)frame, &ranging_counter)) {
+ print_text(COLOR_ERROR, " Ranging Counter: invalid size");
+ return false;
+ }
+
+ print_field(" Ranging Counter: 0x%4.4x", ranging_counter);
+
+ if (!l2cap_frame_get_u8((void *)frame, &first_segment)) {
+ print_text(COLOR_ERROR,
+ " First Segment Index: invalid size");
+ return false;
+ }
+
+ print_field(" First Segment Index: %u", first_segment);
+
+ if (!l2cap_frame_get_u8((void *)frame, &last_segment)) {
+ print_text(COLOR_ERROR, " Last Segment Index: invalid size");
+ return false;
+ }
+
+ if (last_segment == 0xFF)
+ print_field(" Last Segment Index: All remaining (0xFF)");
+ else
+ print_field(" Last Segment Index: %u", last_segment);
+
+ return true;
+}
+
+static bool ras_set_filter_cmd(const struct l2cap_frame *frame)
+{
+ uint16_t filter_config;
+ uint8_t mode;
+ uint16_t filter_mask;
+
+ if (!l2cap_frame_get_le16((void *)frame, &filter_config)) {
+ print_text(COLOR_ERROR,
+ " Filter Configuration: invalid size");
+ return false;
+ }
+
+ mode = filter_config & 0x03;
+ filter_mask = (filter_config >> 2) & 0x3FFF;
+
+ print_field(" Filter Configuration: 0x%4.4x", filter_config);
+ print_field(" Mode: %u", mode);
+ print_field(" Filter Bit Mask: 0x%4.4x", filter_mask);
+
+ return true;
+}
+
+#define RAS_CMD(_op, _desc, _func) \
+[_op] = { \
+ .desc = _desc, \
+ .func = _func, \
+}
+
+static const struct ras_cmd {
+ const char *desc;
+ bool (*func)(const struct l2cap_frame *frame);
+} ras_cmd_table[] = {
+ /* Opcode = 0x00 (Get Ranging Data) */
+ RAS_CMD(0x00, "Get Ranging Data", ras_get_ranging_data_cmd),
+ /* Opcode = 0x01 (ACK Ranging Data) */
+ RAS_CMD(0x01, "ACK Ranging Data", ras_ack_ranging_data_cmd),
+ /* Opcode = 0x02 (Retrieve Lost Ranging Data Segments) */
+ RAS_CMD(0x02, "Retrieve Lost Ranging Data Segments",
+ ras_retrieve_lost_segments_cmd),
+ /* Opcode = 0x03 (Abort Operation) */
+ RAS_CMD(0x03, "Abort Operation", NULL),
+ /* Opcode = 0x04 (Set Filter) */
+ RAS_CMD(0x04, "Set Filter", ras_set_filter_cmd),
+};
+
+static const struct ras_cmd *ras_get_cmd(uint8_t op)
+{
+ if (op > ARRAY_SIZE(ras_cmd_table))
+ return NULL;
+
+ return &ras_cmd_table[op];
+}
+
+static void ras_control_point_write(const struct l2cap_frame *frame)
+{
+ uint8_t opcode;
+ const struct ras_cmd *cmd;
+
+ if (!l2cap_frame_get_u8((void *)frame, &opcode)) {
+ print_text(COLOR_ERROR, " Opcode: invalid size");
+ goto done;
+ }
+
+ cmd = ras_get_cmd(opcode);
+ if (!cmd) {
+ print_field(" Opcode: Reserved (0x%2.2x)", opcode);
+ goto done;
+ }
+
+ print_field(" Opcode: %s (0x%2.2x)", cmd->desc, opcode);
+
+ if (cmd->func && !cmd->func(frame))
+ goto done;
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void ras_data_ready_read(const struct l2cap_frame *frame)
+{
+ uint16_t counter;
+
+ if (!l2cap_frame_get_le16((void *)frame, &counter)) {
+ print_text(COLOR_ERROR, " Counter: invalid size");
+ goto done;
+ }
+
+ print_field(" Counter: %u", counter);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void ras_data_ready_notify(const struct l2cap_frame *frame)
+{
+ ras_data_ready_read(frame);
+}
+
+static void ras_data_overwritten_read(const struct l2cap_frame *frame)
+{
+ uint16_t counter;
+
+ if (!l2cap_frame_get_le16((void *)frame, &counter)) {
+ print_text(COLOR_ERROR, " Counter: invalid size");
+ goto done;
+ }
+
+ print_field(" Overwritten Count: %u", counter);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void ras_data_overwritten_notify(const struct l2cap_frame *frame)
+{
+ ras_data_overwritten_read(frame);
+}
+
#define GMAS \
GATT_HANDLER(0x2c00, gmap_role_read, NULL, NULL), \
GATT_HANDLER(0x2c01, ugg_features_read, NULL, NULL), \
@@ -4062,6 +4494,18 @@ static void bgr_features_read(const struct l2cap_frame *frame)
GATT_HANDLER(0x2c02, bgs_features_read, NULL, NULL), \
GATT_HANDLER(0x2c03, bgr_features_read, NULL, NULL)
+#define RAS \
+ GATT_HANDLER(0x2c14, ras_features_read, NULL, NULL), \
+ GATT_HANDLER(0x2c15, ras_ranging_data_read, NULL, \
+ ras_ranging_data_notify), \
+ GATT_HANDLER(0x2c16, ras_ranging_data_read, NULL, \
+ ras_ranging_data_notify), \
+ GATT_HANDLER(0x2c17, NULL, ras_control_point_write, NULL), \
+ GATT_HANDLER(0x2c18, ras_data_ready_read, NULL, \
+ ras_data_ready_notify), \
+ GATT_HANDLER(0x2c19, ras_data_overwritten_read, NULL, \
+ ras_data_overwritten_notify)
+
#define GATT_HANDLER(_uuid, _read, _write, _notify) \
{ \
.uuid = { \
@@ -4144,7 +4588,8 @@ static const struct gatt_handler {
GATT_HANDLER(0x2bc1, incoming_call_read, NULL, incoming_call_notify),
GATT_HANDLER(0x2bc2, call_friendly_name_read, NULL,
call_friendly_name_notify),
- GMAS
+ GMAS,
+ RAS
};
static const struct gatt_handler *get_handler_uuid(const bt_uuid_t *uuid)
diff --git a/src/shared/util.c b/src/shared/util.c
index 6f7ce0a25..07e718bca 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -844,7 +844,8 @@ static const struct {
{ 0x1855, "Telephony and Media Audio" },
{ 0x1856, "Public Broadcast Announcement" },
{ 0x1858, "Gaming Audio" },
- /* 0x1857 to 0x27ff undefined */
+ { 0x185b, "Ranging Service" },
+ /* 0x185c to 0x27ff undefined */
{ 0x2800, "Primary Service" },
{ 0x2801, "Secondary Service" },
{ 0x2802, "Include" },
@@ -1157,6 +1158,12 @@ static const struct {
{ 0x2c02, "UGT Features" },
{ 0x2c03, "BGS Features" },
{ 0x2c03, "BGR Features" },
+ { 0x2c14, "RAS Features" },
+ { 0x2c15, "RAS Real-time Ranging Data" },
+ { 0x2c16, "RAS On-demand Ranging Data" },
+ { 0x2c17, "RAS Control Point" },
+ { 0x2c18, "RAS Ranging Data Ready" },
+ { 0x2c19, "RAS Ranging Data Overwritten" },
/* vendor defined */
{ 0xfeff, "GN Netcom" },
{ 0xfefe, "GN ReSound A/S" },
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* RE: [BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including:
2026-01-23 11:09 [PATCH BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including: Prathibha Madugonde
@ 2026-01-23 12:08 ` bluez.test.bot
2026-01-27 18:00 ` [PATCH BlueZ] " patchwork-bot+bluetooth
1 sibling, 0 replies; 3+ messages in thread
From: bluez.test.bot @ 2026-01-23 12:08 UTC (permalink / raw)
To: linux-bluetooth, prathibha.madugonde
[-- Attachment #1: Type: text/plain, Size: 1262 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=1046215
---Test result---
Test Summary:
CheckPatch PENDING 0.37 seconds
GitLint PENDING 0.45 seconds
BuildEll PASS 20.06 seconds
BluezMake PASS 652.37 seconds
MakeCheck PASS 18.34 seconds
MakeDistcheck PASS 246.08 seconds
CheckValgrind PASS 292.75 seconds
CheckSmatch PASS 349.63 seconds
bluezmakeextell PASS 180.60 seconds
IncrementalBuild PENDING 0.41 seconds
ScanBuild PASS 1020.51 seconds
Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:
##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including:
2026-01-23 11:09 [PATCH BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including: Prathibha Madugonde
2026-01-23 12:08 ` [BlueZ] " bluez.test.bot
@ 2026-01-27 18:00 ` patchwork-bot+bluetooth
1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+bluetooth @ 2026-01-27 18:00 UTC (permalink / raw)
To: Prathibha Madugonde
Cc: linux-bluetooth, luiz.dentz, quic_mohamull, quic_hbandi,
quic_anubhavg
Hello:
This patch was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Fri, 23 Jan 2026 16:39:44 +0530 you wrote:
> - RAS Features characteristic (0x2c14) with feature bitfield decoding
> - Real-time and On-demand Ranging Data characteristics (0x2c15, 0x2c16)
> with segmentation header, ranging header, and subevent header parsing
> - RAS Control Point (0x2c17) with command opcodes:
> * Get Ranging Data
> * ACK Ranging Data
> * Retrieve Lost Ranging Data Segments
> * Abort Operation
> * Set Filter
> - RAS Ranging Data Ready (0x2c18) and Data Overwritten (0x2c19)
> notification characteristics
>
> [...]
Here is the summary with links:
- [BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=daaf5177720a
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-01-27 18:00 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-23 11:09 [PATCH BlueZ] monitor: Add support for Ranging Service (RAS) Implement comprehensive decoding support for the Bluetooth Ranging Service (RAS) in the ATT monitor, including: Prathibha Madugonde
2026-01-23 12:08 ` [BlueZ] " bluez.test.bot
2026-01-27 18:00 ` [PATCH BlueZ] " patchwork-bot+bluetooth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox