linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ 03/16] audio/AVRCP: Add support for GetFolderItems command
Date: Fri, 14 Jun 2013 15:41:37 +0300	[thread overview]
Message-ID: <1371213710-16607-4-git-send-email-luiz.dentz@gmail.com> (raw)
In-Reply-To: <1371213710-16607-1-git-send-email-luiz.dentz@gmail.com>

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

This adds support for GetFolderItems command via player callback.
---
 profiles/audio/avrcp.c  | 197 +++++++++++++++++++++++++++++++++++++++++++++++-
 profiles/audio/player.h |   2 +
 2 files changed, 195 insertions(+), 4 deletions(-)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 90c80c3..1da9541 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -177,14 +177,24 @@ struct pending_pdu {
 	uint16_t offset;
 };
 
+struct pending_list_items {
+	GSList *items;
+	uint32_t start;
+	uint32_t end;
+};
+
 struct avrcp_player {
 	struct avrcp_server *server;
 	GSList *sessions;
 	uint16_t id;
+	uint8_t scope;
+	uint64_t uid;
 	uint16_t uid_counter;
 	bool browsed;
 	uint8_t *features;
 
+	struct pending_list_items *p;
+
 	struct avrcp_player_cb *cb;
 	void *user_data;
 	GDestroyNotify destroy;
@@ -2077,6 +2087,158 @@ static const char *subtype_to_string(uint32_t subtype)
 	return "None";
 }
 
+static struct media_item *parse_media_element(struct avrcp *session,
+					uint8_t *operands, uint16_t len)
+{
+	struct avrcp_player *player;
+	struct media_player *mp;
+	uint16_t namelen;
+	char name[255];
+	uint64_t uid;
+
+	if (len < 13)
+		return NULL;
+
+	uid = bt_get_be64(&operands[0]);
+
+	namelen = MIN(bt_get_be16(&operands[11]), sizeof(name) - 1);
+	if (namelen > 0) {
+		memcpy(name, &operands[13], namelen);
+		name[namelen] = '\0';
+	}
+
+	player = session->player;
+	mp = player->user_data;
+
+	return media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
+}
+
+static struct media_item *parse_media_folder(struct avrcp *session,
+					uint8_t *operands, uint16_t len)
+{
+	struct avrcp_player *player = session->player;
+	struct media_player *mp = player->user_data;
+	uint16_t namelen;
+	char name[255];
+	uint64_t uid;
+	uint8_t type;
+
+	if (len < 12)
+		return NULL;
+
+	uid = bt_get_be64(&operands[0]);
+	type = operands[9];
+
+	namelen = MIN(bt_get_be16(&operands[12]), sizeof(name) - 1);
+	if (namelen > 0) {
+		memcpy(name, &operands[14], namelen);
+		name[namelen] = '\0';
+	}
+
+	return media_player_create_folder(mp, name, type, uid);
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+								uint32_t end);
+static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct pending_list_items *p = player->p;
+	uint16_t count;
+	uint32_t items, total;
+	size_t i;
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5)
+		goto done;
+
+	count = bt_get_be16(&operands[6]);
+	if (count == 0)
+		goto done;
+
+	for (i = 8; count && i + 3 < operand_count; count--) {
+		struct media_item *item;
+		uint8_t type;
+		uint16_t len;
+
+		type = operands[i++];
+		len = bt_get_be16(&operands[i]);
+		i += 2;
+
+		if (type != 0x03 && type != 0x02) {
+			i += len;
+			continue;
+		}
+
+		if (i + len > operand_count) {
+			error("Invalid item length");
+			break;
+		}
+
+		if (type == 0x03)
+			item = parse_media_element(session, &operands[i], len);
+		else if (type == 0x02)
+			item = parse_media_folder(session, &operands[i], len);
+
+		if (item) {
+			if (g_slist_find(p->items, item))
+				goto done;
+			p->items = g_slist_append(p->items, item);
+		}
+
+		i += len;
+	}
+
+	items = g_slist_length(p->items);
+	total = p->end - p->start;
+	if (items < total) {
+		avrcp_list_items(session, p->start + items + 1, p->end);
+		return FALSE;
+	}
+
+done:
+	g_slist_free(p->items);
+	g_free(p);
+	player->p = NULL;
+
+	return FALSE;
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+								uint32_t end)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10 +
+			AVRCP_MEDIA_ATTRIBUTE_LAST * sizeof(uint32_t)];
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu = (void *) buf;
+	uint16_t length = AVRCP_BROWSING_HEADER_LENGTH + 10;
+	uint32_t attribute;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+	pdu->param_len = htons(10 + sizeof(uint32_t));
+
+	pdu->params[0] = player->scope;
+
+	bt_put_be32(start, &pdu->params[1]);
+	bt_put_be32(end, &pdu->params[5]);
+
+	pdu->params[9] = 1;
+
+	/* Only the title (0x01) is mandatory. This can be extended to
+	 * support AVRCP_MEDIA_ATTRIBUTE_* attributes */
+	attribute = htonl(AVRCP_MEDIA_ATTRIBUTE_TITLE);
+	memcpy(&pdu->params[10], &attribute, sizeof(uint32_t));
+
+	length += sizeof(uint32_t);
+
+	avctp_send_browsing_req(session->conn, buf, length,
+					avrcp_list_items_rsp, session);
+}
+
 static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
 						uint8_t *operands,
 						size_t operand_count,
@@ -2339,6 +2501,33 @@ static int ct_rewind(struct media_player *mp, void *user_data)
 	return ct_press(player, AVC_REWIND);
 }
 
+static int ct_list_items(struct media_player *mp, const char *name,
+				uint32_t start, uint32_t end, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+	struct pending_list_items *p;
+
+	if (player->p != NULL)
+		return -EBUSY;
+
+	session = player->sessions->data;
+
+	if (g_str_has_prefix(name, "/NowPlaying"))
+		player->scope = 0x03;
+	else
+		player->scope = 0x01;
+
+	avrcp_list_items(session, start, end);
+
+	p = g_new0(struct pending_list_items, 1);
+	p->start = start;
+	p->end = end;
+	player->p = p;
+
+	return 0;
+}
+
 static const struct media_player_callback ct_cbs = {
 	.set_setting	= ct_set_setting,
 	.play		= ct_play,
@@ -2348,6 +2537,7 @@ static const struct media_player_callback ct_cbs = {
 	.previous	= ct_previous,
 	.fast_forward	= ct_fast_forward,
 	.rewind		= ct_rewind,
+	.list_items	= ct_list_items,
 };
 
 static struct avrcp_player *create_ct_player(struct avrcp *session,
@@ -2582,11 +2772,10 @@ static void avrcp_status_changed(struct avrcp *session,
 static void avrcp_track_changed(struct avrcp *session,
 						struct avrcp_header *pdu)
 {
-	uint64_t uid;
-
 	if (session->browsing_id) {
-		uid = bt_get_be64(&pdu->params[1]);
-		avrcp_get_item_attributes(session, uid);
+		struct avrcp_player *player = session->player;
+		player->uid = bt_get_be64(&pdu->params[1]);
+		avrcp_get_item_attributes(session, player->uid);
 	} else
 		avrcp_get_element_attributes(session);
 }
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 6e44308..e7a885e 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -54,6 +54,8 @@ struct media_player_callback {
 	int (*previous) (struct media_player *mp, void *user_data);
 	int (*fast_forward) (struct media_player *mp, void *user_data);
 	int (*rewind) (struct media_player *mp, void *user_data);
+	int (*list_items) (struct media_player *mp, const char *name,
+				uint32_t start, uint32_t end, void *user_data);
 };
 
 struct media_player *media_player_controller_create(const char *path,
-- 
1.8.1.4


  parent reply	other threads:[~2013-06-14 12:41 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-06-14 12:41 [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 01/16] audio/player: Split item creation Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 02/16] audio/AVRCP: Add browsed flag to player Luiz Augusto von Dentz
2013-06-14 12:41 ` Luiz Augusto von Dentz [this message]
2013-06-14 12:41 ` [PATCH BlueZ 04/16] audio/player: Add implementation of MediaFolder.ListItems Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 05/16] audio/player: Add support for setting current Item object Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 06/16] audio/player: Add function media_item_set_playable Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 07/16] audio/player: Add support for MediaItem.Metadata property Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 08/16] audio/AVRCP: Add support for ChangePath command Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 09/16] audio/player: Add implementation of MediaFolder.ChangeFolder Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 10/16] audio/AVRCP: Add support for PlayItem command Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 11/16] audio/player: Add implementation of MediaItem.Play Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 12/16] audio/AVRCP: Add support for AddToNowPlaying command Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 13/16] audio/player: Add implementation of MediaItem.AddToNowPlaying Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 14/16] audio/AVRCP: Add support for Search command Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 15/16] audio/player: Add implementation of MediaFolder.Search Luiz Augusto von Dentz
2013-06-14 12:41 ` [PATCH BlueZ 16/16] audio/player: Create item on media_player_set_playlist_item Luiz Augusto von Dentz
2013-06-17 16:46 ` [PATCH v3 BlueZ 00/16] MediaFolder and MediaItem implementation Luiz Augusto von Dentz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1371213710-16607-4-git-send-email-luiz.dentz@gmail.com \
    --to=luiz.dentz@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).