2011-08-10 13:06:14

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 00/22] Implement AVRCP 1.3 for TG role

Changes from previous version:
- Removal of patch to use C99 instead of gcc extension
- Use of heap instead of relying on VLA to use memory from stack
- Coding style fixes
- SettingChanged signal and ChangeSetting method were renamed to
PropertyChanged and SetProperty in order to use the common infrastructure
already present in BlueZ.


regads,
Lucas De Marchi



Lucas De Marchi (22):
audio: move interface declarations to their headers
avrcp: add skeleton of MediaPlayer interface
avrcp: implement SetProperty() method of MediaPlayer
avrcp: implement ChangePlayback() method
avrcp: implement ChangeTrack() method
avrcp: handle query for company ids
avrcp: handle ListPlayerApplicationSettingAttributes pdu
avrcp: handle ListPlayerApplicationSettingValues pdu
avrcp: handle GetCurrentPlayerAplicationSettingValue pdu
avrcp: handle SetPlayerApplicationSettingValue pdu
avrcp: handle commands for future extension
avrcp: handle InformDisplayableCharacterSet pdu
avrcp: handle InformBatteryStatusOfCT pdu
avrcp: handle GetPlayStatus pdu
avrcp: handle RegisterNotification pdu
avrcp: handle query for supported events
avrcp: handle GetElementAttributes pdu
avrcp: send response for registered events
avrcp: change TG record to use version 1.3
avrcp: update copyright
Add script to test MediaPlayer interface
Update Control documentation

audio/control.c | 1399 +++++++++++++++++++++++++++++++++++++++++++++++-
audio/control.h | 4 +-
audio/device.h | 4 -
audio/manager.c | 11 +-
audio/manager.h | 1 +
audio/unix.c | 1 +
doc/control-api.txt | 94 ++--
test/test-media-player | 108 ++++
8 files changed, 1562 insertions(+), 60 deletions(-)
create mode 100755 test/test-media-player

--
1.7.6



2011-08-11 11:23:36

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH v3 18/22] avrcp: send response for registered events

Hi Luiz,

On Thu, Aug 11, 2011 at 5:15 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> Hi Lucas,
>
> On Wed, Aug 10, 2011 at 4:06 PM, Lucas De Marchi
> <[email protected]> wrote:
>> ?static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint32_t *elapsed, uint32_t *track_len)
>> ?{
>> @@ -754,6 +823,13 @@ static void mp_set_playback_status(struct control *control, uint8_t status,
>> ? ? ? ? ? ? ? ?return;
>>
>> ? ? ? ?mp->status = status;
>> +
>> + ? ? ? if (control->state == AVCTP_STATE_CONNECTED && ?!control->target &&
>> + ? ? ? ? ? ? ? ? ? ? ? (control->registered_events &
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED))) {
>> + ? ? ? ? ? ? ? avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &status);
>> + ? ? ? }
>
> Are you sure it would be valid to call mp_set_playback_status when
> acting as a controller? Im afraid we should not cache anything, or

No it's not valid. But in all cases it's implemented in this patch
we're actually acting as target, not controller. The control->target
fields refers to the remote device.

> perhaps not even register MediaPlayer interface in case the device is
> a target.

We can't since MediaPlayer accepts commands before it's connected.

Lucas De Marchi

2011-08-11 08:15:53

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH v3 18/22] avrcp: send response for registered events

Hi Lucas,

On Wed, Aug 10, 2011 at 4:06 PM, Lucas De Marchi
<[email protected]> wrote:
> ?static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?uint32_t *elapsed, uint32_t *track_len)
> ?{
> @@ -754,6 +823,13 @@ static void mp_set_playback_status(struct control *control, uint8_t status,
> ? ? ? ? ? ? ? ?return;
>
> ? ? ? ?mp->status = status;
> +
> + ? ? ? if (control->state == AVCTP_STATE_CONNECTED && ?!control->target &&
> + ? ? ? ? ? ? ? ? ? ? ? (control->registered_events &
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED))) {
> + ? ? ? ? ? ? ? avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &status);
> + ? ? ? }

Are you sure it would be valid to call mp_set_playback_status when
acting as a controller? Im afraid we should not cache anything, or
perhaps not even register MediaPlayer interface in case the device is
a target.

