diff --git a/audio/avctp.c b/audio/avctp.c
index 83f79e1..37b6664 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
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;
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;
}
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); }
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);
}
{
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 e2bdc5b..300ecd8 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
#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
#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;
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;
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;
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);
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);