All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling
@ 2011-09-12 11:29 Luiz Augusto von Dentz
  2011-09-12 11:29 ` [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header Luiz Augusto von Dentz
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 11:29 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This simplify a bit the handling by introducing common checks before
calling the handler callback, it is also much easier to add/remove
new PDUs in this way.
---
 audio/control.c |  321 +++++++++++++++++++++----------------------------------
 1 files changed, 124 insertions(+), 197 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 9990b06..f84c7f7 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -986,8 +986,9 @@ static void mp_set_media_attributes(struct control *control,
 	avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
 }
 
-static int avrcp_handle_get_capabilities(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_capabilities(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	unsigned int i;
@@ -1008,31 +1009,35 @@ static int avrcp_handle_get_capabilities(struct control *control,
 		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));
+		return CTYPE_STABLE;
 	case CAP_EVENTS_SUPPORTED:
 		pdu->params_len = htons(4);
 		pdu->params[1] = 2;
 		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
 		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
 
-		return 4;
+		return CTYPE_STABLE;
 	}
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_list_player_attributes(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_list_player_attributes(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	struct media_player *mp = control->mp;
 	unsigned int i;
 
 	if (len != 0) {
+		pdu->params_len = htons(1);
 		pdu->params[0] = E_INVALID_PARAM;
-		return -EINVAL;
+		return CTYPE_REJECTED;
 	}
 
 	if (!mp)
@@ -1052,11 +1057,12 @@ done:
 	pdu->params[0] = len;
 	pdu->params_len = htons(len + 1);
 
-	return len + 1;
+	return CTYPE_STABLE;
 }
 
-static int avrcp_handle_list_player_values(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_list_player_values(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	struct media_player *mp = control->mp;
@@ -1077,15 +1083,17 @@ static int avrcp_handle_list_player_values(struct control *control,
 	pdu->params[0] = len;
 	pdu->params_len = htons(len + 1);
 
-	return len + 1;
+	return CTYPE_STABLE;
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_get_element_attributes(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_element_attributes(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	uint64_t *identifier = (void *) &pdu->params[0];
@@ -1145,14 +1153,16 @@ done:
 	pdu->params[0] = len;
 	pdu->params_len = htons(pos);
 
-	return pos;
+	return CTYPE_STABLE;
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_get_current_player_value(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_current_player_value(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	struct media_player *mp = control->mp;
@@ -1202,19 +1212,21 @@ static int avrcp_handle_get_current_player_value(struct control *control,
 		pdu->params[0] = len;
 		pdu->params_len = htons(2 * len + 1);
 
-		return 2 * len + 1;
+		return CTYPE_STABLE;
 	}
 
 	error("No valid attributes in request");
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
 
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_set_player_value(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_set_player_value(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	unsigned int i;
@@ -1256,16 +1268,38 @@ static int avrcp_handle_set_player_value(struct control *control,
 	if (len) {
 		pdu->params_len = 0;
 
-		return 0;
+		return CTYPE_STABLE;
 	}
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_ct_battery_status(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_displayable_charset(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+
+	if (len < 3) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = E_INVALID_PARAM;
+		return CTYPE_REJECTED;
+	}
+
+	/*
+	 * We acknowledge the commands, but we always use UTF-8 for
+	 * encoding since CT is obliged to support it.
+	 */
+	pdu->params_len = 0;
+	return CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_ct_battery_status(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	const char *valstr;
@@ -1282,15 +1316,17 @@ static int avrcp_handle_ct_battery_status(struct control *control,
 					DBUS_TYPE_STRING, &valstr);
 	pdu->params_len = 0;
 
-	return 0;
+	return CTYPE_STABLE;
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
-static int avrcp_handle_get_play_status(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_play_status(struct control *control,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
 	uint32_t elapsed;
@@ -1298,8 +1334,9 @@ static int avrcp_handle_get_play_status(struct control *control,
 	uint8_t status;
 
 	if (len != 0) {
+		pdu->params_len = htons(1);
 		pdu->params[0] = E_INVALID_PARAM;
-		return -EINVAL;
+		return CTYPE_REJECTED;
 	}
 
 	if (control->mp) {
@@ -1319,10 +1356,10 @@ static int avrcp_handle_get_play_status(struct control *control,
 
 	pdu->params_len = htons(9);
 
-	return 9;
+	return CTYPE_STABLE;
 }
 
-static int avrcp_handle_register_notification(struct control *control,
+static uint8_t avrcp_handle_register_notification(struct control *control,
 						struct avrcp_spec_avc_pdu *pdu,
 						uint8_t transaction)
 {
@@ -1369,24 +1406,59 @@ static int avrcp_handle_register_notification(struct control *control,
 
 	pdu->params_len = htons(len);
 
-	return len;
+	return CTYPE_INTERIM;
 
 err:
+	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return -EINVAL;
+	return CTYPE_REJECTED;
 }
 
+static struct pdu_handler {
+	uint8_t pdu_id;
+	uint8_t code;
+	uint8_t (*func) (struct control *control,
+					struct avrcp_spec_avc_pdu *pdu,
+					uint8_t transaction);
+} handlers[] = {
+		{ AVRCP_GET_CAPABILITIES, CTYPE_STATUS,
+					avrcp_handle_get_capabilities },
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES, CTYPE_STATUS,
+					avrcp_handle_list_player_attributes },
+		{ AVRCP_LIST_PLAYER_VALUES, CTYPE_STATUS,
+					avrcp_handle_list_player_values },
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES, CTYPE_STATUS,
+					avrcp_handle_get_element_attributes },
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE, CTYPE_STATUS,
+					avrcp_handle_get_current_player_value },
+		{ AVRCP_SET_PLAYER_VALUE, CTYPE_CONTROL,
+					avrcp_handle_set_player_value },
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, CTYPE_STATUS,
+					NULL },
+		{ AVRCP_GET_PLAYER_VALUE_TEXT, CTYPE_STATUS,
+					NULL },
+		{ AVRCP_DISPLAYABLE_CHARSET, CTYPE_STATUS,
+					avrcp_handle_displayable_charset },
+		{ AVRCP_CT_BATTERY_STATUS, CTYPE_STATUS,
+					avrcp_handle_ct_battery_status },
+		{ AVRCP_GET_PLAY_STATUS, CTYPE_STATUS,
+					avrcp_handle_get_play_status },
+		{ AVRCP_REGISTER_NOTIFICATION, CTYPE_NOTIFY,
+					avrcp_handle_register_notification },
+		{ },
+};
+
 /* handle vendordep pdu inside an avctp packet */
 static int handle_vendordep_pdu(struct control *control,
 					struct avctp_header *avctp,
 					struct avrcp_header *avrcp,
 					int operand_count)
 {
+	struct pdu_handler *handler;
 	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) {
@@ -1397,177 +1469,32 @@ static int handle_vendordep_pdu(struct control *control,
 	pdu->packet_type = 0;
 	pdu->rsvd = 0;
 
-	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
-		pdu->params[0] = E_INVALID_COMMAND;
+	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH)
 		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;
-	case AVRCP_LIST_PLAYER_ATTRIBUTES:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
 
-		len = avrcp_handle_list_player_attributes(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_LIST_PLAYER_VALUES:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_list_player_values(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_GET_ELEMENT_ATTRIBUTES:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_get_element_attributes(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_GET_CURRENT_PLAYER_VALUE:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_get_current_player_value(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_SET_PLAYER_VALUE:
-		if (avrcp->code != CTYPE_CONTROL) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_set_player_value(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
-	case AVRCP_GET_PLAYER_VALUE_TEXT:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
+	for (handler = handlers; handler; handler++) {
+		if (handler->pdu_id == pdu->pdu_id)
+			break;
+	}
 
-		/*
-		 * As per sec. 5.2.5 of AVRCP 1.3 spec, this command is
-		 * expected to be used only for extended attributes, i.e.
-		 * custom attributes defined by the application. As we
-		 * currently don't have any such attribute, we respond with
-		 * invalid param id.
-		 */
-		pdu->params[0] = E_INVALID_PARAM;
+	if (!handler || handler->code != avrcp->code) {
+		pdu->params[0] = E_INVALID_COMMAND;
 		goto err_metadata;
-	case AVRCP_DISPLAYABLE_CHARSET:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		if (pdu->params[0] < 3) {
-			pdu->params[0] = E_INVALID_PARAM;
-			goto err_metadata;
-		}
-
-		/*
-		 * We acknowledge the commands, but we always use UTF-8 for
-		 * encoding since CT is obliged to support it.
-		 */
-		pdu->params_len = 0;
-		avrcp->code = CTYPE_STABLE;
-		len = 0;
-
-		break;
-	case AVRCP_CT_BATTERY_STATUS:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_ct_battery_status(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_GET_PLAY_STATUS:
-		if (avrcp->code != CTYPE_STATUS) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_get_play_status(control, pdu);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_STABLE;
-
-		break;
-	case AVRCP_REGISTER_NOTIFICATION:
-		if (avrcp->code != CTYPE_NOTIFY) {
-			pdu->params[0] = E_INVALID_COMMAND;
-			goto err_metadata;
-		}
-
-		len = avrcp_handle_register_notification(control, pdu,
-							avctp->transaction);
-		if (len < 0)
-			goto err_metadata;
-
-		avrcp->code = CTYPE_INTERIM;
+	}
 
-		break;
-	default:
-		/* Invalid pdu_id */
-		pdu->params[0] = E_INVALID_COMMAND;
+	if (!handler->func) {
+		pdu->params[0] = E_INVALID_PARAM;
 		goto err_metadata;
 	}
 
-	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;
+	avrcp->code = handler->func(control, pdu, avctp->transaction);
+
+	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
+						ntohs(pdu->params_len);
 
 err_metadata:
-	avrcp->code = CTYPE_REJECTED;
 	pdu->params_len = htons(1);
+	avrcp->code = CTYPE_REJECTED;
 
 	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
 }
-- 
1.7.6.1


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

* [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
@ 2011-09-12 11:29 ` Luiz Augusto von Dentz
  2011-09-12 13:10   ` Lucas De Marchi
  2011-09-12 11:29 ` [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 11:29 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

AVCTP carries AV/C packets/PDUs not AVRCP as avrcp_header suggests.
---
 audio/control.c |   98 +++++++++++++++++++++++++++---------------------------
 1 files changed, 49 insertions(+), 49 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index f84c7f7..37b0806 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -212,14 +212,14 @@ struct avctp_header {
 } __attribute__ ((packed));
 #define AVCTP_HEADER_LENGTH 3
 
-struct avrcp_header {
+struct avc_header {
 	uint8_t code:4;
 	uint8_t _hdr0:4;
 	uint8_t subunit_id:3;
 	uint8_t subunit_type:5;
 	uint8_t opcode;
 } __attribute__ ((packed));
-#define AVRCP_HEADER_LENGTH 3
+#define AVC_HEADER_LENGTH 3
 
 struct avrcp_spec_avc_pdu {
 	uint8_t company_id[3];
@@ -242,14 +242,14 @@ struct avctp_header {
 } __attribute__ ((packed));
 #define AVCTP_HEADER_LENGTH 3
 
-struct avrcp_header {
+struct avc_header {
 	uint8_t _hdr0:4;
 	uint8_t code:4;
 	uint8_t subunit_type:5;
 	uint8_t subunit_id:3;
 	uint8_t opcode;
 } __attribute__ ((packed));
-#define AVRCP_HEADER_LENGTH 3
+#define AVC_HEADER_LENGTH 3
 
 struct avrcp_spec_avc_pdu {
 	uint8_t company_id[3];
@@ -721,13 +721,13 @@ static const char *battery_status_to_str(enum battery_status status)
 
 static int avctp_send_event(struct control *control, uint8_t id, void *data)
 {
-	uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+	uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
 					AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
 	struct avctp_header *avctp = (void *) buf;
-	struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
+	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
 	struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
-							AVRCP_HEADER_LENGTH];
-	int sk;
+							AVC_HEADER_LENGTH];
+	int sk = g_io_channel_unix_get_fd(control->io);
 	uint16_t size;
 
 	if (control->state != AVCTP_STATE_CONNECTED)
@@ -743,9 +743,9 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
 	avctp->cr = AVCTP_RESPONSE;
 	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
 
-	avrcp->code = CTYPE_CHANGED;
-	avrcp->subunit_type = SUBUNIT_PANEL;
-	avrcp->opcode = OP_VENDORDEP;
+	avc->code = CTYPE_CHANGED;
+	avc->subunit_type = SUBUNIT_PANEL;
+	avc->opcode = OP_VENDORDEP;
 
 	pdu->company_id[0] = IEEEID_BTSIG >> 16;
 	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
@@ -780,7 +780,7 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
 	}
 
 	pdu->params_len = htons(size);
-	size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+	size += AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
 					AVRCP_SPECAVCPDU_HEADER_LENGTH;
 
 	sk = g_io_channel_unix_get_fd(control->io);
@@ -1451,19 +1451,19 @@ static struct pdu_handler {
 /* handle vendordep pdu inside an avctp packet */
 static int handle_vendordep_pdu(struct control *control,
 					struct avctp_header *avctp,
-					struct avrcp_header *avrcp,
+					struct avc_header *avc,
 					int operand_count)
 {
 	struct pdu_handler *handler;
-	struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
+	struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
 	uint32_t company_id = (pdu->company_id[0] << 16) |
 				(pdu->company_id[1] << 8) |
 				(pdu->company_id[2]);
 
 	if (company_id != IEEEID_BTSIG ||
 				pdu->packet_type != AVCTP_PACKET_SINGLE) {
-		avrcp->code = CTYPE_NOT_IMPLEMENTED;
-		return AVRCP_HEADER_LENGTH;
+		avc->code = CTYPE_NOT_IMPLEMENTED;
+		return AVC_HEADER_LENGTH;
 	}
 
 	pdu->packet_type = 0;
@@ -1477,7 +1477,7 @@ static int handle_vendordep_pdu(struct control *control,
 			break;
 	}
 
-	if (!handler || handler->code != avrcp->code) {
+	if (!handler || handler->code != avc->code) {
 		pdu->params[0] = E_INVALID_COMMAND;
 		goto err_metadata;
 	}
@@ -1487,16 +1487,16 @@ static int handle_vendordep_pdu(struct control *control,
 		goto err_metadata;
 	}
 
-	avrcp->code = handler->func(control, pdu, avctp->transaction);
+	avc->code = handler->func(control, pdu, avctp->transaction);
 
-	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
+	return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
 						ntohs(pdu->params_len);
 
 err_metadata:
 	pdu->params_len = htons(1);
-	avrcp->code = CTYPE_REJECTED;
+	avc->code = CTYPE_REJECTED;
 
-	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
+	return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
 }
 
 static void avctp_disconnected(struct audio_device *dev)
@@ -1593,7 +1593,7 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
 	struct control *control = data;
 	unsigned char buf[1024], *operands;
 	struct avctp_header *avctp;
-	struct avrcp_header *avrcp;
+	struct avc_header *avc;
 	int ret, packet_size, operand_count, sock;
 
 	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
@@ -1622,65 +1622,65 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
 			avctp->cr, avctp->ipid, ntohs(avctp->pid));
 
 	ret -= sizeof(struct avctp_header);
-	if ((unsigned int) ret < sizeof(struct avrcp_header)) {
+	if ((unsigned int) ret < sizeof(struct avc_header)) {
 		error("Too small AVRCP packet");
 		goto failed;
 	}
 
-	avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header));
+	avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
 
-	ret -= sizeof(struct avrcp_header);
+	ret -= sizeof(struct avc_header);
 
-	operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header);
+	operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
 	operand_count = ret;
 
-	DBG("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
+	DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
 			"opcode 0x%02X, %d operands",
 			avctp->cr ? "response" : "command",
-			avrcp->code, avrcp->subunit_type, avrcp->subunit_id,
-			avrcp->opcode, operand_count);
+			avc->code, avc->subunit_type, avc->subunit_id,
+			avc->opcode, operand_count);
 
 	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
 		avctp->cr = AVCTP_RESPONSE;
-		avrcp->code = CTYPE_NOT_IMPLEMENTED;
+		avc->code = CTYPE_NOT_IMPLEMENTED;
 	} else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
 		avctp->ipid = 1;
 		avctp->cr = AVCTP_RESPONSE;
 		packet_size = sizeof(*avctp);
 	} else if (avctp->cr == AVCTP_COMMAND &&
-			avrcp->code == CTYPE_CONTROL &&
-			avrcp->subunit_type == SUBUNIT_PANEL &&
-			avrcp->opcode == OP_PASSTHROUGH) {
+			avc->code == CTYPE_CONTROL &&
+			avc->subunit_type == SUBUNIT_PANEL &&
+			avc->opcode == OP_PASSTHROUGH) {
 		handle_panel_passthrough(control, operands, operand_count);
 		avctp->cr = AVCTP_RESPONSE;
-		avrcp->code = CTYPE_ACCEPTED;
+		avc->code = CTYPE_ACCEPTED;
 	} else if (avctp->cr == AVCTP_COMMAND &&
-			avrcp->code == CTYPE_STATUS &&
-			(avrcp->opcode == OP_UNITINFO
-			|| avrcp->opcode == OP_SUBUNITINFO)) {
+			avc->code == CTYPE_STATUS &&
+			(avc->opcode == OP_UNITINFO
+			|| avc->opcode == OP_SUBUNITINFO)) {
 		avctp->cr = AVCTP_RESPONSE;
-		avrcp->code = CTYPE_STABLE;
+		avc->code = CTYPE_STABLE;
 		/* The first operand should be 0x07 for the UNITINFO response.
 		 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
 		 * Interface Command Set (section 9.2.1, page 45) specs
 		 * explain this value but both use it */
-		if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO)
+		if (operand_count >= 1 && avc->opcode == OP_UNITINFO)
 			operands[0] = 0x07;
 		if (operand_count >= 2)
 			operands[1] = SUBUNIT_PANEL << 3;
-		DBG("reply to %s", avrcp->opcode == OP_UNITINFO ?
+		DBG("reply to %s", avc->opcode == OP_UNITINFO ?
 				"OP_UNITINFO" : "OP_SUBUNITINFO");
 	} else if (avctp->cr == AVCTP_COMMAND &&
-			avrcp->opcode == OP_VENDORDEP) {
+			avc->opcode == OP_VENDORDEP) {
 		int r_size;
 		operand_count -= 3;
 		avctp->cr = AVCTP_RESPONSE;
-		r_size = handle_vendordep_pdu(control, avctp, avrcp,
+		r_size = handle_vendordep_pdu(control, avctp, avc,
 								operand_count);
 		packet_size = AVCTP_HEADER_LENGTH + r_size;
 	} else {
 		avctp->cr = AVCTP_RESPONSE;
-		avrcp->code = CTYPE_REJECTED;
+		avc->code = CTYPE_REJECTED;
 	}
 
 	ret = write(sock, buf, packet_size);
@@ -2092,10 +2092,10 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
 
 static int avctp_send_passthrough(struct control *control, uint8_t op)
 {
-	unsigned char buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH + 2];
+	unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
 	struct avctp_header *avctp = (void *) buf;
-	struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
-	uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH];
+	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+	uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
 	int sk = g_io_channel_unix_get_fd(control->io);
 	static uint8_t transaction = 0;
 
@@ -2106,9 +2106,9 @@ static int avctp_send_passthrough(struct control *control, uint8_t op)
 	avctp->cr = AVCTP_COMMAND;
 	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
 
-	avrcp->code = CTYPE_CONTROL;
-	avrcp->subunit_type = SUBUNIT_PANEL;
-	avrcp->opcode = OP_PASSTHROUGH;
+	avc->code = CTYPE_CONTROL;
+	avc->subunit_type = SUBUNIT_PANEL;
+	avc->opcode = OP_PASSTHROUGH;
 
 	operands[0] = op & 0x7f;
 	operands[1] = 0;
-- 
1.7.6.1


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