>
> ?/*
> @@ -901,6 +977,12 @@ static void mp_set_media_attributes(struct control *control,
> ? ? ? ? ? ? ? ? ? ? ? ? ? "\tTrack number: %u\n\tTrack duration: %u",
> ? ? ? ? ? ? ? ? ? ? ? ? ? mi->title, mi->artist, mi->album, mi->genre,
> ? ? ? ? ? ? ? ? ? ? ? ? ? mi->ntracks, mi->track, mi->track_len);
> +
> + ? ? ? if (control->state == AVCTP_STATE_CONNECTED && !control->target &&
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (control->registered_events &
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(1 << AVRCP_EVENT_TRACK_CHANGED))) {
> + ? ? ? ? ? ? ? avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
> + ? ? ? }

Same here.


--
Luiz Augusto von Dentz

2011-08-10 13:06:36

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 22/22] Update Control documentation

Move methods and signal to an experimental interface named MediaPlayer
as currently implemented.
---
doc/control-api.txt | 94 +++++++++++++++++++++++++-------------------------
1 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/doc/control-api.txt b/doc/control-api.txt
index 1a42846..a7e5cbb 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -45,44 +45,6 @@ Methods void Connect()
Metadata or Events defined in the AVRCP+Metadata
specification.

- void ChangePlayback(string status, uint32 elapsed)
-
- The status can be "playing", "stopped", "paused",
- "forward-seek", "reverse-seek" or "error". Elapsed is
- the position within the track in milliseconds.
-
- void ChangeTrack(dict metadata)
-
- Called to send the mandated TrackChange event and
- potential metadata information.
-
- Current defined metadata information are represented
- with the following keys:
-
- Title string (mandatory)
- Artist string
- Album string
- Genre string
- NumberOfTracks uint32
- TrackNumber uint32
- TrackDuration uint32 (in milliseconds)
-
- void ChangeSetting(string setting, variant value)
-
- Called to transmit Application Settings, CT Status
- and the like.
-
- Currenet defined settings are represented with the
- following keys:
-
- Equalizer off, on
- Repeat off, singletrack, alltracks, group
- Shuffle off, alltracks, group
- Scan off, alltracks, group
- Battery normal, warning, critical, external, fullcharge
- System powered, unpowered, unplugged
- Volume uint8
-
Signals Connected()

Sent when a successful AVRCP connection has been made
@@ -112,15 +74,6 @@ Signals Connected()
connected device (except for Metadata defined in
Bluetooth SIG AVRCP+Metadata spec).

- TrackChanged(dict metadata)
-
- Called when Metadata is received from connected device.
- May be multiple meta attribute/element pairs.
-
- PlaybackChanged(string status, uint32 elapsed)
-
- SettingChanged(string setting, variant value)
-
Properties uint8 SubUnitID [readonly]

The three-bit Subunit ID from the connected device.
@@ -140,3 +93,50 @@ Properties uint8 SubUnitID [readonly]
array{string} Capabilities [readonly]

List of Capabilities provided by the connected device.
+
+
+MediaPlayer hierarchy [experimental]
+=====================
+
+Service org.bluez
+Interface org.bluez.MediaPlayer
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void ChangePlayback(string status, uint32 elapsed)
+
+ The status can be "playing", "stopped", "paused",
+ "forward-seek", "reverse-seek" or "error". Elapsed is
+ the position within the track in milliseconds.
+
+ void ChangeTrack(dict metadata)
+
+ Called to send the mandated TrackChange event and
+ potential metadata information.
+
+ Current defined metadata information are represented
+ with the following keys:
+
+ Title string (mandatory)
+ Artist string
+ Album string
+ Genre string
+ NumberOfTracks uint32
+ TrackNumber uint32
+ TrackDuration uint32 (in milliseconds)
+
+ void SetProperty(string property, variant value)
+
+ Called to set the media-player's properties
+
+ Current defined properties are represented with the
+ following keys and values:
+
+ Equalizer off, on
+ Repeat off, singletrack, alltracks, group
+ Shuffle off, alltracks, group
+ Scan off, alltracks, group
+
+Signals PropertyChanged(string setting, variant value)
+
+ Called when one of the settings are changed by the
+ remote device or to inform its battery status.
--
1.7.6


2011-08-10 13:06:35

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 21/22] Add script to test MediaPlayer interface

---
test/test-media-player | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 108 insertions(+), 0 deletions(-)
create mode 100755 test/test-media-player

diff --git a/test/test-media-player b/test/test-media-player
new file mode 100755
index 0000000..712cf31
--- /dev/null
+++ b/test/test-media-player
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+USAGE = "Usage: %prog [options] <command> [args]"
+COMMANDS = """
+Commands:
+ changetrack <bdaddr> <key> <value> [<key> <value> ...]
+
+ changeplayback <bdaddr> status elapsed-time
+ status: one of playing, stopped, paused, forward-seek, reverse-seek
+ or error.
+ elapsed-time: in milliseconds
+
+ setproperty <bdaddr> property value
+ setting: one of Equalizer, Repeat, Shuffle or Scan
+ value: value correspondent to the setting specified
+"""
+
+class MyParser(OptionParser):
+ def format_epilog(self, formatter):
+ return self.epilog
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = MyParser(option_list=option_list, usage=USAGE, epilog=COMMANDS)
+
+(options, args) = parser.parse_args()
+
+if len(args) < 2:
+ parser.print_help()
+ sys.exit(1)
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+device = adapter.FindDevice(args[1])
+control = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Control")
+mp = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.MediaPlayer")
+
+def handle_change_track(mp, args):
+ if len(args) % 2 != 0:
+ print("Don't know how to handle odd number of parameters")
+ print(USAGE)
+ sys.exit(1)
+
+ d = dict()
+ for i in range(2, len(args), 2):
+ key = args[i]
+ if key == "Title" or key == "Artist" or key == "Album" \
+ or key == "Genre":
+ d[key] = dbus.String(args[i + 1].decode(sys.stdin.encoding))
+ elif key == "NumberOfTracks" or key == "TrackNumber" \
+ or key == "TrackDuration":
+ d[key] = dbus.UInt32(int(args[i + 1]))
+ else:
+ print("Unknown metadata: %s" % key)
+ sys.exit(1)
+
+ d = dbus.Dictionary(d, signature='sv')
+ mp.ChangeTrack(d)
+
+def handle_change_playback(mp, args):
+ if len(args) != 4:
+ print(USAGE)
+ sys.exit(1)
+
+ status = dbus.String(args[2])
+ elapsed = dbus.UInt32(long(args[3]))
+
+ mp.ChangePlayback(status, elapsed)
+
+def handle_set_property(mp, args):
+ if len(args) != 4:
+ print(USAGE)
+ sys.exit(1)
+
+ prop = dbus.String(args[2])
+ value = dbus.String(args[3])
+
+ mp.SetProperty(prop, value)
+
+
+handlers = { 'changetrack': handle_change_track,
+ 'changeplayback': handle_change_playback,
+ 'setproperty': handle_set_property }
+
+if not args[0] in handlers:
+ print("Unknown command -- %s" % argv[1])
+ print(USAGE)
+ sys.exit(1)
+
+handlers[args[0]](mp, args)
--
1.7.6


2011-08-10 13:06:34

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 20/22] avrcp: update copyright

---
audio/control.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index d0c44fd..ef73980 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2011 Texas Instruments, Inc.
*
*
* This program is free software; you can redistribute it and/or modify
--
1.7.6


2011-08-10 13:06:33

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 19/22] avrcp: change TG record to use version 1.3

---
audio/control.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 44a2808..d0c44fd 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -414,7 +414,7 @@ static sdp_record_t *avrcp_tg_record(void)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_PSM;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;

record = sdp_record_alloc();
if (!record)
--
1.7.6


2011-08-10 13:06:32

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 18/22] avrcp: send response for registered events

When a certain event occur, check if CT registered to receive that
notification and send a response.

Example event for PTS test TC_TG_NFY_BV_02_C:

> ACL data: handle 11 flags 0x02 dlen 22
L2CAP(d): cid 0x0043 len 18 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Notify: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: RegisterNotification: pt 0x00 len 0x0005
EventID: 0x02 (EVENT_TRACK_CHANGED)
Interval: 0x00000000 (0 seconds)
< ACL data: handle 11 flags 0x02 dlen 26
L2CAP(d): cid 0x0043 len 22 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Interim: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: RegisterNotification: pt 0x00 len 0x0009
EventID: 0x02 (EVENT_TRACK_CHANGED)
Identifier: 0x0 (PLAYING)

[...]

< ACL data: handle 11 flags 0x02 dlen 26
L2CAP(d): cid 0x0043 len 22 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Changed: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: RegisterNotification: pt 0x00 len 0x0009
EventID: 0x02 (EVENT_TRACK_CHANGED)
Identifier: 0x0 (PLAYING)
---
audio/control.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index c4a947c..44a2808 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -79,6 +79,7 @@
#define CTYPE_ACCEPTED 0x9
#define CTYPE_REJECTED 0xA
#define CTYPE_STABLE 0xC
+#define CTYPE_CHANGED 0xD
#define CTYPE_INTERIM 0xF

/* opcodes */
@@ -313,6 +314,7 @@ struct control {
uint8_t key_quirks[256];

uint16_t registered_events;
+ uint8_t transaction_events[AVRCP_EVENT_TRACK_CHANGED + 1];
};

static struct {
@@ -716,6 +718,73 @@ static const char *battery_status_to_str(enum battery_status status)
return NULL;
}

+static int avctp_send_event(struct control *control, uint8_t id, void *data)
+{
+ uint8_t buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+ AVRCP_SPECAVCPDU_HEADER_LENGTH + 9];
+ struct avctp_header *avctp = (void *) buf;
+ struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
+ struct avrcp_spec_avc_pdu *pdu = (void *) &buf[AVCTP_HEADER_LENGTH +
+ AVRCP_HEADER_LENGTH];
+ int sk = g_io_channel_unix_get_fd(control->io);
+ uint16_t size;
+
+ memset(buf, 0, sizeof(buf));
+
+ avctp->transaction = control->transaction_events[id];
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = AVCTP_RESPONSE;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ avrcp->code = CTYPE_CHANGED;
+ avrcp->subunit_type = SUBUNIT_PANEL;
+ avrcp->opcode = OP_VENDORDEP;
+
+ pdu->company_id[0] = IEEEID_BTSIG >> 16;
+ pdu->company_id[1] = (IEEEID_BTSIG >> 8) & 0xFF;
+ pdu->company_id[2] = IEEEID_BTSIG & 0xFF;
+
+ pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ pdu->params[0] = id;
+
+ DBG("id=%u", id);
+
+ switch (id) {
+ case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+ size = 2;
+ pdu->params[1] = *((uint8_t *)data);
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED: {
+ size = 9;
+
+ /*
+ * AVRCP 1.3 supports only one track identifier: PLAYING
+ * (0x0). When 1.4 version is added, this shall be changed to
+ * contain the identifier of the track.
+ */
+ memset(&pdu->params[1], 0, 8);
+
+ break;
+ }
+ default:
+ error("Unknown event %u", id);
+ return -EINVAL;
+ }
+
+ pdu->params_len = htons(size);
+ size += AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH +
+ AVRCP_SPECAVCPDU_HEADER_LENGTH;
+
+ if (write(sk, buf, size) < 0)
+ return -errno;
+
+ /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+ control->registered_events ^= 1 << id;
+
+ return 0;
+}
+
static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
uint32_t *elapsed, uint32_t *track_len)
{
@@ -754,6 +823,13 @@ static void mp_set_playback_status(struct control *control, uint8_t status,
return;

mp->status = status;
+
+ if (control->state == AVCTP_STATE_CONNECTED && !control->target &&
+ (control->registered_events &
+ (1 << AVRCP_EVENT_PLAYBACK_STATUS_CHANGED))) {
+ avctp_send_event(control, AVRCP_EVENT_PLAYBACK_STATUS_CHANGED,
+ &status);
+ }
}

/*
@@ -901,6 +977,12 @@ static void mp_set_media_attributes(struct control *control,
"\tTrack number: %u\n\tTrack duration: %u",
mi->title, mi->artist, mi->album, mi->genre,
mi->ntracks, mi->track, mi->track_len);
+
+ if (control->state == AVCTP_STATE_CONNECTED && !control->target &&
+ (control->registered_events &
+ (1 << AVRCP_EVENT_TRACK_CHANGED))) {
+ avctp_send_event(control, AVRCP_EVENT_TRACK_CHANGED, NULL);
+ }
}

static int avrcp_handle_get_capabilities(struct control *control,
@@ -1241,7 +1323,8 @@ static int avrcp_handle_get_play_status(struct control *control,
}

static int avrcp_handle_register_notification(struct control *control,
- struct avrcp_spec_avc_pdu *pdu)
+ struct avrcp_spec_avc_pdu *pdu,
+ uint8_t transaction)
{
uint16_t len = ntohs(pdu->params_len);
uint8_t status;
@@ -1280,8 +1363,9 @@ static int avrcp_handle_register_notification(struct control *control,
goto err;
}

- /* Register event */
+ /* Register event and save the transaction used */
control->registered_events |= (1 << pdu->params[0]);
+ control->transaction_events[pdu->params[0]] = transaction;

pdu->params_len = htons(len);

@@ -1294,6 +1378,7 @@ err:

/* 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)
{
@@ -1464,7 +1549,8 @@ static int handle_vendordep_pdu(struct control *control,
goto err_metadata;
}

- len = avrcp_handle_register_notification(control, pdu);
+ len = avrcp_handle_register_notification(control, pdu,
+ avctp->transaction);
if (len < 0)
goto err_metadata;

@@ -1662,7 +1748,8 @@ static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
int r_size;
operand_count -= 3;
avctp->cr = AVCTP_RESPONSE;
- r_size = handle_vendordep_pdu(control, avrcp, operand_count);
+ r_size = handle_vendordep_pdu(control, avctp, avrcp,
+ operand_count);
packet_size = AVCTP_HEADER_LENGTH + r_size;
} else {
avctp->cr = AVCTP_RESPONSE;
--
1.7.6


2011-08-10 13:06:31

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 17/22] avrcp: handle GetElementAttributes pdu

Example responses for PTS test TC_TG_MDI_BV_04_C:

> ACL data: handle 11 flags 0x02 dlen 26
L2CAP(d): cid 0x0042 len 22 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetElementAttributes: pt 0x00 len 0x0009
Identifier: 0x0 (PLAYING)
AttributeCount: 0x00
< ACL data: handle 11 flags 0x02 dlen 70
L2CAP(d): cid 0x0042 len 66 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetElementAttributes: pt 0x00 len 0x0035
AttributeCount: 0x03
Attribute: 0x00000001 (Title)
CharsetID: 0x006a (UTF-8)
AttributeValueLength: 0x0012
AttributeValue: Walking on the Sun
Attribute: 0x00000002 (Artist)
CharsetID: 0x006a (UTF-8)
AttributeValueLength: 0x0005
AttributeValue: Lucas
Attribute: 0x00000007 (Progress)
CharsetID: 0x006a (UTF-8)
AttributeValueLength: 0x0005
AttributeValue: 32029

And TC_TG_MDI_BV_05_C:

> ACL data: handle 11 flags 0x02 dlen 30
L2CAP(d): cid 0x0043 len 26 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetElementAttributes: pt 0x00 len 0x000d
Identifier: 0x0 (PLAYING)
AttributeCount: 0x01
Attribute: 0x00000001 (Title)
< ACL data: handle 11 flags 0x02 dlen 44
L2CAP(d): cid 0x0043 len 40 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetElementAttributes: pt 0x00 len 0x001b
AttributeCount: 0x01
Attribute: 0x00000001 (Title)
CharsetID: 0x006a (UTF-8)
AttributeValueLength: 0x0012
AttributeValue: Walking on the Sun
---
audio/control.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 189 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 66bb55b..c4a947c 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -123,6 +123,7 @@
#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
#define AVRCP_DISPLAYABLE_CHARSET 0x17
#define AVRCP_CT_BATTERY_STATUS 0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20
#define AVRCP_GET_PLAY_STATUS 0x30
#define AVRCP_REGISTER_NOTIFICATION 0x31

@@ -184,6 +185,16 @@ enum battery_status {
BATTERY_STATUS_FULL_CHARGE = 4,
};

+enum media_info_id {
+ MEDIA_INFO_TITLE = 1,
+ MEDIA_INFO_ARTIST = 2,
+ MEDIA_INFO_ALBUM = 3,
+ MEDIA_INFO_TRACK = 4,
+ MEDIA_INFO_N_TRACKS = 5,
+ MEDIA_INFO_GENRE = 6,
+ MEDIA_INFO_CURRENT_POSITION = 7,
+};
+
static DBusConnection *connection = NULL;

static GSList *servers = NULL;
@@ -745,6 +756,103 @@ static void mp_set_playback_status(struct control *control, uint8_t status,
mp->status = status;
}

+/*
+ * Copy media_info field to a buffer, intended to be used in a response to
+ * GetElementAttributes message.
+ *
+ * It assumes there's enough space in the buffer and on success it returns the
+ * size written.
+ *
+ * If @param id is not valid, -EINVAL is returned. If there's no such media
+ * attribute, -ENOENT is returned.
+ */
+static int mp_get_media_attribute(struct media_player *mp,
+ uint32_t id, uint8_t *buf)
+{
+ struct media_info_elem {
+ uint32_t id;
+ uint16_t charset;
+ uint16_t len;
+ uint8_t val[];
+ };
+ const struct media_info *mi = &mp->mi;
+ struct media_info_elem *elem = (void *)buf;
+ uint16_t len;
+ char valstr[20];
+
+ switch (id) {
+ case MEDIA_INFO_TITLE:
+ if (mi->title) {
+ len = strlen(mi->title);
+ memcpy(elem->val, mi->title, len);
+ } else {
+ len = 0;
+ }
+
+ break;
+ case MEDIA_INFO_ARTIST:
+ if (mi->artist == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->artist);
+ memcpy(elem->val, mi->artist, len);
+ break;
+ case MEDIA_INFO_ALBUM:
+ if (mi->album == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->album);
+ memcpy(elem->val, mi->album, len);
+ break;
+ case MEDIA_INFO_GENRE:
+ if (mi->genre == NULL)
+ return -ENOENT;
+
+ len = strlen(mi->genre);
+ memcpy(elem->val, mi->genre, len);
+ break;
+
+ case MEDIA_INFO_TRACK:
+ if (!mi->track)
+ return -ENOENT;
+
+ snprintf(valstr, 20, "%u", mi->track);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ break;
+ case MEDIA_INFO_N_TRACKS:
+ if (!mi->ntracks)
+ return -ENOENT;
+
+ snprintf(valstr, 20, "%u", mi->ntracks);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ break;
+ case MEDIA_INFO_CURRENT_POSITION:
+ if (mi->elapsed != 0xFFFFFFFF) {
+ uint32_t elapsed;
+
+ mp_get_playback_status(mp, NULL, &elapsed, NULL);
+
+ snprintf(valstr, 20, "%u", elapsed);
+ len = strlen(valstr);
+ memcpy(elem->val, valstr, len);
+ } else {
+ return -ENOENT;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ elem->id = htonl(id);
+ elem->charset = htons(0x6A); /* Always use UTF-8 */
+ elem->len = htons(len);
+
+ return sizeof(struct media_info_elem) + len;
+}
+
static void mp_set_attribute(struct media_player *mp,
uint8_t attr, uint8_t val)
{
@@ -893,6 +1001,74 @@ err:
return -EINVAL;
}

+static int avrcp_handle_get_element_attributes(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ uint64_t *identifier = (void *) &pdu->params[0];
+ uint16_t pos;
+ uint8_t nattr;
+ int size;
+ unsigned int i;
+
+ if (len < 8 || *identifier != 0 || !control->mp)
+ goto err;
+
+ len = 0;
+ pos = 1; /* Keep track of current position in reponse */
+ nattr = pdu->params[8];
+
+ if (!control->mp)
+ goto done;
+
+ if (!nattr) {
+ /*
+ * Return all available information, at least
+ * title must be returned.
+ */
+ for (i = 1; i <= MEDIA_INFO_CURRENT_POSITION; i++) {
+ size = mp_get_media_attribute(control->mp, i,
+ &pdu->params[pos]);
+
+ if (size > 0) {
+ len++;
+ pos += size;
+ }
+ }
+ } else {
+ uint32_t *attr_ids = g_malloc(sizeof(uint32_t) * nattr);
+
+ /* save a copy of requested attributes */
+ memcpy(&attr_ids[0], &pdu->params[9], nattr * 4);
+
+ for (i = 0; i < nattr; i++) {
+ uint32_t attr = ntohl(attr_ids[i]);
+
+ size = mp_get_media_attribute(control->mp, attr,
+ &pdu->params[pos]);
+
+ if (size > 0) {
+ len++;
+ pos += size;
+ }
+ }
+
+ g_free(attr_ids);
+
+ if (!len)
+ goto err;
+ }
+
+done:
+ pdu->params[0] = len;
+ pdu->params_len = htons(pos);
+
+ return pos;
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
static int avrcp_handle_get_current_player_value(struct control *control,
struct avrcp_spec_avc_pdu *pdu)
{
@@ -1181,6 +1357,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
--
1.7.6


2011-08-10 13:06:30

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 16/22] avrcp: handle query for supported events

---
audio/control.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index ea240d9..66bb55b 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -132,6 +132,7 @@

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
+#define CAP_EVENTS_SUPPORTED 0x03

#define QUIRK_NO_RELEASE 1 << 0

@@ -817,6 +818,13 @@ static int avrcp_handle_get_capabilities(struct control *control,
pdu->params[1] = G_N_ELEMENTS(company_ids);

return 2 + (3 * G_N_ELEMENTS(company_ids));
+ 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;
}

err:
--
1.7.6


2011-08-10 13:06:29

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 15/22] avrcp: handle RegisterNotification pdu

Handle mandatory events according to AVRCP 1.3 spec.
---
audio/control.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index d3cc06fa..ea240d9 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -74,10 +74,12 @@
/* ctype entries */
#define CTYPE_CONTROL 0x0
#define CTYPE_STATUS 0x1
+#define CTYPE_NOTIFY 0x3
#define CTYPE_NOT_IMPLEMENTED 0x8
#define CTYPE_ACCEPTED 0x9
#define CTYPE_REJECTED 0xA
#define CTYPE_STABLE 0xC
+#define CTYPE_INTERIM 0xF

/* opcodes */
#define OP_VENDORDEP 0x00
@@ -122,6 +124,11 @@
#define AVRCP_DISPLAYABLE_CHARSET 0x17
#define AVRCP_CT_BATTERY_STATUS 0x18
#define AVRCP_GET_PLAY_STATUS 0x30
+#define AVRCP_REGISTER_NOTIFICATION 0x31
+
+/* Notification events */
+#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED 0x01
+#define AVRCP_EVENT_TRACK_CHANGED 0x02

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -292,6 +299,8 @@ struct control {
gboolean target;

uint8_t key_quirks[256];
+
+ uint16_t registered_events;
};

static struct {
@@ -1047,6 +1056,58 @@ static int avrcp_handle_get_play_status(struct control *control,
return 9;
}

+static int avrcp_handle_register_notification(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ uint8_t status;
+
+ /*
+ * 1 byte for EventID, 4 bytes for Playback interval but the latest
+ * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
+ * 1.3 spec, section 5.4.2.
+ */
+ if (len != 5)
+ goto err;
+
+ switch (pdu->params[0]) {
+ case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+ len = 2;
+ if (control->mp) {
+ mp_get_playback_status(control->mp, &status,
+ NULL, NULL);
+ pdu->params[1] = status;
+ } else {
+ pdu->params[1] = PLAY_STATUS_ERROR;
+ }
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ len = 9;
+
+ if (!control->mp)
+ memset(&pdu->params[1], 0xFF, 8);
+ else
+ memset(&pdu->params[1], 0, 8);
+
+ break;
+ default:
+ /* All other events are not supported yet */
+ goto err;
+ }
+
+ /* Register event */
+ control->registered_events |= (1 << pdu->params[0]);
+
+ pdu->params_len = htons(len);
+
+ return len;
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -1200,6 +1261,19 @@ static int handle_vendordep_pdu(struct control *control,
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);
+ if (len < 0)
+ goto err_metadata;
+
+ avrcp->code = CTYPE_INTERIM;
+
+ break;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:28

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 14/22] avrcp: handle GetPlayStatus pdu

Example response for PTS test TC_TG_MDI_BV_02_C:

> ACL data: handle 11 flags 0x02 dlen 17
L2CAP(d): cid 0x0043 len 13 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetPlayStatus: pt 0x00 len 0x0000
< ACL data: handle 11 flags 0x02 dlen 26
L2CAP(d): cid 0x0043 len 22 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetPlayStatus: pt 0x00 len 0x0009
SongLength: 0x0000a7f8 (43000 miliseconds)
SongPosition: 0x00012fad (77741 miliconds)
PlayStatus: 0x01 (PLAYING)
---
audio/control.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 9d87d0f..d3cc06fa 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -121,6 +121,7 @@
#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
#define AVRCP_DISPLAYABLE_CHARSET 0x17
#define AVRCP_CT_BATTERY_STATUS 0x18
+#define AVRCP_GET_PLAY_STATUS 0x30

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -694,6 +695,30 @@ static const char *battery_status_to_str(enum battery_status status)
return NULL;
}

+static void mp_get_playback_status(struct media_player *mp, uint8_t *status,
+ uint32_t *elapsed, uint32_t *track_len)
+{
+ if (status)
+ *status = mp->status;
+ if (track_len)
+ *track_len = mp->mi.track_len;
+
+ if (!elapsed)
+ return;
+
+ *elapsed = mp->mi.elapsed;
+
+ if (mp->status == PLAY_STATUS_PLAYING) {
+ double timedelta = g_timer_elapsed(mp->timer, NULL);
+ uint32_t sec, msec;
+
+ sec = (uint32_t) timedelta;
+ msec = (uint32_t)((timedelta - sec) * 1000);
+
+ *elapsed += sec * 1000 + msec;
+ }
+}
+
static void mp_set_playback_status(struct control *control, uint8_t status,
uint32_t elapsed)
{
@@ -989,6 +1014,39 @@ err:
return -EINVAL;
}

+static int avrcp_handle_get_play_status(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ uint32_t elapsed;
+ uint32_t track_len;
+ uint8_t status;
+
+ if (len != 0) {
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+ }
+
+ if (control->mp) {
+ mp_get_playback_status(control->mp, &status,
+ &elapsed, &track_len);
+ track_len = htonl(track_len);
+ elapsed = htonl(elapsed);
+ } else {
+ track_len = 0xFFFFFFFF;
+ elapsed = 0xFFFFFFFF;
+ status = PLAY_STATUS_ERROR;
+ }
+
+ memcpy(&pdu->params[0], &track_len, 4);
+ memcpy(&pdu->params[4], &elapsed, 4);
+ pdu->params[8] = status;
+
+ pdu->params_len = htons(9);
+
+ return 9;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -1129,6 +1187,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:27

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 13/22] avrcp: handle InformBatteryStatusOfCT pdu

---
audio/control.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 6590cbb..9d87d0f 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -120,6 +120,7 @@
#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15
#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
#define AVRCP_DISPLAYABLE_CHARSET 0x17
+#define AVRCP_CT_BATTERY_STATUS 0x18

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -166,6 +167,14 @@ enum play_status {
PLAY_STATUS_ERROR = 0xFF
};

+enum battery_status {
+ BATTERY_STATUS_NORMAL = 0,
+ BATTERY_STATUS_WARNING = 1,
+ BATTERY_STATUS_CRITICAL = 2,
+ BATTERY_STATUS_EXTERNAL = 3,
+ BATTERY_STATUS_FULL_CHARGE = 4,
+};
+
static DBusConnection *connection = NULL;

static GSList *servers = NULL;
@@ -667,6 +676,24 @@ static int play_status_to_val(const char *status)
return -EINVAL;
}

+static const char *battery_status_to_str(enum battery_status status)
+{
+ switch (status) {
+ case BATTERY_STATUS_NORMAL:
+ return "normal";
+ case BATTERY_STATUS_WARNING:
+ return "warning";
+ case BATTERY_STATUS_CRITICAL:
+ return "critical";
+ case BATTERY_STATUS_EXTERNAL:
+ return "external";
+ case BATTERY_STATUS_FULL_CHARGE:
+ return "fullcharge";
+ }
+
+ return NULL;
+}
+
static void mp_set_playback_status(struct control *control, uint8_t status,
uint32_t elapsed)
{
@@ -937,6 +964,31 @@ err:
return -EINVAL;
}

+static int avrcp_handle_ct_battery_status(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ const char *valstr;
+
+ if (len != 1)
+ goto err;
+
+ valstr = battery_status_to_str(pdu->params[0]);
+ if (valstr == NULL)
+ goto err;
+
+ emit_property_changed(control->dev->conn, control->dev->path,
+ MEDIA_PLAYER_INTERFACE, "Battery",
+ DBUS_TYPE_STRING, &valstr);
+ pdu->params_len = 0;
+
+ return 0;
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -1064,6 +1116,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:26

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 12/22] avrcp: handle InformDisplayableCharacterSet pdu

---
audio/control.c | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 009cb81..6590cbb 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -119,6 +119,7 @@
#define AVRCP_SET_PLAYER_VALUE 0x14
#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15
#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
+#define AVRCP_DISPLAYABLE_CHARSET 0x17

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -1043,6 +1044,26 @@ static int handle_vendordep_pdu(struct control *control,
*/
pdu->params[0] = E_INVALID_PARAM;
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:25

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 11/22] avrcp: handle commands for future extension

AVRCP_GET_PLAYER_ATTRIBUTE_TEXT and AVRCP_GET_PLAYER_VALUE_TEXT shall
only be used if TG has extended attributes.

For the ones defined in AVRCP spec these commands should not be called.
Since we do not have extended attributes yet we can ignore those
commands.
---
audio/control.c | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index cb40490..009cb81 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -117,6 +117,8 @@
#define AVRCP_LIST_PLAYER_VALUES 0x12
#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
#define AVRCP_SET_PLAYER_VALUE 0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -1025,6 +1027,22 @@ static int handle_vendordep_pdu(struct control *control,
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;
+ }
+
+ /*
+ * 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;
+ goto err_metadata;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:24

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 10/22] avrcp: handle SetPlayerApplicationSettingValue pdu

---
audio/control.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 125 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 14c56ba..cb40490 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -116,6 +116,7 @@
#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11
#define AVRCP_LIST_PLAYER_VALUES 0x12
#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
+#define AVRCP_SET_PLAYER_VALUE 0x14

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -518,6 +519,49 @@ static unsigned int attr_get_max_val(uint8_t attr)
return 0;
}

+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case PLAYER_SETTING_EQUALIZER:
+ switch (value) {
+ case EQUALIZER_MODE_ON:
+ return "on";
+ case EQUALIZER_MODE_OFF:
+ return "off";
+ }
+
+ break;
+ case PLAYER_SETTING_REPEAT:
+ switch (value) {
+ case REPEAT_MODE_OFF:
+ return "off";
+ case REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case REPEAT_MODE_ALL:
+ return "alltracks";
+ case REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case PLAYER_SETTING_SHUFFLE:
+ case PLAYER_SETTING_SCAN:
+ switch (value) {
+ case SCAN_MODE_OFF:
+ return "off";
+ case SCAN_MODE_ALL:
+ return "alltracks";
+ case SCAN_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
static int attrval_to_val(uint8_t attr, const char *value)
{
int ret;
@@ -572,6 +616,22 @@ static int attrval_to_val(uint8_t attr, const char *value)
return -EINVAL;
}

+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case PLAYER_SETTING_EQUALIZER:
+ return "Equalizer";
+ case PLAYER_SETTING_REPEAT:
+ return "Repeat";
+ case PLAYER_SETTING_SHUFFLE:
+ return "Shuffle";
+ case PLAYER_SETTING_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
static int attr_to_val(const char *str)
{
if (!strcmp(str, "Equalizer"))
@@ -823,6 +883,57 @@ err:
return -EINVAL;
}

+static int avrcp_handle_set_player_value(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ unsigned int i;
+
+ if (len < 3 || !control->mp)
+ goto err;
+
+ len = 0;
+
+ /*
+ * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+ * and set the existent ones. Sec. 5.2.4 is not clear however how to
+ * indicate that a certain ID was not accepted. If at least one
+ * attribute is valid, we respond with no parameters. Otherwise an
+ * E_INVALID_PARAM is sent.
+ */
+ for (i = 1; i < pdu->params[0]; i += 2) {
+ uint8_t attr = pdu->params[i];
+ uint8_t val = pdu->params[i + 1];
+ const char *attrstr;
+ const char *valstr;
+
+ attrstr = attr_to_str(attr);
+ if (!attrstr)
+ continue;
+
+ valstr = attrval_to_str(attr, val);
+ if (!valstr)
+ continue;
+
+ len++;
+
+ mp_set_attribute(control->mp, attr, val);
+ emit_property_changed(control->dev->conn, control->dev->path,
+ MEDIA_PLAYER_INTERFACE, attrstr,
+ DBUS_TYPE_STRING, &valstr);
+ }
+
+ if (len) {
+ pdu->params_len = 0;
+
+ return 0;
+ }
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -901,6 +1012,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
@@ -1807,6 +1931,7 @@ static GDBusMethodTable mp_methods[] = {
};

static GDBusSignalTable mp_signals[] = {
+ { "PropertyChanged", "sv" },
{ }
};

--
1.7.6


2011-08-10 13:06:23

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 09/22] avrcp: handle GetCurrentPlayerAplicationSettingValue pdu

