Linux bluetooth development
 help / color / mirror / Atom feed
* Re: [PATCH BlueZ v2] src/shared: add custom CCC callbacks
From: patchwork-bot+bluetooth @ 2026-04-24 15:00 UTC (permalink / raw)
  To: Prathibha Madugonde
  Cc: linux-bluetooth, luiz.dentz, quic_mohamull, quic_hbandi,
	quic_anubhavg
In-Reply-To: <20260424042830.2374708-1-prathm@qti.qualcomm.com>

Hello:

This patch was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri, 24 Apr 2026 09:58:30 +0530 you wrote:
> From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
> 
> Introduce custom CCC callbacks to allow interception of
> client notification subscription changes. This enables
> stack-level handling of CCC writes.
> ---
>  src/shared/gatt-db.c | 119 ++++++++++++++++++++++++++++++++++++++++++-
>  src/shared/gatt-db.h |   4 ++
>  2 files changed, 122 insertions(+), 1 deletion(-)

Here is the summary with links:
  - [BlueZ,v2] src/shared: add custom CCC callbacks
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=83ddf46ccc06

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [bluez/bluez] 756da3: input: Fix checking LE bonding on HIDP
From: prathibhamadugonde @ 2026-04-24 15:10 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/master
  Home:   https://github.com/bluez/bluez
  Commit: 756da3fa17b9a892ce22bc340b9a293a6c68931f
      https://github.com/bluez/bluez/commit/756da3fa17b9a892ce22bc340b9a293a6c68931f
  Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M profiles/input/device.c

  Log Message:
  -----------
  input: Fix checking LE bonding on HIDP

HIDP is classic only, LE uses HOG, so there is no point in checking the
bonding with the address type which in case the device supports
dual-mode will map to LE random/public address.

Fixes: https://github.com/bluez/bluez/issues/2034


  Commit: aca0a01c3f5e73e4ab44b186b6a0a00c589be87c
      https://github.com/bluez/bluez/commit/aca0a01c3f5e73e4ab44b186b6a0a00c589be87c
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M src/shared/hci.c
    M src/shared/hci.h
    M src/shared/rap.c
    M src/shared/rap.h

  Log Message:
  -----------
  shared: rap: Introduce Channel Sounding HCI raw interface support

Implement stub callbacks for Channel Sounding HCI events and add the
required protocol definitions for CS configuration, procedure control,
and subevent result parsing

Add data structures to support Channel Sounding Processing
Add helper function to get hci conn info list and integrate it with RAP


  Commit: c34ed95fa59b4a61882794f329f74a32a5b1f949
      https://github.com/bluez/bluez/commit/c34ed95fa59b4a61882794f329f74a32a5b1f949
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M src/bluetooth.service.in
    M src/btd.h
    M src/main.c
    M src/main.conf

  Log Message:
  -----------
  main.conf: Add Channel Sounding config parsing support

Add support for parsing Channel Sounding (CS) configuration options
from the configuration file.

Add CAP_NET_RAW to CapabilityBoundingSet in bluetooth.service.
bluetoothd requires CAP_NET_RAW to receive and process HCI LE events
when running under a constrained systemd capability bounding set


  Commit: f1363b27467197525f60f3bbbf43ad1d905719af
      https://github.com/bluez/bluez/commit/f1363b27467197525f60f3bbbf43ad1d905719af
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M Makefile.plugins
    M profiles/ranging/rap.c
    A profiles/ranging/rap_hci.c

  Log Message:
  -----------
  profiles: ranging: Add HCI LE Event Handling in Reflector role

Open RAW HCI Channel for CS Event Handling
Parse the following HCI LE CS Events in reflector role
and route the events to RAP Profile.
 1. HCI_EVT_LE_CS_READ_RMT_SUPP_CAP_COMPLETE
 2. HCI_EVT_LE_CS_CONFIG_COMPLETE
 3. HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE
 4. HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE
 5. HCI_EVT_LE_CS_SUBEVENT_RESULT
 6. HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE
Send HCI_OP_LE_CS_SET_DEFAULT_SETTINGS to the controller
with default settings selected by the user.
Map connection handle received to device connection


  Commit: 6cbdfc70601364435b97276ba9a4feac6179a1ff
      https://github.com/bluez/bluez/commit/6cbdfc70601364435b97276ba9a4feac6179a1ff
  Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M profiles/ranging/rap.c
    M profiles/ranging/rap_hci.c

  Log Message:
  -----------
  rap: Cleanup coding style and unnecessary code


  Commit: 49e2a15adc79eeb082d6eb52fc5ba7c2cf94c20c
      https://github.com/bluez/bluez/commit/49e2a15adc79eeb082d6eb52fc5ba7c2cf94c20c
  Author: Marek Czerski <ma.czerski@gmail.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M plugins/sixaxis.c

  Log Message:
  -----------
  sixaxis: Fix pairing Esperanza EGG109k controller

This change is required for Esperanza EGG109k ps controller clone.
EGG109k looks like PS3 controller but presents itself to the system
as PS4 controller. It does not respond to 0x81 command.
Command 0x12 contains both the device bluetooth address as well as
configured host bluetooth address, so it can be used to query
both. Kernel driver hid-playstation also uses 0x12 command for that.

Manufacturer link:
https://esperanza.pl/esperanza-gamepad-bezprzewodowy-ps3-marine-czarny,176,1701.html


  Commit: 83ddf46ccc0653fb2aa460b57eb60db92ab37597
      https://github.com/bluez/bluez/commit/83ddf46ccc0653fb2aa460b57eb60db92ab37597
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M src/shared/gatt-db.c
    M src/shared/gatt-db.h

  Log Message:
  -----------
  src/shared: add custom CCC callbacks

Introduce custom CCC callbacks to allow interception of
client notification subscription changes. This enables
stack-level handling of CCC writes.


Compare: https://github.com/bluez/bluez/compare/bc87ee6b0c56...83ddf46ccc06

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez] 3464e8: client: add public-broadcast advertising command
From: raghava447 @ 2026-04-24 15:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1085227
  Home:   https://github.com/bluez/bluez
  Commit: 3464e8c6114561c15efd0b2938662a3d4025f7ed
      https://github.com/bluez/bluez/commit/3464e8c6114561c15efd0b2938662a3d4025f7ed
  Author: raghavendra <raghavendra.rao@collabora.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M client/advertising.c
    M client/advertising.h
    M client/main.c

  Log Message:
  -----------
  client: add public-broadcast advertising command


  Commit: 713b66d6075ddb8ff8adaca90276b3058803fffb
      https://github.com/bluez/bluez/commit/713b66d6075ddb8ff8adaca90276b3058803fffb
  Author: raghavendra <raghavendra.rao@collabora.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M client/advertising.c

  Log Message:
  -----------
  client: make advertise.name use public broadcast name


Compare: https://github.com/bluez/bluez/compare/3464e8c61145%5E...713b66d6075d

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-04-24 15:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1085008
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-04-24 15:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1084770
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-04-24 15:39 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1084434
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* RE: client: Add public broadcast advertising support
From: bluez.test.bot @ 2026-04-24 16:23 UTC (permalink / raw)
  To: linux-bluetooth, raghavendra.rao
In-Reply-To: <20260424141831.9172-2-raghavendra.rao@collabora.com>

[-- Attachment #1: Type: text/plain, Size: 12505 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=1085227

---Test result---

Test Summary:
CheckPatch                    FAIL      1.30 seconds
GitLint                       PASS      0.67 seconds
BuildEll                      PASS      19.93 seconds
BluezMake                     PASS      604.72 seconds
CheckSmatch                   PASS      318.76 seconds
bluezmakeextell               PASS      162.72 seconds
IncrementalBuild              PASS      597.91 seconds
ScanBuild                     PASS      910.00 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v2,1/2] client: add public-broadcast advertising command
WARNING:LEADING_SPACE: please, no spaces at the start of a line
#106: FILE: client/advertising.c:1014:
+    return uuid && !bt_uuid_strcmp(uuid, AD_PUBLIC_BROADCAST_UUID);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#111: FILE: client/advertising.c:1019:
+    if (!ad_is_public_broadcast_uuid(ad.service[AD_TYPE_AD].uuid))$

ERROR:CODE_INDENT: code indent should use tabs where possible
#112: FILE: client/advertising.c:1020:
+        return NULL;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#112: FILE: client/advertising.c:1020:
+        return NULL;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#114: FILE: client/advertising.c:1022:
+    if (ad.service[AD_TYPE_AD].data.len != 2)$

ERROR:CODE_INDENT: code indent should use tabs where possible
#115: FILE: client/advertising.c:1023:
+        return NULL;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#115: FILE: client/advertising.c:1023:
+        return NULL;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#117: FILE: client/advertising.c:1025:
+    if (ad.service[AD_TYPE_AD].data.data[0] == 0x02 &&$

ERROR:CODE_INDENT: code indent should use tabs where possible
#118: FILE: client/advertising.c:1026:
+        ad.service[AD_TYPE_AD].data.data[1] == 0x00)$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#118: FILE: client/advertising.c:1026:
+        ad.service[AD_TYPE_AD].data.data[1] == 0x00)$

ERROR:CODE_INDENT: code indent should use tabs where possible
#119: FILE: client/advertising.c:1027:
+        return "sq";$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#119: FILE: client/advertising.c:1027:
+        return "sq";$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#121: FILE: client/advertising.c:1029:
+    if (ad.service[AD_TYPE_AD].data.data[0] == 0x04 &&$

ERROR:CODE_INDENT: code indent should use tabs where possible
#122: FILE: client/advertising.c:1030:
+        ad.service[AD_TYPE_AD].data.data[1] == 0x00)$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#122: FILE: client/advertising.c:1030:
+        ad.service[AD_TYPE_AD].data.data[1] == 0x00)$

ERROR:CODE_INDENT: code indent should use tabs where possible
#123: FILE: client/advertising.c:1031:
+        return "hq";$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#123: FILE: client/advertising.c:1031:
+        return "hq";$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#125: FILE: client/advertising.c:1033:
+    return NULL;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#130: FILE: client/advertising.c:1038:
+    struct ad_data data = {$

ERROR:CODE_INDENT: code indent should use tabs where possible
#131: FILE: client/advertising.c:1039:
+        .data = { 0x00, 0x00 },$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#131: FILE: client/advertising.c:1039:
+        .data = { 0x00, 0x00 },$

ERROR:CODE_INDENT: code indent should use tabs where possible
#132: FILE: client/advertising.c:1040:
+        .len = 2,$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#132: FILE: client/advertising.c:1040:
+        .len = 2,$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#133: FILE: client/advertising.c:1041:
+    };$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#134: FILE: client/advertising.c:1042:
+    const char *state;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#136: FILE: client/advertising.c:1044:
+    if (argc < 2) {$

ERROR:CODE_INDENT: code indent should use tabs where possible
#137: FILE: client/advertising.c:1045:
+        state = ad_public_broadcast_state();$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#137: FILE: client/advertising.c:1045:
+        state = ad_public_broadcast_state();$

WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (16, 12)
#138: FILE: client/advertising.c:1046:
+		if (state)
+            bt_shell_printf("Public Broadcast: %s\n", state);

ERROR:CODE_INDENT: code indent should use tabs where possible
#139: FILE: client/advertising.c:1047:
+            bt_shell_printf("Public Broadcast: %s\n", state);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#139: FILE: client/advertising.c:1047:
+            bt_shell_printf("Public Broadcast: %s\n", state);$

ERROR:CODE_INDENT: code indent should use tabs where possible
#140: FILE: client/advertising.c:1048:
+        else$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#140: FILE: client/advertising.c:1048:
+        else$

WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (8, 12)
#140: FILE: client/advertising.c:1048:
+        else
+            bt_shell_printf("Public Broadcast not set\n");

ERROR:CODE_INDENT: code indent should use tabs where possible
#141: FILE: client/advertising.c:1049:
+            bt_shell_printf("Public Broadcast not set\n");$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#141: FILE: client/advertising.c:1049:
+            bt_shell_printf("Public Broadcast not set\n");$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#144: FILE: client/advertising.c:1052:
+    }$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#146: FILE: client/advertising.c:1054:
+    if (!strlen(argv[1])) {$

ERROR:CODE_INDENT: code indent should use tabs where possible
#147: FILE: client/advertising.c:1055:
+        bt_shell_printf("Public broadcast value cannot be empty\n");$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#147: FILE: client/advertising.c:1055:
+        bt_shell_printf("Public broadcast value cannot be empty\n");$

ERROR:CODE_INDENT: code indent should use tabs where possible
#148: FILE: client/advertising.c:1056:
+        return bt_shell_noninteractive_quit(EXIT_FAILURE);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#148: FILE: client/advertising.c:1056:
+        return bt_shell_noninteractive_quit(EXIT_FAILURE);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#149: FILE: client/advertising.c:1057:
+    }$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#151: FILE: client/advertising.c:1059:
+    if (!strcasecmp(argv[1], "sq"))$

ERROR:CODE_INDENT: code indent should use tabs where possible
#152: FILE: client/advertising.c:1060:
+        data.data[0] = 0x02;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#152: FILE: client/advertising.c:1060:
+        data.data[0] = 0x02;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#153: FILE: client/advertising.c:1061:
+    else if (!strcasecmp(argv[1], "hq"))$

ERROR:CODE_INDENT: code indent should use tabs where possible
#154: FILE: client/advertising.c:1062:
+        data.data[0] = 0x04;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#154: FILE: client/advertising.c:1062:
+        data.data[0] = 0x04;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#155: FILE: client/advertising.c:1063:
+    else {$

ERROR:CODE_INDENT: code indent should use tabs where possible
#156: FILE: client/advertising.c:1064:
+        bt_shell_printf("Invalid argument: accepted values are sq or hq\n");$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#156: FILE: client/advertising.c:1064:
+        bt_shell_printf("Invalid argument: accepted values are sq or hq\n");$

ERROR:CODE_INDENT: code indent should use tabs where possible
#157: FILE: client/advertising.c:1065:
+        return bt_shell_noninteractive_quit(EXIT_FAILURE);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#157: FILE: client/advertising.c:1065:
+        return bt_shell_noninteractive_quit(EXIT_FAILURE);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#158: FILE: client/advertising.c:1066:
+    }$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#160: FILE: client/advertising.c:1068:
+    ad_clear_service(AD_TYPE_AD);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#162: FILE: client/advertising.c:1070:
+    ad.service[AD_TYPE_AD].uuid = g_strdup(AD_PUBLIC_BROADCAST_UUID);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#163: FILE: client/advertising.c:1071:
+    ad.service[AD_TYPE_AD].data = data;$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#165: FILE: client/advertising.c:1073:
+    g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,$