* [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header
  2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
  2011-09-12 11:29 ` [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header Luiz Augusto von Dentz
@ 2011-09-12 11:29 ` Luiz Augusto von Dentz
  2011-09-12 13:47   ` Lucas De Marchi
  2011-09-12 11:29 ` [PATCH BlueZ 4/5] AVRCP: split AVCTP specific code from control.c Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 11:29 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Since old avrcp_header got renamed to avc_header now we can use
avrcp_header for AVRCP PDUs.
---
 audio/control.c |   44 ++++++++++++++++++++++----------------------
 1 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 37b0806..73130e4 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -221,7 +221,7 @@ struct avc_header {
 } __attribute__ ((packed));
 #define AVC_HEADER_LENGTH 3
 
-struct avrcp_spec_avc_pdu {
+struct avrcp_header {
 	uint8_t company_id[3];
 	uint8_t pdu_id;
 	uint8_t packet_type:2;
@@ -229,7 +229,7 @@ struct avrcp_spec_avc_pdu {
 	uint16_t params_len;
 	uint8_t params[0];
 } __attribute__ ((packed));
-#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7
+#define AVRCP_HEADER_LENGTH 7
 
 #elif __BYTE_ORDER == __BIG_ENDIAN
 
@@ -251,7 +251,7 @@ struct avc_header {
 } __attribute__ ((packed));
 #define AVC_HEADER_LENGTH 3
 
-struct avrcp_spec_avc_pdu {
+struct avrcp_header {
 	uint8_t company_id[3];
 	uint8_t pdu_id;
 	uint8_t rsvd:6;
@@ -259,7 +259,7 @@ struct avrcp_spec_avc_pdu {
 	uint16_t params_len;
 	uint8_t params[0];
 } __attribute__ ((packed));
-#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7
+#define AVRCP_HEADER_LENGTH 7
 
 #else
 #error "Unknown byte order"
@@ -722,10 +722,10 @@ static const char *battery_status_to_str(enum battery_status status)
 static int avctp_send_event(struct control *control, uint8_t id, void *data)
 {
 	uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
-					AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
+					AVRCP_HEADER_LENGTH + 9];
 	struct avctp_header *avctp = (void *) buf;
 	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
-	struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
+	struct avrcp_header *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
 							AVC_HEADER_LENGTH];
 	int sk = g_io_channel_unix_get_fd(control->io);
 	uint16_t size;
@@ -781,7 +781,7 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
 
 	pdu->params_len = htons(size);
 	size += AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
-					AVRCP_SPECAVCPDU_HEADER_LENGTH;
+					AVRCP_HEADER_LENGTH;
 
 	sk = g_io_channel_unix_get_fd(control->io);
 
@@ -987,7 +987,7 @@ static void mp_set_media_attributes(struct control *control,
 }
 
 static uint8_t avrcp_handle_get_capabilities(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1027,7 +1027,7 @@ err:
 }
 
 static uint8_t avrcp_handle_list_player_attributes(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1061,7 +1061,7 @@ done:
 }
 
 static uint8_t avrcp_handle_list_player_values(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1092,7 +1092,7 @@ err:
 }
 
 static uint8_t avrcp_handle_get_element_attributes(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1161,7 +1161,7 @@ err:
 }
 
 static uint8_t avrcp_handle_get_current_player_value(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1225,7 +1225,7 @@ err:
 }
 
 static uint8_t avrcp_handle_set_player_value(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1278,7 +1278,7 @@ err:
 }
 
 static uint8_t avrcp_handle_displayable_charset(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1298,7 +1298,7 @@ static uint8_t avrcp_handle_displayable_charset(struct control *control,
 }
 
 static uint8_t avrcp_handle_ct_battery_status(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1325,7 +1325,7 @@ err:
 }
 
 static uint8_t avrcp_handle_get_play_status(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1360,7 +1360,7 @@ static uint8_t avrcp_handle_get_play_status(struct control *control,
 }
 
 static uint8_t avrcp_handle_register_notification(struct control *control,
-						struct avrcp_spec_avc_pdu *pdu,
+						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
 	uint16_t len = ntohs(pdu->params_len);
@@ -1418,7 +1418,7 @@ static struct pdu_handler {
 	uint8_t pdu_id;
 	uint8_t code;
 	uint8_t (*func) (struct control *control,
-					struct avrcp_spec_avc_pdu *pdu,
+					struct avrcp_header *pdu,
 					uint8_t transaction);
 } handlers[] = {
 		{ AVRCP_GET_CAPABILITIES, CTYPE_STATUS,
@@ -1455,7 +1455,7 @@ static int handle_vendordep_pdu(struct control *control,
 					int operand_count)
 {
 	struct pdu_handler *handler;
-	struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
+	struct avrcp_header *pdu = (void *) avc + AVC_HEADER_LENGTH;
 	uint32_t company_id = (pdu->company_id[0] << 16) |
 				(pdu->company_id[1] << 8) |
 				(pdu->company_id[2]);
@@ -1469,7 +1469,7 @@ static int handle_vendordep_pdu(struct control *control,
 	pdu->packet_type = 0;
 	pdu->rsvd = 0;
 
-	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH)
+	if (operand_count + 3 < AVRCP_HEADER_LENGTH)
 		goto err_metadata;
 
 	for (handler = handlers; handler; handler++) {
@@ -1489,14 +1489,14 @@ static int handle_vendordep_pdu(struct control *control,
 
 	avc->code = handler->func(control, pdu, avctp->transaction);
 
-	return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
+	return AVC_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
 						ntohs(pdu->params_len);
 
 err_metadata:
 	pdu->params_len = htons(1);
 	avc->code = CTYPE_REJECTED;
 
-	return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
+	return AVC_HEADER_LENGTH + AVRCP_HEADER_LENGTH + 1;
 }
 
 static void avctp_disconnected(struct audio_device *dev)
-- 
1.7.6.1


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

* [PATCH BlueZ 4/5] AVRCP: split AVCTP specific code from control.c
  2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
  2011-09-12 11:29 ` [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header Luiz Augusto von Dentz
  2011-09-12 11:29 ` [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header Luiz Augusto von Dentz
@ 2011-09-12 11:29 ` Luiz Augusto von Dentz
  2011-09-12 11:29 ` [PATCH BlueZ 5/5] AVRCP: move handling of vendor dependent PDU from control.c to avrcp.c Luiz Augusto von Dentz
  2011-09-12 13:25 ` [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Lucas De Marchi
  4 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 11:29 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

The code is moved to avctp.c to simplify control.c
---
 Makefile.am     |    1 +
 audio/avctp.c   | 1028 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 audio/avctp.h   |   95 +++++
 audio/control.c |  875 +++++++----------------------------------------
 audio/control.h |   14 -
 audio/device.c  |    1 +
 6 files changed, 1242 insertions(+), 772 deletions(-)
 create mode 100644 audio/avctp.c
 create mode 100644 audio/avctp.h

diff --git a/Makefile.am b/Makefile.am
index f4113b1..ef546d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -143,6 +143,7 @@ builtin_sources += audio/main.c \
 			audio/gateway.h audio/gateway.c \
 			audio/headset.h audio/headset.c \
 			audio/control.h audio/control.c \
+			audio/avctp.h audio/avctp.c \
 			audio/device.h audio/device.c \
 			audio/source.h audio/source.c \
 			audio/sink.h audio/sink.c \
diff --git a/audio/avctp.c b/audio/avctp.c
new file mode 100644
index 0000000..9608d6f
--- /dev/null
+++ b/audio/avctp.c
@@ -0,0 +1,1028 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+
+#include "adapter.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "error.h"
+#include "uinput.h"
+#include "btio.h"
+#include "manager.h"
+#include "device.h"
+#include "avctp.h"
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND		0
+#define AVCTP_RESPONSE		1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE	0
+#define AVCTP_PACKET_START	1
+#define AVCTP_PACKET_CONTINUE	2
+#define AVCTP_PACKET_END	3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+	uint8_t ipid:1;
+	uint8_t cr:1;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+	uint8_t code:4;
+	uint8_t _hdr0:4;
+	uint8_t subunit_id:3;
+	uint8_t subunit_type:5;
+	uint8_t opcode;
+} __attribute__ ((packed));
+#define AVC_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t cr:1;
+	uint8_t ipid:1;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+	uint8_t _hdr0:4;
+	uint8_t code:4;
+	uint8_t subunit_type:5;
+	uint8_t subunit_id:3;
+	uint8_t opcode;
+} __attribute__ ((packed));
+#define AVC_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_state_callback {
+	avctp_state_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_server {
+	bdaddr_t src;
+	GIOChannel *io;
+	GSList *sessions;
+};
+
+struct avctp {
+	struct avctp_server *server;
+	bdaddr_t dst;
+
+	avctp_state_t state;
+
+	int uinput;
+
+	GIOChannel *io;
+	guint io_id;
+
+	uint16_t mtu;
+
+	uint8_t key_quirks[256];
+};
+
+struct avctp_pdu_handler {
+	uint8_t opcode;
+	avctp_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+static struct {
+	const char *name;
+	uint8_t avc;
+	uint16_t uinput;
+} key_map[] = {
+	{ "PLAY",		PLAY_OP,		KEY_PLAYCD },
+	{ "STOP",		STAVC_OP_OP,		KEY_STOPCD },
+	{ "PAUSE",		PAUSE_OP,		KEY_PAUSECD },
+	{ "FORWARD",		FORWARD_OP,		KEY_NEXTSONG },
+	{ "BACKWARD",		BACKWARD_OP,		KEY_PREVIOUSSONG },
+	{ "REWIND",		REWIND_OP,		KEY_REWIND },
+	{ "FAST FORWARD",	FAST_FORWARD_OP,	KEY_FASTFORWARD },
+	{ NULL }
+};
+
+static GSList *callbacks = NULL;
+static GSList *servers = NULL;
+static GSList *handlers = NULL;
+
+static void auth_cb(DBusError *derr, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+	struct uinput_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type	= type;
+	event.code	= code;
+	event.value	= value;
+
+	return write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+	if (fd < 0)
+		return;
+
+	send_event(fd, EV_KEY, key, pressed);
+	send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static size_t handle_panel_passthrough(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	const char *status;
+	int pressed, i;
+
+	if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	if (operand_count == 0)
+		goto done;
+
+	if (operands[0] & 0x80) {
+		status = "released";
+		pressed = 0;
+	} else {
+		status = "pressed";
+		pressed = 1;
+	}
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		uint8_t key_quirks;
+
+		if ((operands[0] & 0x7F) != key_map[i].avc)
+			continue;
+
+		DBG("AVRCP: %s %s", key_map[i].name, status);
+
+		key_quirks = session->key_quirks[key_map[i].avc];
+
+		if (key_quirks & QUIRK_NO_RELEASE) {
+			if (!pressed) {
+				DBG("AVRCP: Ignoring release");
+				break;
+			}
+
+			DBG("AVRCP: treating key press as press + release");
+			send_key(session->uinput, key_map[i].uinput, 1);
+			send_key(session->uinput, key_map[i].uinput, 0);
+			break;
+		}
+
+		send_key(session->uinput, key_map[i].uinput, pressed);
+		break;
+	}
+
+	if (key_map[i].name == NULL)
+		DBG("AVRCP: unknown button 0x%02X %s",
+						operands[0] & 0x7F, status);
+
+done:
+	*code = AVC_CTYPE_ACCEPTED;
+	return operand_count;
+}
+
+static size_t handle_unit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/* The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it */
+	if (operand_count >= 1)
+		operands[0] = 0x07;
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_UNITINFO");
+
+	return 0;
+}
+
+static size_t handle_subunit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/* The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it */
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_SUBUNITINFO");
+
+	return 0;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+	for (; list; list = list->next) {
+		struct avctp_pdu_handler *handler = list->data;
+
+		if (handler->opcode == opcode)
+			return handler;
+	}
+
+	return NULL;
+}
+
+static void avctp_disconnected(struct avctp *session)
+{
+	struct avctp_server *server = session->server;
+
+	if (!session)
+		return;
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, TRUE, NULL);
+		g_io_channel_unref(session->io);
+		session->io = NULL;
+	}
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+
+		if (session->state == AVCTP_STATE_CONNECTING) {
+			struct audio_device *dev;
+
+			dev = manager_get_device(&session->server->src,
+							&session->dst, FALSE);
+			audio_device_cancel_authorization(dev, auth_cb,
+								session);
+		}
+	}
+
+	if (session->uinput >= 0) {
+		char address[18];
+
+		ba2str(&session->dst, address);
+		DBG("AVCTP: closing uinput for %s", address);
+
+		ioctl(session->uinput, UI_DEV_DESTROY);
+		close(session->uinput);
+		session->uinput = -1;
+	}
+
+	server->sessions = g_slist_remove(server->sessions, session);
+	g_free(session);
+}
+
+static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
+{
+	GSList *l;
+	struct audio_device *dev;
+	avctp_state_t old_state = session->state;
+
+	dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+	if (dev == NULL) {
+		error("avdtp_set_state(): no matching audio device");
+		return;
+	}
+
+	session->state = new_state;
+
+	for (l = callbacks; l != NULL; l = l->next) {
+		struct avctp_state_callback *cb = l->data;
+		cb->cb(dev, old_state, new_state, cb->user_data);
+	}
+
+	switch (new_state) {
+	case AVCTP_STATE_DISCONNECTED:
+		DBG("AVCTP Disconnected");
+
+		avctp_disconnected(session);
+
+		if (old_state != AVCTP_STATE_CONNECTED)
+			break;
+
+		if (!audio_device_is_active(dev, NULL))
+			audio_device_set_authorized(dev, FALSE);
+
+		break;
+	case AVCTP_STATE_CONNECTING:
+		DBG("AVCTP Connecting");
+		break;
+	case AVCTP_STATE_CONNECTED:
+		DBG("AVCTP Connected");
+		break;
+	default:
+		error("Invalid AVCTP state %d", new_state);
+		return;
+	}
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	uint8_t buf[1024], *operands, code, subunit;
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	int ret, packet_size, operand_count, sock;
+	struct avctp_pdu_handler *handler;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(session->io);
+
+	ret = read(sock, buf, sizeof(buf));
+	if (ret <= 0)
+		goto failed;
+
+	DBG("Got %d bytes of data for AVCTP session %p", ret, session);
+
+	if ((unsigned int) ret < sizeof(struct avctp_header)) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
+			"PID 0x%04X",
+			avctp->transaction, avctp->packet_type,
+			avctp->cr, avctp->ipid, ntohs(avctp->pid));
+
+	ret -= sizeof(struct avctp_header);
+	if ((unsigned int) ret < sizeof(struct avc_header)) {
+		error("Too small AVRCP packet");
+		goto failed;
+	}
+
+	avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
+
+	ret -= sizeof(struct avc_header);
+
+	operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
+	operand_count = ret;
+
+	DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
+			"opcode 0x%02X, %d operands",
+			avctp->cr ? "response" : "command",
+			avc->code, avc->subunit_type, avc->subunit_id,
+			avc->opcode, operand_count);
+
+	if (avctp->cr == AVCTP_RESPONSE)
+		return TRUE;
+
+	packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+		goto done;
+	}
+
+	if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+		avctp->ipid = 1;
+		avc->code = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	handler = find_handler(handlers, avc->opcode);
+	if (!handler) {
+		avc->code = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	code = avc->code;
+	subunit = avc->subunit_type;
+
+	packet_size += handler->cb(session, avctp->transaction, &code,
+					&subunit, operands, operand_count,
+					handler->user_data);
+
+	avc->code = code;
+	avc->subunit_type = subunit;
+
+done:
+	ret = write(sock, buf, packet_size);
+	if (ret != packet_size)
+		goto failed;
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP session %p got disconnected", session);
+	avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+	return FALSE;
+}
+
+static int uinput_create(char *name)
+{
+	struct uinput_dev dev;
+	int fd, err, i;
+
+	fd = open("/dev/uinput", O_RDWR);
+	if (fd < 0) {
+		fd = open("/dev/input/uinput", O_RDWR);
+		if (fd < 0) {
+			fd = open("/dev/misc/uinput", O_RDWR);
+			if (fd < 0) {
+				err = errno;
+				error("Can't open input device: %s (%d)",
+							strerror(err), err);
+				return -err;
+			}
+		}
+	}
+
+	memset(&dev, 0, sizeof(dev));
+	if (name)
+		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+	dev.id.bustype = BUS_BLUETOOTH;
+	dev.id.vendor  = 0x0000;
+	dev.id.product = 0x0000;
+	dev.id.version = 0x0000;
+
+	if (write(fd, &dev, sizeof(dev)) < 0) {
+		err = errno;
+		error("Can't write device information: %s (%d)",
+						strerror(err), err);
+		close(fd);
+		errno = err;
+		return -err;
+	}
+
+	ioctl(fd, UI_SET_EVBIT, EV_KEY);
+	ioctl(fd, UI_SET_EVBIT, EV_REL);
+	ioctl(fd, UI_SET_EVBIT, EV_REP);
+	ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+	for (i = 0; key_map[i].name != NULL; i++)
+		ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+		err = errno;
+		error("Can't create uinput device: %s (%d)",
+						strerror(err), err);
+		close(fd);
+		errno = err;
+		return -err;
+	}
+
+	return fd;
+}
+
+static void init_uinput(struct avctp *session)
+{
+	struct audio_device *dev;
+	char address[18], name[248 + 1];
+
+	dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+	device_get_name(dev->btd_dev, name, sizeof(name));
+	if (g_str_equal(name, "Nokia CK-20W")) {
+		session->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
+		session->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
+		session->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
+		session->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
+	}
+
+	ba2str(&session->dst, address);
+
+	session->uinput = uinput_create(address);
+	if (session->uinput < 0)
+		error("AVRCP: failed to init uinput for %s", address);
+	else
+		DBG("AVRCP: uinput initialized for %s", address);
+}
+
+static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	struct avctp *session = data;
+	char address[18];
+	uint16_t imtu;
+	GError *gerr = NULL;
+
+	if (err) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, BT_IO_L2CAP, &gerr,
+			BT_IO_OPT_DEST, &address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	DBG("AVCTP: connected to %s", address);
+
+	if (!session->io)
+		session->io = g_io_channel_ref(chan);
+
+	init_uinput(session);
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTED);
+	session->mtu = imtu;
+	session->io_id = g_io_add_watch(chan,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) session_cb, session);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+	struct avctp *session = user_data;
+	GError *err = NULL;
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+	}
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access denied: %s", derr->message);
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+		return;
+	}
+
+	if (!bt_io_accept(session->io, avctp_connect_cb, session,
+								NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+	}
+}
+
+static struct avctp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+	for (; list; list = list->next) {
+		struct avctp_server *server = list->data;
+
+		if (bacmp(&server->src, src) == 0)
+			return server;
+	}
+
+	return NULL;
+}
+
+static struct avctp *find_session(GSList *list, const bdaddr_t *dst)
+{
+	for (; list != NULL; list = g_slist_next(list)) {
+		struct avctp *s = list->data;
+
+		if (bacmp(dst, &s->dst))
+			continue;
+
+		return s;
+	}
+
+	return NULL;
+}
+
+static struct avctp *avctp_get_internal(const bdaddr_t *src,
+							const bdaddr_t *dst)
+{
+	struct avctp_server *server;
+	struct avctp *session;
+
+	assert(src != NULL);
+	assert(dst != NULL);
+
+	server = find_server(servers, src);
+	if (server == NULL)
+		return NULL;
+
+	session = find_session(server->sessions, dst);
+	if (session)
+		return session;
+
+	session = g_new0(struct avctp, 1);
+
+	session->server = server;
+	bacpy(&session->dst, dst);
+	session->state = AVCTP_STATE_DISCONNECTED;
+
+	server->sessions = g_slist_append(server->sessions, session);
+
+	return session;
+}
+
+static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct avctp *session;
+	struct audio_device *dev;
+	char address[18];
+	bdaddr_t src, dst;
+	GError *err = NULL;
+
+	bt_io_get(chan, BT_IO_L2CAP, &err,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	DBG("AVCTP: incoming connect from %s", address);
+
+	session = avctp_get_internal(&src, &dst);
+	if (!session)
+		goto drop;
+
+	dev = manager_get_device(&src, &dst, FALSE);
+	if (!dev) {
+		dev = manager_get_device(&src, &dst, TRUE);
+		if (!dev) {
+			error("Unable to get audio device object for %s",
+					address);
+			goto drop;
+		}
+		btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID);
+	}
+
+	if (session->io) {
+		error("Refusing unexpected connect from %s", address);
+		goto drop;
+	}
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING);
+	session->io = g_io_channel_ref(chan);
+
+	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+						auth_cb, session) < 0)
+		goto drop;
+
+	session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+							session_cb, session);
+	return;
+
+drop:
+	if (!session || !session->io)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+	if (session)
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
+
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+
+	io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_PSM, AVCTP_PSM,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_MASTER, master,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+	}
+
+	return io;
+}
+
+static unsigned int passthrough_id = 0;
+static unsigned int unit_id = 0;
+static unsigned int subunit_id = 0;
+
+int avctp_register(const bdaddr_t *src, gboolean master)
+{
+	struct avctp_server *server;
+
+	server = g_new0(struct avctp_server, 1);
+	if (!server)
+		return -ENOMEM;
+
+	server->io = avctp_server_socket(src, master);
+	if (!server->io) {
+		g_free(server);
+		return -1;
+	}
+
+	bacpy(&server->src, src);
+
+	servers = g_slist_append(servers, server);
+
+	if (!passthrough_id)
+		passthrough_id = avctp_register_pdu_handler(AVC_OP_PASSTHROUGH,
+					handle_panel_passthrough, NULL);
+
+	if (!unit_id)
+		unit_id = avctp_register_pdu_handler(AVC_OP_UNITINFO, handle_unit_info,
+									NULL);
+
+	if (!subunit_id)
+		subunit_id = avctp_register_pdu_handler(AVC_OP_SUBUNITINFO,
+						handle_subunit_info, NULL);
+
+	return 0;
+}
+
+void avctp_unregister(const bdaddr_t *src)
+{
+	struct avctp_server *server;
+
+	server = find_server(servers, src);
+	if (!server)
+		return;
+
+	while (server->sessions)
+		avctp_disconnected(server->sessions->data);
+
+	servers = g_slist_remove(servers, server);
+
+	g_io_channel_shutdown(server->io, TRUE, NULL);
+	g_io_channel_unref(server->io);
+	g_free(server);
+
+	if (servers)
+		return;
+
+	if (passthrough_id)
+		avctp_unregister_pdu_handler(passthrough_id);
+
+	if (unit_id)
+		avctp_unregister_pdu_handler(unit_id);
+
+	if (subunit_id)
+		avctp_unregister_pdu_handler(unit_id);
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+	unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
+	struct avctp_header *avctp = (void *) buf;
+	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+	uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+	int sk = g_io_channel_unix_get_fd(session->io);
+	static uint8_t transaction = 0;
+
+	if (session->state != AVCTP_STATE_CONNECTED)
+		return -ENOTCONN;
+
+	memset(buf, 0, sizeof(buf));
+
+	avctp->transaction = transaction++;
+	avctp->packet_type = AVCTP_PACKET_SINGLE;
+	avctp->cr = AVCTP_COMMAND;
+	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	avc->code = AVC_CTYPE_CONTROL;
+	avc->subunit_type = AVC_SUBUNIT_PANEL;
+	avc->opcode = AVC_OP_PASSTHROUGH;
+
+	operands[0] = op & 0x7f;
+	operands[1] = 0;
+
+	if (write(sk, buf, sizeof(buf)) < 0)
+		return -errno;
+
+	/* Button release */
+	avctp->transaction = transaction++;
+	operands[0] |= 0x80;
+
+	if (write(sk, buf, sizeof(buf)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				uint8_t *operands, size_t operand_count)
+{
+	uint8_t *buf;
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	uint8_t *pdu;
+	int sk, err;
+	uint16_t size;
+
+	if (session->state != AVCTP_STATE_CONNECTED)
+		return -ENOTCONN;
+
+	sk = g_io_channel_unix_get_fd(session->io);
+	size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + operand_count;
+	buf = g_malloc0(size);
+
+	avctp = (void *) buf;
+	avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+	pdu = (void *) &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+
+	avctp->transaction = transaction;
+	avctp->packet_type = AVCTP_PACKET_SINGLE;
+	avctp->cr = AVCTP_RESPONSE;
+	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	avc->code = code;
+	avc->subunit_type = subunit;
+	avc->opcode = AVC_OP_VENDORDEP;
+
+	memcpy(pdu, operands, operand_count);
+
+	err = write(sk, buf, size);
+	if (err < 0) {
+		g_free(buf);
+		return -errno;
+	}
+
+	g_free(buf);
+	return 0;
+}
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
+{
+	struct avctp_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new(struct avctp_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->user_data = user_data;
+	state_cb->id = ++id;
+
+	callbacks = g_slist_append(callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+gboolean avctp_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = callbacks; l != NULL; l = l->next) {
+		struct avctp_state_callback *cb = l->data;
+		if (cb && cb->id == id) {
+			callbacks = g_slist_remove(callbacks, cb);
+			g_free(cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+							void *user_data)
+{
+	struct avctp_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	handler = find_handler(handlers, opcode);
+	if (handler)
+		return 0;
+
+	handler = g_new(struct avctp_pdu_handler, 1);
+	handler->opcode = opcode;
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	handlers = g_slist_append(handlers, handler);
+
+	return handler->id;
+}
+
+gboolean avctp_unregister_pdu_handler(unsigned int id)
+{
+	GSList *l;
+
+	for (l = handlers; l != NULL; l = l->next) {
+		struct avctp_pdu_handler *handler = l->data;
+
+		if (handler->id == id) {
+			handlers = g_slist_remove(handlers, handler);
+			g_free(handler);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	struct avctp *session;
+	GError *err = NULL;
+	GIOChannel *io;
+
+	session = avctp_get_internal(src, dst);
+	if (!session)
+		return NULL;
+
+	if (session->state > AVCTP_STATE_DISCONNECTED)
+		return session;
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING);
+
+	io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, session, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
+				BT_IO_OPT_DEST_BDADDR, &session->dst,
+				BT_IO_OPT_PSM, AVCTP_PSM,
+				BT_IO_OPT_INVALID);
+	if (err) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+		error("%s", err->message);
+		g_error_free(err);
+		return NULL;
+	}
+
+	session->io = io;
+
+	return session;
+}
+
+void avctp_disconnect(struct avctp *session)
+{
+	if (session->io)
+		return;
+
+	avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
diff --git a/audio/avctp.h b/audio/avctp.h
new file mode 100644
index 0000000..157ecc6
--- /dev/null
+++ b/audio/avctp.h
@@ -0,0 +1,95 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AVCTP_PSM 23
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL		0x09
+
+/* operands in passthrough commands */
+#define VOL_UP_OP			0x41
+#define VOL_DOWN_OP			0x42
+#define MUTE_OP				0x43
+#define PLAY_OP				0x44
+#define STAVC_OP_OP			0x45
+#define PAUSE_OP			0x46
+#define RECORD_OP			0x47
+#define REWIND_OP			0x48
+#define FAST_FORWARD_OP			0x49
+#define EJECT_OP			0x4a
+#define FORWARD_OP			0x4b
+#define BACKWARD_OP			0x4c
+
+struct avctp;
+
+typedef enum {
+	AVCTP_STATE_DISCONNECTED = 0,
+	AVCTP_STATE_CONNECTING,
+	AVCTP_STATE_CONNECTED
+} avctp_state_t;
+
+typedef void (*avctp_state_cb) (struct audio_device *dev,
+				avctp_state_t old_state,
+				avctp_state_t new_state,
+				void *user_data);
+
+typedef size_t (*avctp_pdu_cb) (struct avctp *session, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
+gboolean avctp_remove_state_cb(unsigned int id);
+
+int avctp_register(const bdaddr_t *src, gboolean master);
+void avctp_unregister(const bdaddr_t *src);
+
+struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst);
+void avctp_disconnect(struct avctp *session);
+
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+							void *user_data);
+gboolean avctp_unregister_pdu_handler(unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op);
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				uint8_t *operands, size_t operand_count);
diff --git a/audio/control.c b/audio/control.c
index 73130e4..dceb004 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -36,7 +36,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <netinet/in.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sdp.h>
@@ -48,64 +47,14 @@
 
 #include "log.h"
 #include "error.h"
-#include "uinput.h"
-#include "adapter.h"
-#include "../src/device.h"
 #include "device.h"
 #include "manager.h"
-#include "avdtp.h"
+#include "avctp.h"
 #include "control.h"
 #include "sdpd.h"
 #include "glib-helper.h"
-#include "btio.h"
 #include "dbus-common.h"
 
-#define AVCTP_PSM 23
-
-/* Message types */
-#define AVCTP_COMMAND		0
-#define AVCTP_RESPONSE		1
-
-/* Packet types */
-#define AVCTP_PACKET_SINGLE	0
-#define AVCTP_PACKET_START	1
-#define AVCTP_PACKET_CONTINUE	2
-#define AVCTP_PACKET_END	3
-
-/* ctype entries */
-#define CTYPE_CONTROL		0x0
-#define CTYPE_STATUS		0x1
-#define CTYPE_NOTIFY		0x3
-#define CTYPE_NOT_IMPLEMENTED	0x8
-#define CTYPE_ACCEPTED		0x9
-#define CTYPE_REJECTED		0xA
-#define CTYPE_STABLE		0xC
-#define CTYPE_CHANGED		0xD
-#define CTYPE_INTERIM		0xF
-
-/* opcodes */
-#define OP_VENDORDEP		0x00
-#define OP_UNITINFO		0x30
-#define OP_SUBUNITINFO		0x31
-#define OP_PASSTHROUGH		0x7c
-
-/* subunits of interest */
-#define SUBUNIT_PANEL		0x09
-
-/* operands in passthrough commands */
-#define VOL_UP_OP		0x41
-#define VOL_DOWN_OP		0x42
-#define MUTE_OP			0x43
-#define PLAY_OP			0x44
-#define STOP_OP			0x45
-#define PAUSE_OP		0x46
-#define RECORD_OP		0x47
-#define REWIND_OP		0x48
-#define FAST_FORWARD_OP		0x49
-#define EJECT_OP		0x4a
-#define FORWARD_OP		0x4b
-#define BACKWARD_OP		0x4c
-
 /* Company IDs for vendor dependent commands */
 #define IEEEID_BTSIG		0x001958
 
@@ -137,8 +86,6 @@
 #define CAP_COMPANY_ID		0x02
 #define CAP_EVENTS_SUPPORTED	0x03
 
-#define QUIRK_NO_RELEASE	1 << 0
-
 enum player_setting {
 	PLAYER_SETTING_EQUALIZER =	1,
 	PLAYER_SETTING_REPEAT =		2,
@@ -200,27 +147,10 @@ enum media_info_id {
 static DBusConnection *connection = NULL;
 
 static GSList *servers = NULL;
+static unsigned int avctp_id = 0;
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 
-struct avctp_header {
-	uint8_t ipid:1;
-	uint8_t cr:1;
-	uint8_t packet_type:2;
-	uint8_t transaction:4;
-	uint16_t pid;
-} __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
-
-struct avc_header {
-	uint8_t code:4;
-	uint8_t _hdr0:4;
-	uint8_t subunit_id:3;
-	uint8_t subunit_type:5;
-	uint8_t opcode;
-} __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
-
 struct avrcp_header {
 	uint8_t company_id[3];
 	uint8_t pdu_id;
@@ -233,24 +163,6 @@ struct avrcp_header {
 
 #elif __BYTE_ORDER == __BIG_ENDIAN
 
-struct avctp_header {
-	uint8_t transaction:4;
-	uint8_t packet_type:2;
-	uint8_t cr:1;
-	uint8_t ipid:1;
-	uint16_t pid;
-} __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
-
-struct avc_header {
-	uint8_t _hdr0:4;
-	uint8_t code:4;
-	uint8_t subunit_type:5;
-	uint8_t subunit_id:3;
-	uint8_t opcode;
-} __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
-
 struct avrcp_header {
 	uint8_t company_id[3];
 	uint8_t pdu_id;
@@ -265,15 +177,8 @@ struct avrcp_header {
 #error "Unknown byte order"
 #endif
 
-struct avctp_state_callback {
-	avctp_state_cb cb;
-	void *user_data;
-	unsigned int id;
-};
-
-struct avctp_server {
+struct avrcp_server {
 	bdaddr_t src;
-	GIOChannel *io;
 	uint32_t tg_record_id;
 	uint32_t ct_record_id;
 };
@@ -295,53 +200,25 @@ struct media_player {
 
 	struct media_info mi;
 	GTimer *timer;
+	unsigned int handler;
 };
 
 struct control {
 	struct audio_device *dev;
 	struct media_player *mp;
-
-	avctp_state_t state;
-
-	int uinput;
-
-	GIOChannel *io;
-	guint io_id;
-
-	uint16_t mtu;
+	struct avctp *session;
 
 	gboolean target;
 
-	uint8_t key_quirks[256];
-
 	uint16_t registered_events;
 	uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
 };
 
-static struct {
-	const char *name;
-	uint8_t avrcp;
-	uint16_t uinput;
-} key_map[] = {
-	{ "PLAY",		PLAY_OP,		KEY_PLAYCD },
-	{ "STOP",		STOP_OP,		KEY_STOPCD },
-	{ "PAUSE",		PAUSE_OP,		KEY_PAUSECD },
-	{ "FORWARD",		FORWARD_OP,		KEY_NEXTSONG },
-	{ "BACKWARD",		BACKWARD_OP,		KEY_PREVIOUSSONG },
-	{ "REWIND",		REWIND_OP,		KEY_REWIND },
-	{ "FAST FORWARD",	FAST_FORWARD_OP,	KEY_FASTFORWARD },
-	{ 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);
-
 static sdp_record_t *avrcp_ct_record(void)
 {
 	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
@@ -470,76 +347,6 @@ static sdp_record_t *avrcp_tg_record(void)
 	return record;
 }
 
-static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
-{
-	struct uinput_event event;
-
-	memset(&event, 0, sizeof(event));
-	event.type	= type;
-	event.code	= code;
-	event.value	= value;
-
-	return write(fd, &event, sizeof(event));
-}
-
-static void send_key(int fd, uint16_t key, int pressed)
-{
-	if (fd < 0)
-		return;
-
-	send_event(fd, EV_KEY, key, pressed);
-	send_event(fd, EV_SYN, SYN_REPORT, 0);
-}
-
-static void handle_panel_passthrough(struct control *control,
-					const unsigned char *operands,
-					int operand_count)
-{
-	const char *status;
-	int pressed, i;
-
-	if (operand_count == 0)
-		return;
-
-	if (operands[0] & 0x80) {
-		status = "released";
-		pressed = 0;
-	} else {
-		status = "pressed";
-		pressed = 1;
-	}
-
-	for (i = 0; key_map[i].name != NULL; i++) {
-		uint8_t key_quirks;
-
-		if ((operands[0] & 0x7F) != key_map[i].avrcp)
-			continue;
-
-		DBG("AVRCP: %s %s", key_map[i].name, status);
-
-		key_quirks = control->key_quirks[key_map[i].avrcp];
-
-		if (key_quirks & QUIRK_NO_RELEASE) {
-			if (!pressed) {
-				DBG("AVRCP: Ignoring release");
-				break;
-			}
-
-			DBG("AVRCP: treating key press as press + release");
-			send_key(control->uinput, key_map[i].uinput, 1);
-			send_key(control->uinput, key_map[i].uinput, 0);
-			break;
-		}
-
-		send_key(control->uinput, key_map[i].uinput, pressed);
-		break;
-	}
-
-	if (key_map[i].name == NULL)
-		DBG("AVRCP: unknown button 0x%02X %s",
-						operands[0] & 0x7F, status);
-}
-
 static unsigned int attr_get_max_val(uint8_t attr)
 {
 	switch (attr) {
@@ -719,18 +526,14 @@ static const char *battery_status_to_str(enum battery_status status)
 	return NULL;
 }
 
-static int avctp_send_event(struct control *control, uint8_t id, void *data)
+static int avrcp_send_event(struct control *control, uint8_t id, void *data)
 {
-	uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
-					AVRCP_HEADER_LENGTH + 9];
-	struct avctp_header *avctp = (void *) buf;
-	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
-	struct avrcp_header *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
-							AVC_HEADER_LENGTH];
-	int sk = g_io_channel_unix_get_fd(control->io);
+	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+	struct avrcp_header *pdu = (void *) buf;
 	uint16_t size;
+	int err;
 
-	if (control->state != AVCTP_STATE_CONNECTED)
+	if (control->session)
 		return -ENOTCONN;
 
 	if (!(control->registered_events & (1 << id)))
@@ -738,15 +541,6 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
 
 	memset(buf, 0, sizeof(buf));
 
-	avctp->transaction = control->transaction_events[id];
-	avctp->packet_type = AVCTP_PACKET_SINGLE;
-	avctp->cr = AVCTP_RESPONSE;
-	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
-	avc->code = CTYPE_CHANGED;
-	avc->subunit_type = SUBUNIT_PANEL;
-	avc->opcode = OP_VENDORDEP;
-
 	pdu->company_id[0] = IEEEID_BTSIG >> 16;
 	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
 	pdu->company_id[2] = IEEEID_BTSIG & 0xFF;
@@ -780,13 +574,12 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
 	}
 
 	pdu->params_len = htons(size);
-	size += AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
-					AVRCP_HEADER_LENGTH;
-
-	sk = g_io_channel_unix_get_fd(control->io);
 
-	if (write(sk, buf, size) < 0)
-		return -errno;
+	err = avctp_send_vendordep(control->session, control->transaction_events[id],
+					AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+					buf, size);
+	if (err < 0)
+		return err;
 
 	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
 	control->registered_events ^= 1 << id;
@@ -833,7 +626,7 @@ static void mp_set_playback_status(struct control *control, uint8_t status,
 
 	mp->status = status;
 
-	avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
+	avrcp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
 								&status);
 }
 
@@ -983,7 +776,7 @@ static void mp_set_media_attributes(struct control *control,
 			mi->title, mi->artist, mi->album, mi->genre,
 			mi->ntracks, mi->track, mi->track_len);
 
-	avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
+	avrcp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
 }
 
 static uint8_t avrcp_handle_get_capabilities(struct control *control,
@@ -1009,21 +802,21 @@ static uint8_t avrcp_handle_get_capabilities(struct control *control,
 		pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
 		pdu->params[1] = G_N_ELEMENTS(company_ids);
 
-		return CTYPE_STABLE;
+		return AVC_CTYPE_STABLE;
 	case CAP_EVENTS_SUPPORTED:
 		pdu->params_len = htons(4);
 		pdu->params[1] = 2;
 		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
 		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
 
-		return CTYPE_STABLE;
+		return AVC_CTYPE_STABLE;
 	}
 
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
 
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_list_player_attributes(struct control *control,
@@ -1037,7 +830,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct control *control,
 	if (len != 0) {
 		pdu->params_len = htons(1);
 		pdu->params[0] = E_INVALID_PARAM;
-		return CTYPE_REJECTED;
+		return AVC_CTYPE_REJECTED;
 	}
 
 	if (!mp)
@@ -1057,7 +850,7 @@ done:
 	pdu->params[0] = len;
 	pdu->params_len = htons(len + 1);
 
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 }
 
 static uint8_t avrcp_handle_list_player_values(struct control *control,
@@ -1083,12 +876,12 @@ static uint8_t avrcp_handle_list_player_values(struct control *control,
 	pdu->params[0] = len;
 	pdu->params_len = htons(len + 1);
 
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_get_element_attributes(struct control *control,
@@ -1153,11 +946,11 @@ done:
 	pdu->params[0] = len;
 	pdu->params_len = htons(pos);
 
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_get_current_player_value(struct control *control,
@@ -1212,7 +1005,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct control *control,
 		pdu->params[0] = len;
 		pdu->params_len = htons(2 * len + 1);
 
-		return CTYPE_STABLE;
+		return AVC_CTYPE_STABLE;
 	}
 
 	error("No valid attributes in request");
@@ -1221,7 +1014,7 @@ err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
 
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_set_player_value(struct control *control,
@@ -1268,13 +1061,13 @@ static uint8_t avrcp_handle_set_player_value(struct control *control,
 	if (len) {
 		pdu->params_len = 0;
 
-		return CTYPE_STABLE;
+		return AVC_CTYPE_STABLE;
 	}
 
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_displayable_charset(struct control *control,
@@ -1286,7 +1079,7 @@ static uint8_t avrcp_handle_displayable_charset(struct control *control,
 	if (len < 3) {
 		pdu->params_len = htons(1);
 		pdu->params[0] = E_INVALID_PARAM;
-		return CTYPE_REJECTED;
+		return AVC_CTYPE_REJECTED;
 	}
 
 	/*
@@ -1294,7 +1087,7 @@ static uint8_t avrcp_handle_displayable_charset(struct control *control,
 	 * encoding since CT is obliged to support it.
 	 */
 	pdu->params_len = 0;
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 }
 
 static uint8_t avrcp_handle_ct_battery_status(struct control *control,
@@ -1316,12 +1109,12 @@ static uint8_t avrcp_handle_ct_battery_status(struct control *control,
 					DBUS_TYPE_STRING, &valstr);
 	pdu->params_len = 0;
 
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static uint8_t avrcp_handle_get_play_status(struct control *control,
@@ -1336,7 +1129,7 @@ static uint8_t avrcp_handle_get_play_status(struct control *control,
 	if (len != 0) {
 		pdu->params_len = htons(1);
 		pdu->params[0] = E_INVALID_PARAM;
-		return CTYPE_REJECTED;
+		return AVC_CTYPE_REJECTED;
 	}
 
 	if (control->mp) {
@@ -1356,7 +1149,7 @@ static uint8_t avrcp_handle_get_play_status(struct control *control,
 
 	pdu->params_len = htons(9);
 
-	return CTYPE_STABLE;
+	return AVC_CTYPE_STABLE;
 }
 
 static uint8_t avrcp_handle_register_notification(struct control *control,
@@ -1406,12 +1199,12 @@ static uint8_t avrcp_handle_register_notification(struct control *control,
 
 	pdu->params_len = htons(len);
 
-	return CTYPE_INTERIM;
+	return AVC_CTYPE_INTERIM;
 
 err:
 	pdu->params_len = htons(1);
 	pdu->params[0] = E_INVALID_PARAM;
-	return CTYPE_REJECTED;
+	return AVC_CTYPE_REJECTED;
 }
 
 static struct pdu_handler {
@@ -1421,51 +1214,54 @@ static struct pdu_handler {
 					struct avrcp_header *pdu,
 					uint8_t transaction);
 } handlers[] = {
-		{ AVRCP_GET_CAPABILITIES, CTYPE_STATUS,
+		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
 					avrcp_handle_get_capabilities },
-		{ AVRCP_LIST_PLAYER_ATTRIBUTES, CTYPE_STATUS,
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
 					avrcp_handle_list_player_attributes },
-		{ AVRCP_LIST_PLAYER_VALUES, CTYPE_STATUS,
+		{ AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
 					avrcp_handle_list_player_values },
-		{ AVRCP_GET_ELEMENT_ATTRIBUTES, CTYPE_STATUS,
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
 					avrcp_handle_get_element_attributes },
-		{ AVRCP_GET_CURRENT_PLAYER_VALUE, CTYPE_STATUS,
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
 					avrcp_handle_get_current_player_value },
-		{ AVRCP_SET_PLAYER_VALUE, CTYPE_CONTROL,
+		{ AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
 					avrcp_handle_set_player_value },
-		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, CTYPE_STATUS,
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
 					NULL },
-		{ AVRCP_GET_PLAYER_VALUE_TEXT, CTYPE_STATUS,
+		{ AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
 					NULL },
-		{ AVRCP_DISPLAYABLE_CHARSET, CTYPE_STATUS,
+		{ AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
 					avrcp_handle_displayable_charset },
-		{ AVRCP_CT_BATTERY_STATUS, CTYPE_STATUS,
+		{ AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
 					avrcp_handle_ct_battery_status },
-		{ AVRCP_GET_PLAY_STATUS, CTYPE_STATUS,
+		{ AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
 					avrcp_handle_get_play_status },
-		{ AVRCP_REGISTER_NOTIFICATION, CTYPE_NOTIFY,
+		{ AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
 					avrcp_handle_register_notification },
 		{ },
 };
 
 /* handle vendordep pdu inside an avctp packet */
-static int handle_vendordep_pdu(struct control *control,
-					struct avctp_header *avctp,
-					struct avc_header *avc,
-					int operand_count)
+static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
 {
+	struct control *control = user_data;
 	struct pdu_handler *handler;
-	struct avrcp_header *pdu = (void *) avc + AVC_HEADER_LENGTH;
+	struct avrcp_header *pdu = (void *) operands;
 	uint32_t company_id = (pdu->company_id[0] << 16) |
 				(pdu->company_id[1] << 8) |
 				(pdu->company_id[2]);
 
-	if (company_id != IEEEID_BTSIG ||
-				pdu->packet_type != AVCTP_PACKET_SINGLE) {
-		avc->code = CTYPE_NOT_IMPLEMENTED;
-		return AVC_HEADER_LENGTH;
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
 	}
 
+	DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+			pdu->pdu_id, company_id, pdu->params_len);
+
 	pdu->packet_type = 0;
 	pdu->rsvd = 0;
 
@@ -1477,7 +1273,7 @@ static int handle_vendordep_pdu(struct control *control,
 			break;
 	}
 
-	if (!handler || handler->code != avc->code) {
+	if (!handler || handler->code != *code) {
 		pdu->params[0] = E_INVALID_COMMAND;
 		goto err_metadata;
 	}
@@ -1487,64 +1283,31 @@ static int handle_vendordep_pdu(struct control *control,
 		goto err_metadata;
 	}
 
-	avc->code = handler->func(control, pdu, avctp->transaction);
+	*code = handler->func(control, pdu, transaction);
 
-	return AVC_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
-						ntohs(pdu->params_len);
+	return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
 
 err_metadata:
 	pdu->params_len = htons(1);
-	avc->code = CTYPE_REJECTED;
+	*code = AVC_CTYPE_REJECTED;
 
-	return AVC_HEADER_LENGTH + AVRCP_HEADER_LENGTH + 1;
+	return AVRCP_HEADER_LENGTH + 1;
 }
 
-static void avctp_disconnected(struct audio_device *dev)
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+				avctp_state_t new_state, void *user_data)
 {
 	struct control *control = dev->control;
-
-	if (!control)
-		return;
-
-	if (control->io) {
-		g_io_channel_shutdown(control->io, TRUE, NULL);
-		g_io_channel_unref(control->io);
-		control->io = NULL;
-	}
-
-	if (control->io_id) {
-		g_source_remove(control->io_id);
-		control->io_id = 0;
-
-		if (control->state == AVCTP_STATE_CONNECTING)
-			audio_device_cancel_authorization(dev, auth_cb,
-								control);
-	}
-
-	if (control->uinput >= 0) {
-		char address[18];
-
-		ba2str(&dev->dst, address);
-		DBG("AVRCP: closing uinput for %s", address);
-
-		ioctl(control->uinput, UI_DEV_DESTROY);
-		close(control->uinput);
-		control->uinput = -1;
-	}
-}
-
-static void avctp_set_state(struct control *control, avctp_state_t new_state)
-{
-	GSList *l;
-	struct audio_device *dev = control->dev;
-	avctp_state_t old_state = control->state;
 	gboolean value;
 
 	switch (new_state) {
 	case AVCTP_STATE_DISCONNECTED:
-		DBG("AVCTP Disconnected");
+		control->session = NULL;
 
-		avctp_disconnected(control->dev);
+		if (control->mp && control->mp->handler) {
+			avctp_unregister_pdu_handler(control->mp->handler);
+			control->mp->handler = 0;
+		}
 
 		if (old_state != AVCTP_STATE_CONNECTED)
 			break;
@@ -1557,221 +1320,32 @@ static void avctp_set_state(struct control *control, avctp_state_t new_state)
 					AUDIO_CONTROL_INTERFACE, "Connected",
 					DBUS_TYPE_BOOLEAN, &value);
 
-		if (!audio_device_is_active(dev, NULL))
-			audio_device_set_authorized(dev, FALSE);
-
 		break;
 	case AVCTP_STATE_CONNECTING:
-		DBG("AVCTP Connecting");
+		if (control->session)
+			break;
+
+		control->session = avctp_connect(&dev->src, &dev->dst);
+		if (!control->mp)
+			break;
+
+		control->mp->handler = avctp_register_pdu_handler(
+							AVC_OP_VENDORDEP,
+							handle_vendordep_pdu,
+							control);
 		break;
 	case AVCTP_STATE_CONNECTED:
-		DBG("AVCTP Connected");
 		value = TRUE;
-		g_dbus_emit_signal(control->dev->conn, control->dev->path,
+		g_dbus_emit_signal(dev->conn, dev->path,
 				AUDIO_CONTROL_INTERFACE, "Connected",
 				DBUS_TYPE_INVALID);
-		emit_property_changed(control->dev->conn, control->dev->path,
+		emit_property_changed(dev->conn, dev->path,
 				AUDIO_CONTROL_INTERFACE, "Connected",
 				DBUS_TYPE_BOOLEAN, &value);
 		break;
 	default:
-		error("Invalid AVCTP state %d", new_state);
 		return;
 	}
-
-	control->state = new_state;
-
-	for (l = avctp_callbacks; l != NULL; l = l->next) {
-		struct avctp_state_callback *cb = l->data;
-		cb->cb(control->dev, old_state, new_state, cb->user_data);
-	}
-}
-
-static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
-				gpointer data)
-{
-	struct control *control = data;
-	unsigned char buf[1024], *operands;
-	struct avctp_header *avctp;
-	struct avc_header *avc;
-	int ret, packet_size, operand_count, sock;
-
-	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
-		goto failed;
-
-	sock = g_io_channel_unix_get_fd(control->io);
-
-	ret = read(sock, buf, sizeof(buf));
-	if (ret <= 0)
-		goto failed;
-
-	DBG("Got %d bytes of data for AVCTP session %p", ret, control);
-
-	if ((unsigned int) ret < sizeof(struct avctp_header)) {
-		error("Too small AVCTP packet");
-		goto failed;
-	}
-
-	packet_size = ret;
-
-	avctp = (struct avctp_header *) buf;
-
-	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
-			"PID 0x%04X",
-			avctp->transaction, avctp->packet_type,
-			avctp->cr, avctp->ipid, ntohs(avctp->pid));
-
-	ret -= sizeof(struct avctp_header);
-	if ((unsigned int) ret < sizeof(struct avc_header)) {
-		error("Too small AVRCP packet");
-		goto failed;
-	}
-
-	avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
-
-	ret -= sizeof(struct avc_header);
-
-	operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
-	operand_count = ret;
-
-	DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
-			"opcode 0x%02X, %d operands",
-			avctp->cr ? "response" : "command",
-			avc->code, avc->subunit_type, avc->subunit_id,
-			avc->opcode, operand_count);
-
-	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
-		avctp->cr = AVCTP_RESPONSE;
-		avc->code = CTYPE_NOT_IMPLEMENTED;
-	} else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
-		avctp->ipid = 1;
-		avctp->cr = AVCTP_RESPONSE;
-		packet_size = sizeof(*avctp);
-	} else if (avctp->cr == AVCTP_COMMAND &&
-			avc->code == CTYPE_CONTROL &&
-			avc->subunit_type == SUBUNIT_PANEL &&
-			avc->opcode == OP_PASSTHROUGH) {
-		handle_panel_passthrough(control, operands, operand_count);
-		avctp->cr = AVCTP_RESPONSE;
-		avc->code = CTYPE_ACCEPTED;
-	} else if (avctp->cr == AVCTP_COMMAND &&
-			avc->code == CTYPE_STATUS &&
-			(avc->opcode == OP_UNITINFO
-			|| avc->opcode == OP_SUBUNITINFO)) {
-		avctp->cr = AVCTP_RESPONSE;
-		avc->code = CTYPE_STABLE;
-		/* The first operand should be 0x07 for the UNITINFO response.
-		 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
-		 * Interface Command Set (section 9.2.1, page 45) specs
-		 * explain this value but both use it */
-		if (operand_count >= 1 && avc->opcode == OP_UNITINFO)
-			operands[0] = 0x07;
-		if (operand_count >= 2)
-			operands[1] = SUBUNIT_PANEL << 3;
-		DBG("reply to %s", avc->opcode == OP_UNITINFO ?
-				"OP_UNITINFO" : "OP_SUBUNITINFO");
-	} else if (avctp->cr == AVCTP_COMMAND &&
-			avc->opcode == OP_VENDORDEP) {
-		int r_size;
-		operand_count -= 3;
-		avctp->cr = AVCTP_RESPONSE;
-		r_size = handle_vendordep_pdu(control, avctp, avc,
-								operand_count);
-		packet_size = AVCTP_HEADER_LENGTH + r_size;
-	} else {
-		avctp->cr = AVCTP_RESPONSE;
-		avc->code = CTYPE_REJECTED;
-	}
-
-	ret = write(sock, buf, packet_size);
-	if (ret != packet_size)
-		goto failed;
-
-	return TRUE;
-
-failed:
-	DBG("AVCTP session %p got disconnected", control);
-	avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-	return FALSE;
-}
-
-static int uinput_create(char *name)
-{
-	struct uinput_dev dev;
-	int fd, err, i;
-
-	fd = open("/dev/uinput", O_RDWR);
-	if (fd < 0) {
-		fd = open("/dev/input/uinput", O_RDWR);
-		if (fd < 0) {
-			fd = open("/dev/misc/uinput", O_RDWR);
-			if (fd < 0) {
-				err = errno;
-				error("Can't open input device: %s (%d)",
-							strerror(err), err);
-				return -err;
-			}
-		}
-	}
-
-	memset(&dev, 0, sizeof(dev));
-	if (name)
-		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
-
-	dev.id.bustype = BUS_BLUETOOTH;
-	dev.id.vendor  = 0x0000;
-	dev.id.product = 0x0000;
-	dev.id.version = 0x0000;
-
-	if (write(fd, &dev, sizeof(dev)) < 0) {
-		err = errno;
-		error("Can't write device information: %s (%d)",
-						strerror(err), err);
-		close(fd);
-		errno = err;
-		return -err;
-	}
-
-	ioctl(fd, UI_SET_EVBIT, EV_KEY);
-	ioctl(fd, UI_SET_EVBIT, EV_REL);
-	ioctl(fd, UI_SET_EVBIT, EV_REP);
-	ioctl(fd, UI_SET_EVBIT, EV_SYN);
-
-	for (i = 0; key_map[i].name != NULL; i++)
-		ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
-
-	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
-		err = errno;
-		error("Can't create uinput device: %s (%d)",
-						strerror(err), err);
-		close(fd);
-		errno = err;
-		return -err;
-	}
-
-	return fd;
-}
-
-static void init_uinput(struct control *control)
-{
-	struct audio_device *dev = control->dev;
-	char address[18], name[248 + 1];
-
-	device_get_name(dev->btd_dev, name, sizeof(name));
-	if (g_str_equal(name, "Nokia CK-20W")) {
-		control->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
-		control->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
-		control->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
-		control->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
-	}
-
-	ba2str(&dev->dst, address);
-
-	control->uinput = uinput_create(address);
-	if (control->uinput < 0)
-		error("AVRCP: failed to init uinput for %s", address);
-	else
-		DBG("AVRCP: uinput initialized for %s", address);
 }
 
 static void media_info_init(struct media_info *mi)
@@ -1787,169 +1361,16 @@ static void media_info_init(struct media_info *mi)
 	mi->elapsed = 0xFFFFFFFF;
 }
 
-static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
-{
-	struct control *control = data;
-	char address[18];
-	uint16_t imtu;
-	GError *gerr = NULL;
-
-	if (err) {
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-		error("%s", err->message);
-		return;
-	}
-
-	bt_io_get(chan, BT_IO_L2CAP, &gerr,
-			BT_IO_OPT_DEST, &address,
-			BT_IO_OPT_IMTU, &imtu,
-			BT_IO_OPT_INVALID);
-	if (gerr) {
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-		error("%s", gerr->message);
-		g_error_free(gerr);
-		return;
-	}
-
-	DBG("AVCTP: connected to %s", address);
-
-	if (!control->io)
-		control->io = g_io_channel_ref(chan);
-
-	init_uinput(control);
-
-	avctp_set_state(control, AVCTP_STATE_CONNECTED);
-	control->mtu = imtu;
-	control->io_id = g_io_add_watch(chan,
-				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-				(GIOFunc) control_cb, control);
-}
-
-static void auth_cb(DBusError *derr, void *user_data)
-{
-	struct control *control = user_data;
-	GError *err = NULL;
-
-	if (control->io_id) {
-		g_source_remove(control->io_id);
-		control->io_id = 0;
-	}
-
-	if (derr && dbus_error_is_set(derr)) {
-		error("Access denied: %s", derr->message);
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-		return;
-	}
-
-	if (!bt_io_accept(control->io, avctp_connect_cb, control,
-								NULL, &err)) {
-		error("bt_io_accept: %s", err->message);
-		g_error_free(err);
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-	}
-}
-
-static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
-{
-	struct control *control = NULL;
-	struct audio_device *dev;
-	char address[18];
-	bdaddr_t src, dst;
-	GError *err = NULL;
-
-	bt_io_get(chan, BT_IO_L2CAP, &err,
-			BT_IO_OPT_SOURCE_BDADDR, &src,
-			BT_IO_OPT_DEST_BDADDR, &dst,
-			BT_IO_OPT_DEST, address,
-			BT_IO_OPT_INVALID);
-	if (err) {
-		error("%s", err->message);
-		g_error_free(err);
-		g_io_channel_shutdown(chan, TRUE, NULL);
-		return;
-	}
-
-	dev = manager_get_device(&src, &dst, TRUE);
-	if (!dev) {
-		error("Unable to get audio device object for %s", address);
-		goto drop;
-	}
-
-	if (!dev->control) {
-		btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID);
-		if (!dev->control)
-			goto drop;
-	}
-
-	control = dev->control;
-
-	if (control->io) {
-		error("Refusing unexpected connect from %s", address);
-		goto drop;
-	}
-
-	avctp_set_state(control, AVCTP_STATE_CONNECTING);
-	control->io = g_io_channel_ref(chan);
-
-	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
-						auth_cb, dev->control) < 0)
-		goto drop;
-
-	control->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-							control_cb, control);
-	return;
-
-drop:
-	if (!control || !control->io)
-		g_io_channel_shutdown(chan, TRUE, NULL);
-	if (control)
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-}
-
-static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
-{
-	GError *err = NULL;
-	GIOChannel *io;
-
-	io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
-				NULL, &err,
-				BT_IO_OPT_SOURCE_BDADDR, src,
-				BT_IO_OPT_PSM, AVCTP_PSM,
-				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
-				BT_IO_OPT_MASTER, master,
-				BT_IO_OPT_INVALID);
-	if (!io) {
-		error("%s", err->message);
-		g_error_free(err);
-	}
-
-	return io;
-}
-
 gboolean avrcp_connect(struct audio_device *dev)
 {
 	struct control *control = dev->control;
-	GError *err = NULL;
-	GIOChannel *io;
 
-	if (control->state > AVCTP_STATE_DISCONNECTED)
+	if (control->session)
 		return TRUE;
 
-	avctp_set_state(control, AVCTP_STATE_CONNECTING);
-
-	io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, control, NULL, &err,
-				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
-				BT_IO_OPT_DEST_BDADDR, &dev->dst,
-				BT_IO_OPT_PSM, AVCTP_PSM,
-				BT_IO_OPT_INVALID);
-	if (err) {
-		avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
-		error("%s", err->message);
-		g_error_free(err);
+	control->session = avctp_connect(&dev->src, &dev->dst);
+	if (!control->session)
 		return FALSE;
-	}
-
-	control->io = io;
 
 	return TRUE;
 }
@@ -1958,10 +1379,10 @@ void avrcp_disconnect(struct audio_device *dev)
 {
 	struct control *control = dev->control;
 
-	if (!(control && control->io))
+	if (!(control && control->session))
 		return;
 
-	avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+	avctp_disconnect(control->session);
 }
 
 int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
@@ -1969,7 +1390,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
 	sdp_record_t *record;
 	gboolean tmp, master = TRUE;
 	GError *err = NULL;
-	struct avctp_server *server;
+	struct avrcp_server *server;
 
 	if (config) {
 		tmp = g_key_file_get_boolean(config, "General",
@@ -1981,7 +1402,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
 			master = tmp;
 	}
 
-	server = g_new0(struct avctp_server, 1);
+	server = g_new0(struct avrcp_server, 1);
 	if (!server)
 		return -ENOMEM;
 
@@ -2018,8 +1439,7 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
 	}
 	server->ct_record_id = record->handle;
 
-	server->io = avctp_server_socket(src, master);
-	if (!server->io) {
+	if (avctp_register(src, master) < 0) {
 		remove_record_from_server(server->ct_record_id);
 		remove_record_from_server(server->tg_record_id);
 		g_free(server);
@@ -2033,10 +1453,10 @@ int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
 	return 0;
 }
 
-static struct avctp_server *find_server(GSList *list, const bdaddr_t *src)
+static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
 {
 	for (; list; list = list->next) {
-		struct avctp_server *server = list->data;
+		struct avrcp_server *server = list->data;
 
 		if (bacmp(&server->src, src) == 0)
 			return server;
@@ -2047,7 +1467,7 @@ static struct avctp_server *find_server(GSList *list, const bdaddr_t *src)
 
 void avrcp_unregister(const bdaddr_t *src)
 {
-	struct avctp_server *server;
+	struct avrcp_server *server;
 
 	server = find_server(servers, src);
 	if (!server)
@@ -2058,13 +1478,15 @@ void avrcp_unregister(const bdaddr_t *src)
 	remove_record_from_server(server->ct_record_id);
 	remove_record_from_server(server->tg_record_id);
 
-	g_io_channel_shutdown(server->io, TRUE, NULL);
-	g_io_channel_unref(server->io);
+	avctp_unregister(&server->src);
 	g_free(server);
 
 	if (servers)
 		return;
 
+	if (avctp_id)
+		avctp_remove_state_cb(avctp_id);
+
 	dbus_connection_unref(connection);
 	connection = NULL;
 }
@@ -2082,7 +1504,7 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
 	if (!reply)
 		return NULL;
 
-	connected = (control->state == AVCTP_STATE_CONNECTED);
+	connected = (control->session != NULL);
 
 	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
 					DBUS_TYPE_INVALID);
@@ -2090,42 +1512,6 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
 	return reply;
 }
 
-static int avctp_send_passthrough(struct control *control, uint8_t op)
-{
-	unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
-	struct avctp_header *avctp = (void *) buf;
-	struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
-	uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
-	int sk = g_io_channel_unix_get_fd(control->io);
-	static uint8_t transaction = 0;
-
-	memset(buf, 0, sizeof(buf));
-
-	avctp->transaction = transaction++;
-	avctp->packet_type = AVCTP_PACKET_SINGLE;
-	avctp->cr = AVCTP_COMMAND;
-	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
-	avc->code = CTYPE_CONTROL;
-	avc->subunit_type = SUBUNIT_PANEL;
-	avc->opcode = OP_PASSTHROUGH;
-
-	operands[0] = op & 0x7f;
-	operands[1] = 0;
-
-	if (write(sk, buf, sizeof(buf)) < 0)
-		return -errno;
-
-	/* Button release */
-	avctp->transaction = transaction++;
-	operands[0] |= 0x80;
-
-	if (write(sk, buf, sizeof(buf)) < 0)
-		return -errno;
-
-	return 0;
-}
-
 static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
 								void *data)
 {
@@ -2133,13 +1519,13 @@ static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
 	struct control *control = device->control;
 	int err;
 
-	if (control->state != AVCTP_STATE_CONNECTED)
+	if (!control->session)
 		return btd_error_not_connected(msg);
 
 	if (!control->target)
 		return btd_error_not_supported(msg);
 
-	err = avctp_send_passthrough(control, VOL_UP_OP);
+	err = avctp_send_passthrough(control->session, VOL_UP_OP);
 	if (err < 0)
 		return btd_error_failed(msg, strerror(-err));
 
@@ -2153,13 +1539,13 @@ static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
 	struct control *control = device->control;
 	int err;
 
-	if (control->state != AVCTP_STATE_CONNECTED)
+	if (!control->session)
 		return btd_error_not_connected(msg);
 
 	if (!control->target)
 		return btd_error_not_supported(msg);
 
-	err = avctp_send_passthrough(control, VOL_DOWN_OP);
+	err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
 	if (err < 0)
 		return btd_error_failed(msg, strerror(-err));
 
@@ -2187,7 +1573,7 @@ static DBusMessage *control_get_properties(DBusConnection *conn,
 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
 
 	/* Connected */
-	value = (device->control->state == AVCTP_STATE_CONNECTED);
+	value = (device->control->session != NULL);
 	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
 
 	dbus_message_iter_close_container(&iter, &dict);
@@ -2404,8 +1790,8 @@ static void path_unregister(void *data)
 	DBG("Unregistered interface %s on path %s",
 		AUDIO_CONTROL_INTERFACE, dev->path);
 
-	if (control->state != AVCTP_STATE_DISCONNECTED)
-		avctp_disconnected(dev);
+	if (control->session)
+		avctp_disconnect(control->session);
 
 	g_free(control);
 	dev->control = NULL;
@@ -2420,6 +1806,9 @@ static void mp_path_unregister(void *data)
 	DBG("Unregistered interface %s on path %s",
 		MEDIA_PLAYER_INTERFACE, dev->path);
 
+	if (mp->handler)
+		avctp_unregister_pdu_handler(mp->handler);
+
 	g_timer_destroy(mp->timer);
 	g_free(mp);
 	control->mp = NULL;
@@ -2494,11 +1883,12 @@ struct control *control_init(struct audio_device *dev, uint16_t uuid16,
 
 	control = g_new0(struct control, 1);
 	control->dev = dev;
-	control->state = AVCTP_STATE_DISCONNECTED;
-	control->uinput = -1;
 
 	control_update(control, uuid16, media_player);
 
+	if (!avctp_id)
+		avctp_id = avctp_add_state_cb(state_changed, NULL);
+
 	return control;
 }
 
@@ -2506,39 +1896,8 @@ gboolean control_is_active(struct audio_device *dev)
 {
 	struct control *control = dev->control;
 
-	if (control && control->state != AVCTP_STATE_DISCONNECTED)
+	if (control && control->session)
 		return TRUE;
 
 	return FALSE;
 }
-
-unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
-{
-	struct avctp_state_callback *state_cb;
-	static unsigned int id = 0;
-
-	state_cb = g_new(struct avctp_state_callback, 1);
-	state_cb->cb = cb;
-	state_cb->user_data = user_data;
-	state_cb->id = ++id;
-
-	avctp_callbacks = g_slist_append(avctp_callbacks, state_cb);
-
-	return state_cb->id;
-}
-
-gboolean avctp_remove_state_cb(unsigned int id)
-{
-	GSList *l;
-
-	for (l = avctp_callbacks; l != NULL; l = l->next) {
-		struct avctp_state_callback *cb = l->data;
-		if (cb && cb->id == id) {
-			avctp_callbacks = g_slist_remove(avctp_callbacks, cb);
-			g_free(cb);
-			return TRUE;
-		}
-	}
-
-	return FALSE;
-}
diff --git a/audio/control.h b/audio/control.h
index 77e7595..f5cfef2 100644
--- a/audio/control.h
+++ b/audio/control.h
@@ -25,20 +25,6 @@
 #define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
 #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
 
-typedef enum {
-	AVCTP_STATE_DISCONNECTED = 0,
-	AVCTP_STATE_CONNECTING,
-	AVCTP_STATE_CONNECTED
-} avctp_state_t;
-
-typedef void (*avctp_state_cb) (struct audio_device *dev,
-				avctp_state_t old_state,
-				avctp_state_t new_state,
-				void *user_data);
-
-unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
-gboolean avctp_remove_state_cb(unsigned int id);
-
 int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
 void avrcp_unregister(const bdaddr_t *src);
 
diff --git a/audio/device.c b/audio/device.c
index ea268bc..16f0701 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -52,6 +52,7 @@
 #include "unix.h"
 #include "avdtp.h"
 #include "control.h"
+#include "avctp.h"
 #include "headset.h"
 #include "gateway.h"
 #include "sink.h"
-- 
1.7.6.1


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

* [PATCH BlueZ 5/5] AVRCP: move handling of vendor dependent PDU from control.c to avrcp.c
  2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2011-09-12 11:29 ` [PATCH BlueZ 4/5] AVRCP: split AVCTP specific code from control.c Luiz Augusto von Dentz
@ 2011-09-12 11:29 ` Luiz Augusto von Dentz
  2011-09-12 13:25 ` [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Lucas De Marchi
  4 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 11:29 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This helps to isolate AVRCP PDU handling and MediaPlayer interface.
---
 Makefile.am     |    1 +
 audio/avctp.c   |    5 +
 audio/avctp.h   |    1 +
 audio/avrcp.c   | 1661 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 audio/avrcp.h   |   34 ++
 audio/control.c | 1636 +------------------------------------------------------
 audio/control.h |   13 +-
 audio/device.c  |    4 +
 audio/device.h  |    2 +
 audio/manager.c |   10 +-
 10 files changed, 1720 insertions(+), 1647 deletions(-)
 create mode 100644 audio/avrcp.c
 create mode 100644 audio/avrcp.h

diff --git a/Makefile.am b/Makefile.am
index ef546d4..6988d03 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -144,6 +144,7 @@ builtin_sources += audio/main.c \
 			audio/headset.h audio/headset.c \
 			audio/control.h audio/control.c \
 			audio/avctp.h audio/avctp.c \
+			audio/avrcp.h audio/avrcp.c \
 			audio/device.h audio/device.c \
 			audio/source.h audio/source.c \
 			audio/sink.h audio/sink.c \
diff --git a/audio/avctp.c b/audio/avctp.c
index 9608d6f..59a0334 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -1026,3 +1026,8 @@ void avctp_disconnect(struct avctp *session)
 
 	avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
 }
+
+struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	return avctp_get_internal(src, dst);
+}
diff --git a/audio/avctp.h b/audio/avctp.h
index 157ecc6..2dab8fa 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -83,6 +83,7 @@ int avctp_register(const bdaddr_t *src, gboolean master);
 void avctp_unregister(const bdaddr_t *src);
 
 struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst);
+struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst);
 void avctp_disconnect(struct avctp *session);
 
 unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
diff --git a/audio/avrcp.c b/audio/avrcp.c
new file mode 100644
index 0000000..78bf491
--- /dev/null
+++ b/audio/avrcp.c
@@ -0,0 +1,1661 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "manager.h"
+#include "avctp.h"
+#include "avrcp.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+#include "dbus-common.h"
+
+/* 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
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+
+/* Notification events */
+#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED			0x02
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID		0x02
+#define CAP_EVENTS_SUPPORTED	0x03
+
+enum player_setting {
+	PLAYER_SETTING_EQUALIZER =	1,
+	PLAYER_SETTING_REPEAT =		2,
+	PLAYER_SETTING_SHUFFLE =	3,
+	PLAYER_SETTING_SCAN =		4,
+};
+
+enum equalizer_mode {
+	EQUALIZER_MODE_OFF =	1,
+	EQUALIZER_MODE_ON =	2,
+};
+
+enum repeat_mode {
+	REPEAT_MODE_OFF =	1,
+	REPEAT_MODE_SINGLE =	2,
+	REPEAT_MODE_ALL =	3,
+	REPEAT_MODE_GROUP =	4,
+};
+
+enum shuffle_mode {
+	SHUFFLE_MODE_OFF =	1,
+	SHUFFLE_MODE_ALL =	2,
+	SHUFFLE_MODE_GROUP =	3,
+};
+
+enum scan_mode {
+	SCAN_MODE_OFF =		1,
+	SCAN_MODE_ALL =		2,
+	SCAN_MODE_GROUP =	3,
+};
+
+enum play_status {
+	PLAY_STATUS_STOPPED =		0x00,
+	PLAY_STATUS_PLAYING =		0x01,
+	PLAY_STATUS_PAUSED =		0x02,
+	PLAY_STATUS_FWD_SEEK =		0x03,
+	PLAY_STATUS_REV_SEEK =		0x04,
+	PLAY_STATUS_ERROR =		0xFF
+};
+
+enum battery_status {
+	BATTERY_STATUS_NORMAL =		0,
+	BATTERY_STATUS_WARNING =	1,
+	BATTERY_STATUS_CRITICAL =	2,
+	BATTERY_STATUS_EXTERNAL =	3,
+	BATTERY_STATUS_FULL_CHARGE =	4,
+};
+
+enum media_info_id {
+	MEDIA_INFO_TITLE =		1,
+	MEDIA_INFO_ARTIST =		2,
+	MEDIA_INFO_ALBUM =		3,
+	MEDIA_INFO_TRACK =		4,
+	MEDIA_INFO_N_TRACKS =		5,
+	MEDIA_INFO_GENRE =		6,
+	MEDIA_INFO_CURRENT_POSITION =	7,
+};
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_spec_avc_pdu {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t packet_type:2;
+	uint8_t rsvd:6;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_spec_avc_pdu {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t rsvd:6;
+	uint8_t packet_type:2;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_SPECAVCPDU_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avrcp_server {
+	bdaddr_t src;
+	uint32_t tg_record_id;
+	uint32_t ct_record_id;
+};
+
+struct media_info {
+	char *title;
+	char *artist;
+	char *album;
+	char *genre;
+	uint32_t ntracks;
+	uint32_t track;
+	uint32_t track_len;
+	uint32_t elapsed;
+};
+
+struct media_player {
+	struct avctp *session;
+	struct audio_device *dev;
+	uint8_t settings[PLAYER_SETTING_SCAN + 1];
+	enum play_status status;
+
+	struct media_info mi;
+	GTimer *timer;
+	unsigned int handler;
+	uint16_t registered_events;
+	uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
+};
+
+static GSList *servers = NULL;
+
+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+	IEEEID_BTSIG,
+};
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrct;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVCTP_PSM;
+	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &avrct);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(0, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP CT", 0, 0);
+
+	free(psm);
+	free(version);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+
+	return record;
+}
+
+static sdp_record_t *avrcp_tg_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrtg;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVCTP_PSM;
+	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &avrtg);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(0, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP TG", 0, 0);
+
+	free(psm);
+	free(version);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+
+	return record;
+}
+
+static unsigned int attr_get_max_val(uint8_t attr)
+{
+	switch (attr) {
+	case PLAYER_SETTING_EQUALIZER:
+		return EQUALIZER_MODE_ON;
+	case PLAYER_SETTING_REPEAT:
+		return REPEAT_MODE_GROUP;
+	case PLAYER_SETTING_SHUFFLE:
+		return SHUFFLE_MODE_GROUP;
+	case PLAYER_SETTING_SCAN:
+		return SCAN_MODE_GROUP;
+	}
+
+	return 0;
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case PLAYER_SETTING_EQUALIZER:
+		switch (value) {
+		case EQUALIZER_MODE_ON:
+			return "on";
+		case EQUALIZER_MODE_OFF:
+			return "off";
+		}
+
+		break;
+	case PLAYER_SETTING_REPEAT:
+		switch (value) {
+		case REPEAT_MODE_OFF:
+			return "off";
+		case REPEAT_MODE_SINGLE:
+			return "singletrack";
+		case REPEAT_MODE_ALL:
+			return "alltracks";
+		case REPEAT_MODE_GROUP:
+			return "group";
+		}
+
+		break;
+	/* Shuffle and scan have the same values */
+	case PLAYER_SETTING_SHUFFLE:
+	case PLAYER_SETTING_SCAN:
+		switch (value) {
+		case SCAN_MODE_OFF:
+			return "off";
+		case SCAN_MODE_ALL:
+			return "alltracks";
+		case SCAN_MODE_GROUP:
+			return "group";
+		}
+
+		break;
+	}
+
+	return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+	int ret;
+
+	switch (attr) {
+	case PLAYER_SETTING_EQUALIZER:
+		if (!strcmp(value, "off"))
+			ret = EQUALIZER_MODE_OFF;
+		else if (!strcmp(value, "on"))
+			ret = EQUALIZER_MODE_ON;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case PLAYER_SETTING_REPEAT:
+		if (!strcmp(value, "off"))
+			ret = REPEAT_MODE_OFF;
+		else if (!strcmp(value, "singletrack"))
+			ret = REPEAT_MODE_SINGLE;
+		else if (!strcmp(value, "alltracks"))
+			ret = REPEAT_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = REPEAT_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case PLAYER_SETTING_SHUFFLE:
+		if (!strcmp(value, "off"))
+			ret = SHUFFLE_MODE_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = SHUFFLE_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = SHUFFLE_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case PLAYER_SETTING_SCAN:
+		if (!strcmp(value, "off"))
+			ret = SCAN_MODE_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = SCAN_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = SCAN_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+	switch (attr) {
+	case PLAYER_SETTING_EQUALIZER:
+		return "Equalizer";
+	case PLAYER_SETTING_REPEAT:
+		return "Repeat";
+	case PLAYER_SETTING_SHUFFLE:
+		return "Shuffle";
+	case PLAYER_SETTING_SCAN:
+		return "Scan";
+	}
+
+	return NULL;
+}
+
+static int attr_to_val(const char *str)
+{
+	if (!strcmp(str, "Equalizer"))
+		return PLAYER_SETTING_EQUALIZER;
+	else if (!strcmp(str, "Repeat"))
+		return PLAYER_SETTING_REPEAT;
+	else if (!strcmp(str, "Shuffle"))
+		return PLAYER_SETTING_SHUFFLE;
+	else if (!strcmp(str, "Scan"))
+		return PLAYER_SETTING_SCAN;
+
+	return -EINVAL;
+}
+
+static int play_status_to_val(const char *status)
+{
+	if (!strcmp(status, "stopped"))
+		return PLAY_STATUS_STOPPED;
+	else if (!strcmp(status, "playing"))
+		return PLAY_STATUS_PLAYING;
+	else if (!strcmp(status, "paused"))
+		return PLAY_STATUS_PAUSED;
+	else if (!strcmp(status, "forward-seek"))
+		return PLAY_STATUS_FWD_SEEK;
+	else if (!strcmp(status, "reverse-seek"))
+		return PLAY_STATUS_REV_SEEK;
+	else if (!strcmp(status, "error"))
+		return PLAY_STATUS_ERROR;
+
+	return -EINVAL;
+}
+
+static const char *battery_status_to_str(enum battery_status status)
+{
+	switch (status) {
+	case BATTERY_STATUS_NORMAL:
+		return "normal";
+	case BATTERY_STATUS_WARNING:
+		return "warning";
+	case BATTERY_STATUS_CRITICAL:
+		return "critical";
+	case BATTERY_STATUS_EXTERNAL:
+		return "external";
+	case BATTERY_STATUS_FULL_CHARGE:
+		return "fullcharge";
+	}
+
+	return NULL;
+}
+
+static int avrcp_send_event(struct media_player *mp, uint8_t id, void *data)
+{
+	uint8_t buf[AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
+	struct avrcp_spec_avc_pdu *pdu = (void *) buf;
+	uint16_t size;
+	int err;
+
+	if (mp->session)
+		return -ENOTCONN;
+
+	if (!(mp->registered_events & (1 << id)))
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->company_id[0] = IEEEID_BTSIG >> 16;
+	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
+	pdu->company_id[2] = IEEEID_BTSIG & 0xFF;
+
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+	pdu->params[0] = id;
+
+	DBG("id=%u", id);
+
+	switch (id) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		size = 2;
+		pdu->params[1] = *((uint8_t *)data);
+
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED: {
+		size = 9;
+
+		/*
+		 * AVRCP 1.3 supports only one track identifier: PLAYING
+		 * (0x0). When 1.4 version is added, this shall be changed to
+		 * contain the identifier of the track.
+		 */
+		memset(&pdu->params[1], 0, 8);
+
+		break;
+	}
+	default:
+		error("Unknown event %u", id);
+		return -EINVAL;
+	}
+
+	pdu->params_len = htons(size);
+
+	err = avctp_send_vendordep(mp->session, mp->transaction_events[id],
+					AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+					buf, size);
+	if (err < 0)
+		return err;
+
+	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+	mp->registered_events ^= 1 << id;
+
+	return 0;
+}
+
+static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
+					uint32_t *elapsed, uint32_t *track_len)
+{
+	if (status)
+		*status = mp->status;
+	if (track_len)
+		*track_len = mp->mi.track_len;
+
+	if (!elapsed)
+		return;
+
+	*elapsed = mp->mi.elapsed;
+
+	if (mp->status == PLAY_STATUS_PLAYING) {
+		double timedelta = g_timer_elapsed(mp->timer, NULL);
+		uint32_t sec, msec;
+
+		sec = (uint32_t) timedelta;
+		msec = (uint32_t)((timedelta - sec) * 1000);
+
+		*elapsed += sec * 1000 + msec;
+	}
+}
+
+static void mp_set_playback_status(struct media_player *mp, uint8_t status,
+							uint32_t elapsed)
+{
+	DBG("Change playback: %u %u", status, elapsed);
+
+	mp->mi.elapsed = elapsed;
+	g_timer_start(mp->timer);
+
+	if (status == mp->status)
+		return;
+
+	mp->status = status;
+
+	avrcp_send_event(mp, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED, &status);
+}
+
+/*
+ * Copy media_info field to a buffer, intended to be used in a response to
+ * GetElementAttributes message.
+ *
+ * It assumes there's enough space in the buffer and on success it returns the
+ * size written.
+ *
+ * If @param id is not valid, -EINVAL is returned. If there's no such media
+ * attribute, -ENOENT is returned.
+ */
+static int mp_get_media_attribute(struct media_player *mp,
+						uint32_t id, uint8_t *buf)
+{
+	struct media_info_elem {
+		uint32_t id;
+		uint16_t charset;
+		uint16_t len;
+		uint8_t val[];
+	};
+	const struct media_info *mi = &mp->mi;
+	struct media_info_elem *elem = (void *)buf;
+	uint16_t len;
+	char valstr[20];
+
+	switch (id) {
+	case MEDIA_INFO_TITLE:
+		if (mi->title) {
+			len = strlen(mi->title);
+			memcpy(elem->val, mi->title, len);
+		} else {
+			len = 0;
+		}
+
+		break;
+	case MEDIA_INFO_ARTIST:
+		if (mi->artist == NULL)
+			return -ENOENT;
+
+		len = strlen(mi->artist);
+		memcpy(elem->val, mi->artist, len);
+		break;
+	case MEDIA_INFO_ALBUM:
+		if (mi->album == NULL)
+			return -ENOENT;
+
+		len = strlen(mi->album);
+		memcpy(elem->val, mi->album, len);
+		break;
+	case MEDIA_INFO_GENRE:
+		if (mi->genre == NULL)
+			return -ENOENT;
+
+		len = strlen(mi->genre);
+		memcpy(elem->val, mi->genre, len);
+		break;
+
+	case MEDIA_INFO_TRACK:
+		if (!mi->track)
+			return -ENOENT;
+
+		snprintf(valstr, 20, "%u", mi->track);
+		len = strlen(valstr);
+		memcpy(elem->val, valstr, len);
+		break;
+	case MEDIA_INFO_N_TRACKS:
+		if (!mi->ntracks)
+			return -ENOENT;
+
+		snprintf(valstr, 20, "%u", mi->ntracks);
+		len = strlen(valstr);
+		memcpy(elem->val, valstr, len);
+		break;
+	case MEDIA_INFO_CURRENT_POSITION:
+		if (mi->elapsed != 0xFFFFFFFF) {
+			uint32_t elapsed;
+
+			mp_get_playback_status(mp, NULL, &elapsed, NULL);
+
+			snprintf(valstr, 20, "%u", elapsed);
+			len = strlen(valstr);
+			memcpy(elem->val, valstr, len);
+		} else {
+			return -ENOENT;
+		}
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	elem->id = htonl(id);
+	elem->charset = htons(0x6A); /* Always use UTF-8 */
+	elem->len = htons(len);
+
+	return sizeof(struct media_info_elem) + len;
+}
+
+static void mp_set_attribute(struct media_player *mp,
+						uint8_t attr, uint8_t val)
+{
+	DBG("Change attribute: %u %u", attr, val);
+
+	mp->settings[attr] = val;
+}
+
+static int mp_get_attribute(struct media_player *mp, uint8_t attr)
+{
+	DBG("Get attribute: %u", attr);
+
+	return mp->settings[attr];
+}
+
+static void mp_set_media_attributes(struct media_player *mp,
+							struct media_info *mi)
+{
+	g_free(mp->mi.title);
+	mp->mi.title = g_strdup(mi->title);
+
+	g_free(mp->mi.artist);
+	mp->mi.artist = g_strdup(mi->artist);
+
+	g_free(mp->mi.album);
+	mp->mi.album = g_strdup(mi->album);
+
+	g_free(mp->mi.genre);
+	mp->mi.genre = g_strdup(mi->genre);
+
+	mp->mi.ntracks = mi->ntracks;
+	mp->mi.track = mi->track;
+	mp->mi.track_len = mi->track_len;
+
+	/*
+	 * elapsed is special. Whenever the track changes, we reset it to 0,
+	 * so client doesn't have to make another call to change_playback
+	 */
+	mp->mi.elapsed = 0;
+	g_timer_start(mp->timer);
+
+	DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
+			"\tgenre: %s\n\tNumber of tracks: %u\n"
+			"\tTrack number: %u\n\tTrack duration: %u",
+			mi->title, mi->artist, mi->album, mi->genre,
+			mi->ntracks, mi->track, mi->track_len);
+
+	avrcp_send_event(mp, AVRCP_EVENT_TRACK_CHANGED, NULL);
+}
+
+static uint8_t avrcp_handle_get_capabilities(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	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 AVC_CTYPE_STABLE;
+	case CAP_EVENTS_SUPPORTED:
+		pdu->params_len = htons(4);
+		pdu->params[1] = 2;
+		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
+		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
+
+		return AVC_CTYPE_STABLE;
+	}
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_list_player_attributes(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 0) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = E_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	if (!mp)
+		goto done;
+
+	for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
+		if (!mp_get_attribute(mp, i)) {
+			DBG("Ignoring setting %u: not supported by player", i);
+			continue;
+		}
+
+		len++;
+		pdu->params[len] = i;
+	}
+
+done:
+	pdu->params[0] = len;
+	pdu->params_len = htons(len + 1);
+
+	return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_list_player_values(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 1 || !mp)
+		goto err;
+
+	len = attr_get_max_val(pdu->params[0]);
+	if (!len) {
+		error("Attribute is invalid: %u", pdu->params[0]);
+		goto err;
+	}
+
+	for (i = 1; i <= len; i++)
+		pdu->params[i] = i;
+
+	pdu->params[0] = len;
+	pdu->params_len = htons(len + 1);
+
+	return AVC_CTYPE_STABLE;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_element_attributes(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	uint64_t *identifier = (void *) &pdu->params[0];
+	uint16_t pos;
+	uint8_t nattr;
+	int size;
+	unsigned int i;
+
+	if (len < 8 || *identifier != 0)
+		goto err;
+
+	len = 0;
+	pos = 1; /* Keep track of current position in reponse */
+	nattr = pdu->params[8];
+
+	if (!nattr) {
+		/*
+		 * Return all available information, at least
+		 * title must be returned.
+		 */
+		for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
+			size = mp_get_media_attribute(mp, i,
+							&pdu->params[pos]);
+
+			if (size > 0) {
+				len++;
+				pos += size;
+			}
+		}
+	} else {
+		uint32_t *attr_ids;
+
+		attr_ids = g_memdup(&pdu->params[9], sizeof(uint32_t) * nattr);
+
+		for (i = 0; i < nattr; i++) {
+			uint32_t attr = ntohl(attr_ids[i]);
+
+			size = mp_get_media_attribute(mp, attr,
+							&pdu->params[pos]);
+
+			if (size > 0) {
+				len++;
+				pos += size;
+			}
+		}
+
+		g_free(attr_ids);
+
+		if (!len)
+			goto err;
+	}
+
+	pdu->params[0] = len;
+	pdu->params_len = htons(pos);
+
+	return AVC_CTYPE_STABLE;
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_current_player_value(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	uint8_t *settings;
+	unsigned int i;
+
+	if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
+		goto err;
+
+	/*
+	 * Save a copy of requested settings because we can override them
+	 * while responding
+	 */
+	settings = g_malloc(pdu->params[0]);
+	memcpy(settings, &pdu->params[1], pdu->params[0]);
+	len = 0;
+
+	/*
+	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+	 * and send a response with the existent ones. Only if all IDs are
+	 * non-existent we should send an error.
+	 */
+	for (i = 0; i < pdu->params[0]; i++) {
+		uint8_t val;
+
+		if (settings[i] < PLAYER_SETTING_EQUALIZER ||
+					settings[i] > PLAYER_SETTING_SCAN) {
+			DBG("Ignoring %u", settings[i]);
+			continue;
+		}
+
+		val = mp_get_attribute(mp, settings[i]);
+		if (!val) {
+			DBG("Ignoring %u: not supported by player",
+								settings[i]);
+			continue;
+		}
+
+		pdu->params[len] = settings[i];
+		pdu->params[len + 1] = val;
+		len += 2;
+	}
+
+	g_free(settings);
+
+	if (len) {
+		pdu->params[0] = len;
+		pdu->params_len = htons(2 * len + 1);
+
+		return AVC_CTYPE_STABLE;
+	}
+
+	error("No valid attributes in request");
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_set_player_value(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len < 3)
+		goto err;
+
+	len = 0;
+
+	/*
+	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+	 * and set the existent ones. Sec. 5.2.4 is not clear however how to
+	 * indicate that a certain ID was not accepted. If at least one
+	 * attribute is valid, we respond with no parameters. Otherwise an
+	 * E_INVALID_PARAM is sent.
+	 */
+	for (i = 1; i < pdu->params[0]; i += 2) {
+		uint8_t attr = pdu->params[i];
+		uint8_t val = pdu->params[i + 1];
+		const char *attrstr;
+		const char *valstr;
+
+		attrstr = attr_to_str(attr);
+		if (!attrstr)
+			continue;
+
+		valstr = attrval_to_str(attr, val);
+		if (!valstr)
+			continue;
+
+		len++;
+
+		mp_set_attribute(mp, attr, val);
+		emit_property_changed(mp->dev->conn, mp->dev->path,
+					MEDIA_PLAYER_INTERFACE, attrstr,
+					DBUS_TYPE_STRING, &valstr);
+	}
+
+	if (len) {
+		pdu->params_len = 0;
+
+		return AVC_CTYPE_STABLE;
+	}
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_displayable_charset(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+
+	if (len < 3) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = E_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	/*
+	 * We acknowledge the commands, but we always use UTF-8 for
+	 * encoding since CT is obliged to support it.
+	 */
+	pdu->params_len = 0;
+	return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_ct_battery_status(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	const char *valstr;
+
+	if (len != 1)
+		goto err;
+
+	valstr = battery_status_to_str(pdu->params[0]);
+	if (valstr == NULL)
+		goto err;
+
+	emit_property_changed(mp->dev->conn, mp->dev->path,
+					MEDIA_PLAYER_INTERFACE, "Battery",
+					DBUS_TYPE_STRING, &valstr);
+	pdu->params_len = 0;
+
+	return AVC_CTYPE_STABLE;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_play_status(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	uint32_t elapsed;
+	uint32_t track_len;
+	uint8_t status;
+
+	if (len != 0) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = E_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	mp_get_playback_status(mp, &status, &elapsed, &track_len);
+	track_len = htonl(track_len);
+	elapsed = htonl(elapsed);
+
+	memcpy(&pdu->params[0], &track_len, 4);
+	memcpy(&pdu->params[4], &elapsed, 4);
+	pdu->params[8] = status;
+
+	pdu->params_len = htons(9);
+
+	return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_register_notification(struct media_player *mp,
+						struct avrcp_spec_avc_pdu *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	uint8_t status;
+
+	/*
+	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
+	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
+	 * 1.3 spec, section 5.4.2.
+	 */
+	if (len != 5)
+		goto err;
+
+	switch (pdu->params[0]) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		len = 2;
+		mp_get_playback_status(mp, &status, NULL, NULL);
+		pdu->params[1] = status;
+
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		len = 9;
+
+		memset(&pdu->params[1], 0, 8);
+
+		break;
+	default:
+		/* All other events are not supported yet */
+		goto err;
+	}
+
+	/* Register event and save the transaction used */
+	mp->registered_events |= (1 << pdu->params[0]);
+	mp->transaction_events[pdu->params[0]] = transaction;
+
+	pdu->params_len = htons(len);
+
+	return AVC_CTYPE_INTERIM;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = E_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static struct pdu_handler {
+	uint8_t pdu_id;
+	uint8_t code;
+	uint8_t (*func) (struct media_player *mp,
+					struct avrcp_spec_avc_pdu *pdu,
+					uint8_t transaction);
+} handlers[] = {
+		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
+					avrcp_handle_get_capabilities },
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
+					avrcp_handle_list_player_attributes },
+		{ AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
+					avrcp_handle_list_player_values },
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
+					avrcp_handle_get_element_attributes },
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
+					avrcp_handle_get_current_player_value },
+		{ AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
+					avrcp_handle_set_player_value },
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
+					NULL },
+		{ AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
+					NULL },
+		{ AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
+					avrcp_handle_displayable_charset },
+		{ AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
+					avrcp_handle_ct_battery_status },
+		{ AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
+					avrcp_handle_get_play_status },
+		{ AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
+					avrcp_handle_register_notification },
+		{ },
+};
+
+/* handle vendordep pdu inside an avctp packet */
+static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct media_player *mp = user_data;
+	struct pdu_handler *handler;
+	struct avrcp_spec_avc_pdu *pdu = (void *) operands;
+	uint32_t company_id = (pdu->company_id[0] << 16) |
+				(pdu->company_id[1] << 8) |
+				(pdu->company_id[2]);
+
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+			pdu->pdu_id, company_id, pdu->params_len);
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH)
+		goto err_metadata;
+
+	for (handler = handlers; handler; handler++) {
+		if (handler->pdu_id == pdu->pdu_id)
+			break;
+	}
+
+	if (!handler || handler->code != *code) {
+		pdu->params[0] = E_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = E_INVALID_PARAM;
+		goto err_metadata;
+	}
+
+	*code = handler->func(mp, pdu, transaction);
+
+	return AVRCP_SPECAVCPDU_HEADER_LENGTH + ntohs(pdu->params_len);
+
+err_metadata:
+	pdu->params_len = htons(1);
+	*code = AVC_CTYPE_REJECTED;
+
+	return AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
+}
+
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+				avctp_state_t new_state, void *user_data)
+{
+	struct media_player *mp = dev->media_player;
+
+
+	if (!mp)
+		return;
+
+	switch (new_state) {
+	case AVCTP_STATE_DISCONNECTED:
+		mp->session = NULL;
+
+		if (mp->handler) {
+			avctp_unregister_pdu_handler(mp->handler);
+			mp->handler = 0;
+		}
+
+		break;
+	case AVCTP_STATE_CONNECTING:
+		mp->session = avctp_connect(&dev->src, &dev->dst);
+
+		if (!mp->handler)
+			mp->handler = avctp_register_pdu_handler(
+							AVC_OP_VENDORDEP,
+							handle_vendordep_pdu,
+							mp);
+		break;
+	default:
+		return;
+	}
+}
+
+static void media_info_init(struct media_info *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+
+	/*
+	 * As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG
+	 * does not support these attributes (i.e. they were never set via
+	 * D-Bus)
+	 */
+	mi->track_len = 0xFFFFFFFF;
+	mi->elapsed = 0xFFFFFFFF;
+}
+
+gboolean avrcp_connect(struct audio_device *dev)
+{
+	struct avctp *session;
+
+	session = avctp_connect(&dev->src, &dev->dst);
+	if (session)
+		return FALSE;
+
+	return TRUE;
+}
+
+void avrcp_disconnect(struct audio_device *dev)
+{
+	struct avctp *session;
+
+	session = avctp_get(&dev->src, &dev->dst);
+	if (!session)
+		return;
+
+	avctp_disconnect(session);
+}
+
+static unsigned int avctp_id = 0;
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
+{
+	sdp_record_t *record;
+	gboolean tmp, master = TRUE;
+	GError *err = NULL;
+	struct avrcp_server *server;
+
+	if (config) {
+		tmp = g_key_file_get_boolean(config, "General",
+							"Master", &err);
+		if (err) {
+			DBG("audio.conf: %s", err->message);
+			g_error_free(err);
+		} else
+			master = tmp;
+	}
+
+	server = g_new0(struct avrcp_server, 1);
+	if (!server)
+		return -ENOMEM;
+
+	record = avrcp_tg_record();
+	if (!record) {
+		error("Unable to allocate new service record");
+		g_free(server);
+		return -1;
+	}
+
+	if (add_record_to_server(src, record) < 0) {
+		error("Unable to register AVRCP target service record");
+		g_free(server);
+		sdp_record_free(record);
+		return -1;
+	}
+	server->tg_record_id = record->handle;
+
+	record = avrcp_ct_record();
+	if (!record) {
+		error("Unable to allocate new service record");
+		g_free(server);
+		return -1;
+	}
+
+	if (add_record_to_server(src, record) < 0) {
+		error("Unable to register AVRCP mpler service record");
+		sdp_record_free(record);
+		g_free(server);
+		return -1;
+	}
+	server->ct_record_id = record->handle;
+
+	if (avctp_register(src, master) < 0) {
+		remove_record_from_server(server->ct_record_id);
+		remove_record_from_server(server->tg_record_id);
+		g_free(server);
+		return -1;
+	}
+
+	bacpy(&server->src, src);
+
+	servers = g_slist_append(servers, server);
+
+	return 0;
+}
+
+static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+	for (; list; list = list->next) {
+		struct avrcp_server *server = list->data;
+
+		if (bacmp(&server->src, src) == 0)
+			return server;
+	}
+
+	return NULL;
+}
+
+void avrcp_unregister(const bdaddr_t *src)
+{
+	struct avrcp_server *server;
+
+	server = find_server(servers, src);
+	if (!server)
+		return;
+
+	servers = g_slist_remove(servers, server);
+
+	remove_record_from_server(server->ct_record_id);
+	remove_record_from_server(server->tg_record_id);
+
+	avctp_unregister(&server->src);
+	g_free(server);
+
+	if (servers)
+		return;
+
+	if (avctp_id)
+		avctp_remove_state_cb(avctp_id);
+}
+
+static DBusMessage *mp_set_property(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct media_player *mp = device->media_player;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *attrstr, *valstr;
+	int attr, val;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &attrstr);
+
+	attr = attr_to_val(attrstr);
+	if (attr < 0)
+		return btd_error_not_supported(msg);
+
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&iter, &var);
+
+	/* Only string arguments are supported for now */
+	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&var, &valstr);
+
+	val = attrval_to_val(attr, valstr);
+	if (val < 0)
+		return btd_error_not_supported(msg);
+
+	mp_set_attribute(mp, attr, val);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *mp_change_playback(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct media_player *mp = device->media_player;
+	const char *statusstr;
+	int status;
+	uint32_t elapsed;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr,
+						DBUS_TYPE_UINT32, &elapsed,
+						DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	status = play_status_to_val(statusstr);
+	if (status < 0)
+		return btd_error_invalid_args(msg);
+
+	mp_set_playback_status(mp, status, elapsed);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi)
+{
+	DBusMessageIter dict;
+	DBusMessageIter var;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	media_info_init(mi);
+	dbus_message_iter_recurse(iter, &dict);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return FALSE;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return FALSE;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+			return FALSE;
+
+		dbus_message_iter_recurse(&entry, &var);
+
+		if (!strcmp(key, "Title")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_STRING)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->title);
+		} else if (!strcmp(key, "Artist")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_STRING)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->artist);
+		} else if (!strcmp(key, "Album")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_STRING)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->album);
+		} else if (!strcmp(key, "Genre")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_STRING)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->genre);
+		} else if (!strcmp(key, "NumberOfTracks")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_UINT32)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->ntracks);
+		} else if (!strcmp(key, "TrackNumber")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_UINT32)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->track);
+		} else if (!strcmp(key, "TrackDuration")) {
+			if (dbus_message_iter_get_arg_type(&var) !=
+							DBUS_TYPE_UINT32)
+				return FALSE;
+
+			dbus_message_iter_get_basic(&var, &mi->track_len);
+		} else {
+			return FALSE;
+		}
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (mi->title == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static DBusMessage *mp_change_track(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct media_player *mp = device->media_player;
+	DBusMessageIter iter;
+	struct media_info mi;
+
+
+	dbus_message_iter_init(msg, &iter);
+	if (!media_info_parse(&iter, &mi))
+		return btd_error_invalid_args(msg);
+
+	mp_set_media_attributes(mp, &mi);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable mp_methods[] = {
+	{ "SetProperty",	"sv",		"",	mp_set_property },
+	{ "ChangePlayback",	"su",		"",	mp_change_playback },
+	{ "ChangeTrack",	"a{sv}",	"",	mp_change_track },
+	{ }
+};
+
+static GDBusSignalTable mp_signals[] = {
+	{ "PropertyChanged",		"sv"	},
+	{ }
+};
+
+static void mp_path_unregister(void *data)
+{
+	struct audio_device *dev = data;
+	struct media_player *mp = dev->media_player;
+
+	DBG("Unregistered interface %s on path %s",
+		MEDIA_PLAYER_INTERFACE, dev->path);
+
+	if (mp->handler)
+		avctp_unregister_pdu_handler(mp->handler);
+
+	g_timer_destroy(mp->timer);
+	g_free(mp);
+}
+
+void media_player_unregister(struct audio_device *dev)
+{
+	g_dbus_unregister_interface(dev->conn, dev->path,
+						MEDIA_PLAYER_INTERFACE);
+}
+
+struct media_player *media_player_init(struct audio_device *dev)
+{
+	struct media_player *mp;
+
+	if (!g_dbus_register_interface(dev->conn, dev->path,
+						MEDIA_PLAYER_INTERFACE,
+						mp_methods, mp_signals, NULL,
+						dev, mp_path_unregister)) {
+		error("D-Bus failed do register %s on path %s",
+					MEDIA_PLAYER_INTERFACE, dev->path);
+		return NULL;
+	}
+
+	DBG("Registered interface %s on path %s",
+					MEDIA_PLAYER_INTERFACE, dev->path);
+
+	mp = g_new0(struct media_player, 1);
+	mp->timer = g_timer_new();
+	mp->dev = dev;
+	media_info_init(&mp->mi);
+
+	if (!avctp_id)
+		avctp_id = avctp_add_state_cb(state_changed, NULL);
+
+	return mp;
+}
diff --git a/audio/avrcp.h b/audio/avrcp.h
new file mode 100644
index 0000000..1fd912d
--- /dev/null
+++ b/audio/avrcp.h
@@ -0,0 +1,34 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void avrcp_unregister(const bdaddr_t *src);
+
+gboolean avrcp_connect(struct audio_device *dev);
+void avrcp_disconnect(struct audio_device *dev);
+
+struct media_player *media_player_init(struct audio_device *dev);
+void media_player_unregister(struct audio_device *dev);
diff --git a/audio/control.c b/audio/control.c
index dceb004..a75e992 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -55,1245 +55,15 @@
 #include "glib-helper.h"
 #include "dbus-common.h"
 
-/* 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
-#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
-#define AVRCP_LIST_PLAYER_VALUES	0x12
-#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
-#define AVRCP_SET_PLAYER_VALUE		0x14
-#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
-#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
-#define AVRCP_DISPLAYABLE_CHARSET	0x17
-#define AVRCP_CT_BATTERY_STATUS		0x18
-#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
-#define AVRCP_GET_PLAY_STATUS		0x30
-#define AVRCP_REGISTER_NOTIFICATION	0x31
-
-/* Notification events */
-#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
-#define AVRCP_EVENT_TRACK_CHANGED			0x02
-
-/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
-#define CAP_COMPANY_ID		0x02
-#define CAP_EVENTS_SUPPORTED	0x03
-
-enum player_setting {
-	PLAYER_SETTING_EQUALIZER =	1,
-	PLAYER_SETTING_REPEAT =		2,
-	PLAYER_SETTING_SHUFFLE =	3,
-	PLAYER_SETTING_SCAN =		4,
-};
-
-enum equalizer_mode {
-	EQUALIZER_MODE_OFF =	1,
-	EQUALIZER_MODE_ON =	2,
-};
-
-enum repeat_mode {
-	REPEAT_MODE_OFF =	1,
-	REPEAT_MODE_SINGLE =	2,
-	REPEAT_MODE_ALL =	3,
-	REPEAT_MODE_GROUP =	4,
-};
-
-enum shuffle_mode {
-	SHUFFLE_MODE_OFF =	1,
-	SHUFFLE_MODE_ALL =	2,
-	SHUFFLE_MODE_GROUP =	3,
-};
-
-enum scan_mode {
-	SCAN_MODE_OFF =		1,
-	SCAN_MODE_ALL =		2,
-	SCAN_MODE_GROUP =	3,
-};
-
-enum play_status {
-	PLAY_STATUS_STOPPED =		0x00,
-	PLAY_STATUS_PLAYING =		0x01,
-	PLAY_STATUS_PAUSED =		0x02,
-	PLAY_STATUS_FWD_SEEK =		0x03,
-	PLAY_STATUS_REV_SEEK =		0x04,
-	PLAY_STATUS_ERROR =		0xFF
-};
-
-enum battery_status {
-	BATTERY_STATUS_NORMAL =		0,
-	BATTERY_STATUS_WARNING =	1,
-	BATTERY_STATUS_CRITICAL =	2,
-	BATTERY_STATUS_EXTERNAL =	3,
-	BATTERY_STATUS_FULL_CHARGE =	4,
-};
-
-enum media_info_id {
-	MEDIA_INFO_TITLE =		1,
-	MEDIA_INFO_ARTIST =		2,
-	MEDIA_INFO_ALBUM =		3,
-	MEDIA_INFO_TRACK =		4,
-	MEDIA_INFO_N_TRACKS =		5,
-	MEDIA_INFO_GENRE =		6,
-	MEDIA_INFO_CURRENT_POSITION =	7,
-};
-
-static DBusConnection *connection = NULL;
-
-static GSList *servers = NULL;
 static unsigned int avctp_id = 0;
 
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-struct avrcp_header {
-	uint8_t company_id[3];
-	uint8_t pdu_id;
-	uint8_t packet_type:2;
-	uint8_t rsvd:6;
-	uint16_t params_len;
-	uint8_t params[0];
-} __attribute__ ((packed));
-#define AVRCP_HEADER_LENGTH 7
-
-#elif __BYTE_ORDER == __BIG_ENDIAN
-
-struct avrcp_header {
-	uint8_t company_id[3];
-	uint8_t pdu_id;
-	uint8_t rsvd:6;
-	uint8_t packet_type:2;
-	uint16_t params_len;
-	uint8_t params[0];
-} __attribute__ ((packed));
-#define AVRCP_HEADER_LENGTH 7
-
-#else
-#error "Unknown byte order"
-#endif
-
-struct avrcp_server {
-	bdaddr_t src;
-	uint32_t tg_record_id;
-	uint32_t ct_record_id;
-};
-
-struct media_info {
-	char *title;
-	char *artist;
-	char *album;
-	char *genre;
-	uint32_t ntracks;
-	uint32_t track;
-	uint32_t track_len;
-	uint32_t elapsed;
-};
-
-struct media_player {
-	uint8_t settings[PLAYER_SETTING_SCAN + 1];
-	enum play_status status;
-
-	struct media_info mi;
-	GTimer *timer;
-	unsigned int handler;
-};
-
 struct control {
 	struct audio_device *dev;
-	struct media_player *mp;
 	struct avctp *session;
 
 	gboolean target;
-
-	uint16_t registered_events;
-	uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
 };
 
-/* Company IDs supported by this device */
-static uint32_t company_ids[] = {
-	IEEEID_BTSIG,
-};
-
-static sdp_record_t *avrcp_ct_record(void)
-{
-	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
-	uuid_t root_uuid, l2cap, avctp, avrct;
-	sdp_profile_desc_t profile[1];
-	sdp_list_t *aproto, *proto[2];
-	sdp_record_t *record;
-	sdp_data_t *psm, *version, *features;
-	uint16_t lp = AVCTP_PSM;
-	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
-
-	record = sdp_record_alloc();
-	if (!record)
-		return NULL;
-
-	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-	root = sdp_list_append(0, &root_uuid);
-	sdp_set_browse_groups(record, root);
-
-	/* Service Class ID List */
-	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
-	svclass_id = sdp_list_append(0, &avrct);
-	sdp_set_service_classes(record, svclass_id);
-
-	/* Protocol Descriptor List */
-	sdp_uuid16_create(&l2cap, L2CAP_UUID);
-	proto[0] = sdp_list_append(0, &l2cap);
-	psm = sdp_data_alloc(SDP_UINT16, &lp);
-	proto[0] = sdp_list_append(proto[0], psm);
-	apseq = sdp_list_append(0, proto[0]);
-
-	sdp_uuid16_create(&avctp, AVCTP_UUID);
-	proto[1] = sdp_list_append(0, &avctp);
-	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
-	proto[1] = sdp_list_append(proto[1], version);
-	apseq = sdp_list_append(apseq, proto[1]);
-
-	aproto = sdp_list_append(0, apseq);
-	sdp_set_access_protos(record, aproto);
-
-	/* Bluetooth Profile Descriptor List */
-	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
-	profile[0].version = avrcp_ver;
-	pfseq = sdp_list_append(0, &profile[0]);
-	sdp_set_profile_descs(record, pfseq);
-
-	features = sdp_data_alloc(SDP_UINT16, &feat);
-	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
-
-	sdp_set_info_attr(record, "AVRCP CT", 0, 0);
-
-	free(psm);
-	free(version);
-	sdp_list_free(proto[0], 0);
-	sdp_list_free(proto[1], 0);
-	sdp_list_free(apseq, 0);
-	sdp_list_free(pfseq, 0);
-	sdp_list_free(aproto, 0);
-	sdp_list_free(root, 0);
-	sdp_list_free(svclass_id, 0);
-
-	return record;
-}
-
-static sdp_record_t *avrcp_tg_record(void)
-{
-	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
-	uuid_t root_uuid, l2cap, avctp, avrtg;
-	sdp_profile_desc_t profile[1];
-	sdp_list_t *aproto, *proto[2];
-	sdp_record_t *record;
-	sdp_data_t *psm, *version, *features;
-	uint16_t lp = AVCTP_PSM;
-	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;
-
-	record = sdp_record_alloc();
-	if (!record)
-		return NULL;
-
-	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-	root = sdp_list_append(0, &root_uuid);
-	sdp_set_browse_groups(record, root);
-
-	/* Service Class ID List */
-	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
-	svclass_id = sdp_list_append(0, &avrtg);
-	sdp_set_service_classes(record, svclass_id);
-
-	/* Protocol Descriptor List */
-	sdp_uuid16_create(&l2cap, L2CAP_UUID);
-	proto[0] = sdp_list_append(0, &l2cap);
-	psm = sdp_data_alloc(SDP_UINT16, &lp);
-	proto[0] = sdp_list_append(proto[0], psm);
-	apseq = sdp_list_append(0, proto[0]);
-
-	sdp_uuid16_create(&avctp, AVCTP_UUID);
-	proto[1] = sdp_list_append(0, &avctp);
-	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
-	proto[1] = sdp_list_append(proto[1], version);
-	apseq = sdp_list_append(apseq, proto[1]);
-
-	aproto = sdp_list_append(0, apseq);
-	sdp_set_access_protos(record, aproto);
-
-	/* Bluetooth Profile Descriptor List */
-	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
-	profile[0].version = avrcp_ver;
-	pfseq = sdp_list_append(0, &profile[0]);
-	sdp_set_profile_descs(record, pfseq);
-
-	features = sdp_data_alloc(SDP_UINT16, &feat);
-	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
-
-	sdp_set_info_attr(record, "AVRCP TG", 0, 0);
-
-	free(psm);
-	free(version);
-	sdp_list_free(proto[0], 0);
-	sdp_list_free(proto[1], 0);
-	sdp_list_free(apseq, 0);
-	sdp_list_free(aproto, 0);
-	sdp_list_free(pfseq, 0);
-	sdp_list_free(root, 0);
-	sdp_list_free(svclass_id, 0);
-
-	return record;
-}
-
-static unsigned int attr_get_max_val(uint8_t attr)
-{
-	switch (attr) {
-	case PLAYER_SETTING_EQUALIZER:
-		return EQUALIZER_MODE_ON;
-	case PLAYER_SETTING_REPEAT:
-		return REPEAT_MODE_GROUP;
-	case PLAYER_SETTING_SHUFFLE:
-		return SHUFFLE_MODE_GROUP;
-	case PLAYER_SETTING_SCAN:
-		return SCAN_MODE_GROUP;
-	}
-
-	return 0;
-}
-
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
-	switch (attr) {
-	case PLAYER_SETTING_EQUALIZER:
-		switch (value) {
-		case EQUALIZER_MODE_ON:
-			return "on";
-		case EQUALIZER_MODE_OFF:
-			return "off";
-		}
-
-		break;
-	case PLAYER_SETTING_REPEAT:
-		switch (value) {
-		case REPEAT_MODE_OFF:
-			return "off";
-		case REPEAT_MODE_SINGLE:
-			return "singletrack";
-		case REPEAT_MODE_ALL:
-			return "alltracks";
-		case REPEAT_MODE_GROUP:
-			return "group";
-		}
-
-		break;
-	/* Shuffle and scan have the same values */
-	case PLAYER_SETTING_SHUFFLE:
-	case PLAYER_SETTING_SCAN:
-		switch (value) {
-		case SCAN_MODE_OFF:
-			return "off";
-		case SCAN_MODE_ALL:
-			return "alltracks";
-		case SCAN_MODE_GROUP:
-			return "group";
-		}
-
-		break;
-	}
-
-	return NULL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
-	int ret;
-
-	switch (attr) {
-	case PLAYER_SETTING_EQUALIZER:
-		if (!strcmp(value, "off"))
-			ret = EQUALIZER_MODE_OFF;
-		else if (!strcmp(value, "on"))
-			ret = EQUALIZER_MODE_ON;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case PLAYER_SETTING_REPEAT:
-		if (!strcmp(value, "off"))
-			ret = REPEAT_MODE_OFF;
-		else if (!strcmp(value, "singletrack"))
-			ret = REPEAT_MODE_SINGLE;
-		else if (!strcmp(value, "alltracks"))
-			ret = REPEAT_MODE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = REPEAT_MODE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case PLAYER_SETTING_SHUFFLE:
-		if (!strcmp(value, "off"))
-			ret = SHUFFLE_MODE_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = SHUFFLE_MODE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = SHUFFLE_MODE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case PLAYER_SETTING_SCAN:
-		if (!strcmp(value, "off"))
-			ret = SCAN_MODE_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = SCAN_MODE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = SCAN_MODE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	}
-
-	return -EINVAL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
-	switch (attr) {
-	case PLAYER_SETTING_EQUALIZER:
-		return "Equalizer";
-	case PLAYER_SETTING_REPEAT:
-		return "Repeat";
-	case PLAYER_SETTING_SHUFFLE:
-		return "Shuffle";
-	case PLAYER_SETTING_SCAN:
-		return "Scan";
-	}
-
-	return NULL;
-}
-
-static int attr_to_val(const char *str)
-{
-	if (!strcmp(str, "Equalizer"))
-		return PLAYER_SETTING_EQUALIZER;
-	else if (!strcmp(str, "Repeat"))
-		return PLAYER_SETTING_REPEAT;
-	else if (!strcmp(str, "Shuffle"))
-		return PLAYER_SETTING_SHUFFLE;
-	else if (!strcmp(str, "Scan"))
-		return PLAYER_SETTING_SCAN;
-
-	return -EINVAL;
-}
-
-static int play_status_to_val(const char *status)
-{
-	if (!strcmp(status, "stopped"))
-		return PLAY_STATUS_STOPPED;
-	else if (!strcmp(status, "playing"))
-		return PLAY_STATUS_PLAYING;
-	else if (!strcmp(status, "paused"))
-		return PLAY_STATUS_PAUSED;
-	else if (!strcmp(status, "forward-seek"))
-		return PLAY_STATUS_FWD_SEEK;
-	else if (!strcmp(status, "reverse-seek"))
-		return PLAY_STATUS_REV_SEEK;
-	else if (!strcmp(status, "error"))
-		return PLAY_STATUS_ERROR;
-
-	return -EINVAL;
-}
-
-static const char *battery_status_to_str(enum battery_status status)
-{
-	switch (status) {
-	case BATTERY_STATUS_NORMAL:
-		return "normal";
-	case BATTERY_STATUS_WARNING:
-		return "warning";
-	case BATTERY_STATUS_CRITICAL:
-		return "critical";
-	case BATTERY_STATUS_EXTERNAL:
-		return "external";
-	case BATTERY_STATUS_FULL_CHARGE:
-		return "fullcharge";
-	}
-
-	return NULL;
-}
-
-static int avrcp_send_event(struct control *control, uint8_t id, void *data)
-{
-	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
-	struct avrcp_header *pdu = (void *) buf;
-	uint16_t size;
-	int err;
-
-	if (control->session)
-		return -ENOTCONN;
-
-	if (!(control->registered_events & (1 << id)))
-		return 0;
-
-	memset(buf, 0, sizeof(buf));
-
-	pdu->company_id[0] = IEEEID_BTSIG >> 16;
-	pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
-	pdu->company_id[2] = IEEEID_BTSIG & 0xFF;
-
-	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
-	pdu->params[0] = id;
-
-	DBG("id=%u", id);
-
-	switch (id) {
-	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
-		size = 2;
-		pdu->params[1] = *((uint8_t *)data);
-
-		break;
-	case AVRCP_EVENT_TRACK_CHANGED: {
-		size = 9;
-
-		/*
-		 * AVRCP 1.3 supports only one track identifier: PLAYING
-		 * (0x0). When 1.4 version is added, this shall be changed to
-		 * contain the identifier of the track.
-		 */
-		memset(&pdu->params[1], 0, 8);
-
-		break;
-	}
-	default:
-		error("Unknown event %u", id);
-		return -EINVAL;
-	}
-
-	pdu->params_len = htons(size);
-
-	err = avctp_send_vendordep(control->session, control->transaction_events[id],
-					AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
-					buf, size);
-	if (err < 0)
-		return err;
-
-	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
-	control->registered_events ^= 1 << id;
-
-	return 0;
-}
-
-static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
-					uint32_t *elapsed, uint32_t *track_len)
-{
-	if (status)
-		*status = mp->status;
-	if (track_len)
-		*track_len = mp->mi.track_len;
-
-	if (!elapsed)
-		return;
-
-	*elapsed = mp->mi.elapsed;
-
-	if (mp->status == PLAY_STATUS_PLAYING) {
-		double timedelta = g_timer_elapsed(mp->timer, NULL);
-		uint32_t sec, msec;
-
-		sec = (uint32_t) timedelta;
-		msec = (uint32_t)((timedelta - sec) * 1000);
-
-		*elapsed += sec * 1000 + msec;
-	}
-}
-
-static void mp_set_playback_status(struct control *control, uint8_t status,
-							uint32_t elapsed)
-{
-	struct media_player *mp = control->mp;
-
-	DBG("Change playback: %u %u", status, elapsed);
-
-	mp->mi.elapsed = elapsed;
-	g_timer_start(mp->timer);
-
-	if (status == mp->status)
-		return;
-
-	mp->status = status;
-
-	avrcp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
-								&status);
-}
-
-/*
- * Copy media_info field to a buffer, intended to be used in a response to
- * GetElementAttributes message.
- *
- * It assumes there's enough space in the buffer and on success it returns the
- * size written.
- *
- * If @param id is not valid, -EINVAL is returned. If there's no such media
- * attribute, -ENOENT is returned.
- */
-static int mp_get_media_attribute(struct media_player *mp,
-						uint32_t id, uint8_t *buf)
-{
-	struct media_info_elem {
-		uint32_t id;
-		uint16_t charset;
-		uint16_t len;
-		uint8_t val[];
-	};
-	const struct media_info *mi = &mp->mi;
-	struct media_info_elem *elem = (void *)buf;
-	uint16_t len;
-	char valstr[20];
-
-	switch (id) {
-	case MEDIA_INFO_TITLE:
-		if (mi->title) {
-			len = strlen(mi->title);
-			memcpy(elem->val, mi->title, len);
-		} else {
-			len = 0;
-		}
-
-		break;
-	case MEDIA_INFO_ARTIST:
-		if (mi->artist == NULL)
-			return -ENOENT;
-
-		len = strlen(mi->artist);
-		memcpy(elem->val, mi->artist, len);
-		break;
-	case MEDIA_INFO_ALBUM:
-		if (mi->album == NULL)
-			return -ENOENT;
-
-		len = strlen(mi->album);
-		memcpy(elem->val, mi->album, len);
-		break;
-	case MEDIA_INFO_GENRE:
-		if (mi->genre == NULL)
-			return -ENOENT;
-
-		len = strlen(mi->genre);
-		memcpy(elem->val, mi->genre, len);
-		break;
-
-	case MEDIA_INFO_TRACK:
-		if (!mi->track)
-			return -ENOENT;
-
-		snprintf(valstr, 20, "%u", mi->track);
-		len = strlen(valstr);
-		memcpy(elem->val, valstr, len);
-		break;
-	case MEDIA_INFO_N_TRACKS:
-		if (!mi->ntracks)
-			return -ENOENT;
-
-		snprintf(valstr, 20, "%u", mi->ntracks);
-		len = strlen(valstr);
-		memcpy(elem->val, valstr, len);
-		break;
-	case MEDIA_INFO_CURRENT_POSITION:
-		if (mi->elapsed != 0xFFFFFFFF) {
-			uint32_t elapsed;
-
-			mp_get_playback_status(mp, NULL, &elapsed, NULL);
-
-			snprintf(valstr, 20, "%u", elapsed);
-			len = strlen(valstr);
-			memcpy(elem->val, valstr, len);
-		} else {
-			return -ENOENT;
-		}
-
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	elem->id = htonl(id);
-	elem->charset = htons(0x6A); /* Always use UTF-8 */
-	elem->len = htons(len);
-
-	return sizeof(struct media_info_elem) + len;
-}
-
-static void mp_set_attribute(struct media_player *mp,
-						uint8_t attr, uint8_t val)
-{
-	DBG("Change attribute: %u %u", attr, val);
-
-	mp->settings[attr] = val;
-}
-
-static int mp_get_attribute(struct media_player *mp, uint8_t attr)
-{
-	DBG("Get attribute: %u", attr);
-
-	return mp->settings[attr];
-}
-
-static void mp_set_media_attributes(struct control *control,
-							struct media_info *mi)
-{
-	struct media_player *mp = control->mp;
-
-	g_free(mp->mi.title);
-	mp->mi.title = g_strdup(mi->title);
-
-	g_free(mp->mi.artist);
-	mp->mi.artist = g_strdup(mi->artist);
-
-	g_free(mp->mi.album);
-	mp->mi.album = g_strdup(mi->album);
-
-	g_free(mp->mi.genre);
-	mp->mi.genre = g_strdup(mi->genre);
-
-	mp->mi.ntracks = mi->ntracks;
-	mp->mi.track = mi->track;
-	mp->mi.track_len = mi->track_len;
-
-	/*
-	 * elapsed is special. Whenever the track changes, we reset it to 0,
-	 * so client doesn't have to make another call to change_playback
-	 */
-	mp->mi.elapsed = 0;
-	g_timer_start(mp->timer);
-
-	DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
-			"\tgenre: %s\n\tNumber of tracks: %u\n"
-			"\tTrack number: %u\n\tTrack duration: %u",
-			mi->title, mi->artist, mi->album, mi->genre,
-			mi->ntracks, mi->track, mi->track_len);
-
-	avrcp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
-}
-
-static uint8_t avrcp_handle_get_capabilities(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	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 AVC_CTYPE_STABLE;
-	case CAP_EVENTS_SUPPORTED:
-		pdu->params_len = htons(4);
-		pdu->params[1] = 2;
-		pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
-		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
-
-		return AVC_CTYPE_STABLE;
-	}
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_list_player_attributes(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	struct media_player *mp = control->mp;
-	unsigned int i;
-
-	if (len != 0) {
-		pdu->params_len = htons(1);
-		pdu->params[0] = E_INVALID_PARAM;
-		return AVC_CTYPE_REJECTED;
-	}
-
-	if (!mp)
-		goto done;
-
-	for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
-		if (!mp_get_attribute(mp, i)) {
-			DBG("Ignoring setting %u: not supported by player", i);
-			continue;
-		}
-
-		len++;
-		pdu->params[len] = i;
-	}
-
-done:
-	pdu->params[0] = len;
-	pdu->params_len = htons(len + 1);
-
-	return AVC_CTYPE_STABLE;
-}
-
-static uint8_t avrcp_handle_list_player_values(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	struct media_player *mp = control->mp;
-	unsigned int i;
-
-	if (len != 1 || !mp)
-		goto err;
-
-	len = attr_get_max_val(pdu->params[0]);
-	if (!len) {
-		error("Attribute is invalid: %u", pdu->params[0]);
-		goto err;
-	}
-
-	for (i = 1; i <= len; i++)
-		pdu->params[i] = i;
-
-	pdu->params[0] = len;
-	pdu->params_len = htons(len + 1);
-
-	return AVC_CTYPE_STABLE;
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_get_element_attributes(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	uint64_t *identifier = (void *) &pdu->params[0];
-	uint16_t pos;
-	uint8_t nattr;
-	int size;
-	unsigned int i;
-
-	if (len < 8 || *identifier != 0 || !control->mp)
-		goto err;
-
-	len = 0;
-	pos = 1; /* Keep track of current position in reponse */
-	nattr = pdu->params[8];
-
-	if (!control->mp)
-		goto done;
-
-	if (!nattr) {
-		/*
-		 * Return all available information, at least
-		 * title must be returned.
-		 */
-		for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
-			size = mp_get_media_attribute(control->mp, i,
-							&pdu->params[pos]);
-
-			if (size > 0) {
-				len++;
-				pos += size;
-			}
-		}
-	} else {
-		uint32_t *attr_ids;
-
-		attr_ids = g_memdup(&pdu->params[9], sizeof(uint32_t) * nattr);
-
-		for (i = 0; i < nattr; i++) {
-			uint32_t attr = ntohl(attr_ids[i]);
-
-			size = mp_get_media_attribute(control->mp, attr,
-							&pdu->params[pos]);
-
-			if (size > 0) {
-				len++;
-				pos += size;
-			}
-		}
-
-		g_free(attr_ids);
-
-		if (!len)
-			goto err;
-	}
-
-done:
-	pdu->params[0] = len;
-	pdu->params_len = htons(pos);
-
-	return AVC_CTYPE_STABLE;
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_get_current_player_value(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	struct media_player *mp = control->mp;
-	uint8_t *settings;
-	unsigned int i;
-
-	if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
-		goto err;
-
-	/*
-	 * Save a copy of requested settings because we can override them
-	 * while responding
-	 */
-	settings = g_malloc(pdu->params[0]);
-	memcpy(settings, &pdu->params[1], pdu->params[0]);
-	len = 0;
-
-	/*
-	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
-	 * and send a response with the existent ones. Only if all IDs are
-	 * non-existent we should send an error.
-	 */
-	for (i = 0; i < pdu->params[0]; i++) {
-		uint8_t val;
-
-		if (settings[i] < PLAYER_SETTING_EQUALIZER ||
-					settings[i] > PLAYER_SETTING_SCAN) {
-			DBG("Ignoring %u", settings[i]);
-			continue;
-		}
-
-		val = mp_get_attribute(mp, settings[i]);
-		if (!val) {
-			DBG("Ignoring %u: not supported by player",
-								settings[i]);
-			continue;
-		}
-
-		pdu->params[len] = settings[i];
-		pdu->params[len + 1] = val;
-		len += 2;
-	}
-
-	g_free(settings);
-
-	if (len) {
-		pdu->params[0] = len;
-		pdu->params_len = htons(2 * len + 1);
-
-		return AVC_CTYPE_STABLE;
-	}
-
-	error("No valid attributes in request");
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_set_player_value(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	unsigned int i;
-
-	if (len < 3 || !control->mp)
-		goto err;
-
-	len = 0;
-
-	/*
-	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
-	 * and set the existent ones. Sec. 5.2.4 is not clear however how to
-	 * indicate that a certain ID was not accepted. If at least one
-	 * attribute is valid, we respond with no parameters. Otherwise an
-	 * E_INVALID_PARAM is sent.
-	 */
-	for (i = 1; i < pdu->params[0]; i += 2) {
-		uint8_t attr = pdu->params[i];
-		uint8_t val = pdu->params[i + 1];
-		const char *attrstr;
-		const char *valstr;
-
-		attrstr = attr_to_str(attr);
-		if (!attrstr)
-			continue;
-
-		valstr = attrval_to_str(attr, val);
-		if (!valstr)
-			continue;
-
-		len++;
-
-		mp_set_attribute(control->mp, attr, val);
-		emit_property_changed(control->dev->conn, control->dev->path,
-					MEDIA_PLAYER_INTERFACE, attrstr,
-					DBUS_TYPE_STRING, &valstr);
-	}
-
-	if (len) {
-		pdu->params_len = 0;
-
-		return AVC_CTYPE_STABLE;
-	}
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_displayable_charset(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-
-	if (len < 3) {
-		pdu->params_len = htons(1);
-		pdu->params[0] = E_INVALID_PARAM;
-		return AVC_CTYPE_REJECTED;
-	}
-
-	/*
-	 * We acknowledge the commands, but we always use UTF-8 for
-	 * encoding since CT is obliged to support it.
-	 */
-	pdu->params_len = 0;
-	return AVC_CTYPE_STABLE;
-}
-
-static uint8_t avrcp_handle_ct_battery_status(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	const char *valstr;
-
-	if (len != 1)
-		goto err;
-
-	valstr = battery_status_to_str(pdu->params[0]);
-	if (valstr == NULL)
-		goto err;
-
-	emit_property_changed(control->dev->conn, control->dev->path,
-					MEDIA_PLAYER_INTERFACE, "Battery",
-					DBUS_TYPE_STRING, &valstr);
-	pdu->params_len = 0;
-
-	return AVC_CTYPE_STABLE;
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-	return AVC_CTYPE_REJECTED;
-}
-
-static uint8_t avrcp_handle_get_play_status(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	uint32_t elapsed;
-	uint32_t track_len;
-	uint8_t status;
-
-	if (len != 0) {
-		pdu->params_len = htons(1);
-		pdu->params[0] = E_INVALID_PARAM;
-		return AVC_CTYPE_REJECTED;
-	}
-
-	if (control->mp) {
-		mp_get_playback_status(control->mp, &status,
-							&elapsed, &track_len);
-		track_len = htonl(track_len);
-		elapsed = htonl(elapsed);
-	} else {
-		track_len = 0xFFFFFFFF;
-		elapsed = 0xFFFFFFFF;
-		status = PLAY_STATUS_ERROR;
-	}
-
-	memcpy(&pdu->params[0], &track_len, 4);
-	memcpy(&pdu->params[4], &elapsed, 4);
-	pdu->params[8] = status;
-
-	pdu->params_len = htons(9);
-
-	return AVC_CTYPE_STABLE;
-}
-
-static uint8_t avrcp_handle_register_notification(struct control *control,
-						struct avrcp_header *pdu,
-						uint8_t transaction)
-{
-	uint16_t len = ntohs(pdu->params_len);
-	uint8_t status;
-
-	/*
-	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
-	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
-	 * 1.3 spec, section 5.4.2.
-	 */
-	if (len != 5)
-		goto err;
-
-	switch (pdu->params[0]) {
-	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
-		len = 2;
-		if (control->mp) {
-			mp_get_playback_status(control->mp, &status,
-								NULL, NULL);
-			pdu->params[1] = status;
-		} else {
-			pdu->params[1] = PLAY_STATUS_ERROR;
-		}
-
-		break;
-	case AVRCP_EVENT_TRACK_CHANGED:
-		len = 9;
-
-		if (!control->mp)
-			memset(&pdu->params[1], 0xFF, 8);
-		else
-			memset(&pdu->params[1], 0, 8);
-
-		break;
-	default:
-		/* All other events are not supported yet */
-		goto err;
-	}
-
-	/* Register event and save the transaction used */
-	control->registered_events |= (1 << pdu->params[0]);
-	control->transaction_events[pdu->params[0]] = transaction;
-
-	pdu->params_len = htons(len);
-
-	return AVC_CTYPE_INTERIM;
-
-err:
-	pdu->params_len = htons(1);
-	pdu->params[0] = E_INVALID_PARAM;
-	return AVC_CTYPE_REJECTED;
-}
-
-static struct pdu_handler {
-	uint8_t pdu_id;
-	uint8_t code;
-	uint8_t (*func) (struct control *control,
-					struct avrcp_header *pdu,
-					uint8_t transaction);
-} handlers[] = {
-		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
-					avrcp_handle_get_capabilities },
-		{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
-					avrcp_handle_list_player_attributes },
-		{ AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
-					avrcp_handle_list_player_values },
-		{ AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
-					avrcp_handle_get_element_attributes },
-		{ AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
-					avrcp_handle_get_current_player_value },
-		{ AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
-					avrcp_handle_set_player_value },
-		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
-					NULL },
-		{ AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
-					NULL },
-		{ AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
-					avrcp_handle_displayable_charset },
-		{ AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
-					avrcp_handle_ct_battery_status },
-		{ AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
-					avrcp_handle_get_play_status },
-		{ AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
-					avrcp_handle_register_notification },
-		{ },
-};
-
-/* handle vendordep pdu inside an avctp packet */
-static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
-					uint8_t *code, uint8_t *subunit,
-					uint8_t *operands, size_t operand_count,
-					void *user_data)
-{
-	struct control *control = user_data;
-	struct pdu_handler *handler;
-	struct avrcp_header *pdu = (void *) operands;
-	uint32_t company_id = (pdu->company_id[0] << 16) |
-				(pdu->company_id[1] << 8) |
-				(pdu->company_id[2]);
-
-	if (company_id != IEEEID_BTSIG) {
-		*code = AVC_CTYPE_NOT_IMPLEMENTED;
-		return 0;
-	}
-
-	DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
-			pdu->pdu_id, company_id, pdu->params_len);
-
-	pdu->packet_type = 0;
-	pdu->rsvd = 0;
-
-	if (operand_count + 3 < AVRCP_HEADER_LENGTH)
-		goto err_metadata;
-
-	for (handler = handlers; handler; handler++) {
-		if (handler->pdu_id == pdu->pdu_id)
-			break;
-	}
-
-	if (!handler || handler->code != *code) {
-		pdu->params[0] = E_INVALID_COMMAND;
-		goto err_metadata;
-	}
-
-	if (!handler->func) {
-		pdu->params[0] = E_INVALID_PARAM;
-		goto err_metadata;
-	}
-
-	*code = handler->func(control, pdu, transaction);
-
-	return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
-
-err_metadata:
-	pdu->params_len = htons(1);
-	*code = AVC_CTYPE_REJECTED;
-
-	return AVRCP_HEADER_LENGTH + 1;
-}
-
 static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 				avctp_state_t new_state, void *user_data)
 {
@@ -1304,11 +74,6 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 	case AVCTP_STATE_DISCONNECTED:
 		control->session = NULL;
 
-		if (control->mp && control->mp->handler) {
-			avctp_unregister_pdu_handler(control->mp->handler);
-			control->mp->handler = 0;
-		}
-
 		if (old_state != AVCTP_STATE_CONNECTED)
 			break;
 
@@ -1325,14 +90,8 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 		if (control->session)
 			break;
 
-		control->session = avctp_connect(&dev->src, &dev->dst);
-		if (!control->mp)
-			break;
+		control->session = avctp_get(&dev->src, &dev->dst);
 
-		control->mp->handler = avctp_register_pdu_handler(
-							AVC_OP_VENDORDEP,
-							handle_vendordep_pdu,
-							control);
 		break;
 	case AVCTP_STATE_CONNECTED:
 		value = TRUE;
@@ -1348,149 +107,6 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 	}
 }
 
