From 007909a842ce3fb04969223988b9eb21a68d8913 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Apr 2014 11:27:19 +0300 Subject: [PATCH] android/avrcp: Add avrcp_set_browsed_player() function --- android/avrcp-lib.c | 155 ++++++++++++++++++++++++++++++++++++++++++-- android/avrcp-lib.h | 4 ++ 2 files changed, 154 insertions(+), 5 deletions(-) diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c index 98453b558..1d11ea424 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 aa8b74a21..eaa03f1b3 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);