ERROR:CODE_INDENT: code indent should use tabs where possible
#166: FILE: client/advertising.c:1074:
+        prop_names.service[AD_TYPE_AD]);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#166: FILE: client/advertising.c:1074:
+        prop_names.service[AD_TYPE_AD]);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#168: FILE: client/advertising.c:1076:
+    return bt_shell_noninteractive_quit(EXIT_SUCCESS);$

ERROR:CODE_INDENT: code indent should use tabs where possible
#183: FILE: client/advertising.h:38:
+                                char *argv[]);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#183: FILE: client/advertising.h:38:
+                                char *argv[]);$

WARNING:STATIC_CONST_CHAR_ARRAY: static const char * array should probably be static const char * const
#195: FILE: client/main.c:2923:
+static const char *public_broadcast_arguments[] = {

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#196: FILE: client/main.c:2924:
+    "sq",$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#197: FILE: client/main.c:2925:
+    "hq",$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#198: FILE: client/main.c:2926:
+    NULL$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#203: FILE: client/main.c:2931:
+    return argument_generator(text, state, public_broadcast_arguments);$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#215: FILE: client/main.c:2986:
+    ad_advertise_public_broadcast(dbus_conn, argc, argv);$

ERROR:CODE_INDENT: code indent should use tabs where possible
#226: FILE: client/main.c:3673:
+            "Set/Get BLE Audio Public Broadcast Announcement",$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#226: FILE: client/main.c:3673:
+            "Set/Get BLE Audio Public Broadcast Announcement",$

ERROR:CODE_INDENT: code indent should use tabs where possible
#227: FILE: client/main.c:3674:
+            public_broadcast_generator },$

WARNING:LEADING_SPACE: please, no spaces at the start of a line
#227: FILE: client/main.c:3674:
+            public_broadcast_generator },$

/github/workspace/src/patch/14537907.patch total: 22 errors, 52 warnings, 134 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

NOTE: Whitespace errors detected.
      You may wish to use scripts/cleanpatch or scripts/cleanfile

/github/workspace/src/patch/14537907.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




https://github.com/bluez/bluez/pull/2069

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH BlueZ v1 0/2] Add RAS Packet format and Notification support
From: Prathibha Madugonde @ 2026-04-24 17:52 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Patch overview:
  1/2 src/shared: Add RAS packet format and sending notifications to client
  2/2 uint/test-rap : Add PTS tests for CS reflector

 src/shared/rap.c | 1142 +++++++++++++++++++++++++++++++++++++++++++++-
 unit/test-rap.c  |  264 ++++++++++-
 2 files changed, 1390 insertions(+), 16 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH BlueZ v1 1/2] src/shared: Add RAS packet format and notification support
From: Prathibha Madugonde @ 2026-04-24 17:52 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260424175254.2905834-1-prathm@qti.qualcomm.com>

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Add RAS packet formatting and notification support for CS reflector
Implement complete RAS data pipeline:
- Handle HCI CS subevent result/continuation events
- Serialize ranging/subevent headers per spec
- GATT notifications for real-time ranging data
---
 src/shared/rap.c | 1142 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1130 insertions(+), 12 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index ac6de04e0..54346e12f 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -33,6 +33,169 @@
 /* Total number of attribute handles reserved for the RAS service */
 #define RAS_TOTAL_NUM_HANDLES		18
 
+/* 2(rc+cfg) + 1(tx_pwr) + 1(4 bits antenna_mask, 2 bits reserved,
+ * 2 bits pct_format)
+ */
+#define RAS_RANGING_HEADER_SIZE 4
+#define TOTAL_RAS_RANGING_HEADER_SIZE 5
+#define ATT_OVERHEAD 3 /* 1(opcode) + 2(char handle) */
+#define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
+#define RAS_SUBEVENT_HEADER_SIZE 8
+
+enum pct_format {
+	IQ = 0,
+	PHASE = 1,
+};
+
+enum ranging_done_status {
+	RANGING_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	RANGING_DONE_PARTIAL_RESULTS = 0x1,
+	RANGING_DONE_ABORTED = 0xF,
+};
+
+enum subevent_done_status {
+	SUBEVENT_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	SUBEVENT_DONE_PARTIAL_RESULTS = 0x1,
+	SUBEVENT_DONE_ABORTED = 0xF,
+};
+
+enum ranging_abort_reason {
+	RANGING_ABORT_NO_ABORT = 0x0,
+	RANGING_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	RANGING_ABORT_INSUFFICIENT_FILTERED_CHANNELS = 0x2,
+	RANGING_ABORT_INSTANT_HAS_PASSED = 0x3,
+	RANGING_ABORT_UNSPECIFIED = 0xF,
+};
+
+enum subevent_abort_reason {
+	SUBEVENT_ABORT_NO_ABORT = 0x0,
+	SUBEVENT_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	SUBEVENT_ABORT_NO_CS_SYNC_RECEIVED = 0x2,
+	SUBEVENT_ABORT_SCHEDULING_CONFLICTS_OR_LIMITED_RESOURCES = 0x3,
+	SUBEVENT_ABORT_UNSPECIFIED = 0xF,
+};
+
+/* Segmentation header: 1 byte
+ * bit 0: first_segment
+ * bit 1: last_segment
+ * bits 2-7: rolling_segment_counter (6 bits)
+ */
+struct segmentation_header {
+	uint8_t first_segment;
+	uint8_t last_segment;
+	uint8_t rolling_segment_counter;
+};
+
+/* Macros to pack/unpack segmentation header */
+#define SEG_HDR_PACK(first, last, counter) \
+	((uint8_t)(((first) ? 0x01 : 0x00) | \
+		((last) ? 0x02 : 0x00) | \
+		(((counter) & 0x3F) << 2)))
+
+#define SEG_HDR_UNPACK_FIRST(byte)    ((byte) & 0x01)
+#define SEG_HDR_UNPACK_LAST(byte)     (((byte) >> 1) & 0x01)
+#define SEG_HDR_UNPACK_COUNTER(byte)  (((byte) >> 2) & 0x3F)
+
+/* Ranging header: 4 bytes
+ * 0-1: ranging_counter (12 bits) | configuration_id (4 bits)
+ *      [little-endian]
+ * byte 2: selected_tx_power (signed)
+ * byte 3: antenna_paths_mask (4 bits) | reserved (2 bits) |
+ *         pct_format (2 bits)
+ */
+struct ranging_header {
+	uint16_t ranging_counter;
+	uint8_t configuration_id;
+	int8_t selected_tx_power;
+	uint8_t antenna_paths_mask;
+	uint8_t pct_format;
+} __packed;
+
+/* Macros to pack/unpack ranging header */
+#define RANGING_HDR_PACK_BYTE0_1(rc, cfg) \
+	((uint16_t)(((rc) & 0x0FFF) | (((cfg) & 0x0F) << 12)))
+
+#define RANGING_HDR_PACK_BYTE3(ant_mask, pct_fmt) \
+	((uint8_t)(((ant_mask) & 0x0F) | (((pct_fmt) & 0x03) << 6)))
+
+#define RANGING_HDR_UNPACK_RC(byte0_1) \
+	((uint16_t)((byte0_1) & 0x0FFF))
+
+#define RANGING_HDR_UNPACK_CFG(byte0_1) \
+	((uint8_t)(((byte0_1) >> 12) & 0x0F))
+
+#define RANGING_HDR_UNPACK_ANT_MASK(byte3) \
+	((uint8_t)((byte3) & 0x0F))
+
+#define RANGING_HDR_UNPACK_PCT_FMT(byte3) \
+	((uint8_t)(((byte3) >> 6) & 0x03))
+
+struct ras_subevent_header {
+	uint16_t start_acl_conn_event;
+	uint16_t frequency_compensation;
+	uint8_t ranging_done_status;
+	uint8_t subevent_done_status;
+	uint8_t ranging_abort_reason;
+	uint8_t subevent_abort_reason;
+	int8_t reference_power_level;
+	uint8_t num_steps_reported;
+};
+
+struct ras_subevent {
+	struct ras_subevent_header subevent_header;
+	uint8_t subevent_data[];
+};
+
+/* Role maps to Core CS roles (initiator/reflector) */
+enum cs_role {
+	CS_ROLE_INITIATOR = 0x00,
+	CS_ROLE_REFLECTOR = 0x01,
+};
+
+#define CS_INVALID_CONFIG_ID   0xFF
+/* 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
+};
+
+/* Main cs_procedure_data  */
+struct cs_procedure_data {
+	/* Identity and counters */
+	uint16_t counter;
+	uint8_t  num_antenna_paths;
+	/* Flags and status */
+	enum cs_procedure_done_status local_status;
+	enum cs_procedure_done_status remote_status;
+	bool contains_complete_subevent_;
+	/* RAS aggregation */
+	struct segmentation_header segmentation_header_;
+	struct ranging_header      ranging_header_;
+	struct iovec       ras_raw_data_;        /* raw concatenated */
+	uint16_t           ras_raw_data_index_;
+	struct ras_subevent_header  ras_subevent_header_;
+	struct iovec       ras_subevent_data_;   /* buffer per subevent */
+	uint8_t            ras_subevent_counter_;
+	/* Reference power levels */
+	int8_t initiator_reference_power_level;
+	int8_t reflector_reference_power_level;
+	bool ranging_header_prepended_;
+	bool ras_subevent_header_emitted;
+};
+
+struct cstracker {
+	enum cs_role        role;                 /* INITIATOR/REFLECTOR */
+	uint8_t             config_id;            /* CS_INVALID_CONFIG_ID */
+	uint8_t             selected_tx_power;    /* PROC_ENABLE_COMPLETE */
+	struct cs_procedure_data *current_proc;
+	/* Cached header values for CONT events (per-connection state) */
+	uint16_t last_proc_counter;
+	uint16_t last_start_acl_conn_evt_counter;
+	uint16_t last_freq_comp;
+	int8_t last_ref_pwr_lvl;
+};
+
 /* Ranging Service context */
 struct ras {
 	struct bt_rap_db *rapdb;
@@ -43,9 +206,17 @@ struct ras {
 	struct gatt_db_attribute *realtime_chrc;
 	struct gatt_db_attribute *realtime_chrc_ccc;
 	struct gatt_db_attribute *ondemand_chrc;
+	struct gatt_db_attribute *ondemand_ccc;
 	struct gatt_db_attribute *cp_chrc;
+	struct gatt_db_attribute *cp_ccc;
 	struct gatt_db_attribute *ready_chrc;
+	struct gatt_db_attribute *ready_ccc;
 	struct gatt_db_attribute *overwritten_chrc;
+	struct gatt_db_attribute *overwritten_ccc;
+
+	/* CCC state tracking for mutual exclusivity */
+	uint16_t realtime_ccc_value;
+	uint16_t ondemand_ccc_value;
 };
 
 struct bt_rap_db {
@@ -70,6 +241,7 @@ struct bt_rap {
 	bt_rap_destroy_func_t debug_destroy;
 	void *debug_data;
 	void *user_data;
+	struct cstracker *resptracker;
 };
 
 static struct queue *rap_db;
@@ -90,6 +262,209 @@ struct bt_rap_ready {
 	void *data;
 };
 
+uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
+uint8_t ras_segment_header_size = 1;
+
+struct cs_procedure_data *cs_procedure_data_create(
+					uint16_t procedure_counter,
+					uint8_t num_antenna_paths,
+					uint8_t configuration_id,
+					uint8_t selected_tx_power)
+{
+	struct cs_procedure_data *d;
+	uint8_t i;
+
+	d = calloc(1, sizeof(struct cs_procedure_data));
+
+	if (!d)
+		return NULL;
+
+	d->counter = procedure_counter;
+	d->num_antenna_paths = num_antenna_paths;
+	d->local_status = CS_PROC_PARTIAL_RESULTS;
+	d->remote_status = CS_PROC_PARTIAL_RESULTS;
+	d->contains_complete_subevent_ = false;
+	d->segmentation_header_.first_segment = 1;
+	d->segmentation_header_.last_segment = 0;
+	d->segmentation_header_.rolling_segment_counter = 0;
+	d->ranging_header_.ranging_counter = procedure_counter;
+	d->ranging_header_.configuration_id = configuration_id;
+	d->ranging_header_.selected_tx_power = selected_tx_power;
+	d->ranging_header_.antenna_paths_mask = 0;
+
+	for (i = 0; i < num_antenna_paths; i++)
+		d->ranging_header_.antenna_paths_mask |= (1u << i);
+
+	d->ranging_header_.pct_format = IQ;
+	memset(&d->ras_raw_data_, 0, sizeof(d->ras_raw_data_));
+	d->ras_raw_data_index_ = 0;
+	memset(&d->ras_subevent_data_, 0, sizeof(d->ras_subevent_data_));
+	d->ras_subevent_counter_ = 0;
+	d->initiator_reference_power_level = 0;
+	d->reflector_reference_power_level = 0;
+	d->ranging_header_prepended_ = false;
+	d->ras_subevent_header_emitted = false;
+
+	return d;
+}
+
+void cs_procedure_data_destroy(struct cs_procedure_data *d)
+{
+	if (!d)
+		return;
+
+	free(d->ras_raw_data_.iov_base);
+	free(d->ras_subevent_data_.iov_base);
+	free(d);
+}
+
+void cs_pd_set_local_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->local_status = s;
+}
+
+void cs_pd_set_remote_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->remote_status = s;
+}
+
+void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
+					int8_t init_lvl, int8_t ref_lvl)
+{
+	if (!d)
+		return;
+
+	d->initiator_reference_power_level = init_lvl;
+	d->reflector_reference_power_level = ref_lvl;
+}
+
+void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
+				uint16_t start_acl_conn_event,
+				uint16_t frequency_compensation,
+				int8_t reference_power_level)
+{
+	if (!d)
+		return;
+
+	d->ras_subevent_counter_++;
+	d->ras_subevent_header_.start_acl_conn_event = start_acl_conn_event;
+	d->ras_subevent_header_.frequency_compensation =
+		frequency_compensation;
+	d->ras_subevent_header_.reference_power_level = reference_power_level;
+	d->ras_subevent_header_.num_steps_reported = 0;
+	d->ras_subevent_header_emitted = false;
+	d->ras_subevent_data_.iov_len = 0;
+}
+
+bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
+					const uint8_t *bytes, size_t len)
+{
+	if (!d || !bytes || len == 0)
+		return false;
+
+	return util_iov_append(&d->ras_subevent_data_, bytes, len) != NULL;
+}
+
+static inline size_t serialize_ras_subevent_header(
+				const struct ras_subevent_header *h,
+				uint8_t *out, size_t out_len)
+{
+	uint16_t start_le;
+	uint16_t freq_le;
+
+	if (!h || !out || out_len < RAS_SUBEVENT_HEADER_SIZE)
+		return 0;
+
+	start_le = (uint16_t)h->start_acl_conn_event;
+	out[0] = (uint8_t)(start_le & 0xFF);
+	out[1] = (uint8_t)((start_le >> 8) & 0xFF);
+
+	freq_le = (uint16_t)h->frequency_compensation;
+	out[2] = (uint8_t)(freq_le & 0xFF);
+	out[3] = (uint8_t)((freq_le >> 8) & 0xFF);
+
+	out[4] = (uint8_t)((h->ranging_done_status & 0x0F) |
+			((h->subevent_done_status & 0x0F) << 4));
+
+	out[5] = (uint8_t)((h->ranging_abort_reason & 0x0F) |
+			((h->subevent_abort_reason & 0x0F) << 4));
+
+	out[6] = (uint8_t)h->reference_power_level;
+	out[7] = (uint8_t)h->num_steps_reported;
+
+	return RAS_SUBEVENT_HEADER_SIZE;
+}
+
+bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
+				uint8_t num_steps_reported,
+				uint8_t ranging_done_status,
+				uint8_t subevent_done_status,
+				uint8_t ranging_abort_reason,
+				uint8_t subevent_abort_reason)
+{
+	size_t hdr_sz;
+	size_t payload_sz;
+	size_t total;
+	uint8_t *buf;
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	d->ras_subevent_header_.num_steps_reported =
+		(uint8_t)(d->ras_subevent_header_.num_steps_reported +
+			num_steps_reported);
+	d->ras_subevent_header_.ranging_done_status = ranging_done_status;
+	d->ras_subevent_header_.subevent_done_status = subevent_done_status;
+	d->ras_subevent_header_.ranging_abort_reason = ranging_abort_reason;
+	d->ras_subevent_header_.subevent_abort_reason = subevent_abort_reason;
+
+	if (subevent_done_status == SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
+		d->contains_complete_subevent_ = true;
+
+	if (subevent_done_status == SUBEVENT_DONE_PARTIAL_RESULTS)
+		return true;
+
+	if (!d->ras_subevent_header_emitted) {
+		hdr_sz = RAS_SUBEVENT_HEADER_SIZE;
+		payload_sz = d->ras_subevent_data_.iov_len;
+		total = hdr_sz + payload_sz;
+		buf = (uint8_t *)malloc(total);
+
+		if (!buf)
+			return false;
+
+		w = serialize_ras_subevent_header(&d->ras_subevent_header_,
+						buf, total);
+
+		if (w != hdr_sz) {
+			free(buf);
+			return false;
+		}
+
+		if (payload_sz > 0)
+			memcpy(buf + hdr_sz,
+				(const uint8_t *)d->ras_subevent_data_.iov_base,
+				payload_sz);
+
+		ok = util_iov_append(&d->ras_raw_data_, buf, total) != NULL;
+		free(buf);
+
+		if (!ok)
+			return false;
+
+		d->ras_subevent_data_.iov_len = 0;
+		d->ras_subevent_header_emitted = true;
+	}
+
+	return true;
+}
+
 static struct ras *rap_get_ras(struct bt_rap *rap)
 {
 	if (!rap)
@@ -155,6 +530,11 @@ static void rap_free(void *data)
 
 	rap_db_free(rap->rrapdb);
 
+	if (rap->resptracker) {
+		free(rap->resptracker);
+		rap->resptracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -240,6 +620,21 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 	return true;
 }
 
+static void cs_tracker_init(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	memset(t, 0, sizeof(*t));
+	t->role = CS_ROLE_REFLECTOR;
+	t->config_id = CS_INVALID_CONFIG_ID;
+	t->selected_tx_power = 0;
+	t->last_proc_counter = 0;
+	t->last_start_acl_conn_evt_counter = 0;
+	t->last_freq_comp = 0;
+	t->last_ref_pwr_lvl = 0;
+}
+
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
 				 unsigned int id, uint16_t offset,
 				 uint8_t opcode, struct bt_att *att,
@@ -304,6 +699,225 @@ static void ras_data_overwritten_read_cb(struct gatt_db_attribute *attrib,
 	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
 }
 
+/* CCC write callbacks for custom handling */
+static void ras_realtime_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable realtime
+	 * while ondemand is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->ondemand_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->realtime_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ondemand_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+				BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 && ccc_value != 0x0002 &&
+	    ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable ondemand
+	 * while realtime is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->realtime_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->ondemand_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_cp_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ready_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_overwritten_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
 /* Service registration – store attribute pointers */
 static struct ras *register_ras_service(struct gatt_db *db)
 {
@@ -349,9 +963,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  NULL, NULL, ras);
 
 	ras->realtime_chrc_ccc =
-		gatt_db_service_add_ccc(ras->svc,
-					BT_ATT_PERM_READ |
-					BT_ATT_PERM_WRITE);
+		gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_realtime_ccc_write_cb, ras);
 
 	/* On-demand Ranging Data */
 	bt_uuid16_create(&uuid, RAS_ONDEMAND_DATA_UUID);
@@ -364,8 +978,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_ondemand_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ondemand_ccc_write_cb, ras);
 
 	/* RAS Control Point */
 	bt_uuid16_create(&uuid, RAS_CONTROL_POINT_UUID);