-static void media_info_init(struct media_info *mi)
-{
-	memset(mi, 0, sizeof(*mi));
-
-	/*
-	 * As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG
-	 * does not support these attributes (i.e. they were never set via
-	 * D-Bus)
-	 */
-	mi->track_len = 0xFFFFFFFF;
-	mi->elapsed = 0xFFFFFFFF;
-}
-
-gboolean avrcp_connect(struct audio_device *dev)
-{
-	struct control *control = dev->control;
-
-	if (control->session)
-		return TRUE;
-
-	control->session = avctp_connect(&dev->src, &dev->dst);
-	if (!control->session)
-		return FALSE;
-
-	return TRUE;
-}
-
-void avrcp_disconnect(struct audio_device *dev)
-{
-	struct control *control = dev->control;
-
-	if (!(control && control->session))
-		return;
-
-	avctp_disconnect(control->session);
-}
-
-int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
-{
-	sdp_record_t *record;
-	gboolean tmp, master = TRUE;
-	GError *err = NULL;
-	struct avrcp_server *server;
-
-	if (config) {
-		tmp = g_key_file_get_boolean(config, "General",
-							"Master", &err);
-		if (err) {
-			DBG("audio.conf: %s", err->message);
-			g_error_free(err);
-		} else
-			master = tmp;
-	}
-
-	server = g_new0(struct avrcp_server, 1);
-	if (!server)
-		return -ENOMEM;
-
-	if (!connection)
-		connection = dbus_connection_ref(conn);
-
-	record = avrcp_tg_record();
-	if (!record) {
-		error("Unable to allocate new service record");
-		g_free(server);
-		return -1;
-	}
-
-	if (add_record_to_server(src, record) < 0) {
-		error("Unable to register AVRCP target service record");
-		g_free(server);
-		sdp_record_free(record);
-		return -1;
-	}
-	server->tg_record_id = record->handle;
-
-	record = avrcp_ct_record();
-	if (!record) {
-		error("Unable to allocate new service record");
-		g_free(server);
-		return -1;
-	}
-
-	if (add_record_to_server(src, record) < 0) {
-		error("Unable to register AVRCP controller service record");
-		sdp_record_free(record);
-		g_free(server);
-		return -1;
-	}
-	server->ct_record_id = record->handle;
-
-	if (avctp_register(src, master) < 0) {
-		remove_record_from_server(server->ct_record_id);
-		remove_record_from_server(server->tg_record_id);
-		g_free(server);
-		return -1;
-	}
-
-	bacpy(&server->src, src);
-
-	servers = g_slist_append(servers, server);
-
-	return 0;
-}
-
-static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
-{
-	for (; list; list = list->next) {
-		struct avrcp_server *server = list->data;
-
-		if (bacmp(&server->src, src) == 0)
-			return server;
-	}
-
-	return NULL;
-}
-
-void avrcp_unregister(const bdaddr_t *src)
-{
-	struct avrcp_server *server;
-
-	server = find_server(servers, src);
-	if (!server)
-		return;
-
-	servers = g_slist_remove(servers, server);
-
-	remove_record_from_server(server->ct_record_id);
-	remove_record_from_server(server->tg_record_id);
-
-	avctp_unregister(&server->src);
-	g_free(server);
-
-	if (servers)
-		return;
-
-	if (avctp_id)
-		avctp_remove_state_cb(avctp_id);
-
-	dbus_connection_unref(connection);
-	connection = NULL;
-}
-
 static DBusMessage *control_is_connected(DBusConnection *conn,
 						DBusMessage *msg,
 						void *data)
