diff --git a/audio/control.c b/audio/control.c
index c5e175a..a29a392 100644
--- a/audio/control.c
+++ b/audio/control.c
#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
#define AVRCP_DISPLAYABLE_CHARSET 0x17
#define AVRCP_CT_BATTERY_STATUS 0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20
#define AVRCP_GET_PLAY_STATUS 0x30
#define AVRCP_REGISTER_NOTIFICATION 0x31
BATTERY_STATUS_FULL_CHARGE = 4,
};
+enum media_info_id {
+ MEDIA_INFO_TITLE = 1,
+ MEDIA_INFO_ARTIST = 2,
+ MEDIA_INFO_ALBUM = 3,
+ MEDIA_INFO_TRACK = 4,
+ MEDIA_INFO_N_TRACKS = 5,
+ MEDIA_INFO_GENRE = 6,
+ MEDIA_INFO_CURRENT_POSITION = 7,
+};
+
static DBusConnection *connection = NULL;
static GSList *servers = NULL;
mp->status = status;
}
+/*
+ * Copy media_info field to a buffer, intended to be used in a response to
+ * GetElementAttributes message.
+ *
+ * It assumes there's enough space in the buffer and on success it returns the
+ * size written.
+ *
+ * If @param id is not valid, -EINVAL is returned. If there's no such media
+ * attribute, -ENOENT is returned.
+ */
+static int mp_get_media_attribute(struct media_player *mp,
+ uint32_t id, uint8_t *buf)
+{
+ struct media_info_elem {
+ uint32_t id;
+ uint16_t charset;
+ uint16_t len;
+ uint8_t val[];
+ };
+ const struct media_info *mi = &mp->mi;
+ struct media_info_elem *elem = (void *)buf;
+ uint16_t len;
+ char valstr[20];
+
+ switch (id) {
+ case MEDIA_INFO_TITLE:
+ if (mi->title) {
+ len = strlen(mi->title);
+ memcpy(elem->val, mi->title, len);
+ } else {
+ len = 0;
+ }
+
+ break;
+ case MEDIA_INFO_ARTIST:
+ if (mi->artist == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->artist);
+ memcpy(elem->val, mi->artist, len);
+ break;
+ case MEDIA_INFO_ALBUM:
+ if (mi->album == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->album);
+ memcpy(elem->val, mi->album, len);
+ break;
+ case MEDIA_INFO_GENRE:
+ if (mi->genre == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->genre);
+ memcpy(elem->val, mi->genre, len);
+ break;
+
+ case MEDIA_INFO_TRACK:
+ if (!mi->track)
+ return -ENOENT;
+
+ snprintf(valstr, 20, "%u", mi->track);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ break;
+ case MEDIA_INFO_N_TRACKS:
+ if (!mi->ntracks)
+ return -ENOENT;
+
+ snprintf(valstr, 20, "%u", mi->ntracks);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ break;
+ case MEDIA_INFO_CURRENT_POSITION:
+ if (mi->elapsed != 0xFFFFFFFF) {
+ uint32_t elapsed;
+
+ mp_get_playback_status(mp, NULL, &elapsed, NULL);
+
+ snprintf(valstr, 20, "%u", elapsed);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ } else {
+ return -ENOENT;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ elem->id = htonl(id);
+ elem->charset = htons(0x6A); /* Always use UTF-8 */
+ elem->len = htons(len);
+
+ return sizeof(struct media_info_elem) + len;
+}
+
static void mp_set_attribute(struct media_player *mp,
uint8_t attr, uint8_t val)
{
return -EINVAL;
}
+static int avrcp_handle_get_element_attributes(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ uint64_t *identifier = (void *) &pdu->params[0];
+ uint16_t pos;
+ uint8_t nattr;
+ int size;
+ unsigned int i;
+
+ if (len < 8 || *identifier != 0 || !control->mp)
+ goto err;
+
+ len = 0;
+ pos = 1; /* Keep track of current position in reponse */
+ nattr = pdu->params[8];
+
+ if (!control->mp)
+ goto done;
+
+ if (!nattr) {
+ /*
+ * Return all available information, at least
+ * title must be returned.
+ */
+ for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
+ size = mp_get_media_attribute(control->mp, i,
+ &pdu->params[pos]);
+
+ if (size > 0) {
+ len++;
+ pos += size;
+ }
+ }
+ } else {
+ uint32_t *attr_ids = g_malloc(sizeof(uint32_t) * nattr);
+
+ /* save a copy of requested attributes */
+ memcpy(&attr_ids[0], &pdu->params[9], nattr * 4);
+
+ for (i = 0; i < nattr; i++) {
+ uint32_t attr = ntohl(attr_ids[i]);
+
+ size = mp_get_media_attribute(control->mp, attr,
+ &pdu->params[pos]);
+
+ if (size > 0) {
+ len++;
+ pos += size;
+ }
+ }
+
+ g_free(attr_ids);
+
+ if (!len)
+ goto err;
+ }
+
+done:
+ pdu->params[0] = len;
+ pdu->params_len = htons(pos);
+
+ return pos;
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
static int avrcp_handle_get_current_player_value(struct control *control,
struct avrcp_spec_avc_pdu *pdu)
{
avrcp->code = CTYPE_STABLE;
break;
+ case AVRCP_GET_ELEMENT_ATTRIBUTES:
+ if (avrcp->code != CTYPE_STATUS) {
+ pdu->params[0] = E_INVALID_COMMAND;
+ goto err_metadata;
+ }
+
+ len = avrcp_handle_get_element_attributes(control, pdu);
+ if (len < 0)
+ goto err_metadata;
+
+ avrcp->code = CTYPE_STABLE;
+
+ break;
case AVRCP_GET_CURRENT_PLAYER_VALUE:
if (avrcp->code != CTYPE_STATUS) {
pdu->params[0] = E_INVALID_COMMAND;