@@ -379,8 +994,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_control_point_write_cb,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_cp_ccc_write_cb, ras);
 
 	/* RAS Data Ready */
 	bt_uuid16_create(&uuid, RAS_DATA_READY_UUID);
@@ -394,8 +1010,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_ready_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ready_ccc_write_cb, ras);
 
 	/* RAS Data Overwritten */
 	bt_uuid16_create(&uuid, RAS_DATA_OVERWRITTEN_UUID);
@@ -409,8 +1026,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_overwritten_read_cb,
 						  NULL, ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_overwritten_ccc_write_cb, ras);
 
 	/* Activate the service */
 	gatt_db_service_set_active(ras->svc, true);
@@ -503,32 +1121,515 @@ bool bt_rap_unregister(unsigned int id)
 	return true;
 }
 
+static inline size_t serialize_segmentation_header(
+				const struct segmentation_header *s,
+				uint8_t *out, size_t out_len)
+{
+	if (!s || !out || out_len < 1)
+		return 0;
+
+	/* [0] bit0: first, bit1: last, bits2..7: rolling counter */
+	out[0] = (s->first_segment ? 0x01 : 0x00) |
+		(s->last_segment ? 0x02 : 0x00) |
+		((s->rolling_segment_counter & 0x3F) << 2);
+
+	return 1;
+}
+
+static inline size_t serialize_ranging_header(const struct ranging_header *r,
+						uint8_t *out, size_t out_len)
+{
+	uint16_t rcid;
+
+	if (!r || !out || out_len < RAS_RANGING_HEADER_SIZE)
+		return 0;
+
+	/* Little-endian pack: [rc (12 bits) | cfg_id (4 bits)] */
+	rcid = RANGING_HDR_PACK_BYTE0_1(r->ranging_counter,
+					r->configuration_id);
+
+	put_le16(rcid, out + 0);
+	out[2] = (uint8_t)r->selected_tx_power;
+	/* Byte 3: antenna_paths_mask | reserved | pct_format */
+	out[3] = RANGING_HDR_PACK_BYTE3(r->antenna_paths_mask,
+					r->pct_format);
+
+	return RAS_RANGING_HEADER_SIZE;
+}
+
+static inline uint16_t ras_att_value_payload_max(struct bt_rap *rap)
+{
+	struct bt_att *att = bt_rap_get_att(rap);
+	uint16_t mtu = att ? bt_att_get_mtu(att) : default_ras_mtu;
+
+	return (uint16_t)(mtu > ATT_OVERHEAD ?
+		(mtu - ATT_OVERHEAD - TOTAL_RAS_RANGING_HEADER_SIZE -
+		ras_segment_header_size) : 0);
+}
+
+/* Prepend data to an iovec */
+static bool iov_prepend_bytes(struct iovec *iov, const uint8_t *bytes,
+				size_t len)
+{
+	size_t new_len;
+	void *new_base;
+
+	if (!iov || !bytes || len == 0)
+		return false;
+
+	new_len = iov->iov_len + len;
+	new_base = malloc(new_len);
+
+	if (!new_base)
+		return false;
+
+	memcpy(new_base, bytes, len);
+
+	if (iov->iov_len > 0)
+		memcpy((uint8_t *)new_base + len, iov->iov_base,
+			iov->iov_len);
+
+	free(iov->iov_base);
+	iov->iov_base = new_base;
+	iov->iov_len = new_len;
+
+	return true;
+}
+
+/* Append the 4-byte RangingHeader to ras_raw_data_ on first segment */
+static bool ras_maybe_prepend_ranging_header(struct cs_procedure_data *d)
+{
+	uint8_t hdr[RAS_RANGING_HEADER_SIZE];
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	if (d->ranging_header_prepended_)
+		return false;
+
+	if (!d->segmentation_header_.first_segment)
+		return false;
+
+	if (d->ras_raw_data_index_ != 0)
+		return false;
+
+	w = serialize_ranging_header(&d->ranging_header_, hdr, sizeof(hdr));
+
+	if (w != RAS_RANGING_HEADER_SIZE)
+		return false;
+
+	ok = iov_prepend_bytes(&d->ras_raw_data_, hdr, w);
+
+	if (ok)
+		d->ranging_header_prepended_ = true;
+
+	return ok;
+}
+
+static void send_ras_segment_data(struct bt_rap *rap,
+				struct cs_procedure_data *proc)
+{
+	struct ras *ras;
+	uint16_t value_max;
+	const uint16_t header_len = ras_segment_header_size;
+	uint16_t raw_payload_size;
+	bool ok;
+
+	if (!rap || !proc)
+		return;
+
+	if (!rap->lrapdb || !rap->lrapdb->ras)
+		return;
+	ras = rap->lrapdb->ras;
+	value_max = ras_att_value_payload_max(rap);
+
+	if (value_max == 0) {
+		DBG(rap, "value_max=0 (MTU not available?)");
+		return;
+	}
+
+	if (value_max <= header_len) {
+		DBG(rap, "value_max(%u) too small for header", value_max);
+		return;
+	}
+
+	raw_payload_size = (uint16_t)(value_max - header_len);
+
+	/* Convert tail recursion to loop */
+	while (true) {
+		size_t total_len = proc->ras_raw_data_.iov_len;
+		size_t index = proc->ras_raw_data_index_;
+		size_t unsent_data_size;
+		uint16_t copy_size;
+		uint16_t seg_len;
+		uint8_t *seg;
+		uint16_t wr = 0;
+
+		if (index > total_len)
+			index = total_len;
+
+		unsent_data_size = total_len - index;
+
+		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)) {
+			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);
+		seg = (uint8_t *)malloc(seg_len);
+
+		if (!seg) {
+			DBG(rap, "OOM (%u)", seg_len);
+			return;
+		}
+
+		wr += (uint16_t)serialize_segmentation_header(
+				&proc->segmentation_header_, seg + wr,
+				seg_len - wr);
+		memcpy(seg + wr,
+			(const uint8_t *)proc->ras_raw_data_.iov_base + index,
+			copy_size);
+		wr += copy_size;
+
+		/* Try sending to real-time characteristic */
+		if (ras->realtime_chrc)
+			ok = gatt_db_attribute_notify(ras->realtime_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		/* Try sending to on-demand characteristic */
+		if (ras->ondemand_chrc)
+			ok = gatt_db_attribute_notify(ras->ondemand_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		free(seg);
+
+		if (!ok) {
+			DBG(rap, "Failed to send RAS notification");
+			return;
+		}
+
+		/* Advance read cursor and update segmentation state */
+		proc->ras_raw_data_index_ += copy_size;
+		proc->segmentation_header_.first_segment = 0;
+		proc->segmentation_header_.rolling_segment_counter =
+			(uint8_t)((proc->segmentation_header_
+				.rolling_segment_counter + 1) & 0x3F);
+
+		if (proc->segmentation_header_.last_segment ||
+				proc->ras_raw_data_index_ >=
+				proc->ras_raw_data_.iov_len) {
+			DBG(rap, "RAS clear ras buffers");
+			proc->ras_raw_data_.iov_len = 0;
+			proc->ras_raw_data_index_ = 0;
+			proc->ranging_header_prepended_ = false;
+			return;
+		}
+	}
+}
+
+static inline void resptracker_reset_current_proc(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	if (t->current_proc) {
+		cs_procedure_data_destroy(t->current_proc);
+		t->current_proc = NULL;
+	}
+}
+/* Unified local subevent handler */
+static void handle_local_subevent_result(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 void *step_bytes)
+{
+	struct cstracker *resptracker;
+	struct cs_procedure_data *proc;
+	const struct cs_step_data *steps;
+	uint8_t idx;
+
+	if (!rap || !rap->resptracker || !step_bytes)
+		return;
+
+	resptracker = rap->resptracker;
+
+	if (resptracker->current_proc) {
+		struct cs_procedure_data *cur = resptracker->current_proc;
+
+		if (has_header_fields && cur->counter != proc_counter) {
+			/* Safety: a new procedure; destroy the previous one */
+			resptracker_reset_current_proc(resptracker);
+		}
+	}
+
+	proc = resptracker->current_proc;
+	/* Cache header info from a RESULT event for later CONT usage */
+	if (has_header_fields) {
+		resptracker->last_proc_counter = proc_counter;
+		resptracker->last_start_acl_conn_evt_counter =
+			start_acl_conn_evt_counter;
+		resptracker->last_freq_comp = freq_comp;
+		resptracker->last_ref_pwr_lvl = ref_pwr_lvl;
+	}
+
+	/* Create the procedure on first use */
+	if (!proc) {
+		uint16_t create_counter = has_header_fields ? proc_counter :
+					resptracker->last_proc_counter;
+
+		proc = cs_procedure_data_create(create_counter,
+						num_ant_paths,
+						config_id,
+						resptracker->selected_tx_power);
+		if (!proc)
+			return;
+
+		resptracker->current_proc = proc;
+
+		/* Reference power levels and status defaults */
+		cs_pd_set_reference_power_levels(proc,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl);
+		cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+		cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+	}
+
+	/* Begin a new RAS subevent only when we have header fields */
+	if (has_header_fields) {
+		cs_pd_ras_begin_subevent(proc,
+					start_acl_conn_evt_counter,
+					freq_comp,
+					ref_pwr_lvl);
+	}
+
+	/* step_bytes points to an array of struct cs_step_data */
+	steps = (const struct cs_step_data *)step_bytes;
+
+	/* Process each step */
+	for (idx = 0; idx < num_steps_reported; idx++) {
+		const struct cs_step_data *step = &steps[idx];
+		const uint8_t mode = step->step_mode;
+		const uint8_t channel = step->step_chnl;
+		const uint8_t payload_len = step->step_data_length;
+		uint8_t mode_byte;
+		const uint8_t *payload;
+		uint8_t plen;
+		bool step_aborted;
+
+		/* Check if step is aborted: bit 7 of step_mode or
+		 * 0 payload len
+		 */
+		step_aborted = (mode & RAS_STEP_ABORTED_BIT) ||
+			(payload_len == 0);
+
+		DBG(rap, "step[%u]: mode=0x%02x channel=%u payload_len=%u "
+			"aborted=%s", idx, mode, channel, payload_len,
+			step_aborted ? "YES" : "NO");
+
+		/* Slim-step serialization for RAS:
+		 * - 1 byte: mode (bit7 set if aborted)
+		 * - payload: exactly step_data_length bytes (raw mode data)
+		 */
+		mode_byte = step->step_mode;
+		payload = (const uint8_t *)&step->step_mode_data;
+		plen = step->step_data_length;
+
+		if (step_aborted) {
+			/* Ensure abort bit is set */
+			mode_byte |= RAS_STEP_ABORTED_BIT;
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* No payload when aborted - per RAS spec Table 3.8 */
+			DBG(rap, "step[%u]: mode=0x%02x aborted, "
+				"no payload sent", idx, mode_byte);
+		} else {
+			/* Mode byte first (without abort bit) */
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* Then the raw payload bytes */
+			cs_pd_ras_append_subevent_bytes(proc, payload, plen);
+			DBG(rap, "step[%u]: mode=0x%02x payload_len=%u sent",
+				idx, mode_byte, (unsigned int)plen);
+		}
+	}
+
+	/* Update status for this chunk */
+	cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+	cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+
+	/* Commit subevent chunk (RESULT or CONT) */
+	cs_pd_ras_commit_subevent(proc,
+		num_steps_reported,
+		proc_done_status,
+		subevt_done_status,
+		abort_reason & 0x0F,
+		(abort_reason >> 4) & 0x0F);
+
+	/* Ensure first segment body starts with the 4-byte RangingHeader */
+	ras_maybe_prepend_ranging_header(proc);
+
+	if (subevt_done_status != SUBEVENT_DONE_PARTIAL_RESULTS)
+		/* Send RAS raw segment data */
+		send_ras_segment_data(rap, proc);
+
+	/* Procedure complete? Clean up */
+	if (proc_done_status == CS_PROC_ALL_RESULTS_COMPLETE) {
+		DBG(rap, "Destroying CsProcedureData counter=%u and "
+			"clearing current_proc", proc->counter);
+		resptracker_reset_current_proc(resptracker);
+		/* Reset cached header values for next procedure */
+		resptracker->last_proc_counter = 0;
+		resptracker->last_start_acl_conn_evt_counter = 0;
+		resptracker->last_freq_comp = 0;
+		resptracker->last_ref_pwr_lvl = 0;
+	}
+}
+
+static void form_ras_data_with_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->resptracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+	handle_local_subevent_result(rap,
+		true,			/* has header fields */
+		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);	/* start of steps */
+}
+static void form_ras_data_with_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 *resptracker;
+
+	if (!rap || !rap->resptracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+
+	resptracker = rap->resptracker;
+
+	/* Use cached header values captured from the last RESULT event */
+	handle_local_subevent_result(rap,
+		false,			/* CONT has no header fields */
+		cont->config_id,
+		cont->num_ant_paths,
+		resptracker->last_proc_counter,
+		resptracker->last_start_acl_conn_evt_counter,
+		resptracker->last_freq_comp,
+		resptracker->last_ref_pwr_lvl,
+		cont->proc_done_status,
+		cont->subevt_done_status,
+		cont->abort_reason,
+		cont->num_steps_reported,
+		cont->step_data);
+}
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_subevent_result_cont *cont = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
+
+	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_subevent_result *data = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent: len=%d", length);
+
+	/* Populate CsProcedureData and send RAS payload */
+	form_ras_data_with_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_proc_enable_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
 
 	DBG(rap, "Received CS procedure enable complete subevent: len=%d",
 	    length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Populate responder tracker */
+	resptracker->config_id = data->config_id;
+	resptracker->selected_tx_power = data->sel_tx_pwr;
 }
 
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
@@ -544,9 +1645,26 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
+
+	if (!rap)
+		return;
 
 	DBG(rap, "Received CS config complete subevent: len=%d", length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Basic fields */
+	resptracker->config_id = data->config_id;
+	resptracker->role = data->role;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -786,7 +1904,7 @@ bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client)
 
 	bt_uuid16_create(&uuid, RAS_UUID16);
 
