diff --git a/audio/control.c b/audio/control.c
index 9990b06..f84c7f7 100644
--- a/audio/control.c
+++ b/audio/control.c
avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
}
-static int avrcp_handle_get_capabilities(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_capabilities(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
pdu->params[1] = G_N_ELEMENTS(company_ids);
- return 2 + (3 * G_N_ELEMENTS(company_ids));
+ return CTYPE_STABLE;
case CAP_EVENTS_SUPPORTED:
pdu->params_len = htons(4);
pdu->params[1] = 2;
pdu->params[2] = AVRCP_EVENT_PLAYBACK_STATUS_CHANGED;
pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
- return 4;
+ return CTYPE_STABLE;
}
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_list_player_attributes(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_list_player_attributes(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
struct media_player *mp = control->mp;
unsigned int i;
if (len != 0) {
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
if (!mp)
pdu->params[0] = len;
pdu->params_len = htons(len + 1);
- return len + 1;
+ return CTYPE_STABLE;
}
-static int avrcp_handle_list_player_values(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_list_player_values(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
struct media_player *mp = control->mp;
pdu->params[0] = len;
pdu->params_len = htons(len + 1);
- return len + 1;
+ return CTYPE_STABLE;
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_get_element_attributes(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_element_attributes(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
uint64_t *identifier = (void *) &pdu->params[0];
pdu->params[0] = len;
pdu->params_len = htons(pos);
- return pos;
+ return CTYPE_STABLE;
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_get_current_player_value(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_current_player_value(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
struct media_player *mp = control->mp;
pdu->params[0] = len;
pdu->params_len = htons(2 * len + 1);
- return 2 * len + 1;
+ return CTYPE_STABLE;
}
error("No valid attributes in request");
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_set_player_value(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_set_player_value(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
if (len) {
pdu->params_len = 0;
- return 0;
+ return CTYPE_STABLE;
}
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_ct_battery_status(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_displayable_charset(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
+{
+ uint16_t len = ntohs(pdu->params_len);
+
+ if (len < 3) {
+ pdu->params_len = htons(1);
+ pdu->params[0] = E_INVALID_PARAM;
+ return CTYPE_REJECTED;
+ }
+
+ /*
+ * We acknowledge the commands, but we always use UTF-8 for
+ * encoding since CT is obliged to support it.
+ */
+ pdu->params_len = 0;
+ return CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_ct_battery_status(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
const char *valstr;
DBUS_TYPE_STRING, &valstr);
pdu->params_len = 0;
- return 0;
+ return CTYPE_STABLE;
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
-static int avrcp_handle_get_play_status(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+static uint8_t avrcp_handle_get_play_status(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
uint32_t elapsed;
uint8_t status;
if (len != 0) {
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
if (control->mp) {
pdu->params_len = htons(9);
- return 9;
+ return CTYPE_STABLE;
}
-static int avrcp_handle_register_notification(struct control *control,
+static uint8_t avrcp_handle_register_notification(struct control *control,
struct avrcp_spec_avc_pdu *pdu,
uint8_t transaction)
{
pdu->params_len = htons(len);
- return len;
+ return CTYPE_INTERIM;
err:
+ pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
- return -EINVAL;
+ return CTYPE_REJECTED;
}
+static struct pdu_handler {
+ uint8_t pdu_id;
+ uint8_t code;
+ uint8_t (*func) (struct control *control,
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction);
+} handlers[] = {
+ { AVRCP_GET_CAPABILITIES, CTYPE_STATUS,
+ avrcp_handle_get_capabilities },
+ { AVRCP_LIST_PLAYER_ATTRIBUTES, CTYPE_STATUS,
+ avrcp_handle_list_player_attributes },
+ { AVRCP_LIST_PLAYER_VALUES, CTYPE_STATUS,
+ avrcp_handle_list_player_values },
+ { AVRCP_GET_ELEMENT_ATTRIBUTES, CTYPE_STATUS,
+ avrcp_handle_get_element_attributes },
+ { AVRCP_GET_CURRENT_PLAYER_VALUE, CTYPE_STATUS,
+ avrcp_handle_get_current_player_value },
+ { AVRCP_SET_PLAYER_VALUE, CTYPE_CONTROL,
+ avrcp_handle_set_player_value },
+ { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, CTYPE_STATUS,
+ NULL },
+ { AVRCP_GET_PLAYER_VALUE_TEXT, CTYPE_STATUS,
+ NULL },
+ { AVRCP_DISPLAYABLE_CHARSET, CTYPE_STATUS,
+ avrcp_handle_displayable_charset },
+ { AVRCP_CT_BATTERY_STATUS, CTYPE_STATUS,
+ avrcp_handle_ct_battery_status },
+ { AVRCP_GET_PLAY_STATUS, CTYPE_STATUS,
+ avrcp_handle_get_play_status },
+ { AVRCP_REGISTER_NOTIFICATION, CTYPE_NOTIFY,
+ avrcp_handle_register_notification },
+ { },
+};
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avctp_header *avctp,
struct avrcp_header *avrcp,
int operand_count)
{
+ struct pdu_handler *handler;
struct avrcp_spec_avc_pdu *pdu = (void *) avrcp + AVRCP_HEADER_LENGTH;
uint32_t company_id = (pdu->company_id[0] << 16) |
(pdu->company_id[1] << 8) |
(pdu->company_id[2]);
- int len;
if (company_id != IEEEID_BTSIG ||
pdu->packet_type != AVCTP_PACKET_SINGLE) {
pdu->packet_type = 0;
pdu->rsvd = 0;
- if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
- pdu->params[0] = E_INVALID_COMMAND;
+ if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH)
goto err_metadata;
- }
-
- switch (pdu->pdu_id) {
- case AVRCP_GET_CAPABILITIES:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_get_capabilities(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_LIST_PLAYER_ATTRIBUTES:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
- len = avrcp_handle_list_player_attributes(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_LIST_PLAYER_VALUES:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_list_player_values(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_GET_ELEMENT_ATTRIBUTES:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_get_element_attributes(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_GET_CURRENT_PLAYER_VALUE:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_get_current_player_value(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_SET_PLAYER_VALUE:
- if (avrcp->code != CTYPE_CONTROL) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_set_player_value(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
- case AVRCP_GET_PLAYER_VALUE_TEXT:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
+ for (handler = handlers; handler; handler++) {
+ if (handler->pdu_id == pdu->pdu_id)
+ break;
+ }
- /*
- * As per sec. 5.2.5 of AVRCP 1.3 spec, this command is
- * expected to be used only for extended attributes, i.e.
- * custom attributes defined by the application. As we
- * currently don't have any such attribute, we respond with
- * invalid param id.
- */
- pdu->params[0] = E_INVALID_PARAM;
+ if (!handler || handler->code != avrcp->code) {
+ pdu->params[0] = E_INVALID_COMMAND;
goto err_metadata;
- case AVRCP_DISPLAYABLE_CHARSET:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- if (pdu->params[0] < 3) {
- pdu->params[0] = E_INVALID_PARAM;
- goto err_metadata;
- }
-
- /*
- * We acknowledge the commands, but we always use UTF-8 for
- * encoding since CT is obliged to support it.
- */
- pdu->params_len = 0;
- avrcp->code = CTYPE_STABLE;
- len = 0;
-
- break;
- case AVRCP_CT_BATTERY_STATUS:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_ct_battery_status(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_GET_PLAY_STATUS:
- if (avrcp->code != CTYPE_STATUS) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_get_play_status(control, pdu);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_STABLE;
-
- break;
- case AVRCP_REGISTER_NOTIFICATION:
- if (avrcp->code != CTYPE_NOTIFY) {
- pdu->params[0] = E_INVALID_COMMAND;
- goto err_metadata;
- }
-
- len = avrcp_handle_register_notification(control, pdu,
- avctp->transaction);
- if (len < 0)
- goto err_metadata;
-
- avrcp->code = CTYPE_INTERIM;
+ }
- break;
- default:
- /* Invalid pdu_id */
- pdu->params[0] = E_INVALID_COMMAND;
+ if (!handler->func) {
+ pdu->params[0] = E_INVALID_PARAM;
goto err_metadata;
}
- return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;
+ avrcp->code = handler->func(control, pdu, avctp->transaction);
+
+ return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH +
+ ntohs(pdu->params_len);
err_metadata:
- avrcp->code = CTYPE_REJECTED;
pdu->params_len = htons(1);
+ avrcp->code = CTYPE_REJECTED;
return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
}