All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/19] Implement AVRCP 1.3 profile - TG role
@ 2011-07-19 19:49 Lucas De Marchi
  2011-07-19 19:49 ` [RFC 01/19] avrcp: handle query for company ids Lucas De Marchi
                   ` (19 more replies)
  0 siblings, 20 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

This series implement all the mandatory and some optional features in AVRCP 1.3
profile, target role.

It passed all the related PTS tests and it's slightly tested with a Sony
MEX-BT2900 car kit.

Current limitation:
 * It does not support message continuation in case the metadata
   doesn't fit one AV/C packet.
 * The D-Bus API doesn't use an agent. Thus it's possible that two applications
   screw up the data passed to the control interface. I made this on purpose,
   since it allows me to easily test the interface. Future versions shall
   remove this limitation.

I preferred the API written on doc/control-api.txt rather than the past tentatives
of using mpris. I'm not sure it's worth supporting it, but I'd like to hear
opinions. Considering both approaches need the client to be modified in
order to support the agent, maybe using mpris would just be too much hassle.

regards,
Lucas De Marchi



Lucas De Marchi (19):
  avrcp: handle query for company ids
  avrcp: implement ChangeSetting() method for TG role
  avrcp: handle ListPlayerApplicationSettingAttributes pdu
  avrcp: handle ListPlayerApplicationSettingValues pdu
  avrcp: handle GetCurrentPlayerAplicationSettingValue pdu
  avrcp: handle SetPlayerApplicationSettingValue pdu
  avrcp: handle commands for future extension
  avrcp: handle InformDisplayableCharacterSet pdu
  avrcp: handle InformBatteryStatusOfCT pdu
  avrcp: implement ChangePlayback() method
  avrcp: handle GetPlayStatus pdu
  avrcp: implement ChangeTrack() method
  Add script to test control interface
  avrcp: handle RegisterNotification pdu
  avrcp: answer query for supported events
  avrcp: handle GetElementAttributes pdu
  avrcp: send response for registered events
  avrcp: change TG record to use version 1.3
  avrcp: update copyright

 audio/control.c     | 1193 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 doc/control-api.txt |   18 +-
 test/test-control   |   67 +++
 3 files changed, 1268 insertions(+), 10 deletions(-)
 create mode 100755 test/test-control

-- 
1.7.6


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

* [RFC 01/19] avrcp: handle query for company ids
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-20  8:25   ` Luiz Augusto von Dentz
  2011-07-19 19:49 ` [RFC 02/19] avrcp: implement ChangeSetting() method for TG role Lucas De Marchi
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   88 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index c3ef737..983c8cd 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -102,6 +102,22 @@
 #define FORWARD_OP		0x4b
 #define BACKWARD_OP		0x4c
 
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+/* Error codes for metadata transfer */
+#define E_INVALID_COMMAND	0x00
+#define E_INVALID_PARAM		0x01
+#define E_PARAM_NOT_FOUND	0x02
+#define E_INTERNAL		0x03
+
+/* PDU types for metadata transfer */
+#define AVRCP_GET_CAPABILITIES		0x10
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID		0x02
+#define CAP_EVENTS_SUPPORTED	0x03
+
 #define QUIRK_NO_RELEASE	1 << 0
 
 static DBusConnection *connection = NULL;
@@ -217,6 +233,11 @@ static struct {
 	{ NULL }
 };
 
+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+	IEEEID_BTSIG,
+};
+
 static GSList *avctp_callbacks = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
@@ -424,8 +445,71 @@ static int handle_vendordep_pdu(struct control *control,
 					struct avrcp_header *avrcp,
 					int operand_count)
 {
-	avrcp->code = CTYPE_NOT_IMPLEMENTED;
-	return AVRCP_HEADER_LENGTH;
+	struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
+	uint32_t company_id = (pdu->company_id[0] << 16) |
+				(pdu->company_id[1] << 8) |
+				(pdu->company_id[2]);
+	uint16_t len;
+	unsigned int i;
+
+	if (company_id != IEEEID_BTSIG ||
+				pdu->packet_type != AVCTP_PACKET_SINGLE) {
+		avrcp->code = CTYPE_NOT_IMPLEMENTED;
+		return AVRCP_HEADER_LENGTH;
+	}
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
+		pdu->params[0] = E_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	len = ntohs(pdu->params_len);
+
+	switch (pdu->pdu_id) {
+	case AVRCP_GET_CAPABILITIES:
+		if (len != 1 || avrcp->code != CTYPE_STATUS)
+			break;
+
+		DBG("GET_CAPABILITIES id=%u", pdu->params[0]);
+
+		switch (pdu->params[0]) { /* capability id */
+		case CAP_COMPANY_ID:
+			avrcp->code = CTYPE_STABLE;
+			pdu->params_len = htons(1 +
+						3 * G_N_ELEMENTS(company_ids));
+			pdu->params[1] = G_N_ELEMENTS(company_ids);
+
+			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;
+			}
+
+			return AVRCP_HEADER_LENGTH +
+					AVRCP_SPECAVCPDU_HEADER_LENGTH + 1 +
+					3 * G_N_ELEMENTS(company_ids);
+		}
+
+		pdu->params[0] = E_INVALID_PARAM;
+		goto err_metadata;
+	}
+
+	/*
+	 * If either pdu_id was invalid or message was malformed, respond with
+	 * E_INVALID_COMMAND. For other errors, we already jumped into
+	 * err_metadata.
+	 */
+	pdu->params[0] = E_INVALID_COMMAND;
+
+err_metadata:
+	avrcp->code = CTYPE_REJECTED;
+	pdu->params_len = htons(1);
+
+	return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
 }
 
 static void avctp_disconnected(struct audio_device *dev)
-- 
1.7.6


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

* [RFC 02/19] avrcp: implement ChangeSetting() method for TG role
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
  2011-07-19 19:49 ` [RFC 01/19] avrcp: handle query for company ids Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 03/19] avrcp: handle ListPlayerApplicationSettingAttributes pdu Lucas De Marchi
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

ChangeSetting() is used by an application to communicate with the
bluetooth device in TG role in order to notify the change in a certain
setting.
---
 audio/control.c     |  152 +++++++++++++++++++++++++++++++++++++++++++++++++++
 doc/control-api.txt |   12 ++--
 2 files changed, 159 insertions(+), 5 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 983c8cd..905f57a 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -120,6 +120,37 @@
 
 #define QUIRK_NO_RELEASE	1 << 0
 
+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,
+};
+
 static DBusConnection *connection = NULL;
 
 static GSList *servers = NULL;
@@ -216,6 +247,8 @@ struct control {
 	gboolean target;
 
 	uint8_t key_quirks[256];
+
+	uint8_t player_setting[PLAYER_SETTING_SCAN + 1];
 };
 
 static struct {
@@ -1193,12 +1226,131 @@ static DBusMessage *control_get_properties(DBusConnection *conn,
 	return reply;
 }
 
+static int setting_string_to_val(uint8_t setting, const char *value) {
+	int ret;
+
+	switch (setting) {
+	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 DBusMessage *setting_set(DBusMessage *msg, struct control *control,
+				const char *settingstr, const char *valstr)
+{
+	int val;
+	uint8_t setting;
+
+	if (!strcmp(settingstr, "Equalizer"))
+		setting = PLAYER_SETTING_EQUALIZER;
+	else if (!strcmp(settingstr, "Repeat"))
+		setting = PLAYER_SETTING_REPEAT;
+	else if (!strcmp(settingstr, "Shuffle"))
+		setting = PLAYER_SETTING_SHUFFLE;
+	else if (!strcmp(settingstr, "Scan"))
+		setting = PLAYER_SETTING_SCAN;
+	else
+		return btd_error_not_supported(msg);
+
+	val = setting_string_to_val(setting, valstr);
+	if (val == -1)
+		return btd_error_invalid_args(msg);
+
+	control->player_setting[setting] = val;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *control_change_setting(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct control *control = device->control;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *setting;
+	const char *value;
+
+	if (control->state != AVCTP_STATE_CONNECTED)
+		return btd_error_not_connected(msg);
+
+	if (control->target) /* Only supported if this device is in TG role */
+		return btd_error_not_supported(msg);
+
+	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, &setting);
+	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, &value);
+
+	return setting_set(msg, control, setting, value);
+}
+
 static GDBusMethodTable control_methods[] = {
 	{ "IsConnected",	"",	"b",	control_is_connected,
 						G_DBUS_METHOD_FLAG_DEPRECATED },
 	{ "GetProperties",	"",	"a{sv}",control_get_properties },
 	{ "VolumeUp",		"",	"",	volume_up },
 	{ "VolumeDown",		"",	"",	volume_down },
+	{ "ChangeSetting",	"sv",	"",	control_change_setting },
 	{ NULL, NULL, NULL, NULL }
 };
 
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 1a42846..931fa10 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -70,18 +70,20 @@ Methods		void Connect()
 		void ChangeSetting(string setting, variant value)
 
 			Called to transmit Application Settings, CT Status
-			and the like.
+			and the like. It's ONLY implemented for TG role. In
+			case they're never set, they will not be transmitted to
+			CT as a supported setting.
 
-			Currenet defined settings are represented with the
+			Current defined settings are represented with the
 			following keys:
 
 				Equalizer	off, on
 				Repeat		off, singletrack, alltracks, group
 				Shuffle		off, alltracks, group
 				Scan		off, alltracks, group
-				Battery		normal, warning, critical, external, fullcharge
-				System		powered, unpowered, unplugged
-				Volume		uint8
+				Battery		normal, warning, critical, external, fullcharge [ Not implemented]
+				System		powered, unpowered, unplugged [ Not implemented ]
+				Volume		uint8 [ Not implemented ]
 
 Signals		Connected()
 
-- 
1.7.6


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

* [RFC 03/19] avrcp: handle ListPlayerApplicationSettingAttributes pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
  2011-07-19 19:49 ` [RFC 01/19] avrcp: handle query for company ids Lucas De Marchi
  2011-07-19 19:49 ` [RFC 02/19] avrcp: implement ChangeSetting() method for TG role Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 04/19] avrcp: handle ListPlayerApplicationSettingValues pdu Lucas De Marchi
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 905f57a..56115df 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -113,6 +113,7 @@
 
 /* PDU types for metadata transfer */
 #define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -529,6 +530,27 @@ static int handle_vendordep_pdu(struct control *control,
 
 		pdu->params[0] = E_INVALID_PARAM;
 		goto err_metadata;
+	case AVRCP_LIST_PLAYER_ATTRIBUTES:
+		if (len != 0 || avrcp->code != CTYPE_STATUS)
+			break;
+
+		for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
+			if (!control->player_setting[i]) {
+				DBG("Ignoring setting %u since value"
+						"has never never been set", i);
+				continue;
+			}
+
+			len++;
+			pdu->params[len] = i;
+		}
+
+		avrcp->code = CTYPE_STABLE;
+		pdu->params[0] = len;
+		pdu->params_len = htons(len + 1);
+
+		return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + len + 1;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 04/19] avrcp: handle ListPlayerApplicationSettingValues pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (2 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 03/19] avrcp: handle ListPlayerApplicationSettingAttributes pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 05/19] avrcp: handle GetCurrentPlayerAplicationSettingValue pdu Lucas De Marchi
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 56115df..cd3beb7 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -114,6 +114,7 @@
 /* PDU types for metadata transfer */
 #define AVRCP_GET_CAPABILITIES		0x10
 #define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -474,6 +475,22 @@ static void handle_panel_passthrough(struct control *control,
 						operands[0] & 0x7F, status);
 }
 
+static unsigned int setting_get_max_val(uint8_t setting)
+{
+	switch (setting) {
+	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;
+}
+
 /* handle vendordep pdu inside an avctp packet */
 static int handle_vendordep_pdu(struct control *control,
 					struct avrcp_header *avrcp,
@@ -551,6 +568,26 @@ static int handle_vendordep_pdu(struct control *control,
 
 		return AVRCP_HEADER_LENGTH +
 				AVRCP_SPECAVCPDU_HEADER_LENGTH + len + 1;
+	case AVRCP_LIST_PLAYER_VALUES:
+		if (len != 1 || avrcp->code != CTYPE_STATUS)
+			break;
+
+		len = setting_get_max_val(pdu->params[0]);
+		if (!len) {
+			error("Setting is invalid: %u", pdu->params[0]);
+			pdu->params[0] = E_INVALID_PARAM;
+			goto err_metadata;
+		}
+
+		for (i = 1; i <= len; i++)
+			pdu->params[i] = i;
+
+		avrcp->code = CTYPE_STABLE;
+		pdu->params[0] = len;
+		pdu->params_len = htons(len + 1);
+
+		return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + len + 1;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 05/19] avrcp: handle GetCurrentPlayerAplicationSettingValue pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (3 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 04/19] avrcp: handle ListPlayerApplicationSettingValues pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 06/19] avrcp: handle SetPlayerApplicationSettingValue pdu Lucas De Marchi
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index cd3beb7..884e01c 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -115,6 +115,7 @@
 #define AVRCP_GET_CAPABILITIES		0x10
 #define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
 #define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -588,6 +589,64 @@ static int handle_vendordep_pdu(struct control *control,
 
 		return AVRCP_HEADER_LENGTH +
 				AVRCP_SPECAVCPDU_HEADER_LENGTH + len + 1;
+	case AVRCP_GET_CURRENT_PLAYER_VALUE:
+		if (len > 1 && pdu->params[0] == len - 1 &&
+						avrcp->code == CTYPE_STATUS) {
+			uint8_t settings[pdu->params[0]];
+
+			memcpy(settings, &pdu->params[1], len);
+			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) {
+					DBG("Ignoring invalid setting %u",
+								settings[i]);
+					continue;
+				}
+
+				if (settings[i] > PLAYER_SETTING_SCAN) {
+					DBG("Ignoring invalid setting %u",
+								settings[i]);
+					continue;
+				}
+
+				val = control->player_setting[settings[i]];
+				if (!val) {
+					DBG("Ignoring setting %u since value"
+							"has never been set",
+							settings[i]);
+					continue;
+				}
+
+				pdu->params[len * 2 + 1] = settings[i];
+				pdu->params[len * 2 + 2] = val;
+				len++;
+			}
+
+			if (!len) {
+				error("GetCurrentPlayerValue: no valid "
+						"attribute found in request");
+				pdu->params[0] = E_INVALID_PARAM;
+				goto err_metadata;
+			}
+
+			avrcp->code = CTYPE_STABLE;
+			pdu->params[0] = len;
+			pdu->params_len = htons(2 * len + 1);
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + 2 * len + 1;
+		}
+
+		break;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 06/19] avrcp: handle SetPlayerApplicationSettingValue pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (4 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 05/19] avrcp: handle GetCurrentPlayerAplicationSettingValue pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 07/19] avrcp: handle commands for future extension Lucas De Marchi
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |  177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 177 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 884e01c..2d6d9e6 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -116,6 +116,7 @@
 #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
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -492,6 +493,133 @@ static unsigned int setting_get_max_val(uint8_t setting)
 	return 0;
 }
 
+static const char *setting_equalizer_to_string(enum equalizer_mode value)
+{
+	switch (value) {
+	case (EQUALIZER_MODE_ON):
+		return "on";
+	case (EQUALIZER_MODE_OFF):
+		return "off";
+	}
+
+	return NULL;
+}
+
+static const char *setting_repeat_to_string(enum repeat_mode value)
+{
+	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";
+	}
+
+	return NULL;
+}
+
+static const char *setting_shuffle_to_string(enum shuffle_mode value)
+{
+	switch (value) {
+	case (SHUFFLE_MODE_OFF):
+		return "off";
+	case (SHUFFLE_MODE_ALL):
+		return "alltracks";
+	case (SHUFFLE_MODE_GROUP):
+		return "group";
+	}
+
+	return NULL;
+}
+
+static const char *setting_scan_to_string(enum scan_mode value)
+{
+	switch (value) {
+	case (SCAN_MODE_OFF):
+		return "off";
+	case (SCAN_MODE_ALL):
+		return "alltracks";
+	case (SCAN_MODE_GROUP):
+		return "group";
+	}
+
+	return NULL;
+}
+
+static const char *setting_value_to_string(enum player_setting setting,
+								uint8_t value)
+{
+	switch (setting) {
+	case (PLAYER_SETTING_EQUALIZER):
+		return setting_equalizer_to_string(value);
+	case(PLAYER_SETTING_REPEAT):
+		return setting_repeat_to_string(value);
+	case(PLAYER_SETTING_SHUFFLE):
+		return setting_shuffle_to_string(value);
+	case(PLAYER_SETTING_SCAN):
+		return setting_scan_to_string(value);
+	}
+
+	return NULL;
+}
+
+static const char *setting_to_string(enum player_setting setting)
+{
+	switch (setting) {
+	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 void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+	DBusMessageIter value;
+	char sig[2] = { type, '\0' };
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+	dbus_message_iter_append_basic(&value, type, val);
+
+	dbus_message_iter_close_container(iter, &value);
+}
+
+static dbus_bool_t emit_setting_changed(DBusConnection *conn,
+						const char *path,
+						const char *interface,
+						const char *name,
+						int type, void *value)
+{
+	DBusMessage *signal;
+	DBusMessageIter iter;
+
+	signal = dbus_message_new_signal(path, interface, "SettingChanged");
+
+	if (!signal) {
+		error("Unable to allocate new %s.SettingChanged signal",
+				interface);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+	append_variant(&iter, type, value);
+
+	return g_dbus_send_message(conn, signal);
+}
+
 /* handle vendordep pdu inside an avctp packet */
 static int handle_vendordep_pdu(struct control *control,
 					struct avrcp_header *avrcp,
@@ -647,6 +775,55 @@ static int handle_vendordep_pdu(struct control *control,
 		}
 
 		break;
+	case AVRCP_SET_PLAYER_VALUE:
+		if (len > 2 && avrcp->code == CTYPE_CONTROL) {
+			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 we respond with no parameters. Otherwise
+			 * an E_INVALID_PARAM is sent.
+			 */
+			for (i = 0; i < pdu->params[0]; i++) {
+				uint8_t setting = pdu->params[i * 2 + 1];
+				uint8_t val = pdu->params[i * 2 + 2];
+				const char *settingstr;
+				const char *valstr;
+
+				settingstr = setting_to_string(setting);
+				if (!setting)
+					continue;
+
+				valstr = setting_value_to_string(setting, val);
+				if (!valstr)
+					continue;
+
+				len++;
+				control->player_setting[setting] = val;
+
+				emit_setting_changed(control->dev->conn,
+						control->dev->path,
+						AUDIO_CONTROL_INTERFACE,
+						settingstr,
+						DBUS_TYPE_STRING, &valstr);
+			}
+
+			if (!len) {
+				pdu->params[0] = E_INVALID_PARAM;
+				goto err_metadata;
+			}
+
+			avrcp->code = CTYPE_STABLE;
+			pdu->params_len = 0;
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH;
+		}
+
+		break;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 07/19] avrcp: handle commands for future extension
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (5 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 06/19] avrcp: handle SetPlayerApplicationSettingValue pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 08/19] avrcp: handle InformDisplayableCharacterSet pdu Lucas De Marchi
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

AVRCP_GET_PLAYER_ATTRIBUTE_TEXT and AVRCP_GET_PLAYER_VALUE_TEXT shall
only be used if TG has extended attributes.

For the ones defined in AVRCP spec these commands should not be called.
Since we do not have extended attributes yet we can ignore those
commands.
---
 audio/control.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 2d6d9e6..36f727f 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -117,6 +117,8 @@
 #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
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -824,6 +826,20 @@ static int handle_vendordep_pdu(struct control *control,
 		}
 
 		break;
+	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
+	case AVRCP_GET_PLAYER_VALUE_TEXT:
+		if (avrcp->code != CTYPE_STATUS)
+			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;
+		goto err_metadata;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 08/19] avrcp: handle InformDisplayableCharacterSet pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (6 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 07/19] avrcp: handle commands for future extension Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 09/19] avrcp: handle InformBatteryStatusOfCT pdu Lucas De Marchi
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 36f727f..2825e6c 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -119,6 +119,7 @@
 #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
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -840,6 +841,18 @@ static int handle_vendordep_pdu(struct control *control,
 		 */
 		pdu->params[0] = E_INVALID_PARAM;
 		goto err_metadata;
+	case AVRCP_DISPLAYABLE_CHARSET:
+		if (len < 3 || avrcp->code != CTYPE_STATUS)
+			break;
+
+		/*
+		 * We acknowledge the commands, but we always use UTF-8 for
+		 * encoding since CT is obliged to support it
+		 */
+		pdu->params_len = 0;
+
+		return AVRCP_HEADER_LENGTH +
+			AVRCP_SPECAVCPDU_HEADER_LENGTH;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 09/19] avrcp: handle InformBatteryStatusOfCT pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (7 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 08/19] avrcp: handle InformDisplayableCharacterSet pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 10/19] avrcp: implement ChangePlayback() method Lucas De Marchi
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 2825e6c..866a558 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -120,6 +120,7 @@
 #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
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -158,6 +159,14 @@ enum scan_mode {
 	SCAN_MODE_GROUP =	3,
 };
 
+enum battery_status {
+	BATTERY_STATUS_NORMAL =		0,
+	BATTERY_STATUS_WARNING =	1,
+	BATTERY_STATUS_CRITICAL =	2,
+	BATTERY_STATUS_EXTERNAL =	3,
+	BATTERY_STATUS_FULL_CHARGE =	4,
+};
+
 static DBusConnection *connection = NULL;
 
 static GSList *servers = NULL;
@@ -585,6 +594,24 @@ static const char *setting_to_string(enum player_setting setting)
 	return NULL;
 }
 
+static const char *battery_status_to_string(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 void append_variant(DBusMessageIter *iter, int type, void *val)
 {
 	DBusMessageIter value;
@@ -853,6 +880,29 @@ static int handle_vendordep_pdu(struct control *control,
 
 		return AVRCP_HEADER_LENGTH +
 			AVRCP_SPECAVCPDU_HEADER_LENGTH;
+	case AVRCP_CT_BATTERY_STATUS:
+		if (len == 1 && avrcp->code == CTYPE_STATUS) {
+			const char *value;
+
+			value = battery_status_to_string(pdu->params[0]);
+			if (value == NULL) {
+				pdu->params[0] = E_INVALID_PARAM;
+				goto err_metadata;
+			}
+
+			emit_setting_changed(control->dev->conn,
+						control->dev->path,
+						AUDIO_CONTROL_INTERFACE,
+						"Battery",
+						DBUS_TYPE_STRING, &value);
+
+			pdu->params_len = 0;
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH;
+		}
+
+		break;
 	}
 
 	/*
@@ -1682,6 +1732,7 @@ static GDBusSignalTable control_signals[] = {
 	{ "Connected",			"",	G_DBUS_SIGNAL_FLAG_DEPRECATED},
 	{ "Disconnected",		"",	G_DBUS_SIGNAL_FLAG_DEPRECATED},
 	{ "PropertyChanged",		"sv"	},
+	{ "SettingChanged",		"sv"	},
 	{ NULL, NULL }
 };
 
-- 
1.7.6


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

* [RFC 10/19] avrcp: implement ChangePlayback() method
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (8 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 09/19] avrcp: handle InformBatteryStatusOfCT pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 11/19] avrcp: handle GetPlayStatus pdu Lucas De Marchi
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

ChangePlayback() is used by applications to inform BlueZ of the current
status of playback. It's expected to work only when the device is in TG
role.
---
 audio/control.c     |   66 +++++++++++++++++++++++++++++++++++++++++++++++++++
 doc/control-api.txt |    3 +-
 2 files changed, 68 insertions(+), 1 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 866a558..77e0e6d 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -167,6 +167,15 @@ enum battery_status {
 	BATTERY_STATUS_FULL_CHARGE =	4,
 };
 
+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
+};
+
 static DBusConnection *connection = NULL;
 
 static GSList *servers = NULL;
@@ -248,6 +257,11 @@ struct avctp_server {
 	uint32_t ct_record_id;
 };
 
+struct media_info {
+	enum play_status status;
+	uint32_t current_position;
+};
+
 struct control {
 	struct audio_device *dev;
 
@@ -265,6 +279,7 @@ struct control {
 	uint8_t key_quirks[256];
 
 	uint8_t player_setting[PLAYER_SETTING_SCAN + 1];
+	struct media_info mi;
 };
 
 static struct {
@@ -1653,6 +1668,24 @@ static int setting_string_to_val(uint8_t setting, const char *value) {
 	return -EINVAL;
 }
 
+static int play_status_string_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 DBusMessage *setting_set(DBusMessage *msg, struct control *control,
 				const char *settingstr, const char *valstr)
 {
@@ -1718,6 +1751,38 @@ static DBusMessage *control_change_setting(DBusConnection *conn,
 	return setting_set(msg, control, setting, value);
 }
 
+static DBusMessage *control_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 (control->state != AVCTP_STATE_CONNECTED)
+		return btd_error_not_connected(msg);
+
+	if (control->target) /* Only supported if this device is in TG role */
+		return btd_error_not_supported(msg);
+
+	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_string_to_val(statusstr);
+	if (status < 0)
+		return btd_error_invalid_args(msg);
+
+	control->mi.status = status;
+	control->mi.current_position = elapsed;
+
+	DBG("Change playback: %s %u", statusstr, elapsed);
+
+	return dbus_message_new_method_return(msg);
+}
+
 static GDBusMethodTable control_methods[] = {
 	{ "IsConnected",	"",	"b",	control_is_connected,
 						G_DBUS_METHOD_FLAG_DEPRECATED },
@@ -1725,6 +1790,7 @@ static GDBusMethodTable control_methods[] = {
 	{ "VolumeUp",		"",	"",	volume_up },
 	{ "VolumeDown",		"",	"",	volume_down },
 	{ "ChangeSetting",	"sv",	"",	control_change_setting },
+	{ "ChangePlayback",	"su",	"",	control_change_playback },
 	{ NULL, NULL, NULL, NULL }
 };
 
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 931fa10..ca97544 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -49,7 +49,8 @@ Methods		void Connect()
 
 			The status can be "playing", "stopped", "paused",
 			"forward-seek", "reverse-seek" or "error". Elapsed is
-			the position within the track in milliseconds.
+			the position within the track in milliseconds. ONLY
+			valid if BlueZ device is in TG role.
 
 		void ChangeTrack(dict metadata)
 
-- 
1.7.6


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

* [RFC 11/19] avrcp: handle GetPlayStatus pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (9 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 10/19] avrcp: implement ChangePlayback() method Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 12/19] avrcp: implement ChangeTrack() method Lucas De Marchi
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   34 ++++++++++++++++++++++++++++++++++
 1 files changed, 34 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 77e0e6d..03f1cb4 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -121,6 +121,7 @@
 #define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
 #define AVRCP_DISPLAYABLE_CHARSET	0x17
 #define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_PLAY_STATUS		0x30
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -260,6 +261,7 @@ struct avctp_server {
 struct media_info {
 	enum play_status status;
 	uint32_t current_position;
+	uint32_t track_length;
 };
 
 struct control {
@@ -918,6 +920,23 @@ static int handle_vendordep_pdu(struct control *control,
 		}
 
 		break;
+	case AVRCP_GET_PLAY_STATUS:
+		if (len == 0 && avrcp->code == CTYPE_STATUS) {
+			uint32_t track_length = htonl(control->mi.track_length);
+			uint32_t pos = htonl(control->mi.current_position);
+
+			memcpy(&pdu->params[0], &track_length, 4);
+			memcpy(&pdu->params[4], &pos, 4);
+			pdu->params[8] = control->mi.status;
+
+			avrcp->code = CTYPE_STABLE;
+			pdu->params_len = htons(9);
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + 9;
+		}
+
+		break;
 	}
 
 	/*
@@ -1207,6 +1226,20 @@ static void init_uinput(struct control *control)
 		DBG("AVRCP: uinput initialized for %s", address);
 }
 
+static void media_info_init(struct media_info *mi)
+{
+	memset(mi, 0, sizeof(*mi));
+	mi->status = PLAY_STATUS_ERROR;
+
+	/*
+	 * 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_length = 0xFFFFFFFF;
+	mi->current_position = 0xFFFFFFFF;
+}
+
 static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
 	struct control *control = data;
@@ -1237,6 +1270,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 		control->io = g_io_channel_ref(chan);
 
 	init_uinput(control);
+	media_info_init(&control->mi);
 
 	avctp_set_state(control, AVCTP_STATE_CONNECTED);
 	control->mtu = imtu;
-- 
1.7.6


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

* [RFC 12/19] avrcp: implement ChangeTrack() method
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (10 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 11/19] avrcp: handle GetPlayStatus pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 13/19] Add script to test control interface Lucas De Marchi
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

ChangeTrack() is used by applications to inform BlueZ that current track
changed, passing also the metadata. It's expected to work only when the
device is in TG role.
---
 audio/control.c     |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++
 doc/control-api.txt |    3 +
 2 files changed, 170 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 03f1cb4..670660f 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -261,6 +261,13 @@ struct avctp_server {
 struct media_info {
 	enum play_status status;
 	uint32_t current_position;
+
+	char *title;
+	char *artist;
+	char *album;
+	char *genre;
+	uint32_t ntracks;
+	uint32_t track;
 	uint32_t track_length;
 };
 
@@ -1240,6 +1247,47 @@ static void media_info_init(struct media_info *mi)
 	mi->current_position = 0xFFFFFFFF;
 }
 
+static void media_info_reset(struct media_info *mi)
+{
+	DBG("");
+
+	if (mi->title)
+		free(mi->title);
+
+	if (mi->artist)
+		free(mi->artist);
+
+	if (mi->album)
+		free(mi->album);
+
+	if (mi->genre)
+		free(mi->genre);
+
+	media_info_init(mi);
+}
+
+static void media_info_copy(struct media_info *dest, struct media_info *src)
+{
+	DBG("");
+
+	if (src->title)
+		dest->title = strdup(src->title);
+
+	if (src->artist)
+		dest->artist = strdup(src->artist);
+
+	if (src->album)
+		dest->album = strdup(src->album);
+
+	if (src->genre)
+		dest->genre = strdup(src->genre);
+
+	dest->ntracks = src->ntracks;
+	dest->track = src->track;
+	dest->track_length = src->track_length;
+}
+
+
 static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
 	struct control *control = data;
@@ -1817,6 +1865,124 @@ static DBusMessage *control_change_playback(DBusConnection *conn,
 	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_length);
+		} else {
+			return FALSE;
+		}
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (mi->title == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static DBusMessage *control_change_track(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct control *control = device->control;
+	DBusMessageIter iter;
+	struct media_info mi;
+
+	if (control->state != AVCTP_STATE_CONNECTED)
+		return btd_error_not_connected(msg);
+
+	if (control->target) /* Only supported if this device is in TG role */
+		return btd_error_not_supported(msg);
+
+	dbus_message_iter_init(msg, &iter);
+	if (!media_info_parse(&iter, &mi))
+		return btd_error_invalid_args(msg);
+
+	media_info_reset(&control->mi);
+	media_info_copy(&control->mi, &mi);
+
+	DBG("Track change:\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_length);
+
+	return dbus_message_new_method_return(msg);
+}
+
 static GDBusMethodTable control_methods[] = {
 	{ "IsConnected",	"",	"b",	control_is_connected,
 						G_DBUS_METHOD_FLAG_DEPRECATED },
@@ -1825,6 +1991,7 @@ static GDBusMethodTable control_methods[] = {
 	{ "VolumeDown",		"",	"",	volume_down },
 	{ "ChangeSetting",	"sv",	"",	control_change_setting },
 	{ "ChangePlayback",	"su",	"",	control_change_playback },
+	{ "ChangeTrack",	"a{sv}","",	control_change_track },
 	{ NULL, NULL, NULL, NULL }
 };
 
diff --git a/doc/control-api.txt b/doc/control-api.txt
index ca97544..c2fa0e9 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -68,6 +68,9 @@ Methods		void Connect()
 				TrackNumber	uint32
 				TrackDuration	uint32	(in milliseconds)
 
+			When track is changed, all metadata information not
+			supplied is treat as absent.
+
 		void ChangeSetting(string setting, variant value)
 
 			Called to transmit Application Settings, CT Status
-- 
1.7.6


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

* [RFC 13/19] Add script to test control interface
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (11 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 12/19] avrcp: implement ChangeTrack() method Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 14/19] avrcp: handle RegisterNotification pdu Lucas De Marchi
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 test/test-control |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 67 insertions(+), 0 deletions(-)
 create mode 100755 test/test-control

diff --git a/test/test-control b/test/test-control
new file mode 100755
index 0000000..4351d73
--- /dev/null
+++ b/test/test-control
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+USAGE = """Usage: %s <command> [args]
+
+	changetrack <bdaddr> <key> <value> [<key> <value> ...]
+	""" % sys.argv[0]
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+		make_option("-i", "--device", action="store",
+				type="string", dest="dev_id"),
+		]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+	adapter_path = manager.FindAdapter(options.dev_id)
+else:
+	adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+							"org.bluez.Adapter")
+
+if len(args) < 2:
+	print(USAGE)
+	sys.exit(1)
+
+device = adapter.FindDevice(args[1])
+control = dbus.Interface(bus.get_object("org.bluez", device),
+                                "org.bluez.Control")
+
+def handle_change_track(control, args):
+    if len(args) % 2 != 0:
+        print("Don't know how to handle odd number of parameters")
+        print(USAGE)
+        sys.exit(1)
+
+    d = dict()
+    for i in range(2, len(args), 2):
+        key = args[i]
+        if key == "Title" or key == "Artist" or key == "Album" \
+                                            or key == "Genre":
+            d[key] = dbus.String(args[i + 1].decode(sys.stdin.encoding))
+        elif key == "NumberOfTracks" or key == "TrackNumber"       \
+                                            or key == "TrackDuration":
+            d[key] = dbus.UInt32(int(args[i + 1]))
+        else:
+            print("Unknown metadata: %s" % key)
+            sys.exit(1)
+
+    d = dbus.Dictionary(d, signature='sv')
+    control.ChangeTrack(d)
+
+if args[0] == "changetrack":
+    handle_change_track(control, args)
+else:
+    print("Unknown command -- %s" % argv[1])
+    print(USAGE)
+    sys.exit(1)
-- 
1.7.6


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

* [RFC 14/19] avrcp: handle RegisterNotification pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (12 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 13/19] Add script to test control interface Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 15/19] avrcp: answer query for supported events Lucas De Marchi
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

Handle mandatory events according to AVRCP 1.3 spec.
---
 audio/control.c |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 670660f..212e95c 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -74,10 +74,12 @@
 /* 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_INTERIM		0xF
 
 /* opcodes */
 #define OP_VENDORDEP		0x00
@@ -122,6 +124,22 @@
 #define AVRCP_DISPLAYABLE_CHARSET	0x17
 #define AVRCP_CT_BATTERY_STATUS		0x18
 #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
+#define AVRCP_EVENT_TRACK_REACHED_END			0x03
+#define AVRCP_EVENT_TRACK_REACHED_START			0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED		0x05
+#define AVRCP_EVENT_BATT_STATUS_CHANGED			0x06
+#define AVRCP_EVENT_SYSTEM_STATUS_CHANGED		0x07
+#define AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED	0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED		0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED		0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED		0x0b
+#define AVRCP_EVENT_UIDS_CHANGED			0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED			0x0d
 
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
@@ -289,6 +307,7 @@ struct control {
 
 	uint8_t player_setting[PLAYER_SETTING_SCAN + 1];
 	struct media_info mi;
+	uint16_t registered_events;
 };
 
 static struct {
@@ -944,6 +963,38 @@ static int handle_vendordep_pdu(struct control *control,
 		}
 
 		break;
+	case AVRCP_REGISTER_NOTIFICATION:
+		if (len == 5 && avrcp->code == CTYPE_NOTIFY) {
+			int size;
+
+			switch (pdu->params[0]) {
+			case (AVRCP_EVENT_PLAYBACK_STATUS_CHANGED):
+				size = 2;
+				pdu->params[1] = control->mi.status;
+
+				break;
+			case (AVRCP_EVENT_TRACK_CHANGED):
+				size = 9;
+				memset(&pdu->params[1], 0, 8);
+
+				break;
+			default:
+				/* All other events are not supported yet */
+				pdu->params[0] = E_INVALID_PARAM;
+				goto err_metadata;
+			}
+
+			/* Register event */
+			control->registered_events |= (1 << pdu->params[0]);
+
+			avrcp->code = CTYPE_INTERIM;
+			pdu->params_len = htons(size);
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + size;
+		}
+
+		break;
 	}
 
 	/*
-- 
1.7.6


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

* [RFC 15/19] avrcp: answer query for supported events
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (13 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 14/19] avrcp: handle RegisterNotification pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 16/19] avrcp: handle GetElementAttributes pdu Lucas De Marchi
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 212e95c..88efdb5 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -745,6 +745,16 @@ static int handle_vendordep_pdu(struct control *control,
 			return AVRCP_HEADER_LENGTH +
 					AVRCP_SPECAVCPDU_HEADER_LENGTH + 1 +
 					3 * G_N_ELEMENTS(company_ids);
+		case CAP_EVENTS_SUPPORTED:
+			avrcp->code = CTYPE_STABLE;
+			/* CpabilityID + CapabilityCount + n_events */
+			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 AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + 4;
 		}
 
 		pdu->params[0] = E_INVALID_PARAM;
-- 
1.7.6


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

* [RFC 16/19] avrcp: handle GetElementAttributes pdu
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (14 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 15/19] avrcp: answer query for supported events Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 17/19] avrcp: send response for registered events Lucas De Marchi
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |  162 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 162 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 88efdb5..d37848e 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -123,6 +123,7 @@
 #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
 
@@ -195,6 +196,16 @@ enum play_status {
 	PLAY_STATUS_ERROR =		0xFF
 };
 
+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;
@@ -655,6 +666,95 @@ static const char *battery_status_to_string(enum battery_status status)
 	return NULL;
 }
 
+/*
+ * 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 returns the size written.
+ * If there's an error, nothing is written and -EINVAL is returned.
+ */
+static int media_info_get_elem(struct control *control, uint32_t id,
+							uint8_t *buf)
+{
+	struct media_info_elem {
+		uint32_t id;
+		uint16_t charset;
+		uint16_t len;
+		uint8_t val[];
+	};
+	struct media_info_elem *elem = (void *)buf;
+	struct media_info *mi = &control->mi;
+	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->current_position == 0xFFFFFFFF)
+			return -ENOENT;
+
+		snprintf(valstr, 20, "%u", mi->current_position);
+		len = strlen(valstr);
+		memcpy(elem->val, valstr, len);
+		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 append_variant(DBusMessageIter *iter, int type, void *val)
 {
 	DBusMessageIter value;
@@ -800,6 +900,68 @@ static int handle_vendordep_pdu(struct control *control,
 
 		return AVRCP_HEADER_LENGTH +
 				AVRCP_SPECAVCPDU_HEADER_LENGTH + len + 1;
+	case AVRCP_GET_ELEMENT_ATTRIBUTES:
+		if (len > 8 && avrcp->code == CTYPE_STATUS) {
+			uint16_t pos = 1; /* keep track of position in resp */
+			uint8_t nattr = pdu->params[8];
+			uint64_t *identifier = (void *) &pdu->params[0];
+			int size;
+
+			if (*identifier != 0) {
+				pdu->params[0] = E_INVALID_PARAM;
+				goto err_metadata;
+			}
+
+			len = 0;
+
+			if (!nattr) {
+				/*
+				 * Return all available information, at least
+				 * title must be returned
+				 */
+				for (i = 1; i < MEDIA_INFO_CURRENT_POSITION;
+									i++) {
+					size = media_info_get_elem(control,
+							i, &pdu->params[pos]);
+
+					if (size > 0) {
+						len++;
+						pos += size;
+					}
+				}
+			} else {
+				uint32_t attr_ids[nattr];
+
+				memcpy(&attr_ids[0], &pdu->params[9],
+								nattr * 4);
+
+				for (i = 0; i < nattr; i++) {
+					uint32_t attr = ntohl(attr_ids[i]);
+
+					size = media_info_get_elem(control,
+						attr, &pdu->params[pos]);
+
+					if (size > 0) {
+						len++;
+						pos += size;
+					}
+				}
+
+				if (!len) {
+					pdu->params[0] = E_INVALID_PARAM;
+					goto err_metadata;
+				}
+			}
+
+			avrcp->code = CTYPE_STABLE;
+			pdu->params[0] = len;
+			pdu->params_len = htons(pos);
+
+			return AVRCP_HEADER_LENGTH +
+				AVRCP_SPECAVCPDU_HEADER_LENGTH + pos;
+		}
+
+		break;
 	case AVRCP_GET_CURRENT_PLAYER_VALUE:
 		if (len > 1 && pdu->params[0] == len - 1 &&
 						avrcp->code == CTYPE_STATUS) {
-- 
1.7.6


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

* [RFC 17/19] avrcp: send response for registered events
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (15 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 16/19] avrcp: handle GetElementAttributes pdu Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 18/19] avrcp: change TG record to use version 1.3 Lucas De Marchi
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

When a certain event occur, check if CT registered to receive that
notification and send a response.
---
 audio/control.c |   87 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index d37848e..894a706 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -79,6 +79,7 @@
 #define CTYPE_ACCEPTED		0x9
 #define CTYPE_REJECTED		0xA
 #define CTYPE_STABLE		0xC
+#define CTYPE_CHANGED		0xD
 #define CTYPE_INTERIM		0xF
 
 /* opcodes */
@@ -319,6 +320,7 @@ struct control {
 	uint8_t player_setting[PLAYER_SETTING_SCAN + 1];
 	struct media_info mi;
 	uint16_t registered_events;
+	uint8_t transaction_events[AVRCP_EVENT_VOLUME_CHANGED + 1];
 };
 
 static struct {
@@ -793,8 +795,74 @@ static dbus_bool_t emit_setting_changed(DBusConnection *conn,
 	return g_dbus_send_message(conn, signal);
 }
 
+static int avctp_send_event(struct control *control, uint8_t id)
+{
+	uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+					AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
+	struct avctp_header *avctp = (void *) buf;
+	struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
+	struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
+							AVRCP_HEADER_LENGTH];
+	int sk = g_io_channel_unix_get_fd(control->io);
+	uint16_t size;
+
+	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);
+
+	avrcp->code = CTYPE_CHANGED;
+	avrcp->subunit_type = SUBUNIT_PANEL;
+	avrcp->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;
+
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+	pdu->params[0] = id;
+
+	switch (id) {
+	case (AVRCP_EVENT_PLAYBACK_STATUS_CHANGED):
+		size = 2;
+		pdu->params[1] = control->mi.status;
+
+		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);
+	size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+					AVRCP_SPECAVCPDU_HEADER_LENGTH;
+
+	if (write(sk, buf, size) < 0)
+		return -errno;
+
+	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+	control->registered_events ^= 1 << id;
+
+	return 0;
+}
+
 /* 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)
 {
@@ -1158,6 +1226,8 @@ static int handle_vendordep_pdu(struct control *control,
 
 			/* Register event */
 			control->registered_events |= (1 << pdu->params[0]);
+			control->transaction_events[pdu->params[0]] =
+							avctp->transaction;
 
 			avrcp->code = CTYPE_INTERIM;
 			pdu->params_len = htons(size);
@@ -1359,7 +1429,8 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
 		int r_size;
 		operand_count -= 3;
 		avctp->cr = AVCTP_RESPONSE;
-		r_size = handle_vendordep_pdu(control, avrcp, operand_count);
+		r_size = handle_vendordep_pdu(control, avctp, avrcp,
+								operand_count);
 		packet_size = AVCTP_HEADER_LENGTH + r_size;
 	} else {
 		avctp->cr = AVCTP_RESPONSE;
@@ -2080,7 +2151,16 @@ static DBusMessage *control_change_playback(DBusConnection *conn,
 	if (status < 0)
 		return btd_error_invalid_args(msg);
 
-	control->mi.status = status;
+	if ((uint8_t) status != control->mi.status) {
+		control->mi.status = status;
+
+		if (control->registered_events &
+				(1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED)) {
+			avctp_send_event(control,
+					AVRCP_EVENT_PLAYBACK_STATUS_CHANGED);
+		}
+	}
+
 	control->mi.current_position = elapsed;
 
 	DBG("Change playback: %s %u", statusstr, elapsed);
@@ -2203,6 +2283,9 @@ static DBusMessage *control_change_track(DBusConnection *conn,
 			   mi.title, mi.artist, mi.album, mi.genre,
 			   mi.ntracks, mi.track, mi.track_length);
 
+	if (control->registered_events & (1 << AVRCP_EVENT_TRACK_CHANGED))
+		avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED);
+
 	return dbus_message_new_method_return(msg);
 }
 
-- 
1.7.6


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

* [RFC 18/19] avrcp: change TG record to use version 1.3
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (16 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 17/19] avrcp: send response for registered events Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-19 19:49 ` [RFC 19/19] avrcp: update copyright Lucas De Marchi
  2011-07-20  8:21 ` [RFC 00/19] Implement AVRCP 1.3 profile - TG role Luiz Augusto von Dentz
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 894a706..1704c0a 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -420,7 +420,7 @@ static sdp_record_t *avrcp_tg_record(void)
 	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;
+	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;
 
 	record = sdp_record_alloc();
 	if (!record)
-- 
1.7.6


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

* [RFC 19/19] avrcp: update copyright
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (17 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 18/19] avrcp: change TG record to use version 1.3 Lucas De Marchi
@ 2011-07-19 19:49 ` Lucas De Marchi
  2011-07-20  8:21 ` [RFC 00/19] Implement AVRCP 1.3 profile - TG role Luiz Augusto von Dentz
  19 siblings, 0 replies; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-19 19:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

---
 audio/control.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 1704c0a..0f03ec0 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -4,6 +4,7 @@
  *
  *  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
-- 
1.7.6


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

* Re: [RFC 00/19] Implement AVRCP 1.3 profile - TG role
  2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
                   ` (18 preceding siblings ...)
  2011-07-19 19:49 ` [RFC 19/19] avrcp: update copyright Lucas De Marchi
@ 2011-07-20  8:21 ` Luiz Augusto von Dentz
  2011-07-20 12:55   ` Lucas De Marchi
  19 siblings, 1 reply; 24+ messages in thread
From: Luiz Augusto von Dentz @ 2011-07-20  8:21 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth

Hi Lucas,

On Tue, Jul 19, 2011 at 10:49 PM, Lucas De Marchi
<lucas.demarchi@profusion.mobi> wrote:
> This series implement all the mandatory and some optional features in AVRCP 1.3
> profile, target role.
>
> It passed all the related PTS tests and it's slightly tested with a Sony
> MEX-BT2900 car kit.
>
> Current limitation:
>  * It does not support message continuation in case the metadata
>   doesn't fit one AV/C packet.
>  * The D-Bus API doesn't use an agent. Thus it's possible that two applications
>   screw up the data passed to the control interface. I made this on purpose,
>   since it allows me to easily test the interface. Future versions shall
>   remove this limitation.

I don't think it is a good idea to introduce a new API knowing its
limitation, also in some systems we can have different players e.g.
video player and music player and iirc some pdus can address those
players separately. So if you really want this for testing purpose I
would suggest using another interface which should be disabled by
default, this way applications know it is unstable and we can
experiment with it.

> I preferred the API written on doc/control-api.txt rather than the past tentatives
> of using mpris. I'm not sure it's worth supporting it, but I'd like to hear
> opinions. Considering both approaches need the client to be modified in
> order to support the agent, maybe using mpris would just be too much hassle.

I think we might need an agent API anyway, since MPRIS players might
be siting in the session bus so bluetoothd cannot really connect to
them directly, in the other hand there is nothing preventing someone
to write an agent that uses MPRIS to talk to players.

> regards,
> Lucas De Marchi
>
>
>
> Lucas De Marchi (19):
>  avrcp: handle query for company ids
>  avrcp: implement ChangeSetting() method for TG role
>  avrcp: handle ListPlayerApplicationSettingAttributes pdu
>  avrcp: handle ListPlayerApplicationSettingValues pdu
>  avrcp: handle GetCurrentPlayerAplicationSettingValue pdu
>  avrcp: handle SetPlayerApplicationSettingValue pdu
>  avrcp: handle commands for future extension
>  avrcp: handle InformDisplayableCharacterSet pdu
>  avrcp: handle InformBatteryStatusOfCT pdu
>  avrcp: implement ChangePlayback() method
>  avrcp: handle GetPlayStatus pdu
>  avrcp: implement ChangeTrack() method
>  Add script to test control interface
>  avrcp: handle RegisterNotification pdu
>  avrcp: answer query for supported events
>  avrcp: handle GetElementAttributes pdu
>  avrcp: send response for registered events
>  avrcp: change TG record to use version 1.3
>  avrcp: update copyright
>
>  audio/control.c     | 1193 ++++++++++++++++++++++++++++++++++++++++++++++++++-
>  doc/control-api.txt |   18 +-
>  test/test-control   |   67 +++
>  3 files changed, 1268 insertions(+), 10 deletions(-)
>  create mode 100755 test/test-control
>
> --
> 1.7.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



-- 
Luiz Augusto von Dentz

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

* Re: [RFC 01/19] avrcp: handle query for company ids
  2011-07-19 19:49 ` [RFC 01/19] avrcp: handle query for company ids Lucas De Marchi
@ 2011-07-20  8:25   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 24+ messages in thread
From: Luiz Augusto von Dentz @ 2011-07-20  8:25 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth

Hi Lucas,

On Tue, Jul 19, 2011 at 10:49 PM, Lucas De Marchi
<lucas.demarchi@profusion.mobi> wrote:
> ---
>  audio/control.c |   88 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 86 insertions(+), 2 deletions(-)
>
> diff --git a/audio/control.c b/audio/control.c
> index c3ef737..983c8cd 100644
> --- a/audio/control.c
> +++ b/audio/control.c
> @@ -102,6 +102,22 @@
>  #define FORWARD_OP             0x4b
>  #define BACKWARD_OP            0x4c
>
> +/* Company IDs for vendor dependent commands */
> +#define IEEEID_BTSIG           0x001958
> +
> +/* Error codes for metadata transfer */
> +#define E_INVALID_COMMAND      0x00
> +#define E_INVALID_PARAM                0x01
> +#define E_PARAM_NOT_FOUND      0x02
> +#define E_INTERNAL             0x03
> +
> +/* PDU types for metadata transfer */
> +#define AVRCP_GET_CAPABILITIES         0x10
> +
> +/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
> +#define CAP_COMPANY_ID         0x02
> +#define CAP_EVENTS_SUPPORTED   0x03
> +
>  #define QUIRK_NO_RELEASE       1 << 0
>
>  static DBusConnection *connection = NULL;
> @@ -217,6 +233,11 @@ static struct {
>        { NULL }
>  };
>
> +/* Company IDs supported by this device */
> +static uint32_t company_ids[] = {
> +       IEEEID_BTSIG,
> +};
> +
>  static GSList *avctp_callbacks = NULL;
>
>  static void auth_cb(DBusError *derr, void *user_data);
> @@ -424,8 +445,71 @@ static int handle_vendordep_pdu(struct control *control,
>                                        struct avrcp_header *avrcp,
>                                        int operand_count)
>  {
> -       avrcp->code = CTYPE_NOT_IMPLEMENTED;
> -       return AVRCP_HEADER_LENGTH;
> +       struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
> +       uint32_t company_id = (pdu->company_id[0] << 16) |
> +                               (pdu->company_id[1] << 8) |
> +                               (pdu->company_id[2]);
> +       uint16_t len;
> +       unsigned int i;
> +
> +       if (company_id != IEEEID_BTSIG ||
> +                               pdu->packet_type != AVCTP_PACKET_SINGLE) {
> +               avrcp->code = CTYPE_NOT_IMPLEMENTED;
> +               return AVRCP_HEADER_LENGTH;
> +       }
> +
> +       pdu->packet_type = 0;
> +       pdu->rsvd = 0;
> +
> +       if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
> +               pdu->params[0] = E_INVALID_COMMAND;
> +               goto err_metadata;
> +       }
> +
> +       len = ntohs(pdu->params_len);

I would consider doing the each pdu handling in its own function,
otherwise handle_vendordep_pdu might become a little too big.

> +       switch (pdu->pdu_id) {
> +       case AVRCP_GET_CAPABILITIES:
> +               if (len != 1 || avrcp->code != CTYPE_STATUS)
> +                       break;
> +
> +               DBG("GET_CAPABILITIES id=%u", pdu->params[0]);
> +
> +               switch (pdu->params[0]) { /* capability id */
> +               case CAP_COMPANY_ID:
> +                       avrcp->code = CTYPE_STABLE;
> +                       pdu->params_len = htons(1 +
> +                                               3 * G_N_ELEMENTS(company_ids));
> +                       pdu->params[1] = G_N_ELEMENTS(company_ids);
> +
> +                       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;
> +                       }
> +
> +                       return AVRCP_HEADER_LENGTH +
> +                                       AVRCP_SPECAVCPDU_HEADER_LENGTH + 1 +
> +                                       3 * G_N_ELEMENTS(company_ids);
> +               }
> +
> +               pdu->params[0] = E_INVALID_PARAM;
> +               goto err_metadata;
> +       }
> +
> +       /*
> +        * If either pdu_id was invalid or message was malformed, respond with
> +        * E_INVALID_COMMAND. For other errors, we already jumped into
> +        * err_metadata.
> +        */
> +       pdu->params[0] = E_INVALID_COMMAND;
> +
> +err_metadata:
> +       avrcp->code = CTYPE_REJECTED;
> +       pdu->params_len = htons(1);
> +
> +       return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
>  }
>
>  static void avctp_disconnected(struct audio_device *dev)
> --
> 1.7.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



-- 
Luiz Augusto von Dentz

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

* Re: [RFC 00/19] Implement AVRCP 1.3 profile - TG role
  2011-07-20  8:21 ` [RFC 00/19] Implement AVRCP 1.3 profile - TG role Luiz Augusto von Dentz
@ 2011-07-20 12:55   ` Lucas De Marchi
  2011-07-21  7:11     ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 24+ messages in thread
From: Lucas De Marchi @ 2011-07-20 12:55 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz

On Wed, Jul 20, 2011 at 5:21 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi Lucas,
>
> On Tue, Jul 19, 2011 at 10:49 PM, Lucas De Marchi
> <lucas.demarchi@profusion.mobi> wrote:
> > This series implement all the mandatory and some optional features in AVRCP 1.3
> > profile, target role.
> >
> > It passed all the related PTS tests and it's slightly tested with a Sony
> > MEX-BT2900 car kit.
> >
> > Current limitation:
> >  * It does not support message continuation in case the metadata
> >   doesn't fit one AV/C packet.
> >  * The D-Bus API doesn't use an agent. Thus it's possible that two applications
> >   screw up the data passed to the control interface. I made this on purpose,
> >   since it allows me to easily test the interface. Future versions shall
> >   remove this limitation.
>
> I don't think it is a good idea to introduce a new API knowing its
> limitation, also in some systems we can have different players e.g.
> video player and music player and iirc some pdus can address those
> players separately. So if you really want this for testing purpose I
> would suggest using another interface which should be disabled by
> default, this way applications know it is unstable and we can
> experiment with it.

What I tried to say is that the interface is as is because it's still
for testing. I'm not intending to upstream it without the agent.

> > I preferred the API written on doc/control-api.txt rather than the past tentatives
> > of using mpris. I'm not sure it's worth supporting it, but I'd like to hear
> > opinions. Considering both approaches need the client to be modified in
> > order to support the agent, maybe using mpris would just be too much hassle.
>
> I think we might need an agent API anyway, since MPRIS players might
> be siting in the session bus so bluetoothd cannot really connect to
> them directly, in the other hand there is nothing preventing someone
> to write an agent that uses MPRIS to talk to players.

In that case, the agent could perfectly talk to our D-Bus interface
and use MPRIS to talk to players. Or use MPRIS on both ends.

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

* Re: [RFC 00/19] Implement AVRCP 1.3 profile - TG role
  2011-07-20 12:55   ` Lucas De Marchi
@ 2011-07-21  7:11     ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 24+ messages in thread
From: Luiz Augusto von Dentz @ 2011-07-21  7:11 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth

Hi Lucas,

On Wed, Jul 20, 2011 at 3:55 PM, Lucas De Marchi
<lucas.demarchi@profusion.mobi> wrote:
>> I think we might need an agent API anyway, since MPRIS players might
>> be siting in the session bus so bluetoothd cannot really connect to
>> them directly, in the other hand there is nothing preventing someone
>> to write an agent that uses MPRIS to talk to players.
>
> In that case, the agent could perfectly talk to our D-Bus interface
> and use MPRIS to talk to players. Or use MPRIS on both ends.

Yep, but I think we don't want to use agents as proxy because D-Bus
round trips are expensive and sometimes we need to contact the agent
to respond to a request.

-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2011-07-21  7:11 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-19 19:49 [RFC 00/19] Implement AVRCP 1.3 profile - TG role Lucas De Marchi
2011-07-19 19:49 ` [RFC 01/19] avrcp: handle query for company ids Lucas De Marchi
2011-07-20  8:25   ` Luiz Augusto von Dentz
2011-07-19 19:49 ` [RFC 02/19] avrcp: implement ChangeSetting() method for TG role Lucas De Marchi
2011-07-19 19:49 ` [RFC 03/19] avrcp: handle ListPlayerApplicationSettingAttributes pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 04/19] avrcp: handle ListPlayerApplicationSettingValues pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 05/19] avrcp: handle GetCurrentPlayerAplicationSettingValue pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 06/19] avrcp: handle SetPlayerApplicationSettingValue pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 07/19] avrcp: handle commands for future extension Lucas De Marchi
2011-07-19 19:49 ` [RFC 08/19] avrcp: handle InformDisplayableCharacterSet pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 09/19] avrcp: handle InformBatteryStatusOfCT pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 10/19] avrcp: implement ChangePlayback() method Lucas De Marchi
2011-07-19 19:49 ` [RFC 11/19] avrcp: handle GetPlayStatus pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 12/19] avrcp: implement ChangeTrack() method Lucas De Marchi
2011-07-19 19:49 ` [RFC 13/19] Add script to test control interface Lucas De Marchi
2011-07-19 19:49 ` [RFC 14/19] avrcp: handle RegisterNotification pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 15/19] avrcp: answer query for supported events Lucas De Marchi
2011-07-19 19:49 ` [RFC 16/19] avrcp: handle GetElementAttributes pdu Lucas De Marchi
2011-07-19 19:49 ` [RFC 17/19] avrcp: send response for registered events Lucas De Marchi
2011-07-19 19:49 ` [RFC 18/19] avrcp: change TG record to use version 1.3 Lucas De Marchi
2011-07-19 19:49 ` [RFC 19/19] avrcp: update copyright Lucas De Marchi
2011-07-20  8:21 ` [RFC 00/19] Implement AVRCP 1.3 profile - TG role Luiz Augusto von Dentz
2011-07-20 12:55   ` Lucas De Marchi
2011-07-21  7:11     ` Luiz Augusto von Dentz

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.