Example response for PTS test TC_TG_PAS_BV_10_C:

> ACL data: handle 11 flags 0x02 dlen 19
L2CAP(d): cid 0x0043 len 15 [psm 23]
AVCTP: Command : pt 0x00 transaction 3 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetCurrentPlayerApplicationSettingValue: pt 0x00 len 0x0002
AttributeCount: 0x01
AttributeID: 0x01 (Equalizer ON/OFF Status)
< ACL data: handle 11 flags 0x02 dlen 20
L2CAP(d): cid 0x0043 len 16 [psm 23]
AVCTP: Response : pt 0x00 transaction 3 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetCurrentPlayerApplicationSettingValue: pt 0x00 len 0x0003
ValueCount: 0x01
AttributeID: 0x01 (Equalizer ON/OFF Status)
ValueID: 0x02 (ON)
---
audio/control.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index d82409d..14c56ba 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -115,6 +115,7 @@
#define AVRCP_GET_CAPABILITIES 0x10
#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11
#define AVRCP_LIST_PLAYER_VALUES 0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -760,6 +761,68 @@ err:
return -EINVAL;
}

+static int avrcp_handle_get_current_player_value(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ struct media_player *mp = control->mp;
+ uint8_t *settings;
+ unsigned int i;
+
+ if (mp == NULL || len <= 1 || pdu->params[0] != len - 1)
+ goto err;
+
+ /*
+ * Save a copy of requested settings because we can override them
+ * while responding
+ */
+ settings = g_malloc(pdu->params[0]);
+ memcpy(settings, &pdu->params[1], pdu->params[0]);
+ len = 0;
+
+ /*
+ * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+ * and send a response with the existent ones. Only if all IDs are
+ * non-existent we should send an error.
+ */
+ for (i = 0; i < pdu->params[0]; i++) {
+ uint8_t val;
+
+ if (settings[i] < PLAYER_SETTING_EQUALIZER ||
+ settings[i] > PLAYER_SETTING_SCAN) {
+ DBG("Ignoring %u", settings[i]);
+ continue;
+ }
+
+ val = mp_get_attribute(mp, settings[i]);
+ if (!val) {
+ DBG("Ignoring %u: not supported by player",
+ settings[i]);
+ continue;
+ }
+
+ pdu->params[len] = settings[i];
+ pdu->params[len + 1] = val;
+ len += 2;
+ }
+
+ g_free(settings);
+
+ if (len) {
+ pdu->params[0] = len;
+ pdu->params_len = htons(2 * len + 1);
+
+ return 2 * len + 1;
+ }
+
+ error("No valid attributes in request");
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -825,6 +888,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:22

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 08/22] avrcp: handle ListPlayerApplicationSettingValues pdu

Example of response obtained with PTS test TC_TG_PAS_BV_06_C:

> ACL data: handle 11 flags 0x02 dlen 18
L2CAP(d): cid 0x0043 len 14 [psm 23]
AVCTP: Command : pt 0x00 transaction 3 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: ListPlayerApplicationSettingValues: pt 0x00 len 0x0001
AttributeID: 0x01 (Equalizer ON/OFF Status)
< ACL data: handle 11 flags 0x02 dlen 20
L2CAP(d): cid 0x0043 len 16 [psm 23]
AVCTP: Response : pt 0x00 transaction 3 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: ListPlayerApplicationSettingValues: pt 0x00 len 0x0003
ValueCount: 0x02
ValueID: 0x01 (OFF)
ValueID: 0x02 (ON)
---
audio/control.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 36ddde2..d82409d 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -114,6 +114,7 @@
/* PDU types for metadata transfer */
#define AVRCP_GET_CAPABILITIES 0x10
#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11
+#define AVRCP_LIST_PLAYER_VALUES 0x12

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -500,6 +501,22 @@ static void handle_panel_passthrough(struct control *control,
operands[0] & 0x7F, status);
}

+static unsigned int attr_get_max_val(uint8_t attr)
+{
+ switch (attr) {
+ case PLAYER_SETTING_EQUALIZER:
+ return EQUALIZER_MODE_ON;
+ case PLAYER_SETTING_REPEAT:
+ return REPEAT_MODE_GROUP;
+ case PLAYER_SETTING_SHUFFLE:
+ return SHUFFLE_MODE_GROUP;
+ case PLAYER_SETTING_SCAN:
+ return SCAN_MODE_GROUP;
+ }
+
+ return 0;
+}
+
static int attrval_to_val(uint8_t attr, const char *value)
{
int ret;
@@ -714,6 +731,35 @@ done:
return len + 1;
}

+static int avrcp_handle_list_player_values(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ struct media_player *mp = control->mp;
+ unsigned int i;
+
+ if (len != 1 || !mp)
+ goto err;
+
+ len = attr_get_max_val(pdu->params[0]);
+ if (!len) {
+ error("Attribute is invalid: %u", pdu->params[0]);
+ goto err;
+ }
+
+ for (i = 1; i <= len; i++)
+ pdu->params[i] = i;
+
+ pdu->params[0] = len;
+ pdu->params_len = htons(len + 1);
+
+ return len + 1;
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -766,6 +812,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:21

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 07/22] avrcp: handle ListPlayerApplicationSettingAttributes pdu

Example of response obtained with PTS test TC_TG_PAS_BV_06_C:

> ACL data: handle 11 flags 0x02 dlen 17
L2CAP(d): cid 0x0043 len 13 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: ListPlayerApplicationSettingAttributes: pt 0x00 len 0x0000
< ACL data: handle 11 flags 0x02 dlen 20
L2CAP(d): cid 0x0043 len 16 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: ListPlayerApplicationSettingAttributes: pt 0x00 len 0x0003
AttributeCount: 0x02
AttributeID: 0x01 (Equalizer ON/OFF Status)
AttributeID: 0x04 (Scan ON/OFF Status)
---
audio/control.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 53 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index a42cafd..36ddde2 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -113,6 +113,7 @@

/* PDU types for metadata transfer */
#define AVRCP_GET_CAPABILITIES 0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11

/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
@@ -609,6 +610,13 @@ static void mp_set_attribute(struct media_player *mp,
mp->settings[attr] = val;
}

+static int mp_get_attribute(struct media_player *mp, uint8_t attr)
+{
+ DBG("Get attribute: %u", attr);
+
+ return mp->settings[attr];
+}
+
static void mp_set_media_attributes(struct control *control,
struct media_info *mi)
{
@@ -674,6 +682,38 @@ err:
return -EINVAL;
}

+static int avrcp_handle_list_player_attributes(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ struct media_player *mp = control->mp;
+ unsigned int i;
+
+ if (len != 0) {
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+ }
+
+ if (!mp)
+ goto done;
+
+ for (i = 1; i <= PLAYER_SETTING_SCAN; i++) {
+ if (!mp_get_attribute(mp, i)) {
+ DBG("Ignoring setting %u: not supported by player", i);
+ continue;
+ }
+
+ len++;
+ pdu->params[len] = i;
+ }
+
+done:
+ pdu->params[0] = len;
+ pdu->params_len = htons(len + 1);
+
+ return len + 1;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -713,6 +753,19 @@ static int handle_vendordep_pdu(struct control *control,
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;
default:
/* Invalid pdu_id */
pdu->params[0] = E_INVALID_COMMAND;
--
1.7.6


2011-08-10 13:06:20

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 06/22] avrcp: handle query for company ids

Example of response for PTS test TC_TG_CFG_BV_02_C:

> ACL data: handle 11 flags 0x02 dlen 18
L2CAP(d): cid 0x0043 len 14 [psm 23]
AVCTP: Command : pt 0x00 transaction 2 pid 0x110e
AV/C: Status: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetCapabilities: pt 0x00 len 0x0001
CapabilityID: 0x02 (CompanyID)
< ACL data: handle 11 flags 0x02 dlen 22
L2CAP(d): cid 0x0043 len 18 [psm 23]
AVCTP: Response : pt 0x00 transaction 2 pid 0x110e
AV/C: Stable: address 0x48 opcode 0x00
Subunit: Panel
Opcode: Vendor Dependent
Company ID: 0x001958
AVRCP: GetCapabilities: pt 0x00 len 0x0005
CapabilityID: 0x02 (CompanyID)
CapabilityCount: 0x01
CompanyID: 0x001958
---
audio/control.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index cf3bd1f..a42cafd 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -102,6 +102,21 @@
#define FORWARD_OP 0x4b
#define BACKWARD_OP 0x4c

+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG 0x001958
+
+/* Error codes for metadata transfer */
+#define E_INVALID_COMMAND 0x00
+#define E_INVALID_PARAM 0x01
+#define E_PARAM_NOT_FOUND 0x02
+#define E_INTERNAL 0x03
+
+/* PDU types for metadata transfer */
+#define AVRCP_GET_CAPABILITIES 0x10
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID 0x02
+
#define QUIRK_NO_RELEASE 1 << 0

enum player_setting {
@@ -277,6 +292,11 @@ static struct {
{ NULL }
};

+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+ IEEEID_BTSIG,
+};
+
static GSList *avctp_callbacks = NULL;

static void auth_cb(DBusError *derr, void *user_data);
@@ -624,13 +644,88 @@ static void mp_set_media_attributes(struct control *control,
mi->ntracks, mi->track, mi->track_len);
}

+static int avrcp_handle_get_capabilities(struct control *control,
+ struct avrcp_spec_avc_pdu *pdu)
+{
+ uint16_t len = ntohs(pdu->params_len);
+ unsigned int i;
+
+ if (len != 1)
+ goto err;
+
+ DBG("id=%u", pdu->params[0]);
+
+ switch (pdu->params[0]) {
+ case CAP_COMPANY_ID:
+ for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
+ pdu->params[2 + i * 3] = company_ids[i] >> 16;
+ pdu->params[3 + i * 3] = (company_ids[i] >> 8) & 0xFF;
+ pdu->params[4 + i * 3] = company_ids[i] & 0xFF;
+ }
+
+ 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));
+ }
+
+err:
+ pdu->params[0] = E_INVALID_PARAM;
+ return -EINVAL;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
int operand_count)
{
- avrcp->code = CTYPE_NOT_IMPLEMENTED;
- return AVRCP_HEADER_LENGTH;
+ 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) {
+ avrcp->code = CTYPE_NOT_IMPLEMENTED;
+ return AVRCP_HEADER_LENGTH;
+ }
+
+ pdu->packet_type = 0;
+ pdu->rsvd = 0;
+
+ if (operand_count + 3 < AVRCP_SPECAVCPDU_HEADER_LENGTH) {
+ pdu->params[0] = E_INVALID_COMMAND;
+ 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;
+ default:
+ /* Invalid pdu_id */
+ pdu->params[0] = E_INVALID_COMMAND;
+ goto err_metadata;
+ }
+
+ return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + len;
+
+err_metadata:
+ avrcp->code = CTYPE_REJECTED;
+ pdu->params_len = htons(1);
+
+ return AVRCP_HEADER_LENGTH + AVRCP_SPECAVCPDU_HEADER_LENGTH + 1;
}

static void avctp_disconnected(struct audio_device *dev)
--
1.7.6


2011-08-10 13:06:19

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 05/22] avrcp: implement ChangeTrack() method

ChangeTrack() is used by applications to inform bluetoothd that current
track changed, passing also the metadata.
---
audio/control.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 163 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index 672f640..cf3bd1f 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -226,6 +226,13 @@ struct avctp_server {
};

struct media_info {
+ char *title;
+ char *artist;
+ char *album;
+ char *genre;
+ uint32_t ntracks;
+ uint32_t track;
+ uint32_t track_len;
uint32_t elapsed;
};

@@ -582,6 +589,41 @@ static void mp_set_attribute(struct media_player *mp,
mp->settings[attr] = val;
}

+static void mp_set_media_attributes(struct control *control,
+ struct media_info *mi)
+{
+ struct media_player *mp = control->mp;
+
+ g_free(mp->mi.title);
+ mp->mi.title = g_strdup(mi->title);
+
+ g_free(mp->mi.artist);
+ mp->mi.artist = g_strdup(mi->artist);
+
+ g_free(mp->mi.album);
+ mp->mi.album = g_strdup(mi->album);
+
+ g_free(mp->mi.genre);
+ mp->mi.genre = g_strdup(mi->genre);
+
+ mp->mi.ntracks = mi->ntracks;
+ mp->mi.track = mi->track;
+ mp->mi.track_len = mi->track_len;
+
+ /*
+ * elapsed is special. Whenever the track changes, we reset it to 0,
+ * so client doesn't have to make another call to change_playback
+ */
+ mp->mi.elapsed = 0;
+ g_timer_start(mp->timer);
+
+ DBG("Track changed:\n\ttitle: %s\n\tartist: %s\n\talbum: %s\n"
+ "\tgenre: %s\n\tNumber of tracks: %u\n"
+ "\tTrack number: %u\n\tTrack duration: %u",
+ mi->title, mi->artist, mi->album, mi->genre,
+ mi->ntracks, mi->track, mi->track_len);
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -864,6 +906,19 @@ static void init_uinput(struct control *control)
DBG("AVRCP: uinput initialized for %s", address);
}

+static void media_info_init(struct media_info *mi)
+{
+ memset(mi, 0, sizeof(*mi));
+
+ /*
+ * As per section 5.4.1 of AVRCP 1.3 spec, return 0xFFFFFFFF if TG
+ * does not support these attributes (i.e. they were never set via
+ * D-Bus)
+ */
+ mi->track_len = 0xFFFFFFFF;
+ mi->elapsed = 0xFFFFFFFF;
+}
+
static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
{
struct control *control = data;
@@ -1355,9 +1410,116 @@ static DBusMessage *mp_change_playback(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static gboolean media_info_parse(DBusMessageIter *iter, struct media_info *mi)
+{
+ DBusMessageIter dict;
+ DBusMessageIter var;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ media_info_init(mi);
+ dbus_message_iter_recurse(iter, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return FALSE;
+
+ dbus_message_iter_recurse(&entry, &var);
+
+ if (!strcmp(key, "Title")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->title);
+ } else if (!strcmp(key, "Artist")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->artist);
+ } else if (!strcmp(key, "Album")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->album);
+ } else if (!strcmp(key, "Genre")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->genre);
+ } else if (!strcmp(key, "NumberOfTracks")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->ntracks);
+ } else if (!strcmp(key, "TrackNumber")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->track);
+ } else if (!strcmp(key, "TrackDuration")) {
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &mi->track_len);
+ } else {
+ return FALSE;
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (mi->title == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static DBusMessage *mp_change_track(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ DBusMessageIter iter;
+ struct media_info mi;
+
+
+ dbus_message_iter_init(msg, &iter);
+ if (!media_info_parse(&iter, &mi))
+ return btd_error_invalid_args(msg);
+
+ mp_set_media_attributes(control, &mi);
+
+ return dbus_message_new_method_return(msg);
+}
+
static GDBusMethodTable mp_methods[] = {
{ "SetProperty", "sv", "", mp_set_property },
{ "ChangePlayback", "su", "", mp_change_playback },
+ { "ChangeTrack", "a{sv}", "", mp_change_track },
{ }
};

@@ -1451,6 +1613,7 @@ struct control *control_init(struct audio_device *dev, uint16_t uuid16,
MEDIA_PLAYER_INTERFACE, dev->path);

mp->timer = g_timer_new();
+ media_info_init(&mp->mi);
control->mp = mp;
}

--
1.7.6


2011-08-10 13:06:18

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 04/22] avrcp: implement ChangePlayback() method

ChangePlayback() is used by applications to inform bluetoothd of the
current status of playback.
---
audio/control.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index d2365bf..672f640 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -135,6 +135,15 @@ enum scan_mode {
SCAN_MODE_GROUP = 3,
};

+enum play_status {
+ PLAY_STATUS_STOPPED = 0x00,
+ PLAY_STATUS_PLAYING = 0x01,
+ PLAY_STATUS_PAUSED = 0x02,
+ PLAY_STATUS_FWD_SEEK = 0x03,
+ PLAY_STATUS_REV_SEEK = 0x04,
+ PLAY_STATUS_ERROR = 0xFF
+};
+
static DBusConnection *connection = NULL;

static GSList *servers = NULL;
@@ -216,8 +225,16 @@ struct avctp_server {
uint32_t ct_record_id;
};

+struct media_info {
+ uint32_t elapsed;
+};
+
struct media_player {
uint8_t settings[PLAYER_SETTING_SCAN + 1];
+ enum play_status status;
+
+ struct media_info mi;
+ GTimer *timer;
};

struct control {
@@ -523,6 +540,40 @@ static int attr_to_val(const char *str)
return -EINVAL;
}

+static int play_status_to_val(const char *status)
+{
+ if (!strcmp(status, "stopped"))
+ return PLAY_STATUS_STOPPED;
+ else if (!strcmp(status, "playing"))
+ return PLAY_STATUS_PLAYING;
+ else if (!strcmp(status, "paused"))
+ return PLAY_STATUS_PAUSED;
+ else if (!strcmp(status, "forward-seek"))
+ return PLAY_STATUS_FWD_SEEK;
+ else if (!strcmp(status, "reverse-seek"))
+ return PLAY_STATUS_REV_SEEK;
+ else if (!strcmp(status, "error"))
+ return PLAY_STATUS_ERROR;
+
+ return -EINVAL;
+}
+
+static void mp_set_playback_status(struct control *control, uint8_t status,
+ uint32_t elapsed)
+{
+ struct media_player *mp = control->mp;
+
+ DBG("Change playback: %u %u", status, elapsed);
+
+ mp->mi.elapsed = elapsed;
+ g_timer_start(mp->timer);
+
+ if (status == mp->status)
+ return;
+
+ mp->status = status;
+}
+
static void mp_set_attribute(struct media_player *mp,
uint8_t attr, uint8_t val)
{
@@ -1281,8 +1332,32 @@ static DBusMessage *mp_set_property(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *mp_change_playback(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ const char *statusstr;
+ int status;
+ uint32_t elapsed;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &statusstr,
+ DBUS_TYPE_UINT32, &elapsed,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ status = play_status_to_val(statusstr);
+ if (status < 0)
+ return btd_error_invalid_args(msg);
+
+ mp_set_playback_status(control, status, elapsed);
+
+ return dbus_message_new_method_return(msg);
+}
+
static GDBusMethodTable mp_methods[] = {
{ "SetProperty", "sv", "", mp_set_property },
+ { "ChangePlayback", "su", "", mp_change_playback },
{ }
};

@@ -1314,6 +1389,7 @@ static void mp_path_unregister(void *data)
DBG("Unregistered interface %s on path %s",
MEDIA_PLAYER_INTERFACE, dev->path);

+ g_timer_destroy(mp->timer);
g_free(mp);
control->mp = NULL;
}
@@ -1374,6 +1450,7 @@ struct control *control_init(struct audio_device *dev, uint16_t uuid16,
DBG("Registered interface %s on path %s",
MEDIA_PLAYER_INTERFACE, dev->path);

+ mp->timer = g_timer_new();
control->mp = mp;
}

--
1.7.6


2011-08-10 13:06:17

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 03/22] avrcp: implement SetProperty() method of MediaPlayer

SetProperty() is used by an application to set player specific
settings.
---
audio/control.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 153 insertions(+), 0 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index cc6532e..d2365bf 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -104,6 +104,37 @@

#define QUIRK_NO_RELEASE 1 << 0

+enum player_setting {
+ PLAYER_SETTING_EQUALIZER = 1,
+ PLAYER_SETTING_REPEAT = 2,
+ PLAYER_SETTING_SHUFFLE = 3,
+ PLAYER_SETTING_SCAN = 4,
+};
+
+enum equalizer_mode {
+ EQUALIZER_MODE_OFF = 1,
+ EQUALIZER_MODE_ON = 2,
+};
+
+enum repeat_mode {
+ REPEAT_MODE_OFF = 1,
+ REPEAT_MODE_SINGLE = 2,
+ REPEAT_MODE_ALL = 3,
+ REPEAT_MODE_GROUP = 4,
+};
+
+enum shuffle_mode {
+ SHUFFLE_MODE_OFF = 1,
+ SHUFFLE_MODE_ALL = 2,
+ SHUFFLE_MODE_GROUP = 3,
+};
+
+enum scan_mode {
+ SCAN_MODE_OFF = 1,
+ SCAN_MODE_ALL = 2,
+ SCAN_MODE_GROUP = 3,
+};
+
static DBusConnection *connection = NULL;

static GSList *servers = NULL;
@@ -186,6 +217,7 @@ struct avctp_server {
};

struct media_player {
+ uint8_t settings[PLAYER_SETTING_SCAN + 1];
};

struct control {
@@ -423,6 +455,82 @@ static void handle_panel_passthrough(struct control *control,
operands[0] & 0x7F, status);
}

+static int attrval_to_val(uint8_t attr, const char *value)
+{
+ int ret;
+
+ switch (attr) {
+ case PLAYER_SETTING_EQUALIZER:
+ if (!strcmp(value, "off"))
+ ret = EQUALIZER_MODE_OFF;
+ else if (!strcmp(value, "on"))
+ ret = EQUALIZER_MODE_ON;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case PLAYER_SETTING_REPEAT:
+ if (!strcmp(value, "off"))
+ ret = REPEAT_MODE_OFF;
+ else if (!strcmp(value, "singletrack"))
+ ret = REPEAT_MODE_SINGLE;
+ else if (!strcmp(value, "alltracks"))
+ ret = REPEAT_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = REPEAT_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case PLAYER_SETTING_SHUFFLE:
+ if (!strcmp(value, "off"))
+ ret = SHUFFLE_MODE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = SHUFFLE_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = SHUFFLE_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case PLAYER_SETTING_SCAN:
+ if (!strcmp(value, "off"))
+ ret = SCAN_MODE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = SCAN_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = SCAN_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int attr_to_val(const char *str)
+{
+ if (!strcmp(str, "Equalizer"))
+ return PLAYER_SETTING_EQUALIZER;
+ else if (!strcmp(str, "Repeat"))
+ return PLAYER_SETTING_REPEAT;
+ else if (!strcmp(str, "Shuffle"))
+ return PLAYER_SETTING_SHUFFLE;
+ else if (!strcmp(str, "Scan"))
+ return PLAYER_SETTING_SCAN;
+
+ return -EINVAL;
+}
+
+static void mp_set_attribute(struct media_player *mp,
+ uint8_t attr, uint8_t val)
+{
+ DBG("Change attribute: %u %u", attr, val);
+
+ mp->settings[attr] = val;
+}
+
/* handle vendordep pdu inside an avctp packet */
static int handle_vendordep_pdu(struct control *control,
struct avrcp_header *avrcp,
@@ -1129,7 +1237,52 @@ static GDBusSignalTable control_signals[] = {
{ NULL, NULL }
};

+static DBusMessage *mp_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *attrstr, *valstr;
+ int attr, val;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &attrstr);
+
+ attr = attr_to_val(attrstr);
+ if (attr < 0)
+ return btd_error_not_supported(msg);
+
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ /* Only string arguments are supported for now */
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &valstr);
+
+ val = attrval_to_val(attr, valstr);
+ if (val < 0)
+ return btd_error_not_supported(msg);
+
+ mp_set_attribute(control->mp, attr, val);
+
+ return dbus_message_new_method_return(msg);
+}
+
static GDBusMethodTable mp_methods[] = {
+ { "SetProperty", "sv", "", mp_set_property },
{ }
};

--
1.7.6


2011-08-10 13:06:16

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 02/22] avrcp: add skeleton of MediaPlayer interface

---
audio/control.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
audio/control.h | 4 ++-
audio/manager.c | 11 ++++++++-
audio/manager.h | 1 +
4 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/audio/control.c b/audio/control.c
index c3ef737..cc6532e 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -185,8 +185,12 @@ struct avctp_server {
uint32_t ct_record_id;
};

+struct media_player {
+};
+
struct control {
struct audio_device *dev;
+ struct media_player *mp;

avctp_state_t state;

@@ -1125,6 +1129,14 @@ static GDBusSignalTable control_signals[] = {
{ NULL, NULL }
};

+static GDBusMethodTable mp_methods[] = {
+ { }
+};
+
+static GDBusSignalTable mp_signals[] = {
+ { }
+};
+
static void path_unregister(void *data)
{
struct audio_device *dev = data;
@@ -1140,10 +1152,29 @@ static void path_unregister(void *data)
dev->control = NULL;
}

+static void mp_path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+ struct control *control = dev->control;
+ struct media_player *mp = control->mp;
+
+ DBG("Unregistered interface %s on path %s",
+ MEDIA_PLAYER_INTERFACE, dev->path);
+
+ g_free(mp);
+ control->mp = NULL;
+}
+
void control_unregister(struct audio_device *dev)
{
+ struct control *control = dev->control;
+
+ if (control->mp)
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ MEDIA_PLAYER_INTERFACE);
+
g_dbus_unregister_interface(dev->conn, dev->path,
- AUDIO_CONTROL_INTERFACE);
+ AUDIO_CONTROL_INTERFACE);
}

void control_update(struct audio_device *dev, uint16_t uuid16)
@@ -1154,7 +1185,8 @@ void control_update(struct audio_device *dev, uint16_t uuid16)
control->target = TRUE;
}

-struct control *control_init(struct audio_device *dev, uint16_t uuid16)
+struct control *control_init(struct audio_device *dev, uint16_t uuid16,
+ gboolean media_player)
{
struct control *control;

@@ -1172,6 +1204,26 @@ struct control *control_init(struct audio_device *dev, uint16_t uuid16)
control->state = AVCTP_STATE_DISCONNECTED;
control->uinput = -1;

+ if (media_player) {
+ struct media_player *mp;
+
+ mp = g_new0(struct media_player, 1);
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ MEDIA_PLAYER_INTERFACE,
+ mp_methods, mp_signals, NULL,
+ dev, mp_path_unregister)) {
+ error("D-Bus failed do register %s on path %s",
+ MEDIA_PLAYER_INTERFACE, dev->path);
+ g_free(mp);
+ }
+
+ DBG("Registered interface %s on path %s",
+ MEDIA_PLAYER_INTERFACE, dev->path);
+
+ control->mp = mp;
+ }
+
if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
control->target = TRUE;

diff --git a/audio/control.h b/audio/control.h
index 49f25c2..635a2d5 100644
--- a/audio/control.h
+++ b/audio/control.h
@@ -23,6 +23,7 @@
*/

#define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"

typedef enum {
AVCTP_STATE_DISCONNECTED = 0,
@@ -44,7 +45,8 @@ void avrcp_unregister(const bdaddr_t *src);
gboolean avrcp_connect(struct audio_device *dev);
void avrcp_disconnect(struct audio_device *dev);

-struct control *control_init(struct audio_device *dev, uint16_t uuid16);
+struct control *control_init(struct audio_device *dev, uint16_t uuid16,
+ gboolean media_player);
void control_update(struct audio_device *dev, uint16_t uuid16);
void control_unregister(struct audio_device *dev);
gboolean control_is_active(struct audio_device *dev);
diff --git a/audio/manager.c b/audio/manager.c
index 05ca654..4afa688 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -117,7 +117,8 @@ static struct enabled_interfaces enabled = {
.source = FALSE,
.control = TRUE,
.socket = TRUE,
- .media = FALSE
+ .media = FALSE,
+ .media_player = FALSE,
};

static struct audio_adapter *find_adapter(GSList *list,
@@ -220,7 +221,8 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
if (device->control)
control_update(device, uuid16);
else
- device->control = control_init(device, uuid16);
+ device->control = control_init(device, uuid16,
+ enabled.media_player);
if (device->sink && sink_is_active(device))
avrcp_connect(device);
break;
@@ -1169,6 +1171,9 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.socket = TRUE;
else if (g_str_equal(list[i], "Media"))
enabled.media = TRUE;
+ else if (g_str_equal(list[i], "MediaPlayer"))
+ enabled.media_player = TRUE;
+
}
g_strfreev(list);

@@ -1189,6 +1194,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.socket = FALSE;
else if (g_str_equal(list[i], "Media"))
enabled.media = FALSE;
+ else if (g_str_equal(list[i], "MediaPlayer"))
+ enabled.media_player = FALSE;
}
g_strfreev(list);

diff --git a/audio/manager.h b/audio/manager.h
index 0bf7663..cfc646c 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -31,6 +31,7 @@ struct enabled_interfaces {
gboolean control;
gboolean socket;
gboolean media;
+ gboolean media_player;
};

int audio_manager_init(DBusConnection *conn, GKeyFile *config,
--
1.7.6


2011-08-10 13:06:15

by Lucas De Marchi

[permalink] [raw]
Subject: [PATCH v3 01/22] audio: move interface declarations to their headers

---
audio/device.h | 4 ----
audio/unix.c | 1 +
2 files changed, 1 insertions(+), 4 deletions(-)

diff --git a/audio/device.h b/audio/device.h
index 35af788..5117fca 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -38,10 +38,6 @@
#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb"
#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb"

-/* Move these to respective .h files once they exist */
-#define AUDIO_SOURCE_INTERFACE "org.bluez.AudioSource"
-#define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
-
struct source;
struct control;
struct target;
diff --git a/audio/unix.c b/audio/unix.c
index 8ce50b0..1e0ab30 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -49,6 +49,7 @@
#include "a2dp.h"
#include "headset.h"
#include "sink.h"
+#include "source.h"
#include "gateway.h"
#include "unix.h"
#include "glib-helper.h"
--
1.7.6