All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply
@ 2012-10-25 13:59 Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values Luiz Augusto von Dentz
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

avctp_disconnect does disconnect immediatelly so there is no time to
set the pending message.
---
 audio/control.c | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 37b027b..1620128 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -65,7 +65,6 @@ struct control {
 	struct avctp *session;
 	gboolean target;
 	DBusMessage *connect;
-	DBusMessage *disconnect;
 };
 
 static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -88,13 +87,6 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 			control->connect = NULL;
 		}
 
-		if (control->disconnect) {
-			g_dbus_send_reply(conn, control->disconnect,
-							DBUS_TYPE_INVALID);
-			dbus_message_unref(control->disconnect);
-			control->disconnect = NULL;
-		}
-
 		if (old_state != AVCTP_STATE_CONNECTED)
 			break;
 
@@ -184,14 +176,9 @@ static DBusMessage *control_disconnect(DBusConnection *conn, DBusMessage *msg,
 	if (!control->session)
 		return btd_error_not_connected(msg);
 
-	if (control->disconnect)
-		return btd_error_in_progress(msg);
-
 	avctp_disconnect(control->session);
 
-	control->disconnect = dbus_message_ref(msg);
-
-	return NULL;
+	return dbus_message_new_method_return(msg);
 }
 
 static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
@@ -273,7 +260,7 @@ static const GDBusMethodTable control_methods[] = {
 				NULL, GDBUS_ARGS({ "connected", "b" }),
 				control_is_connected) },
 	{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
-	{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, control_disconnect) },
+	{ GDBUS_METHOD("Disconnect", NULL, NULL, control_disconnect) },
 	{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
 	{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
 	{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -309,9 +296,6 @@ static void path_unregister(void *data)
 	if (control->connect)
 		dbus_message_unref(control->connect);
 
-	if (control->disconnect)
-		dbus_message_unref(control->disconnect);
-
 	g_free(control);
 	dev->control = NULL;
 }
-- 
1.7.11.7


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

* [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 03/11] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

This replaces get_metadata callback with get_string and get_uint32
which uses proper types as return.
---
 audio/avrcp.c | 23 ++++----------
 audio/avrcp.h |  3 +-
 audio/media.c | 96 ++++++++++++++++++++++-------------------------------------
 3 files changed, 42 insertions(+), 80 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 2f5df21..fe304d1 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -500,8 +500,7 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
 {
 	uint16_t len;
 	uint16_t attr_len;
-	char valstr[20];
-	void *value;
+	const char *value = NULL;
 
 	DBG("%u", id);
 
@@ -511,15 +510,6 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
 		return 0;
 	}
 
-	switch (id) {
-	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
-	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
-	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
-		snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value));
-		value = valstr;
-		break;
-	}
-
 	attr_len = strlen(value);
 	value = ((char *) value) + *offset;
 	len = attr_len - *offset;
@@ -946,7 +936,6 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
 	uint16_t len = ntohs(pdu->params_len);
 	uint32_t position;
 	uint32_t duration;
-	void *pduration;
 
 	if (len != 0 || player == NULL) {
 		pdu->params_len = htons(1);
@@ -955,14 +944,12 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
 	}
 
 	position = player->cb->get_position(player->user_data);
-	pduration = player->cb->get_metadata(AVRCP_MEDIA_ATTRIBUTE_DURATION,
-							player->user_data);
-	if (pduration != NULL)
-		duration = htonl(GPOINTER_TO_UINT(pduration));
-	else
-		duration = htonl(UINT32_MAX);
+	duration = player->cb->get_duration(player->user_data);
+	if (duration == 0)
+		duration = UINT32_MAX;
 
 	position = htonl(position);
+	duration = htonl(duration);
 
 	memcpy(&pdu->params[0], &duration, 4);
 	memcpy(&pdu->params[4], &position, 4);
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 6c651dd..31fdf8d 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -80,10 +80,11 @@ struct avrcp_player_cb {
 	int (*get_setting) (uint8_t attr, void *user_data);
 	int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
 	uint64_t (*get_uid) (void *user_data);
-	void *(*get_metadata) (uint32_t id, void *user_data);
+	const char *(*get_metadata) (uint32_t id, void *user_data);
 	GList *(*list_metadata) (void *user_data);
 	uint8_t (*get_status) (void *user_data);
 	uint32_t (*get_position) (void *user_data);
+	uint32_t (*get_duration) (void *user_data);
 	void (*set_volume) (uint8_t volume, struct audio_device *dev,
 							void *user_data);
 };
diff --git a/audio/media.c b/audio/media.c
index f2b5b2f..28ed942 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -100,18 +100,11 @@ struct media_player {
 	guint			track_watch;
 	uint8_t			status;
 	uint32_t		position;
+	uint32_t		duration;
 	uint8_t			volume;
 	GTimer			*timer;
 };
 
-struct metadata_value {
-	int			type;
-	union {
-		char		*str;
-		uint32_t	num;
-	} value;
-};
-
 static GSList *adapters = NULL;
 
 static void endpoint_request_free(struct endpoint_request *request)
@@ -1281,28 +1274,16 @@ static uint64_t get_uid(void *user_data)
 	return 0;
 }
 
-static void *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(uint32_t id, void *user_data)
 {
 	struct media_player *mp = user_data;
-	struct metadata_value *value;
 
 	DBG("%s", metadata_to_str(id));
 
 	if (mp->track == NULL)
 		return NULL;
 
-	value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
-	if (!value)
-		return NULL;
-
-	switch (value->type) {
-	case DBUS_TYPE_STRING:
-		return value->value.str;
-	case DBUS_TYPE_UINT32:
-		return GUINT_TO_POINTER(value->value.num);
-	}
-
-	return NULL;
+	return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
 }
 
 static uint8_t get_status(void *user_data)
@@ -1329,6 +1310,13 @@ static uint32_t get_position(void *user_data)
 	return mp->position + sec * 1000 + msec;
 }
 
+static uint32_t get_duration(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	return mp->duration;
+}
+
 static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data)
 {
 	struct media_player *mp = user_data;
@@ -1362,6 +1350,7 @@ static struct avrcp_player_cb player_cb = {
 	.get_uid = get_uid,
 	.get_metadata = get_metadata,
 	.get_position = get_position,
+	.get_duration = get_duration,
 	.get_status = get_status,
 	.set_volume = set_volume
 };
@@ -1407,7 +1396,6 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
 static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
 {
 	uint32_t value;
-	struct metadata_value *duration;
 
 	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
 			return FALSE;
@@ -1424,15 +1412,11 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
 		return TRUE;
 	}
 
-	duration = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(
-					AVRCP_MEDIA_ATTRIBUTE_DURATION));
-
 	/*
 	 * If position is the maximum value allowed or greater than track's
 	 * duration, we send a track-reached-end event.
 	 */
-	if (mp->position == UINT32_MAX ||
-			(duration && mp->position >= duration->value.num))
+	if (mp->position == UINT32_MAX || mp->position >= mp->duration)
 		avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
 									NULL);
 
@@ -1505,19 +1489,6 @@ static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
 	return TRUE;
 }
 
-static void metadata_value_free(gpointer data)
-{
-	struct metadata_value *value = data;
-
-	switch (value->type) {
-	case DBUS_TYPE_STRING:
-		g_free(value->value.str);
-		break;
-	}
-
-	g_free(value);
-}
-
 static gboolean parse_player_metadata(struct media_player *mp,
 							DBusMessageIter *iter)
 {
@@ -1535,14 +1506,18 @@ static gboolean parse_player_metadata(struct media_player *mp,
 	dbus_message_iter_recurse(iter, &dict);
 
 	track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
-							metadata_value_free);
+								g_free);
 
 	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
 							DBUS_TYPE_INVALID) {
 		DBusMessageIter entry;
 		const char *key;
-		struct metadata_value *value;
+		const char *string;
+		char valstr[20];
+		char *value;
+		uint32_t num;
 		int id;
+		int type;
 
 		if (ctype != DBUS_TYPE_DICT_ENTRY)
 			goto parse_error;
@@ -1563,8 +1538,7 @@ static gboolean parse_player_metadata(struct media_player *mp,
 
 		dbus_message_iter_recurse(&entry, &var);
 
-		value = g_new0(struct metadata_value, 1);
-		value->type = dbus_message_iter_get_arg_type(&var);
+		type = dbus_message_iter_get_arg_type(&var);
 
 		switch (id) {
 		case AVRCP_MEDIA_ATTRIBUTE_TITLE:
@@ -1572,36 +1546,39 @@ static gboolean parse_player_metadata(struct media_player *mp,
 		case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
 		case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
 		case AVRCP_MEDIA_ATTRIBUTE_GENRE:
-			if (value->type != DBUS_TYPE_STRING) {
-				g_free(value);
+			if (type != DBUS_TYPE_STRING)
 				goto parse_error;
-			}
 
-			dbus_message_iter_get_basic(&var, &value->value.str);
+			dbus_message_iter_get_basic(&var, &string);
 			break;
 		case AVRCP_MEDIA_ATTRIBUTE_TRACK:
 		case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+			if (type != DBUS_TYPE_UINT32)
+				goto parse_error;
+
+			dbus_message_iter_get_basic(&var, &num);
+			break;
 		case AVRCP_MEDIA_ATTRIBUTE_DURATION:
-			if (value->type != DBUS_TYPE_UINT32) {
-				g_free(value);
+			if (type != DBUS_TYPE_UINT32)
 				goto parse_error;
-			}
 
-			dbus_message_iter_get_basic(&var, &value->value.num);
+			dbus_message_iter_get_basic(&var, &num);
+			mp->duration = num;
 			break;
 		default:
 			goto parse_error;
 		}
 
-		switch (value->type) {
+		switch (dbus_message_iter_get_arg_type(&var)) {
 		case DBUS_TYPE_STRING:
-			value->value.str = g_strdup(value->value.str);
-			DBG("%s=%s", key, value->value.str);
+			value = g_strdup(string);
 			break;
 		default:
-			DBG("%s=%u", key, value->value.num);
+			snprintf(valstr, 20, "%u", num);
+			value = g_strdup(valstr);
 		}
 
+		DBG("%s=%s", key, value);
 		g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
 		dbus_message_iter_next(&dict);
 	}
@@ -1610,12 +1587,9 @@ static gboolean parse_player_metadata(struct media_player *mp,
 		g_hash_table_unref(track);
 		track = NULL;
 	} else if (title == FALSE) {
-		struct metadata_value *value = g_new(struct metadata_value, 1);
 		uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
 
-		value->type = DBUS_TYPE_STRING;
-		value->value.str = g_strdup("");
-		g_hash_table_insert(track, GUINT_TO_POINTER(id), value);
+		g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
 	}
 
 	if (mp->track != NULL)
-- 
1.7.11.7


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

* [PATCH BlueZ 03/11] AVRCP: Don't respond with errors when no player is registered
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 04/11] AVRCP: Fix not adding session to player's list of sessions Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

Some devices w.g. Sony MW600 will stop using certain commands if an
error happen, so the code now just fake a player and once a real
player is registered it takes place of the fake one.
---
 audio/avrcp.c | 117 +++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 84 insertions(+), 33 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index fe304d1..a14833a 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -493,6 +493,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 	return;
 }
 
+static const char *player_get_metadata(struct avrcp_player *player, uint32_t attr)
+{
+	if (player != NULL)
+		return player->cb->get_metadata(attr, player->user_data);
+
+	if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+		return "";
+
+	return NULL;
+}
+
 static uint16_t player_write_media_attribute(struct avrcp_player *player,
 						uint32_t id, uint8_t *buf,
 						uint16_t *pos,
@@ -504,7 +515,7 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
 
 	DBG("%u", id);
 
-	value = player->cb->get_metadata(id, player->user_data);
+	value = player_get_metadata(player, id);
 	if (value == NULL) {
 		*offset = 0;
 		return 0;
@@ -589,25 +600,21 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
 	return TRUE;
 }
 
-static int player_set_attribute(struct avrcp_player *player,
-						uint8_t attr, uint8_t val)
+static int player_set_setting(struct avrcp_player *player, uint8_t id,
+								uint8_t val)
 {
-	DBG("Change attribute: %u %u", attr, val);
+	if (player == NULL)
+		return -ENOENT;
 
-	return player->cb->set_setting(attr, val, player->user_data);
+	return player->cb->set_setting(id, val, player->user_data);
 }
 
-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
 {
-	int value;
-
-	DBG("attr %u", attr);
-
-	value = player->cb->get_setting(attr, player->user_data);
-	if (value < 0)
-		DBG("attr %u not supported by player", attr);
+	if (player == NULL)
+		return -ENOENT;
 
-	return value;
+	return player->cb->get_setting(id, player->user_data);
 }
 
 static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -659,7 +666,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
 	uint16_t len = ntohs(pdu->params_len);
 	unsigned int i;
 
-	if (len != 0 || player == NULL) {
+	if (len != 0) {
 		pdu->params_len = htons(1);
 		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
 		return AVC_CTYPE_REJECTED;
@@ -669,7 +676,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
 		goto done;
 
 	for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
-		if (player_get_attribute(player, i) < 0)
+		if (player_get_setting(player, i) < 0)
 			continue;
 
 		len++;
@@ -691,10 +698,10 @@ static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
 	uint16_t len = ntohs(pdu->params_len);
 	unsigned int i;
 
-	if (len != 1 || player == NULL)
+	if (len != 1)
 		goto err;
 
-	if (player_get_attribute(player, pdu->params[0]) < 0)
+	if (player_get_setting(player, pdu->params[0]) < 0)
 		goto err;
 
 	len = attr_get_max_val(pdu->params[0]);
@@ -713,6 +720,15 @@ err:
 	return AVC_CTYPE_REJECTED;
 }
 
+static GList *player_list_metadata(struct avrcp_player *player)
+{
+	if (player != NULL)
+		return player->cb->list_metadata(player->user_data);
+
+	return g_list_prepend(NULL,
+				GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE));
+}
+
 static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -725,7 +741,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
 	GList *attr_ids;
 	uint16_t offset;
 
-	if (len < 9 || identifier != 0 || player == NULL)
+	if (len < 9 || identifier != 0)
 		goto err;
 
 	nattr = pdu->params[8];
@@ -738,7 +754,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
 		 * Return all available information, at least
 		 * title must be returned if there's a track selected.
 		 */
-		attr_ids = player->cb->list_metadata(player->user_data);
+		attr_ids = player_list_metadata(player);
 		len = g_list_length(attr_ids);
 	} else {
 		unsigned int i;
@@ -794,8 +810,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
 	uint8_t *settings;
 	unsigned int i;
 
-	if (player == NULL || len <= 1 || pdu->params[0] != len - 1 ||
-							player == NULL)
+	if (len <= 1 || pdu->params[0] != len - 1)
 		goto err;
 
 	/*
@@ -819,7 +834,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
 			continue;
 		}
 
-		val = player_get_attribute(player, settings[i]);
+		val = player_get_setting(player, settings[i]);
 		if (val < 0)
 			continue;
 
@@ -866,7 +881,7 @@ static uint8_t avrcp_handle_set_player_value(struct avrcp *session,
 	 */
 	for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0];
 							i++, param += 2) {
-		if (player_set_attribute(player, param[0], param[1]) < 0)
+		if (player_set_setting(player, param[0], param[1]) < 0)
 			continue;
 
 		len++;
@@ -928,6 +943,36 @@ err:
 	return AVC_CTYPE_REJECTED;
 }
 
+static uint32_t player_get_position(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0;
+
+	return player->cb->get_position(player->user_data);
+}
+
+static uint32_t player_get_duration(struct avrcp_player *player)
+{
+	uint32_t num;
+
+	if (player == NULL)
+		return UINT32_MAX;
+
+	num = player->cb->get_duration(player->user_data);
+	if (num == 0)
+		return UINT32_MAX;
+
+	return num;
+}
+
+static uint8_t player_get_status(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return AVRCP_PLAY_STATUS_STOPPED;
+
+	return player->cb->get_status(player->user_data);
+}
+
 static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -937,29 +982,35 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
 	uint32_t position;
 	uint32_t duration;
 
-	if (len != 0 || player == NULL) {
+	if (len != 0) {
 		pdu->params_len = htons(1);
 		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
 		return AVC_CTYPE_REJECTED;
 	}
 
-	position = player->cb->get_position(player->user_data);
-	duration = player->cb->get_duration(player->user_data);
-	if (duration == 0)
-		duration = UINT32_MAX;
+	position = player_get_position(player);
+	duration = player_get_duration(player);
 
 	position = htonl(position);
 	duration = htonl(duration);
 
 	memcpy(&pdu->params[0], &duration, 4);
 	memcpy(&pdu->params[4], &position, 4);
-	pdu->params[8] = player->cb->get_status(player->user_data);;
+	pdu->params[8] = player_get_status(player);;
 
 	pdu->params_len = htons(9);
 
 	return AVC_CTYPE_STABLE;
 }
 
+static uint64_t player_get_uid(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return UINT64_MAX;
+
+	return player->cb->get_uid(player->user_data);
+}
+
 static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -973,18 +1024,18 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
 	 * 1.3 spec, section 5.4.2.
 	 */
-	if (len != 5 || player == NULL)
+	if (len != 5)
 		goto err;
 
 	switch (pdu->params[0]) {
 	case AVRCP_EVENT_STATUS_CHANGED:
 		len = 2;
-		pdu->params[1] = player->cb->get_status(player->user_data);
+		pdu->params[1] = player_get_status(player);
 
 		break;
 	case AVRCP_EVENT_TRACK_CHANGED:
 		len = 9;
-		uid = player->cb->get_uid(player->user_data);
+		uid = player_get_uid(player);
 		memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
 
 		break;
-- 
1.7.11.7


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

* [PATCH BlueZ 04/11] AVRCP: Fix not adding session to player's list of sessions
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 03/11] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 05/11] AVCTP: Reduce verbosity of PDU parsing Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

If the session is not added in the list if the player gets
unregistered it will not be cleared from the session which might cause a
crash if the tries to access it.
---
 audio/avrcp.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index a14833a..88ba3aa 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -1442,10 +1442,16 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
 static void session_tg_init(struct avrcp *session)
 {
 	struct avrcp_server *server = session->server;
+	struct avrcp_player *player;
 
 	DBG("%p version 0x%04x", session, session->version);
 
-	session->player = g_slist_nth_data(server->players, 0);
+	player = g_slist_nth_data(server->players, 0);
+	if (player != NULL) {
+		session->player = player;
+		player->sessions = g_slist_prepend(player->sessions, session);
+	}
+
 	session->control_handlers = tg_control_handlers;
 
 	if (session->version >= 0x0104) {
-- 
1.7.11.7


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

* [PATCH BlueZ 05/11] AVCTP: Reduce verbosity of PDU parsing
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 04/11] AVRCP: Fix not adding session to player's list of sessions Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 06/11] AVRCP: Add support for settings changed event Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

This information is not really useful as debug and there are better
tools for sniffing traffic such as hcidump and wireshark.
---
 audio/avctp.c | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index 0db6031..5f6a677 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -471,8 +471,6 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
 	struct iovec iov[2];
 	int sk, err = 0;
 
-	DBG("transaction %u", transaction);
-
 	iov[0].iov_base = control->buffer;
 	iov[0].iov_len  = sizeof(*avctp) + sizeof(*avc);
 	iov[1].iov_base = operands;
@@ -637,10 +635,6 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 		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));
 
 	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
 		goto failed;
@@ -698,8 +692,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 	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;
@@ -707,11 +699,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 
 	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 AVCTP packet");
@@ -725,12 +712,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 	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) {
 		control_response(control, avctp, avc, operands, operand_count);
 		return TRUE;
@@ -1295,8 +1276,6 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data)
 {
-	DBG("code %u", code);
-
 	if (code != AVC_CTYPE_ACCEPTED)
 		return FALSE;
 
@@ -1315,8 +1294,6 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
 {
 	uint8_t operands[2];
 
-	DBG("");
-
 	/* Button pressed */
 	operands[0] = op & 0x7f;
 	operands[1] = 0;
-- 
1.7.11.7


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

* [PATCH BlueZ 06/11] AVRCP: Add support for settings changed event
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 05/11] AVCTP: Reduce verbosity of PDU parsing Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

This event can be registered by the CT to get notified when any setting
is changed.

Per spec every single setting must be sent so the CT can track settings
being added or removed.
---
 audio/avrcp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 audio/avrcp.h |  2 ++
 audio/media.c | 26 ++++++++++++++++++++++++-
 3 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 88ba3aa..02e4581 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -431,12 +431,29 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
 	cid[2] = cid_in;
 }
 
+static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+{
+	int value;
+
+	DBG("attr %u", attr);
+
+	if (player == NULL)
+		return -ENOENT;
+
+	value = player->cb->get_setting(attr, player->user_data);
+	if (value < 0)
+		DBG("attr %u not supported by player", attr);
+
+	return value;
+}
+
 void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 {
 	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
 	struct avrcp_header *pdu = (void *) buf;
 	uint16_t size;
 	GSList *l;
+	GList *settings;
 
 	if (player->sessions == NULL)
 		return;
@@ -465,6 +482,22 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 	case AVRCP_EVENT_TRACK_REACHED_START:
 		size = 1;
 		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		size = 2;
+		settings = data;
+		pdu->params[1] = g_list_length(settings);
+		for (; settings; settings = settings->next) {
+			uint8_t attr = GPOINTER_TO_UINT(settings->data);
+			int val;
+
+			val = player_get_attribute(player, attr);
+			if (val < 0)
+				continue;
+
+			pdu->params[++size] = attr;
+			pdu->params[++size] = val;
+		}
+		break;
 	default:
 		error("Unknown event %u", id);
 		return;
@@ -641,11 +674,12 @@ static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
 
 		return AVC_CTYPE_STABLE;
 	case CAP_EVENTS_SUPPORTED:
-		pdu->params[1] = 4;
+		pdu->params[1] = 5;
 		pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
 		pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
 		pdu->params[4] = AVRCP_EVENT_TRACK_REACHED_START;
 		pdu->params[5] = AVRCP_EVENT_TRACK_REACHED_END;
+		pdu->params[6] = AVRCP_EVENT_SETTINGS_CHANGED;
 
 		pdu->params_len = htons(2 + pdu->params[1]);
 		return AVC_CTYPE_STABLE;
@@ -1011,6 +1045,14 @@ static uint64_t player_get_uid(struct avrcp_player *player)
 	return player->cb->get_uid(player->user_data);
 }
 
+static GList *player_list_settings(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return NULL;
+
+	return player->cb->list_settings(player->user_data);
+}
+
 static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -1018,6 +1060,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 	struct avrcp_player *player = session->player;
 	uint16_t len = ntohs(pdu->params_len);
 	uint64_t uid;
+	GList *settings;
 
 	/*
 	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
@@ -1043,6 +1086,23 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 	case AVRCP_EVENT_TRACK_REACHED_START:
 		len = 1;
 		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		settings = player_list_settings(player);
+
+		pdu->params[++len] = g_list_length(settings);
+		for (; settings; settings = settings->next) {
+			uint8_t attr = GPOINTER_TO_UINT(settings->data);
+			int val;
+
+			val = player_get_attribute(player, attr);
+			if (val < 0)
+				continue;
+
+			pdu->params[++len] = attr;
+			pdu->params[++len] = val;
+		}
+
+		break;
 	default:
 		/* All other events are not supported yet */
 		goto err;
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 31fdf8d..7f54adb 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -73,10 +73,12 @@
 #define AVRCP_EVENT_TRACK_CHANGED	0x02
 #define AVRCP_EVENT_TRACK_REACHED_END	0x03
 #define AVRCP_EVENT_TRACK_REACHED_START	0x04
+#define AVRCP_EVENT_SETTINGS_CHANGED	0x08
 #define AVRCP_EVENT_VOLUME_CHANGED	0x0d
 #define AVRCP_EVENT_LAST		AVRCP_EVENT_VOLUME_CHANGED
 
 struct avrcp_player_cb {
+	GList *(*list_settings) (void *user_data);
 	int (*get_setting) (uint8_t attr, void *user_data);
 	int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
 	uint64_t (*get_uid) (void *user_data);
diff --git a/audio/media.c b/audio/media.c
index 28ed942..7abf077 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -1194,6 +1194,18 @@ static const char *metadata_to_str(uint32_t id)
 	return NULL;
 }
 
+static GList *list_settings(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (mp->settings == NULL)
+		return NULL;
+
+	return g_hash_table_get_keys(mp->settings);
+}
+
 static int get_setting(uint8_t attr, void *user_data)
 {
 	struct media_player *mp = user_data;
@@ -1344,6 +1356,7 @@ static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data
 }
 
 static struct avrcp_player_cb player_cb = {
+	.list_settings = list_settings,
 	.get_setting = get_setting,
 	.set_setting = set_setting,
 	.list_metadata = list_metadata,
@@ -1427,8 +1440,9 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
 							DBusMessageIter *entry)
 {
 	DBusMessageIter var;
-	const char *value;
+	const char *value, *curval;
 	int attr, val;
+	GList *settings;
 
 	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
 		return FALSE;
@@ -1454,11 +1468,21 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
 	if (val < 0)
 		return FALSE;
 
+	curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+	if (g_strcmp0(curval, value) == 0)
+		return TRUE;
+
 	DBG("%s=%s", key, value);
 
 	g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
 						GUINT_TO_POINTER(val));
 
+	settings = list_settings(mp);
+
+	avrcp_player_event(mp->player, AVRCP_EVENT_SETTINGS_CHANGED, settings);
+
+	g_list_free(settings);
+
 	return TRUE;
 }
 
-- 
1.7.11.7


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

* [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 06/11] AVRCP: Add support for settings changed event Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-26  7:44   ` Johan Hedberg
  2012-10-25 13:59 ` [PATCH BlueZ 08/11] AVRCP: Remove conversions inside media.c Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

This also bump controller record to 1.3.
---
 Makefile.am    |   1 +
 audio/avrcp.c  | 686 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 audio/player.c | 404 +++++++++++++++++++++++++++++++++
 audio/player.h |  46 ++++
 4 files changed, 1047 insertions(+), 90 deletions(-)
 create mode 100644 audio/player.c
 create mode 100644 audio/player.h

diff --git a/Makefile.am b/Makefile.am
index 35b1520..6ac6a73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,6 +148,7 @@ builtin_sources += audio/main.c \
 			audio/avdtp.h audio/avdtp.c \
 			audio/media.h audio/media.c \
 			audio/transport.h audio/transport.c \
+			audio/player.h audio/player.c \
 			audio/telephony.h audio/a2dp-codecs.h
 builtin_nodist += audio/telephony.c
 
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 02e4581..35fc249 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -61,6 +61,9 @@
 #include "control.h"
 #include "avdtp.h"
 #include "sink.h"
+#include "player.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
 
 /* Company IDs for vendor dependent commands */
 #define IEEEID_BTSIG		0x001958
@@ -190,6 +193,7 @@ struct avrcp {
 	int features;
 
 	void (*init) (struct avrcp *session);
+	void (*destroy) (struct avrcp *sesion);
 
 	const struct control_pdu_handler *control_handlers;
 
@@ -216,7 +220,7 @@ static uint32_t company_ids[] = {
 	IEEEID_BTSIG,
 };
 
-static void register_notification(struct avrcp *session, uint8_t event);
+static void avrcp_register_notification(struct avrcp *session, uint8_t event);
 
 static sdp_record_t *avrcp_ct_record(void)
 {
@@ -227,7 +231,7 @@ static sdp_record_t *avrcp_ct_record(void)
 	sdp_record_t *record;
 	sdp_data_t *psm, *version, *features;
 	uint16_t lp = AVCTP_CONTROL_PSM;
-	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+	uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103;
 	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
 						AVRCP_FEATURE_CATEGORY_2 |
 						AVRCP_FEATURE_CATEGORY_3 |
@@ -431,20 +435,12 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
 	cid[2] = cid_in;
 }
 
-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
 {
-	int value;
-
-	DBG("attr %u", attr);
-
 	if (player == NULL)
 		return -ENOENT;
 
-	value = player->cb->get_setting(attr, player->user_data);
-	if (value < 0)
-		DBG("attr %u not supported by player", attr);
-
-	return value;
+	return player->cb->get_setting(id, player->user_data);
 }
 
 void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -490,7 +486,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 			uint8_t attr = GPOINTER_TO_UINT(settings->data);
 			int val;
 
-			val = player_get_attribute(player, attr);
+			val = player_get_setting(player, attr);
 			if (val < 0)
 				continue;
 
@@ -642,14 +638,6 @@ static int player_set_setting(struct avrcp_player *player, uint8_t id,
 	return player->cb->set_setting(id, val, player->user_data);
 }
 
-static int player_get_setting(struct avrcp_player *player, uint8_t id)
-{
-	if (player == NULL)
-		return -ENOENT;
-
-	return player->cb->get_setting(id, player->user_data);
-}
-
 static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -1094,7 +1082,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 			uint8_t attr = GPOINTER_TO_UINT(settings->data);
 			int val;
 
-			val = player_get_attribute(player, attr);
+			val = player_get_setting(player, attr);
 			if (val < 0)
 				continue;
 
@@ -1356,6 +1344,330 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
 	return NULL;
 }
 