-	gatt_db_foreach_service(rap->lrapdb->db, &uuid,
+	gatt_db_foreach_service(rap->rrapdb->db, &uuid,
 				foreach_rap_service, rap);
 
 	return true;
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v1 2/2] unit/test-rap: Add PTS tests for CS reflector
From: Prathibha Madugonde @ 2026-04-24 17:52 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260424175254.2905834-1-prathm@qti.qualcomm.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 11085 bytes --]

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Add below RAS - Real time Ranging PTS testcases

RAS/SR/RCO/BV-01-C [Characteristic Read – RAS Features]
RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
---
 unit/test-rap.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 260 insertions(+), 4 deletions(-)

diff --git a/unit/test-rap.c b/unit/test-rap.c
index 884cb1c96..6f65967a4 100644
--- a/unit/test-rap.c
+++ b/unit/test-rap.c
@@ -37,6 +37,7 @@ struct test_data_ras {
 	size_t iovcnt;
 	struct iovec *iov;
 	unsigned int ras_id;
+	struct bt_rap *rap;  /* Store rap instance for CS injection */
 };
 
 struct test_data_rap {
@@ -68,8 +69,8 @@ struct notify {
 	do {							\
 		const struct iovec iov[] = { args };		\
 		static struct test_data_ras data;			\
-		data.iovcnt = ARRAY_SIZE(iov_data(args));	\
-		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
+		data.iovcnt = ARRAY_SIZE(iov);	\
+		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov)); \
 		tester_add(name, &data, NULL, function,	\
 				test_teardown_ras);			\
 	} while (0)
@@ -155,6 +156,94 @@ static void gatt_notify_cb(struct gatt_db_attribute *attrib,
 		printf("%s: Failed to send notification\n", __func__);
 }
 
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct test_data_ras *data = user_data;
+	struct ccc_state *ccc;
+	uint16_t handle;
+	uint16_t ccc_value;
+	uint8_t ecode = 0;
+
+	handle = gatt_db_attribute_get_handle(attrib);
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	ccc_value = get_le16(value);
+
+	ccc = get_ccc_state(data, handle);
+	if (!ccc) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	ccc->value = ccc_value;
+
+	/* Send write response first */
+	gatt_db_attribute_write_result(attrib, id, 0);
+
+	/* If notifications/indications enabled on Real-time Ranging Data CCCD,
+	 * inject fake HCI CS subevent data to trigger notifications
+	 */
+	if (handle == 0x0006 && ccc_value != 0x0000 && data->rap) {
+		size_t event_size = sizeof(struct rap_ev_cs_subevent_result) +
+				    sizeof(struct cs_step_data);
+		struct rap_ev_cs_subevent_result *fake_event;
+		struct cs_mode_zero_data *mode_zero;
+
+		if (tester_use_debug())
+			tester_debug("Injecting fake CS data...");
+
+		fake_event = g_malloc0(event_size);
+		if (!fake_event)
+			return;  /* Already sent write response */
+
+		/* Fill in the header fields */
+		fake_event->conn_hdl = 0x0001;
+		fake_event->config_id = 0x01;
+		fake_event->start_acl_conn_evt_counter = 0x0000;
+		fake_event->proc_counter = 0x0001;
+		fake_event->freq_comp = 0x0000;
+		fake_event->ref_pwr_lvl = 0x00;
+		fake_event->proc_done_status = 0x00;
+		fake_event->subevt_done_status = 0x00;
+		fake_event->abort_reason = 0x00;
+		fake_event->num_ant_paths = 0x01;
+		fake_event->num_steps_reported = 0x01;
+
+		fake_event->step_data[0].step_mode = CS_MODE_ZERO;
+		fake_event->step_data[0].step_chnl = 0x00;
+		/* Mode 0: 1+1+1+4 bytes */
+		fake_event->step_data[0].step_data_length = 4;
+mode_zero = &fake_event->step_data[0].step_mode_data.mode_zero_data;
+		mode_zero->packet_quality = 0x01;
+		mode_zero->packet_rssi_dbm = 0x02;
+		mode_zero->packet_ant = 0x03;
+		mode_zero->init_measured_freq_offset = 0x04;
+
+		/* Inject the fake event to trigger notification */
+		bt_rap_hci_cs_subevent_result_callback(event_size, fake_event,
+						       data->rap);
+
+		g_free(fake_event);
+	}
+	return;
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
 static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					uint8_t opcode, struct bt_att *att,
@@ -184,10 +273,21 @@ done:
 
 static void ras_attached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data) {
+		data->rap = rap;
+		bt_rap_ref(rap);  /* Keep a reference */
+	}
 }
 
 static void ras_detached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data && data->rap == rap)
+		data->rap = NULL;
+
 	bt_rap_unref(rap);
 }
 
@@ -205,17 +305,18 @@ static void test_server(const void *user_data)
 	att = bt_att_new(io_get_fd(io), false);
 	g_assert(att);
 
+	bt_att_set_security(att, BT_ATT_SECURITY_MEDIUM);
 	bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL);
 
 	data->db = gatt_db_new();
 	g_assert(data->db);
 
-	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
+	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
 					gatt_notify_cb, data);
 
 	bt_rap_add_db(data->db);
 
-	data->ras_id = bt_rap_register(ras_attached, ras_detached, NULL);
+	data->ras_id = bt_rap_register(ras_attached, ras_detached, data);
 
 	data->server = bt_gatt_server_new(data->db, att, 64, 0);
 	g_assert(data->server);
@@ -426,6 +527,150 @@ static void test_server(const void *user_data)
 	DISC_RAS_CHAR_AFTER_TYPE, \
 	RAS_FIND_INFO
 
+/*
+ * RAS/SR/RCO/BV-01-C – Characteristic Read: RAS Features
+ *
+ *  ATT: Read Request (0x0a) len 2
+ *       Handle: 0x0003 (RAS Features value handle)
+ *
+ *  ATT: Read Response (0x0b) len 5
+ *       Value: 0x01 0x00 0x00 0x00
+ *       Feature bits:
+ *         Bit 0: Real-time ranging (1 = supported)
+ *         Bit 1: Retrieve stored results (0 = not supported)
+ *         Bit 2: Abort operation (0 = not supported)
+ *
+ *  Note: The RAS Features characteristic is registered with
+ *  BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT. Since the test sets
+ *  BT_ATT_SECURITY_MEDIUM, the encryption requirement is satisfied
+ *  and the server returns the feature value showing real-time ranging
+ *  support.
+ */
+
+#define ATT_READ_RAS_FEATURES \
+	IOV_DATA(0x0a, 0x03, 0x00), \
+	IOV_DATA(0x0b, 0x01, 0x00, 0x00, 0x00)
+
+#define RAS_SR_RCO_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	ATT_READ_RAS_FEATURES
+
+/*
+ * RAS Real-time Ranging Data CCCD Configuration
+ * Round 1: Enable/Disable notifications (CCCD = 0x0001)
+ * Round 2: Enable/Disable indications (CCCD = 0x0002)
+ */
+#define RAS_REALTIME_CCCD_CONFIG \
+	/* Round 1: Enable notifications on Real-time Ranging Data CCCD */ \
+	/* (handle 0x0006) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Round 2: Enable indications on Real-time Ranging Data CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x02, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable indications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Enable both notifications and indications (CCCD = 0x0003)
+ * Expect notification (0x1b) to be sent, not indication (0x1d)
+ * Then disable CCCD
+ *
+ * Note: This test is currently disabled because the GATT server rejects
+ * CCCD value 0x0003 (both notifications and indications enabled) before
+ * reaching the custom callback. The test infrastructure needs to be updated
+ * to support this scenario.
+ */
+#define RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE \
+	/* Enable notifications only (CCCD = 0x0001) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Disconnection/Reconnection simulation for Real-time Ranging Data
+ * Enable notifications, disable (disconnect), re-enable (reconnect), disable
+ */
+#define RAS_REALTIME_CCCD_DISCONNECT_RECONNECT \
+	/* Step 1: Enable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 3: Disable CCCD (simulates disconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 4: Re-enable notifications (simulates reconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD to clean up */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
+ * Verify that the IUT can configure CCCD for notifications/indications
+ * of the Real-time Ranging Data characteristic.
+ */
+#define RAS_SR_RRD_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_CONFIG
+
+/*
+ * RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
+ * Verify that the IUT only sends Real-time Ranging Data notifications when
+ * configured for both notifications and indications (CCCD = 0x0003).
+ *
+ * Test Procedure:
+ * 1. Write 0x0003 to Real-time Ranging Data CCCD (enable both
+ *    notifications and indications)
+ * 2. Trigger CS Subevent Data (via fake HCI event injection)
+ * 3. Verify IUT sends only notifications (0x1b), not indications (0x1d)
+ */
+#define RAS_SR_RRD_BV_03_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE
+
+/*
+ * RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
+ * Verify that the IUT does not resume sending Real-time Ranging Data
+ * notifications or indications after a disconnection occurs.
+ *
+ * Test Procedure:
+ * 1. Enable Real-time Ranging Data notifications
+ * 2. Trigger CS Subevent Data (IUT sends notifications)
+ * 3. Disable CCCD (simulates disconnection - CCCD resets to 0x0000)
+ * 4. Re-enable notifications (simulates reconnection and reconfiguration)
+ * 5. Verify IUT does not send old ranging data
+ *
+ * Note: In a unit test, we simulate disconnection by disabling and re-enabling
+ * the CCCD. The RAP implementation should clear any pending data when CCCD is
+ * disabled, ensuring no old data is sent after re-enabling.
+ */
+#define RAS_SR_RRD_BV_05_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_DISCONNECT_RECONNECT
+
 int main(int argc, char *argv[])
 {
 	tester_init(&argc, &argv);
@@ -441,6 +686,17 @@ int main(int argc, char *argv[])
 					RAS_SR_SGGIT_CHA_BV_03_C);
 	define_test_ras("RAS/SR/SGGIT/CHA/BV-04-C", test_server,
 					RAS_SR_SGGIT_CHA_BV_04_C);
