Diff between 65e27905fe4ea833ae9e33a66a0f909e835258e6 and f25568f3b6ca0a575abb2faf700d99d26baad86a

Changed Files

File Additions Deletions Status
audio/control.c +97 -2 modified

Full Patch

diff --git a/audio/control.c b/audio/control.c
index 7fe4af5..87dcdbf 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -102,6 +102,21 @@
 #define FORWARD_OP		0x4b
 #define BACKWARD_OP		0x4c
 
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+/* Error codes for metadata transfer */
+#define E_INVALID_COMMAND	0x00
+#define E_INVALID_PARAM		0x01
+#define E_PARAM_NOT_FOUND	0x02
+#define E_INTERNAL		0x03
+
+/* PDU types for metadata transfer */
+#define AVRCP_GET_CAPABILITIES		0x10
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID		0x02
+
 #define QUIRK_NO_RELEASE	1 << 0
 
 enum player_setting {
@@ -277,6 +292,11 @@ static struct {
 	{ NULL }
 };
 
+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+	IEEEID_BTSIG,
+};
+
 static GSList *avctp_callbacks = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
@@ -624,13 +644,88 @@ static void mp_set_media_attributes(struct control *control,
 			   mi->ntracks, mi->track, mi->track_len);
 }
 
+static int avrcp_handle_get_capabilities(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 1)
+		goto err;
+
+	DBG("id=%u", pdu->params[0]);
+
+	switch (pdu->params[0]) {
+	case CAP_COMPANY_ID:
+		for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
+			pdu->params[2 + i * 3] = company_ids[i] >> 16;
+			pdu->params[3 + i * 3] = (company_ids[i] >> 8) & 0xFF;
+			pdu->params[4 + i * 3] = company_ids[i] & 0xFF;
+		}
+
+		pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
+		pdu->params[1] = G_N_ELEMENTS(company_ids);
+
+		return 2 + (3 * G_N_ELEMENTS(company_ids));
+	}
+
+err:
+	pdu->params[0] = E_INVALID_PARAM;
+	return -EINVAL;
+}
+
 /* handle vendordep pdu inside an avctp packet */
 static int handle_vendordep_pdu(struct control *control,
 					struct avrcp_header *avrcp,
 					int operand_count)
 {
-	avrcp->code = CTYPE_NOT_IMPLEMENTED;
-	return AVRCP_HEADER_LENGTH;
+	struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
+	uint32_t company_id = (pdu->company_id[0] << 16) |
+				(pdu->company_id[1] << 8) |
+				(pdu->company_id[2]);
+	int len;
+
+	if (company_id != IEEEID_BTSIG ||
+				pdu->packet_type != AVCTP_PACKET_SINGLE) {
+		avrcp->code = CTYPE_NOT_IMPLEMENTED;
+		return AVRCP_HEADER_LENGTH;
+	}
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
+		pdu->params[0] = E_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	switch (pdu->pdu_id) {
+	case AVRCP_GET_CAPABILITIES:
+		if (avrcp->code != CTYPE_STATUS) {
+			pdu->params[0] = E_INVALID_COMMAND;
+			goto err_metadata;
+		}
+
+		len = avrcp_handle_get_capabilities(control, pdu);
+		if (len < 0)
+			goto err_metadata;
+
+		avrcp->code = CTYPE_STABLE;
+
+		break;
+	default:
+		/* Invalid pdu_id */
+		pdu->params[0] = E_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;
+
+err_metadata:
+	avrcp->code = CTYPE_REJECTED;
+	pdu->params_len = htons(1);
+
+	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
 }
 
 static void avctp_disconnected(struct audio_device *dev)