+static const char *status_to_string(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_PLAY_STATUS_STOPPED:
+		return "stopped";
+	case AVRCP_PLAY_STATUS_PLAYING:
+		return "playing";
+	case AVRCP_PLAY_STATUS_PAUSED:
+		return "paused";
+	case AVRCP_PLAY_STATUS_FWD_SEEK:
+		return "forward-seek";
+	case AVRCP_PLAY_STATUS_REV_SEEK:
+		return "reverse-seek";
+	case AVRCP_PLAY_STATUS_ERROR:
+		return "error";
+	default:
+		return NULL;
+	}
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint32_t duration;
+	uint32_t position;
+	uint8_t status;
+
+	if (code == AVC_CTYPE_REJECTED || pdu->params_len != 9)
+		return FALSE;
+
+	memcpy(&duration, pdu->params, sizeof(uint32_t));
+	duration = ntohl(duration);
+
+	memcpy(&position, pdu->params + 4, sizeof(uint32_t));
+	position = ntohl(position);
+	media_player_set_position(mp, position);
+
+	memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+	media_player_set_status(mp, status_to_string(status));
+
+	return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_get_play_status_rsp,
+					session);
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		switch (value) {
+		case AVRCP_EQUALIZER_ON:
+			return "on";
+		case AVRCP_EQUALIZER_OFF:
+			return "off";
+		}
+
+		break;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		switch (value) {
+		case AVRCP_REPEAT_MODE_OFF:
+			return "off";
+		case AVRCP_REPEAT_MODE_SINGLE:
+			return "singletrack";
+		case AVRCP_REPEAT_MODE_ALL:
+			return "alltracks";
+		case AVRCP_REPEAT_MODE_GROUP:
+			return "group";
+		}
+
+		break;
+	/* Shuffle and scan have the same values */
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+	case AVRCP_ATTRIBUTE_SCAN:
+		switch (value) {
+		case AVRCP_SCAN_OFF:
+			return "off";
+		case AVRCP_SCAN_ALL:
+			return "alltracks";
+		case AVRCP_SCAN_GROUP:
+			return "group";
+		}
+
+		break;
+	}
+
+	return NULL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return "Equalizer";
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return "Repeat";
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return "Shuffle";
+	case AVRCP_ATTRIBUTE_SCAN:
+		return "Scan";
+	}
+
+	return NULL;
+}
+
+static gboolean avrcp_player_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t count;
+	int i;
+
+	if (code == AVC_CTYPE_REJECTED)
+		return FALSE;
+
+	count = pdu->params[0];
+
+	if (pdu->params_len < count * 2)
+		return FALSE;
+
+	for (i = 1; count > 0; count--, i += 2) {
+		const char *key;
+		const char *value;
+
+		key = attr_to_str(pdu->params[i]);
+		if (key == NULL)
+			continue;
+
+		value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+		if (value == NULL)
+			continue;
+
+		media_player_set_setting(mp, key, value);
+	}
+
+	return FALSE;
+}
+
+static void avrcp_get_current_player_value(struct avrcp *session,
+						uint8_t *attrs, uint8_t count)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 5];
+	struct avrcp_header *pdu = (void *) buf;
+	int i;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params_len = htons(count + 1);
+	pdu->params[0] = count;
+
+	for (i = 0; count > 0; count--, i++)
+		pdu->params[i + 1] = attrs[i];
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_player_value_rsp, session);
+}
+
+static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t count;
+
+	if (code == AVC_CTYPE_REJECTED)
+		return FALSE;
+
+	count = pdu->params[0];
+
+	if (ntohs(pdu->params_len) < count) {
+		error("Invalid parameters");
+		return FALSE;
+	}
+
+	avrcp_get_current_player_value(session, &pdu->params[1],
+							pdu->params[0]);
+
+	return FALSE;
+}
+
+static void avrcp_list_player_attributes(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_list_player_attributes_rsp,
+					session);
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+	switch (id) {
+	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		return "Title";
+	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+		return "Artist";
+	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+		return "Album";
+	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+		return "Genre";
+	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+		return "Track";
+	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+		return "NumberOfTracks";
+	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+		return "Duration";
+	}
+
+	return NULL;
+}
+
+static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t count;
+	int i;
+
+	if (code == AVC_CTYPE_REJECTED)
+		return FALSE;
+
+	count = pdu->params[0];
+
+	if (ntohs(pdu->params_len) - 1 < count * 8) {
+		error("Invalid parameters");
+		return FALSE;
+	}
+
+	for (i = 1; count > 0; count--) {
+		uint32_t id;
+		uint16_t charset, len;
+
+		memcpy(&id, &pdu->params[i], sizeof(uint32_t));
+		id = ntohl(id);
+		i += sizeof(uint32_t);
+
+		memcpy(&charset, &pdu->params[i], sizeof(uint16_t));
+		charset = ntohs(charset);
+		i += sizeof(uint16_t);
+
+		memcpy(&len, &pdu->params[i], sizeof(uint16_t));
+		len = ntohs(len);
+		i += sizeof(uint16_t);
+
+		if (charset == 106) {
+			const char *key = metadata_to_str(id);
+
+			if (key != NULL)
+				media_player_set_metadata(mp,
+							metadata_to_str(id),
+							&pdu->params[i], len);
+		}
+
+		i += len;
+	}
+
+	return FALSE;
+}
+
+static void avrcp_get_element_attributes(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+	pdu->params_len = htons(9);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_get_attributes_rsp,
+					session);
+}
+
 static gboolean avrcp_handle_event(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
@@ -1364,8 +1676,12 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 	struct avrcp *session = user_data;
 	struct avrcp_player *player = session->player;
 	struct avrcp_header *pdu = (void *) operands;
+	struct media_player *mp;
 	uint8_t event;
-	uint8_t volume;
+	uint8_t value;
+	uint8_t count;
+	const char *curval, *strval;
+	int i;
 
 	if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
 		return FALSE;
@@ -1374,24 +1690,70 @@ static gboolean avrcp_handle_event(struct avctp *conn,
 
 	switch (event) {
 	case AVRCP_EVENT_VOLUME_CHANGED:
-		volume = pdu->params[1] & 0x7F;
+		value = pdu->params[1] & 0x7F;
 
 		if (player)
-			player->cb->set_volume(volume, session->dev,
+			player->cb->set_volume(value, session->dev,
 							player->user_data);
 
 		break;
+	case AVRCP_EVENT_STATUS_CHANGED:
+		mp = player->user_data;
+		value = pdu->params[1];
+
+		curval = media_player_get_status(mp);
+		strval = status_to_string(value);
+
+		if (g_strcmp0(curval, strval) != 0) {
+			media_player_set_status(mp, strval);
+			avrcp_get_play_status(session);
+		}
+
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		mp = player->user_data;
+		if (code == AVC_CTYPE_CHANGED)
+			media_player_set_position(mp, 0);
+
+		avrcp_get_element_attributes(session);
+
+		break;
+
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		mp = player->user_data;
+		count = pdu->params[1];
+
+		for (i = 2; count > 0; count--, i += 2) {
+			const char *key;
+			const char *value;
+
+			key = attr_to_str(pdu->params[i]);
+			if (key == NULL)
+				continue;
+
+			value = attrval_to_str(pdu->params[i],
+						pdu->params[i + 1]);
+			if (value == NULL)
+				continue;
+
+			media_player_set_setting(mp, key, value);
+		}
+
+		break;
 	}
 
 	if (code == AVC_CTYPE_CHANGED) {
-		register_notification(session, event);
+		session->registered_events ^= (1 << event);
+		avrcp_register_notification(session, event);
 		return FALSE;
 	}
 
+	session->registered_events |= (1 << event);
+
 	return TRUE;
 }
 
-static void register_notification(struct avrcp *session, uint8_t event)
+static void avrcp_register_notification(struct avrcp *session, uint8_t event)
 {
 	uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
 	struct avrcp_header *pdu = (void *) buf;
@@ -1412,14 +1774,139 @@ static void register_notification(struct avrcp *session, uint8_t event)
 					avrcp_handle_event, session);
 }
 
+static int attr_to_val(const char *str)
+{
+	if (!strcasecmp(str, "Equalizer"))
+		return AVRCP_ATTRIBUTE_EQUALIZER;
+	else if (!strcasecmp(str, "Repeat"))
+		return AVRCP_ATTRIBUTE_REPEAT_MODE;
+	else if (!strcasecmp(str, "Shuffle"))
+		return AVRCP_ATTRIBUTE_SHUFFLE;
+	else if (!strcasecmp(str, "Scan"))
+		return AVRCP_ATTRIBUTE_SCAN;
+
+	return -EINVAL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+	int ret;
+
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_EQUALIZER_OFF;
+		else if (!strcmp(value, "on"))
+			ret = AVRCP_EQUALIZER_ON;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_REPEAT_MODE_OFF;
+		else if (!strcmp(value, "singletrack"))
+			ret = AVRCP_REPEAT_MODE_SINGLE;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_REPEAT_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_REPEAT_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SHUFFLE_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SHUFFLE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SHUFFLE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SCAN:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SCAN_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SCAN_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SCAN_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
+								uint8_t val)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 3];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params[0] = 1;
+	pdu->params[1] = attr;
+	pdu->params[2] = val;
+	pdu->params_len = htons(3);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_player_value_rsp, session);
+}
+
+static bool ct_set_setting(struct media_player *mp, const char *key,
+					const char *value, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	int attr = attr_to_val(key);
+	int val = attrval_to_val(attr, value);
+	struct avrcp *session;
+
+	session = player->sessions->data;
+	if (session == NULL)
+		return false;
+
+	attr = attr_to_val(key);
+	if (attr < 0)
+		return false;
+
+	val = attrval_to_val(attr, value);
+	if (val < 0)
+		return false;
+
+	avrcp_set_player_value(session, attr, val);
+
+	return true;
+}
+
+static const struct media_player_callback ct_cbs = {
+	.set_setting = ct_set_setting,
+};
+
 static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
 	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct media_player *mp;
 	struct avrcp_header *pdu = (void *) operands;
+	uint16_t events = 0;
 	uint8_t count;
+	const char *path;
 
 	if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
 		return FALSE;
@@ -1429,14 +1916,29 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
 	for (; count > 0; count--) {
 		uint8_t event = pdu->params[1 + count];
 
+		events |= (1 << event);
+
 		switch (event) {
 		case AVRCP_EVENT_STATUS_CHANGED:
 		case AVRCP_EVENT_TRACK_CHANGED:
-			register_notification(session, event);
+		case AVRCP_EVENT_SETTINGS_CHANGED:
+			avrcp_register_notification(session, event);
 			break;
 		}
 	}
 
+	path = device_get_path(session->dev->btd_dev);
+	mp = media_player_controller_create(path);
+	media_player_set_callbacks(mp, &ct_cbs, player);
+	player->user_data = mp;
+	player->destroy = (GDestroyNotify) media_player_destroy;
+
+	if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+		avrcp_list_player_attributes(session);
+
+	if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+		avrcp_get_play_status(session);
+
 	return FALSE;
 }
 
@@ -1462,31 +1964,6 @@ static void avrcp_get_capabilities(struct avrcp *session)
 					session);
 }
 
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
-					uint8_t *operands, size_t operand_count,
-					void *user_data)
-{
-	return FALSE;
-}
-
-static void avrcp_get_play_status(struct avrcp *session)
-{
-	uint8_t buf[AVRCP_HEADER_LENGTH];
-	struct avrcp_header *pdu = (void *) buf;
-
-	memset(buf, 0, sizeof(buf));
-
-	set_company_id(pdu->company_id, IEEEID_BTSIG);
-	pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
-	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
-	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
-					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
-					avrcp_get_play_status_rsp,
-					session);
-}
-
 static struct avrcp *find_session(GSList *list, struct audio_device *dev)
 {
 	for (; list; list = list->next) {
@@ -1515,7 +1992,8 @@ static void session_tg_init(struct avrcp *session)
 	session->control_handlers = tg_control_handlers;
 
 	if (session->version >= 0x0104) {
-		register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
+		avrcp_register_notification(session,
+						AVRCP_EVENT_VOLUME_CHANGED);
 		if (session->features & AVRCP_FEATURE_BROWSING)
 			avctp_connect_browsing(session->conn);
 	}
@@ -1532,19 +2010,75 @@ static void session_tg_init(struct avrcp *session)
 
 static void session_ct_init(struct avrcp *session)
 {
+	struct avrcp_player *player;
+
 	session->control_handlers = ct_control_handlers;
 
 	DBG("%p version 0x%04x", session, session->version);
 
-	if (session->version >= 0x0103) {
-		avrcp_get_capabilities(session);
-		avrcp_get_play_status(session);
-	}
-
 	session->control_id = avctp_register_pdu_handler(session->conn,
 							AVC_OP_VENDORDEP,
 							handle_vendordep_pdu,
 							session);
+
+	if (session->version < 0x0103)
+		return;
+
+	player = g_new0(struct avrcp_player, 1);
+	player->sessions = g_slist_prepend(player->sessions, session);
+	session->player = player;
+
+	avrcp_get_capabilities(session);
+}
+
+static void session_destroy(struct avrcp *session)
+{
+	struct avrcp_server *server = session->server;
+
+	server->sessions = g_slist_remove(server->sessions, session);
+
+	if (session->control_id > 0)
+		avctp_unregister_pdu_handler(session->control_id);
+
+	if (session->browsing_id > 0)
+		avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+	g_free(session);
+}
+
+static void session_tg_destroy(struct avrcp *session)
+{
+	struct avrcp_player *player = session->player;
+
+	DBG("%p", session);
+
+	if (player != NULL)
+		player->sessions = g_slist_remove(player->sessions, session);
+
+	session_destroy(session);
+}
+
+static void player_destroy(gpointer data)
+{
+	struct avrcp_player *player = data;
+
+	if (player->destroy)
+		player->destroy(player->user_data);
+
+	g_slist_free(player->sessions);
+	g_free(player);
+}
+
+static void session_ct_destroy(struct avrcp *session)
+{
+	struct avrcp_player *player = session->player;
+
+	DBG("%p", session);
+
+	if (player != NULL)
+		player_destroy(player);
+
+	session_destroy(session);
 }
 
 static struct avrcp *session_create(struct avrcp_server *server,
@@ -1573,9 +2107,11 @@ static struct avrcp *session_create(struct avrcp_server *server,
 
 	if (session->target) {
 		session->init = session_tg_init;
+		session->destroy = session_tg_destroy;
 		rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
 	} else {
 		session->init = session_ct_init;
+		session->destroy = session_ct_destroy;
 		rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
 	}
 
@@ -1594,25 +2130,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
 	return session;
 }
 
-static void session_destroy(struct avrcp *session)
-{
-	struct avrcp_server *server = session->server;
-	struct avrcp_player *player = session->player;
-
-	server->sessions = g_slist_remove(server->sessions, session);
-
-	if (session->control_id > 0)
-		avctp_unregister_pdu_handler(session->control_id);
-
-	if (session->browsing_id > 0)
-		avctp_unregister_browsing_pdu_handler(session->browsing_id);
-
-	if (player != NULL)
-		player->sessions = g_slist_remove(player->sessions, session);
-
-	g_free(session);
-}
-
 static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 				avctp_state_t new_state, void *user_data)
 {
@@ -1630,7 +2147,7 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 		if (session == NULL)
 			break;
 
-		session_destroy(session);
+		session->destroy(session);
 
 		break;
 	case AVCTP_STATE_CONNECTING:
@@ -1739,17 +2256,6 @@ int avrcp_register(const bdaddr_t *src, GKeyFile *config)
 	return 0;
 }
 
-static void player_destroy(gpointer data)
-{
-	struct avrcp_player *player = data;
-
-	if (player->destroy)
-		player->destroy(player->user_data);
-
-	g_slist_free(player->sessions);
-	g_free(player);
-}
-
 void avrcp_unregister(const bdaddr_t *src)
 {
 	struct avrcp_server *server;
diff --git a/audio/player.c b/audio/player.c
new file mode 100644
index 0000000..d6e499c
--- /dev/null
+++ b/audio/player.c
@@ -0,0 +1,404 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012-2012  Intel Corporation
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "player.h"
+#include "dbus-common.h"
+#include "error.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+struct player_callback {
+	const struct media_player_callback *cbs;
+	void *user_data;
+};
+
+struct media_player {
+	char			*path;		/* Player object path */
+	GHashTable		*settings;	/* Player settings */
+	GHashTable		*track;		/* Player current track */
+	char			*status;
+	uint32_t		position;
+	GTimer			*progress;
+	guint			process_id;
+	struct player_callback	*cb;
+	GSList			*pending;
+};
+
+static void append_settings(void *key, void *value, void *user_data)
+{
+	DBusMessageIter *dict = user_data;
+
+	dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static void append_metadata(void *key, void *value, void *user_data)
+{
+	DBusMessageIter *dict = user_data;
+
+	if (strcasecmp((char *) key, "Duration") == 0 ||
+			strcasecmp((char *) key, "Track") == 0 ||
+			strcasecmp((char *) key, "NumberOfTracks") == 0)  {
+		uint32_t num = atoi(value);
+		dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+		return;
+	}
+
+	dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static DBusMessage *media_player_get_properties(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	DBusMessage *reply;
+	DBusMessageIter iter, dict;
+	uint32_t position;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	position = media_player_get_position(mp);
+	dict_append_entry(&dict, "Position", DBUS_TYPE_UINT32, &position);
+
+	dict_append_entry(&dict, "Status", DBUS_TYPE_STRING, &mp->status);
+
+	g_hash_table_foreach(mp->settings, append_settings, &dict);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *media_player_get_track(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	DBusMessage *reply;
+	DBusMessageIter iter, dict;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *media_player_set_property(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *key, *value, *curval;
+
+	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, &key);
+	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);
+
+	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&var, &value);
+
+	if (g_strcmp0(key, "Equalizer") != 0 &&
+				g_strcmp0(key, "Repeat") != 0 &&
+				g_strcmp0(key, "Shuffle") != 0 &&
+				g_strcmp0(key, "Scan") != 0)
+		return btd_error_invalid_args(msg);
+
+	if (cb == NULL || cb->cbs->set_setting == NULL)
+		return btd_error_not_supported(msg);
+
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (g_strcmp0(curval, value) == 0)
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
+		return btd_error_invalid_args(msg);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+	{ GDBUS_METHOD("GetProperties",
+			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+			media_player_get_properties) },
+	{ GDBUS_METHOD("GetTrack",
+			NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
+			media_player_get_track) },
+	{ GDBUS_METHOD("SetProperty",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+			NULL, media_player_set_property) },
+	{ }
+};
+
+static const GDBusSignalTable media_player_signals[] = {
+	{ GDBUS_SIGNAL("PropertyChanged",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+	{ GDBUS_SIGNAL("TrackChanged",
+			GDBUS_ARGS({ "metadata", "a{sv}" })) },
+	{ }
+};
+
+void media_player_destroy(struct media_player *mp)
+{
+	DBG("%s", mp->path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
+						MEDIA_PLAYER_INTERFACE);
+
+	if (mp->track)
+		g_hash_table_unref(mp->track);
+
+	if (mp->settings)
+		g_hash_table_unref(mp->settings);
+
+	if (mp->process_id > 0)
+		g_source_remove(mp->process_id);
+
+	g_timer_destroy(mp->progress);
+	g_free(mp->cb);
+	g_free(mp->status);
+	g_free(mp->path);
+	g_free(mp);
+}
+
+struct media_player *media_player_controller_create(const char *path)
+{
+	struct media_player *mp;
+
+	mp = g_new0(struct media_player, 1);
+	mp->path = g_strdup_printf("%s/player1", path);
+	mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+							g_free, g_free);
+	mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
+							g_free, g_free);
+	mp->progress = g_timer_new();
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					media_player_methods,
+					media_player_signals,
+					NULL, mp, NULL)) {
+		error("D-Bus failed to register %s path", mp->path);
+		media_player_destroy(mp);
+		return NULL;
+	}
+
+	DBG("%s", mp->path);
+
+	return mp;
+}
+
+uint32_t media_player_get_position(struct media_player *mp)
+{
+	double timedelta;
+	uint32_t sec, msec;
+
+	if (g_strcmp0(mp->status, "playing") != 0)
+		return mp->position;
+
+	timedelta = g_timer_elapsed(mp->progress, NULL);
+
+	sec = (uint32_t) timedelta;
+	msec = (uint32_t) ((timedelta - sec) * 1000);
+
+	return mp->position + sec * 1000 + msec;
+}
+
+void media_player_set_position(struct media_player *mp, uint32_t position)
+{
+	DBG("%u", position);
+
+	if (mp->position == position)
+		return;
+
+	mp->position = position;
+	g_timer_start(mp->progress);
+
+	emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Position",
+					DBUS_TYPE_UINT32, &mp->position);
+}
+
+void media_player_set_setting(struct media_player *mp, const char *key,
+							const char *value)
+{
+	char *curval;
+
+	DBG("%s: %s", key, value);
+
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (g_strcmp0(curval, value) == 0)
+		return;
+
+	g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+
+	emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
+					DBUS_TYPE_STRING, &value);
+}
+
+const char *media_player_get_status(struct media_player *mp)
+{
+	return mp->status;
+}
+
+void media_player_set_status(struct media_player *mp, const char *status)
+{
+	DBG("%s", status);
+
+	if (g_strcmp0(mp->status, status) == 0)
+		return;
+
+	g_free(mp->status);
+	mp->status = g_strdup(status);
+
+	emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Status",
+					DBUS_TYPE_STRING, &status);
+
+	mp->position = media_player_get_position(mp);
+	g_timer_start(mp->progress);
+}
+
+static gboolean process_metadata_changed(void *user_data)
+{
+	struct media_player *mp = user_data;
+	DBusMessage *signal;
+	DBusMessageIter iter, dict;
+
+	mp->process_id = 0;
+
+	signal = dbus_message_new_signal(mp->path, MEDIA_PLAYER_INTERFACE,
+							"TrackChanged");
+	if (signal == NULL) {
+		error("Unable to allocate TrackChanged signal");
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+
+	g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	g_dbus_send_message(btd_get_dbus_connection(), signal);
+
+	return FALSE;
+}
+
+void media_player_set_metadata(struct media_player *mp, const char *key,
+						void *data, size_t len)
+{
+	char *value, *curval;
+
+	value = g_strndup(data, len);
+
+	DBG("%s: %s", key, value);
+
+	curval = g_hash_table_lookup(mp->track, key);
+	if (g_strcmp0(curval, value) == 0) {
+		g_free(value);
+		return;
+	}
+
+	if (mp->process_id == 0) {
+		g_hash_table_remove_all(mp->track);
+		mp->process_id = g_idle_add(process_metadata_changed, mp);
+	}
+
+	g_hash_table_replace(mp->track, g_strdup(key), value);
+}
+
+void media_player_set_callbacks(struct media_player *mp,
+				const struct media_player_callback *cbs,
+				void *user_data)
+{
+	struct player_callback *cb;
+
+	if (mp->cb)
+		g_free(mp->cb);
+
+	cb = g_new0(struct player_callback, 1);
+	cb->cbs = cbs;
+	cb->user_data = user_data;
+
+	mp->cb = cb;
+}
diff --git a/audio/player.h b/audio/player.h
new file mode 100644
index 0000000..4a6a9cc
--- /dev/null
+++ b/audio/player.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012-2012  Intel Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+struct media_player;
+
+struct media_player_callback {
+	bool (*set_setting) (struct media_player *mp, const char *key,
+				const char *value, void *user_data);
+};
+
+struct media_player *media_player_controller_create(const char *path);
+void media_player_destroy(struct media_player *mp);
+uint32_t media_player_get_position(struct media_player *mp);
+void media_player_set_position(struct media_player *mp, uint32_t position);
+void media_player_set_setting(struct media_player *mp, const char *key,
+							const char *value);
+const char *media_player_get_status(struct media_player *mp);
+void media_player_set_status(struct media_player *mp, const char *status);
+void media_player_set_metadata(struct media_player *mp, const char *key,
+						void *data, size_t len);
+
+void media_player_set_callbacks(struct media_player *mp,
+				const struct media_player_callback *cbs,
+				void *user_data);
-- 
1.7.11.7


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

* [PATCH BlueZ 08/11] AVRCP: Remove conversions inside media.c
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 09/11] test: Fix using Number instead of Track in simple-player Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

The conversion should be done in avrcp.c as it is already doing for
controller players.
---
 audio/avrcp.c | 369 +++++++++++++++++++++++++++++++++-------------------------
 audio/avrcp.h |   9 +-
 audio/media.c | 315 ++++++++-----------------------------------------
 3 files changed, 267 insertions(+), 426 deletions(-)

diff --git a/audio/avrcp.c b/audio/avrcp.c
index 35fc249..6ed085b 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -435,12 +435,123 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
 	cid[2] = cid_in;
 }
 
+static const char *attr_to_str(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return "Equalizer";
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return "Repeat";
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return "Shuffle";
+	case AVRCP_ATTRIBUTE_SCAN:
+		return "Scan";
+	}
+
+	return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+	int ret;
+
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_EQUALIZER_OFF;
+		else if (!strcmp(value, "on"))
+			ret = AVRCP_EQUALIZER_ON;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_REPEAT_MODE_OFF;
+		else if (!strcmp(value, "singletrack"))
+			ret = AVRCP_REPEAT_MODE_SINGLE;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_REPEAT_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_REPEAT_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SHUFFLE_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SHUFFLE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SHUFFLE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SCAN:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SCAN_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SCAN_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SCAN_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static int attr_to_val(const char *str)
+{
+	if (!strcasecmp(str, "Equalizer"))
+		return AVRCP_ATTRIBUTE_EQUALIZER;
+	else if (!strcasecmp(str, "Repeat"))
+		return AVRCP_ATTRIBUTE_REPEAT_MODE;
+	else if (!strcasecmp(str, "Shuffle"))
+		return AVRCP_ATTRIBUTE_SHUFFLE;
+	else if (!strcasecmp(str, "Scan"))
+		return AVRCP_ATTRIBUTE_SCAN;
+
+	return -EINVAL;
+}
+
 static int player_get_setting(struct avrcp_player *player, uint8_t id)
 {
+	const char *key;
+	const char *value;
+
 	if (player == NULL)
 		return -ENOENT;
 
-	return player->cb->get_setting(id, player->user_data);
+	key = attr_to_str(id);
+	if (key == NULL)
+		return -EINVAL;
+
+	value = player->cb->get_setting(key, player->user_data);
+
+	return attrval_to_val(id, value);
+}
+
+static int play_status_to_val(const char *status)
+{
+	if (!strcasecmp(status, "stopped"))
+		return AVRCP_PLAY_STATUS_STOPPED;
+	else if (!strcasecmp(status, "playing"))
+		return AVRCP_PLAY_STATUS_PLAYING;
+	else if (!strcasecmp(status, "paused"))
+		return AVRCP_PLAY_STATUS_PAUSED;
+	else if (!strcasecmp(status, "forward-seek"))
+		return AVRCP_PLAY_STATUS_FWD_SEEK;
+	else if (!strcasecmp(status, "reverse-seek"))
+		return AVRCP_PLAY_STATUS_REV_SEEK;
+	else if (!strcasecmp(status, "error"))
+		return AVRCP_PLAY_STATUS_ERROR;
+
+	return -EINVAL;
 }
 
 void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -466,7 +577,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 	switch (id) {
 	case AVRCP_EVENT_STATUS_CHANGED:
 		size = 2;
-		pdu->params[1] = *((uint8_t *)data);
+		pdu->params[1] = play_status_to_val(data);
 
 		break;
 	case AVRCP_EVENT_TRACK_CHANGED:
@@ -483,9 +594,14 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 		settings = data;
 		pdu->params[1] = g_list_length(settings);
 		for (; settings; settings = settings->next) {
-			uint8_t attr = GPOINTER_TO_UINT(settings->data);
+			const char *key = settings->data;
+			int attr;
 			int val;
 
+			attr = attr_to_val(key);
+			if (attr < 0)
+				continue;
+
 			val = player_get_setting(player, attr);
 			if (val < 0)
 				continue;
@@ -522,12 +638,40 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
 	return;
 }
 
-static const char *player_get_metadata(struct avrcp_player *player, uint32_t attr)
+static const char *metadata_to_str(uint32_t id)
+{
+	switch (id) {
+	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		return "Title";
+	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+		return "Artist";
+	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+		return "Album";
+	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+		return "Genre";
+	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+		return "Track";
+	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+		return "NumberOfTracks";
+	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+		return "Duration";
+	}
+
+	return NULL;
+}
+
+static const char *player_get_metadata(struct avrcp_player *player, uint32_t id)
 {
+	const char *key;
+
+	key = metadata_to_str(id);
+	if (key == NULL)
+		return NULL;
+
 	if (player != NULL)
-		return player->cb->get_metadata(attr, player->user_data);
+		return player->cb->get_metadata(key, player->user_data);
 
-	if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+	if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
 		return "";
 
 	return NULL;
@@ -629,13 +773,66 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
 	return TRUE;
 }
 
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		switch (value) {
+		case AVRCP_EQUALIZER_ON:
+			return "on";
+		case AVRCP_EQUALIZER_OFF:
+			return "off";
+		}
+
+		break;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		switch (value) {
+		case AVRCP_REPEAT_MODE_OFF:
+			return "off";
+		case AVRCP_REPEAT_MODE_SINGLE:
+			return "singletrack";
+		case AVRCP_REPEAT_MODE_ALL:
+			return "alltracks";
+		case AVRCP_REPEAT_MODE_GROUP:
+			return "group";
+		}
+
+		break;
+	/* Shuffle and scan have the same values */
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+	case AVRCP_ATTRIBUTE_SCAN:
+		switch (value) {
+		case AVRCP_SCAN_OFF:
+			return "off";
+		case AVRCP_SCAN_ALL:
+			return "alltracks";
+		case AVRCP_SCAN_GROUP:
+			return "group";
+		}
+
+		break;
+	}
+
+	return NULL;
+}
+
 static int player_set_setting(struct avrcp_player *player, uint8_t id,
 								uint8_t val)
 {
+	const char *key, *value;
+
+	key = attr_to_str(id);
+	if (key == NULL)
+		return -EINVAL;
+
+	value = attrval_to_str(id, val);
+	if (value == NULL)
+		return -EINVAL;
+
 	if (player == NULL)
 		return -ENOENT;
 
-	return player->cb->set_setting(id, val, player->user_data);
+	return player->cb->set_setting(key, value, player->user_data);
 }
 
 static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -989,10 +1186,14 @@ static uint32_t player_get_duration(struct avrcp_player *player)
 
 static uint8_t player_get_status(struct avrcp_player *player)
 {
+	const char *value;
+
 	if (player == NULL)
 		return AVRCP_PLAY_STATUS_STOPPED;
 
-	return player->cb->get_status(player->user_data);
+	value = player->cb->get_status(player->user_data);
+
+	return play_status_to_val(value);
 }
 
 static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
@@ -1079,7 +1280,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
 
 		pdu->params[++len] = g_list_length(settings);
 		for (; settings; settings = settings->next) {
-			uint8_t attr = GPOINTER_TO_UINT(settings->data);
+			const char *key = settings->data;
+			uint8_t attr = attr_to_val(key);
 			int val;
 
 			val = player_get_setting(player, attr);
@@ -1410,65 +1612,6 @@ static void avrcp_get_play_status(struct avrcp *session)
 					session);
 }
 
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		switch (value) {
-		case AVRCP_EQUALIZER_ON:
-			return "on";
-		case AVRCP_EQUALIZER_OFF:
-			return "off";
-		}
-
-		break;
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		switch (value) {
-		case AVRCP_REPEAT_MODE_OFF:
-			return "off";
-		case AVRCP_REPEAT_MODE_SINGLE:
-			return "singletrack";
-		case AVRCP_REPEAT_MODE_ALL:
-			return "alltracks";
-		case AVRCP_REPEAT_MODE_GROUP:
-			return "group";
-		}
-
-		break;
-	/* Shuffle and scan have the same values */
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-	case AVRCP_ATTRIBUTE_SCAN:
-		switch (value) {
-		case AVRCP_SCAN_OFF:
-			return "off";
-		case AVRCP_SCAN_ALL:
-			return "alltracks";
-		case AVRCP_SCAN_GROUP:
-			return "group";
-		}
-
-		break;
-	}
-
-	return NULL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		return "Equalizer";
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		return "Repeat";
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-		return "Shuffle";
-	case AVRCP_ATTRIBUTE_SCAN:
-		return "Scan";
-	}
-
-	return NULL;
-}
-
 static gboolean avrcp_player_value_rsp(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
@@ -1572,28 +1715,6 @@ static void avrcp_list_player_attributes(struct avrcp *session)
 					session);
 }
 
-static const char *metadata_to_str(uint32_t id)
-{
-	switch (id) {
-	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
-		return "Title";
-	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
-		return "Artist";
-	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
-		return "Album";
-	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
-		return "Genre";
-	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
-		return "Track";
-	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
-		return "NumberOfTracks";
-	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
-		return "Duration";
-	}
-
-	return NULL;
-}
-
 static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
@@ -1774,74 +1895,6 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
 					avrcp_handle_event, session);
 }
 
-static int attr_to_val(const char *str)
-{
-	if (!strcasecmp(str, "Equalizer"))
-		return AVRCP_ATTRIBUTE_EQUALIZER;
-	else if (!strcasecmp(str, "Repeat"))
-		return AVRCP_ATTRIBUTE_REPEAT_MODE;
-	else if (!strcasecmp(str, "Shuffle"))
-		return AVRCP_ATTRIBUTE_SHUFFLE;
-	else if (!strcasecmp(str, "Scan"))
-		return AVRCP_ATTRIBUTE_SCAN;
-
-	return -EINVAL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
-	int ret;
-
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_EQUALIZER_OFF;
-		else if (!strcmp(value, "on"))
-			ret = AVRCP_EQUALIZER_ON;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_REPEAT_MODE_OFF;
-		else if (!strcmp(value, "singletrack"))
-			ret = AVRCP_REPEAT_MODE_SINGLE;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_REPEAT_MODE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_REPEAT_MODE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_SHUFFLE_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_SHUFFLE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_SHUFFLE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_SCAN:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_SCAN_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_SCAN_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_SCAN_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	}
-
-	return -EINVAL;
-}
-
 static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
 								uint8_t val)
 {
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 7f54adb..e607fb1 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -79,12 +79,13 @@
 
 struct avrcp_player_cb {
 	GList *(*list_settings) (void *user_data);
-	int (*get_setting) (uint8_t attr, void *user_data);
-	int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+	const char *(*get_setting) (const char *key, void *user_data);
+	int (*set_setting) (const char *key, const char *value,
+							void *user_data);
 	uint64_t (*get_uid) (void *user_data);
-	const char *(*get_metadata) (uint32_t id, void *user_data);
+	const char *(*get_metadata) (const char *key, void *user_data);
 	GList *(*list_metadata) (void *user_data);
-	uint8_t (*get_status) (void *user_data);
+	const char *(*get_status) (void *user_data);
 	uint32_t (*get_position) (void *user_data);
 	uint32_t (*get_duration) (void *user_data);
 	void (*set_volume) (uint8_t volume, struct audio_device *dev,
diff --git a/audio/media.c b/audio/media.c
index 7abf077..cec938a 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -98,7 +98,7 @@ struct media_player {
 	guint			watch;
 	guint			property_watch;
 	guint			track_watch;
-	uint8_t			status;
+	char			*status;
 	uint32_t		position;
 	uint32_t		duration;
 	uint8_t			volume;
@@ -980,6 +980,7 @@ static void media_player_free(gpointer data)
 	g_timer_destroy(mp->timer);
 	g_free(mp->sender);
 	g_free(mp->path);
+	g_free(mp->status);
 	g_free(mp);
 }
 
@@ -1007,193 +1008,6 @@ static void media_player_remove(struct media_player *mp)
 	media_player_destroy(mp);
 }
 
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		switch (value) {
-		case AVRCP_EQUALIZER_ON:
-			return "on";
-		case AVRCP_EQUALIZER_OFF:
-			return "off";
-		}
-
-		break;
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		switch (value) {
-		case AVRCP_REPEAT_MODE_OFF:
-			return "off";
-		case AVRCP_REPEAT_MODE_SINGLE:
-			return "singletrack";
-		case AVRCP_REPEAT_MODE_ALL:
-			return "alltracks";
-		case AVRCP_REPEAT_MODE_GROUP:
-			return "group";
-		}
-
-		break;
-	/* Shuffle and scan have the same values */
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-	case AVRCP_ATTRIBUTE_SCAN:
-		switch (value) {
-		case AVRCP_SCAN_OFF:
-			return "off";
-		case AVRCP_SCAN_ALL:
-			return "alltracks";
-		case AVRCP_SCAN_GROUP:
-			return "group";
-		}
-
-		break;
-	}
-
-	return NULL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
-	int ret;
-
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_EQUALIZER_OFF;
-		else if (!strcmp(value, "on"))
-			ret = AVRCP_EQUALIZER_ON;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_REPEAT_MODE_OFF;
-		else if (!strcmp(value, "singletrack"))
-			ret = AVRCP_REPEAT_MODE_SINGLE;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_REPEAT_MODE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_REPEAT_MODE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_SHUFFLE_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_SHUFFLE_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_SHUFFLE_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	case AVRCP_ATTRIBUTE_SCAN:
-		if (!strcmp(value, "off"))
-			ret = AVRCP_SCAN_OFF;
-		else if (!strcmp(value, "alltracks"))
-			ret = AVRCP_SCAN_ALL;
-		else if (!strcmp(value, "group"))
-			ret = AVRCP_SCAN_GROUP;
-		else
-			ret = -EINVAL;
-
-		return ret;
-	}
-
-	return -EINVAL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
-	switch (attr) {
-	case AVRCP_ATTRIBUTE_EQUALIZER:
-		return "Equalizer";
-	case AVRCP_ATTRIBUTE_REPEAT_MODE:
-		return "Repeat";
-	case AVRCP_ATTRIBUTE_SHUFFLE:
-		return "Shuffle";
-	case AVRCP_ATTRIBUTE_SCAN:
-		return "Scan";
-	}
-
-	return NULL;
-}
-
-static int attr_to_val(const char *str)
-{
-	if (!strcasecmp(str, "Equalizer"))
-		return AVRCP_ATTRIBUTE_EQUALIZER;
-	else if (!strcasecmp(str, "Repeat"))
-		return AVRCP_ATTRIBUTE_REPEAT_MODE;
-	else if (!strcasecmp(str, "Shuffle"))
-		return AVRCP_ATTRIBUTE_SHUFFLE;
-	else if (!strcasecmp(str, "Scan"))
-		return AVRCP_ATTRIBUTE_SCAN;
-
-	return -EINVAL;
-}
-
-static int play_status_to_val(const char *status)
-{
-	if (!strcasecmp(status, "stopped"))
-		return AVRCP_PLAY_STATUS_STOPPED;
-	else if (!strcasecmp(status, "playing"))
-		return AVRCP_PLAY_STATUS_PLAYING;
-	else if (!strcasecmp(status, "paused"))
-		return AVRCP_PLAY_STATUS_PAUSED;
-	else if (!strcasecmp(status, "forward-seek"))
-		return AVRCP_PLAY_STATUS_FWD_SEEK;
-	else if (!strcasecmp(status, "reverse-seek"))
-		return AVRCP_PLAY_STATUS_REV_SEEK;
-	else if (!strcasecmp(status, "error"))
-		return AVRCP_PLAY_STATUS_ERROR;
-
-	return -EINVAL;
-}
-
-static int metadata_to_val(const char *str)
-{
-	if (!strcasecmp(str, "Title"))
-		return AVRCP_MEDIA_ATTRIBUTE_TITLE;
-	else if (!strcasecmp(str, "Artist"))
-		return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
-	else if (!strcasecmp(str, "Album"))
-		return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
-	else if (!strcasecmp(str, "Genre"))
-		return AVRCP_MEDIA_ATTRIBUTE_GENRE;
-	else if (!strcasecmp(str, "NumberOfTracks"))
-		return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
-	else if (!strcasecmp(str, "Number"))
-		return AVRCP_MEDIA_ATTRIBUTE_TRACK;
-	else if (!strcasecmp(str, "Duration"))
-		return AVRCP_MEDIA_ATTRIBUTE_DURATION;
-
-	return -EINVAL;
-}
-
-static const char *metadata_to_str(uint32_t id)
-{
-	switch (id) {
-	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
-		return "Title";
-	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
-		return "Artist";
-	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
-		return "Album";
-	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
-		return "Genre";
-	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
-		return "Track";
-	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
-		return "NumberOfTracks";
-	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
-		return "Duration";
-	}
-
-	return NULL;
-}
-
 static GList *list_settings(void *user_data)
 {
 	struct media_player *mp = user_data;
@@ -1206,38 +1020,24 @@ static GList *list_settings(void *user_data)
 	return g_hash_table_get_keys(mp->settings);
 }
 
-static int get_setting(uint8_t attr, void *user_data)
+static const char *get_setting(const char *key, void *user_data)
 {
 	struct media_player *mp = user_data;
-	guint attr_uint = attr;
-	void *value;
-
-	DBG("%s", attr_to_str(attr));
 
-	value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint));
-	if (!value)
-		return -EINVAL;
+	DBG("%s", key);
 
-	return GPOINTER_TO_UINT(value);
+	return g_hash_table_lookup(mp->settings, key);
 }
 
-static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+static int set_setting(const char *key, const char *value, void *user_data)
 {
 	struct media_player *mp = user_data;
-	const char *property, *value;
-	guint attr_uint = attr;
 	DBusMessage *msg;
 	DBusMessageIter iter, var;
 
-	property = attr_to_str(attr);
-	value = attrval_to_str(attr, val);
-
-	DBG("%s = %s", property, value);
+	DBG("%s = %s", key, value);
 
-	if (property == NULL || value == NULL)
-		return -EINVAL;
-
-	if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)))
+	if (!g_hash_table_lookup(mp->settings, key))
 		return -EINVAL;
 
 	msg = dbus_message_new_method_call(mp->sender, mp->path,
@@ -1249,7 +1049,7 @@ static int set_setting(uint8_t attr, uint8_t val, void *user_data)
 	}
 
 	dbus_message_iter_init_append(msg, &iter);
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
 
 	dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
 						DBUS_TYPE_STRING_AS_STRING,
@@ -1286,19 +1086,19 @@ static uint64_t get_uid(void *user_data)
 	return 0;
 }
 
-static const char *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(const char *key, void *user_data)
 {
 	struct media_player *mp = user_data;
 
-	DBG("%s", metadata_to_str(id));
+	DBG("%s", key);
 
 	if (mp->track == NULL)
 		return NULL;
 
-	return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+	return g_hash_table_lookup(mp->track, key);
 }
 
-static uint8_t get_status(void *user_data)
+static const char *get_status(void *user_data)
 {
 	struct media_player *mp = user_data;
 
@@ -1311,7 +1111,7 @@ static uint32_t get_position(void *user_data)
 	double timedelta;
 	uint32_t sec, msec;
 
-	if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+	if (g_strcmp0(mp->status, "playing") != 0)
 		return mp->position;
 
 	timedelta = g_timer_elapsed(mp->timer, NULL);
@@ -1379,7 +1179,6 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
 static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
 {
 	const char *value;
-	int val;
 
 	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
 		return FALSE;
@@ -1387,21 +1186,16 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
 	dbus_message_iter_get_basic(iter, &value);
 	DBG("Status=%s", value);
 
-	val = play_status_to_val(value);
-	if (val < 0) {
-		error("Invalid status");
-		return FALSE;
-	}
-
-	if (mp->status == val)
+	if (g_strcmp0(mp->status, value) == 0)
 		return TRUE;
 
 	mp->position = get_position(mp);
 	g_timer_start(mp->timer);
 
-	mp->status = val;
+	g_free(mp->status);
+	mp->status = g_strdup(value);
 
-	avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+	avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
 
 	return TRUE;
 }
@@ -1441,7 +1235,6 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
 {
 	DBusMessageIter var;
 	const char *value, *curval;
-	int attr, val;
 	GList *settings;
 
 	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
@@ -1455,27 +1248,18 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
 	if (strcasecmp(key, "Position") == 0)
 		return set_position(mp, &var);
 
-	attr = attr_to_val(key);
-	if (attr < 0)
-		return FALSE;
-
 	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
 		return FALSE;
 
 	dbus_message_iter_get_basic(&var, &value);
 
-	val = attrval_to_val(attr, value);
-	if (val < 0)
-		return FALSE;
-
-	curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+	curval = g_hash_table_lookup(mp->settings, key);
 	if (g_strcmp0(curval, value) == 0)
 		return TRUE;
 
 	DBG("%s=%s", key, value);
 
-	g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
-						GUINT_TO_POINTER(val));
+	g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
 
 	settings = list_settings(mp);
 
@@ -1529,7 +1313,7 @@ static gboolean parse_player_metadata(struct media_player *mp,
 
 	dbus_message_iter_recurse(iter, &dict);
 
-	track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+	track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 								g_free);
 
 	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
@@ -1540,7 +1324,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
 		char valstr[20];
 		char *value;
 		uint32_t num;
-		int id;
 		int type;
 
 		if (ctype != DBUS_TYPE_DICT_ENTRY)
@@ -1553,10 +1336,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
 		dbus_message_iter_get_basic(&entry, &key);
 		dbus_message_iter_next(&entry);
 
-		id = metadata_to_val(key);
-		if (id < 0)
-			goto parse_error;
-
 		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
 			goto parse_error;
 
@@ -1564,36 +1343,46 @@ static gboolean parse_player_metadata(struct media_player *mp,
 
 		type = dbus_message_iter_get_arg_type(&var);
 
-		switch (id) {
-		case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		if (strcasecmp(key, "Title") == 0) {
+			if (type != DBUS_TYPE_STRING)
+				goto parse_error;
 			title = TRUE;
-		case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
-		case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
-		case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+			dbus_message_iter_get_basic(&var, &string);
+		} else if (strcasecmp(key, "Artist") == 0) {
 			if (type != DBUS_TYPE_STRING)
 				goto parse_error;
 
 			dbus_message_iter_get_basic(&var, &string);
-			break;
-		case AVRCP_MEDIA_ATTRIBUTE_TRACK:
-		case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+		} else if (strcasecmp(key, "Album") == 0) {
+			if (type != DBUS_TYPE_STRING)
+				goto parse_error;
+
+			dbus_message_iter_get_basic(&var, &string);
+		} else if (strcasecmp(key, "Genre") == 0) {
+			if (type != DBUS_TYPE_STRING)
+				goto parse_error;
+
+			dbus_message_iter_get_basic(&var, &string);
+		} else if (strcasecmp(key, "Duration") == 0) {
 			if (type != DBUS_TYPE_UINT32)
 				goto parse_error;
 
 			dbus_message_iter_get_basic(&var, &num);
-			break;
-		case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+			mp->duration = num;
+		} else if (strcasecmp(key, "Track") == 0) {
 			if (type != DBUS_TYPE_UINT32)
 				goto parse_error;
 
 			dbus_message_iter_get_basic(&var, &num);
-			mp->duration = num;
-			break;
-		default:
+		} else if (strcasecmp(key, "NumberOfTracks") == 0) {
+			if (type != DBUS_TYPE_UINT32)
+				goto parse_error;
+
+			dbus_message_iter_get_basic(&var, &num);
+		} else
 			goto parse_error;
-		}
 
-		switch (dbus_message_iter_get_arg_type(&var)) {
+		switch (type) {
 		case DBUS_TYPE_STRING:
 			value = g_strdup(string);
 			break;
@@ -1603,18 +1392,15 @@ static gboolean parse_player_metadata(struct media_player *mp,
 		}
 
 		DBG("%s=%s", key, value);
-		g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
+		g_hash_table_replace(track, g_strdup(key), value);
 		dbus_message_iter_next(&dict);
 	}
 
 	if (g_hash_table_size(track) == 0) {
 		g_hash_table_unref(track);
 		track = NULL;
-	} else if (title == FALSE) {
-		uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
-
-		g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
-	}
+	} else if (title == FALSE)
+		g_hash_table_insert(track, g_strdup("Title"), g_strdup(""));
 
 	if (mp->track != NULL)
 		g_hash_table_unref(mp->track);
@@ -1692,7 +1478,8 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
 		return NULL;
 	}
 
-	mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+	mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+								g_free);
 
 	adapter->players = g_slist_append(adapter->players, mp);
 
-- 
1.7.11.7


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

* [PATCH BlueZ 09/11] test: Fix using Number instead of Track in simple-player
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 08/11] AVRCP: Remove conversions inside media.c Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 10/11] test: Fix using Number instead of Track in mpris-player Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 11/11] test: Add support for using external player Luiz Augusto von Dentz
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 test/simple-player | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/simple-player b/test/simple-player
index 70701da..f8751d6 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -111,7 +111,7 @@ if __name__ == '__main__':
 					"Album" : "Album",
 					"Genre" : "Genre",
 					"NumberOfTracks" : dbus.UInt32(10),
-					"Number" : dbus.UInt32(1),
+					"Track" : dbus.UInt32(1),
 					"Duration" : dbus.UInt32(10000) }, signature="sv")
 
 	print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
-- 
1.7.11.7


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

* [PATCH BlueZ 10/11] test: Fix using Number instead of Track in mpris-player
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 09/11] test: Fix using Number instead of Track in simple-player Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  2012-10-25 13:59 ` [PATCH BlueZ 11/11] test: Add support for using external player Luiz Augusto von Dentz
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

---
 test/mpris-player.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/mpris-player.c b/test/mpris-player.c
index a2c4cc6..13f5c85 100644
--- a/test/mpris-player.c
+++ b/test/mpris-player.c
@@ -338,7 +338,7 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
 
 		dbus_message_iter_get_basic(&var, &value);
 
-		dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+		dict_append_entry(metadata, "Track", DBUS_TYPE_UINT32,
 								&value);
 	}
 
-- 
1.7.11.7


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

* [PATCH BlueZ 11/11] test: Add support for using external player
  2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
                   ` (8 preceding siblings ...)
  2012-10-25 13:59 ` [PATCH BlueZ 10/11] test: Fix using Number instead of Track in mpris-player Luiz Augusto von Dentz
@ 2012-10-25 13:59 ` Luiz Augusto von Dentz
  9 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
  To: linux-bluetooth

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

This add support for passing a device player object which is then
registered as a local one in a loopback fashion.
---
 test/simple-player | 80 +++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 61 insertions(+), 19 deletions(-)

diff --git a/test/simple-player b/test/simple-player
index f8751d6..9e72f3e 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -9,6 +9,50 @@ import dbus.mainloop.glib
 import gobject
 
 class Player(dbus.service.Object):
+	properties = None
+	metadata = None
+
+	def set_object(self, obj = None):
+		if obj != None:
+			bus = dbus.SystemBus()
+			mp = dbus.Interface(bus.get_object("org.bluez", obj),
+						"org.bluez.MediaPlayer")
+
+			self.properties = mp.GetProperties()
+			self.metadata = mp.GetTrack()
+
+			bus.add_signal_receiver(self.property_changed,
+				path = obj,
+				dbus_interface = "org.bluez.MediaPlayer",
+				signal_name = "PropertyChanged")
+
+			bus.add_signal_receiver(self.track_changed,
+				path = obj,
+				dbus_interface ="org.bluez.MediaPlayer",
+				signal_name = "TrackChanged")
+		else:
+			self.properties = dbus.Dictionary({
+					"Equalizer" : "off",
+					"Repeat" : "off",
+					"Shuffle" : "off",
+					"Scan" : "off",
+					"Status" : "playing",
+					"Position" : dbus.UInt32(0) },
+					signature="sv")
+
+			self.metadata = dbus.Dictionary({
+					"Title" : "Title",
+					"Artist" : "Artist",
+					"Album" : "Album",
+					"Genre" : "Genre",
+					"NumberOfTracks" : dbus.UInt32(10),
+					"Track" : dbus.UInt32(1),
+					"Duration" : dbus.UInt32(10000) },
+					signature="sv")
+			handler = InputHandler(self)
+			gobject.io_add_watch(sys.stdin, gobject.IO_IN,
+							handler.handle)
+
 	@dbus.service.method("org.bluez.MediaPlayer",
 					in_signature="", out_signature="")
 	def Release(self):
@@ -42,6 +86,16 @@ class Player(dbus.service.Object):
 	def help(self, func):
 		help(self.__class__.__dict__[func])
 
+	def property_changed(self, setting, value):
+		print("property_changed(%s, %s)" % (setting, value))
+
+		self.PropertyChanged(setting, value)
+
+	def track_changed(self, metadata):
+		print("track_changed(%s)" % (metadata))
+
+		self.TrackChanged(metadata)
+
 class InputHandler:
 	commands = { 'TrackChanged': '(metadata)',
 					'PropertyChanged': '(key, value)',
@@ -79,7 +133,6 @@ class InputHandler:
 		return True
 
 
-
 if __name__ == '__main__':
 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
@@ -99,27 +152,16 @@ if __name__ == '__main__':
 	player = Player(bus, path)
 	mainloop = gobject.MainLoop()
 
-	properties = dbus.Dictionary({ "Equalizer" : "off",
-					"Repeat" : "off",
-					"Shuffle" : "off",
-					"Scan" : "off",
-					"Status" : "playing",
-					"Position" : dbus.UInt32(0) }, signature="sv")
-
-	metadata = dbus.Dictionary({ "Title" : "Title",
-					"Artist" : "Artist",
-					"Album" : "Album",
-					"Genre" : "Genre",
-					"NumberOfTracks" : dbus.UInt32(10),
-					"Track" : dbus.UInt32(1),
-					"Duration" : dbus.UInt32(10000) }, signature="sv")
+	if len(sys.argv) > 2:
+		player.set_object(sys.argv[2])
+	else:
+		player.set_object()
 
 	print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
-						% (properties, metadata))
+					% (player.properties, player.metadata))
 
-	handler = InputHandler(player)
-	gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)
 
-	media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+	media.RegisterPlayer(dbus.ObjectPath(path), player.properties,
+							player.metadata)
 
 	mainloop.run()
-- 
1.7.11.7


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

* Re: [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player
  2012-10-25 13:59 ` [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player Luiz Augusto von Dentz
@ 2012-10-26  7:44   ` Johan Hedberg
  0 siblings, 0 replies; 12+ messages in thread
From: Johan Hedberg @ 2012-10-26  7:44 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

On Thu, Oct 25, 2012, Luiz Augusto von Dentz wrote:
> This also bump controller record to 1.3.
> ---
>  Makefile.am    |   1 +
>  audio/avrcp.c  | 686 +++++++++++++++++++++++++++++++++++++++++++++++++--------
>  audio/player.c | 404 +++++++++++++++++++++++++++++++++
>  audio/player.h |  46 ++++
>  4 files changed, 1047 insertions(+), 90 deletions(-)
>  create mode 100644 audio/player.c
>  create mode 100644 audio/player.h

I've applied patches 1-6 but this one is just huge. Isn't there some
easy way you could split it up a bit? Also, the necessary D-Bus API
documentation changes seem to be missing.

Johan

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

end of thread, other threads:[~2012-10-26  7:44 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-25 13:59 [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 03/11] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 04/11] AVRCP: Fix not adding session to player's list of sessions Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 05/11] AVCTP: Reduce verbosity of PDU parsing Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 06/11] AVRCP: Add support for settings changed event Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player Luiz Augusto von Dentz
2012-10-26  7:44   ` Johan Hedberg
2012-10-25 13:59 ` [PATCH BlueZ 08/11] AVRCP: Remove conversions inside media.c Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 09/11] test: Fix using Number instead of Track in simple-player Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 10/11] test: Fix using Number instead of Track in mpris-player Luiz Augusto von Dentz
2012-10-25 13:59 ` [PATCH BlueZ 11/11] test: Add support for using external player 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.