android/avrcp: Add avrcp_set_browsed_player() function

This commit is contained in:
Luiz Augusto von Dentz 2014-04-02 11:27:19 +03:00
parent 6114561af4
commit 007909a842
2 changed files with 154 additions and 5 deletions

View File

@ -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)
{

View File

@ -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);