Hi Bharat, On Fri, Aug 14, 2015 at 4:36 PM, Bharat Panda <bharat.panda@xxxxxxxxxxx> wrote: > Support added to handle Set Addressed Player PDU in TG role. > Send EVENT_ADDRESSED_PLAYER_CHANGED on SetAddressedPlayer > SUCCESS and follow procedure to reject all player specific events > currently registered with the player. > > Channel: 64 len 15 [PSM 23 mode 0] {chan 0} > AVCTP Control: Command: type 0x00 label 0 PID 0x110e > AV/C: Control: address 0x48 opcode 0x00 > Subunit: Panel > Opcode: Vendor Dependent > Company ID: 0x001958 > AVRCP: SetAddressedPlayer pt Single len 0x0002 > PlayerID: 0x0000 (0) > > Channel: 64 len 15 [PSM 23 mode 0] {chan 0} > AVCTP Control: Response: type 0x00 label 0 PID 0x110e > AV/C: Accepted: address 0x48 opcode 0x00 > Subunit: Panel > Opcode: Vendor Dependent > Company ID: 0x001958 > AVRCP: SetAddressedPlayer pt Single len 0x0002 > Status: 0x04 (Success) > > Channel: 64 len 18 [PSM 23 mode 0] {chan 0} > AVCTP Control: Response: type 0x00 label 0 PID 0x110e > AV/C: Changed: address 0x48 opcode 0x00 > Subunit: Panel > Opcode: Vendor Dependent > Company ID: 0x001958 > AVRCP: RegisterNotification pt Single len 0x0005 > EventID: 0x0b (EVENT_ADDRESSED_PLAYER_CHANGED) > PlayerID: 0x0000 (0) > UIDCounter: 0x0000 (0) > --- I went ahead and made some changes to make the code look simpler and reuse avrcp_player_event, please check if it still work as before. -- Luiz Augusto von Dentz
From d2cddd0a6d919649d1d396d249f306e931ea609b Mon Sep 17 00:00:00 2001 From: Bharat Panda <bharat.panda@xxxxxxxxxxx> Date: Fri, 14 Aug 2015 19:06:36 +0530 Subject: [PATCH BlueZ] audio/avrcp: Add Set Addressed Player support Support added to handle Set Addressed Player PDU in TG role. Send EVENT_ADDRESSED_PLAYER_CHANGED on SetAddressedPlayer SUCCESS and follow procedure to reject all player specific events currently registered with the player. Channel: 64 len 15 [PSM 23 mode 0] {chan 0} AVCTP Control: Command: type 0x00 label 0 PID 0x110e AV/C: Control: address 0x48 opcode 0x00 Subunit: Panel Opcode: Vendor Dependent Company ID: 0x001958 AVRCP: SetAddressedPlayer pt Single len 0x0002 PlayerID: 0x0000 (0) Channel: 64 len 15 [PSM 23 mode 0] {chan 0} AVCTP Control: Response: type 0x00 label 0 PID 0x110e AV/C: Accepted: address 0x48 opcode 0x00 Subunit: Panel Opcode: Vendor Dependent Company ID: 0x001958 AVRCP: SetAddressedPlayer pt Single len 0x0002 Status: 0x04 (Success) Channel: 64 len 18 [PSM 23 mode 0] {chan 0} AVCTP Control: Response: type 0x00 label 0 PID 0x110e AV/C: Changed: address 0x48 opcode 0x00 Subunit: Panel Opcode: Vendor Dependent Company ID: 0x001958 AVRCP: RegisterNotification pt Single len 0x0005 EventID: 0x0b (EVENT_ADDRESSED_PLAYER_CHANGED) PlayerID: 0x0000 (0) UIDCounter: 0x0000 (0) --- profiles/audio/avrcp.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index d66f670..76e89af 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -103,6 +103,7 @@ #define AVRCP_REQUEST_CONTINUING 0x40 #define AVRCP_ABORT_CONTINUING 0x41 #define AVRCP_SET_ABSOLUTE_VOLUME 0x50 +#define AVRCP_SET_ADDRESSED_PLAYER 0x60 #define AVRCP_SET_BROWSED_PLAYER 0x70 #define AVRCP_GET_FOLDER_ITEMS 0x71 #define AVRCP_CHANGE_PATH 0x72 @@ -204,8 +205,10 @@ struct avrcp_player { uint64_t uid; uint16_t uid_counter; bool browsed; + bool addressed; uint8_t *features; char *path; + guint changed_id; struct pending_list_items *p; char *change_path; @@ -627,6 +630,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, { uint8_t buf[AVRCP_HEADER_LENGTH + 9]; struct avrcp_header *pdu = (void *) buf; + uint8_t code; uint16_t size; GSList *l; int attr; @@ -640,10 +644,19 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, set_company_id(pdu->company_id, IEEEID_BTSIG); pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; - pdu->params[0] = id; DBG("id=%u", id); + if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) { + code = AVC_CTYPE_REJECTED; + size = 1; + pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED; + goto done; + } + + code = AVC_CTYPE_CHANGED; + pdu->params[0] = id; + switch (id) { case AVRCP_EVENT_STATUS_CHANGED: size = 2; @@ -674,6 +687,11 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, pdu->params[size++] = attr; pdu->params[size++] = val; break; + case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: + size = 5; + memcpy(&pdu->params[1], &player->id, sizeof(uint16_t)); + memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t)); + break; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: size = 1; break; @@ -682,6 +700,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, return; } +done: pdu->params_len = htons(size); for (l = player->sessions; l; l = l->next) { @@ -693,8 +712,9 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, err = avctp_send_vendordep(session->conn, session->transaction_events[id], - AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL, + code, AVC_SUBUNIT_PANEL, buf, size + AVRCP_HEADER_LENGTH); + if (err < 0) continue; @@ -1494,6 +1514,11 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, } break; + case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: + len = 5; + memcpy(&pdu->params[1], &player->id, sizeof(uint16_t)); + memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t)); + break; case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: len = 1; break; @@ -1617,6 +1642,90 @@ err: return AVC_CTYPE_REJECTED; } +static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id) +{ + struct avrcp_server *server = session->server; + GSList *l; + + for (l = server->players; l; l = l->next) { + struct avrcp_player *player = l->data; + + if (player->id == id) + return player; + } + + return NULL; +} + +static gboolean notify_addressed_player_changed(gpointer user_data) +{ + struct avrcp_player *player = user_data; + uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED, + AVRCP_EVENT_TRACK_CHANGED, + AVRCP_EVENT_TRACK_REACHED_START, + AVRCP_EVENT_TRACK_REACHED_END, + AVRCP_EVENT_SETTINGS_CHANGED, + AVRCP_EVENT_PLAYBACK_POS_CHANGED + }; + uint8_t i; + + avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL); + + /* + * TG shall complete all player specific + * notifications with AV/C C-Type REJECTED + * with error code as Addressed Player Changed. + */ + for (i = 0; i < sizeof(events); i++) + avrcp_player_event(player, events[i], NULL); + + player->changed_id = 0; + + return FALSE; +} + +static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session, + struct avrcp_header *pdu, + uint8_t transaction) +{ + struct avrcp_player *player; + uint16_t len = ntohs(pdu->params_len); + uint16_t player_id = 0; + uint8_t status; + + if (len < 1) { + status = AVRCP_STATUS_INVALID_PARAM; + goto err; + } + + player_id = bt_get_be16(&pdu->params[0]); + player = find_tg_player(session, player_id); + pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; + + if (player) { + player->addressed = true; + status = AVRCP_STATUS_SUCCESS; + pdu->params_len = htons(len); + pdu->params[0] = status; + } else { + status = AVRCP_STATUS_INVALID_PLAYER_ID; + goto err; + } + + /* Don't emit player changed immediately since PTS expect the + * response of SetAddressedPlayer before the event. + */ + player->changed_id = g_idle_add(notify_addressed_player_changed, + player); + + return AVC_CTYPE_ACCEPTED; + +err: + pdu->params_len = htons(sizeof(status)); + pdu->params[0] = status; + return AVC_CTYPE_REJECTED; +} + static const struct control_pdu_handler control_handlers[] = { { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS, avrcp_handle_get_capabilities }, @@ -1648,6 +1757,8 @@ static const struct control_pdu_handler control_handlers[] = { avrcp_handle_request_continuing }, { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL, avrcp_handle_abort_continuing }, + { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL, + avrcp_handle_set_addressed_player }, { }, }; @@ -3029,6 +3140,9 @@ static void player_destroy(gpointer data) if (player->destroy) player->destroy(player->user_data); + if (player->changed_id > 0) + g_source_remove(player->changed_id); + g_slist_free(player->sessions); g_free(player->path); g_free(player->change_path); @@ -3521,6 +3635,7 @@ static void target_init(struct avrcp *session) return; session->supported_events |= + (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) | (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED); /* Only check capabilities if controller is not supported */ -- 2.4.3