* [PATCH BlueZ v2 0/1] Fix unsolicited AVRCP RegisterNotification response
@ 2026-04-03 12:03 Kirill Samburskiy
2026-04-03 12:03 ` [PATCH BlueZ v2 1/1] audio/avrcp: Track TG and CT events separately Kirill Samburskiy
0 siblings, 1 reply; 4+ messages in thread
From: Kirill Samburskiy @ 2026-04-03 12:03 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Kirill Samburskiy
Issue details: https://github.com/bluez/bluez/issues/1997
This patch separates registered_events and transaction_events tracking for
target and controller AVRCP roles.
Changes:
v2: fix coding style
Kirill Samburskiy (1):
audio/avrcp: Track TG and CT events separately
profiles/audio/avrcp.c | 51 +++++++++++++++++++++++++++---------------
1 file changed, 33 insertions(+), 18 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH BlueZ v2 1/1] audio/avrcp: Track TG and CT events separately
2026-04-03 12:03 [PATCH BlueZ v2 0/1] Fix unsolicited AVRCP RegisterNotification response Kirill Samburskiy
@ 2026-04-03 12:03 ` Kirill Samburskiy
2026-04-03 14:07 ` Fix unsolicited AVRCP RegisterNotification response bluez.test.bot
0 siblings, 1 reply; 4+ messages in thread
From: Kirill Samburskiy @ 2026-04-03 12:03 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Kirill Samburskiy
Since we support both target and controller AVRCP roles, we need to
track events for each role separately to avoid confusing events that we
registered for notification from remote TG, and events that remote CT
registered for notification from us. This change is only relevant if
both sides support both CT and TG roles.
Fixes: https://github.com/bluez/bluez/issues/1997
---
profiles/audio/avrcp.c | 51 +++++++++++++++++++++++++++---------------
1 file changed, 33 insertions(+), 18 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 326dfdab7e..9d0e0851ce 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -263,6 +263,8 @@ struct avrcp_data {
int features;
uint16_t obex_port;
GSList *players;
+ uint16_t registered_events;
+ uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
};
struct avrcp {
@@ -280,9 +282,7 @@ struct avrcp {
unsigned int browsing_id;
unsigned int browsing_timer;
uint16_t supported_events;
- uint16_t registered_events;
uint8_t transaction;
- uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
struct pending_pdu *pending_pdu;
};
@@ -850,13 +850,15 @@ done:
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
+ struct avrcp_data *target = session->target;
int err;
- if (!(session->registered_events & (1 << id)))
+ if (target == NULL ||
+ !(target->registered_events & (1 << id)))
continue;
err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
+ target->transaction_events[id],
code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
@@ -864,7 +866,7 @@ done:
continue;
/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
+ target->registered_events ^= 1 << id;
}
return;
@@ -1644,6 +1646,12 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
if (!(session->supported_events & (1 << pdu->params[0])))
goto err;
+ if (session->target == NULL) {
+ pdu->params_len = cpu_to_be16(1);
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ return AVC_CTYPE_REJECTED;
+ }
+
switch (pdu->params[0]) {
case AVRCP_EVENT_STATUS_CHANGED:
len = 2;
@@ -1686,8 +1694,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
}
/* Register event and save the transaction used */
- session->registered_events |= (1 << pdu->params[0]);
- session->transaction_events[pdu->params[0]] = transaction;
+ session->target->registered_events |= (1 << pdu->params[0]);
+ session->target->transaction_events[pdu->params[0]] = transaction;
pdu->params_len = cpu_to_be16(len);
@@ -3943,12 +3951,16 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
void *user_data)
{
struct avrcp *session = user_data;
+ struct avrcp_data *controller = session->controller;
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
if (!pdu)
return FALSE;
+ if (controller == NULL)
+ return FALSE;
+
if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
code == AVC_CTYPE_REJECTED) {
@@ -3956,7 +3968,7 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
/* Lookup event by transaction */
for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
- if (session->transaction_events[i] ==
+ if (controller->transaction_events[i] ==
transaction) {
event = i;
goto changed;
@@ -4008,14 +4020,14 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
break;
}
- session->registered_events |= (1 << event);
- session->transaction_events[event] = transaction;
+ controller->registered_events |= (1 << event);
+ controller->transaction_events[event] = transaction;
return TRUE;
changed:
- session->registered_events ^= (1 << event);
- session->transaction_events[event] = 0;
+ controller->registered_events ^= (1 << event);
+ controller->transaction_events[event] = 0;
avrcp_register_notification(session, event);
return FALSE;
}
@@ -4667,7 +4679,8 @@ static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
int err;
/* Verify that the event is registered */
- if (!(session->registered_events & (1 << id)))
+ if (session->target == NULL ||
+ !(session->target->registered_events & (1 << id)))
return -ENOENT;
memset(buf, 0, sizeof(buf));
@@ -4692,21 +4705,22 @@ static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
pdu->params_len = cpu_to_be16(size);
err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
+ session->target->transaction_events[id],
code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
if (err < 0)
return err;
/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
+ session->target->registered_events ^= 1 << id;
return err;
}
-static bool avrcp_event_registered(struct avrcp *session, uint8_t event)
+static bool avrcp_target_event_registered(struct avrcp *session, uint8_t event)
{
- return session->registered_events & (1 << event);
+ return session->target &&
+ session->target->registered_events & (1 << event);
}
int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
@@ -4741,7 +4755,8 @@ int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
* for it), allow the call to pass through if the remote
* controller registered for a volume changed event.
*/
- if (!session->controller && !avrcp_event_registered(session,
+ if (!session->controller &&
+ !avrcp_target_event_registered(session,
AVRCP_EVENT_VOLUME_CHANGED))
return -ENOTSUP;
} else if (!avrcp_volume_supported(session->controller)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH BlueZ 1/1] audio/avrcp: Track TG and CT events separately
@ 2026-04-01 11:42 Kirill Samburskiy
2026-04-01 12:56 ` Fix unsolicited AVRCP RegisterNotification response bluez.test.bot
0 siblings, 1 reply; 4+ messages in thread
From: Kirill Samburskiy @ 2026-04-01 11:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Kirill Samburskiy
Since we support both target and controller AVRCP roles, we need to
track events for each role separately to avoid confusing events that we
registered for notification from remote TG, and events that remote CT
registered for notification from us. This change is only relevant if
both sides support both CT and TG roles.
Fixes: https://github.com/bluez/bluez/issues/1997
---
profiles/audio/avrcp.c | 48 ++++++++++++++++++++++++++----------------
1 file changed, 30 insertions(+), 18 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 326dfdab7..2968bff63 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -263,6 +263,8 @@ struct avrcp_data {
int features;
uint16_t obex_port;
GSList *players;
+ uint16_t registered_events;
+ uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
};
struct avrcp {
@@ -280,9 +282,7 @@ struct avrcp {
unsigned int browsing_id;
unsigned int browsing_timer;
uint16_t supported_events;
- uint16_t registered_events;
uint8_t transaction;
- uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
struct pending_pdu *pending_pdu;
};
@@ -852,11 +852,12 @@ done:
struct avrcp *session = l->data;
int err;
- if (!(session->registered_events & (1 << id)))
+ if (session->target == NULL ||
+ !(session->target->registered_events & (1 << id)))
continue;
err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
+ session->target->transaction_events[id],
code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
@@ -864,7 +865,7 @@ done:
continue;
/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
+ session->target->registered_events ^= 1 << id;
}
return;
@@ -1644,6 +1645,12 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
if (!(session->supported_events & (1 << pdu->params[0])))
goto err;
+ if (session->target == NULL) {
+ pdu->params_len = cpu_to_be16(1);
+ pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+ return AVC_CTYPE_REJECTED;
+ }
+
switch (pdu->params[0]) {
case AVRCP_EVENT_STATUS_CHANGED:
len = 2;
@@ -1686,8 +1693,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
}
/* Register event and save the transaction used */
- session->registered_events |= (1 << pdu->params[0]);
- session->transaction_events[pdu->params[0]] = transaction;
+ session->target->registered_events |= (1 << pdu->params[0]);
+ session->target->transaction_events[pdu->params[0]] = transaction;
pdu->params_len = cpu_to_be16(len);
@@ -3949,6 +3956,9 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
if (!pdu)
return FALSE;
+ if (session->controller == NULL)
+ return FALSE;
+
if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
code == AVC_CTYPE_REJECTED) {
@@ -3956,7 +3966,7 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
/* Lookup event by transaction */
for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
- if (session->transaction_events[i] ==
+ if (session->controller->transaction_events[i] ==
transaction) {
event = i;
goto changed;
@@ -4008,14 +4018,14 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
break;
}
- session->registered_events |= (1 << event);
- session->transaction_events[event] = transaction;
+ session->controller->registered_events |= (1 << event);
+ session->controller->transaction_events[event] = transaction;
return TRUE;
changed:
- session->registered_events ^= (1 << event);
- session->transaction_events[event] = 0;
+ session->controller->registered_events ^= (1 << event);
+ session->controller->transaction_events[event] = 0;
avrcp_register_notification(session, event);
return FALSE;
}
@@ -4667,7 +4677,8 @@ static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
int err;
/* Verify that the event is registered */
- if (!(session->registered_events & (1 << id)))
+ if (session->target == NULL ||
+ !(session->target->registered_events & (1 << id)))
return -ENOENT;
memset(buf, 0, sizeof(buf));
@@ -4692,21 +4703,22 @@ static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
pdu->params_len = cpu_to_be16(size);
err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
+ session->target->transaction_events[id],
code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
if (err < 0)
return err;
/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
+ session->target->registered_events ^= 1 << id;
return err;
}
-static bool avrcp_event_registered(struct avrcp *session, uint8_t event)
+static bool avrcp_target_event_registered(struct avrcp *session, uint8_t event)
{
- return session->registered_events & (1 << event);
+ return session->target &&
+ session->target->registered_events & (1 << event);
}
int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
@@ -4741,7 +4753,7 @@ int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
* for it), allow the call to pass through if the remote
* controller registered for a volume changed event.
*/
- if (!session->controller && !avrcp_event_registered(session,
+ if (!session->controller && !avrcp_target_event_registered(session,
AVRCP_EVENT_VOLUME_CHANGED))
return -ENOTSUP;
} else if (!avrcp_volume_supported(session->controller)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-03 14:07 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-03 12:03 [PATCH BlueZ v2 0/1] Fix unsolicited AVRCP RegisterNotification response Kirill Samburskiy
2026-04-03 12:03 ` [PATCH BlueZ v2 1/1] audio/avrcp: Track TG and CT events separately Kirill Samburskiy
2026-04-03 14:07 ` Fix unsolicited AVRCP RegisterNotification response bluez.test.bot
-- strict thread matches above, loose matches on Subject: below --
2026-04-01 11:42 [PATCH BlueZ 1/1] audio/avrcp: Track TG and CT events separately Kirill Samburskiy
2026-04-01 12:56 ` Fix unsolicited AVRCP RegisterNotification response bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox