AVRCP: Add parsing for GetFolderItems PDU
diff --git a/parser/avrcp.c b/parser/avrcp.c
index cb5985d..55b8a88 100644
--- a/parser/avrcp.c
+++ b/parser/avrcp.c
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <inttypes.h>
#include "parser/parser.h"
@@ -179,6 +180,12 @@
#define AVRCP_PLAY_STATUS_REV_SEEK 0x04
#define AVRCP_PLAY_STATUS_ERROR 0xFF
+/* media scope */
+#define AVRCP_MEDIA_PLAYER_LIST 0x00
+#define AVRCP_MEDIA_PLAYER_VFS 0x01
+#define AVRCP_MEDIA_SEARCH 0x02
+#define AVRCP_MEDIA_NOW_PLAYING 0x03
+
static struct avrcp_continuing {
uint16_t num;
uint16_t size;
@@ -1207,6 +1214,7 @@
uid = get_u16(frm);
printf("PlayerID: 0x%04x (%u)\n", uid, uid);
+
p_indent(level, frm);
uid = get_u16(frm);
@@ -1499,6 +1507,384 @@
}
}
+static const char *scope2str(uint8_t scope)
+{
+ switch (scope) {
+ case AVRCP_MEDIA_PLAYER_LIST:
+ return "Media Player List";
+ case AVRCP_MEDIA_PLAYER_VFS:
+ return "Media Player Virtual Filesystem";
+ case AVRCP_MEDIA_SEARCH:
+ return "Search";
+ case AVRCP_MEDIA_NOW_PLAYING:
+ return "Now Playing";
+ default:
+ return "Unknown";
+ }
+}
+
+static const char *playertype2str(uint8_t type)
+{
+ switch (type & 0x0F) {
+ case 0x01:
+ return "Audio";
+ case 0x02:
+ return "Video";
+ case 0x03:
+ return "Audio, Video";
+ case 0x04:
+ return "Audio Broadcasting";
+ case 0x05:
+ return "Audio, Audio Broadcasting";
+ case 0x06:
+ return "Video, Audio Broadcasting";
+ case 0x07:
+ return "Audio, Video, Audio Broadcasting";
+ case 0x08:
+ return "Video Broadcasting";
+ case 0x09:
+ return "Audio, Video Broadcasting";
+ case 0x0A:
+ return "Video, Video Broadcasting";
+ case 0x0B:
+ return "Audio, Video, Video Broadcasting";
+ case 0x0C:
+ return "Audio Broadcasting, Video Broadcasting";
+ case 0x0D:
+ return "Audio, Audio Broadcasting, Video Broadcasting";
+ case 0x0E:
+ return "Video, Audio Broadcasting, Video Broadcasting";
+ case 0x0F:
+ return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+ }
+
+ return "None";
+}
+
+static const char *playersubtype2str(uint32_t subtype)
+{
+ switch (subtype & 0x03) {
+ case 0x01:
+ return "Audio Book";
+ case 0x02:
+ return "Podcast";
+ case 0x03:
+ return "Audio Book, Podcast";
+ }
+
+ return "None";
+}
+
+static void avrcp_media_player_item_dump(int level, struct frame *frm,
+ uint16_t len)
+{
+ uint16_t id, charset;
+ uint8_t type, status, namelen;
+ uint32_t subtype;
+ uint64_t features[2];
+
+ p_indent(level, frm);
+
+ if (len < 28) {
+ printf("PDU Malformed\n");
+ raw_dump(level, frm);
+ return;
+ }
+
+ id = get_u16(frm);
+ printf("PlayerID: 0x%04x (%u)\n", id, id);
+
+ p_indent(level, frm);
+
+ type = get_u8(frm);
+ printf("PlayerType: 0x%04x (%s)\n", type, playertype2str(type));
+
+ p_indent(level, frm);
+
+ subtype = get_u32(frm);
+ printf("PlayerSubtype: 0x%08x (%s)\n", subtype,
+ playersubtype2str(subtype));
+
+ p_indent(level, frm);
+
+ status = get_u8(frm);
+ printf("PlayStatus: 0x%02x (%s)\n", status, playstatus2str(status));
+
+ p_indent(level, frm);
+
+ get_u128(frm, &features[0], &features[1]);
+ printf("Features: 0x%16" PRIx64 "%16" PRIx64 "\n", features[1],
+ features[0]);
+
+ p_indent(level, frm);
+
+ charset = get_u16(frm);
+ printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+ p_indent(level, frm);
+
+ namelen = get_u8(frm);
+ printf("NameLength: 0x%02x (%u)\n", namelen, namelen);
+
+ p_indent(level, frm);
+
+ printf("Name: ");
+ for (; namelen > 0; namelen--) {
+ uint8_t c = get_u8(frm);
+ printf("%1c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+}
+
+static const char *foldertype2str(uint8_t type)
+{
+ switch (type) {
+ case 0x00:
+ return "Mixed";
+ case 0x01:
+ return "Titles";
+ case 0x02:
+ return "Albuns";
+ case 0x03:
+ return "Artists";
+ case 0x04:
+ return "Genres";
+ case 0x05:
+ return "Playlists";
+ case 0x06:
+ return "Years";
+ }
+
+ return "Reserved";
+}
+
+static void avrcp_folder_item_dump(int level, struct frame *frm, uint16_t len)
+{
+ uint8_t type, playable, namelen;
+ uint16_t charset;
+ uint64_t uid;
+
+ p_indent(level, frm);
+
+ if (len < 14) {
+ printf("PDU Malformed\n");
+ raw_dump(level, frm);
+ return;
+ }
+
+ uid = get_u64(frm);
+ printf("FolderUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+ p_indent(level, frm);
+
+ type = get_u8(frm);
+ printf("FolderType: 0x%02x (%s)\n", type, foldertype2str(type));
+
+ p_indent(level, frm);
+
+ playable = get_u8(frm);
+ printf("IsPlayable: 0x%02x (%s)\n", playable,
+ playable & 0x01 ? "True" : "False");
+
+ p_indent(level, frm);
+
+ charset = get_u16(frm);
+ printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+ p_indent(level, frm);
+
+ namelen = get_u8(frm);
+ printf("NameLength: 0x%02x (%u)\n", namelen, namelen);
+
+ p_indent(level, frm);
+
+ printf("Name: ");
+ for (; namelen > 0; namelen--) {
+ uint8_t c = get_u8(frm);
+ printf("%1c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+}
+
+static const char *elementtype2str(uint8_t type)
+{
+ switch (type) {
+ case 0x00:
+ return "Audio";
+ case 0x01:
+ return "Video";
+ }
+
+ return "Reserved";
+}
+
+static void avrcp_media_element_item_dump(int level, struct frame *frm,
+ uint16_t len)
+{
+ uint64_t uid;
+ uint16_t charset;
+ uint8_t type, namelen, count;
+
+ if (len < 14) {
+ printf("PDU Malformed\n");
+ raw_dump(level, frm);
+ return;
+ }
+
+ uid = get_u64(frm);
+ printf("ElementUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+ p_indent(level, frm);
+
+ type = get_u8(frm);
+ printf("ElementType: 0x%02x (%s)\n", type, elementtype2str(type));
+
+ p_indent(level, frm);
+
+ charset = get_u16(frm);
+ printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+ p_indent(level, frm);
+
+ namelen = get_u8(frm);
+ printf("NameLength: 0x%02x (%u)\n", namelen, namelen);
+
+ p_indent(level, frm);
+
+ printf("Name: ");
+ for (; namelen > 0; namelen--) {
+ uint8_t c = get_u8(frm);
+ printf("%1c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+
+ p_indent(level, frm);
+
+ count = get_u8(frm);
+ printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+ for (; count > 0; count--) {
+ uint32_t attr;
+
+ p_indent(level, frm);
+
+ attr = get_u32(frm);
+ printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+
+ p_indent(level, frm);
+
+ charset = get_u16(frm);
+ printf("CharsetID: 0x%04x (%s)\n", charset,
+ charset2str(charset));
+
+ p_indent(level, frm);
+
+ namelen = get_u8(frm);
+ printf("AttributeLength: 0x%02x (%u)\n", namelen, namelen);
+
+ p_indent(level, frm);
+
+ printf("AttributeValue: ");
+ for (; namelen > 0; namelen--) {
+ uint8_t c = get_u8(frm);
+ printf("%1c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+ }
+}
+
+static void avrcp_get_folder_items_dump(int level, struct frame *frm,
+ uint8_t hdr, uint16_t len)
+{
+ uint8_t scope, count, status;
+ uint32_t start, end;
+ uint16_t uid, num;
+
+ p_indent(level, frm);
+
+ if (hdr & 0x02)
+ goto response;
+
+ if (len < 10) {
+ printf("PDU Malformed\n");
+ raw_dump(level, frm);
+ return;
+ }
+
+ scope = get_u8(frm);
+ printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+ p_indent(level, frm);
+
+ start = get_u32(frm);
+ printf("StartItem: 0x%08x (%u)\n", start, start);
+
+ p_indent(level, frm);
+
+ end = get_u32(frm);
+ printf("EndItem: 0x%08x (%u)\n", end, end);
+
+ p_indent(level, frm);
+
+ count = get_u8(frm);
+ printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+ for (; count > 0; count--) {
+ uint32_t attr;
+
+ p_indent(level, frm);
+
+ attr = get_u32(frm);
+ printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+ }
+
+ return;
+
+response:
+ status = get_u8(frm);
+ printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+ if (len == 1)
+ return;
+
+ p_indent(level, frm);
+
+ uid = get_u16(frm);
+ printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+
+ p_indent(level, frm);
+
+ num = get_u16(frm);
+ printf("Number of Items: 0x%04x (%u)\n", num, num);
+
+ for (; num > 0; num--) {
+ uint8_t type;
+ uint16_t len;
+
+ p_indent(level, frm);
+
+ type = get_u8(frm);
+ len = get_u16(frm);
+ switch (type) {
+ case 0x01:
+ printf("Item: 0x01 (Media Player)) ");
+ printf("Length: 0x%04x (%u)\n", len, len);
+ avrcp_media_player_item_dump(level, frm, len);
+ break;
+ case 0x02:
+ printf("Item: 0x02 (Folder) ");
+ printf("Length: 0x%04x (%u)\n", len, len);
+ avrcp_folder_item_dump(level, frm, len);
+ break;
+ case 0x03:
+ printf("Item: 0x03 (Media Element) ");
+ printf("Length: 0x%04x (%u)\n", len, len);
+ avrcp_media_element_item_dump(level, frm, len);
+ break;
+ }
+ }
+}
+
static void avrcp_browsing_dump(int level, struct frame *frm, uint8_t hdr)
{
uint8_t pduid;
@@ -1520,6 +1906,9 @@
case AVRCP_SET_BROWSED_PLAYER:
avrcp_set_browsed_player_dump(level + 1, frm, hdr, len);
break;
+ case AVRCP_GET_FOLDER_ITEMS:
+ avrcp_get_folder_items_dump(level + 1, frm, hdr, len);
+ break;
default:
raw_dump(level, frm);
}