@@ -1597,191 +213,6 @@ static GDBusSignalTable control_signals[] = {
 	{ NULL, NULL }
 };
 
-static DBusMessage *mp_set_property(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct control *control = device->control;
-	DBusMessageIter iter;
-	DBusMessageIter var;
-	const char *attrstr, *valstr;
-	int attr, val;
-
-	if (!dbus_message_iter_init(msg, &iter))
-		return btd_error_invalid_args(msg);
-
-	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&iter, &attrstr);
-
-	attr = attr_to_val(attrstr);
-	if (attr < 0)
-		return btd_error_not_supported(msg);
-
-	dbus_message_iter_next(&iter);
-
-	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_recurse(&iter, &var);
-
-	/* Only string arguments are supported for now */
-	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&var, &valstr);
-
-	val = attrval_to_val(attr, valstr);
-	if (val < 0)
-		return btd_error_not_supported(msg);
-
-	mp_set_attribute(control->mp, attr, val);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *mp_change_playback(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct control *control = device->control;
-	const char *statusstr;
-	int status;
-	uint32_t elapsed;
-
-	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr,
-						DBUS_TYPE_UINT32, &elapsed,
-						DBUS_TYPE_INVALID))
-		return btd_error_invalid_args(msg);
-
-	status = play_status_to_val(statusstr);
-	if (status < 0)
-		return btd_error_invalid_args(msg);
-
-	mp_set_playback_status(control, status, elapsed);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi)
-{
-	DBusMessageIter dict;
-	DBusMessageIter var;
-	int ctype;
-
-	ctype = dbus_message_iter_get_arg_type(iter);
-	if (ctype != DBUS_TYPE_ARRAY)
-		return FALSE;
-
-	media_info_init(mi);
-	dbus_message_iter_recurse(iter, &dict);
-
-	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
-							DBUS_TYPE_INVALID) {
-		DBusMessageIter entry;
-		const char *key;
-
-		if (ctype != DBUS_TYPE_DICT_ENTRY)
-			return FALSE;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
-			return FALSE;
-
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-
-		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
-			return FALSE;
-
-		dbus_message_iter_recurse(&entry, &var);
-
-		if (!strcmp(key, "Title")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_STRING)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->title);
-		} else if (!strcmp(key, "Artist")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_STRING)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->artist);
-		} else if (!strcmp(key, "Album")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_STRING)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->album);
-		} else if (!strcmp(key, "Genre")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_STRING)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->genre);
-		} else if (!strcmp(key, "NumberOfTracks")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_UINT32)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->ntracks);
-		} else if (!strcmp(key, "TrackNumber")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_UINT32)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->track);
-		} else if (!strcmp(key, "TrackDuration")) {
-			if (dbus_message_iter_get_arg_type(&var) !=
-							DBUS_TYPE_UINT32)
-				return FALSE;
-
-			dbus_message_iter_get_basic(&var, &mi->track_len);
-		} else {
-			return FALSE;
-		}
-
-		dbus_message_iter_next(&dict);
-	}
-
-	if (mi->title == NULL)
-		return FALSE;
-
-	return TRUE;
-}
-
-static DBusMessage *mp_change_track(DBusConnection *conn,
-						DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct control *control = device->control;
-	DBusMessageIter iter;
-	struct media_info mi;
-
-
-	dbus_message_iter_init(msg, &iter);
-	if (!media_info_parse(&iter, &mi))
-		return btd_error_invalid_args(msg);
-
-	mp_set_media_attributes(control, &mi);
-
-	return dbus_message_new_method_return(msg);
-}
-
-static GDBusMethodTable mp_methods[] = {
-	{ "SetProperty",	"sv",		"",	mp_set_property },
-	{ "ChangePlayback",	"su",		"",	mp_change_playback },
-	{ "ChangeTrack",	"a{sv}",	"",	mp_change_track },
-	{ }
-};
-
-static GDBusSignalTable mp_signals[] = {
-	{ "PropertyChanged",		"sv"	},
-	{ }
-};
-
 static void path_unregister(void *data)
 {
 	struct audio_device *dev = data;
@@ -1797,78 +228,19 @@ static void path_unregister(void *data)
 	dev->control = NULL;
 }
 
-static void mp_path_unregister(void *data)
-{
-	struct audio_device *dev = data;
-	struct control *control = dev->control;
-	struct media_player *mp = control->mp;
-
-	DBG("Unregistered interface %s on path %s",
-		MEDIA_PLAYER_INTERFACE, dev->path);
-
-	if (mp->handler)
-		avctp_unregister_pdu_handler(mp->handler);
-
-	g_timer_destroy(mp->timer);
-	g_free(mp);
-	control->mp = NULL;
-}
-
-static void mp_unregister(struct control *control)
-{
-	struct audio_device *dev = control->dev;
-
-	g_dbus_unregister_interface(dev->conn, dev->path,
-						MEDIA_PLAYER_INTERFACE);
-}
-
 void control_unregister(struct audio_device *dev)
 {
-	struct control *control = dev->control;
-
-	if (control->mp)
-		mp_unregister(control);
-
 	g_dbus_unregister_interface(dev->conn, dev->path,
 						AUDIO_CONTROL_INTERFACE);
 }
 
-static void mp_register(struct control *control)
-{
-	struct audio_device *dev = control->dev;
-	struct media_player *mp;
-
-	mp = g_new0(struct media_player, 1);
-
-	if (!g_dbus_register_interface(dev->conn, dev->path,
-						MEDIA_PLAYER_INTERFACE,
-						mp_methods, mp_signals, NULL,
-						dev, mp_path_unregister)) {
-		error("D-Bus failed do register %s on path %s",
-					MEDIA_PLAYER_INTERFACE, dev->path);
-		g_free(mp);
-		return;
-	}
-
-	DBG("Registered interface %s on path %s",
-					MEDIA_PLAYER_INTERFACE, dev->path);
-
-	mp->timer = g_timer_new();
-	media_info_init(&mp->mi);
-	control->mp = mp;
-}
-
-void control_update(struct control *control, uint16_t uuid16,
-							gboolean media_player)
+void control_update(struct control *control, uint16_t uuid16)
 {
 	if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
 		control->target = TRUE;
-	else if (media_player && !control->mp)
-		mp_register(control);
 }
 
-struct control *control_init(struct audio_device *dev, uint16_t uuid16,
-							gboolean media_player)
+struct control *control_init(struct audio_device *dev, uint16_t uuid16)
 {
 	struct control *control;
 
@@ -1884,7 +256,7 @@ struct control *control_init(struct audio_device *dev, uint16_t uuid16,
 	control = g_new0(struct control, 1);
 	control->dev = dev;
 
-	control_update(control, uuid16, media_player);
+	control_update(control, uuid16);
 
 	if (!avctp_id)
 		avctp_id = avctp_add_state_cb(state_changed, NULL);
diff --git a/audio/control.h b/audio/control.h
index f5cfef2..2219e5f 100644
--- a/audio/control.h
+++ b/audio/control.h
@@ -23,17 +23,8 @@
  */
 
 #define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
-#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
 
-int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
-void avrcp_unregister(const bdaddr_t *src);
-
-gboolean avrcp_connect(struct audio_device *dev);
-void avrcp_disconnect(struct audio_device *dev);
-
-struct control *control_init(struct audio_device *dev, uint16_t uuid16,
-							gboolean media_player);
-void control_update(struct control *control, uint16_t uuid16,
-							gboolean media_player);
+struct control *control_init(struct audio_device *dev, uint16_t uuid16);
+void control_update(struct control *control, uint16_t uuid16);
 void control_unregister(struct audio_device *dev);
 gboolean control_is_active(struct audio_device *dev);
diff --git a/audio/device.c b/audio/device.c
index 16f0701..9ec6fc4 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -53,6 +53,7 @@
 #include "avdtp.h"
 #include "control.h"
 #include "avctp.h"
+#include "avrcp.h"
 #include "headset.h"
 #include "gateway.h"
 #include "sink.h"
@@ -752,6 +753,9 @@ void audio_device_unregister(struct audio_device *device)
 	if (device->control)
 		control_unregister(device);
 
+	if (device->media_player)
+		media_player_unregister(device);
+
 	g_dbus_unregister_interface(device->conn, device->path,
 						AUDIO_INTERFACE);
 
diff --git a/audio/device.h b/audio/device.h
index 5671f77..3975108 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -44,6 +44,7 @@ struct target;
 struct sink;
 struct headset;
 struct gateway;
+struct media_player;
 struct dev_priv;
 
 struct audio_device {
@@ -63,6 +64,7 @@ struct audio_device {
 	struct source *source;
 	struct control *control;
 	struct target *target;
+	struct media_player *media_player;
 
 	guint hs_preauth_id;
 
diff --git a/audio/manager.c b/audio/manager.c
index 880872f..dd4fa6b 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -63,6 +63,7 @@
 #include "gateway.h"
 #include "sink.h"
 #include "source.h"
+#include "avrcp.h"
 #include "control.h"
 #include "manager.h"
 #include "sdpd.h"
@@ -220,11 +221,12 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		DBG("Found AV %s", uuid16 == AV_REMOTE_SVCLASS_ID ?
 							"Remote" : "Target");
 		if (device->control)
-			control_update(device->control, uuid16,
-							enabled.media_player);
+			control_update(device->control, uuid16);
 		else
-			device->control = control_init(device, uuid16,
-							enabled.media_player);
+			device->control = control_init(device, uuid16);
+
+		if (enabled.media_player && !device->media_player)
+			device->media_player = media_player_init(device);
 		if (device->sink && sink_is_active(device))
 			avrcp_connect(device);
 		break;
-- 
1.7.6.1


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

* Re: [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 11:29 ` [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header Luiz Augusto von Dentz
@ 2011-09-12 13:10   ` Lucas De Marchi
  2011-09-12 13:37     ` Szymon Janc
  2011-09-12 14:48     ` Luiz Augusto von Dentz
  0 siblings, 2 replies; 14+ messages in thread
From: Lucas De Marchi @ 2011-09-12 13:10 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz

On Mon, Sep 12, 2011 at 8:29 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> AVCTP carries AV/C packets/PDUs not AVRCP as avrcp_header suggests.
> ---
>  audio/control.c |   98 +++++++++++++++++++++++++++---------------------------
>  1 files changed, 49 insertions(+), 49 deletions(-)

This was the next one in my TODO list. I'm glad you did it :-)




>
> diff --git a/audio/control.c b/audio/control.c
> index f84c7f7..37b0806 100644
> --- a/audio/control.c
> +++ b/audio/control.c
> @@ -212,14 +212,14 @@ struct avctp_header {
>  } __attribute__ ((packed));
>  #define AVCTP_HEADER_LENGTH 3
>
> -struct avrcp_header {
> +struct avc_header {
>        uint8_t code:4;
>        uint8_t _hdr0:4;
>        uint8_t subunit_id:3;
>        uint8_t subunit_type:5;
>        uint8_t opcode;
>  } __attribute__ ((packed));
> -#define AVRCP_HEADER_LENGTH 3
> +#define AVC_HEADER_LENGTH 3
>
>  struct avrcp_spec_avc_pdu {
>        uint8_t company_id[3];
> @@ -242,14 +242,14 @@ struct avctp_header {
>  } __attribute__ ((packed));
>  #define AVCTP_HEADER_LENGTH 3
>
> -struct avrcp_header {
> +struct avc_header {
>        uint8_t _hdr0:4;
>        uint8_t code:4;
>        uint8_t subunit_type:5;
>        uint8_t subunit_id:3;
>        uint8_t opcode;
>  } __attribute__ ((packed));
> -#define AVRCP_HEADER_LENGTH 3
> +#define AVC_HEADER_LENGTH 3
>
>  struct avrcp_spec_avc_pdu {
>        uint8_t company_id[3];
> @@ -721,13 +721,13 @@ static const char *battery_status_to_str(enum battery_status status)
>
>  static int avctp_send_event(struct control *control, uint8_t id, void *data)
>  {
> -       uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
> +       uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
>                                        AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
>        struct avctp_header *avctp = (void *) buf;
> -       struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
> +       struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
>        struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
> -                                                       AVRCP_HEADER_LENGTH];
> -       int sk;
> +                                                       AVC_HEADER_LENGTH];
> +       int sk = g_io_channel_unix_get_fd(control->io);

You are re-introducing a bug here. Please see "dec26ee Fix fd usage
when not connected"


>        uint16_t size;
>
>        if (control->state != AVCTP_STATE_CONNECTED)
> @@ -743,9 +743,9 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
>        avctp->cr = AVCTP_RESPONSE;
>        avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
>
> -       avrcp->code = CTYPE_CHANGED;
> -       avrcp->subunit_type = SUBUNIT_PANEL;
> -       avrcp->opcode = OP_VENDORDEP;
> +       avc->code = CTYPE_CHANGED;
> +       avc->subunit_type = SUBUNIT_PANEL;
> +       avc->opcode = OP_VENDORDEP;
>
>        pdu->company_id[0] = IEEEID_BTSIG >> 16;
>        pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
> @@ -780,7 +780,7 @@ static int avctp_send_event(struct control *control, uint8_t id, void *data)
>        }
>
>        pdu->params_len = htons(size);
> -       size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
> +       size += AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +
>                                        AVRCP_SPECAVCPDU_HEADER_LENGTH;
>
>        sk = g_io_channel_unix_get_fd(control->io);
> @@ -1451,19 +1451,19 @@ static struct pdu_handler {
>  /* handle vendordep pdu inside an avctp packet */
>  static int handle_vendordep_pdu(struct control *control,
>                                        struct avctp_header *avctp,
> -                                       struct avrcp_header *avrcp,
> +                                       struct avc_header *avc,
>                                        int operand_count)
>  {
>        struct pdu_handler *handler;
> -       struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
> +       struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;

We might want to change this to:

struct avrcp_spec_avc_pdu *pdu = (avrcp_spec_avc_pdu *)(avc +
AVC_HEADER_LENGTH);

This way we kill some warnings on ARM: arithmetic with void pointer


>        uint32_t company_id = (pdu->company_id[0] << 16) |
>                                (pdu->company_id[1] << 8) |
>                                (pdu->company_id[2]);
>
>        if (company_id != IEEEID_BTSIG ||
>                                pdu->packet_type != AVCTP_PACKET_SINGLE) {
> -               avrcp->code = CTYPE_NOT_IMPLEMENTED;
> -               return AVRCP_HEADER_LENGTH;
> +               avc->code = CTYPE_NOT_IMPLEMENTED;
> +               return AVC_HEADER_LENGTH;
>        }
>
>        pdu->packet_type = 0;
> @@ -1477,7 +1477,7 @@ static int handle_vendordep_pdu(struct control *control,
>                        break;
>        }
>
> -       if (!handler || handler->code != avrcp->code) {
> +       if (!handler || handler->code != avc->code) {
>                pdu->params[0] = E_INVALID_COMMAND;
>                goto err_metadata;
>        }
> @@ -1487,16 +1487,16 @@ static int handle_vendordep_pdu(struct control *control,
>                goto err_metadata;
>        }
>
> -       avrcp->code = handler->func(control, pdu, avctp->transaction);
> +       avc->code = handler->func(control, pdu, avctp->transaction);
>
> -       return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
> +       return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
>                                                ntohs(pdu->params_len);
>
>  err_metadata:
>        pdu->params_len = htons(1);
> -       avrcp->code = CTYPE_REJECTED;
> +       avc->code = CTYPE_REJECTED;
>
> -       return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
> +       return AVC_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
>  }
>
>  static void avctp_disconnected(struct audio_device *dev)
> @@ -1593,7 +1593,7 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
>        struct control *control = data;
>        unsigned char buf[1024], *operands;
>        struct avctp_header *avctp;
> -       struct avrcp_header *avrcp;
> +       struct avc_header *avc;
>        int ret, packet_size, operand_count, sock;
>
>        if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> @@ -1622,65 +1622,65 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
>                        avctp->cr, avctp->ipid, ntohs(avctp->pid));
>
>        ret -= sizeof(struct avctp_header);
> -       if ((unsigned int) ret < sizeof(struct avrcp_header)) {
> +       if ((unsigned int) ret < sizeof(struct avc_header)) {
>                error("Too small AVRCP packet");
>                goto failed;
>        }
>
> -       avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header));
> +       avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
>
> -       ret -= sizeof(struct avrcp_header);
> +       ret -= sizeof(struct avc_header);
>
> -       operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header);
> +       operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
>        operand_count = ret;
>
> -       DBG("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
> +       DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
>                        "opcode 0x%02X, %d operands",
>                        avctp->cr ? "response" : "command",
> -                       avrcp->code, avrcp->subunit_type, avrcp->subunit_id,
> -                       avrcp->opcode, operand_count);
> +                       avc->code, avc->subunit_type, avc->subunit_id,
> +                       avc->opcode, operand_count);
>
>        if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
>                avctp->cr = AVCTP_RESPONSE;
> -               avrcp->code = CTYPE_NOT_IMPLEMENTED;
> +               avc->code = CTYPE_NOT_IMPLEMENTED;
>        } else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
>                avctp->ipid = 1;
>                avctp->cr = AVCTP_RESPONSE;
>                packet_size = sizeof(*avctp);
>        } else if (avctp->cr == AVCTP_COMMAND &&
> -                       avrcp->code == CTYPE_CONTROL &&
> -                       avrcp->subunit_type == SUBUNIT_PANEL &&
> -                       avrcp->opcode == OP_PASSTHROUGH) {
> +                       avc->code == CTYPE_CONTROL &&
> +                       avc->subunit_type == SUBUNIT_PANEL &&
> +                       avc->opcode == OP_PASSTHROUGH) {
>                handle_panel_passthrough(control, operands, operand_count);
>                avctp->cr = AVCTP_RESPONSE;
> -               avrcp->code = CTYPE_ACCEPTED;
> +               avc->code = CTYPE_ACCEPTED;
>        } else if (avctp->cr == AVCTP_COMMAND &&
> -                       avrcp->code == CTYPE_STATUS &&
> -                       (avrcp->opcode == OP_UNITINFO
> -                       || avrcp->opcode == OP_SUBUNITINFO)) {
> +                       avc->code == CTYPE_STATUS &&
> +                       (avc->opcode == OP_UNITINFO
> +                       || avc->opcode == OP_SUBUNITINFO)) {
>                avctp->cr = AVCTP_RESPONSE;
> -               avrcp->code = CTYPE_STABLE;
> +               avc->code = CTYPE_STABLE;
>                /* The first operand should be 0x07 for the UNITINFO response.
>                 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
>                 * Interface Command Set (section 9.2.1, page 45) specs
>                 * explain this value but both use it */
> -               if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO)
> +               if (operand_count >= 1 && avc->opcode == OP_UNITINFO)
>                        operands[0] = 0x07;
>                if (operand_count >= 2)
>                        operands[1] = SUBUNIT_PANEL << 3;
> -               DBG("reply to %s", avrcp->opcode == OP_UNITINFO ?
> +               DBG("reply to %s", avc->opcode == OP_UNITINFO ?
>                                "OP_UNITINFO" : "OP_SUBUNITINFO");
>        } else if (avctp->cr == AVCTP_COMMAND &&
> -                       avrcp->opcode == OP_VENDORDEP) {
> +                       avc->opcode == OP_VENDORDEP) {
>                int r_size;
>                operand_count -= 3;
>                avctp->cr = AVCTP_RESPONSE;
> -               r_size = handle_vendordep_pdu(control, avctp, avrcp,
> +               r_size = handle_vendordep_pdu(control, avctp, avc,
>                                                                operand_count);
>                packet_size = AVCTP_HEADER_LENGTH + r_size;
>        } else {
>                avctp->cr = AVCTP_RESPONSE;
> -               avrcp->code = CTYPE_REJECTED;
> +               avc->code = CTYPE_REJECTED;
>        }
>
>        ret = write(sock, buf, packet_size);
> @@ -2092,10 +2092,10 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
>
>  static int avctp_send_passthrough(struct control *control, uint8_t op)
>  {
> -       unsigned char buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH + 2];
> +       unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
>        struct avctp_header *avctp = (void *) buf;
> -       struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
> -       uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH];
> +       struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
> +       uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
>        int sk = g_io_channel_unix_get_fd(control->io);
>        static uint8_t transaction = 0;
>
> @@ -2106,9 +2106,9 @@ static int avctp_send_passthrough(struct control *control, uint8_t op)
>        avctp->cr = AVCTP_COMMAND;
>        avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
>
> -       avrcp->code = CTYPE_CONTROL;
> -       avrcp->subunit_type = SUBUNIT_PANEL;
> -       avrcp->opcode = OP_PASSTHROUGH;
> +       avc->code = CTYPE_CONTROL;
> +       avc->subunit_type = SUBUNIT_PANEL;
> +       avc->opcode = OP_PASSTHROUGH;
>
>        operands[0] = op & 0x7f;
>        operands[1] = 0;
> --

The rest looks good to me.

Lucas De Marchi

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

* Re: [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling
  2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2011-09-12 11:29 ` [PATCH BlueZ 5/5] AVRCP: move handling of vendor dependent PDU from control.c to avrcp.c Luiz Augusto von Dentz
@ 2011-09-12 13:25 ` Lucas De Marchi
  2011-09-12 14:40   ` Luiz Augusto von Dentz
  4 siblings, 1 reply; 14+ messages in thread
From: Lucas De Marchi @ 2011-09-12 13:25 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

On Mon, Sep 12, 2011 at 8:29 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This simplify a bit the handling by introducing common checks before
> calling the handler callback, it is also much easier to add/remove
> new PDUs in this way.
> ---
>  audio/control.c |  321 +++++++++++++++++++++----------------------------------
>  1 files changed, 124 insertions(+), 197 deletions(-)
>
> diff --git a/audio/control.c b/audio/control.c
> index 9990b06..f84c7f7 100644
> --- a/audio/control.c
> +++ b/audio/control.c
> @@ -986,8 +986,9 @@ static void mp_set_media_attributes(struct control *control,
>        avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
>  }
>
> -static int avrcp_handle_get_capabilities(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_get_capabilities(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        unsigned int i;
> @@ -1008,31 +1009,35 @@ static int avrcp_handle_get_capabilities(struct control *control,
>                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));
> +               return CTYPE_STABLE;
>        case CAP_EVENTS_SUPPORTED:
>                pdu->params_len = htons(4);
>                pdu->params[1] = 2;
>                pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
>                pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
>
> -               return 4;
> +               return CTYPE_STABLE;
>        }
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_list_player_attributes(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_list_player_attributes(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)

It might be my CFLAGS, but aren't you getting warnings of unused
parameters since now every handler carries the transaction id?

>  {
>        uint16_t len = ntohs(pdu->params_len);
>        struct media_player *mp = control->mp;
>        unsigned int i;
>
>        if (len != 0) {
> +               pdu->params_len = htons(1);
>                pdu->params[0] = E_INVALID_PARAM;
> -               return -EINVAL;
> +               return CTYPE_REJECTED;
>        }
>
>        if (!mp)
> @@ -1052,11 +1057,12 @@ done:
>        pdu->params[0] = len;
>        pdu->params_len = htons(len + 1);
>
> -       return len + 1;
> +       return CTYPE_STABLE;
>  }
>
> -static int avrcp_handle_list_player_values(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_list_player_values(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        struct media_player *mp = control->mp;
> @@ -1077,15 +1083,17 @@ static int avrcp_handle_list_player_values(struct control *control,
>        pdu->params[0] = len;
>        pdu->params_len = htons(len + 1);
>
> -       return len + 1;
> +       return CTYPE_STABLE;
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_get_element_attributes(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_get_element_attributes(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        uint64_t *identifier = (void *) &pdu->params[0];
> @@ -1145,14 +1153,16 @@ done:
>        pdu->params[0] = len;
>        pdu->params_len = htons(pos);
>
> -       return pos;
> +       return CTYPE_STABLE;
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_get_current_player_value(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_get_current_player_value(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        struct media_player *mp = control->mp;
> @@ -1202,19 +1212,21 @@ static int avrcp_handle_get_current_player_value(struct control *control,
>                pdu->params[0] = len;
>                pdu->params_len = htons(2 * len + 1);
>
> -               return 2 * len + 1;
> +               return CTYPE_STABLE;
>        }
>
>        error("No valid attributes in request");
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
>
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_set_player_value(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_set_player_value(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        unsigned int i;
> @@ -1256,16 +1268,38 @@ static int avrcp_handle_set_player_value(struct control *control,
>        if (len) {
>                pdu->params_len = 0;
>
> -               return 0;
> +               return CTYPE_STABLE;
>        }
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_ct_battery_status(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_displayable_charset(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
> +{
> +       uint16_t len = ntohs(pdu->params_len);
> +
> +       if (len < 3) {
> +               pdu->params_len = htons(1);
> +               pdu->params[0] = E_INVALID_PARAM;
> +               return CTYPE_REJECTED;
> +       }
> +
> +       /*
> +        * We acknowledge the commands, but we always use UTF-8 for
> +        * encoding since CT is obliged to support it.
> +        */
> +       pdu->params_len = 0;
> +       return CTYPE_STABLE;
> +}
> +
> +static uint8_t avrcp_handle_ct_battery_status(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        const char *valstr;
> @@ -1282,15 +1316,17 @@ static int avrcp_handle_ct_battery_status(struct control *control,
>                                        DBUS_TYPE_STRING, &valstr);
>        pdu->params_len = 0;
>
> -       return 0;
> +       return CTYPE_STABLE;
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> -static int avrcp_handle_get_play_status(struct control *control,
> -                                               struct avrcp_spec_avc_pdu *pdu)
> +static uint8_t avrcp_handle_get_play_status(struct control *control,
> +                                               struct avrcp_spec_avc_pdu *pdu,
> +                                               uint8_t transaction)
>  {
>        uint16_t len = ntohs(pdu->params_len);
>        uint32_t elapsed;
> @@ -1298,8 +1334,9 @@ static int avrcp_handle_get_play_status(struct control *control,
>        uint8_t status;
>
>        if (len != 0) {
> +               pdu->params_len = htons(1);
>                pdu->params[0] = E_INVALID_PARAM;
> -               return -EINVAL;
> +               return CTYPE_REJECTED;
>        }
>
>        if (control->mp) {
> @@ -1319,10 +1356,10 @@ static int avrcp_handle_get_play_status(struct control *control,
>
>        pdu->params_len = htons(9);
>
> -       return 9;
> +       return CTYPE_STABLE;
>  }
>
> -static int avrcp_handle_register_notification(struct control *control,
> +static uint8_t avrcp_handle_register_notification(struct control *control,
>                                                struct avrcp_spec_avc_pdu *pdu,
>                                                uint8_t transaction)
>  {
> @@ -1369,24 +1406,59 @@ static int avrcp_handle_register_notification(struct control *control,
>
>        pdu->params_len = htons(len);
>
> -       return len;
> +       return CTYPE_INTERIM;
>
>  err:
> +       pdu->params_len = htons(1);
>        pdu->params[0] = E_INVALID_PARAM;
> -       return -EINVAL;
> +       return CTYPE_REJECTED;
>  }
>
> +static struct pdu_handler {
> +       uint8_t pdu_id;
> +       uint8_t code;
> +       uint8_t (*func) (struct control *control,
> +                                       struct avrcp_spec_avc_pdu *pdu,
> +                                       uint8_t transaction);
> +} handlers[] = {
> +               { AVRCP_GET_CAPABILITIES, CTYPE_STATUS,
> +                                       avrcp_handle_get_capabilities },
> +               { AVRCP_LIST_PLAYER_ATTRIBUTES, CTYPE_STATUS,
> +                                       avrcp_handle_list_player_attributes },
> +               { AVRCP_LIST_PLAYER_VALUES, CTYPE_STATUS,
> +                                       avrcp_handle_list_player_values },
> +               { AVRCP_GET_ELEMENT_ATTRIBUTES, CTYPE_STATUS,
> +                                       avrcp_handle_get_element_attributes },
> +               { AVRCP_GET_CURRENT_PLAYER_VALUE, CTYPE_STATUS,
> +                                       avrcp_handle_get_current_player_value },
> +               { AVRCP_SET_PLAYER_VALUE, CTYPE_CONTROL,
> +                                       avrcp_handle_set_player_value },
> +               { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, CTYPE_STATUS,
> +                                       NULL },
> +               { AVRCP_GET_PLAYER_VALUE_TEXT, CTYPE_STATUS,
> +                                       NULL },
> +               { AVRCP_DISPLAYABLE_CHARSET, CTYPE_STATUS,
> +                                       avrcp_handle_displayable_charset },
> +               { AVRCP_CT_BATTERY_STATUS, CTYPE_STATUS,
> +                                       avrcp_handle_ct_battery_status },
> +               { AVRCP_GET_PLAY_STATUS, CTYPE_STATUS,
> +                                       avrcp_handle_get_play_status },
> +               { AVRCP_REGISTER_NOTIFICATION, CTYPE_NOTIFY,
> +                                       avrcp_handle_register_notification },
> +               { },
> +};
> +
>  /* handle vendordep pdu inside an avctp packet */
>  static int handle_vendordep_pdu(struct control *control,
>                                        struct avctp_header *avctp,
>                                        struct avrcp_header *avrcp,
>                                        int operand_count)
>  {
> +       struct pdu_handler *handler;
>        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) {
> @@ -1397,177 +1469,32 @@ static int handle_vendordep_pdu(struct control *control,
>        pdu->packet_type = 0;
>        pdu->rsvd = 0;
>
> -       if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
> -               pdu->params[0] = E_INVALID_COMMAND;
> +       if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH)
>                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;
> -       case AVRCP_LIST_PLAYER_ATTRIBUTES:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
>
> -               len = avrcp_handle_list_player_attributes(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_LIST_PLAYER_VALUES:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_list_player_values(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_GET_ELEMENT_ATTRIBUTES:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_get_element_attributes(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_GET_CURRENT_PLAYER_VALUE:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_get_current_player_value(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_SET_PLAYER_VALUE:
> -               if (avrcp->code != CTYPE_CONTROL) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_set_player_value(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
> -       case AVRCP_GET_PLAYER_VALUE_TEXT:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> +       for (handler = handlers; handler; handler++) {
> +               if (handler->pdu_id == pdu->pdu_id)
> +                       break;
> +       }
>
> -               /*
> -                * As per sec. 5.2.5 of AVRCP 1.3 spec, this command is
> -                * expected to be used only for extended attributes, i.e.
> -                * custom attributes defined by the application. As we
> -                * currently don't have any such attribute, we respond with
> -                * invalid param id.
> -                */
> -               pdu->params[0] = E_INVALID_PARAM;
> +       if (!handler || handler->code != avrcp->code) {
> +               pdu->params[0] = E_INVALID_COMMAND;
>                goto err_metadata;
> -       case AVRCP_DISPLAYABLE_CHARSET:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               if (pdu->params[0] < 3) {
> -                       pdu->params[0] = E_INVALID_PARAM;
> -                       goto err_metadata;
> -               }
> -
> -               /*
> -                * We acknowledge the commands, but we always use UTF-8 for
> -                * encoding since CT is obliged to support it.
> -                */
> -               pdu->params_len = 0;
> -               avrcp->code = CTYPE_STABLE;
> -               len = 0;
> -
> -               break;
> -       case AVRCP_CT_BATTERY_STATUS:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_ct_battery_status(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_GET_PLAY_STATUS:
> -               if (avrcp->code != CTYPE_STATUS) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_get_play_status(control, pdu);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_STABLE;
> -
> -               break;
> -       case AVRCP_REGISTER_NOTIFICATION:
> -               if (avrcp->code != CTYPE_NOTIFY) {
> -                       pdu->params[0] = E_INVALID_COMMAND;
> -                       goto err_metadata;
> -               }
> -
> -               len = avrcp_handle_register_notification(control, pdu,
> -                                                       avctp->transaction);
> -               if (len < 0)
> -                       goto err_metadata;
> -
> -               avrcp->code = CTYPE_INTERIM;
> +       }
>
> -               break;
> -       default:
> -               /* Invalid pdu_id */
> -               pdu->params[0] = E_INVALID_COMMAND;
> +       if (!handler->func) {
> +               pdu->params[0] = E_INVALID_PARAM;
>                goto err_metadata;
>        }
>
> -       return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;
> +       avrcp->code = handler->func(control, pdu, avctp->transaction);
> +
> +       return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
> +                                               ntohs(pdu->params_len);
>
>  err_metadata:
> -       avrcp->code = CTYPE_REJECTED;
>        pdu->params_len = htons(1);
> +       avrcp->code = CTYPE_REJECTED;
>
>        return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
>  }

Otherwise looks good.



Lucas De Marchi

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

* Re: [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 13:10   ` Lucas De Marchi
@ 2011-09-12 13:37     ` Szymon Janc
  2011-09-12 14:56       ` Luiz Augusto von Dentz
  2011-09-12 14:48     ` Luiz Augusto von Dentz
  1 sibling, 1 reply; 14+ messages in thread
From: Szymon Janc @ 2011-09-12 13:37 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: Luiz Augusto von Dentz, linux-bluetooth

> Hi Luiz

> >        sk = g_io_channel_unix_get_fd(control->io);
> > @@ -1451,19 +1451,19 @@ static struct pdu_handler {
> >  /* handle vendordep pdu inside an avctp packet */
> >  static int handle_vendordep_pdu(struct control *control,
> >                                        struct avctp_header *avctp,
> > -                                       struct avrcp_header *avrcp,
> > +                                       struct avc_header *avc,
> >                                        int operand_count)
> >  {
> >        struct pdu_handler *handler;
> > -       struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
> > +       struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
> 
> We might want to change this to:
> 
> struct avrcp_spec_avc_pdu *pdu = (avrcp_spec_avc_pdu *)(avc +
> AVC_HEADER_LENGTH);

I don't think this is correct, that would result in jumping
5 * (sizeof (struct avc_header)) bytes..

> This way we kill some warnings on ARM: arithmetic with void pointer

This should happen only if compiling with -Wpointer-arith or -pedantic.

-- 
BR
Szymon Janc

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

* Re: [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header
  2011-09-12 11:29 ` [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header Luiz Augusto von Dentz
@ 2011-09-12 13:47   ` Lucas De Marchi
  0 siblings, 0 replies; 14+ messages in thread
From: Lucas De Marchi @ 2011-09-12 13:47 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

On Mon, Sep 12, 2011 at 8:29 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> Since old avrcp_header got renamed to avc_header now we can use
> avrcp_header for AVRCP PDUs.
> ---
>  audio/control.c |   44 ++++++++++++++++++++++----------------------
>  1 files changed, 22 insertions(+), 22 deletions(-)

ack


Lucas De Marchi

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

* Re: [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling
  2011-09-12 13:25 ` [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Lucas De Marchi
@ 2011-09-12 14:40   ` Luiz Augusto von Dentz
  2011-09-12 14:49     ` Lucas De Marchi
  0 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 14:40 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth

Hi Lucas,

On Mon, Sep 12, 2011 at 4:25 PM, Lucas De Marchi
<lucas.demarchi@profusion.mobi> wrote:
> It might be my CFLAGS, but aren't you getting warnings of unused
> parameters since now every handler carries the transaction id?

Nope and Im not sure why checking unused parameter would be a nice
thing to have, specially dummy callbacks normally ignores some/all the
parameters.

-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 13:10   ` Lucas De Marchi
  2011-09-12 13:37     ` Szymon Janc
@ 2011-09-12 14:48     ` Luiz Augusto von Dentz
  1 sibling, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 14:48 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth

Hi Lucas,

On Mon, Sep 12, 2011 at 4:10 PM, Lucas De Marchi
<lucas.demarchi@profusion.mobi> wrote:
>> -       int sk;
>> +                                                       AVC_HEADER_LENGTH];
>> +       int sk = g_io_channel_unix_get_fd(control->io);
>
> You are re-introducing a bug here. Please see "dec26ee Fix fd usage
> when not connected"

Must have reintroduced it when I was rebasing it, gonna fix it.

>
>> +       struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
>
> We might want to change this to:
>
> struct avrcp_spec_avc_pdu *pdu = (avrcp_spec_avc_pdu *)(avc +
> AVC_HEADER_LENGTH);
>
> This way we kill some warnings on ARM: arithmetic with void pointer

Don't remember seeing any problem with it, but anyway it is not
something Im introducing.

-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling
  2011-09-12 14:40   ` Luiz Augusto von Dentz
@ 2011-09-12 14:49     ` Lucas De Marchi
  0 siblings, 0 replies; 14+ messages in thread
From: Lucas De Marchi @ 2011-09-12 14:49 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

On Mon, Sep 12, 2011 at 11:40 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Lucas,
>
> On Mon, Sep 12, 2011 at 4:25 PM, Lucas De Marchi
> <lucas.demarchi@profusion.mobi> wrote:
>> It might be my CFLAGS, but aren't you getting warnings of unused
>> parameters since now every handler carries the transaction id?
>
> Nope and Im not sure why checking unused parameter would be a nice
> thing to have, specially dummy callbacks normally ignores some/all the
> parameters.
>

Right, I've just checked the file acinclude.m4 and BlueZ uses
"-Wno-unused-parameter" in its CFLAGS.

It's fine, then.


Lucas De Marchi

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

* Re: [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 13:37     ` Szymon Janc
@ 2011-09-12 14:56       ` Luiz Augusto von Dentz
  2011-09-12 15:57         ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 14:56 UTC (permalink / raw)
  To: Szymon Janc; +Cc: Lucas De Marchi, linux-bluetooth

Hi Szymon,

On Mon, Sep 12, 2011 at 4:37 PM, Szymon Janc <szymon.janc@tieto.com> wrote:
>> Hi Luiz
>
>> >        sk = g_io_channel_unix_get_fd(control->io);
>> > @@ -1451,19 +1451,19 @@ static struct pdu_handler {
>> >  /* handle vendordep pdu inside an avctp packet */
>> >  static int handle_vendordep_pdu(struct control *control,
>> >                                        struct avctp_header *avctp,
>> > -                                       struct avrcp_header *avrcp,
>> > +                                       struct avc_header *avc,
>> >                                        int operand_count)
>> >  {
>> >        struct pdu_handler *handler;
>> > -       struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
>> > +       struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
>>
>> We might want to change this to:
>>
>> struct avrcp_spec_avc_pdu *pdu = (avrcp_spec_avc_pdu *)(avc +
>> AVC_HEADER_LENGTH);
>
> I don't think this is correct, that would result in jumping
> 5 * (sizeof (struct avc_header)) bytes..

I fail to see how the multiplication got into you equation, but anyway
I would prefer using something like this (void *)
&avc[AVC_HEADER_LENGTH], but that should be a different patch.

>> This way we kill some warnings on ARM: arithmetic with void pointer
>
> This should happen only if compiling with -Wpointer-arith or -pedantic.

I don't think we use those.

-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header
  2011-09-12 14:56       ` Luiz Augusto von Dentz
@ 2011-09-12 15:57         ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2011-09-12 15:57 UTC (permalink / raw)
  To: Szymon Janc; +Cc: Lucas De Marchi, linux-bluetooth

Hi Szymon,

On Mon, Sep 12, 2011 at 5:56 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Szymon,
>
> On Mon, Sep 12, 2011 at 4:37 PM, Szymon Janc <szymon.janc@tieto.com> wrote:
>>> Hi Luiz
>>
>>> >        sk = g_io_channel_unix_get_fd(control->io);
>>> > @@ -1451,19 +1451,19 @@ static struct pdu_handler {
>>> >  /* handle vendordep pdu inside an avctp packet */
>>> >  static int handle_vendordep_pdu(struct control *control,
>>> >                                        struct avctp_header *avctp,
>>> > -                                       struct avrcp_header *avrcp,
>>> > +                                       struct avc_header *avc,
>>> >                                        int operand_count)
>>> >  {
>>> >        struct pdu_handler *handler;
>>> > -       struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
>>> > +       struct avrcp_spec_avc_pdu *pdu = (void *) avc + AVC_HEADER_LENGTH;
>>>
>>> We might want to change this to:
>>>
>>> struct avrcp_spec_avc_pdu *pdu = (avrcp_spec_avc_pdu *)(avc +
>>> AVC_HEADER_LENGTH);
>>
>> I don't think this is correct, that would result in jumping
>> 5 * (sizeof (struct avc_header)) bytes..
>
> I fail to see how the multiplication got into you equation, but anyway
> I would prefer using something like this (void *)
> &avc[AVC_HEADER_LENGTH], but that should be a different patch.

Somebody (Thanks Hendrik), just point out this is wrong as much it was
wrong before and indeed it does the multiplication due to avc type (I
really failed to see that), anyway this will gonna away with other
cleanups since basically the callback receives a pointer to beginning
of the PDU in the callback (operands):

typedef size_t (*avctp_pdu_cb) (struct avctp *session, uint8_t transaction,
					uint8_t *code, uint8_t *subunit,
					uint8_t *operands, size_t operand_count,
					void *user_data);


-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2011-09-12 15:57 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-12 11:29 [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Luiz Augusto von Dentz
2011-09-12 11:29 ` [PATCH BlueZ 2/5] AVRCP: rename avrcp_header to avc_header Luiz Augusto von Dentz
2011-09-12 13:10   ` Lucas De Marchi
2011-09-12 13:37     ` Szymon Janc
2011-09-12 14:56       ` Luiz Augusto von Dentz
2011-09-12 15:57         ` Luiz Augusto von Dentz
2011-09-12 14:48     ` Luiz Augusto von Dentz
2011-09-12 11:29 ` [PATCH BlueZ 3/5] AVRCP: rename avrcp_spec_avc_pdu to avrcp_header Luiz Augusto von Dentz
2011-09-12 13:47   ` Lucas De Marchi
2011-09-12 11:29 ` [PATCH BlueZ 4/5] AVRCP: split AVCTP specific code from control.c Luiz Augusto von Dentz
2011-09-12 11:29 ` [PATCH BlueZ 5/5] AVRCP: move handling of vendor dependent PDU from control.c to avrcp.c Luiz Augusto von Dentz
2011-09-12 13:25 ` [PATCH BlueZ 1/5] AVRCP: use a vtable to simplify PDU parsing/handling Lucas De Marchi
2011-09-12 14:40   ` Luiz Augusto von Dentz
2011-09-12 14:49     ` Lucas De Marchi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.