[PATCH BlueZ v6 1/2] rap: use session lookup for RAS operations
Naga Bhavani Akella <[email protected]> Mon, 27 Jul 2026 12:29:53 +0530
| Newsgroups | org.kernel.vger.linux-bluetooth |
|---|---|
| Message-ID | <[email protected]> |
Store disconnect information in RAP state and
centralize attachment handling through dedicated helpers.
Add bt_rap_get_session() and use it from all RAS
GATT server callbacks
---
src/shared/rap.c | 143 +++++++++++++++++++++++++++++++++++++++++++++--
src/shared/rap.h | 2 +
2 files changed, 141 insertions(+), 4 deletions(-)
diff --git a/src/shared/rap.c b/src/shared/rap.c
index dfb272d3a..b0c5f8ae7 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -19,7 +19,6 @@
#include "src/shared/queue.h"
#include "src/shared/util.h"
-#include "src/shared/timeout.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
@@ -291,6 +290,7 @@ struct bt_rap {
struct bt_att *att;
unsigned int idle_id;
+ unsigned int disconn_id;
struct queue *notify;
struct queue *pending;
@@ -564,6 +564,40 @@ static void rap_detached(void *data, void *user_data)
cb->detached(rap, cb->user_data);
}
+static void rap_attached(void *data, void *user_data)
+{
+ struct bt_rap_cb *cb = data;
+ struct bt_rap *rap = user_data;
+
+ if (!cb->attached)
+ return;
+
+ cb->attached(rap, cb->user_data);
+}
+
+static void rap_disconnected(int err, void *user_data)
+{
+ struct bt_rap *rap = user_data;
+
+ rap->disconn_id = 0;
+
+ bt_rap_detach(rap);
+}
+
+static void rap_attach_att(struct bt_rap *rap, struct bt_att *att)
+{
+ if (rap->disconn_id) {
+ if (att == bt_rap_get_att(rap))
+ return;
+ bt_att_unregister_disconnect(rap->att, rap->disconn_id);
+ }
+
+ rap->att = att;
+ rap->disconn_id = bt_att_register_disconnect(rap->att,
+ rap_disconnected,
+ rap, NULL);
+}
+
void bt_rap_detach(struct bt_rap *rap)
{
if (!queue_remove(sessions, rap))
@@ -573,6 +607,9 @@ void bt_rap_detach(struct bt_rap *rap)
bt_gatt_client_unref(rap->client);
rap->client = NULL;
+ bt_att_unregister_disconnect(rap->att, rap->disconn_id);
+ rap->att = NULL;
+
queue_foreach(bt_rap_cbs, rap_detached, rap);
}
@@ -653,6 +690,34 @@ struct bt_att *bt_rap_get_att(struct bt_rap *rap)
return bt_gatt_client_get_att(rap->client);
}
+struct bt_rap *bt_rap_get_session(struct bt_att *att, struct gatt_db *db)
+{
+ const struct queue_entry *entry;
+ struct bt_rap *rap;
+
+ for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
+ rap = entry->data;
+
+ if (att == bt_rap_get_att(rap))
+ return rap;
+ }
+
+ rap = bt_rap_new(db, NULL);
+ if (!rap)
+ return NULL;
+
+ rap->att = att;
+
+ /*
+ * A rejected attach (e.g. a duplicate session for a device that
+ * already has one) leaves rap already freed - don't touch it.
+ */
+ if (!bt_rap_attach(rap, NULL))
+ return NULL;
+
+ return rap;
+}
+
struct bt_rap *bt_rap_ref(struct bt_rap *rap)
{
if (!rap)
@@ -729,6 +794,7 @@ static void ras_features_read_cb(struct gatt_db_attribute *attrib,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
+ struct ras *ras = user_data;
/*
* Feature mask: bits 0-2 set:
* - Real-time ranging
@@ -737,6 +803,9 @@ static void ras_features_read_cb(struct gatt_db_attribute *attrib,
*/
uint8_t value[4] = { 0x01, 0x00, 0x00, 0x00 };
+ if (ras)
+ bt_rap_get_session(att, ras->rapdb->db);
+
gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
}
@@ -745,6 +814,11 @@ static void ras_ondemand_read_cb(struct gatt_db_attribute *attrib,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
+ struct ras *ras = user_data;
+
+ if (ras)
+ bt_rap_get_session(att, ras->rapdb->db);
+
/* No static read data – on‑demand data is pushed via
* notifications
*/
@@ -761,6 +835,11 @@ static void ras_control_point_write_cb(struct gatt_db_attribute *attrib,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
+ struct ras *ras = user_data;
+
+ if (ras)
+ bt_rap_get_session(att, ras->rapdb->db);
+
/* Control point handler - implementation TBD */
}
@@ -770,9 +849,13 @@ static void ras_data_ready_read_cb(struct gatt_db_attribute *attrib,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
+ struct ras *ras = user_data;
uint16_t counter = 0;
uint8_t value[2];
+ if (ras)
+ bt_rap_get_session(att, ras->rapdb->db);
+
put_le16(counter, value);
gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
}
@@ -783,8 +866,12 @@ static void ras_data_overwritten_read_cb(struct gatt_db_attribute *attrib,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
+ struct ras *ras = user_data;
uint8_t value[2] = { 0x00, 0x00 };
+ if (ras)
+ bt_rap_get_session(att, ras->rapdb->db);
+
gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
}
@@ -806,6 +893,8 @@ static void ras_ranging_data_ccc_write_cb(struct gatt_db_attribute *attrib,
return;
}
+ bt_rap_get_session(att, ras->rapdb->db);
+
if (offset) {
gatt_db_attribute_write_result(attrib, id,
BT_ATT_ERROR_INVALID_OFFSET);
@@ -2794,28 +2883,74 @@ static void rap_idle(void *data)
bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client)
{
bt_uuid_t uuid;
+ bool attached;
+
+ if (queue_find(sessions, NULL, rap)) {
+ if (client && !rap->client)
+ goto clone;
+ /*
+ * Already attached: treat re-attach as idempotent rather
+ * than failing, so e.g. a second rap_accept() on the same
+ * session succeeds instead of erroring out.
+ */
+ return true;
+ }
if (!sessions)
sessions = queue_new();
queue_push_tail(sessions, rap);
- if (!client)
+ /*
+ * Notify profile callbacks before rap_attach_att() below runs, so
+ * bt_rap_get_att(rap) inside a rap_attached callback only sees the
+ * att already set on rap (e.g. by bt_rap_get_session()), not the
+ * one about to be registered a few lines down.
+ *
+ * A callback may reject this session (e.g. a duplicate ad-hoc
+ * session for a device that already has one) by unref'ing rap,
+ * which can free it. Hold our own reference across the
+ * notification so a rejection can never drop the count to zero
+ * while callbacks are still running.
+ *
+ * Releasing that reference below is what may actually free rap
+ * (via rap_free() -> bt_rap_detach(), which removes it from
+ * `sessions` before the object is freed). Check membership only
+ * *after* the unref, and only by pointer identity - never by
+ * dereferencing rap - so the check itself is safe even if rap no
+ * longer exists.
+ */
+ bt_rap_ref(rap);
+ queue_foreach(bt_rap_cbs, rap_attached, rap);
+ bt_rap_unref(rap);
+
+ attached = queue_find(sessions, NULL, rap);
+ if (!attached)
+ return false;
+
+ if (!client) {
+ if (rap->att)
+ rap_attach_att(rap, rap->att);
return true;
+ }
if (rap->client)
return false;
+clone:
rap->client = bt_gatt_client_clone(client);
if (!rap->client)
return false;
+ rap_attach_att(rap, bt_gatt_client_get_att(client));
+
bt_gatt_client_idle_register(rap->client, rap_idle, rap, NULL);
bt_uuid16_create(&uuid, RAS_UUID16);
- gatt_db_foreach_service(rap->rrapdb->db, &uuid,
- foreach_rap_service, rap);
+ if (rap->rrapdb)
+ gatt_db_foreach_service(rap->rrapdb->db, &uuid,
+ foreach_rap_service, rap);
return true;
}
diff --git a/src/shared/rap.h b/src/shared/rap.h
index 5582635e6..a44831e6e 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -15,6 +15,7 @@
struct bt_rap;
struct gatt_db;
struct bt_gatt_client;
+struct bt_att;
/* Channel Sounding Events */
struct bt_rap_hci_cs_options {
@@ -171,6 +172,7 @@ bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client);
void bt_rap_detach(struct bt_rap *rap);
struct bt_att *bt_rap_get_att(struct bt_rap *rap);
+struct bt_rap *bt_rap_get_session(struct bt_att *att, struct gatt_db *db);
bool bt_rap_set_user_data(struct bt_rap *rap, void *user_data);
--