From 731aec9cae63d5901d6226be885be3dee540786f Mon Sep 17 00:00:00 2001 From: Vani Patel Date: Mon, 24 Sep 2012 14:12:03 +0300 Subject: [PATCH] AVRCP: Add handler for browsing PDUs Implement generic handling of browsing PDU ids --- audio/avctp.c | 40 ++++++++++++++++++++++++++++----- audio/avrcp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/audio/avctp.c b/audio/avctp.c index 83f79e11b..37b666462 100644 --- a/audio/avctp.c +++ b/audio/avctp.c @@ -460,9 +460,9 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; - uint8_t buf[1024]; + uint8_t buf[1024], *operands; struct avctp_header *avctp; - int sock, ret; + int sock, ret, packet_size, operand_count; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; @@ -474,14 +474,35 @@ 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; + operands = buf + AVCTP_HEADER_LENGTH; + ret -= AVCTP_HEADER_LENGTH; + operand_count = ret; + + packet_size = AVCTP_HEADER_LENGTH; + avctp->cr = AVCTP_RESPONSE; + + packet_size += browsing_handler->cb(session, avctp->transaction, + operands, operand_count, + browsing_handler->user_data); + + if (packet_size != 0) { + ret = write(sock, buf, packet_size); + if (ret != packet_size) + goto failed; + } + return TRUE; failed: - DBG("Browsing: disconnected"); + DBG("AVCTP Browsing: disconnected"); return FALSE; } @@ -695,11 +716,12 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err, return; } + DBG("AVCTP Browsing: connected to %s", address); + if (!session->browsing_io) session->browsing_io = g_io_channel_ref(chan); session->browsing_mtu = imtu; - session->browsing_io_id = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_browsing_cb, session); } @@ -737,7 +759,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data) avctp_set_state(session, AVCTP_STATE_CONNECTED); session->control_mtu = imtu; - session->control_io_id = g_io_add_watch(chan, + session->control_io_id = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); } @@ -849,12 +871,18 @@ static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan, { GError *err = NULL; - if (!session->control_io || session->browsing_io) { + if (session->control_io == NULL || session->browsing_io) { error("Browsing: Refusing unexpected connect"); g_io_channel_shutdown(chan, TRUE, NULL); return; } + if (browsing_handler == NULL) { + error("Browsing: Handler not registered"); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL, &err)) return; diff --git a/audio/avrcp.c b/audio/avrcp.c index e2bdc5b77..300ecd802 100644 --- a/audio/avrcp.c +++ b/audio/avrcp.c @@ -94,6 +94,7 @@ #define AVRCP_REQUEST_CONTINUING 0x40 #define AVRCP_ABORT_CONTINUING 0x41 #define AVRCP_SET_ABSOLUTE_VOLUME 0x50 +#define AVRCP_GENERAL_REJECT 0xA0 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */ #define CAP_COMPANY_ID 0x02 @@ -145,6 +146,13 @@ struct avrcp_header { #define AVRCP_MTU (AVC_MTU - AVC_HEADER_LENGTH) #define AVRCP_PDU_MTU (AVRCP_MTU - AVRCP_HEADER_LENGTH) +struct avrcp_browsing_header { + uint8_t pdu_id; + uint16_t param_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_BROWSING_HEADER_LENGTH 3 + struct avrcp_server { bdaddr_t src; uint32_t tg_record_id; @@ -165,7 +173,9 @@ struct avrcp_player { struct audio_device *dev; unsigned int control_handler; + unsigned int browsing_handler; uint16_t registered_events; + uint8_t transaction; uint8_t transaction_events[AVRCP_EVENT_LAST + 1]; struct pending_pdu *pending_pdu; @@ -1139,6 +1149,48 @@ err_metadata: return AVRCP_HEADER_LENGTH + 1; } +static struct browsing_pdu_handler { + uint8_t pdu_id; + void (*func) (struct avrcp_player *player, + struct avrcp_browsing_header *pdu, + uint8_t transaction); +} browsing_handlers[] = { + { }, +}; + +static size_t handle_browsing_pdu(struct avctp *session, + uint8_t transaction, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp_player *player = user_data; + struct browsing_pdu_handler *handler; + struct avrcp_browsing_header *pdu = (void *) operands; + uint8_t status; + + DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, + pdu->param_len); + + for (handler = browsing_handlers; handler->pdu_id; handler++) { + if (handler->pdu_id == pdu->pdu_id) + break; + } + + if (handler == NULL || handler->func == NULL) { + pdu->pdu_id = AVRCP_GENERAL_REJECT; + status = AVRCP_STATUS_INVALID_COMMAND; + goto err; + } + + player->transaction = transaction; + handler->func(player, pdu, transaction); + return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len); + +err: + pdu->param_len = htons(sizeof(status)); + memcpy(pdu->params, &status, (sizeof(status))); + return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status); +} + size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands) { struct avrcp_header *pdu = (void *) operands; @@ -1239,6 +1291,11 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state, player->control_handler = 0; } + if (player->browsing_handler) { + avctp_unregister_browsing_pdu_handler( + player->browsing_handler); + player->browsing_handler = 0; + } break; case AVCTP_STATE_CONNECTING: player->session = avctp_connect(&dev->src, &dev->dst); @@ -1249,6 +1306,11 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state, AVC_OP_VENDORDEP, handle_vendordep_pdu, player); + if (!player->browsing_handler) + player->browsing_handler = + avctp_register_browsing_pdu_handler( + handle_browsing_pdu, + player); break; case AVCTP_STATE_CONNECTED: rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID); -- 2.47.3