diff --git a/audio/control.c b/audio/control.c
index a29a392..2070e10 100644
--- a/audio/control.c
+++ b/audio/control.c
#define CTYPE_ACCEPTED 0x9
#define CTYPE_REJECTED 0xA
#define CTYPE_STABLE 0xC
+#define CTYPE_CHANGED 0xD
#define CTYPE_INTERIM 0xF
/* opcodes */
uint8_t key_quirks[256];
uint16_t registered_events;
+ uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
};
static struct {
return NULL;
}
+static int avctp_send_event(struct control *control, uint8_t id, void *data)
+{
+ uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+ AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
+ struct avctp_header *avctp = (void *) buf;
+ struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
+ struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
+ AVRCP_HEADER_LENGTH];
+ int sk = g_io_channel_unix_get_fd(control->io);
+ uint16_t size;
+
+ memset(buf, 0, sizeof(buf));
+
+ avctp->transaction = control->transaction_events[id];
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = AVCTP_RESPONSE;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ avrcp->code = CTYPE_CHANGED;
+ avrcp->subunit_type = SUBUNIT_PANEL;
+ avrcp->opcode = OP_VENDORDEP;
+
+ pdu->company_id[0] = IEEEID_BTSIG >> 16;
+ pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
+ pdu->company_id[2] = IEEEID_BTSIG & 0xFF;
+
+ pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ pdu->params[0] = id;
+
+ DBG("id=%u", id);
+
+ switch (id) {
+ case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+ size = 2;
+ pdu->params[1] = *((uint8_t *)data);
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED: {
+ size = 9;
+
+ /*
+ * AVRCP 1.3 supports only one track identifier: PLAYING
+ * (0x0). When 1.4 version is added, this shall be changed to
+ * contain the identifier of the track.
+ */
+ memset(&pdu->params[1], 0, 8);
+
+ break;
+ }
+ default:
+ error("Unknown event %u", id);
+ return -EINVAL;
+ }
+
+ pdu->params_len = htons(size);
+ size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+ AVRCP_SPECAVCPDU_HEADER_LENGTH;
+
+ if (write(sk, buf, size) < 0)
+ return -errno;
+
+ /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+ control->registered_events ^= 1 << id;
+
+ return 0;
+}
+
static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
uint32_t *elapsed, uint32_t *track_len)
{
return;
mp->status = status;
+
+ if (control->state == AVCTP_STATE_CONNECTED &&
+ (control->registered_events &
+ (1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED))) {
+ avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
+ &status);
+ }
}
/*
"\tTrack number: %u\n\tTrack duration: %u",
mi->title, mi->artist, mi->album, mi->genre,
mi->ntracks, mi->track, mi->track_len);
+
+ if (control->state == AVCTP_STATE_CONNECTED &&
+ (control->registered_events &
+ (1 << AVRCP_EVENT_TRACK_CHANGED))) {
+ avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
+ }
}
static int avrcp_handle_get_capabilities(struct control *control,
}
static int avrcp_handle_register_notification(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
uint8_t status;
goto err;
}
- /* Register event */
+ /* Register event and save the transaction used */
control->registered_events |= (1 << pdu->params[0]);
+ control->transaction_events[pdu->params[0]] = transaction;
pdu->params_len = htons(len);
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
+ struct avctp_header *avctp,
struct avrcp_header *avrcp,
int operand_count)
{
goto err_metadata;
}
- len = avrcp_handle_register_notification(control, pdu);
+ len = avrcp_handle_register_notification(control, pdu,
+ avctp->transaction);
if (len < 0)
goto err_metadata;
int r_size;
operand_count -= 3;
avctp->cr = AVCTP_RESPONSE;
- r_size = handle_vendordep_pdu(control, avrcp, operand_count);
+ r_size = handle_vendordep_pdu(control, avctp, avrcp,
+ operand_count);
packet_size = AVCTP_HEADER_LENGTH + r_size;
} else {
avctp->cr = AVCTP_RESPONSE;