Diff between 6114561af455ebd177eab1880a378852b263ea74 and 007909a842ce3fb04969223988b9eb21a68d8913

Changed Files

File Additions Deletions Status
android/avrcp-lib.c +150 -5 modified
android/avrcp-lib.h +4 -0 modified

Full Patch

diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 98453b5..1d11ea4 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -74,6 +74,13 @@ struct avrcp_header {
 #error "Unknown byte order"
 #endif
 
+struct avrcp_browsing_header {
+	uint8_t pdu_id;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
 struct avrcp {
 	struct avctp *conn;
 	struct avrcp_player *player;
@@ -156,6 +163,28 @@ static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count)
 	return pdu;
 }
 
+static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands,
+							size_t operand_count)
+{
+	struct avrcp_browsing_header *pdu;
+
+	if (!operands || operand_count < sizeof(*pdu)) {
+		error("AVRCP: packet too small (%zu bytes)", operand_count);
+		return NULL;
+	}
+
+	pdu = (void *) operands;
+	pdu->params_len = ntohs(pdu->params_len);
+
+	if (operand_count != pdu->params_len + sizeof(*pdu)) {
+		error("AVRCP: invalid parameter length (%u bytes)",
+							pdu->params_len);
+		return NULL;
+	}
+
+	return pdu;
+}
+
 static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
 					uint8_t *code, uint8_t *subunit,
 					uint8_t *operands, size_t operand_count,
@@ -698,12 +727,9 @@ int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
 							session->tx_buf, len);
 }
 
-static int parse_status(struct avrcp_header *pdu)
+static int status2errno(uint8_t status)
 {
-	if (pdu->params_len < 1)
-		return -EPROTO;
-
-	switch (pdu->params[0]) {
+	switch (status) {
 	case AVRCP_STATUS_INVALID_COMMAND:
 		return -ENOSYS;
 	case AVRCP_STATUS_INVALID_PARAM:
@@ -723,6 +749,22 @@ static int parse_status(struct avrcp_header *pdu)
 	}
 }
 
+static int parse_status(struct avrcp_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
+static int parse_browsing_status(struct avrcp_browsing_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
 static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
 					uint8_t pdu_id, uint8_t *params,
 					size_t params_len, avctp_rsp_cb func,
@@ -751,6 +793,32 @@ static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
 					session->tx_buf, len, func, user_data);
 }
 
+static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id,
+					uint8_t *params, size_t params_len,
+					avctp_browsing_rsp_cb func,
+					void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) session->tx_buf;
+	size_t len = sizeof(*pdu);
+
+	memset(pdu, 0, len);
+
+	pdu->pdu_id = pdu_id;
+
+	if (params_len > 0) {
+		len += params_len;
+
+		if (len > session->tx_mtu)
+			return -ENOBUFS;
+
+		memcpy(pdu->params, params, params_len);
+		pdu->params_len = htons(params_len);
+	}
+
+	return avctp_send_browsing_req(session->conn, session->tx_buf, len,
+							func, user_data);
+}
+
 static gboolean get_capabilities_rsp(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
 					uint8_t *operands, size_t operand_count,
@@ -1510,6 +1578,83 @@ int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id)
 				session);
 }
 
+static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	uint16_t counter = 0;
+	uint32_t items = 0;
+	uint8_t depth = 0, count;
+	char **folders, *path = NULL;
+	int err;
+	size_t i;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_browsed)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < 10) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	counter = bt_get_be16(&pdu->params[1]);
+	items = bt_get_be32(&pdu->params[3]);
+	depth = pdu->params[9];
+
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
+
+	for (i = 10, count = 1; count - 1 < depth && i < pdu->params_len;
+								count++) {
+		uint8_t len;
+
+		len = pdu->params[i++];
+
+		if (i + len > pdu->params_len || len == 0) {
+			g_strfreev(folders);
+			err = -EPROTO;
+			goto done;
+		}
+
+		folders[count] = g_memdup(&pdu->params[i], len);
+		i += len;
+	}
+
+	path = g_build_pathv("/", folders);
+	g_strfreev(folders);
+
+done:
+	player->cfm->set_browsed(session, err, counter, items, path,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id)
+{
+	uint8_t pdu[2];
+
+	put_be16(player_id, pdu);
+
+	return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER,
+					pdu, sizeof(pdu), set_browsed_rsp,
+					session);
+}
+
 int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
 						uint8_t number, uint8_t *events)
 {
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index aa8b74a..eaa03f1 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -196,6 +196,9 @@ struct avrcp_control_cfm {
 					uint8_t *params, void *user_data);
 	void (*set_addressed) (struct avrcp *session, int err,
 					void *user_data);
+	void (*set_browsed) (struct avrcp *session, int err,
+					uint16_t counter, uint32_t items,
+					char *path, void *user_data);
 };
 
 struct avrcp_passthrough_handler {
@@ -242,6 +245,7 @@ int avrcp_set_volume(struct avrcp *session, uint8_t volume, avctp_rsp_cb func,
 							void *user_data);
 int avrcp_get_element_attributes(struct avrcp *session);
 int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id);
 
 int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
 					uint8_t number, uint8_t *events);