mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 08:14:28 +08:00
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)
This commit is contained in:
parent
e316a77f5a
commit
4827d6b61f
@ -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;
|
||||
|
||||
@ -1493,6 +1513,11 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
|
||||
pdu->params[len++] = val;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user