+	/* RAS Read Characteristic Operations */
+	define_test_ras("RAS/SR/RCO/BV-01-C", test_server,
+					RAS_SR_RCO_BV_01_C);
+	/* RAS Real-time Ranging Data */
+	define_test_ras("RAS/SR/RRD/BV-01-C", test_server,
+					RAS_SR_RRD_BV_01_C);
+	/* RAS Real-time Ranging Data with CS injection */
+	define_test_ras("RAS/SR/RRD/BV-03-C", test_server,
+					RAS_SR_RRD_BV_03_C);
+	define_test_ras("RAS/SR/RRD/BV-05-C", test_server,
+					RAS_SR_RRD_BV_05_C);
 
 	return tester_run();
 }
-- 
2.34.1


^ permalink raw reply related

* [bluez/bluez] 1ed377: src/shared: Add RAS packet format and notification...
From: prathibhamadugonde @ 2026-04-24 18:07 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1085307
  Home:   https://github.com/bluez/bluez
  Commit: 1ed377735b9c70af6933671857d2ec6bfc6723de
      https://github.com/bluez/bluez/commit/1ed377735b9c70af6933671857d2ec6bfc6723de
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M src/shared/rap.c

  Log Message:
  -----------
  src/shared: Add RAS packet format and notification support

Add RAS packet formatting and notification support for CS reflector
Implement complete RAS data pipeline:
- Handle HCI CS subevent result/continuation events
- Serialize ranging/subevent headers per spec
- GATT notifications for real-time ranging data



To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* RE: Add RAS Packet format and Notification support
From: bluez.test.bot @ 2026-04-24 18:15 UTC (permalink / raw)
  To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260424175254.2905834-2-prathm@qti.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 22183 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=1085307

---Test result---

Test Summary:
CheckPatch                    PASS      1.23 seconds
GitLint                       PASS      0.34 seconds
BuildEll                      PASS      20.03 seconds
BluezMake                     FAIL      19.05 seconds
MakeCheck                     FAIL      42.42 seconds
MakeDistcheck                 PASS      242.60 seconds
CheckValgrind                 FAIL      15.45 seconds
CheckSmatch                   FAIL      23.74 seconds
bluezmakeextell               FAIL      13.11 seconds
IncrementalBuild              FAIL      19.43 seconds
ScanBuild                     FAIL      35.14 seconds

Details
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7579: src/shared/libshared_glib_la-rap.lo] Error 1
make: *** [Makefile:10820: check] Error 2
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:10820: check] Error 2
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:

src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:764:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:842:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1335:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1366:23: warning: Variable length array is used.
src/shared/gatt-server.c:279:25: warning: Variable length array is used.
src/shared/gatt-server.c:622:25: warning: Variable length array is used.
src/shared/gatt-server.c:720:25: warning: Variable length array is used.
src/shared/bap.c:312:25: warning: array of flexible structures
src/shared/bap.c: note: in included file:
./src/shared/ascs.h:88:25: warning: array of flexible structures
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
[BlueZ,v1,1/2] src/shared: Add RAS packet format and notification support

src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:

src/shared/gatt-client.c:447:21: warning: Use of memory after it is freed
        gatt_db_unregister(op->client->db, op->db_id);
                           ^~~~~~~~~~
src/shared/gatt-client.c:692:2: warning: Use of memory after it is freed
        discovery_op_complete(op, false, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:992:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1098:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1292:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1357:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1632:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1637:2: warning: Use of memory after it is freed
        discover_all(op);
        ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1693:56: warning: Use of memory after it is freed
        notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
                                          ~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2146:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2154:8: warning: Use of memory after it is freed
                                                        discovery_op_ref(op),
                                                        ^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3332:2: warning: Use of memory after it is freed
        complete_write_long_op(req, success, 0, false);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3354:2: warning: Use of memory after it is freed
        request_unref(req);
        ^~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/rap.c:268:27: error: no previous declaration for ‘cs_procedure_data_create’ [-Werror=missing-declarations]
  268 | struct cs_procedure_data *cs_procedure_data_create(
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:311:6: error: no previous declaration for ‘cs_procedure_data_destroy’ [-Werror=missing-declarations]
  311 | void cs_procedure_data_destroy(struct cs_procedure_data *d)
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:321:6: error: no previous declaration for ‘cs_pd_set_local_status’ [-Werror=missing-declarations]
  321 | void cs_pd_set_local_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:328:6: error: no previous declaration for ‘cs_pd_set_remote_status’ [-Werror=missing-declarations]
  328 | void cs_pd_set_remote_status(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:335:6: error: no previous declaration for ‘cs_pd_set_reference_power_levels’ [-Werror=missing-declarations]
  335 | void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:345:6: error: no previous declaration for ‘cs_pd_ras_begin_subevent’ [-Werror=missing-declarations]
  345 | void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:363:6: error: no previous declaration for ‘cs_pd_ras_append_subevent_bytes’ [-Werror=missing-declarations]
  363 | bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/rap.c:402:6: error: no previous declaration for ‘cs_pd_ras_commit_subevent’ [-Werror=missing-declarations]
  402 | bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
make: *** [Makefile:4172: all] Error 2


https://github.com/bluez/bluez/pull/2070

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH v2] Bluetooth: btusb: Add Realtek RTL8922AE VID/PID 0bda/d922
From: patchwork-bot+bluetooth @ 2026-04-24 18:50 UTC (permalink / raw)
  To: chen zhang
  Cc: luiz.von.dentz, chris.lu, pmenzel, floss, sean.wang, en-wei.wu,
	Zijun.Hu, chharry, ceggers, johan, linux-bluetooth, zhangchen01
In-Reply-To: <20260424120928.397758-1-zhangchen200426@163.com>

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri, 24 Apr 2026 20:09:28 +0800 you wrote:
> From: Chen Zhang <zhangchen01@kylinos.cn>
> 
> Add the vendor/product ID (0x0bda, 0xd922) to usb_device_id table for
> Realtek RTL8922AE.
> 
> The device info from /sys/kernel/debug/usb/devices as below.
> 
> [...]

Here is the summary with links:
  - [v2] Bluetooth: btusb: Add Realtek RTL8922AE VID/PID 0bda/d922
    https://git.kernel.org/bluetooth/bluetooth-next/c/0cbf9408fe30

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] Bluetooth: btusb: Add Realtek RTL8922AE VID/PID 0bda/d923
From: patchwork-bot+bluetooth @ 2026-04-24 18:50 UTC (permalink / raw)
  To: chen zhang
  Cc: luiz.von.dentz, chris.lu, pmenzel, floss, sean.wang, en-wei.wu,
	Zijun.Hu, chharry, ceggers, johan, linux-bluetooth, zhangchen01
In-Reply-To: <20260424122522.402953-1-zhangchen200426@163.com>

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri, 24 Apr 2026 20:25:22 +0800 you wrote:
> From: Chen Zhang <zhangchen01@kylinos.cn>
> 
> Add the vendor/product ID (0x0bda, 0xd923) to usb_device_id table for
> Realtek RTL8922AE.
> 
> The device info from /sys/kernel/debug/usb/devices as below.
> 
> [...]

Here is the summary with links:
  - Bluetooth: btusb: Add Realtek RTL8922AE VID/PID 0bda/d923
    https://git.kernel.org/bluetooth/bluetooth-next/c/516a3ab44299

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v2] Bluetooth: RFCOMM: pull credit byte with skb_pull_data()
From: patchwork-bot+bluetooth @ 2026-04-24 18:50 UTC (permalink / raw)
  To: Pengpeng Hou
  Cc: marcel, luiz.dentz, kees, kuba, mingo, hadess, tglx,
	linux-bluetooth, linux-kernel, stable
In-Reply-To: <20260424070102.1-rfcomm-v2-pengpeng@iscas.ac.cn>

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Thu, 23 Apr 2026 23:31:00 +0800 you wrote:
> rfcomm_recv_data() treats the first payload byte as a credit field when
> the UIH frame carries PF and credit-based flow control is enabled.
> 
> After the header has been stripped, the PF/CFC path consumes that byte
> with a direct skb->data dereference followed by skb_pull(). A malformed
> short frame can reach this path without a byte available.
> 
> [...]

Here is the summary with links:
  - [v2] Bluetooth: RFCOMM: pull credit byte with skb_pull_data()
    https://git.kernel.org/bluetooth/bluetooth-next/c/2940edce391d

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH] Bluetooth: btmtk: accept too short WMT FUNC_CTRL events
From: Pauli Virtanen @ 2026-04-24 19:24 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: Pauli Virtanen, marcel, luiz.dentz, tristan, linux-mediatek,
	sean.wang, mark-yw.chen

MT7925 (USB ID 0e8d:e025) on fw version 20260106153314 sends WMT
FUNC_CTRL events that are missing the status field.

Prior to commit 006b9943b982 ("Bluetooth: btmtk: validate WMT event SKB
length before struct access") the status was read from out-of-bounds of
SKB data, which usually would result to success with
BTMTK_WMT_ON_UNDONE, although I don't know the intent here.  The bounds
check added in that commit returns with error instead, producing
"Bluetooth: hci0: Failed to send wmt func ctrl (-22)" and makes the
device unusable.

Fix the regression by interpreting too short packet as status
BTMTK_WMT_ON_UNDONE, which makes the device work normally again.

Fixes: 006b9943b982 ("Bluetooth: btmtk: validate WMT event SKB length before struct access")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---

Notes:
    AFAICS the commit is not yet pulled and is only in bluetooth-next, so
    maybe this should be just fixup?

 drivers/bluetooth/btmtk.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index ab34f1dd42bc..68a32d11e5ec 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -719,8 +719,8 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
 	case BTMTK_WMT_FUNC_CTRL:
 		if (!skb_pull_data(data->evt_skb,
 				   sizeof(wmt_evt_funcc->status))) {
-			err = -EINVAL;
-			goto err_free_skb;
+			status = BTMTK_WMT_ON_UNDONE;
+			break;
 		}
 
 		wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
-- 
2.53.0


^ permalink raw reply related

* [PATCH] Bluetooth: btusb: deal with MT7925 invalid ISO RX packets
From: Pauli Virtanen @ 2026-04-24 19:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen, linux-mediatek

MT7925 produces empty ISO packets for Handle 0 when RX is active:

  > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #851 [hci0]
  > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #852 [hci0]

The driver should filter these out before passing to hci_core, otherwise
it will printk "Bluetooth: hci0: ISO packet for unknown connection
handle 0" errors.

Filter out by adding hdev->classify_pkt_type

Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
 drivers/bluetooth/btmtk.c | 13 +++++++++++++
 drivers/bluetooth/btmtk.h |  8 ++++++++
 drivers/bluetooth/btusb.c |  1 +
 3 files changed, 22 insertions(+)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index ab34f1dd42bc..9ba58cc9720a 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -1568,6 +1568,19 @@ int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
 	return hci_recv_frame(hdev, skb);
 }
 EXPORT_SYMBOL_GPL(btmtk_recv_event);
+
+u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	/* MT7925 spams invalid ISO packets during ISO RX */
+	if (hci_skb_pkt_type(skb) == HCI_ISODATA_PKT &&
+	    skb->len == sizeof(struct hci_iso_hdr) &&
+	    hci_iso_hdr(skb)->handle == 0 &&
+	    hci_iso_hdr(skb)->dlen == 0)
+		return HCI_DIAG_PKT;
+
+	return hci_skb_pkt_type(skb);
+}
+EXPORT_SYMBOL_GPL(btmtk_classify_pkt_type);
 #endif
 
 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..e573b21cecf7 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -222,6 +222,8 @@ int btmtk_usb_setup(struct hci_dev *hdev);
 int btmtk_usb_shutdown(struct hci_dev *hdev);
 
 int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
+
+u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb);
 #else
 
 static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
@@ -306,4 +308,10 @@ static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	return hci_recv_frame(hdev, skb);
 }
+
+static inline u8 btmtk_classify_pkt_type(struct hci_dev *hdev,
+					 struct sk_buff *skb)
+{
+	return hci_skb_pkt_type(skb);
+}
 #endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index a0a7da498466..7479928eec4b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -4245,6 +4245,7 @@ static int btusb_probe(struct usb_interface *intf,
 		hdev->reset = btmtk_reset_sync;
 		hdev->set_bdaddr = btmtk_set_bdaddr;
 		hdev->send = btusb_send_frame_mtk;
+		hdev->classify_pkt_type = btmtk_classify_pkt_type;
 		hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
 		hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
 		data->recv_acl = btmtk_usb_recv_acl;
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] Bluetooth: btmtk: accept too short WMT FUNC_CTRL events
From: Luiz Augusto von Dentz @ 2026-04-24 19:38 UTC (permalink / raw)
  To: Pauli Virtanen
  Cc: linux-bluetooth, marcel, tristan, linux-mediatek, sean.wang,
	mark-yw.chen
In-Reply-To: <770d36b07311bf88210c187923f243fb9f126f04.1777058551.git.pav@iki.fi>

Hi Pauli, Tristan,

On Fri, Apr 24, 2026 at 3:25 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> MT7925 (USB ID 0e8d:e025) on fw version 20260106153314 sends WMT
> FUNC_CTRL events that are missing the status field.
>
> Prior to commit 006b9943b982 ("Bluetooth: btmtk: validate WMT event SKB
> length before struct access") the status was read from out-of-bounds of
> SKB data, which usually would result to success with
> BTMTK_WMT_ON_UNDONE, although I don't know the intent here.  The bounds
> check added in that commit returns with error instead, producing
> "Bluetooth: hci0: Failed to send wmt func ctrl (-22)" and makes the
> device unusable.
>
> Fix the regression by interpreting too short packet as status
> BTMTK_WMT_ON_UNDONE, which makes the device work normally again.
>
> Fixes: 006b9943b982 ("Bluetooth: btmtk: validate WMT event SKB length before struct access")
> Signed-off-by: Pauli Virtanen <pav@iki.fi>
> ---
>
> Notes:
>     AFAICS the commit is not yet pulled and is only in bluetooth-next, so
>     maybe this should be just fixup?

Yeah, I'll most likely fix it in place and add your Signed-off-by.

>  drivers/bluetooth/btmtk.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> index ab34f1dd42bc..68a32d11e5ec 100644
> --- a/drivers/bluetooth/btmtk.c
> +++ b/drivers/bluetooth/btmtk.c
> @@ -719,8 +719,8 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
>         case BTMTK_WMT_FUNC_CTRL:
>                 if (!skb_pull_data(data->evt_skb,
>                                    sizeof(wmt_evt_funcc->status))) {
> -                       err = -EINVAL;
> -                       goto err_free_skb;
> +                       status = BTMTK_WMT_ON_UNDONE;
> +                       break;

This probably means the original change was never tested on real
hardware. We likely need input from the MediaTek team on how to handle
these events, as I don't think a public spec exists.

>                 }
>
>                 wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
> --
> 2.53.0
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH] Bluetooth: btusb: deal with MT7925 invalid ISO RX packets
From: Luiz Augusto von Dentz @ 2026-04-24 19:47 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth, linux-mediatek
In-Reply-To: <d5d33658e20fc0ad4cfe80df185d31fecad66fcf.1777059145.git.pav@iki.fi>

