* [PATCH BlueZ 2/6] AVRCP: Avoid repeating command due to changed event
2013-02-01 3:58 [PATCH BlueZ 1/6] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
@ 2013-02-01 3:58 ` Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 3/6] AVRCP: Register to addressed player changed event if supported Luiz Augusto von Dentz
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 3:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Currently we are repeating the same command twice for notification, first
for changed and then latter for interim response.
To avoid this the code now just do it for interim responses, changed
responses just trigger another registration.
---
profiles/audio/avrcp.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 728ba91..0a173bc 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1877,6 +1877,12 @@ static gboolean avrcp_handle_event(struct avctp *conn,
event = pdu->params[0];
+ if (code == AVC_CTYPE_CHANGED) {
+ session->registered_events ^= (1 << event);
+ avrcp_register_notification(session, event);
+ return FALSE;
+ }
+
switch (event) {
case AVRCP_EVENT_VOLUME_CHANGED:
value = pdu->params[1] & 0x7F;
@@ -1900,12 +1906,8 @@ static gboolean avrcp_handle_event(struct avctp *conn,
break;
case AVRCP_EVENT_TRACK_CHANGED:
- mp = player->user_data;
-
avrcp_get_element_attributes(session);
-
- if (code == AVC_CTYPE_CHANGED)
- avrcp_get_play_status(session);
+ avrcp_get_play_status(session);
break;
@@ -1932,12 +1934,6 @@ static gboolean avrcp_handle_event(struct avctp *conn,
break;
}
- if (code == AVC_CTYPE_CHANGED) {
- session->registered_events ^= (1 << event);
- avrcp_register_notification(session, event);
- return FALSE;
- }
-
session->registered_events |= (1 << event);
return TRUE;
--
1.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH BlueZ 3/6] AVRCP: Register to addressed player changed event if supported
2013-02-01 3:58 [PATCH BlueZ 1/6] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 2/6] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
@ 2013-02-01 3:58 ` Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 4/6] AVRCP: Get player list " Luiz Augusto von Dentz
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 3:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This enables registering for addressed player changed event and handle
the interim response setting the player id and uid counter to the current
player.
---
profiles/audio/avrcp.c | 14 ++++++++++++++
profiles/audio/avrcp.h | 15 ++++++++-------
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 0a173bc..8187ddf 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -176,6 +176,8 @@ struct pending_pdu {
struct avrcp_player {
struct avrcp_server *server;
GSList *sessions;
+ uint16_t id;
+ uint16_t uid_counter;
struct avrcp_player_cb *cb;
void *user_data;
@@ -1869,6 +1871,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t event;
uint8_t value;
uint8_t count;
+ uint16_t id;
const char *curval, *strval;
int i;
@@ -1932,6 +1935,16 @@ static gboolean avrcp_handle_event(struct avctp *conn,
}
break;
+
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ id = bt_get_be16(&pdu->params[1]);
+
+ if (player->id == id)
+ break;
+
+ player->id = id;
+ player->uid_counter = bt_get_be16(&pdu->params[3]);
+ break;
}
session->registered_events |= (1 << event);
@@ -2112,6 +2125,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
case AVRCP_EVENT_SETTINGS_CHANGED:
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
avrcp_register_notification(session, event);
break;
}
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index b2c0d61..3799da1 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -69,13 +69,14 @@
#define AVRCP_PLAY_STATUS_ERROR 0xFF
/* Notification events */
-#define AVRCP_EVENT_STATUS_CHANGED 0x01
-#define AVRCP_EVENT_TRACK_CHANGED 0x02
-#define AVRCP_EVENT_TRACK_REACHED_END 0x03
-#define AVRCP_EVENT_TRACK_REACHED_START 0x04
-#define AVRCP_EVENT_SETTINGS_CHANGED 0x08
-#define AVRCP_EVENT_VOLUME_CHANGED 0x0d
-#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED
+#define AVRCP_EVENT_STATUS_CHANGED 0x01
+#define AVRCP_EVENT_TRACK_CHANGED 0x02
+#define AVRCP_EVENT_TRACK_REACHED_END 0x03
+#define AVRCP_EVENT_TRACK_REACHED_START 0x04
+#define AVRCP_EVENT_SETTINGS_CHANGED 0x08
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b
+#define AVRCP_EVENT_VOLUME_CHANGED 0x0d
+#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED
struct avrcp_player_cb {
GList *(*list_settings) (void *user_data);
--
1.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH BlueZ 4/6] AVRCP: Get player list if supported
2013-02-01 3:58 [PATCH BlueZ 1/6] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 2/6] AVRCP: Avoid repeating command due to changed event Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 3/6] AVRCP: Register to addressed player changed event if supported Luiz Augusto von Dentz
@ 2013-02-01 3:58 ` Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 5/6] AVRCP: Split event handing to its own functions Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 6/6] AVRCP: Set addressed player as browsed player Luiz Augusto von Dentz
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 3:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
If addressed player changed is supported get the list of players
---
profiles/audio/avrcp.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/player.c | 119 +++++++++++++++++++++++++++++++++++++++
profiles/audio/player.h | 4 ++
3 files changed, 270 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 8187ddf..793b5cf 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -99,6 +99,7 @@
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
+#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_GENERAL_REJECT 0xA0
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
@@ -1859,6 +1860,151 @@ static void avrcp_get_element_attributes(struct avrcp *session)
session);
}
+static const char *type_to_string(uint8_t type)
+{
+ switch (type & 0x0F) {
+ case 0x01:
+ return "Audio";
+ case 0x02:
+ return "Video";
+ case 0x03:
+ return "Audio, Video";
+ case 0x04:
+ return "Audio Broadcasting";
+ case 0x05:
+ return "Audio, Audio Broadcasting";
+ case 0x06:
+ return "Video, Audio Broadcasting";
+ case 0x07:
+ return "Audio, Video, Audio Broadcasting";
+ case 0x08:
+ return "Video Broadcasting";
+ case 0x09:
+ return "Audio, Video Broadcasting";
+ case 0x0A:
+ return "Video, Video Broadcasting";
+ case 0x0B:
+ return "Audio, Video, Video Broadcasting";
+ case 0x0C:
+ return "Audio Broadcasting, Video Broadcasting";
+ case 0x0D:
+ return "Audio, Audio Broadcasting, Video Broadcasting";
+ case 0x0E:
+ return "Video, Audio Broadcasting, Video Broadcasting";
+ case 0x0F:
+ return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+ }
+
+ return "None";
+}
+
+static const char *subtype_to_string(uint32_t subtype)
+{
+ switch (subtype & 0x03) {
+ case 0x01:
+ return "Audio Book";
+ case 0x02:
+ return "Podcast";
+ case 0x03:
+ return "Audio Book, Podcast";
+ }
+
+ return "None";
+}
+
+static void avrcp_parse_media_player_item(struct avrcp *session,
+ uint8_t *operands, uint16_t len)
+{
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ uint16_t id;
+ uint32_t subtype;
+ const char *curval, *strval;
+ uint64_t features[2];
+ char name[255];
+
+ if (len < 28)
+ return;
+
+ id = bt_get_be16(&operands[0]);
+
+ if (player->id != id)
+ return;
+
+ media_player_set_type(mp, type_to_string(operands[2]));
+
+ subtype = bt_get_be32(&operands[3]);
+
+ media_player_set_subtype(mp, subtype_to_string(subtype));
+
+ curval = media_player_get_status(mp);
+ strval = status_to_string(operands[7]);
+
+ if (g_strcmp0(curval, strval) != 0) {
+ media_player_set_status(mp, strval);
+ avrcp_get_play_status(session);
+ }
+
+ features[0] = bt_get_be64(&operands[8]);
+ features[1] = bt_get_be64(&operands[16]);
+
+ media_player_set_features(mp, features);
+
+ if (operands[26] == 0)
+ return;
+
+ memcpy(name, &operands[27], operands[26]);
+
+ media_player_set_name(mp, name);
+}
+
+static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
+ uint8_t *operands,
+ size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ uint16_t count;
+ int i;
+
+ if (operands[3] != AVRCP_STATUS_SUCCESS || operand_count < 5)
+ return FALSE;
+
+ count = bt_get_be16(&operands[6]);
+
+ for (i = 8; count; count--) {
+ uint8_t type;
+ uint16_t len;
+
+ type = operands[i++];
+ len = bt_get_be16(&operands[i]);
+ i += 2;
+
+ if (type != 0x01) {
+ i += len;
+ continue;
+ }
+
+ avrcp_parse_media_player_item(session, &operands[i], len);
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_media_player_list(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10];
+ struct avrcp_browsing_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+ pdu->param_len = htons(10);
+
+ avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+ avrcp_get_media_player_list_rsp, session);
+}
+
static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1944,6 +2090,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
player->id = id;
player->uid_counter = bt_get_be16(&pdu->params[3]);
+ avrcp_get_media_player_list(session);
break;
}
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index fb750fd..b4efa70 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -57,6 +57,10 @@ struct pending_req {
struct media_player {
char *device; /* Device path */
+ char *name; /* Player name */
+ char *type; /* Player type */
+ char *subtype; /* Player subtype */
+ uint64_t features[2]; /* Player features */
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
GHashTable *track; /* Player current track */
@@ -272,6 +276,72 @@ static gboolean get_device(const GDBusPropertyTable *property,
return TRUE;
}
+static gboolean name_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_player *mp = data;
+
+ return mp->name != NULL;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->name == NULL)
+ return FALSE;
+
+ DBG("%s", mp->name);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name);
+
+ return TRUE;
+}
+
+static gboolean type_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_player *mp = data;
+
+ return mp->type != NULL;
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->type == NULL)
+ return FALSE;
+
+ DBG("%s", mp->type);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type);
+
+ return TRUE;
+}
+
+static gboolean subtype_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_player *mp = data;
+
+ return mp->subtype != NULL;
+}
+
+static gboolean get_subtype(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->subtype == NULL)
+ return FALSE;
+
+ DBG("%s", mp->subtype);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype);
+
+ return TRUE;
+}
+
static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -410,6 +480,12 @@ static const GDBusSignalTable media_player_signals[] = {
};
static const GDBusPropertyTable media_player_properties[] = {
+ { "Name", "s", get_name, NULL, name_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Type", "s", get_type, NULL, type_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Subtype", "s", get_subtype, NULL, subtype_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Position", "u", get_position, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Status", "s", get_status, NULL, status_exists,
@@ -452,6 +528,9 @@ void media_player_destroy(struct media_player *mp)
g_free(mp->status);
g_free(mp->path);
g_free(mp->device);
+ g_free(mp->subtype);
+ g_free(mp->type);
+ g_free(mp->name);
g_free(mp);
}
@@ -618,6 +697,46 @@ void media_player_set_metadata(struct media_player *mp, const char *key,
g_hash_table_replace(mp->track, g_strdup(key), value);
}
+void media_player_set_type(struct media_player *mp, const char *type)
+{
+ DBG("%s", type);
+
+ mp->type = g_strdup(type);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ "Type");
+}
+
+void media_player_set_subtype(struct media_player *mp, const char *subtype)
+{
+ DBG("%s", subtype);
+
+ mp->subtype = g_strdup(subtype);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ "Subtype");
+}
+
+void media_player_set_name(struct media_player *mp, const char *name)
+{
+ DBG("%s", name);
+
+ mp->name = g_strdup(name);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ "Name");
+}
+
+void media_player_set_features(struct media_player *mp, uint64_t *features)
+{
+ DBG("0x%08zx %08zx", features[0], features[1]);
+
+ memcpy(features, mp->features, sizeof(mp->features));
+}
+
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
void *user_data)
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index fec1d06..b546ba7 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -47,6 +47,10 @@ const char *media_player_get_status(struct media_player *mp);
void media_player_set_status(struct media_player *mp, const char *status);
void media_player_set_metadata(struct media_player *mp, const char *key,
void *data, size_t len);
+void media_player_set_type(struct media_player *mp, const char *type);
+void media_player_set_subtype(struct media_player *mp, const char *subtype);
+void media_player_set_features(struct media_player *mp, uint64_t *features);
+void media_player_set_name(struct media_player *mp, const char *name);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
--
1.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH BlueZ 5/6] AVRCP: Split event handing to its own functions
2013-02-01 3:58 [PATCH BlueZ 1/6] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
` (2 preceding siblings ...)
2013-02-01 3:58 ` [PATCH BlueZ 4/6] AVRCP: Get player list " Luiz Augusto von Dentz
@ 2013-02-01 3:58 ` Luiz Augusto von Dentz
2013-02-01 3:58 ` [PATCH BlueZ 6/6] AVRCP: Set addressed player as browsed player Luiz Augusto von Dentz
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 3:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
profiles/audio/avrcp.c | 141 +++++++++++++++++++++++++++++--------------------
1 file changed, 85 insertions(+), 56 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 793b5cf..d1eb8ce 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2005,21 +2005,94 @@ static void avrcp_get_media_player_list(struct avrcp *session)
avrcp_get_media_player_list_rsp, session);
}
+static void avrcp_volume_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ struct avrcp_player *player = session->player;
+ uint8_t volume;
+
+ if (player == NULL)
+ return;
+
+ volume = pdu->params[1] & 0x7F;
+
+ player->cb->set_volume(volume, session->dev, player->user_data);
+}
+
+static void avrcp_status_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ uint8_t value;
+ const char *curval, *strval;
+
+ value = pdu->params[1];
+
+ curval = media_player_get_status(mp);
+ strval = status_to_string(value);
+
+ if (g_strcmp0(curval, strval) == 0)
+ return;
+
+ media_player_set_status(mp, strval);
+ avrcp_get_play_status(session);
+}
+
+static void avrcp_track_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ avrcp_get_element_attributes(session);
+ avrcp_get_play_status(session);
+
+}
+
+static void avrcp_setting_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ uint8_t count = pdu->params[1];
+ int i;
+
+ for (i = 2; count > 0; count--, i += 2) {
+ const char *key;
+ const char *value;
+
+ key = attr_to_str(pdu->params[i]);
+ if (key == NULL)
+ continue;
+
+ value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+ if (value == NULL)
+ continue;
+
+ media_player_set_setting(mp, key, value);
+ }
+}
+
+static void avrcp_addressed_player_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ struct avrcp_player *player = session->player;
+ uint16_t id = bt_get_be16(&pdu->params[1]);
+
+ if (player->id == id)
+ return;
+
+ player->id = id;
+ player->uid_counter = bt_get_le16(&pdu->params[3]);
+ avrcp_get_media_player_list(session);
+}
+
static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
{
struct avrcp *session = user_data;
- struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
- struct media_player *mp;
uint8_t event;
- uint8_t value;
- uint8_t count;
- uint16_t id;
- const char *curval, *strval;
- int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
@@ -2034,63 +2107,19 @@ static gboolean avrcp_handle_event(struct avctp *conn,
switch (event) {
case AVRCP_EVENT_VOLUME_CHANGED:
- value = pdu->params[1] & 0x7F;
-
- if (player)
- player->cb->set_volume(value, session->dev,
- player->user_data);
-
+ avrcp_volume_changed(session, pdu);
break;
case AVRCP_EVENT_STATUS_CHANGED:
- mp = player->user_data;
- value = pdu->params[1];
-
- curval = media_player_get_status(mp);
- strval = status_to_string(value);
-
- if (g_strcmp0(curval, strval) != 0) {
- media_player_set_status(mp, strval);
- avrcp_get_play_status(session);
- }
-
+ avrcp_status_changed(session, pdu);
break;
case AVRCP_EVENT_TRACK_CHANGED:
- avrcp_get_element_attributes(session);
- avrcp_get_play_status(session);
-
+ avrcp_track_changed(session, pdu);
break;
-
case AVRCP_EVENT_SETTINGS_CHANGED:
- mp = player->user_data;
- count = pdu->params[1];
-
- for (i = 2; count > 0; count--, i += 2) {
- const char *key;
- const char *value;
-
- key = attr_to_str(pdu->params[i]);
- if (key == NULL)
- continue;
-
- value = attrval_to_str(pdu->params[i],
- pdu->params[i + 1]);
- if (value == NULL)
- continue;
-
- media_player_set_setting(mp, key, value);
- }
-
+ avrcp_setting_changed(session, pdu);
break;
-
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- id = bt_get_be16(&pdu->params[1]);
-
- if (player->id == id)
- break;
-
- player->id = id;
- player->uid_counter = bt_get_be16(&pdu->params[3]);
- avrcp_get_media_player_list(session);
+ avrcp_addressed_player_changed(session, pdu);
break;
}
--
1.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH BlueZ 6/6] AVRCP: Set addressed player as browsed player
2013-02-01 3:58 [PATCH BlueZ 1/6] media-api: Add org.bluez.MediaFolder1 Luiz Augusto von Dentz
` (3 preceding siblings ...)
2013-02-01 3:58 ` [PATCH BlueZ 5/6] AVRCP: Split event handing to its own functions Luiz Augusto von Dentz
@ 2013-02-01 3:58 ` Luiz Augusto von Dentz
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2013-02-01 3:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This send SetBrowsedPlayer after getting the player details.
---
profiles/audio/avrcp.c | 73 +++++++++++++++++++++++--
profiles/audio/player.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/player.h | 2 +
3 files changed, 209 insertions(+), 5 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index d1eb8ce..00eeea1 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -99,6 +99,7 @@
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
+#define AVRCP_SET_BROWSED_PLAYER 0x70
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_GENERAL_REJECT 0xA0
@@ -1912,6 +1913,68 @@ static const char *subtype_to_string(uint32_t subtype)
return "None";
}
+static gboolean avrcp_set_browsed_player_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 media_player *mp = player->user_data;
+ uint32_t items;
+ char **folders, *path;
+ uint8_t depth, count;
+ int i;
+
+ if (operands[3] != AVRCP_STATUS_SUCCESS || operand_count < 13)
+ return FALSE;
+
+ player->uid_counter = bt_get_be16(&operands[4]);
+
+ items = bt_get_be32(&operands[6]);
+
+ depth = operands[13];
+
+ folders = g_new0(char *, depth + 1);
+
+ for (i = 14, count = 0; count < depth; count++) {
+ char *part;
+ uint8_t len;
+
+ len = operands[i++];
+ part = g_memdup(&operands[i], len);
+ i += len;
+ folders[count] = part;
+ }
+
+ path = g_build_pathv("/", folders);
+ g_strfreev(folders);
+
+ media_player_set_folder(mp, path, items);
+
+ g_free(path);
+
+ return FALSE;
+}
+
+static void avrcp_set_browsed_player(struct avrcp *session,
+ struct avrcp_player *player)
+{
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 2];
+ struct avrcp_browsing_header *pdu = (void *) buf;
+ uint16_t id;
+
+ memset(buf, 0, sizeof(buf));
+
+ pdu->pdu_id = AVRCP_SET_BROWSED_PLAYER;
+ id = htons(player->id);
+ memcpy(pdu->params, &id, 2);
+ pdu->param_len = htons(2);
+
+ avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+ avrcp_set_browsed_player_rsp, session);
+}
+
static void avrcp_parse_media_player_item(struct avrcp *session,
uint8_t *operands, uint16_t len)
{
@@ -1950,12 +2013,12 @@ static void avrcp_parse_media_player_item(struct avrcp *session,
media_player_set_features(mp, features);
- if (operands[26] == 0)
- return;
-
- memcpy(name, &operands[27], operands[26]);
+ if (operands[26] != 0) {
+ memcpy(name, &operands[27], operands[26]);
+ media_player_set_name(mp, name);
+ }
- media_player_set_name(mp, name);
+ avrcp_set_browsed_player(session, player);
}
static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index b4efa70..2f4ea83 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -43,6 +43,7 @@
#include "error.h"
#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
struct player_callback {
const struct media_player_callback *cbs;
@@ -55,12 +56,18 @@ struct pending_req {
const char *value;
};
+struct media_folder {
+ char *name; /* Folder name */
+ uint32_t items; /* Number of items */
+};
+
struct media_player {
char *device; /* Device path */
char *name; /* Player name */
char *type; /* Player type */
char *subtype; /* Player subtype */
uint64_t features[2]; /* Player features */
+ struct media_folder *folder; /* Player currenct folder */
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
GHashTable *track; /* Player current track */
@@ -505,6 +512,104 @@ static const GDBusPropertyTable media_player_properties[] = {
{ }
};
+static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static DBusMessage *media_folder_list_items(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+
+static DBusMessage *media_folder_change_folder(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return btd_error_failed(msg, strerror(ENOTSUP));
+}
+
+static gboolean folder_name_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->folder == NULL)
+ return FALSE;
+
+ return mp->folder->name != NULL;
+}
+
+static gboolean get_folder_name(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->folder == NULL || mp->folder->name == NULL)
+ return FALSE;
+
+ DBG("%s", mp->folder->name);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &mp->folder->name);
+
+ return TRUE;
+}
+
+static gboolean items_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct media_player *mp = data;
+
+ return mp->folder != NULL;
+}
+
+static gboolean get_items(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_player *mp = data;
+
+ if (mp->folder == NULL)
+ return FALSE;
+
+ DBG("%u", mp->folder->items);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+ &mp->folder->items);
+
+ return TRUE;
+}
+
+static const GDBusMethodTable media_folder_methods[] = {
+ { GDBUS_EXPERIMENTAL_METHOD("Search",
+ GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
+ GDBUS_ARGS({ "folder", "o" }),
+ media_folder_search) },
+ { GDBUS_EXPERIMENTAL_METHOD("ListItems",
+ GDBUS_ARGS({ "filter", "a{sv}" }),
+ GDBUS_ARGS({ "items", "a{oa{sv}}" }),
+ media_folder_list_items) },
+ { GDBUS_EXPERIMENTAL_METHOD("ChangeFolder",
+ GDBUS_ARGS({ "folder", "o" }), NULL,
+ media_folder_change_folder) },
+ { }
+};
+
+static const GDBusPropertyTable media_folder_properties[] = {
+ { "Name", "s", get_folder_name, NULL, folder_name_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "NumberOfItems", "u", get_items, NULL, items_exists,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { }
+};
+
+static void media_folder_destroy(struct media_folder *folder)
+{
+ g_free(folder->name);
+ g_free(folder);
+}
+
void media_player_destroy(struct media_player *mp)
{
DBG("%s", mp->path);
@@ -521,6 +626,13 @@ void media_player_destroy(struct media_player *mp)
if (mp->process_id > 0)
g_source_remove(mp->process_id);
+ if (mp->folder) {
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ mp->path,
+ MEDIA_FOLDER_INTERFACE);
+ media_folder_destroy(mp->folder);
+ }
+
g_slist_free_full(mp->pending, g_free);
g_timer_destroy(mp->progress);
@@ -730,6 +842,33 @@ void media_player_set_name(struct media_player *mp, const char *name)
"Name");
}
+void media_player_set_folder(struct media_player *mp, const char *path,
+ uint32_t items)
+{
+ struct media_folder *folder;
+
+ DBG("%s items %u", path, items);
+
+ if (mp->folder != NULL)
+ media_folder_destroy(mp->folder);
+
+ folder = g_new0(struct media_folder, 1);
+ folder->name = g_strdup(path);
+ folder->items = items;
+ mp->folder = folder;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ mp->path, MEDIA_FOLDER_INTERFACE,
+ media_folder_methods,
+ NULL,
+ media_folder_properties, mp, NULL)) {
+ error("D-Bus failed to register %s on %s path",
+ MEDIA_FOLDER_INTERFACE, mp->path);
+ media_folder_destroy(mp->folder);
+ mp->folder = NULL;
+ }
+}
+
void media_player_set_features(struct media_player *mp, uint64_t *features)
{
DBG("0x%08zx %08zx", features[0], features[1]);
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index b546ba7..1ac9800 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -51,6 +51,8 @@ void media_player_set_type(struct media_player *mp, const char *type);
void media_player_set_subtype(struct media_player *mp, const char *subtype);
void media_player_set_features(struct media_player *mp, uint64_t *features);
void media_player_set_name(struct media_player *mp, const char *name);
+void media_player_set_folder(struct media_player *mp, const char *path,
+ uint32_t items);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
--
1.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread