* Re: [RFCv1 03/11] Bluetooth: AMP: Process Physical Link Complete evt
From: Andrei Emeltchenko @ 2012-10-29 9:22 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth
In-Reply-To: <alpine.DEB.2.02.1210261012440.2459@mathewm-linux>
Hi Mat,
On Fri, Oct 26, 2012 at 10:16:53AM -0700, Mat Martineau wrote:\
...
> >+static void hci_phy_link_complete_evt(struct hci_dev *hdev,
> >+ struct sk_buff *skb)
> >+{
> >+ struct hci_ev_phy_link_complete *ev = (void *) skb->data;
> >+ struct hci_conn *hcon, *bredr_hcon;
> >+
> >+ BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle,
> >+ ev->status);
> >+
> >+ hci_dev_lock(hdev);
> >+
> >+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
> >+ if (!hcon) {
> >+ hci_dev_unlock(hdev);
> >+ return;
> >+ }
> >+
> >+ if (ev->status) {
> >+ hci_conn_del(hcon);
> >+ hci_dev_unlock(hdev);
> >+ return;
> >+ }
> >+
> >+ bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
> >+
> >+ hcon->state = BT_CONNECTED;
> >+ bacpy(&hcon->dst, &bredr_hcon->dst);
> >+
> >+ hci_conn_hold(hcon);
> >+ hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
> >+ hci_conn_put(hcon);
> >+
> >+ hci_conn_hold_device(hcon);
> >+ hci_conn_add_sysfs(hcon);
> >+
> >+ hci_dev_unlock(hdev);
> >+
> >+ if (hcon->out) {
> >+ struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
> >+
> >+ if (!bredr_hdev)
> >+ return;
> >+
> >+ /* Placeholder - create chan req
> >+ l2cap_chan_create_cfm(bredr_hcon, hcon->remote_id);
> >+ */
>
> I think this is where you would call l2cap_physical_cfm(), but that
> function requires more information. Is there enough context in hcon
> to get the local amp ID
This one is easy to manage: hcon->dev->id
> and l2cap_chan, or does the AMP manager need
> to be notified of the physical link so it can match up the physical
> link with other information?
I get chan from amp_mgr through hcon->amp_mgr
I will send the patch later this week.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [RFCv1 05/11] Bluetooth: AMP: Add Logical Link Create function
From: Andrei Emeltchenko @ 2012-10-29 9:24 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth
In-Reply-To: <alpine.DEB.2.02.1210260958240.2459@mathewm-linux>
Hi Mat,
On Fri, Oct 26, 2012 at 10:01:01AM -0700, Mat Martineau wrote:
...
> >@@ -3948,13 +3955,15 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
> > goto done;
> > }
> >
> >- /* check compatibility */
> >-
> > if (!chan->ctrl_id)
> > l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
> > 0);
> >- else
> >- chan->ident = cmd->ident;
> >+ else {
> >+ if (l2cap_check_efs(chan)) {
> >+ amp_create_logical_link(chan);
> >+ chan->ident = cmd->ident;
> >+ }
> >+ }
>
> Minor style issue - if one block of an if/else needs braces, then
> they all get braces.
Sure. Will fix this.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [PATCH] workqueue: cancel_delayed_work() should return %NULL if work item is idle
From: Andrei Emeltchenko @ 2012-10-29 9:47 UTC (permalink / raw)
To: Tejun Heo, Gustavo Padovan, linux-bluetooth
Cc: Dan Magenheimer, linux-kernel, Konrad Wilk
In-Reply-To: <20121018233928.GY13370@google.com>
Gustavo, could you apply the patch below to bluetooth-next tree, otherwise
it is deadly broken.
Best regards
Andrei Emeltchenko
On Thu, Oct 18, 2012 at 04:39:28PM -0700, Tejun Heo wrote:
> From e65120fcfc1cb9697655d29ecd7982451c05d3c2 Mon Sep 17 00:00:00 2001
> From: Dan Magenheimer <dan.magenheimer@oracle.com>
> Date: Thu, 18 Oct 2012 16:31:37 -0700
>
> 57b30ae77b ("workqueue: reimplement cancel_delayed_work() using
> try_to_grab_pending()") made cancel_delayed_work() always return %true
> unless someone else is also trying to cancel the work item, which is
> broken - if the target work item is idle, the return value should be
> %false.
>
> try_to_grab_pending() indicates that the target work item was idle by
> zero return value. Use it for return. Note that this brings
> cancel_delayed_work() in line with __cancel_work_timer() in return
> value handling.
>
> Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> LKML-Reference: <444a6439-b1a4-4740-9e7e-bc37267cfe73@default>
> ---
> kernel/workqueue.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index d951daa..042d221 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -2982,7 +2982,7 @@ bool cancel_delayed_work(struct delayed_work *dwork)
>
> set_work_cpu_and_clear_pending(&dwork->work, work_cpu(&dwork->work));
> local_irq_restore(flags);
> - return true;
> + return ret;
> }
> EXPORT_SYMBOL(cancel_delayed_work);
>
> --
> 1.7.7.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* Re: [PATCH BlueZ v4] AVDTP: Do not keep a internal reference
From: Johan Hedberg @ 2012-10-29 11:07 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1351259069-21516-1-git-send-email-luiz.dentz@gmail.com>
Hi Luiz,
On Fri, Oct 26, 2012, Luiz Augusto von Dentz wrote:
> Don't initialize reference with 1, instead always start disconnect timer
> when reference drops to 0, so in case nobody reclaims the session it
> automatically disconnect after 1 second and frees the memory.
> ---
> v2: Fix with stream_setup flag being ignored in disconnect_timeout
> v3: Do avrcp_free on connection_lost to avoid having dangling sessions in
> disconnected state waiting the timeout to be freed.
> v4: Remove unnecessary reference when authorizing, disconnect timer is only
> started when the session is really connected.
>
> audio/avdtp.c | 204 +++++++++++++++++++++++-----------------------------------
> 1 file changed, 82 insertions(+), 122 deletions(-)
Applied. Thanks.
Johan
^ permalink raw reply
* Re: Crashing bluetoothd when keyboard enters sleep mode / stand-by
From: Johan Hedberg @ 2012-10-29 11:21 UTC (permalink / raw)
To: Patkos Csaba; +Cc: linux-bluetooth
In-Reply-To: <op.wmwjsbelgqbfom@localhost>
Hi,
On Sun, Oct 28, 2012, Patkos Csaba wrote:
> When my keyboard enters stand-by and disconnects from the computer I
> see high cpu usage by bluetoothd. Sometimes, not always, if I don't
> wake up the keyboard, bluetoothd will crash
Could you please try to get a proper backtrace of what's happening.
I.e. run bluetoothd with valgrind or gdb, e.g.:
sudo killall bluetoothd
sudo valgrind /usr/sbin/bluetoothd -nd
If this doesn't give useful info because of missing debug symbols either
try to install them (no idea how to do that with your distro though) or
compile and run 4.101 from the source tree (no need to install the
custom compilation on your system).
The HCI-level log would also be helpful in addition to the bluetoothd
debug logs, so please run "hcidump -X" in a separate terminal while
reproducing the issue. Thanks.
Johan
^ permalink raw reply
* [PATCH] Add bluetooth support for VAIO VPCEH [0489:e027]
From: Marcos Chaparro @ 2012-10-29 13:05 UTC (permalink / raw)
To: Marcel Holtmann, Gustavo Padovan, Johan Hedberg, linux-bluetooth
Cc: linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 907 bytes --]
Hi,
this patch against 3.7-rc3 identifies my Atheros AR3011 internal bluetooth
device, I just transferred some files from my phone to this debian PC using
bluetooth and it works fine (btw, it needs atheros-firmware package).
Before changing this, lsusb was identifying it as a Foxconn / Hon Hai device,
which is wrong, and it probably loaded a different firmware to the bluetooth
module.
The patches solves the following discussions:
https://answers.launchpad.net/ubuntu/+source/bluez/+question/168992
http://ubuntuforums.org/showthread.php?t=1867214
http://linux-unix-open-source.1053819.n5.nabble.com/Atheros-AR9285-on-
Ubuntu-11-10-Sony-Vaio-VPCEH-wifi-does-t-works-td4931321.html
http://www.vivaolinux.com.br/topico/Rede-Wireless/Bluetooth-vaio-VPCEH-
desabilitado
Please tell me if you won't add it to mainstream for some reason.
Thanks in advance, and greetings from Argentina.
--
Marcos
[-- Attachment #1.2: Type: text/html, Size: 4344 bytes --]
[-- Attachment #2: patchfile --]
[-- Type: text/x-patch, Size: 1165 bytes --]
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c linux-3.7-rc3.new/drivers/bluetooth/ath3k.c
--- linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/ath3k.c 2012-10-29 09:39:03.459764720 -0300
@@ -67,6 +67,7 @@
{ USB_DEVICE(0x13d3, 0x3304) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x0489, 0xE027) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/btusb.c linux-3.7-rc3.new/drivers/bluetooth/btusb.c
--- linux-3.7-rc3.orig/drivers/bluetooth/btusb.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/btusb.c 2012-10-29 09:39:06.219765159 -0300
@@ -124,6 +124,7 @@
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
^ permalink raw reply
* [PATCH] Add bluetooth support for VAIO VPCEH [0489:e027]
From: Marcos Chaparro @ 2012-10-29 13:09 UTC (permalink / raw)
To: Marcel Holtmann, Gustavo Padovan, Johan Hedberg, linux-bluetooth
Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 947 bytes --]
Hi,
this patch against 3.7-rc3 identifies my Atheros AR3011 internal bluetooth
device, I just transferred some files from my phone to this debian PC using
bluetooth and it works fine (btw, it needs atheros-firmware package).
Before changing this, lsusb was identifying it as a Foxconn / Hon Hai device,
which is wrong, and it probably loaded a different firmware to the bluetooth
module.
The patches solves the following discussions:
https://answers.launchpad.net/ubuntu/+source/bluez/+question/168992
http://ubuntuforums.org/showthread.php?t=1867214
http://linux-unix-open-source.1053819.n5.nabble.com/Atheros-AR9285-on-
Ubuntu-11-10-Sony-Vaio-VPCEH-wifi-does-t-works-td4931321.html
http://www.vivaolinux.com.br/topico/Rede-Wireless/Bluetooth-vaio-VPCEH-
desabilitado
Please tell me if you won't add it to mainstream for some reason.
Thanks in advance, and greetings from Argentina.
--
Marcos
PS: Sent again without HTML formatting
[-- Attachment #2: patchfile --]
[-- Type: text/x-patch, Size: 1165 bytes --]
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c linux-3.7-rc3.new/drivers/bluetooth/ath3k.c
--- linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/ath3k.c 2012-10-29 09:39:03.459764720 -0300
@@ -67,6 +67,7 @@
{ USB_DEVICE(0x13d3, 0x3304) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x0489, 0xE027) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/btusb.c linux-3.7-rc3.new/drivers/bluetooth/btusb.c
--- linux-3.7-rc3.orig/drivers/bluetooth/btusb.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/btusb.c 2012-10-29 09:39:06.219765159 -0300
@@ -124,6 +124,7 @@
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
^ permalink raw reply
* [PATCH BlueZ 1/7] AVRCP: Add initial support for controller player
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This also bump controller record to 1.3.
---
audio/avrcp.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 441 insertions(+), 90 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 57502ff..4f14513 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -190,6 +190,7 @@ struct avrcp {
int features;
void (*init) (struct avrcp *session);
+ void (*destroy) (struct avrcp *sesion);
const struct control_pdu_handler *control_handlers;
@@ -216,7 +217,7 @@ static uint32_t company_ids[] = {
IEEEID_BTSIG,
};
-static void register_notification(struct avrcp *session, uint8_t event);
+static void avrcp_register_notification(struct avrcp *session, uint8_t event);
static sdp_record_t *avrcp_ct_record(void)
{
@@ -227,7 +228,7 @@ static sdp_record_t *avrcp_ct_record(void)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -431,20 +432,12 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}
-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
- int value;
-
- DBG("attr %u", attr);
-
if (player == NULL)
return -ENOENT;
- value = player->cb->get_setting(attr, player->user_data);
- if (value < 0)
- DBG("attr %u not supported by player", attr);
-
- return value;
+ return player->cb->get_setting(id, player->user_data);
}
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -490,7 +483,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;
- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -643,14 +636,6 @@ static int player_set_setting(struct avrcp_player *player, uint8_t id,
return player->cb->set_setting(id, val, player->user_data);
}
-static int player_get_setting(struct avrcp_player *player, uint8_t id)
-{
- if (player == NULL)
- return -ENOENT;
-
- return player->cb->get_setting(id, player->user_data);
-}
-
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1095,7 +1080,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;
- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -1357,6 +1342,319 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}
+static const char *status_to_string(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_PLAY_STATUS_STOPPED:
+ return "stopped";
+ case AVRCP_PLAY_STATUS_PLAYING:
+ return "playing";
+ case AVRCP_PLAY_STATUS_PAUSED:
+ return "paused";
+ case AVRCP_PLAY_STATUS_FWD_SEEK:
+ return "forward-seek";
+ case AVRCP_PLAY_STATUS_REV_SEEK:
+ return "reverse-seek";
+ case AVRCP_PLAY_STATUS_ERROR:
+ return "error";
+ default:
+ return NULL;
+ }
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint32_t duration;
+ uint32_t position;
+ uint8_t status;
+
+ if (code == AVC_CTYPE_REJECTED || ntohs(pdu->params_len) != 9)
+ return FALSE;
+
+ memcpy(&duration, pdu->params, sizeof(uint32_t));
+ duration = ntohl(duration);
+
+ memcpy(&position, pdu->params + 4, sizeof(uint32_t));
+ position = ntohl(position);
+
+ memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+ DBG("%s", status_to_string(status));
+
+ return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_get_play_status_rsp,
+ session);
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ switch (value) {
+ case AVRCP_EQUALIZER_ON:
+ return "on";
+ case AVRCP_EQUALIZER_OFF:
+ return "off";
+ }
+
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ switch (value) {
+ case AVRCP_REPEAT_MODE_OFF:
+ return "off";
+ case AVRCP_REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case AVRCP_REPEAT_MODE_ALL:
+ return "alltracks";
+ case AVRCP_REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ case AVRCP_ATTRIBUTE_SCAN:
+ switch (value) {
+ case AVRCP_SCAN_OFF:
+ return "off";
+ case AVRCP_SCAN_ALL:
+ return "alltracks";
+ case AVRCP_SCAN_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return "Equalizer";
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return "Repeat";
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return "Shuffle";
+ case AVRCP_ATTRIBUTE_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
+static gboolean avrcp_player_value_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (pdu->params_len < count * 2)
+ return FALSE;
+
+ for (i = 1; 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;
+
+ DBG("%s: %s", key, value);
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_current_player_value(struct avrcp *session,
+ uint8_t *attrs, uint8_t count)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 5];
+ struct avrcp_header *pdu = (void *) buf;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params_len = htons(count + 1);
+ pdu->params[0] = count;
+
+ for (i = 0; count > 0; count--, i++)
+ pdu->params[i + 1] = attrs[i];
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_player_value_rsp, session);
+}
+
+static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) < count) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ avrcp_get_current_player_value(session, &pdu->params[1],
+ pdu->params[0]);
+
+ return FALSE;
+}
+
+static void avrcp_list_player_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_list_player_attributes_rsp,
+ session);
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ return "Title";
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ return "Artist";
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ return "Album";
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ return "Genre";
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ return "Track";
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ return "NumberOfTracks";
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ return "Duration";
+ }
+
+ return NULL;
+}
+
+static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) - 1 < count * 8) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ for (i = 1; count > 0; count--) {
+ uint32_t id;
+ uint16_t charset, len;
+
+ memcpy(&id, &pdu->params[i], sizeof(uint32_t));
+ id = ntohl(id);
+ i += sizeof(uint32_t);
+
+ memcpy(&charset, &pdu->params[i], sizeof(uint16_t));
+ charset = ntohs(charset);
+ i += sizeof(uint16_t);
+
+ memcpy(&len, &pdu->params[i], sizeof(uint16_t));
+ len = ntohs(len);
+ i += sizeof(uint16_t);
+
+ if (charset == 106) {
+ const char *key = metadata_to_str(id);
+ char *value = g_strndup((char *) &pdu->params[i], len);
+
+ DBG("%s: %s", key , value);
+ g_free(value);
+ }
+
+ i += len;
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_element_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+ pdu->params_len = htons(9);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_get_attributes_rsp,
+ session);
+}
+
static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1366,7 +1664,9 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
- uint8_t volume;
+ uint8_t value;
+ uint8_t count;
+ int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
@@ -1375,24 +1675,58 @@ static gboolean avrcp_handle_event(struct avctp *conn,
switch (event) {
case AVRCP_EVENT_VOLUME_CHANGED:
- volume = pdu->params[1] & 0x7F;
+ value = pdu->params[1] & 0x7F;
if (player)
- player->cb->set_volume(volume, session->dev,
+ player->cb->set_volume(value, session->dev,
player->user_data);
break;
+ case AVRCP_EVENT_STATUS_CHANGED:
+ value = pdu->params[1];
+
+ avrcp_get_play_status(session);
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ avrcp_get_element_attributes(session);
+
+ break;
+
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ 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;
+
+ DBG("%s: %s", key, value);
+ }
+
+ break;
}
if (code == AVC_CTYPE_CHANGED) {
- register_notification(session, event);
+ session->registered_events ^= (1 << event);
+ avrcp_register_notification(session, event);
return FALSE;
}
+ session->registered_events |= (1 << event);
+
return TRUE;
}
-static void register_notification(struct avrcp *session, uint8_t event)
+static void avrcp_register_notification(struct avrcp *session, uint8_t event)
{
uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
struct avrcp_header *pdu = (void *) buf;
@@ -1420,6 +1754,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
{
struct avrcp *session = user_data;
struct avrcp_header *pdu = (void *) operands;
+ uint16_t events = 0;
uint8_t count;
if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
@@ -1430,14 +1765,26 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
for (; count > 0; count--) {
uint8_t event = pdu->params[1 + count];
+ events |= (1 << event);
+
switch (event) {
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
- register_notification(session, event);
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ avrcp_register_notification(session, event);
break;
}
}
+ if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+ avrcp_list_player_attributes(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_play_status(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_element_attributes(session);
+
return FALSE;
}
@@ -1463,31 +1810,6 @@ static void avrcp_get_capabilities(struct avrcp *session)
session);
}
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
-{
- return FALSE;
-}
-
-static void avrcp_get_play_status(struct avrcp *session)
-{
- uint8_t buf[AVRCP_HEADER_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
-
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_get_play_status_rsp,
- session);
-}
-
static struct avrcp *find_session(GSList *list, struct audio_device *dev)
{
for (; list; list = list->next) {
@@ -1516,7 +1838,8 @@ static void session_tg_init(struct avrcp *session)
session->control_handlers = tg_control_handlers;
if (session->version >= 0x0104) {
- register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
+ avrcp_register_notification(session,
+ AVRCP_EVENT_VOLUME_CHANGED);
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
@@ -1533,19 +1856,75 @@ static void session_tg_init(struct avrcp *session)
static void session_ct_init(struct avrcp *session)
{
+ struct avrcp_player *player;
+
session->control_handlers = ct_control_handlers;
DBG("%p version 0x%04x", session, session->version);
- if (session->version >= 0x0103) {
- avrcp_get_capabilities(session);
- avrcp_get_play_status(session);
- }
-
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
session);
+
+ if (session->version < 0x0103)
+ return;
+
+ player = g_new0(struct avrcp_player, 1);
+ player->sessions = g_slist_prepend(player->sessions, session);
+ session->player = player;
+
+ avrcp_get_capabilities(session);
+}
+
+static void session_destroy(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+ g_free(session);
+}
+
+static void session_tg_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player->sessions = g_slist_remove(player->sessions, session);
+
+ session_destroy(session);
+}
+
+static void player_destroy(gpointer data)
+{
+ struct avrcp_player *player = data;
+
+ if (player->destroy)
+ player->destroy(player->user_data);
+
+ g_slist_free(player->sessions);
+ g_free(player);
+}
+
+static void session_ct_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player_destroy(player);
+
+ session_destroy(session);
}
static struct avrcp *session_create(struct avrcp_server *server,
@@ -1574,9 +1953,11 @@ static struct avrcp *session_create(struct avrcp_server *server,
if (session->target) {
session->init = session_tg_init;
+ session->destroy = session_tg_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
} else {
session->init = session_ct_init;
+ session->destroy = session_ct_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
}
@@ -1595,25 +1976,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
return session;
}
-static void session_destroy(struct avrcp *session)
-{
- struct avrcp_server *server = session->server;
- struct avrcp_player *player = session->player;
-
- server->sessions = g_slist_remove(server->sessions, session);
-
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
-
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(session->browsing_id);
-
- if (player != NULL)
- player->sessions = g_slist_remove(player->sessions, session);
-
- g_free(session);
-}
-
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
avctp_state_t new_state, void *user_data)
{
@@ -1631,7 +1993,7 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- session_destroy(session);
+ session->destroy(session);
break;
case AVCTP_STATE_CONNECTING:
@@ -1740,17 +2102,6 @@ int avrcp_register(const bdaddr_t *src, GKeyFile *config)
return 0;
}
-static void player_destroy(gpointer data)
-{
- struct avrcp_player *player = data;
-
- if (player->destroy)
- player->destroy(player->user_data);
-
- g_slist_free(player->sessions);
- g_free(player);
-}
-
void avrcp_unregister(const bdaddr_t *src)
{
struct avrcp_server *server;
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 2/7] audio: Export remote player as children object path of device
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The object implements org.bluez.MediaPlayer similar to the target role.
---
Makefile.am | 1 +
audio/avrcp.c | 260 ++++++++++++++++++++++++++---------------
audio/player.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
audio/player.h | 37 ++++++
doc/media-api.txt | 20 +++-
5 files changed, 564 insertions(+), 97 deletions(-)
create mode 100644 audio/player.c
create mode 100644 audio/player.h
diff --git a/Makefile.am b/Makefile.am
index 35b1520..6ac6a73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,6 +148,7 @@ builtin_sources += audio/main.c \
audio/avdtp.h audio/avdtp.c \
audio/media.h audio/media.c \
audio/transport.h audio/transport.c \
+ audio/player.h audio/player.c \
audio/telephony.h audio/a2dp-codecs.h
builtin_nodist += audio/telephony.c
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 4f14513..7c26491 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -61,6 +61,7 @@
#include "control.h"
#include "avdtp.h"
#include "sink.h"
+#include "player.h"
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -432,6 +433,36 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}
+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return "Equalizer";
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return "Repeat";
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return "Shuffle";
+ case AVRCP_ATTRIBUTE_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
+static int attr_to_val(const char *str)
+{
+ if (!strcasecmp(str, "Equalizer"))
+ return AVRCP_ATTRIBUTE_EQUALIZER;
+ else if (!strcasecmp(str, "Repeat"))
+ return AVRCP_ATTRIBUTE_REPEAT_MODE;
+ else if (!strcasecmp(str, "Shuffle"))
+ return AVRCP_ATTRIBUTE_SHUFFLE;
+ else if (!strcasecmp(str, "Scan"))
+ return AVRCP_ATTRIBUTE_SCAN;
+
+ return -EINVAL;
+}
+
static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
if (player == NULL)
@@ -440,6 +471,24 @@ static int player_get_setting(struct avrcp_player *player, uint8_t id)
return player->cb->get_setting(id, player->user_data);
}
+static int play_status_to_val(const char *status)
+{
+ if (!strcasecmp(status, "stopped"))
+ return AVRCP_PLAY_STATUS_STOPPED;
+ else if (!strcasecmp(status, "playing"))
+ return AVRCP_PLAY_STATUS_PLAYING;
+ else if (!strcasecmp(status, "paused"))
+ return AVRCP_PLAY_STATUS_PAUSED;
+ else if (!strcasecmp(status, "forward-seek"))
+ return AVRCP_PLAY_STATUS_FWD_SEEK;
+ else if (!strcasecmp(status, "reverse-seek"))
+ return AVRCP_PLAY_STATUS_REV_SEEK;
+ else if (!strcasecmp(status, "error"))
+ return AVRCP_PLAY_STATUS_ERROR;
+
+ return -EINVAL;
+}
+
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
@@ -463,7 +512,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
- pdu->params[1] = *((uint8_t *)data);
+ pdu->params[1] = play_status_to_val(data);
break;
case AVRCP_EVENT_TRACK_CHANGED:
@@ -480,9 +529,14 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
settings = data;
pdu->params[1] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ int attr;
int val;
+ attr = attr_to_val(key);
+ if (attr < 0)
+ continue;
+
val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -519,13 +573,35 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}
+static const char *metadata_to_str(uint32_t id)
+{
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ return "Title";
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ return "Artist";
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ return "Album";
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ return "Genre";
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ return "Track";
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ return "NumberOfTracks";
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ return "Duration";
+ }
+
+ return NULL;
+}
+
static const char *player_get_metadata(struct avrcp_player *player,
- uint32_t attr)
+ uint32_t id)
{
if (player != NULL)
- return player->cb->get_metadata(attr, player->user_data);
+ return player->cb->get_metadata(id, player->user_data);
- if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";
return NULL;
@@ -627,6 +703,49 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
return TRUE;
}
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ switch (value) {
+ case AVRCP_EQUALIZER_ON:
+ return "on";
+ case AVRCP_EQUALIZER_OFF:
+ return "off";
+ }
+
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ switch (value) {
+ case AVRCP_REPEAT_MODE_OFF:
+ return "off";
+ case AVRCP_REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case AVRCP_REPEAT_MODE_ALL:
+ return "alltracks";
+ case AVRCP_REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ case AVRCP_ATTRIBUTE_SCAN:
+ switch (value) {
+ case AVRCP_SCAN_OFF:
+ return "off";
+ case AVRCP_SCAN_ALL:
+ return "alltracks";
+ case AVRCP_SCAN_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
static int player_set_setting(struct avrcp_player *player, uint8_t id,
uint8_t val)
{
@@ -1077,7 +1196,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
pdu->params[++len] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ uint8_t attr = attr_to_val(key);
int val;
val = player_get_setting(player, attr);
@@ -1367,6 +1487,9 @@ static gboolean avrcp_get_play_status_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;
struct avrcp_header *pdu = (void *) operands;
uint32_t duration;
uint32_t position;
@@ -1380,9 +1503,10 @@ static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
memcpy(&position, pdu->params + 4, sizeof(uint32_t));
position = ntohl(position);
+ media_player_set_position(mp, position);
memcpy(&status, pdu->params + 8, sizeof(uint8_t));
- DBG("%s", status_to_string(status));
+ media_player_set_status(mp, status_to_string(status));
return FALSE;
}
@@ -1404,70 +1528,14 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- switch (value) {
- case AVRCP_EQUALIZER_ON:
- return "on";
- case AVRCP_EQUALIZER_OFF:
- return "off";
- }
-
- break;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- switch (value) {
- case AVRCP_REPEAT_MODE_OFF:
- return "off";
- case AVRCP_REPEAT_MODE_SINGLE:
- return "singletrack";
- case AVRCP_REPEAT_MODE_ALL:
- return "alltracks";
- case AVRCP_REPEAT_MODE_GROUP:
- return "group";
- }
-
- break;
- /* Shuffle and scan have the same values */
- case AVRCP_ATTRIBUTE_SHUFFLE:
- case AVRCP_ATTRIBUTE_SCAN:
- switch (value) {
- case AVRCP_SCAN_OFF:
- return "off";
- case AVRCP_SCAN_ALL:
- return "alltracks";
- case AVRCP_SCAN_GROUP:
- return "group";
- }
-
- break;
- }
-
- return NULL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return "Equalizer";
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return "Repeat";
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return "Shuffle";
- case AVRCP_ATTRIBUTE_SCAN:
- return "Scan";
- }
-
- return NULL;
-}
-
static gboolean avrcp_player_value_rsp(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 media_player *mp = player->user_data;
struct avrcp_header *pdu = (void *) operands;
uint8_t count;
int i;
@@ -1492,7 +1560,7 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
if (value == NULL)
continue;
- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}
return FALSE;
@@ -1563,33 +1631,14 @@ static void avrcp_list_player_attributes(struct avrcp *session)
session);
}
-static const char *metadata_to_str(uint32_t id)
-{
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
- return "Title";
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- return "Artist";
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- return "Album";
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- return "Genre";
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- return "Track";
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- return "NumberOfTracks";
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- return "Duration";
- }
-
- return NULL;
-}
-
static gboolean avrcp_get_attributes_rsp(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 media_player *mp = player->user_data;
struct avrcp_header *pdu = (void *) operands;
uint8_t count;
int i;
@@ -1622,10 +1671,11 @@ static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
if (charset == 106) {
const char *key = metadata_to_str(id);
- char *value = g_strndup((char *) &pdu->params[i], len);
- DBG("%s: %s", key , value);
- g_free(value);
+ if (key != NULL)
+ media_player_set_metadata(mp,
+ metadata_to_str(id),
+ &pdu->params[i], len);
}
i += len;
@@ -1663,9 +1713,11 @@ static gboolean avrcp_handle_event(struct avctp *conn,
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;
+ const char *curval, *strval;
int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
@@ -1683,17 +1735,29 @@ static gboolean avrcp_handle_event(struct avctp *conn,
break;
case AVRCP_EVENT_STATUS_CHANGED:
+ mp = player->user_data;
value = pdu->params[1];
- avrcp_get_play_status(session);
+ 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);
+ }
break;
case AVRCP_EVENT_TRACK_CHANGED:
+ mp = player->user_data;
+ if (code == AVC_CTYPE_CHANGED)
+ media_player_set_position(mp, 0);
+
avrcp_get_element_attributes(session);
break;
case AVRCP_EVENT_SETTINGS_CHANGED:
+ mp = player->user_data;
count = pdu->params[1];
for (i = 2; count > 0; count--, i += 2) {
@@ -1709,7 +1773,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
if (value == NULL)
continue;
- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}
break;
@@ -1753,9 +1817,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
void *user_data)
{
struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp;
struct avrcp_header *pdu = (void *) operands;
uint16_t events = 0;
uint8_t count;
+ const char *path;
if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
return FALSE;
@@ -1776,6 +1843,11 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
}
}
+ path = device_get_path(session->dev->btd_dev);
+ mp = media_player_controller_create(path);
+ player->user_data = mp;
+ player->destroy = (GDestroyNotify) media_player_destroy;
+
if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
avrcp_list_player_attributes(session);
diff --git a/audio/player.c b/audio/player.c
new file mode 100644
index 0000000..1957594
--- /dev/null
+++ b/audio/player.c
@@ -0,0 +1,343 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "player.h"
+#include "dbus-common.h"
+#include "error.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+struct player_callback {
+ const struct media_player_callback *cbs;
+ void *user_data;
+};
+
+struct media_player {
+ char *path; /* Player object path */
+ GHashTable *settings; /* Player settings */
+ GHashTable *track; /* Player current track */
+ char *status;
+ uint32_t position;
+ GTimer *progress;
+ guint process_id;
+ struct player_callback *cb;
+};
+
+static void append_settings(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static void append_metadata(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ if (strcasecmp((char *) key, "Duration") == 0 ||
+ strcasecmp((char *) key, "Track") == 0 ||
+ strcasecmp((char *) key, "NumberOfTracks") == 0) {
+ uint32_t num = atoi(value);
+ dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+ return;
+ }
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static DBusMessage *media_player_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ uint32_t position;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ position = media_player_get_position(mp);
+ dict_append_entry(&dict, "Position", DBUS_TYPE_UINT32, &position);
+
+ dict_append_entry(&dict, "Status", DBUS_TYPE_STRING, &mp->status);
+
+ g_hash_table_foreach(mp->settings, append_settings, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_get_track(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ media_player_get_properties) },
+ { GDBUS_METHOD("GetTrack",
+ NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
+ media_player_get_track) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, media_player_set_property) },
+ { }
+};
+
+static const GDBusSignalTable media_player_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("TrackChanged",
+ GDBUS_ARGS({ "metadata", "a{sv}" })) },
+ { }
+};
+
+void media_player_destroy(struct media_player *mp)
+{
+ DBG("%s", mp->path);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
+ MEDIA_PLAYER_INTERFACE);
+
+ if (mp->track)
+ g_hash_table_unref(mp->track);
+
+ if (mp->settings)
+ g_hash_table_unref(mp->settings);
+
+ if (mp->process_id > 0)
+ g_source_remove(mp->process_id);
+
+ g_timer_destroy(mp->progress);
+ g_free(mp->cb);
+ g_free(mp->status);
+ g_free(mp->path);
+ g_free(mp);
+}
+
+struct media_player *media_player_controller_create(const char *path)
+{
+ struct media_player *mp;
+
+ mp = g_new0(struct media_player, 1);
+ mp->path = g_strdup_printf("%s/player1", path);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->progress = g_timer_new();
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ media_player_methods,
+ media_player_signals,
+ NULL, mp, NULL)) {
+ error("D-Bus failed to register %s path", mp->path);
+ media_player_destroy(mp);
+ return NULL;
+ }
+
+ DBG("%s", mp->path);
+
+ return mp;
+}
+
+uint32_t media_player_get_position(struct media_player *mp)
+{
+ double timedelta;
+ uint32_t sec, msec;
+
+ if (g_strcmp0(mp->status, "playing") != 0)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->progress, NULL);
+
+ sec = (uint32_t) timedelta;
+ msec = (uint32_t) ((timedelta - sec) * 1000);
+
+ return mp->position + sec * 1000 + msec;
+}
+
+void media_player_set_position(struct media_player *mp, uint32_t position)
+{
+ DBG("%u", position);
+
+ mp->position = position;
+ g_timer_start(mp->progress);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Position",
+ DBUS_TYPE_UINT32, &mp->position);
+}
+
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value)
+{
+ char *curval;
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return;
+
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
+ DBUS_TYPE_STRING, &value);
+}
+
+const char *media_player_get_status(struct media_player *mp)
+{
+ return mp->status;
+}
+
+void media_player_set_status(struct media_player *mp, const char *status)
+{
+ DBG("%s", status);
+
+ if (g_strcmp0(mp->status, status) == 0)
+ return;
+
+ g_free(mp->status);
+ mp->status = g_strdup(status);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Status",
+ DBUS_TYPE_STRING, &status);
+
+ mp->position = media_player_get_position(mp);
+ g_timer_start(mp->progress);
+}
+
+static gboolean process_metadata_changed(void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBusMessage *signal;
+ DBusMessageIter iter, dict;
+
+ mp->process_id = 0;
+
+ signal = dbus_message_new_signal(mp->path, MEDIA_PLAYER_INTERFACE,
+ "TrackChanged");
+ if (signal == NULL) {
+ error("Unable to allocate TrackChanged signal");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(btd_get_dbus_connection(), signal);
+
+ return FALSE;
+}
+
+void media_player_set_metadata(struct media_player *mp, const char *key,
+ void *data, size_t len)
+{
+ char *value, *curval;
+
+ value = g_strndup(data, len);
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->track, key);
+ if (g_strcmp0(curval, value) == 0) {
+ g_free(value);
+ return;
+ }
+
+ if (mp->process_id == 0) {
+ g_hash_table_remove_all(mp->track);
+ mp->process_id = g_idle_add(process_metadata_changed, mp);
+ }
+
+ g_hash_table_replace(mp->track, g_strdup(key), value);
+}
diff --git a/audio/player.h b/audio/player.h
new file mode 100644
index 0000000..f3a421a
--- /dev/null
+++ b/audio/player.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct media_player;
+
+struct media_player *media_player_controller_create(const char *path);
+void media_player_destroy(struct media_player *mp);
+uint32_t media_player_get_position(struct media_player *mp);
+void media_player_set_position(struct media_player *mp, uint32_t position);
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value);
+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);
diff --git a/doc/media-api.txt b/doc/media-api.txt
index d15d22a..b4f2fc6 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -126,11 +126,25 @@ Methods void RegisterEndpoint(object endpoint, dict properties)
MediaPlayer hierarchy
=====================
-Service unique name
+Service unique name (Target role)
+ org.bluez (Controller role)
Interface org.bluez.MediaPlayer
-Object path freely definable
+Object path freely definable (Target role)
+ [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+ (Controller role)
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ dict GetTrack()
+
+ Returns known metadata of the current track.
+
+ See TrackChanged for possible values.
-Methods void SetProperty(string property, variant value)
+ void SetProperty(string property, variant value)
Changes the value of the specified property. Only
properties that are listed as read-write can be changed.
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 3/7] player: Add support for SetProperty
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Properties Equalizer, Repeat, Shuffle and Scan can be set by user
application.
---
audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
audio/player.h | 9 ++++
3 files changed, 288 insertions(+), 4 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 7c26491..724e139 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
return NULL;
}
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+ int ret;
+
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_EQUALIZER_OFF;
+ else if (!strcmp(value, "on"))
+ ret = AVRCP_EQUALIZER_ON;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_REPEAT_MODE_OFF;
+ else if (!strcmp(value, "singletrack"))
+ ret = AVRCP_REPEAT_MODE_SINGLE;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_REPEAT_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_REPEAT_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SHUFFLE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SHUFFLE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SHUFFLE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SCAN:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SCAN_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SCAN_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SCAN_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
static int attr_to_val(const char *str)
{
if (!strcasecmp(str, "Equalizer"))
@@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}
+static const char *status_to_str(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_STATUS_INVALID_COMMAND:
+ return "Invalid Command";
+ case AVRCP_STATUS_INVALID_PARAM:
+ return "Invalid Parameter";
+ case AVRCP_STATUS_INTERNAL_ERROR:
+ return "Internal Error";
+ case AVRCP_STATUS_SUCCESS:
+ return "Success";
+ default:
+ return "Unknown";
+ }
+}
+
static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t count;
int i;
- if (code == AVC_CTYPE_REJECTED)
+ if (code == AVC_CTYPE_REJECTED) {
+ media_player_set_setting(mp, "Error",
+ status_to_str(pdu->params[0]));
return FALSE;
+ }
count = pdu->params[0];
@@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
+static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
+ uint8_t val)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 3];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = 1;
+ pdu->params[1] = attr;
+ pdu->params[2] = val;
+ pdu->params_len = htons(3);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_player_value_rsp, session);
+}
+
+static bool ct_set_setting(struct media_player *mp, const char *key,
+ const char *value, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ int attr = attr_to_val(key);
+ int val = attrval_to_val(attr, value);
+ struct avrcp *session;
+
+ session = player->sessions->data;
+ if (session == NULL)
+ return false;
+
+ attr = attr_to_val(key);
+ if (attr < 0)
+ return false;
+
+ val = attrval_to_val(attr, value);
+ if (val < 0)
+ return false;
+
+ avrcp_set_player_value(session, attr, val);
+
+ return true;
+}
+
+static const struct media_player_callback ct_cbs = {
+ .set_setting = ct_set_setting,
+};
+
static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
path = device_get_path(session->dev->btd_dev);
mp = media_player_controller_create(path);
+ media_player_set_callbacks(mp, &ct_cbs, player);
player->user_data = mp;
player->destroy = (GDestroyNotify) media_player_destroy;
diff --git a/audio/player.c b/audio/player.c
index 1957594..e83b761 100644
--- a/audio/player.c
+++ b/audio/player.c
@@ -49,6 +49,12 @@ struct player_callback {
void *user_data;
};
+struct pending_req {
+ DBusMessage *msg;
+ const char *key;
+ const char *value;
+};
+
struct media_player {
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
@@ -58,6 +64,7 @@ struct media_player {
GTimer *progress;
guint process_id;
struct player_callback *cb;
+ GSList *pending;
};
static void append_settings(void *key, void *value, void *user_data)
@@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
return reply;
}
+static struct pending_req *find_pending(struct media_player *mp,
+ const char *key)
+{
+ GSList *l;
+
+ for (l = mp->pending; l; l = l->next) {
+ struct pending_req *p = l->data;
+
+ if (strcasecmp(key, p->key) == 0)
+ return p;
+ }
+
+ return NULL;
+}
+
+static struct pending_req *pending_new(DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct pending_req *p;
+
+ p = g_new0(struct pending_req, 1);
+ p->msg = dbus_message_ref(msg);
+ p->key = key;
+ p->value = value;
+
+ return p;
+}
+
+static DBusMessage *player_set_setting(struct media_player *mp,
+ DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct player_callback *cb = mp->cb;
+ struct pending_req *p;
+
+ if (cb == NULL || cb->cbs->set_setting == NULL)
+ return btd_error_not_supported(msg);
+
+ p = find_pending(mp, key);
+ if (p != NULL)
+ return btd_error_in_progress(msg);
+
+ if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
+ return btd_error_invalid_args(msg);
+
+ p = pending_new(msg, key, value);
+
+ mp->pending = g_slist_append(mp->pending, p);
+
+ return NULL;
+}
+
static DBusMessage *media_player_set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ struct media_player *mp = data;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *key, *value, *curval;
+
+ 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, &key);
+ 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);
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (g_strcmp0(key, "Equalizer") != 0 &&
+ g_strcmp0(key, "Repeat") != 0 &&
+ g_strcmp0(key, "Shuffle") != 0 &&
+ g_strcmp0(key, "Scan") != 0)
+ return btd_error_invalid_args(msg);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ return player_set_setting(mp, msg, key, value);
}
static const GDBusMethodTable media_player_methods[] = {
@@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("GetTrack",
NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
media_player_get_track) },
- { GDBUS_METHOD("SetProperty",
+ { GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
NULL, media_player_set_property) },
{ }
@@ -169,6 +262,14 @@ static const GDBusSignalTable media_player_signals[] = {
{ }
};
+static void pending_free(void *data)
+{
+ struct pending_req *p = data;
+
+ dbus_message_unref(p->msg);
+ g_free(p);
+}
+
void media_player_destroy(struct media_player *mp)
{
DBG("%s", mp->path);
@@ -185,6 +286,8 @@ void media_player_destroy(struct media_player *mp)
if (mp->process_id > 0)
g_source_remove(mp->process_id);
+ g_slist_free_full(mp->pending, pending_free);
+
g_timer_destroy(mp->progress);
g_free(mp->cb);
g_free(mp->status);
@@ -250,17 +353,46 @@ void media_player_set_setting(struct media_player *mp, const char *key,
const char *value)
{
char *curval;
+ struct pending_req *p;
+ DBusMessage *reply;
DBG("%s: %s", key, value);
+ if (strcasecmp(key, "Error") == 0) {
+ p = g_slist_nth_data(mp->pending, 0);
+ if (p == NULL)
+ return;
+
+ reply = btd_error_failed(p->msg, value);
+ goto send;
+ }
+
curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
- return;
+ goto done;
g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
DBUS_TYPE_STRING, &value);
+
+done:
+ p = find_pending(mp, key);
+ if (p == NULL)
+ return;
+
+ if (strcasecmp(value, p->value) == 0)
+ reply = g_dbus_create_reply(p->msg, DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_not_supported(p->msg);
+
+send:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ mp->pending = g_slist_remove(mp->pending, p);
+ pending_free(p);
+
+ return;
}
const char *media_player_get_status(struct media_player *mp)
@@ -341,3 +473,19 @@ 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_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data)
+{
+ struct player_callback *cb;
+
+ if (mp->cb)
+ g_free(mp->cb);
+
+ cb = g_new0(struct player_callback, 1);
+ cb->cbs = cbs;
+ cb->user_data = user_data;
+
+ mp->cb = cb;
+}
diff --git a/audio/player.h b/audio/player.h
index f3a421a..4a6a9cc 100644
--- a/audio/player.h
+++ b/audio/player.h
@@ -25,6 +25,11 @@
struct media_player;
+struct media_player_callback {
+ bool (*set_setting) (struct media_player *mp, const char *key,
+ const char *value, void *user_data);
+};
+
struct media_player *media_player_controller_create(const char *path);
void media_player_destroy(struct media_player *mp);
uint32_t media_player_get_position(struct media_player *mp);
@@ -35,3 +40,7 @@ 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_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data);
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 4/7] AVRCP: Remove conversions inside media.c
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The conversion should be done in avrcp.c as it is already doing for
controller players.
---
audio/avrcp.c | 37 ++++++-
audio/avrcp.h | 9 +-
audio/media.c | 315 ++++++++++------------------------------------------------
3 files changed, 89 insertions(+), 272 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 724e139..3d9ecc7 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -519,10 +519,19 @@ static int attr_to_val(const char *str)
static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
+ const char *key;
+ const char *value;
+
if (player == NULL)
return -ENOENT;
- return player->cb->get_setting(id, player->user_data);
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = player->cb->get_setting(key, player->user_data);
+
+ return attrval_to_val(id, value);
}
static int play_status_to_val(const char *status)
@@ -652,8 +661,14 @@ static const char *metadata_to_str(uint32_t id)
static const char *player_get_metadata(struct avrcp_player *player,
uint32_t id)
{
+ const char *key;
+
+ key = metadata_to_str(id);
+ if (key == NULL)
+ return NULL;
+
if (player != NULL)
- return player->cb->get_metadata(id, player->user_data);
+ return player->cb->get_metadata(key, player->user_data);
if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";
@@ -803,10 +818,20 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
static int player_set_setting(struct avrcp_player *player, uint8_t id,
uint8_t val)
{
+ const char *key, *value;
+
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = attrval_to_str(id, val);
+ if (value == NULL)
+ return -EINVAL;
+
if (player == NULL)
return -ENOENT;
- return player->cb->set_setting(id, val, player->user_data);
+ return player->cb->set_setting(key, value, player->user_data);
}
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -1160,10 +1185,14 @@ static uint32_t player_get_duration(struct avrcp_player *player)
static uint8_t player_get_status(struct avrcp_player *player)
{
+ const char *value;
+
if (player == NULL)
return AVRCP_PLAY_STATUS_STOPPED;
- return player->cb->get_status(player->user_data);
+ value = player->cb->get_status(player->user_data);
+
+ return play_status_to_val(value);
}
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 7f54adb..e607fb1 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -79,12 +79,13 @@
struct avrcp_player_cb {
GList *(*list_settings) (void *user_data);
- int (*get_setting) (uint8_t attr, void *user_data);
- int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+ const char *(*get_setting) (const char *key, void *user_data);
+ int (*set_setting) (const char *key, const char *value,
+ void *user_data);
uint64_t (*get_uid) (void *user_data);
- const char *(*get_metadata) (uint32_t id, void *user_data);
+ const char *(*get_metadata) (const char *key, void *user_data);
GList *(*list_metadata) (void *user_data);
- uint8_t (*get_status) (void *user_data);
+ const char *(*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
uint32_t (*get_duration) (void *user_data);
void (*set_volume) (uint8_t volume, struct audio_device *dev,
diff --git a/audio/media.c b/audio/media.c
index 7abf077..cec938a 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -98,7 +98,7 @@ struct media_player {
guint watch;
guint property_watch;
guint track_watch;
- uint8_t status;
+ char *status;
uint32_t position;
uint32_t duration;
uint8_t volume;
@@ -980,6 +980,7 @@ static void media_player_free(gpointer data)
g_timer_destroy(mp->timer);
g_free(mp->sender);
g_free(mp->path);
+ g_free(mp->status);
g_free(mp);
}
@@ -1007,193 +1008,6 @@ static void media_player_remove(struct media_player *mp)
media_player_destroy(mp);
}
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- switch (value) {
- case AVRCP_EQUALIZER_ON:
- return "on";
- case AVRCP_EQUALIZER_OFF:
- return "off";
- }
-
- break;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- switch (value) {
- case AVRCP_REPEAT_MODE_OFF:
- return "off";
- case AVRCP_REPEAT_MODE_SINGLE:
- return "singletrack";
- case AVRCP_REPEAT_MODE_ALL:
- return "alltracks";
- case AVRCP_REPEAT_MODE_GROUP:
- return "group";
- }
-
- break;
- /* Shuffle and scan have the same values */
- case AVRCP_ATTRIBUTE_SHUFFLE:
- case AVRCP_ATTRIBUTE_SCAN:
- switch (value) {
- case AVRCP_SCAN_OFF:
- return "off";
- case AVRCP_SCAN_ALL:
- return "alltracks";
- case AVRCP_SCAN_GROUP:
- return "group";
- }
-
- break;
- }
-
- return NULL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
- int ret;
-
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- if (!strcmp(value, "off"))
- ret = AVRCP_EQUALIZER_OFF;
- else if (!strcmp(value, "on"))
- ret = AVRCP_EQUALIZER_ON;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- if (!strcmp(value, "off"))
- ret = AVRCP_REPEAT_MODE_OFF;
- else if (!strcmp(value, "singletrack"))
- ret = AVRCP_REPEAT_MODE_SINGLE;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_REPEAT_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_REPEAT_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SHUFFLE:
- if (!strcmp(value, "off"))
- ret = AVRCP_SHUFFLE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SHUFFLE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SHUFFLE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SCAN:
- if (!strcmp(value, "off"))
- ret = AVRCP_SCAN_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SCAN_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SCAN_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- }
-
- return -EINVAL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return "Equalizer";
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return "Repeat";
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return "Shuffle";
- case AVRCP_ATTRIBUTE_SCAN:
- return "Scan";
- }
-
- return NULL;
-}
-
-static int attr_to_val(const char *str)
-{
- if (!strcasecmp(str, "Equalizer"))
- return AVRCP_ATTRIBUTE_EQUALIZER;
- else if (!strcasecmp(str, "Repeat"))
- return AVRCP_ATTRIBUTE_REPEAT_MODE;
- else if (!strcasecmp(str, "Shuffle"))
- return AVRCP_ATTRIBUTE_SHUFFLE;
- else if (!strcasecmp(str, "Scan"))
- return AVRCP_ATTRIBUTE_SCAN;
-
- return -EINVAL;
-}
-
-static int play_status_to_val(const char *status)
-{
- if (!strcasecmp(status, "stopped"))
- return AVRCP_PLAY_STATUS_STOPPED;
- else if (!strcasecmp(status, "playing"))
- return AVRCP_PLAY_STATUS_PLAYING;
- else if (!strcasecmp(status, "paused"))
- return AVRCP_PLAY_STATUS_PAUSED;
- else if (!strcasecmp(status, "forward-seek"))
- return AVRCP_PLAY_STATUS_FWD_SEEK;
- else if (!strcasecmp(status, "reverse-seek"))
- return AVRCP_PLAY_STATUS_REV_SEEK;
- else if (!strcasecmp(status, "error"))
- return AVRCP_PLAY_STATUS_ERROR;
-
- return -EINVAL;
-}
-
-static int metadata_to_val(const char *str)
-{
- if (!strcasecmp(str, "Title"))
- return AVRCP_MEDIA_ATTRIBUTE_TITLE;
- else if (!strcasecmp(str, "Artist"))
- return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
- else if (!strcasecmp(str, "Album"))
- return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
- else if (!strcasecmp(str, "Genre"))
- return AVRCP_MEDIA_ATTRIBUTE_GENRE;
- else if (!strcasecmp(str, "NumberOfTracks"))
- return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
- else if (!strcasecmp(str, "Number"))
- return AVRCP_MEDIA_ATTRIBUTE_TRACK;
- else if (!strcasecmp(str, "Duration"))
- return AVRCP_MEDIA_ATTRIBUTE_DURATION;
-
- return -EINVAL;
-}
-
-static const char *metadata_to_str(uint32_t id)
-{
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
- return "Title";
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- return "Artist";
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- return "Album";
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- return "Genre";
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- return "Track";
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- return "NumberOfTracks";
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- return "Duration";
- }
-
- return NULL;
-}
-
static GList *list_settings(void *user_data)
{
struct media_player *mp = user_data;
@@ -1206,38 +1020,24 @@ static GList *list_settings(void *user_data)
return g_hash_table_get_keys(mp->settings);
}
-static int get_setting(uint8_t attr, void *user_data)
+static const char *get_setting(const char *key, void *user_data)
{
struct media_player *mp = user_data;
- guint attr_uint = attr;
- void *value;
-
- DBG("%s", attr_to_str(attr));
- value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint));
- if (!value)
- return -EINVAL;
+ DBG("%s", key);
- return GPOINTER_TO_UINT(value);
+ return g_hash_table_lookup(mp->settings, key);
}
-static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+static int set_setting(const char *key, const char *value, void *user_data)
{
struct media_player *mp = user_data;
- const char *property, *value;
- guint attr_uint = attr;
DBusMessage *msg;
DBusMessageIter iter, var;
- property = attr_to_str(attr);
- value = attrval_to_str(attr, val);
-
- DBG("%s = %s", property, value);
+ DBG("%s = %s", key, value);
- if (property == NULL || value == NULL)
- return -EINVAL;
-
- if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)))
+ if (!g_hash_table_lookup(mp->settings, key))
return -EINVAL;
msg = dbus_message_new_method_call(mp->sender, mp->path,
@@ -1249,7 +1049,7 @@ static int set_setting(uint8_t attr, uint8_t val, void *user_data)
}
dbus_message_iter_init_append(msg, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING,
@@ -1286,19 +1086,19 @@ static uint64_t get_uid(void *user_data)
return 0;
}
-static const char *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(const char *key, void *user_data)
{
struct media_player *mp = user_data;
- DBG("%s", metadata_to_str(id));
+ DBG("%s", key);
if (mp->track == NULL)
return NULL;
- return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+ return g_hash_table_lookup(mp->track, key);
}
-static uint8_t get_status(void *user_data)
+static const char *get_status(void *user_data)
{
struct media_player *mp = user_data;
@@ -1311,7 +1111,7 @@ static uint32_t get_position(void *user_data)
double timedelta;
uint32_t sec, msec;
- if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ if (g_strcmp0(mp->status, "playing") != 0)
return mp->position;
timedelta = g_timer_elapsed(mp->timer, NULL);
@@ -1379,7 +1179,6 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
{
const char *value;
- int val;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return FALSE;
@@ -1387,21 +1186,16 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
dbus_message_iter_get_basic(iter, &value);
DBG("Status=%s", value);
- val = play_status_to_val(value);
- if (val < 0) {
- error("Invalid status");
- return FALSE;
- }
-
- if (mp->status == val)
+ if (g_strcmp0(mp->status, value) == 0)
return TRUE;
mp->position = get_position(mp);
g_timer_start(mp->timer);
- mp->status = val;
+ g_free(mp->status);
+ mp->status = g_strdup(value);
- avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
return TRUE;
}
@@ -1441,7 +1235,6 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
{
DBusMessageIter var;
const char *value, *curval;
- int attr, val;
GList *settings;
if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
@@ -1455,27 +1248,18 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
if (strcasecmp(key, "Position") == 0)
return set_position(mp, &var);
- attr = attr_to_val(key);
- if (attr < 0)
- return FALSE;
-
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
return FALSE;
dbus_message_iter_get_basic(&var, &value);
- val = attrval_to_val(attr, value);
- if (val < 0)
- return FALSE;
-
- curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
return TRUE;
DBG("%s=%s", key, value);
- g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
- GUINT_TO_POINTER(val));
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
settings = list_settings(mp);
@@ -1529,7 +1313,7 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_recurse(iter, &dict);
- track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+ track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
g_free);
while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
@@ -1540,7 +1324,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
char valstr[20];
char *value;
uint32_t num;
- int id;
int type;
if (ctype != DBUS_TYPE_DICT_ENTRY)
@@ -1553,10 +1336,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
- id = metadata_to_val(key);
- if (id < 0)
- goto parse_error;
-
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
goto parse_error;
@@ -1564,36 +1343,46 @@ static gboolean parse_player_metadata(struct media_player *mp,
type = dbus_message_iter_get_arg_type(&var);
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ if (strcasecmp(key, "Title") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
title = TRUE;
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Artist") == 0) {
if (type != DBUS_TYPE_STRING)
goto parse_error;
dbus_message_iter_get_basic(&var, &string);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ } else if (strcasecmp(key, "Album") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Genre") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Duration") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;
dbus_message_iter_get_basic(&var, &num);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ mp->duration = num;
+ } else if (strcasecmp(key, "Track") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;
dbus_message_iter_get_basic(&var, &num);
- mp->duration = num;
- break;
- default:
+ } else if (strcasecmp(key, "NumberOfTracks") == 0) {
+ if (type != DBUS_TYPE_UINT32)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &num);
+ } else
goto parse_error;
- }
- switch (dbus_message_iter_get_arg_type(&var)) {
+ switch (type) {
case DBUS_TYPE_STRING:
value = g_strdup(string);
break;
@@ -1603,18 +1392,15 @@ static gboolean parse_player_metadata(struct media_player *mp,
}
DBG("%s=%s", key, value);
- g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
+ g_hash_table_replace(track, g_strdup(key), value);
dbus_message_iter_next(&dict);
}
if (g_hash_table_size(track) == 0) {
g_hash_table_unref(track);
track = NULL;
- } else if (title == FALSE) {
- uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
-
- g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
- }
+ } else if (title == FALSE)
+ g_hash_table_insert(track, g_strdup("Title"), g_strdup(""));
if (mp->track != NULL)
g_hash_table_unref(mp->track);
@@ -1692,7 +1478,8 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
return NULL;
}
- mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);
adapter->players = g_slist_append(adapter->players, mp);
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 5/7] test: Fix using Number instead of Track in simple-player
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
test/simple-player | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/simple-player b/test/simple-player
index 70701da..f8751d6 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -111,7 +111,7 @@ if __name__ == '__main__':
"Album" : "Album",
"Genre" : "Genre",
"NumberOfTracks" : dbus.UInt32(10),
- "Number" : dbus.UInt32(1),
+ "Track" : dbus.UInt32(1),
"Duration" : dbus.UInt32(10000) }, signature="sv")
print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 6/7] test: Fix using Number instead of Track in mpris-player
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
test/mpris-player.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/mpris-player.c b/test/mpris-player.c
index a2c4cc6..13f5c85 100644
--- a/test/mpris-player.c
+++ b/test/mpris-player.c
@@ -338,7 +338,7 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
dbus_message_iter_get_basic(&var, &value);
- dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+ dict_append_entry(metadata, "Track", DBUS_TYPE_UINT32,
&value);
}
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 7/7] test: Add support for using external player
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This add support for passing a device player object which is then
registered as a local one in a loopback fashion.
---
test/simple-player | 80 +++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 61 insertions(+), 19 deletions(-)
diff --git a/test/simple-player b/test/simple-player
index f8751d6..9e72f3e 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -9,6 +9,50 @@ import dbus.mainloop.glib
import gobject
class Player(dbus.service.Object):
+ properties = None
+ metadata = None
+
+ def set_object(self, obj = None):
+ if obj != None:
+ bus = dbus.SystemBus()
+ mp = dbus.Interface(bus.get_object("org.bluez", obj),
+ "org.bluez.MediaPlayer")
+
+ self.properties = mp.GetProperties()
+ self.metadata = mp.GetTrack()
+
+ bus.add_signal_receiver(self.property_changed,
+ path = obj,
+ dbus_interface = "org.bluez.MediaPlayer",
+ signal_name = "PropertyChanged")
+
+ bus.add_signal_receiver(self.track_changed,
+ path = obj,
+ dbus_interface ="org.bluez.MediaPlayer",
+ signal_name = "TrackChanged")
+ else:
+ self.properties = dbus.Dictionary({
+ "Equalizer" : "off",
+ "Repeat" : "off",
+ "Shuffle" : "off",
+ "Scan" : "off",
+ "Status" : "playing",
+ "Position" : dbus.UInt32(0) },
+ signature="sv")
+
+ self.metadata = dbus.Dictionary({
+ "Title" : "Title",
+ "Artist" : "Artist",
+ "Album" : "Album",
+ "Genre" : "Genre",
+ "NumberOfTracks" : dbus.UInt32(10),
+ "Track" : dbus.UInt32(1),
+ "Duration" : dbus.UInt32(10000) },
+ signature="sv")
+ handler = InputHandler(self)
+ gobject.io_add_watch(sys.stdin, gobject.IO_IN,
+ handler.handle)
+
@dbus.service.method("org.bluez.MediaPlayer",
in_signature="", out_signature="")
def Release(self):
@@ -42,6 +86,16 @@ class Player(dbus.service.Object):
def help(self, func):
help(self.__class__.__dict__[func])
+ def property_changed(self, setting, value):
+ print("property_changed(%s, %s)" % (setting, value))
+
+ self.PropertyChanged(setting, value)
+
+ def track_changed(self, metadata):
+ print("track_changed(%s)" % (metadata))
+
+ self.TrackChanged(metadata)
+
class InputHandler:
commands = { 'TrackChanged': '(metadata)',
'PropertyChanged': '(key, value)',
@@ -79,7 +133,6 @@ class InputHandler:
return True
-
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
@@ -99,27 +152,16 @@ if __name__ == '__main__':
player = Player(bus, path)
mainloop = gobject.MainLoop()
- properties = dbus.Dictionary({ "Equalizer" : "off",
- "Repeat" : "off",
- "Shuffle" : "off",
- "Scan" : "off",
- "Status" : "playing",
- "Position" : dbus.UInt32(0) }, signature="sv")
-
- metadata = dbus.Dictionary({ "Title" : "Title",
- "Artist" : "Artist",
- "Album" : "Album",
- "Genre" : "Genre",
- "NumberOfTracks" : dbus.UInt32(10),
- "Track" : dbus.UInt32(1),
- "Duration" : dbus.UInt32(10000) }, signature="sv")
+ if len(sys.argv) > 2:
+ player.set_object(sys.argv[2])
+ else:
+ player.set_object()
print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
- % (properties, metadata))
+ % (player.properties, player.metadata))
- handler = InputHandler(player)
- gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)
- media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+ media.RegisterPlayer(dbus.ObjectPath(path), player.properties,
+ player.metadata)
mainloop.run()
--
1.7.11.7
^ permalink raw reply related
* Re: [PATCH 2/5] Bluetooth: Remove unnecessary include export.h
From: Syam Sidhardhan @ 2012-10-29 15:03 UTC (permalink / raw)
To: Marcel Holtmann, Gustavo Padovan; +Cc: linux-bluetooth
In-Reply-To: <1351003482.1785.28.camel@aeonflux>
Hi Marcel,
----- Original Message -----
From: "Marcel Holtmann" <marcel@holtmann.org>
To: "Syam Sidhardhan" <s.syam@samsung.com>
Cc: <linux-bluetooth@vger.kernel.org>
Sent: Tuesday, October 23, 2012 8:14 PM
Subject: Re: [PATCH 2/5] Bluetooth: Remove unnecessary include export.h
> Hi Syam,
>
>> For files only using THIS_MODULE and/or EXPORT_SYMBOL, map
>> them onto including export.h -- or if the file isn't even
>> using those, then just delete the include.
>
> have you actually check this on other architectures like PowerPC or
> Sparc?
>
I have checked it only for Arm and i686 arch. I do not have other
environment to check.
If any one has other architectures environment ready, kindly do the build
and ack for it. Apart from
build, I have cross checked the code once gain to make sure that nothing
breaks.
> commit 8c520a59927a5600973782505dbb750d985057c4
> Author: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
> Date: Wed May 23 04:04:22 2012 -0300
>
> Bluetooth: Remove unnecessary headers include
>
> Most of the include were unnecessary or already included by some other
> header.
> Replace module.h by export.h where possible.
>
Padovan, In the above commit, you have explicitly added the
export.h in that commit. Did you face any error or warning
while building with out export.h?
Commit Reference:
http://git.kernel.org/?p=linux/kernel/git/bluetooth/bluetooth-next.git;a=commitdiff;h=8c520a59927a5600973782505dbb750d985057c4#patch11
Thanks,
Syam
^ permalink raw reply
* Re: [PATCH 4/5] Bluetooth: mgmt: Use __constant when dealing with constants
From: Syam Sidhardhan @ 2012-10-29 15:05 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1351003568.1785.30.camel@aeonflux>
Hi Marcel,
----- Original Message -----
From: "Marcel Holtmann" <marcel@holtmann.org>
To: "Syam Sidhardhan" <s.syam@samsung.com>
Cc: <linux-bluetooth@vger.kernel.org>
Sent: Tuesday, October 23, 2012 8:16 PM
Subject: Re: [PATCH 4/5] Bluetooth: mgmt: Use __constant when dealing with
constants
> Hi Syam,
>
>> __constant_cpu_to_le*() is the right go here.
>>
>> Signed-off-by: Syam Sidhardhan <s.syam@samsung.com>
>> ---
>> net/bluetooth/mgmt.c | 12 +++++++-----
>> 1 file changed, 7 insertions(+), 5 deletions(-)
>>
>> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
>> index b127b88..e3bb2a7 100644
>> --- a/net/bluetooth/mgmt.c
>> +++ b/net/bluetooth/mgmt.c
>> @@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16
>> cmd, u8 status)
>>
>> hdr = (void *) skb_put(skb, sizeof(*hdr));
>>
>> - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
>> + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
>> hdr->index = cpu_to_le16(index);
>> hdr->len = cpu_to_le16(sizeof(*ev));
>>
>> @@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index,
>> u16 cmd, u8 status,
>>
>> hdr = (void *) skb_put(skb, sizeof(*hdr));
>>
>> - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
>> + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
>> hdr->index = cpu_to_le16(index);
>> hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
>>
>> @@ -832,7 +832,7 @@ static int mgmt_event(u16 event, struct hci_dev
>> *hdev, void *data, u16 data_len,
>> if (hdev)
>> hdr->index = cpu_to_le16(hdev->id);
>> else
>> - hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
>> + hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
>> hdr->len = cpu_to_le16(data_len);
>>
>> if (data)
>> @@ -3570,9 +3570,11 @@ int mgmt_device_found(struct hci_dev *hdev,
>> bdaddr_t *bdaddr, u8 link_type,
>> ev->addr.type = link_to_bdaddr(link_type, addr_type);
>> ev->rssi = rssi;
>> if (cfm_name)
>> - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
>> + ev->flags |=
>> + __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
>> if (!ssp)
>> - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
>> + ev->flags |=
>> + __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
>
> for these ones, break the 80 chars rule. In this case that is
> acceptable.
I'll correct it and send another version.
Thanks,
Syam
^ permalink raw reply
* Re: [PATCH] workqueue: cancel_delayed_work() should return %NULL if work item is idle
From: Tejun Heo @ 2012-10-29 15:14 UTC (permalink / raw)
To: Andrei Emeltchenko, Gustavo Padovan, linux-bluetooth,
Dan Magenheimer, linux-kernel, Konrad Wilk
In-Reply-To: <20121029094707.GC9314@aemeltch-MOBL1>
On Mon, Oct 29, 2012 at 11:47:13AM +0200, Andrei Emeltchenko wrote:
> Gustavo, could you apply the patch below to bluetooth-next tree, otherwise
> it is deadly broken.
Already applied to mainline (c0158ca64da5732dfb86a3f28944e9626776692f).
If pulling mainline is too much, please feel free to pull
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-3.7-fixes
Sorry about the trouble.
--
tejun
^ permalink raw reply
* Re: [PATCH BlueZ 3/7] player: Add support for SetProperty
From: Lucas De Marchi @ 2012-10-29 15:41 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1351521709-4063-3-git-send-email-luiz.dentz@gmail.com>
On Mon, Oct 29, 2012 at 12:41 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> Properties Equalizer, Repeat, Shuffle and Scan can be set by user
> application.
> ---
> audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
> audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> audio/player.h | 9 ++++
> 3 files changed, 288 insertions(+), 4 deletions(-)
>
> diff --git a/audio/avrcp.c b/audio/avrcp.c
> index 7c26491..724e139 100644
> --- a/audio/avrcp.c
> +++ b/audio/avrcp.c
> @@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
> return NULL;
> }
>
> +static int attrval_to_val(uint8_t attr, const char *value)
> +{
> + int ret;
> +
> + switch (attr) {
> + case AVRCP_ATTRIBUTE_EQUALIZER:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_EQUALIZER_OFF;
> + else if (!strcmp(value, "on"))
> + ret = AVRCP_EQUALIZER_ON;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_REPEAT_MODE:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_REPEAT_MODE_OFF;
> + else if (!strcmp(value, "singletrack"))
> + ret = AVRCP_REPEAT_MODE_SINGLE;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_REPEAT_MODE_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_REPEAT_MODE_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_SHUFFLE:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_SHUFFLE_OFF;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_SHUFFLE_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_SHUFFLE_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + case AVRCP_ATTRIBUTE_SCAN:
> + if (!strcmp(value, "off"))
> + ret = AVRCP_SCAN_OFF;
> + else if (!strcmp(value, "alltracks"))
> + ret = AVRCP_SCAN_ALL;
> + else if (!strcmp(value, "group"))
> + ret = AVRCP_SCAN_GROUP;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int attr_to_val(const char *str)
> {
> if (!strcasecmp(str, "Equalizer"))
> @@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
> session);
> }
>
> +static const char *status_to_str(uint8_t status)
> +{
> + switch (status) {
> + case AVRCP_STATUS_INVALID_COMMAND:
> + return "Invalid Command";
> + case AVRCP_STATUS_INVALID_PARAM:
> + return "Invalid Parameter";
> + case AVRCP_STATUS_INTERNAL_ERROR:
> + return "Internal Error";
> + case AVRCP_STATUS_SUCCESS:
> + return "Success";
> + default:
> + return "Unknown";
> + }
> +}
> +
> static gboolean avrcp_player_value_rsp(struct avctp *conn,
> uint8_t code, uint8_t subunit,
> uint8_t *operands, size_t operand_count,
> @@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
> uint8_t count;
> int i;
>
> - if (code == AVC_CTYPE_REJECTED)
> + if (code == AVC_CTYPE_REJECTED) {
> + media_player_set_setting(mp, "Error",
> + status_to_str(pdu->params[0]));
> return FALSE;
> + }
>
> count = pdu->params[0];
>
> @@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
> avrcp_handle_event, session);
> }
>
> +static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
> + uint8_t val)
> +{
> + uint8_t buf[AVRCP_HEADER_LENGTH + 3];
> + struct avrcp_header *pdu = (void *) buf;
> + uint8_t length;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + set_company_id(pdu->company_id, IEEEID_BTSIG);
> + pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
> + pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
> + pdu->params[0] = 1;
> + pdu->params[1] = attr;
> + pdu->params[2] = val;
> + pdu->params_len = htons(3);
> +
> + length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
> +
> + avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
> + AVC_SUBUNIT_PANEL, buf, length,
> + avrcp_player_value_rsp, session);
> +}
> +
> +static bool ct_set_setting(struct media_player *mp, const char *key,
> + const char *value, void *user_data)
> +{
> + struct avrcp_player *player = user_data;
> + int attr = attr_to_val(key);
> + int val = attrval_to_val(attr, value);
> + struct avrcp *session;
> +
> + session = player->sessions->data;
> + if (session == NULL)
> + return false;
> +
> + attr = attr_to_val(key);
> + if (attr < 0)
> + return false;
> +
> + val = attrval_to_val(attr, value);
> + if (val < 0)
> + return false;
> +
> + avrcp_set_player_value(session, attr, val);
> +
> + return true;
> +}
> +
> +static const struct media_player_callback ct_cbs = {
> + .set_setting = ct_set_setting,
> +};
> +
> static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
> uint8_t code, uint8_t subunit,
> uint8_t *operands, size_t operand_count,
> @@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
>
> path = device_get_path(session->dev->btd_dev);
> mp = media_player_controller_create(path);
> + media_player_set_callbacks(mp, &ct_cbs, player);
> player->user_data = mp;
> player->destroy = (GDestroyNotify) media_player_destroy;
>
> diff --git a/audio/player.c b/audio/player.c
> index 1957594..e83b761 100644
> --- a/audio/player.c
> +++ b/audio/player.c
> @@ -49,6 +49,12 @@ struct player_callback {
> void *user_data;
> };
>
> +struct pending_req {
> + DBusMessage *msg;
> + const char *key;
> + const char *value;
> +};
> +
> struct media_player {
> char *path; /* Player object path */
> GHashTable *settings; /* Player settings */
> @@ -58,6 +64,7 @@ struct media_player {
> GTimer *progress;
> guint process_id;
> struct player_callback *cb;
> + GSList *pending;
> };
>
> static void append_settings(void *key, void *value, void *user_data)
> @@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
> return reply;
> }
>
> +static struct pending_req *find_pending(struct media_player *mp,
> + const char *key)
> +{
> + GSList *l;
> +
> + for (l = mp->pending; l; l = l->next) {
> + struct pending_req *p = l->data;
> +
> + if (strcasecmp(key, p->key) == 0)
> + return p;
> + }
> +
> + return NULL;
> +}
> +
> +static struct pending_req *pending_new(DBusMessage *msg, const char *key,
> + const char *value)
> +{
> + struct pending_req *p;
> +
> + p = g_new0(struct pending_req, 1);
> + p->msg = dbus_message_ref(msg);
> + p->key = key;
> + p->value = value;
> +
> + return p;
> +}
> +
> +static DBusMessage *player_set_setting(struct media_player *mp,
> + DBusMessage *msg, const char *key,
> + const char *value)
> +{
> + struct player_callback *cb = mp->cb;
> + struct pending_req *p;
> +
> + if (cb == NULL || cb->cbs->set_setting == NULL)
> + return btd_error_not_supported(msg);
> +
> + p = find_pending(mp, key);
> + if (p != NULL)
> + return btd_error_in_progress(msg);
> +
> + if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
> + return btd_error_invalid_args(msg);
> +
> + p = pending_new(msg, key, value);
> +
> + mp->pending = g_slist_append(mp->pending, p);
> +
> + return NULL;
> +}
> +
> static DBusMessage *media_player_set_property(DBusConnection *conn,
> DBusMessage *msg, void *data)
> {
> - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> + struct media_player *mp = data;
> + DBusMessageIter iter;
> + DBusMessageIter var;
> + const char *key, *value, *curval;
> +
> + 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, &key);
> + 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);
> +
> + if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
> + return btd_error_invalid_args(msg);
> +
> + dbus_message_iter_get_basic(&var, &value);
> +
> + if (g_strcmp0(key, "Equalizer") != 0 &&
> + g_strcmp0(key, "Repeat") != 0 &&
> + g_strcmp0(key, "Shuffle") != 0 &&
> + g_strcmp0(key, "Scan") != 0)
> + return btd_error_invalid_args(msg);
> +
> + curval = g_hash_table_lookup(mp->settings, key);
> + if (g_strcmp0(curval, value) == 0)
> + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> +
> + return player_set_setting(mp, msg, key, value);
> }
>
> static const GDBusMethodTable media_player_methods[] = {
> @@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
> { GDBUS_METHOD("GetTrack",
> NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
> media_player_get_track) },
> - { GDBUS_METHOD("SetProperty",
> + { GDBUS_ASYNC_METHOD("SetProperty",
Why not using the DBus.Properties interface?
Lucas De Marchi
^ permalink raw reply
* [PATCH 4/5 v1] Bluetooth: mgmt: Use __constant when dealing with constants
From: Syam Sidhardhan @ 2012-10-29 17:07 UTC (permalink / raw)
To: linux-bluetooth
__constant_cpu_to_le*() is the right go here.
Signed-off-by: Syam Sidhardhan <s.syam@samsung.com>
---
v1-> Improve readability by breaking 80 chars rule.
net/bluetooth/mgmt.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index b127b88..802ffc5 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
@@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
@@ -832,7 +832,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
- hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+ hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
@@ -3570,9 +3570,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
if (cfm_name)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
if (!ssp)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
if (eir_len > 0)
memcpy(ev->eir, eir, eir_len);
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 4/5 v1] Bluetooth: mgmt: Use __constant when dealing with constants
From: Marcel Holtmann @ 2012-10-29 19:11 UTC (permalink / raw)
To: Syam Sidhardhan; +Cc: linux-bluetooth
In-Reply-To: <1351530456-11873-1-git-send-email-s.syam@samsung.com>
Hi Syam,
> __constant_cpu_to_le*() is the right go here.
>
> Signed-off-by: Syam Sidhardhan <s.syam@samsung.com>
> ---
> v1-> Improve readability by breaking 80 chars rule.
>
> net/bluetooth/mgmt.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* [PATCH BlueZ] hog: Fix selecting suspend backend
From: João Paulo Rechi Vita @ 2012-10-29 20:33 UTC (permalink / raw)
To: linux-bluetooth; +Cc: João Paulo Rechi Vita
In-Reply-To: <1351263933-21676-1-git-send-email-jprvita@openbossa.org>
The dummy backend was always being compiled and the --with-hog-suspend
option was not being accepted by configure. Now the backend can be
selected with --with-hog-suspend and the suspend implementation file is
generated during compile time.
---
.gitignore | 1 +
Makefile.am | 5 ++++-
acinclude.m4 | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index b5c7356..27c4687 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ scripts/bluetooth.rules
scripts/97-bluetooth.rules
scripts/97-bluetooth-hid2hci.rules
+profiles/input/suspend.c
profiles/sap/sap.c
profiles/cups/bluetooth
diff --git a/Makefile.am b/Makefile.am
index 35b1520..291f75a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -184,9 +184,12 @@ if HOGPLUGIN
builtin_modules += hog
builtin_sources += profiles/input/hog_manager.c profiles/input/hog_device.h \
profiles/input/hog_device.c profiles/input/uhid_copy.h \
- profiles/input/suspend-dummy.c profiles/input/suspend.h
+ profiles/input/suspend.h
+builtin_nodist += profiles/input/suspend.c
endif
+EXTRA_DIST += profiles/input/suspend-dummy.c
+
if NETWORKPLUGIN
builtin_modules += network
builtin_sources += profiles/network/main.c \
diff --git a/acinclude.m4 b/acinclude.m4
index 4bac3f0..ddc8183 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -290,7 +290,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
wiimote_enable=${enableval}
])
- AC_ARG_WITH(gatt, AC_HELP_STRING([--with-hog-suspend=DRIVER], [select HoG suspend driver]), [
+ AC_ARG_WITH(hog_suspend, AC_HELP_STRING([--with-hog-suspend=DRIVER], [select HoG suspend driver]), [
hog_suspend_driver=${withval}
])
--
1.7.11.7
^ permalink raw reply related
* Re: [PATCH v3 04/10] sbc: Add msbc flag and generic C primitive
From: Siarhei Siamashka @ 2012-10-30 0:12 UTC (permalink / raw)
To: Dalleau, Frederic; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <CA+QXtDtX0q=Zs3yu1VV8+ZWmOQEtsxoR96QZ=_A8_ZREWiaO=Q@mail.gmail.com>
On Mon, 29 Oct 2012 07:47:03 +0100
"Dalleau, Frederic" <frederic.dalleau@intel.com> wrote:
> Hi Siarhei,
>
> Thanks for review,
Hi Frederic. Thanks for providing the initial working prototype of
mSBC support and for your progress polishing the remaining rough edges.
> I mostly agree but have a few remarks.
> On Sun, Oct 28, 2012 at 12:19 AM, Siarhei Siamashka
> <siarhei.siamashka@gmail.com> wrote:
> > On Fri, 26 Oct 2012 19:20:30 +0200
> > If "position" was not decremented by 16 for 8 samples here, then you
> > would not need to do
> > if (state->pending == state->position)
> > x += 8;
> > elsewhere.
>
> Good idea, just note that in this case, one sample (x[1] = PCM(0 + 7
> * nchannels))
> will receive a negative index x[-7]. there is no technical problème
> just a matter of style.
>
>
> > One more nitpick about "sbc_encoder_process_input_s8". The old
> > variant was taking "position" as a function argument and returning
> > an updated position.
>
> Addtionnally, sbc_encoder_process_input_s8 would return void, and
> state->position
> would be used directly from process_input function. However, it requires changes
> in the neon code, which I'd rather avoid for now. I can send a patch
> later for this one.
Not having the need for 'state->pending' actually allows to keep both
the arguments and return value intact for "sbc_encoder_process_input_*"
functions and make the changes a bit less invasive as a side effect.
It might be even possible to make all the even/odd block checks based on
address alignment instead of "position" divisibility by 16 and get rid
of "state->odd" variable. But this would either require increasing the
effect of SBC_ALIGNED attribute to 32 bytes and make the code
non-portable and GCC specific (right now at least the C implementation
should work correctly with any compiler). Or alternatively the "X"
buffer could be allocated with manual realignment just like the "priv"
struct here:
http://git.kernel.org/?p=bluetooth/sbc.git;a=blob;f=sbc/sbc.c;h=f0c77c7cb546#l943
Which in turn may become rather invasive and wacky, so I wouldn't go
that far.
--
Best regards,
Siarhei Siamashka
^ permalink raw reply
* Re: [PATCH v3 06/10] sbc: Add mmx primitive for 1b 8s analyse
From: Siarhei Siamashka @ 2012-10-30 1:46 UTC (permalink / raw)
To: Dalleau, Frederic; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <CA+QXtDvsWoQd+JVxKC0HVRGZxUKg-JeN7_V+MfHXotg_N6EBKQ@mail.gmail.com>
On Mon, 29 Oct 2012 08:42:08 +0100
"Dalleau, Frederic" <frederic.dalleau@intel.com> wrote:
> Hi Siarhei,
>
> Since only neon is concerned by this, I'd rather add a one liner like this :
>
> #ifdef SBC_BUILD_WITH_NEON_SUPPORT
> sbc_init_primitives_neon(state);
> +
> + if (state->increment == 1)
> + state->sbc_analyze_4b_8s = sbc_analyze_1b_8s_simd;
> #endif
>
> It is more explicit, doesn't change priority and doesn't add needless code
> to other implementations.
It's not just neon, but armv6 and iwmmxt are affected too. Additionally,
neon code also provides optimized "sbc_enc_process_input_*" functions,
which are not going to work correctly for mSBC:
state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le_neon;
state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be_neon;
So I still think that it is safer and cleaner to have
"sbc_init_primitives" function performing the following in order:
1. Initialize function pointers with C implementations.
2. Allow to override them with various platform specific
implementations (which would work fine for old SBC formats).
3. At the end of the function have a check if we are actually dealing
with mSBC format and restore all the function pointers back to C
implementations in this case.
So SBC is accelerated, and mSBC at least works correctly.
Then for the follow up patches just review/fix all the assembly
optimized implementations one at a time, make sure that they do work
with mSBC and move their initialization to the end of the
"sbc_init_primitives" function.
This allows to first take care of C implementation without MMX
getting in the way. Then fix and enable mSBC support for MMX in the
last patch from your series. Later somebody could take care of the
ARM implementations in a similar way.
> And what about sbc_analyze_8s?
Renaming "sbc_analyze_4b_8s" -> "sbc_analyze_8s" in a separate patch
prior to other changes is IMHO better than keeping the old misleading
name.
> Regarding point 2, this is the reason why patch 4 was a bit bigger, the simd
> implementation is complete.
Well, I just don't quite like that after the patches
[PATCH 04/10] sbc: Add msbc flag and generic C primitive
[PATCH 05/10] sbc: Add support for mSBC frame header
we already have a complete mSBC support in C code, which is still
unusable (as in buggy) if run on mmx, iwmmxt, armv6 or arm neon capable
systems.
And having another look at it, we actually may have one more problem.
The sbc-1.0 library is already starting to get packaged in some linux
distributions. If somebody tries to run some application to do mSBC
encoding/decoding, but happens to have an old sbc-1.0 library in his
system, then it would not be able to handle the new SBC flag
gracefully. The comment about returning error code "-3 Unsupported
number of blocks" is not quite true after
http://git.kernel.org/?p=bluetooth/bluez.git;a=commit;h=ce342bf2524b
Having a proper standalone sbc library, we might want to be nicer to the
users and minimize any possible ABI related issues on the upgrade path.
>From this point of view, even introducing new msbc_encode/msbc_decode
functions might be safer. We may not go so far for the first library
update (after all, what would be the purpose of the flags argument in
this case?). But eventually making error checking code more sane
certainly looks like a good idea to me.
Regarding ABI stability in general, there is an interesting project,
which is tracking many open source libraries including bluez:
http://upstream-tracker.org/versions/bluez.html
--
Best regards,
Siarhei Siamashka
^ permalink raw reply
* Re: [PATCH 0/5] mSBC tests
From: Siarhei Siamashka @ 2012-10-30 2:18 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <1348844742.13371.38.camel@aeonflux>
On Fri, 28 Sep 2012 17:05:42 +0200
Marcel Holtmann <marcel@holtmann.org> wrote:
> > > Work done from the spec, not tested with any reference encoder.
> >
> > Does a reference mSBC encoder exist somewhere? Or any ideas how to test
> > the new code with something other than bluez just to be sure that it's
> > correct?
>
> I looked at the Bluetooth SIG website and couldn't find a reference
> encoder or sample files. The old SBC reference encoder and decoder don't
> seem to support other block sizes.
>
> SBC Encoder LIB Version 1.5
> Copyright (c) 2002 Philips Consumer Electronics, ASA Labs
>
> Usage:
> sbc_encoder [-jsv] [-lblk_len] [-nsubbands] [-p] [-rrate] [-ooutputfile] inputfile
> [-s] use the stereo mode for stereo signals
> [-v] verbose mode
> [-j] allow the use of joint coding for stereo signals
> [-lblk_len] blk_len specifies the APCM block length, out of [4,8,12,16]
> [-nsubbands] subbands specifies the number of subbands, out of [4,8]
> [-p] a simple psycho acoustic model is used
> [-rrate] specifies the bit rate in bps
> [-ooutputfile] specifies the name of the bitstream output file
> inputfile specifies the audio input file, the major audio formats are supported
>
>
> SBC Decoder LIB Version 1.5
> Copyright (c) 2002 Philips Consumer Electronics, ASA Labs
>
> Usage:
> sbc_decoder [-v] [-ooutputfile] [-pstartpos] inputfile
> [-v] verbose mode
> [-pstartpos] startpos specifies the byte offset to start with decoding
> [-ooutputfile] specifies the name of the audio output file
> inputfile specifies the name of the bitstream input file
>
> And the sample files are all from 2001. We might have to create our own
> ones and our own naive and stupid reference encoder/decoder.
Hi Marcel,
As the first step, some sort of automated testing ("make check") for
verifying sbc encoding/decoding correctness might be useful.
So far I have been using the following quickly hacked script. It just
takes a directory with *.au files, encodes them using different
settings and provides a log with md5 hashes which can be compared with
the reference log:
#!/usr/bin/env ruby
testfiles = []
Dir.new("testsamples").each {|file|
next if not file =~ /\.au$/
testfiles.push("testsamples/" + file)
}
if File.exists?("sbc-test.ok") then
fh = File.open("sbc-test.ok", "r")
okdata = fh.read()
fh.close()
end
outstring = ""
testfiles.sort!
testfiles.each {|file|
["-b32", "-b64", "-b128", "-b255"].each {|br|
["-s4", "-s8"].each {|sb|
["-S", ""].each {|snr|
["-j", ""].each {|j|
["-d", ""].each {|d|
["-B4", "-B8", "-B12", "-B16"].each {|blk|
outstring += sprintf("%s #{file} (#{br} #{sb} #{snr} #{j} #{d} #{blk})\n",
`./sbcenc #{br} #{sb} #{snr} #{j} #{d} #{blk} #{file} 2>/dev/null | md5sum`.strip).gsub(/[ ]+/, " ")
}
}
}
}
}
}
}
if not okdata then
printf("no reference ok data\n")
end
if outstring != okdata then
printf("test failed!\n")
else
printf("test ok\n")
end
fh = File.open("sbc-test.results", "w")
fh.write(outstring)
fh.close()
############################
Though I see this scripts as a dead end, because my media files are a
bit too large for adding to a git repository and these files are
all copyrighted. Some of them have a permissive license (big buck
bunny soundtrack), some of them do not.
Anyway, based on the fact that we use a fixed point encoder for sbc and
its output is deterministic, we can just encode some pseudo random noise
data and compare the checksums/hashes of the encoded results with some
reference values.
--
Best regards,
Siarhei Siamashka
^ permalink raw reply
* Re: [PATCH v3 06/10] sbc: Add mmx primitive for 1b 8s analyse
From: Dalleau, Frederic @ 2012-10-30 5:26 UTC (permalink / raw)
To: Siarhei Siamashka; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <20121030034633.2242d317@i7>
Hi Siarhei,
On Tue, Oct 30, 2012 at 2:46 AM, Siarhei Siamashka
<siarhei.siamashka@gmail.com> wrote:
> On Mon, 29 Oct 2012 08:42:08 +0100
> "Dalleau, Frederic" <frederic.dalleau@intel.com> wrote:
> So I still think that it is safer and cleaner to have
> "sbc_init_primitives" function performing the following in order:
> 1. Initialize function pointers with C implementations.
> 2. Allow to override them with various platform specific
> implementations (which would work fine for old SBC formats).
> 3. At the end of the function have a check if we are actually dealing
> with mSBC format and restore all the function pointers back to C
> implementations in this case.
That's only ok for simd, but as soon as mmx gets in there would be
a 4th category where implementation supporting msbc would override
the already defined pointer.
Imho, the definitive clean solution is to add the missing primitives.
> Well, I just don't quite like that after the patches
> [PATCH 04/10] sbc: Add msbc flag and generic C primitive
> [PATCH 05/10] sbc: Add support for mSBC frame header
> we already have a complete mSBC support in C code, which is still
> unusable (as in buggy) if run on mmx, iwmmxt, armv6 or arm neon capable
> systems.
This is experimental feature and the time of commit, it is unused.
And btw, you just suggested to split this patch, and this will also lead to a
transitional state where some new functionality can be broken.
> The sbc-1.0 library is already starting to get packaged in some linux
> distributions. If somebody tries to run some application to do mSBC
> encoding/decoding, but happens to have an old sbc-1.0 library in his
I may be mistaking but most projects uses sbc statically, so checking is mostly
done at compile time. So as I often hear in open source : we'll cross the bridge
when we get to it :)
Regards,
Frédéric
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox