Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH] shared/asha: prime volume before starting audio
@ 2026-10-13  3:46 Kyle Pepper
  2026-10-13  5:03 ` bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Kyle Pepper @ 2026-10-13  3:46 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Marcel Holtmann, Luiz Augusto von Dentz

Some tested renderers remain silent unless the Volume characteristic is
written before START. Prime it with the selected volume and keep the
write best effort.

Test volume-before-START ordering and best-effort failure handling.

Assisted-by: OpenCode:gpt-6.1-sol
---
Submission review notes (not part of the commit message):
Local validation is for base 7428ca2df9356363079db0f48412c20cbdb9471f.
Compatibility with newer upstream tips has not been verified locally.
N8 startup evidence is from the combined downstream stack, not a
volume-only hardware ablation. Native ordering/failure unit tests are
included; other renderer coverage and human contribution review remain.

 .gitignore             |  1 +
 Makefile.am            |  6 +++
 src/shared/asha.c      |  4 ++
 unit/test-asha-start.c | 99 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 110 insertions(+)
 create mode 100644 unit/test-asha-start.c

diff --git a/.gitignore b/.gitignore
index 06b24735df..adff1ea84a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,6 +112,7 @@ unit/test-lib
 unit/test-mgmt
 unit/test-uhid
 unit/test-hfp
+unit/test-asha-start
 unit/test-crypto
 unit/test-ecc
 unit/test-hog
diff --git a/Makefile.am b/Makefile.am
index f742458d11..a56d66873b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -706,6 +706,12 @@ unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \
 
 unit_tests += unit/test-hfp
 
+unit_tests += unit/test-asha-start
+
+unit_test_asha_start_SOURCES = unit/test-asha-start.c
+unit_test_asha_start_LDADD = src/libshared-glib.la \
+	lib/libbluetooth-internal.la $(GLIB_LIBS)
+
 unit_test_hfp_SOURCES = unit/test-hfp.c
 unit_test_hfp_LDADD = src/libshared-glib.la $(GLIB_LIBS)
 
diff --git a/src/shared/asha.c b/src/shared/asha.c
index 79bb29bf9a..b5e8d67567 100644
--- a/src/shared/asha.c
+++ b/src/shared/asha.c
@@ -262,6 +262,10 @@ unsigned int bt_asha_start(struct bt_asha *asha, bt_asha_cb_t cb,
 		return 0;
 	}
 
+	/* Some renderers require a Volume write before START. */
+	if (!bt_asha_set_volume(asha, asha->volume))
+		DBG("ASHA volume priming failed, continuing with START");
+
 	ret = asha_send_acp(asha, acp_start_cmd, sizeof(acp_start_cmd), cb,
 			user_data);
 	if (ret < 0)
diff --git a/unit/test-asha-start.c b/unit/test-asha-start.c
new file mode 100644
index 0000000000..49df54c1f3
--- /dev/null
+++ b/unit/test-asha-start.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+#include <assert.h>
+
+/* Source-level mocks remain effective with link-time optimization. */
+#define bt_gatt_client_write_without_response test_write_command
+#define bt_gatt_client_write_value test_write_request
+#include "src/shared/asha.c"
+#undef bt_gatt_client_write_without_response
+#undef bt_gatt_client_write_value
+#include "src/shared/tester.h"
+
+static unsigned int commands, requests;
+static bool volume_ok, start_ok;
+static int8_t selected_volume;
+
+void btd_error(uint16_t index, const char *format, ...)
+{
+}
+
+void btd_debug(uint16_t index, const char *format, ...)
+{
+}
+
+unsigned int test_write_command(struct bt_gatt_client *client, uint16_t handle,
+					bool signed_write,
+					const uint8_t *value, uint16_t length)
+{
+	assert(client && handle == 0x82 && !signed_write);
+	assert(length == 1 && (int8_t) value[0] == selected_volume);
+	assert(requests == 0);
+	commands++;
+	return volume_ok;
+}
+
+unsigned int test_write_request(struct bt_gatt_client *client, uint16_t handle,
+				const uint8_t *value, uint16_t length,
+				bt_gatt_client_callback_t callback, void *data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	assert(client && handle == 0x79);
+	assert(commands == 1 && requests == 0);
+	assert(length == 5 && value[0] == 0x01 && value[1] == 0x01);
+	assert((int8_t) value[3] == selected_volume);
+	assert(callback && data && !destroy);
+	requests++;
+	return start_ok;
+}
+
+static void test_start(const void *user_data)
+{
+	const int8_t volumes[] = { -128, -127, -64, 0 };
+	unsigned int i, fail;
+
+	for (i = 0; i < ARRAY_SIZE(volumes); i++) {
+		for (fail = 0; fail < 2; fail++) {
+			struct bt_asha asha = {
+				.client = (void *) 1, .acp_handle = 0x79,
+				.volume_handle = 0x82, .volume = volumes[i],
+			};
+
+			selected_volume = volumes[i];
+			commands = requests = 0;
+			volume_ok = !fail;
+			start_ok = true;
+			assert(bt_asha_start(&asha, NULL, NULL) == 0);
+			assert(commands == 1 && requests == 1);
+			assert(asha.state == ASHA_STARTING);
+			assert(asha.volume == selected_volume);
+
+			/* A repeated start must not write the volume again. */
+			bt_asha_start(&asha, NULL, NULL);
+			assert(commands == 1 && requests == 1);
+		}
+	}
+
+	{
+		struct bt_asha asha = {
+			.client = (void *) 1, .acp_handle = 0x79,
+			.volume_handle = 0x82, .volume = -64,
+		};
+
+		commands = requests = 0;
+		selected_volume = asha.volume;
+		volume_ok = true;
+		start_ok = false;
+		assert(bt_asha_start(&asha, NULL, NULL) == (unsigned int) -1);
+		assert(asha.state == ASHA_STOPPED);
+		assert(commands == 1 && requests == 1);
+	}
+
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+	tester_add("ASHA/start/volume", NULL, NULL, test_start, NULL);
+	return tester_run();
+}

base-commit: 7428ca2df9356363079db0f48412c20cbdb9471f


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

end of thread, other threads:[~2026-10-13  5:03 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13  3:46 [PATCH] shared/asha: prime volume before starting audio Kyle Pepper
2026-10-13  5:03 ` 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