[PATCH 3/3] AVRCP: Add Passthrough Signal

Subsystems: the rest

STALE5499d

3 messages, 2 authors, 2011-08-24 · open the first message on its own page

[PATCH 3/3] AVRCP: Add Passthrough Signal

From: David Stockwell <hidden>
Date: 2011-08-24 03:27:11

AVRCP: Add Passthrough Signal

Send Passthrough signal, not only for simple keystrokes,
but especially for Vendor Unique key, passing company-id
and vendor-unique string as well.
---
 audio/control.c     |   90 
+++++++++++++++++++++++++++++++++++++++++++--------
 doc/control-api.txt |   14 +++-----
 2 files changed, 81 insertions(+), 23 deletions(-)
diff --git a/audio/control.c b/audio/control.c
index dd2930c..fcaad7c 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -106,6 +106,8 @@
 #define FORWARD_OP		0x4b
 #define BACKWARD_OP		0x4c
 
+#define VENDOR_UNIQUE_OP	0x7E
+
 /* Company IDs for vendor dependent commands */
 #define IEEEID_BTSIG		0x001958
 
@@ -515,28 +517,88 @@ static void send_key(int fd, uint16_t key, int pressed)
 	send_event(fd, EV_SYN, SYN_REPORT, 0);
 }
 
