All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/2] shared/mcp: emit MCS error if value changes during long read
@ 2025-12-16 19:46 Pauli Virtanen
  2025-12-16 19:46 ` [PATCH BlueZ 2/2] test-mcp: add tests for long value reading Pauli Virtanen
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Pauli Virtanen @ 2025-12-16 19:46 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

MCS spec requires emitting Value Changed During Read Long if value
changes between remote reading with zero offset and nonzero offset.

This is session-specific state, so add support for that.

As server, track value changes and emit that error properly.

As client, we don't need to reread if this error occurs, as there should
be a notification or track changed that queues a new read.
---
 src/shared/mcp.c | 109 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 97 insertions(+), 12 deletions(-)

diff --git a/src/shared/mcp.c b/src/shared/mcp.c
index 910089f18..9f8952af1 100644
--- a/src/shared/mcp.c
+++ b/src/shared/mcp.c
@@ -30,6 +30,8 @@
 #include "src/shared/mcp.h"
 #include "src/shared/mcs.h"
 
+#define BT_MCS_ERROR_VALUE_CHANGED_DURING_READ_LONG	0x80
+
 #define DBG_MCP(mcp, fmt, ...) \
 	mcp_debug(mcp, "%s:%s() mcp %p | " fmt, __FILE__, __func__, mcp, \
 								##__VA_ARGS__)
@@ -76,22 +78,19 @@ struct bt_mcs_db {
 	struct gatt_db_attribute *ccid;
 };
 
-struct bt_mcs_client {
+struct bt_mcs_session {
+	struct bt_mcs *mcs;
 	struct bt_att *att;
+	unsigned int disconn_id;
 
-	/* Per-client state.
-	 *
-	 * Concurrency is not specified in MCS v1.0.1, everything currently
-	 * implemented seems OK to be in global state.
-	 *
-	 * TODO: Search Results ID likely should go here
-	 */
+	/* Per-client state */
+	struct queue *changed;
 };
 
 struct bt_mcs {
 	struct gatt_db *db;
 	struct bt_mcs_db ldb;
-	struct queue *clients;
+	struct queue *sessions;
 
 	uint8_t media_state;
 
@@ -557,11 +556,86 @@ static bool set_playing_order(struct bt_mcs *mcs, void *data)
 	return false;
 }
 
+static bool match_session_att(const void *data, const void *match_data)
+{
+	const struct bt_mcs_session *session = data;
+
+	return session->att == match_data;
+}
+
+static void session_destroy(void *data)
+{
+	struct bt_mcs_session *session = data;
+
+	bt_att_unregister_disconnect(session->att, session->disconn_id);
+	queue_destroy(session->changed, NULL);
+	free(session);
+}
+
+static void session_disconnect(int err, void *user_data)
+{
+	struct bt_mcs_session *session = user_data;
+	struct bt_mcs *mcs = session->mcs;
+
+	queue_remove(mcs->sessions, session);
+	session_destroy(session);
+}
+
+static struct bt_mcs_session *get_session(struct bt_mcs *mcs,
+							struct bt_att *att)
+{
+	struct bt_mcs_session *session;
+
+	session = queue_find(mcs->sessions, match_session_att, att);
+	if (session)
+		return session;
+
+	session = new0(struct bt_mcs_session, 1);
+	session->disconn_id = bt_att_register_disconnect(att,
+					session_disconnect, session, NULL);
+	if (!session->disconn_id) {
+		free(session);
+		return NULL;
+	}
+
+	session->mcs = mcs;
+	session->att = att;
+	session->changed = queue_new();
+
+	queue_push_tail(mcs->sessions, session);
+	return session;
+}
+
+static void session_changed(void *data, void *user_data)
+{
+	struct bt_mcs_session *session = data;
+	struct gatt_db_attribute *attrib = user_data;
+
+	if (!queue_find(session->changed, NULL, attrib))
+		queue_push_tail(session->changed, attrib);
+}
+
 static void read_result(struct bt_mcs *mcs, struct gatt_db_attribute *attrib,
-			unsigned int id, uint16_t offset, mcs_get_func_t get)
+			unsigned int id, uint16_t offset, struct bt_att *att,
+			mcs_get_func_t get)
 {
 	uint8_t buf[BT_ATT_MAX_VALUE_LEN];
 	struct iovec iov = { .iov_base = buf, .iov_len = 0 };
+	struct bt_mcs_session *session = get_session(mcs, att);
+
+	if (!session) {
+		gatt_db_attribute_read_result(attrib, id,
+						BT_ATT_ERROR_UNLIKELY, NULL, 0);
+		return;
+	}
+
+	if (!offset) {
+		queue_remove(session->changed, attrib);
+	} else if (queue_find(session->changed, NULL, attrib)) {
+		gatt_db_attribute_read_result(attrib, id,
+			BT_MCS_ERROR_VALUE_CHANGED_DURING_READ_LONG, NULL, 0);
+		return;
+	}
 
 	get(mcs, &iov, sizeof(buf));
 
@@ -582,7 +656,7 @@ static void read_result(struct bt_mcs *mcs, struct gatt_db_attribute *attrib,
 				void *user_data) \
 	{ \
 		DBG_MCS(user_data, ""); \
-		read_result(user_data, attrib, id, offset, get_ ##name); \
+		read_result(user_data, attrib, id, offset, att, get_ ##name); \
 	}
 
 READ_FUNC(media_player_name)
@@ -683,7 +757,9 @@ void bt_mcs_changed(struct bt_mcs *mcs, uint16_t chrc_uuid)
 		if (bt_uuid_cmp(&uuid_attr, &uuid))
 			continue;
 
-		DBG_MCS(mcs, "Notify %u", chrc_uuid);
+		queue_foreach(mcs->sessions, session_changed, attrs[i].attr);
+
+		DBG_MCS(mcs, "Notify 0x%04x", chrc_uuid);
 
 		attrs[i].get(mcs, &iov, sizeof(buf));
 
@@ -925,6 +1001,7 @@ struct bt_mcs *bt_mcs_register(struct gatt_db *db, bool is_gmcs,
 	mcs->user_data = user_data;
 
 	mcs->media_state = BT_MCS_STATE_INACTIVE;
+	mcs->sessions = queue_new();
 
 	if (!mcs_init_db(mcs, is_gmcs)) {
 		free(mcs);
@@ -959,6 +1036,8 @@ void bt_mcs_unregister(struct bt_mcs *mcs)
 		servers = NULL;
 	}
 
+	queue_destroy(mcs->sessions, session_destroy);
+
 	free(mcs);
 }
 
@@ -1367,6 +1446,12 @@ static void update_media_player_name(bool success, uint8_t att_ecode,
 {
 	struct bt_mcp_service *service = user_data;
 
+	if (!success) {
+		DBG_SVC(service, "Unable to read Media Player Name: "
+						"error 0x%02x", att_ecode);
+		return;
+	}
+
 	DBG_SVC(service, "Media Player Name");
 
 	LISTENER_CB(service, media_player_name, value, length);
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-01-13 22:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-16 19:46 [PATCH BlueZ 1/2] shared/mcp: emit MCS error if value changes during long read Pauli Virtanen
2025-12-16 19:46 ` [PATCH BlueZ 2/2] test-mcp: add tests for long value reading Pauli Virtanen
2025-12-16 20:40 ` [BlueZ,1/2] shared/mcp: emit MCS error if value changes during long read bluez.test.bot
2026-01-13 15:41 ` [PATCH BlueZ 1/2] " Pauli Virtanen
2026-01-13 22:30 ` patchwork-bot+bluetooth

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.