Diff between 2f35d76a3f7d2e09f8182c5f6190a0d212e2fd00 and 731aec9cae63d5901d6226be885be3dee540786f

Changed Files

File Additions Deletions Status
audio/avctp.c +34 -6 modified
audio/avrcp.c +62 -0 modified

Full Patch

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