diff mbox series

[BlueZ,2/5] asha: Implement volume on transport

Message ID 20240508154604.276763-3-arun@asymptotic.io
State New
Headers show
Series ASHA plugin | expand

Commit Message

Arun Raghavan May 8, 2024, 3:46 p.m. UTC
We convert from the spec definition of the range to the DBus API range
to make it easier for clients to not have to worry about that detail,
and share an implementation with other uses of MediaTransport1.
---
 profiles/audio/asha.c      | 88 +++++++++++++++++++++++++++++++++++---
 profiles/audio/asha.h      |  4 ++
 profiles/audio/transport.c | 28 +++++++++++-
 3 files changed, 112 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/profiles/audio/asha.c b/profiles/audio/asha.c
index c6769038f..a372e0db3 100644
--- a/profiles/audio/asha.c
+++ b/profiles/audio/asha.c
@@ -65,7 +65,9 @@  struct asha_device {
 	struct gatt_db *db;
 	struct gatt_db_attribute *attr;
 	uint16_t acp_handle;
-	unsigned int notify_id;
+	uint16_t volume_handle;
+	unsigned int status_notify_id;
+	unsigned int volume_notify_id;
 
 	uint16_t psm;
 	bool right_side;
@@ -75,6 +77,7 @@  struct asha_device {
 	uint8_t hisyncid[8];
 	uint16_t render_delay;
 	uint16_t codec_ids;
+	int8_t volume;
 
 	struct media_transport *transport;
 	int fd;
@@ -95,9 +98,15 @@  static struct asha_device *asha_device_new(void)
 
 static void asha_device_reset(struct asha_device *asha)
 {
-	if (asha->notify_id)
+	if (asha->status_notify_id) {
 		bt_gatt_client_unregister_notify(asha->client,
-				asha->notify_id);
+						asha->status_notify_id);
+	}
+
+	if (asha->volume_notify_id) {
+		bt_gatt_client_unregister_notify(asha->client,
+						asha->volume_notify_id);
+	}
 
 	gatt_db_unref(asha->db);
 	asha->db = NULL;
@@ -244,6 +253,7 @@  unsigned int asha_device_start(struct asha_device *asha, asha_cb_t cb,
 		0x01, // START
 		0x01, // G.722, 16 kHz
 		0,   // Unknown media type
+		asha->volume, // Volume
 		0,   // Other disconnected
 	};
 	int ret;
@@ -290,6 +300,24 @@  unsigned int asha_device_stop(struct asha_device *asha, asha_cb_t cb,
 	return asha->resume_id;
 }
 
+int8_t asha_device_get_volume(struct asha_device *asha)
+{
+	return asha->volume;
+}
+
+bool asha_device_set_volume(struct asha_device *asha, int8_t volume)
+{
+	if (!bt_gatt_client_write_value(asha->client, asha->volume_handle,
+				(const uint8_t *)&volume, 1, NULL, NULL,
+				NULL)) {
+		error("Error writing ACP start");
+		return false;
+	}
+
+	asha->volume = volume;
+	return true;
+}
+
 static char *make_endpoint_path(struct asha_device *asha)
 {
 	char *path;
@@ -410,6 +438,39 @@  void audio_status_notify(uint16_t value_handle, const uint8_t *value,
 	}
 }
 
+static void read_volume(bool success,
+			uint8_t att_ecode,
+			const uint8_t *value,
+			uint16_t length,
+			void *user_data)
+{
+	struct asha_device *asha = user_data;
+
+	if (!success) {
+		DBG("Reading volume failed with ATT errror: %u", att_ecode);
+		return;
+	}
+
+	if (length != 2) {
+		DBG("Reading volume failed: unexpected length %u", length);
+		return;
+	}
+
+	asha->volume = get_s8(value);
+
+	DBG("Got volume: %d", asha->volume);
+}
+
+void volume_notify(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct asha_device *asha = user_data;
+
+	asha->volume = get_s8(value);
+
+	DBG("Volume changed: %d", asha->volume);
+}
+
 static void handle_characteristic(struct gatt_db_attribute *attr,
 								void *user_data)
 {
@@ -430,16 +491,29 @@  static void handle_characteristic(struct gatt_db_attribute *attr,
 	} if (uuid_cmp(ASHA_CHRC_READ_ONLY_PROPERTIES_UUID, &uuid)) {
 		if (!bt_gatt_client_read_value(asha->client, value_handle,
 					read_rops, asha, NULL))
-			DBG("Failed to send request to read battery level");
+			DBG("Failed to send request for readonly properties");
 	} if (uuid_cmp(ASHA_CHRC_AUDIO_CONTROL_POINT_UUID, &uuid)) {
 		// Store this for later writes
 		asha->acp_handle = value_handle;
+	} if (uuid_cmp(ASHA_CHRC_VOLUME_UUID, &uuid)) {
+		// Store this for later reads and writes
+		asha->volume_handle = value_handle;
+		asha->volume_notify_id =
+			bt_gatt_client_register_notify(asha->client,
+				value_handle, NULL, volume_notify, asha,
+				NULL);
+		if (!asha->status_notify_id)
+			DBG("Failed to send request to notify volume");
+		if (!bt_gatt_client_read_value(asha->client, value_handle,
+					read_volume, asha, NULL))
+			DBG("Failed to send request to volume");
 	} if (uuid_cmp(ASHA_CHRC_AUDIO_STATUS_UUID, &uuid)) {
-		asha->notify_id = bt_gatt_client_register_notify(asha->client,
+		asha->status_notify_id =
+			bt_gatt_client_register_notify(asha->client,
 				value_handle, NULL, audio_status_notify, asha,
 				NULL);
-		if (!asha->notify_id)
-			DBG("Failed to send request to read battery level");
+		if (!asha->status_notify_id)
+			DBG("Failed to send request to notify AudioStatus");
 	} else {
 		char uuid_str[MAX_LEN_UUID_STR];
 
diff --git a/profiles/audio/asha.h b/profiles/audio/asha.h
index 0fc28e8a3..20bcaa65b 100644
--- a/profiles/audio/asha.h
+++ b/profiles/audio/asha.h
@@ -10,6 +10,7 @@ 
  *
  */
 
+#include <stdbool.h>
 #include <stdint.h>
 
 struct asha_device;
@@ -32,3 +33,6 @@  unsigned int asha_device_start(struct asha_device *asha, asha_cb_t cb,
 		void *user_data);
 unsigned int asha_device_stop(struct asha_device *asha, asha_cb_t cb,
 		void *user_data);
+
+int8_t asha_device_get_volume(struct asha_device *asha);
+bool asha_device_set_volume(struct asha_device *asha, int8_t volume);
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index a104d27c0..b85d96914 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -1841,6 +1841,32 @@  static guint transport_asha_suspend(struct media_transport *transport,
 	return ret;
 }
 
+static int8_t transport_asha_get_volume(struct media_transport *transport)
+{
+	struct asha_device *asha = transport->data;
+	int8_t volume;
+	int scaled_volume;
+
+	volume = asha_device_get_volume(asha);
+
+	// Convert -128-0 to 0-127
+	scaled_volume = ((((int) volume) + 128) * 127) / 128;
+
+	return scaled_volume;
+}
+
+static int transport_asha_set_volume(struct media_transport *transport,
+					int8_t volume)
+{
+	struct asha_device *asha = transport->data;
+	int scaled_volume;
+
+	// Convert 0-127 to -128-0
+	scaled_volume = ((((int) volume) * 128) / 127) - 128;
+
+	return asha_device_set_volume(asha, scaled_volume) ? 0 : -EIO;
+}
+
 static void *transport_asha_init(struct media_transport *transport, void *data)
 {
 	/* We just store the struct asha_device on the transport */
@@ -1893,7 +1919,7 @@  static void *transport_asha_init(struct media_transport *transport, void *data)
 			transport_asha_init, \
 			transport_asha_resume, transport_asha_suspend, \
 			NULL, NULL, NULL, \
-			NULL, NULL, \
+			transport_asha_get_volume, transport_asha_set_volume, \
 			NULL)
 
 static const struct media_transport_ops transport_ops[] = {