summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2015-07-31 20:08:16 +0300
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2016-03-11 11:39:19 +0200
commit96174d78762c4ad2a7eb83fc5c9784dea10122f5 (patch)
tree72d49683e94301255205c5600668b5b97fee5917
parent1a3d320f122b696792dcda0389031ace1dd55b28 (diff)
downloadbluez-96174d78762c4ad2a7eb83fc5c9784dea10122f5.tar.gz
audio/avrcp: Fix not handling Addressed Player Changed error
Some notification are completed in case the addressed player changes: 'On completion of the Addressed Player Changed notification the TG shall complete all player specific notifications with AV/C C-Type REJECTED with error code Addressed Player Changed.' Because reject only has the error code not the event it is necessary to lookup by transaction to find out which event was completed thus the transaction needs to be added to the avctp_rsp_cb callback.
-rw-r--r--profiles/audio/avctp.c20
-rw-r--r--profiles/audio/avctp.h5
-rw-r--r--profiles/audio/avrcp.c56
3 files changed, 53 insertions, 28 deletions
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 22bf35b06..2a43d32f1 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -293,8 +293,9 @@ static GSList *servers = NULL;
static void auth_cb(DBusError *derr, void *user_data);
static gboolean process_queue(gpointer user_data);
static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data);
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
@@ -706,8 +707,8 @@ static void control_req_destroy(void *data)
if (p->err == 0 || req->func == NULL)
goto done;
- req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
- req->user_data);
+ req->func(session, AVC_CTYPE_REJECTED, req->subunit, p->transaction,
+ NULL, 0, req->user_data);
done:
g_free(req->operands);
@@ -829,9 +830,9 @@ static void control_response(struct avctp_channel *control,
continue;
if (req->func && req->func(control->session, avc->code,
- avc->subunit_type,
- operands, operand_count,
- req->user_data))
+ avc->subunit_type, p->transaction,
+ operands, operand_count,
+ req->user_data))
return;
control->processed = g_slist_remove(control->processed, p);
@@ -1724,8 +1725,9 @@ static bool set_pressed(struct avctp *session, uint8_t op)
}
static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data)
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
{
if (code != AVC_CTYPE_ACCEPTED)
return FALSE;
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 6c19ce42b..68a273561 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -132,8 +132,9 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data);
typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data);
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
uint8_t *operands, size_t operand_count,
void *user_data);
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 136f4a463..68cc67753 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2076,8 +2076,8 @@ static const char *status_to_string(uint8_t status)
}
}
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -2140,8 +2140,8 @@ static const char *status_to_str(uint8_t status)
}
}
-static gboolean avrcp_player_value_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -2210,8 +2210,8 @@ static void avrcp_get_current_player_value(struct avrcp *session,
static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
+ uint8_t transaction, uint8_t *operands,
+ size_t operand_count, void *user_data)
{
uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
struct avrcp *session = user_data;
@@ -2297,6 +2297,7 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player,
static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
+ uint8_t transaction,
uint8_t *operands,
size_t operand_count,
void *user_data)
@@ -3545,8 +3546,8 @@ static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu)
player->uid_counter = get_be16(&pdu->params[1]);
}
-static gboolean avrcp_handle_event(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -3554,16 +3555,30 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
- if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) ||
- pdu == NULL)
+ if (!pdu)
+ return FALSE;
+
+ if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
+ if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
+ code == AVC_CTYPE_REJECTED) {
+ int i;
+
+ /* Lookup event by transaction */
+ for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
+ if (session->transaction_events[i] ==
+ transaction) {
+ event = i;
+ goto changed;
+ }
+ }
+ }
return FALSE;
+ }
event = pdu->params[0];
if (code == AVC_CTYPE_CHANGED) {
- session->registered_events ^= (1 << event);
- avrcp_register_notification(session, event);
- return FALSE;
+ goto changed;
}
switch (event) {
@@ -3594,8 +3609,15 @@ static gboolean avrcp_handle_event(struct avctp *conn,
}
session->registered_events |= (1 << event);
+ session->transaction_events[event] = transaction;
return TRUE;
+
+changed:
+ session->registered_events ^= (1 << event);
+ session->transaction_events[event] = 0;
+ avrcp_register_notification(session, event);
+ return FALSE;
}
static void avrcp_register_notification(struct avrcp *session, uint8_t event)
@@ -3627,8 +3649,8 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
-static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -4142,8 +4164,8 @@ void avrcp_unregister_player(struct avrcp_player *player)
player_destroy(player);
}
-static gboolean avrcp_handle_set_volume(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{