Hi Pauli,

On Fri, Apr 24, 2026 at 3:38 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> MT7925 produces empty ISO packets for Handle 0 when RX is active:
>
>   > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #851 [hci0]
>   > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #852 [hci0]
>
> The driver should filter these out before passing to hci_core, otherwise
> it will printk "Bluetooth: hci0: ISO packet for unknown connection
> handle 0" errors.

Aren't these rate limited though?

>
> Filter out by adding hdev->classify_pkt_type
>
> Signed-off-by: Pauli Virtanen <pav@iki.fi>
> ---
>  drivers/bluetooth/btmtk.c | 13 +++++++++++++
>  drivers/bluetooth/btmtk.h |  8 ++++++++
>  drivers/bluetooth/btusb.c |  1 +
>  3 files changed, 22 insertions(+)
>
> diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> index ab34f1dd42bc..9ba58cc9720a 100644
> --- a/drivers/bluetooth/btmtk.c
> +++ b/drivers/bluetooth/btmtk.c
> @@ -1568,6 +1568,19 @@ int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
>         return hci_recv_frame(hdev, skb);
>  }
>  EXPORT_SYMBOL_GPL(btmtk_recv_event);
> +
> +u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> +       /* MT7925 spams invalid ISO packets during ISO RX */
> +       if (hci_skb_pkt_type(skb) == HCI_ISODATA_PKT &&
> +           skb->len == sizeof(struct hci_iso_hdr) &&
> +           hci_iso_hdr(skb)->handle == 0 &&
> +           hci_iso_hdr(skb)->dlen == 0)
> +               return HCI_DIAG_PKT;

Not really following you here, why would marking it as a diagnostic
packet make it any better? In fact I do think this needs to print
something to the output if the firmware generates spurious packets and
have it fixed in the firmware asap, silently discarding the packets
just makes the issue undetectable, which I don't think really helps
users.