+/**
+ *	handle_panel_passthrough:
+ *
+ *	Handles AVRCP 1.0+ PASSTHROUGH command, passes the keystroke to uinput.
+ *
+ *	Added a Passthrough signal, with the key state and the optional
+ *	following company_id and vendor-unique message.
+ */
+
 static void handle_panel_passthrough(struct control *control,
-					const unsigned char *operands,
+					const uint8_t *operands,
 					int operand_count)
 {
 	const char *status;
-	int pressed, i;
-
-	if (operand_count == 0)
+	int i;
+	uint8_t key_pressed;
+	gboolean key_state;
+	uint32_t pass_company_id;
+	char *pass_string;
+	/*
+	 * operands[1] is operation_data_field_length (AV/C Panel Specification
+	 * v1.23, sect 9.4.5). Should always be present, even if zero.
+	 */
+	if (operand_count < 2)
 		return;
 
-	if (operands[0] & 0x80) {
-		status = "released";
-		pressed = 0;
+	key_pressed = operands[0] & 0x7F;
+
+	/* If key is pressed, key state bit is zero (AVRCP v13r00 p89). */
+	key_state = ((operands[0] & 0x80) == 0);
+	status = key_state ? "pressed" : "released";
+
+	DBG("Passthrough Key: %x %s", key_pressed, status);
+
+	if (key_pressed == VENDOR_UNIQUE_OP) {
+		if (operands[1] == 0 || operand_count < 5) {
+			pass_company_id = 0;
+			pass_string = g_malloc0(1);
+			DBG("Passthrough: No Company_ID or String");
+		} else if (operands[1] == 3 && operand_count == 5) {
+			pass_company_id = get_company_id(operands + 2);
+			pass_string = g_malloc0(1);
+			DBG("Passthrough Company_ID: %06X String: <none>",
+						pass_company_id);
+		} else if (operands[1] > 3 &&
+					operand_count == operands[1] + 2) {
+			pass_company_id = get_company_id(operands + 2);
+			pass_string = g_strndup((gchar *) operands + 5,
+							operands[1] - 3);
+			DBG("Passthrough Company_ID: %06X String: %s",
+						pass_company_id, pass_string);
+		} else { /* op_length does not match operand_count */
+			DBG("Passthrough: Malformed message");
+			DBG("op_len %u, op_cnt %u", operands[1], operand_count);
+			pass_company_id = 0;
+			pass_string = g_malloc0(1);
+		}
 	} else {
-		status = "pressed";
-		pressed = 1;
+		pass_company_id = 0;
+		pass_string = g_malloc0(1);
 	}
 
+	/*
+	 * Generate passthrough signal only if not BTSIG Company_ID.
+	 * For BTSIG, passthrough only for Group Navigation (unimplemented).
+	 */
+
+	if (pass_company_id != IEEEID_BTSIG)
+		g_dbus_emit_signal(control->dev->conn, control->dev->path,
+			   AUDIO_CONTROL_INTERFACE, "Passthrough",
+			   DBUS_TYPE_BYTE, &key_pressed,
+			   DBUS_TYPE_BOOLEAN, &key_state,
+			   DBUS_TYPE_UINT32, &pass_company_id,
+			   DBUS_TYPE_STRING, &pass_string,
+			   DBUS_TYPE_INVALID);
+
+	g_free(pass_string);
+
 	for (i = 0; key_map[i].name != NULL; i++) {
 		uint8_t key_quirks;
 
-		if ((operands[0] & 0x7F) != key_map[i].avrcp)
+		if (key_pressed != key_map[i].avrcp)
 			continue;
 
 		DBG("AVRCP: %s %s", key_map[i].name, status);
@@ -544,7 +606,7 @@ static void handle_panel_passthrough(struct control 
*control,
 		key_quirks = control->key_quirks[key_map[i].avrcp];
 
 		if (key_quirks & QUIRK_NO_RELEASE) {
-			if (!pressed) {
+			if (!key_state) {
 				DBG("AVRCP: Ignoring release");
 				break;
 			}
@@ -555,13 +617,12 @@ static void handle_panel_passthrough(struct control 
*control,
 			break;
 		}
 
-		send_key(control->uinput, key_map[i].uinput, pressed);
+		send_key(control->uinput, key_map[i].uinput, key_state);
 		break;
 	}
 
 	if (key_map[i].name == NULL)
-		DBG("AVRCP: unknown button 0x%02X %s",
-						operands[0] & 0x7F, status);
+		DBG("AVRCP: unknown button 0x%02X %s", key_pressed, status);
 }
 
 static unsigned int attr_get_max_val(uint8_t attr)
@@ -2291,6 +2352,7 @@ static GDBusSignalTable control_signals[] = {
 	{ "Connected",			"",	G_DBUS_SIGNAL_FLAG_DEPRECATED},
 	{ "Disconnected",		"",	G_DBUS_SIGNAL_FLAG_DEPRECATED},
 	{ "PropertyChanged",		"sv"	},
+	{ "Passthrough",		"ybus"	},
 	{ NULL, NULL }
 };
 
diff --git a/doc/control-api.txt b/doc/control-api.txt
index a7e5cbb..64ea5d3 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -55,18 +55,14 @@ Signals		Connected()
 			Sent when the AVRCP connection to the remote device
 			has been disconnected.
 
-		Passthrough(uint8 key, boolean state, int32 company_id,
+		Passthrough(uint8 key, boolean state, uint32 company_id,
 								string op_data)
 
-			Called when Passthrough command is received from
-			connected device.
+			Sent when Passthrough received from CT.
 
-			NOTE: according to the AV/C Subpanel Spec, company_id
-			and op_data are passed ONLY when the key is
-			"Vendor_Unique", or 0x7E.
-
-			When the key is NOT 0x7E, the signal returns
-			company_id=-1, and zero-length op_data.
+			Company_id and op_data returned only when key is 0x7E
+			(OP_VENDOR_UNIQUE).  Otherwise, returns zero for
+			company_id, and zero-length op_data.
 
 		VendorDependentReceived(string op_data)
 
-- 
1.7.3.4

Re: [PATCH 3/3] AVRCP: Add Passthrough Signal

From: Johan Hedberg <hidden>
Date: 2011-08-24 08:36:58

Hi David,

On Tue, Aug 23, 2011, David Stockwell wrote:
+	if (key_pressed == VENDOR_UNIQUE_OP) {
+		if (operands[1] == 0 || operand_count < 5) {
+			pass_company_id = 0;
+			pass_string = g_malloc0(1);
The above is a quite obscure way to allocate an empty string. Could you
change it to g_strdup(""). That requires a second or two less thinking
to figure out what the line is supposed to do :)
+		} else if (operands[1] == 3 && operand_count == 5) {
+			pass_company_id = get_company_id(operands + 2);
+			pass_string = g_malloc0(1);
Same here.
+		} else { /* op_length does not match operand_count */
+			DBG("Passthrough: Malformed message");
+			DBG("op_len %u, op_cnt %u", operands[1], operand_count);
+			pass_company_id = 0;
+			pass_string = g_malloc0(1);
And here.
+		}
 	} else {
-		status = "pressed";
-		pressed = 1;
+		pass_company_id = 0;
+		pass_string = g_malloc0(1);
And here.
+		g_dbus_emit_signal(control->dev->conn, control->dev->path,
+			   AUDIO_CONTROL_INTERFACE, "Passthrough",
+			   DBUS_TYPE_BYTE, &key_pressed,
+			   DBUS_TYPE_BOOLEAN, &key_state,
+			   DBUS_TYPE_UINT32, &pass_company_id,
+			   DBUS_TYPE_STRING, &pass_string,
+			   DBUS_TYPE_INVALID);
The above lines still mix tabs and spaces for indentation. Just use
tabs.

Johan

Re: [PATCH 3/3] AVRCP: Add Passthrough Signal

From: David Stockwell <hidden>
Date: 2011-08-24 12:37:51

Johan,

Good suggestions (I guess I am an old assembly-language programmer)

-----Original Message----- 
From: Johan Hedberg
Sent: Wednesday, August 24, 2011 3:36 AM
To: David Stockwell
Cc: linux-bluetooth@vger.kernel.org ; lucas.demarchi@profusion.mobi ; 
luiz.dentz@gmail.com
Subject: Re: [PATCH 3/3] AVRCP: Add Passthrough Signal

Hi David,

On Tue, Aug 23, 2011, David Stockwell wrote:
+ if (key_pressed == VENDOR_UNIQUE_OP) {
+ if (operands[1] == 0 || operand_count < 5) {
+ pass_company_id = 0;
+ pass_string = g_malloc0(1);
The above is a quite obscure way to allocate an empty string. Could you
change it to g_strdup(""). That requires a second or two less thinking
to figure out what the line is supposed to do :)

+++++OK
+ } else if (operands[1] == 3 && operand_count == 5) {
+ pass_company_id = get_company_id(operands + 2);
+ pass_string = g_malloc0(1);
Same here.
+ } else { /* op_length does not match operand_count */
+ DBG("Passthrough: Malformed message");
+ DBG("op_len %u, op_cnt %u", operands[1], operand_count);
+ pass_company_id = 0;
+ pass_string = g_malloc0(1);
And here. +++++
+ }
 } else {
- status = "pressed";
- pressed = 1;
+ pass_company_id = 0;
+ pass_string = g_malloc0(1);
And here.
+ g_dbus_emit_signal(control->dev->conn, control->dev->path,
+    AUDIO_CONTROL_INTERFACE, "Passthrough",
+    DBUS_TYPE_BYTE, &key_pressed,
+    DBUS_TYPE_BOOLEAN, &key_state,
+    DBUS_TYPE_UINT32, &pass_company_id,
+    DBUS_TYPE_STRING, &pass_string,
+    DBUS_TYPE_INVALID);
The above lines still mix tabs and spaces for indentation. Just use
tabs.

++++ Sorry, thought I had fixed this in Kate.  checkpatch.pl did not catch 
it...will fix.

Johan 
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help