* [PATCH BlueZ v3] shared/vcp: have only one volume change in flight at a time
@ 2025-02-01 15:12 Pauli Virtanen
2025-02-01 16:09 ` [BlueZ,v3] " bluez.test.bot
2025-02-03 21:30 ` [PATCH BlueZ v3] " patchwork-bot+bluetooth
0 siblings, 2 replies; 3+ messages in thread
From: Pauli Virtanen @ 2025-02-01 15:12 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
If bt_vcp_set_volume() is called again before the previous operation has
completed, the requests get the same change counter, and all except the
first one fail.
Fix by waiting until the current request completes, and if volume was
set again during waiting, send a new request with the latest pending
volume value. In this definition, bt_vcp_set_volume() will skip over
intermediate volume updates if they are done too rapidly.
Send only volume requests that change the value to a different one than
last notification we have seen: in this case the request either fails,
or succeeds and generates a new notification. In theory this guarantees
we always exit waiting, but safeguard it with a timeout.
---
Notes:
v3: add timeout, wait separately for reply and notify
In theory from VCS v1.0.1 3.2.2.5 + 3.2.2 you expect that request
that is setting volume to different value than that of the current
change counter, can only either fail, or succeed and produce
notification. But it's probably safer to have a timeout regardless,
so that we're guaranteed to stop waiting.
I also changed it to keep track of which of write response & volume
notify have arrived, to avoid a race condition where notify from
another client's volume change caused us to send another request
before previous completed.
v2: reset pending_ops when attaching, needs to be cleared on reconnect
src/shared/vcp.c | 86 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 82 insertions(+), 4 deletions(-)
diff --git a/src/shared/vcp.c b/src/shared/vcp.c
index 6b0f2f9db..f0887ad62 100644
--- a/src/shared/vcp.c
+++ b/src/shared/vcp.c
@@ -32,6 +32,8 @@
#define VCP_STEP_SIZE 1
+#define VCP_CLIENT_OP_TIMEOUT 2000
+
#define VOCS_VOL_OFFSET_UPPER_LIMIT 255
#define VOCS_VOL_OFFSET_LOWER_LIMIT -255
@@ -176,6 +178,14 @@ struct bt_vcp_notify {
void *user_data;
};
+struct bt_vcp_client_op {
+ uint8_t volume;
+ bool resend;
+ bool wait_reply;
+ bool wait_notify;
+ unsigned int timeout_id;
+};
+
struct bt_vcp {
int ref_count;
struct bt_vcp_db *ldb;
@@ -203,6 +213,8 @@ struct bt_vcp {
uint8_t volume;
uint8_t volume_counter;
+ struct bt_vcp_client_op pending_op;
+
void *debug_data;
void *user_data;
};
@@ -395,6 +407,14 @@ static void vcp_remote_client_detached(void *data, void *user_data)
cb->detached(vcp, cb->user_data);
}
+static void vcp_client_op_clear(struct bt_vcp_client_op *op)
+{
+ if (op->timeout_id)
+ timeout_remove(op->timeout_id);
+
+ memset(op, 0, sizeof(*op));
+}
+
void bt_vcp_detach(struct bt_vcp *vcp)
{
if (!queue_remove(sessions, vcp))
@@ -404,6 +424,8 @@ void bt_vcp_detach(struct bt_vcp *vcp)
bt_gatt_client_unref(vcp->client);
vcp->client = NULL;
}
+
+ vcp_client_op_clear(&vcp->pending_op);
}
static void vcp_db_free(void *data)
@@ -2003,6 +2025,22 @@ done:
return vcp;
}
+static void vcp_set_volume_complete(struct bt_vcp *vcp)
+{
+ bool resend = vcp->pending_op.resend;
+ uint8_t volume = vcp->pending_op.volume;
+
+ vcp_client_op_clear(&vcp->pending_op);
+
+ /* If there were more volume set ops while waiting for the one that
+ * completes, send request to set volume to the latest pending value.
+ */
+ if (resend) {
+ DBG(vcp, "set pending volume 0x%x", volume);
+ bt_vcp_set_volume(vcp, volume);
+ }
+}
+
static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle,
const uint8_t *value, uint16_t length,
void *user_data)
@@ -2020,6 +2058,10 @@ static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle,
if (vcp->volume_changed)
vcp->volume_changed(vcp, vcp->volume);
+
+ vcp->pending_op.wait_notify = false;
+ if (!vcp->pending_op.wait_reply)
+ vcp_set_volume_complete(vcp);
}
static void vcp_volume_cp_sent(bool success, uint8_t err, void *user_data)
@@ -2031,12 +2073,23 @@ static void vcp_volume_cp_sent(bool success, uint8_t err, void *user_data)
DBG(vcp, "setting volume failed: invalid counter");
else
DBG(vcp, "setting volume failed: error 0x%x", err);
+
+ vcp_set_volume_complete(vcp);
+ } else {
+ vcp->pending_op.wait_reply = false;
+ if (!vcp->pending_op.wait_notify)
+ vcp_set_volume_complete(vcp);
}
}
-uint8_t bt_vcp_get_volume(struct bt_vcp *vcp)
+static bool vcp_set_volume_timeout(void *data)
{
- return vcp->volume;
+ struct bt_vcp *vcp = data;
+
+ DBG(vcp, "setting volume: timeout");
+ vcp->pending_op.timeout_id = 0;
+ vcp_set_volume_complete(vcp);
+ return false;
}
static bool vcp_set_volume_client(struct bt_vcp *vcp, uint8_t volume)
@@ -2061,9 +2114,24 @@ static bool vcp_set_volume_client(struct bt_vcp *vcp, uint8_t volume)
return false;
}
- vcp->volume = volume;
+ /* If there is another set volume op in flight, just update the wanted
+ * pending volume value. Req with the latest volume value is sent after
+ * the current one completes. This may skip over some volume changes,
+ * as it only sends a request for the final value.
+ */
+ if (vcp->volume == volume) {
+ /* Do not set to current value, as that doesn't generate
+ * a notification
+ */
+ return true;
+ } else if (vcp->pending_op.timeout_id) {
+ vcp->pending_op.volume = volume;
+ vcp->pending_op.resend = true;
+ return true;
+ }
+
req.op = BT_VCS_SET_ABSOLUTE_VOL;
- req.vol_set = vcp->volume;
+ req.vol_set = volume;
req.change_counter = vcp->volume_counter;
if (!bt_gatt_client_write_value(vcp->client, value_handle, (void *)&req,
@@ -2072,6 +2140,11 @@ static bool vcp_set_volume_client(struct bt_vcp *vcp, uint8_t volume)
DBG(vcp, "error writing volume");
return false;
}
+
+ vcp->pending_op.timeout_id = timeout_add(VCP_CLIENT_OP_TIMEOUT,
+ vcp_set_volume_timeout, vcp, NULL);
+ vcp->pending_op.wait_notify = true;
+ vcp->pending_op.wait_reply = true;
return true;
}
@@ -2108,6 +2181,11 @@ bool bt_vcp_set_volume(struct bt_vcp *vcp, uint8_t volume)
return vcp_set_volume_server(vcp, volume);
}
+uint8_t bt_vcp_get_volume(struct bt_vcp *vcp)
+{
+ return vcp->volume;
+}
+
static void vcp_voffset_state_notify(struct bt_vcp *vcp, uint16_t value_handle,
const uint8_t *value, uint16_t length,
void *user_data)
--
2.48.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* RE: [BlueZ,v3] shared/vcp: have only one volume change in flight at a time
2025-02-01 15:12 [PATCH BlueZ v3] shared/vcp: have only one volume change in flight at a time Pauli Virtanen
@ 2025-02-01 16:09 ` bluez.test.bot
2025-02-03 21:30 ` [PATCH BlueZ v3] " patchwork-bot+bluetooth
1 sibling, 0 replies; 3+ messages in thread
From: bluez.test.bot @ 2025-02-01 16:09 UTC (permalink / raw)
To: linux-bluetooth, pav
[-- Attachment #1: Type: text/plain, Size: 1260 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=929711
---Test result---
Test Summary:
CheckPatch PENDING 0.20 seconds
GitLint PENDING 0.20 seconds
BuildEll PASS 20.27 seconds
BluezMake PASS 1521.80 seconds
MakeCheck PASS 13.40 seconds
MakeDistcheck PASS 157.27 seconds
CheckValgrind PASS 212.67 seconds
CheckSmatch PASS 268.29 seconds
bluezmakeextell PASS 97.69 seconds
IncrementalBuild PENDING 0.25 seconds
ScanBuild PASS 848.53 seconds
Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:
##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH BlueZ v3] shared/vcp: have only one volume change in flight at a time
2025-02-01 15:12 [PATCH BlueZ v3] shared/vcp: have only one volume change in flight at a time Pauli Virtanen
2025-02-01 16:09 ` [BlueZ,v3] " bluez.test.bot
@ 2025-02-03 21:30 ` patchwork-bot+bluetooth
1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+bluetooth @ 2025-02-03 21:30 UTC (permalink / raw)
To: Pauli Virtanen; +Cc: linux-bluetooth
Hello:
This patch was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Sat, 1 Feb 2025 17:12:09 +0200 you wrote:
> If bt_vcp_set_volume() is called again before the previous operation has
> completed, the requests get the same change counter, and all except the
> first one fail.
>
> Fix by waiting until the current request completes, and if volume was
> set again during waiting, send a new request with the latest pending
> volume value. In this definition, bt_vcp_set_volume() will skip over
> intermediate volume updates if they are done too rapidly.
>
> [...]
Here is the summary with links:
- [BlueZ,v3] shared/vcp: have only one volume change in flight at a time
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=e77884accdb2
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-02-03 21:30 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-01 15:12 [PATCH BlueZ v3] shared/vcp: have only one volume change in flight at a time Pauli Virtanen
2025-02-01 16:09 ` [BlueZ,v3] " bluez.test.bot
2025-02-03 21:30 ` [PATCH BlueZ v3] " patchwork-bot+bluetooth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).