> +
> +       return hci_skb_pkt_type(skb);
> +}
> +EXPORT_SYMBOL_GPL(btmtk_classify_pkt_type);
>  #endif
>
>  MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
> diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> index c83c24897c95..e573b21cecf7 100644
> --- a/drivers/bluetooth/btmtk.h
> +++ b/drivers/bluetooth/btmtk.h
> @@ -222,6 +222,8 @@ int btmtk_usb_setup(struct hci_dev *hdev);
>  int btmtk_usb_shutdown(struct hci_dev *hdev);
>
>  int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
> +
> +u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb);
>  #else
>
>  static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
> @@ -306,4 +308,10 @@ static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
>  {
>         return hci_recv_frame(hdev, skb);
>  }
> +
> +static inline u8 btmtk_classify_pkt_type(struct hci_dev *hdev,
> +                                        struct sk_buff *skb)
> +{
> +       return hci_skb_pkt_type(skb);
> +}
>  #endif
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index a0a7da498466..7479928eec4b 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -4245,6 +4245,7 @@ static int btusb_probe(struct usb_interface *intf,
>                 hdev->reset = btmtk_reset_sync;
>                 hdev->set_bdaddr = btmtk_set_bdaddr;
>                 hdev->send = btusb_send_frame_mtk;
> +               hdev->classify_pkt_type = btmtk_classify_pkt_type;
>                 hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
>                 hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
>                 data->recv_acl = btmtk_usb_recv_acl;
> --
> 2.53.0
>
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* RE: Bluetooth: btmtk: accept too short WMT FUNC_CTRL events
From: bluez.test.bot @ 2026-04-24 19:59 UTC (permalink / raw)
  To: linux-bluetooth, pav
In-Reply-To: <770d36b07311bf88210c187923f243fb9f126f04.1777058551.git.pav@iki.fi>

[-- Attachment #1: Type: text/plain, Size: 882 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=1085333

---Test result---

Test Summary:
CheckPatch                    PASS      0.60 seconds
GitLint                       PASS      0.24 seconds
SubjectPrefix                 PASS      0.09 seconds
BuildKernel                   PASS      25.71 seconds
CheckAllWarning               PASS      28.17 seconds
CheckSparse                   PASS      26.78 seconds
BuildKernel32                 PASS      25.12 seconds
TestRunnerSetup               PASS      560.60 seconds
IncrementalBuild              PASS      23.89 seconds



https://github.com/bluez/bluetooth-next/pull/121

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: Bluetooth: btusb: deal with MT7925 invalid ISO RX packets
From: bluez.test.bot @ 2026-04-24 20:38 UTC (permalink / raw)
  To: linux-bluetooth, pav
In-Reply-To: <d5d33658e20fc0ad4cfe80df185d31fecad66fcf.1777059145.git.pav@iki.fi>

[-- Attachment #1: Type: text/plain, Size: 882 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=1085337

---Test result---

Test Summary:
CheckPatch                    PASS      0.91 seconds
GitLint                       PASS      0.25 seconds
SubjectPrefix                 PASS      0.08 seconds
BuildKernel                   PASS      24.35 seconds
CheckAllWarning               PASS      26.56 seconds
CheckSparse                   PASS      25.30 seconds
BuildKernel32                 PASS      23.95 seconds
TestRunnerSetup               PASS      524.33 seconds
IncrementalBuild              PASS      22.40 seconds



https://github.com/bluez/bluetooth-next/pull/122

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH] Bluetooth: btusb: deal with MT7925 invalid ISO RX packets
From: Pauli Virtanen @ 2026-04-24 21:11 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, linux-mediatek
In-Reply-To: <CABBYNZ+DhKq9_1kW0Xt6NgDPjMnAVjNXs+DPk-zbVFkhfDR9Uw@mail.gmail.com>

Hi Luiz,

pe, 2026-04-24 kello 15:47 -0400, Luiz Augusto von Dentz kirjoitti:
> On Fri, Apr 24, 2026 at 3:38 PM Pauli Virtanen <pav@iki.fi> wrote:
> > 
> > MT7925 produces empty ISO packets for Handle 0 when RX is active:
> > 
> >   > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #851 [hci0]
> >   > ISO Data RX: Handle 0 SN 36734 flags 0x00 dlen 0 slen 58  #852 [hci0]
> > 
> > The driver should filter these out before passing to hci_core, otherwise
> > it will printk "Bluetooth: hci0: ISO packet for unknown connection
> > handle 0" errors.
> 
> Aren't these rate limited though?

They are rate limited, yes, but it's still 10 messages every 5 seconds
while RX is active.

These are generated at a fairly high rate, something like ~60 such
packets per each valid ISO packet, so I agree this looks like some
firmware or driver bug.

btmtk has some custom USB code for ISO RX, btmtk_recv_isopkt(), that's
maybe one suspicious place to check.

> > Filter out by adding hdev->classify_pkt_type
> > 
> > Signed-off-by: Pauli Virtanen <pav@iki.fi>
> > ---
> >  drivers/bluetooth/btmtk.c | 13 +++++++++++++
> >  drivers/bluetooth/btmtk.h |  8 ++++++++
> >  drivers/bluetooth/btusb.c |  1 +
> >  3 files changed, 22 insertions(+)
> > 
> > diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> > index ab34f1dd42bc..9ba58cc9720a 100644
> > --- a/drivers/bluetooth/btmtk.c
> > +++ b/drivers/bluetooth/btmtk.c
> > @@ -1568,6 +1568,19 @@ int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
> >         return hci_recv_frame(hdev, skb);
> >  }
> >  EXPORT_SYMBOL_GPL(btmtk_recv_event);
> > +
> > +u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
> > +{
> > +       /* MT7925 spams invalid ISO packets during ISO RX */
> > +       if (hci_skb_pkt_type(skb) == HCI_ISODATA_PKT &&
> > +           skb->len == sizeof(struct hci_iso_hdr) &&
> > +           hci_iso_hdr(skb)->handle == 0 &&
> > +           hci_iso_hdr(skb)->dlen == 0)
> > +               return HCI_DIAG_PKT;
> 
> Not really following you here, why would marking it as a diagnostic
> packet make it any better? In fact I do think this needs to print
> something to the output if the firmware generates spurious packets and
> have it fixed in the firmware asap, silently discarding the packets
> just makes the issue undetectable, which I don't think really helps
> users.

The motivation is to not spam user logs with error messages they can do
nothing about. I guess we could eg. emit an error only on the first
such packet.

Maybe it's better to first make sure the issue is not in the driver,
though.

> > +
> > +       return hci_skb_pkt_type(skb);
> > +}
> > +EXPORT_SYMBOL_GPL(btmtk_classify_pkt_type);
> >  #endif
> > 
> >  MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
> > diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> > index c83c24897c95..e573b21cecf7 100644
> > --- a/drivers/bluetooth/btmtk.h
> > +++ b/drivers/bluetooth/btmtk.h
> > @@ -222,6 +222,8 @@ int btmtk_usb_setup(struct hci_dev *hdev);
> >  int btmtk_usb_shutdown(struct hci_dev *hdev);
> > 
> >  int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
> > +
> > +u8 btmtk_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb);
> >  #else
> > 
> >  static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
> > @@ -306,4 +308,10 @@ static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
> >  {
> >         return hci_recv_frame(hdev, skb);
> >  }
> > +
> > +static inline u8 btmtk_classify_pkt_type(struct hci_dev *hdev,
> > +                                        struct sk_buff *skb)
> > +{
> > +       return hci_skb_pkt_type(skb);
> > +}
> >  #endif
> > diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> > index a0a7da498466..7479928eec4b 100644
> > --- a/drivers/bluetooth/btusb.c
> > +++ b/drivers/bluetooth/btusb.c
> > @@ -4245,6 +4245,7 @@ static int btusb_probe(struct usb_interface *intf,
> >                 hdev->reset = btmtk_reset_sync;
> >                 hdev->set_bdaddr = btmtk_set_bdaddr;
> >                 hdev->send = btusb_send_frame_mtk;
> > +               hdev->classify_pkt_type = btmtk_classify_pkt_type;
> >                 hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
> >                 hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
> >                 data->recv_acl = btmtk_usb_recv_acl;
> > --
> > 2.53.0
> > 
> > 
> 

-- 
Pauli Virtanen

^ permalink raw reply

* [Bug 221411] New: 9460/9560 Jefferson Peak (JfP) can't detect certain bluetooth devices
From: bugzilla-daemon @ 2026-04-24 23:10 UTC (permalink / raw)
  To: linux-bluetooth

https://bugzilla.kernel.org/show_bug.cgi?id=221411

            Bug ID: 221411
           Summary: 9460/9560 Jefferson Peak (JfP) can't detect certain
                    bluetooth devices
           Product: Drivers
           Version: 2.5
    Kernel Version: 7.0.0-1-cachyos
          Hardware: Intel
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P3
         Component: Bluetooth
          Assignee: linux-bluetooth@vger.kernel.org
          Reporter: kernel.dinner750@simplelogin.com
        Regression: No

The Star Labs StarLite MkV (https://linux-hardware.org/?probe=d332f0271b) which
uses the 9460/9560 Jefferson Peak (JfP) for Bluetooth, can't detect Keychron K1
QMK Wireless Mechanical Keyboard (ISO Layout) - Version 6. But, it pairs and
connects with my Sony WH-1000XM3 and Pixel 9a running GrapheneOS just fine.

My Desktop (https://linux-hardware.org/?probe=1c5f5e5534) can pair with the
keyboard and works without issue.

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* [PATCH v2 0/2] Add RAS Packet format and Notification support
From: Prathibha Madugonde @ 2026-04-25  7:06 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Changes in v2:
Fixed missing declaration in src/shared/rap.c

Patch overview:
  1/2 src/shared: Add RAS packet format and sending notifications to client
  2/2 uint/test-rap : Add PTS tests for CS reflector


 src/shared/rap.c | 1142 +++++++++++++++++++++++++++++++++++++++++++++-
 unit/test-rap.c  |  264 ++++++++++-
 2 files changed, 1390 insertions(+), 16 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH v2 1/2] src/shared: Add RAS packet format and notification support
From: Prathibha Madugonde @ 2026-04-25  7:06 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260425070620.3381591-1-prathm@qti.qualcomm.com>

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Add RAS packet formatting and notification support for CS reflector
Implement complete RAS data pipeline:
- Handle HCI CS subevent result/continuation events
- Serialize ranging/subevent headers per spec
- GATT notifications for real-time ranging data
---
 src/shared/rap.c | 1142 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1130 insertions(+), 12 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index ac6de04e0..9ed8206bc 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -33,6 +33,169 @@
 /* Total number of attribute handles reserved for the RAS service */
 #define RAS_TOTAL_NUM_HANDLES		18
 
+/* 2(rc+cfg) + 1(tx_pwr) + 1(4 bits antenna_mask, 2 bits reserved,
+ * 2 bits pct_format)
+ */
+#define RAS_RANGING_HEADER_SIZE 4
+#define TOTAL_RAS_RANGING_HEADER_SIZE 5
+#define ATT_OVERHEAD 3 /* 1(opcode) + 2(char handle) */
+#define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
+#define RAS_SUBEVENT_HEADER_SIZE 8
+
+enum pct_format {
+	IQ = 0,
+	PHASE = 1,
+};
+
+enum ranging_done_status {
+	RANGING_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	RANGING_DONE_PARTIAL_RESULTS = 0x1,
+	RANGING_DONE_ABORTED = 0xF,
+};
+
+enum subevent_done_status {
+	SUBEVENT_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	SUBEVENT_DONE_PARTIAL_RESULTS = 0x1,
+	SUBEVENT_DONE_ABORTED = 0xF,
+};
+
+enum ranging_abort_reason {
+	RANGING_ABORT_NO_ABORT = 0x0,
+	RANGING_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	RANGING_ABORT_INSUFFICIENT_FILTERED_CHANNELS = 0x2,
+	RANGING_ABORT_INSTANT_HAS_PASSED = 0x3,
+	RANGING_ABORT_UNSPECIFIED = 0xF,
+};
+
+enum subevent_abort_reason {
+	SUBEVENT_ABORT_NO_ABORT = 0x0,
+	SUBEVENT_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	SUBEVENT_ABORT_NO_CS_SYNC_RECEIVED = 0x2,
+	SUBEVENT_ABORT_SCHEDULING_CONFLICTS_OR_LIMITED_RESOURCES = 0x3,
+	SUBEVENT_ABORT_UNSPECIFIED = 0xF,
+};
+
+/* Segmentation header: 1 byte
+ * bit 0: first_segment
+ * bit 1: last_segment
+ * bits 2-7: rolling_segment_counter (6 bits)
+ */
+struct segmentation_header {
+	uint8_t first_segment;
+	uint8_t last_segment;
+	uint8_t rolling_segment_counter;
+};
+
+/* Macros to pack/unpack segmentation header */
+#define SEG_HDR_PACK(first, last, counter) \
+	((uint8_t)(((first) ? 0x01 : 0x00) | \
+		((last) ? 0x02 : 0x00) | \
+		(((counter) & 0x3F) << 2)))
+
+#define SEG_HDR_UNPACK_FIRST(byte)    ((byte) & 0x01)
+#define SEG_HDR_UNPACK_LAST(byte)     (((byte) >> 1) & 0x01)
+#define SEG_HDR_UNPACK_COUNTER(byte)  (((byte) >> 2) & 0x3F)
+
+/* Ranging header: 4 bytes
+ * 0-1: ranging_counter (12 bits) | configuration_id (4 bits)
+ *      [little-endian]
+ * byte 2: selected_tx_power (signed)
+ * byte 3: antenna_paths_mask (4 bits) | reserved (2 bits) |
+ *         pct_format (2 bits)
+ */
+struct ranging_header {
+	uint16_t ranging_counter;
+	uint8_t configuration_id;
+	int8_t selected_tx_power;
+	uint8_t antenna_paths_mask;
+	uint8_t pct_format;
+} __packed;
+
+/* Macros to pack/unpack ranging header */
+#define RANGING_HDR_PACK_BYTE0_1(rc, cfg) \
+	((uint16_t)(((rc) & 0x0FFF) | (((cfg) & 0x0F) << 12)))
+
+#define RANGING_HDR_PACK_BYTE3(ant_mask, pct_fmt) \
+	((uint8_t)(((ant_mask) & 0x0F) | (((pct_fmt) & 0x03) << 6)))
+
+#define RANGING_HDR_UNPACK_RC(byte0_1) \
+	((uint16_t)((byte0_1) & 0x0FFF))
+
+#define RANGING_HDR_UNPACK_CFG(byte0_1) \
+	((uint8_t)(((byte0_1) >> 12) & 0x0F))
+
+#define RANGING_HDR_UNPACK_ANT_MASK(byte3) \
+	((uint8_t)((byte3) & 0x0F))
+
+#define RANGING_HDR_UNPACK_PCT_FMT(byte3) \
+	((uint8_t)(((byte3) >> 6) & 0x03))
+
+struct ras_subevent_header {
+	uint16_t start_acl_conn_event;
+	uint16_t frequency_compensation;
+	uint8_t ranging_done_status;
+	uint8_t subevent_done_status;
+	uint8_t ranging_abort_reason;
+	uint8_t subevent_abort_reason;
+	int8_t reference_power_level;
+	uint8_t num_steps_reported;
+};
+
+struct ras_subevent {
+	struct ras_subevent_header subevent_header;
+	uint8_t subevent_data[];
+};
+
+/* Role maps to Core CS roles (initiator/reflector) */
+enum cs_role {
+	CS_ROLE_INITIATOR = 0x00,
+	CS_ROLE_REFLECTOR = 0x01,
+};
+
+#define CS_INVALID_CONFIG_ID   0xFF
+/* 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
+};
+
+/* Main cs_procedure_data  */
+struct cs_procedure_data {
+	/* Identity and counters */
+	uint16_t counter;
+	uint8_t  num_antenna_paths;
+	/* Flags and status */
+	enum cs_procedure_done_status local_status;
+	enum cs_procedure_done_status remote_status;
+	bool contains_complete_subevent_;
+	/* RAS aggregation */
+	struct segmentation_header segmentation_header_;
+	struct ranging_header      ranging_header_;
+	struct iovec       ras_raw_data_;        /* raw concatenated */
+	uint16_t           ras_raw_data_index_;
+	struct ras_subevent_header  ras_subevent_header_;
+	struct iovec       ras_subevent_data_;   /* buffer per subevent */
+	uint8_t            ras_subevent_counter_;
+	/* Reference power levels */
+	int8_t initiator_reference_power_level;
+	int8_t reflector_reference_power_level;
+	bool ranging_header_prepended_;
+	bool ras_subevent_header_emitted;
+};
+
+struct cstracker {
+	enum cs_role        role;                 /* INITIATOR/REFLECTOR */
+	uint8_t             config_id;            /* CS_INVALID_CONFIG_ID */
+	uint8_t             selected_tx_power;    /* PROC_ENABLE_COMPLETE */
+	struct cs_procedure_data *current_proc;
+	/* Cached header values for CONT events (per-connection state) */
+	uint16_t last_proc_counter;
+	uint16_t last_start_acl_conn_evt_counter;
+	uint16_t last_freq_comp;
+	int8_t last_ref_pwr_lvl;
+};
+
 /* Ranging Service context */
 struct ras {
 	struct bt_rap_db *rapdb;
@@ -43,9 +206,17 @@ struct ras {
 	struct gatt_db_attribute *realtime_chrc;
 	struct gatt_db_attribute *realtime_chrc_ccc;
 	struct gatt_db_attribute *ondemand_chrc;
+	struct gatt_db_attribute *ondemand_ccc;
 	struct gatt_db_attribute *cp_chrc;
+	struct gatt_db_attribute *cp_ccc;
 	struct gatt_db_attribute *ready_chrc;
+	struct gatt_db_attribute *ready_ccc;
 	struct gatt_db_attribute *overwritten_chrc;
+	struct gatt_db_attribute *overwritten_ccc;
+
+	/* CCC state tracking for mutual exclusivity */
+	uint16_t realtime_ccc_value;
+	uint16_t ondemand_ccc_value;
 };
 
 struct bt_rap_db {
@@ -70,6 +241,7 @@ struct bt_rap {
 	bt_rap_destroy_func_t debug_destroy;
 	void *debug_data;
 	void *user_data;
+	struct cstracker *resptracker;
 };
 
 static struct queue *rap_db;
@@ -90,6 +262,209 @@ struct bt_rap_ready {
 	void *data;
 };
 
+uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
+uint8_t ras_segment_header_size = 1;
+
+static struct cs_procedure_data *cs_procedure_data_create(
+					uint16_t procedure_counter,
+					uint8_t num_antenna_paths,
+					uint8_t configuration_id,
+					uint8_t selected_tx_power)
+{
+	struct cs_procedure_data *d;
+	uint8_t i;
+
+	d = calloc(1, sizeof(struct cs_procedure_data));
+
+	if (!d)
+		return NULL;
+
+	d->counter = procedure_counter;
+	d->num_antenna_paths = num_antenna_paths;
+	d->local_status = CS_PROC_PARTIAL_RESULTS;
+	d->remote_status = CS_PROC_PARTIAL_RESULTS;
+	d->contains_complete_subevent_ = false;
+	d->segmentation_header_.first_segment = 1;
+	d->segmentation_header_.last_segment = 0;
+	d->segmentation_header_.rolling_segment_counter = 0;
+	d->ranging_header_.ranging_counter = procedure_counter;
+	d->ranging_header_.configuration_id = configuration_id;
+	d->ranging_header_.selected_tx_power = selected_tx_power;
+	d->ranging_header_.antenna_paths_mask = 0;
+
+	for (i = 0; i < num_antenna_paths; i++)
+		d->ranging_header_.antenna_paths_mask |= (1u << i);
+
+	d->ranging_header_.pct_format = IQ;
+	memset(&d->ras_raw_data_, 0, sizeof(d->ras_raw_data_));
+	d->ras_raw_data_index_ = 0;
+	memset(&d->ras_subevent_data_, 0, sizeof(d->ras_subevent_data_));
+	d->ras_subevent_counter_ = 0;
+	d->initiator_reference_power_level = 0;
+	d->reflector_reference_power_level = 0;
+	d->ranging_header_prepended_ = false;
+	d->ras_subevent_header_emitted = false;
+
+	return d;
+}
+
+static void cs_procedure_data_destroy(struct cs_procedure_data *d)
+{
+	if (!d)
+		return;
+
+	free(d->ras_raw_data_.iov_base);
+	free(d->ras_subevent_data_.iov_base);
+	free(d);
+}
+
+static void cs_pd_set_local_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->local_status = s;
+}
+
+static void cs_pd_set_remote_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->remote_status = s;
+}
+
+static void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
+					int8_t init_lvl, int8_t ref_lvl)
+{
+	if (!d)
+		return;
+
+	d->initiator_reference_power_level = init_lvl;
+	d->reflector_reference_power_level = ref_lvl;
+}
+
+static void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
+				uint16_t start_acl_conn_event,
+				uint16_t frequency_compensation,
+				int8_t reference_power_level)
+{
+	if (!d)
+		return;
+
+	d->ras_subevent_counter_++;
+	d->ras_subevent_header_.start_acl_conn_event = start_acl_conn_event;
+	d->ras_subevent_header_.frequency_compensation =
+		frequency_compensation;
+	d->ras_subevent_header_.reference_power_level = reference_power_level;
+	d->ras_subevent_header_.num_steps_reported = 0;
+	d->ras_subevent_header_emitted = false;
+	d->ras_subevent_data_.iov_len = 0;
+}
+
+static bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
+					const uint8_t *bytes, size_t len)
+{
+	if (!d || !bytes || len == 0)
+		return false;
+
+	return util_iov_append(&d->ras_subevent_data_, bytes, len) != NULL;
+}
+
+static inline size_t serialize_ras_subevent_header(
+				const struct ras_subevent_header *h,
+				uint8_t *out, size_t out_len)
+{
+	uint16_t start_le;
+	uint16_t freq_le;
+
+	if (!h || !out || out_len < RAS_SUBEVENT_HEADER_SIZE)
+		return 0;
+
+	start_le = (uint16_t)h->start_acl_conn_event;
+	out[0] = (uint8_t)(start_le & 0xFF);
+	out[1] = (uint8_t)((start_le >> 8) & 0xFF);
+
+	freq_le = (uint16_t)h->frequency_compensation;
+	out[2] = (uint8_t)(freq_le & 0xFF);
+	out[3] = (uint8_t)((freq_le >> 8) & 0xFF);
+
+	out[4] = (uint8_t)((h->ranging_done_status & 0x0F) |
+			((h->subevent_done_status & 0x0F) << 4));
+
+	out[5] = (uint8_t)((h->ranging_abort_reason & 0x0F) |
+			((h->subevent_abort_reason & 0x0F) << 4));
+
+	out[6] = (uint8_t)h->reference_power_level;
+	out[7] = (uint8_t)h->num_steps_reported;
+
+	return RAS_SUBEVENT_HEADER_SIZE;
+}
+
+static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
+				uint8_t num_steps_reported,
+				uint8_t ranging_done_status,
+				uint8_t subevent_done_status,
+				uint8_t ranging_abort_reason,
+				uint8_t subevent_abort_reason)
+{
+	size_t hdr_sz;
+	size_t payload_sz;
+	size_t total;
+	uint8_t *buf;
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	d->ras_subevent_header_.num_steps_reported =
+		(uint8_t)(d->ras_subevent_header_.num_steps_reported +
+			num_steps_reported);
+	d->ras_subevent_header_.ranging_done_status = ranging_done_status;
+	d->ras_subevent_header_.subevent_done_status = subevent_done_status;
+	d->ras_subevent_header_.ranging_abort_reason = ranging_abort_reason;
+	d->ras_subevent_header_.subevent_abort_reason = subevent_abort_reason;
+
+	if (subevent_done_status == SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
+		d->contains_complete_subevent_ = true;
+
+	if (subevent_done_status == SUBEVENT_DONE_PARTIAL_RESULTS)
+		return true;
+
+	if (!d->ras_subevent_header_emitted) {
+		hdr_sz = RAS_SUBEVENT_HEADER_SIZE;
+		payload_sz = d->ras_subevent_data_.iov_len;
+		total = hdr_sz + payload_sz;
+		buf = (uint8_t *)malloc(total);
+
+		if (!buf)
+			return false;
+
+		w = serialize_ras_subevent_header(&d->ras_subevent_header_,
+						buf, total);
+
+		if (w != hdr_sz) {
+			free(buf);
+			return false;
+		}
+
+		if (payload_sz > 0)
+			memcpy(buf + hdr_sz,
+				(const uint8_t *)d->ras_subevent_data_.iov_base,
+				payload_sz);
+
+		ok = util_iov_append(&d->ras_raw_data_, buf, total) != NULL;
+		free(buf);
+
+		if (!ok)
+			return false;
+
+		d->ras_subevent_data_.iov_len = 0;
+		d->ras_subevent_header_emitted = true;
+	}
+
+	return true;
+}
+
 static struct ras *rap_get_ras(struct bt_rap *rap)
 {
 	if (!rap)
@@ -155,6 +530,11 @@ static void rap_free(void *data)
 
 	rap_db_free(rap->rrapdb);
 
+	if (rap->resptracker) {
+		free(rap->resptracker);
+		rap->resptracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -240,6 +620,21 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 	return true;
 }
 
+static void cs_tracker_init(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	memset(t, 0, sizeof(*t));
+	t->role = CS_ROLE_REFLECTOR;
+	t->config_id = CS_INVALID_CONFIG_ID;
+	t->selected_tx_power = 0;
+	t->last_proc_counter = 0;
+	t->last_start_acl_conn_evt_counter = 0;
+	t->last_freq_comp = 0;
+	t->last_ref_pwr_lvl = 0;
+}
+
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
 				 unsigned int id, uint16_t offset,
 				 uint8_t opcode, struct bt_att *att,
@@ -304,6 +699,225 @@ static void ras_data_overwritten_read_cb(struct gatt_db_attribute *attrib,
 	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
 }
 
+/* CCC write callbacks for custom handling */
+static void ras_realtime_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable realtime
+	 * while ondemand is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->ondemand_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->realtime_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ondemand_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+				BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 && ccc_value != 0x0002 &&
+	    ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable ondemand
+	 * while realtime is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->realtime_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->ondemand_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_cp_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ready_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_overwritten_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
 /* Service registration – store attribute pointers */
 static struct ras *register_ras_service(struct gatt_db *db)
 {
@@ -349,9 +963,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  NULL, NULL, ras);
 
 	ras->realtime_chrc_ccc =
-		gatt_db_service_add_ccc(ras->svc,
-					BT_ATT_PERM_READ |
-					BT_ATT_PERM_WRITE);
+		gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_realtime_ccc_write_cb, ras);
 
 	/* On-demand Ranging Data */
 	bt_uuid16_create(&uuid, RAS_ONDEMAND_DATA_UUID);
@@ -364,8 +978,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_ondemand_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ondemand_ccc_write_cb, ras);
 
 	/* RAS Control Point */
 	bt_uuid16_create(&uuid, RAS_CONTROL_POINT_UUID);
@@ -379,8 +994,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_control_point_write_cb,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_cp_ccc_write_cb, ras);
 
 	/* RAS Data Ready */
 	bt_uuid16_create(&uuid, RAS_DATA_READY_UUID);
@@ -394,8 +1010,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_ready_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ready_ccc_write_cb, ras);
 
 	/* RAS Data Overwritten */
 	bt_uuid16_create(&uuid, RAS_DATA_OVERWRITTEN_UUID);
@@ -409,8 +1026,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_overwritten_read_cb,
 						  NULL, ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_overwritten_ccc_write_cb, ras);
 
 	/* Activate the service */
 	gatt_db_service_set_active(ras->svc, true);
@@ -503,32 +1121,515 @@ bool bt_rap_unregister(unsigned int id)
 	return true;
 }
 
+static inline size_t serialize_segmentation_header(
+				const struct segmentation_header *s,
+				uint8_t *out, size_t out_len)
+{
+	if (!s || !out || out_len < 1)
+		return 0;
+
+	/* [0] bit0: first, bit1: last, bits2..7: rolling counter */
+	out[0] = (s->first_segment ? 0x01 : 0x00) |
+		(s->last_segment ? 0x02 : 0x00) |
+		((s->rolling_segment_counter & 0x3F) << 2);
+
+	return 1;
+}
+
+static inline size_t serialize_ranging_header(const struct ranging_header *r,
+						uint8_t *out, size_t out_len)
+{
+	uint16_t rcid;
+
+	if (!r || !out || out_len < RAS_RANGING_HEADER_SIZE)
+		return 0;
+
+	/* Little-endian pack: [rc (12 bits) | cfg_id (4 bits)] */
+	rcid = RANGING_HDR_PACK_BYTE0_1(r->ranging_counter,
+					r->configuration_id);
+
+	put_le16(rcid, out + 0);
+	out[2] = (uint8_t)r->selected_tx_power;
+	/* Byte 3: antenna_paths_mask | reserved | pct_format */
+	out[3] = RANGING_HDR_PACK_BYTE3(r->antenna_paths_mask,
+					r->pct_format);
+
+	return RAS_RANGING_HEADER_SIZE;
+}
+
+static inline uint16_t ras_att_value_payload_max(struct bt_rap *rap)
+{
+	struct bt_att *att = bt_rap_get_att(rap);
+	uint16_t mtu = att ? bt_att_get_mtu(att) : default_ras_mtu;
+
+	return (uint16_t)(mtu > ATT_OVERHEAD ?
+		(mtu - ATT_OVERHEAD - TOTAL_RAS_RANGING_HEADER_SIZE -
+		ras_segment_header_size) : 0);
+}
+
+/* Prepend data to an iovec */
+static bool iov_prepend_bytes(struct iovec *iov, const uint8_t *bytes,
+				size_t len)
+{
+	size_t new_len;
+	void *new_base;
+
+	if (!iov || !bytes || len == 0)
+		return false;
+
+	new_len = iov->iov_len + len;
+	new_base = malloc(new_len);
+
+	if (!new_base)
+		return false;
+
+	memcpy(new_base, bytes, len);
+
+	if (iov->iov_len > 0)
+		memcpy((uint8_t *)new_base + len, iov->iov_base,
+			iov->iov_len);
+
+	free(iov->iov_base);
+	iov->iov_base = new_base;
+	iov->iov_len = new_len;
+
+	return true;
+}
+
+/* Append the 4-byte RangingHeader to ras_raw_data_ on first segment */
+static bool ras_maybe_prepend_ranging_header(struct cs_procedure_data *d)
+{
+	uint8_t hdr[RAS_RANGING_HEADER_SIZE];
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	if (d->ranging_header_prepended_)
+		return false;
+
+	if (!d->segmentation_header_.first_segment)
+		return false;
+
+	if (d->ras_raw_data_index_ != 0)
+		return false;
+
+	w = serialize_ranging_header(&d->ranging_header_, hdr, sizeof(hdr));
+
+	if (w != RAS_RANGING_HEADER_SIZE)
+		return false;
+
+	ok = iov_prepend_bytes(&d->ras_raw_data_, hdr, w);
+
+	if (ok)
+		d->ranging_header_prepended_ = true;
+
+	return ok;
+}
+
+static void send_ras_segment_data(struct bt_rap *rap,
+				struct cs_procedure_data *proc)
+{
+	struct ras *ras;
+	uint16_t value_max;
+	const uint16_t header_len = ras_segment_header_size;
+	uint16_t raw_payload_size;
+	bool ok;
+
+	if (!rap || !proc)
+		return;
+
+	if (!rap->lrapdb || !rap->lrapdb->ras)
+		return;
+	ras = rap->lrapdb->ras;
+	value_max = ras_att_value_payload_max(rap);
+
+	if (value_max == 0) {
+		DBG(rap, "value_max=0 (MTU not available?)");
+		return;
+	}
+
+	if (value_max <= header_len) {
+		DBG(rap, "value_max(%u) too small for header", value_max);
+		return;
+	}
+
+	raw_payload_size = (uint16_t)(value_max - header_len);
+
+	/* Convert tail recursion to loop */
+	while (true) {
+		size_t total_len = proc->ras_raw_data_.iov_len;
+		size_t index = proc->ras_raw_data_index_;
+		size_t unsent_data_size;
+		uint16_t copy_size;
+		uint16_t seg_len;
+		uint8_t *seg;
+		uint16_t wr = 0;
+
+		if (index > total_len)
+			index = total_len;
+
+		unsent_data_size = total_len - index;
+
+		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)) {
+			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);
+		seg = (uint8_t *)malloc(seg_len);
+
+		if (!seg) {
+			DBG(rap, "OOM (%u)", seg_len);
+			return;
+		}
+
+		wr += (uint16_t)serialize_segmentation_header(
+				&proc->segmentation_header_, seg + wr,
+				seg_len - wr);
+		memcpy(seg + wr,
+			(const uint8_t *)proc->ras_raw_data_.iov_base + index,
+			copy_size);
+		wr += copy_size;
+
+		/* Try sending to real-time characteristic */
+		if (ras->realtime_chrc)
+			ok = gatt_db_attribute_notify(ras->realtime_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		/* Try sending to on-demand characteristic */
+		if (ras->ondemand_chrc)
+			ok = gatt_db_attribute_notify(ras->ondemand_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		free(seg);
+
+		if (!ok) {
+			DBG(rap, "Failed to send RAS notification");
+			return;
+		}
+
+		/* Advance read cursor and update segmentation state */
+		proc->ras_raw_data_index_ += copy_size;
+		proc->segmentation_header_.first_segment = 0;
+		proc->segmentation_header_.rolling_segment_counter =
+			(uint8_t)((proc->segmentation_header_
+				.rolling_segment_counter + 1) & 0x3F);
+
+		if (proc->segmentation_header_.last_segment ||
+				proc->ras_raw_data_index_ >=
+				proc->ras_raw_data_.iov_len) {
+			DBG(rap, "RAS clear ras buffers");
+			proc->ras_raw_data_.iov_len = 0;
+			proc->ras_raw_data_index_ = 0;
+			proc->ranging_header_prepended_ = false;
+			return;
+		}
+	}
+}
+
+static inline void resptracker_reset_current_proc(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	if (t->current_proc) {
+		cs_procedure_data_destroy(t->current_proc);
+		t->current_proc = NULL;
+	}
+}
+/* Unified local subevent handler */
+static void handle_local_subevent_result(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 void *step_bytes)
+{
+	struct cstracker *resptracker;
+	struct cs_procedure_data *proc;
+	const struct cs_step_data *steps;
+	uint8_t idx;
+
+	if (!rap || !rap->resptracker || !step_bytes)
+		return;
+
+	resptracker = rap->resptracker;
+
+	if (resptracker->current_proc) {
+		struct cs_procedure_data *cur = resptracker->current_proc;
+
+		if (has_header_fields && cur->counter != proc_counter) {
+			/* Safety: a new procedure; destroy the previous one */
+			resptracker_reset_current_proc(resptracker);
+		}
+	}
+
+	proc = resptracker->current_proc;
+	/* Cache header info from a RESULT event for later CONT usage */
+	if (has_header_fields) {
+		resptracker->last_proc_counter = proc_counter;
+		resptracker->last_start_acl_conn_evt_counter =
+			start_acl_conn_evt_counter;
+		resptracker->last_freq_comp = freq_comp;
+		resptracker->last_ref_pwr_lvl = ref_pwr_lvl;
+	}
+
+	/* Create the procedure on first use */
+	if (!proc) {
+		uint16_t create_counter = has_header_fields ? proc_counter :
+					resptracker->last_proc_counter;
+
+		proc = cs_procedure_data_create(create_counter,
+						num_ant_paths,
+						config_id,
+						resptracker->selected_tx_power);
+		if (!proc)
+			return;
+
+		resptracker->current_proc = proc;
+
+		/* Reference power levels and status defaults */
+		cs_pd_set_reference_power_levels(proc,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl);
+		cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+		cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+	}
+
+	/* Begin a new RAS subevent only when we have header fields */
+	if (has_header_fields) {
+		cs_pd_ras_begin_subevent(proc,
+					start_acl_conn_evt_counter,
+					freq_comp,
+					ref_pwr_lvl);
+	}
+
+	/* step_bytes points to an array of struct cs_step_data */
+	steps = (const struct cs_step_data *)step_bytes;
+
+	/* Process each step */
+	for (idx = 0; idx < num_steps_reported; idx++) {
+		const struct cs_step_data *step = &steps[idx];
+		const uint8_t mode = step->step_mode;
+		const uint8_t channel = step->step_chnl;
+		const uint8_t payload_len = step->step_data_length;
+		uint8_t mode_byte;
+		const uint8_t *payload;
+		uint8_t plen;
+		bool step_aborted;
+
+		/* Check if step is aborted: bit 7 of step_mode or
+		 * 0 payload len
+		 */
+		step_aborted = (mode & RAS_STEP_ABORTED_BIT) ||
+			(payload_len == 0);
+
+		DBG(rap, "step[%u]: mode=0x%02x channel=%u payload_len=%u "
+			"aborted=%s", idx, mode, channel, payload_len,
+			step_aborted ? "YES" : "NO");
+
+		/* Slim-step serialization for RAS:
+		 * - 1 byte: mode (bit7 set if aborted)
+		 * - payload: exactly step_data_length bytes (raw mode data)
+		 */
+		mode_byte = step->step_mode;
+		payload = (const uint8_t *)&step->step_mode_data;
+		plen = step->step_data_length;
+
+		if (step_aborted) {
+			/* Ensure abort bit is set */
+			mode_byte |= RAS_STEP_ABORTED_BIT;
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* No payload when aborted - per RAS spec Table 3.8 */
+			DBG(rap, "step[%u]: mode=0x%02x aborted, "
+				"no payload sent", idx, mode_byte);
+		} else {
+			/* Mode byte first (without abort bit) */
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* Then the raw payload bytes */
+			cs_pd_ras_append_subevent_bytes(proc, payload, plen);
+			DBG(rap, "step[%u]: mode=0x%02x payload_len=%u sent",
+				idx, mode_byte, (unsigned int)plen);
+		}
+	}
+
+	/* Update status for this chunk */
+	cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+	cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+
+	/* Commit subevent chunk (RESULT or CONT) */
+	cs_pd_ras_commit_subevent(proc,
+		num_steps_reported,
+		proc_done_status,
+		subevt_done_status,
+		abort_reason & 0x0F,
+		(abort_reason >> 4) & 0x0F);
+
+	/* Ensure first segment body starts with the 4-byte RangingHeader */
+	ras_maybe_prepend_ranging_header(proc);
+
+	if (subevt_done_status != SUBEVENT_DONE_PARTIAL_RESULTS)
+		/* Send RAS raw segment data */
+		send_ras_segment_data(rap, proc);
+
+	/* Procedure complete? Clean up */
+	if (proc_done_status == CS_PROC_ALL_RESULTS_COMPLETE) {
+		DBG(rap, "Destroying CsProcedureData counter=%u and "
+			"clearing current_proc", proc->counter);
+		resptracker_reset_current_proc(resptracker);
+		/* Reset cached header values for next procedure */
+		resptracker->last_proc_counter = 0;
+		resptracker->last_start_acl_conn_evt_counter = 0;
+		resptracker->last_freq_comp = 0;
+		resptracker->last_ref_pwr_lvl = 0;
+	}
+}
+
+static void form_ras_data_with_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->resptracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+	handle_local_subevent_result(rap,
+		true,			/* has header fields */
+		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);	/* start of steps */
+}
+static void form_ras_data_with_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 *resptracker;
+
+	if (!rap || !rap->resptracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+
+	resptracker = rap->resptracker;
+
+	/* Use cached header values captured from the last RESULT event */
+	handle_local_subevent_result(rap,
+		false,			/* CONT has no header fields */
+		cont->config_id,
+		cont->num_ant_paths,
+		resptracker->last_proc_counter,
+		resptracker->last_start_acl_conn_evt_counter,
+		resptracker->last_freq_comp,
+		resptracker->last_ref_pwr_lvl,
+		cont->proc_done_status,
+		cont->subevt_done_status,
+		cont->abort_reason,
+		cont->num_steps_reported,
+		cont->step_data);
+}
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_subevent_result_cont *cont = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
+
+	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_subevent_result *data = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent: len=%d", length);
+
+	/* Populate CsProcedureData and send RAS payload */
+	form_ras_data_with_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_proc_enable_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
 
 	DBG(rap, "Received CS procedure enable complete subevent: len=%d",
 	    length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Populate responder tracker */
+	resptracker->config_id = data->config_id;
+	resptracker->selected_tx_power = data->sel_tx_pwr;
 }
 
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
@@ -544,9 +1645,26 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
+
+	if (!rap)
+		return;
 
 	DBG(rap, "Received CS config complete subevent: len=%d", length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Basic fields */
+	resptracker->config_id = data->config_id;
+	resptracker->role = data->role;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -786,7 +1904,7 @@ bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client)
 
 	bt_uuid16_create(&uuid, RAS_UUID16);
 
-	gatt_db_foreach_service(rap->lrapdb->db, &uuid,
+	gatt_db_foreach_service(rap->rrapdb->db, &uuid,
 				foreach_rap_service, rap);
 
 	return true;
-- 
2.34.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox