* [PATCH 2/3] Bluetooth: Add support for reading LE White List Size
From: Johan Hedberg @ 2013-01-22 12:02 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358856121-1928-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
The LE White List Size is necessary to be known before attempting to
feed the controller with any addresses intended for the white list. This
patch adds the necessary HCI command sending to the HCI init sequence.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/hci.h | 6 ++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_event.c | 20 ++++++++++++++++++++
3 files changed, 27 insertions(+)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 521eefa..f1766a6 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1001,6 +1001,12 @@ struct hci_cp_le_create_conn {
#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
+#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f
+struct hci_rp_le_read_white_list_size {
+ __u8 status;
+ __u8 size;
+} __packed;
+
#define HCI_OP_LE_CONN_UPDATE 0x2013
struct hci_cp_le_conn_update {
__le16 handle;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3f607c9..d6ed4ac 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -153,6 +153,7 @@ struct hci_dev {
__u8 features[8];
__u8 host_features[8];
__u8 le_features[8];
+ __u8 le_white_list_size;
__u8 commands[64];
__u8 hci_ver;
__u16 hci_rev;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 07c8c79..d2fee64 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -614,6 +614,9 @@ static void le_setup(struct hci_dev *hdev)
/* Read LE Advertising Channel TX Power */
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+
+ /* Read LE White List Size */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
}
static void hci_setup(struct hci_dev *hdev)
@@ -1306,6 +1309,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
}
}
+static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_white_list_size *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size);
+
+ if (!rp->status)
+ hdev->le_white_list_size = rp->size;
+
+ hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status);
+}
+
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
@@ -2684,6 +2700,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_set_scan_enable(hdev, skb);
break;
+ case HCI_OP_LE_READ_WHITE_LIST_SIZE:
+ hci_cc_le_read_white_list_size(hdev, skb);
+ break;
+
case HCI_OP_LE_LTK_REPLY:
hci_cc_le_ltk_reply(hdev, skb);
break;
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/3] Bluetooth: Add LE Local Features reading support
From: Johan Hedberg @ 2013-01-22 12:01 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
To be able to make the appropriate decisions for some LE procedures we
need to know the LE features that the local controller supports.
Therefore, it's important to have the LE Read Local Supported Features
HCI comand as part of the HCI init sequence.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/hci.h | 6 ++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_event.c | 20 ++++++++++++++++++++
3 files changed, 27 insertions(+)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 45eee08..521eefa 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt;
} __packed;
+#define HCI_OP_LE_READ_LOCAL_FEATURES 0x2003
+struct hci_rp_le_read_local_features {
+ __u8 status;
+ __u8 features[8];
+} __packed;
+
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
struct hci_rp_le_read_adv_tx_power {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 769a740..3f607c9 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -152,6 +152,7 @@ struct hci_dev {
__u8 minor_class;
__u8 features[8];
__u8 host_features[8];
+ __u8 le_features[8];
__u8 commands[64];
__u8 hci_ver;
__u16 hci_rev;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 705078a..07c8c79 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -609,6 +609,9 @@ static void le_setup(struct hci_dev *hdev)
/* Read LE Buffer Size */
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+ /* Read LE Local Supported Features */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+
/* Read LE Advertising Channel TX Power */
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
}
@@ -1090,6 +1093,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
}
+static void hci_cc_le_read_local_features(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_local_features *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (!rp->status)
+ memcpy(hdev->le_features, rp->features, 8);
+
+ hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status);
+}
+
static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2628,6 +2644,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_buffer_size(hdev, skb);
break;
+ case HCI_OP_LE_READ_LOCAL_FEATURES:
+ hci_cc_le_read_local_features(hdev, skb);
+ break;
+
case HCI_OP_LE_READ_ADV_TX_POWER:
hci_cc_le_read_adv_tx_power(hdev, skb);
break;
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH 00/10 v2] Bluetooth: Fix more mgmt parameter checks
From: Marcel Holtmann @ 2013-01-22 10:45 UTC (permalink / raw)
To: Johan Hedberg; +Cc: linux-bluetooth
In-Reply-To: <1358684842-4441-1-git-send-email-johan.hedberg@gmail.com>
Hi Johan,
> Here's an updated patch set based on feedback from Marcel for the first
> one.
>
> This one still needs my patch (acked by Marcel)
>
> "Bluetooth: Fix Class of Device indication when powering off"
>
> and Szymons patch (acked by me)
>
> "Bluetooth: Fix pair device command reply if adapter is powered off"
>
> to be applied first to bluetooth-next.git. At least I've got those two
> in my local tree before this set, so no guarantees that there wont be
> conflicts if they are not applied first.
they all look good to me.
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* Re: [RFC] Bluetooth: Fix missing MITM protection when being responding LM
From: Marcel Holtmann @ 2013-01-22 10:36 UTC (permalink / raw)
To: Johan Hedberg; +Cc: mail, linux-bluetooth, Timo Mueller
In-Reply-To: <20130122081320.GA27138@x220>
Hi Johan,
> > A MITM protected SSP associaton model can be used for pairing if both
> > local and remote IO capabilities are set to something other than
> > NoInputNoOutput.
> >
> > With these IO capabilities a MITM protected SSP association model is
> > used if we are initiating the pairing process (initiating LM).
> >
> > When responding to a pairing request - remote device is the initiating
> > LM - the pairing should also be proteced against MITM attacks.
> >
> > Signed-off-by: Timo Mueller <timo.mueller@bmw-carit.de>
> > ---
> > When we were testing the iPhone 5 we noticed that the association
> > model changes depending on which side initiates the pairing. For
> > example if we paired from the phone "Just Works" was used while if the
> > phone was the responding LM a "Numeric Comparison" was used instead.
> >
> > We'd like to enforce MITM protection in our cars whenever possible.
> > That is why we want to set the MITM protection even when being the
> > responding LM. The patch proposes this policy as the default approach.
> >
> > Expected SSP accociation model:
> > |-------------------------------------------|
> > | Device | SSP assocation model |
> > |===========================================|
> > | KeyboardDisplay | Numeric Comparison |
> > | ------------------------------------------|
> > | NoInputNoOutput | Just Works |
> > | ------------------------------------------|
> > | KeyboardOnly | Passkey Entry |
> > |-------------------------------------------|
> >
> > Tested Devices:
> > KeyboardDisplay:
> > iPhone 4 (iOS4), iPhone 5 (iOS6), Nokia N9, HTC One S,
> > Samsung Galaxy (CM 10.1), Nexus 4, Nokia 6313 Classic,
> > BlueZ 5 - Simple Agent
> >
> > NoInputNoOutput:
> > BlueZ 5 - Simple Agent
> >
> > KeyboardOnly:
> > Logitech Keyboard Case, BlueZ 5 - Simple Agent
> >
> > I've also tested this patch with the following kernels:
> > 3.8-rc4
> > 3.4
> >
> > Best regards,
> > Timo
> >
> > net/bluetooth/hci_conn.c | 6 +++++-
> > 1 file changed, 5 insertions(+), 1 deletion(-)
> >
> > diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> > index 25bfce0..806583b 100644
> > --- a/net/bluetooth/hci_conn.c
> > +++ b/net/bluetooth/hci_conn.c
> > @@ -357,11 +357,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
> > conn->type = type;
> > conn->mode = HCI_CM_ACTIVE;
> > conn->state = BT_OPEN;
> > - conn->auth_type = HCI_AT_GENERAL_BONDING;
> > conn->io_capability = hdev->io_capability;
> > conn->remote_auth = 0xff;
> > conn->key_type = 0xff;
> >
> > + if (hdev->io_capability == 0x03)
> > + conn->auth_type = HCI_AT_GENERAL_BONDING;
> > + else
> > + conn->auth_type = HCI_AT_GENERAL_BONDING_MITM;
> > +
> > set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
> > conn->disc_timeout = HCI_DISCONN_TIMEOUT;
>
> The question that could equally be asked is why does iOS *not* set the
> MITM flag when initiating pairing to a remote device. If it did this
> issue would not exist.
because iOS does not support SIM Access Profile. Outside of SIM Access,
there is really no need to support high security.
The difference between medium security and high security is really only
the man-in-the-middle protection. From an encryption and link key
strength point of view they are identical. Both link keys are P-192
derived and the encryption cipher is still E0.
> Since the over all sequence of the IO capability negotiation with iOS devices
> when we're on the acceptor side might be a bit unclear to people by just
> reading your commit message and patch I'll provide here a HCI trace of it:
>
> > HCI Event: IO Capability Response (0x32) plen 9
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> IO capability: DisplayYesNo (0x01)
> OOB data: Authentication data not present (0x00)
> Authentication: General Bonding - MITM not required (0x04)
> > HCI Event: IO Capability Request (0x31) plen 6
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> < HCI Command: IO Capability Request Reply (0x01|0x002b) plen 9
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> IO capability: DisplayYesNo (0x01)
> OOB data: Authentication data not present (0x00)
> Authentication: General Bonding - MITM not required (0x04)
>
> Basically the BlueZ side has so far given the remote initiator the
> choice whether to do just-works or not. However, I do agree that it's
> good to strive for a best-possible security in the pairing (within the
> limits of the available IO capabilities) so setting the MITM flag on our
> side should be fine.
This is still something we need to think about carefully. I am not sure
we should just always be doing this. This might actually be better
solved with a sysfs option or a mgmt command to pick different behavior.
Since this is General Bonding and not Dedicated Bonding, I am not
convinced that this is a good idea.
> The one thing that I'd still consider improving is to make the setting
> of the MITM flag also dependent on the remote IO capability and not just
> the local one, since we do know the remote one before we need to give
> our own value when we are on the acceptor side of the pairing. Thoughts?
This is a minimum requirement that we check the remote IO capabilities
here. Since there is no point in trying MITM protection if the other
side has no capabilities to ever create such a key.
I would be also curious if we still can qualify our own behavior and not
end up with cases where we can't because we have no way to trigger it.
Regards
Marcel
^ permalink raw reply
* [RFC 1/1] avctp: Set browsing channel connection for ct
From: Alexandros Antonopoulos @ 2013-01-22 10:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358850844-19752-1-git-send-email-alexandros.antonopoulos@oss.bmw-carit.de>
Add two extra states for the browsing channel and handle the connection
sequence in avrcp.c
---
profiles/audio/avctp.c | 11 +++++++++++
profiles/audio/avctp.h | 4 +++-
profiles/audio/avrcp.c | 28 +++++++++++++++++++++++-----
profiles/audio/device.c | 4 ++++
4 files changed, 41 insertions(+), 6 deletions(-)
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index e65594d..753f8fe 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -476,6 +476,12 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
case AVCTP_STATE_CONNECTED:
DBG("AVCTP Connected");
break;
+ case AVCTP_STATE_BROWSING_CONNECTING:
+ DBG("AVCTP Browsing Connecting");
+ break;
+ case AVCTP_STATE_BROWSING_CONNECTED:
+ DBG("AVCTP Browsing Connected");
+ break;
default:
error("Invalid AVCTP state %d", new_state);
return;
@@ -913,9 +919,12 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_browsing_cb, session);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED);
return;
fail:
+ avctp_set_state(session, AVCTP_STATE_CONNECTED);
+
if (session->browsing) {
avctp_channel_destroy(session->browsing);
session->browsing = NULL;
@@ -1648,6 +1657,8 @@ int avctp_connect_browsing(struct avctp *session)
if (session->browsing != NULL)
return 0;
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
+
io = bt_io_connect(avctp_connect_browsing_cb, session, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR,
adapter_get_address(session->server->adapter),
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index c25a3b3..d1fe693 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -67,7 +67,9 @@ struct avctp;
typedef enum {
AVCTP_STATE_DISCONNECTED = 0,
AVCTP_STATE_CONNECTING,
- AVCTP_STATE_CONNECTED
+ AVCTP_STATE_CONNECTED,
+ AVCTP_STATE_BROWSING_CONNECTING,
+ AVCTP_STATE_BROWSING_CONNECTED
} avctp_state_t;
typedef void (*avctp_state_cb) (struct audio_device *dev,
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 636d3e4..6cab45f 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2182,12 +2182,9 @@ static void session_tg_init(struct avrcp *session)
(1 << AVRCP_EVENT_TRACK_REACHED_END) |
(1 << AVRCP_EVENT_SETTINGS_CHANGED);
- if (session->version >= 0x0104) {
+ if (session->version >= 0x0104)
avrcp_register_notification(session,
AVRCP_EVENT_VOLUME_CHANGED);
- if (session->features & AVRCP_FEATURE_BROWSING)
- avctp_connect_browsing(session->conn);
- }
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
@@ -2341,6 +2338,7 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
{
struct avrcp_server *server;
struct avrcp *session;
+ uint8_t browsing_failed = false;
server = find_server(servers, device_get_adapter(dev->btd_dev));
if (!server)
@@ -2367,8 +2365,28 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- session->init(session);
+ if (session->version <= 0x0103) {
+ session->init(session);
+ break;
+ }
+
+ /*AVCRP >= 1.4*/
+ if (old_state == AVCTP_STATE_CONNECTING) {
+ if (avctp_connect_browsing(session->conn) != 0)
+ browsing_failed = true;
+ } else if (old_state == AVCTP_STATE_BROWSING_CONNECTING) {
+ browsing_failed = true;
+ }
+ if (browsing_failed)
+ session->init(session);
+ break;
+ case AVCTP_STATE_BROWSING_CONNECTED:
+ if (session == NULL)
+ break;
+
+ session->init(session);
+ break;
default:
return;
}
diff --git a/profiles/audio/device.c b/profiles/audio/device.c
index d4ba6d2..2aa22bb 100644
--- a/profiles/audio/device.c
+++ b/profiles/audio/device.c
@@ -289,6 +289,10 @@ static void device_avctp_cb(struct audio_device *dev,
break;
case AVCTP_STATE_CONNECTED:
break;
+ case AVCTP_STATE_BROWSING_CONNECTING:
+ break;
+ case AVCTP_STATE_BROWSING_CONNECTED:
+ break;
}
}
--
1.8.1
^ permalink raw reply related
* [RFC 0/1] avctp: Set browsing channel connection for ct
From: Alexandros Antonopoulos @ 2013-01-22 10:34 UTC (permalink / raw)
To: linux-bluetooth
A few comments:
Handling channel creation for AVRCP:
For AVRCP >= 1.4 the browsing channel can be connected after the
control channel is connected successfully.
It's not clear for me what bluez should do in case the browsing
channel connection fails. There is nothing stopping bluez for still
providing avrcp 1.3 functionality over the control channel.
If the choice is to allow the use of the control channel even if the
browsing one fails then bluez needs the logic in
avrcp.c:state_changed(). If not then both channels must be destroyed
and the session must not be initialized.
State AVCTP_STATE_CONNECTED in state_changed has also to differentiate
between AVRCP <= 1.3 and AVRCP >= 1.4. In AVRCP <= 1.3 after the
control channel is created the avrcp session can be initialized.
In AVRCP >= 1.4 we need first to try and connect the browsing channel.
The session can be created if:
- The state is AVCTP_STATE_BROWSING_CONNECTED (if the browsing channel
was connected successfully),
- The state is AVCTP_STATE_CONNECTED and old_state is
AVCTP_STATE_BROWSING_CONNECTING (if the browsing channel connection
failed),
- The state is AVCTP_STATE_CONNECTED but avctp_connect_browsing fails.
Handling the feature mask:
Since bluez will support AVRCP 1.5 is the AVRCP_FEATURE_BROWSING
actually required? It was checked on the target side during the session
init.
Alexandros Antonopoulos (1):
avctp: Set browsing channel connection for ct
profiles/audio/avctp.c | 11 +++++++++++
profiles/audio/avctp.h | 4 +++-
profiles/audio/avrcp.c | 28 +++++++++++++++++++++++-----
profiles/audio/device.c | 4 ++++
4 files changed, 41 insertions(+), 6 deletions(-)
--
1.8.1
^ permalink raw reply
* [PATCH] avctp: Add functionality for CT browsing commands
From: Alexandros Antonopoulos @ 2013-01-22 10:32 UTC (permalink / raw)
To: linux-bluetooth
Add the missing functionality for full support of browsing channel
on the CT side. This is required for AVRCP >= 1.4
The patch is based on the control channel functionality
---
profiles/audio/avctp.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/avctp.h | 6 +++
2 files changed, 146 insertions(+)
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index e65594d..02bee2b 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -136,6 +136,14 @@ struct avctp_control_req {
void *user_data;
};
+struct avctp_browsing_req {
+ struct avctp_pending_req *p;
+ uint8_t *operands;
+ uint16_t operand_count;
+ avctp_browsing_rsp_cb func;
+ void *user_data;
+};
+
typedef int (*avctp_process_cb) (void *data);
struct avctp_pending_req {
@@ -527,6 +535,44 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
return err;
}
+static int avctp_browsing_send(struct avctp_channel *browsing,
+ uint8_t transaction, uint8_t cr,
+ uint8_t *operands, size_t operand_count)
+{
+ struct avctp_header *avctp;
+ struct msghdr msg;
+ struct iovec iov[2];
+ int sk, err = 0;
+
+ iov[0].iov_base = browsing->buffer;
+ iov[0].iov_len = sizeof(*avctp);
+ iov[1].iov_base = operands;
+ iov[1].iov_len = operand_count;
+
+ if (browsing->omtu < (iov[0].iov_len + iov[1].iov_len))
+ return -EOVERFLOW;
+
+ sk = g_io_channel_unix_get_fd(browsing->io);
+
+ memset(browsing->buffer, 0, iov[0].iov_len);
+
+ avctp = (void *) browsing->buffer;
+
+ avctp->transaction = transaction;
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = cr;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ if (sendmsg(sk, &msg, 0) < 0)
+ err = -errno;
+
+ return err;
+}
+
static void control_req_destroy(void *data)
{
struct avctp_control_req *req = data;
@@ -535,6 +581,14 @@ static void control_req_destroy(void *data)
g_free(req);
}
+static void browsing_req_destroy(void *data)
+{
+ struct avctp_browsing_req *req = data;
+
+ g_free(req->operands);
+ g_free(req);
+}
+
static gboolean req_timeout(gpointer user_data)
{
struct avctp_channel *chan = user_data;
@@ -563,6 +617,15 @@ static int process_control(void *data)
req->operands, req->operand_count);
}
+static int process_browsing(void *data)
+{
+ struct avctp_browsing_req *req = data;
+ struct avctp_pending_req *p = req->p;
+
+ return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+ req->operands, req->operand_count);
+}
+
static gboolean process_queue(void *user_data)
{
struct avctp_channel *chan = user_data;
@@ -636,6 +699,48 @@ static void control_response(struct avctp_channel *control,
}
}
+static void browsing_response(struct avctp_channel *browsing,
+ struct avctp_header *avctp,
+ uint8_t *operands,
+ size_t operand_count)
+{
+ struct avctp_pending_req *p = browsing->p;
+ struct avctp_browsing_req *req;
+ GSList *l;
+
+ if (p && p->transaction == avctp->transaction) {
+ browsing->processed = g_slist_prepend(browsing->processed, p);
+
+ if (p->timeout > 0) {
+ g_source_remove(p->timeout);
+ p->timeout = 0;
+ }
+
+ browsing->p = NULL;
+
+ if (browsing->process_id == 0)
+ browsing->process_id = g_idle_add(process_queue,
+ browsing);
+ }
+
+ for (l = browsing->processed; l; l = l->next) {
+ p = l->data;
+ req = p->data;
+
+ if (p->transaction != avctp->transaction)
+ continue;
+
+ if (req->func && req->func(browsing->session,
+ operands, operand_count,
+ req->user_data))
+ return;
+
+ browsing->processed = g_slist_remove(browsing->processed, p);
+
+ return;
+ }
+}
+
static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
@@ -665,6 +770,11 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
ret -= AVCTP_HEADER_LENGTH;
operand_count = ret;
+ if (avctp->cr == AVCTP_RESPONSE) {
+ browsing_response(browsing, avctp, operands, operand_count);
+ return TRUE;
+ }
+
packet_size = AVCTP_HEADER_LENGTH;
avctp->cr = AVCTP_RESPONSE;
@@ -1294,6 +1404,36 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
return 0;
}
+int avctp_send_browsing_req(struct avctp *session,
+ uint8_t *operands, size_t operand_count,
+ avctp_browsing_rsp_cb func, void *user_data)
+{
+ struct avctp_channel *browsing = session->browsing;
+ struct avctp_pending_req *p;
+ struct avctp_browsing_req *req;
+
+ if (browsing == NULL)
+ return -ENOTCONN;
+
+ req = g_new0(struct avctp_browsing_req, 1);
+ req->func = func;
+ req->operands = g_memdup(operands, operand_count);
+ req->operand_count = operand_count;
+ req->user_data = user_data;
+
+ p = pending_create(browsing, process_browsing, req,
+ browsing_req_destroy);
+
+ req->p = p;
+
+ g_queue_push_tail(browsing->queue, p);
+
+ if (browsing->process_id == 0)
+ browsing->process_id = g_idle_add(process_queue, browsing);
+
+ return 0;
+}
+
static char *op2str(uint8_t op)
{
switch (op & 0x7f) {
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index c25a3b3..b9107bd 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -82,6 +82,9 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
uint8_t subunit, uint8_t *operands,
size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
uint8_t transaction,
uint8_t *operands, size_t operand_count,
@@ -116,3 +119,6 @@ int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
uint8_t subunit, uint8_t *operands,
size_t operand_count,
avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing_req(struct avctp *session,
+ uint8_t *operands, size_t operand_count,
+ avctp_browsing_rsp_cb func, void *user_data);
--
1.8.1
^ permalink raw reply related
* RE: [RFC] Bluetooth: Fix missing MITM protection when being responding LM
From: Oleksandr.Domin @ 2013-01-22 8:53 UTC (permalink / raw)
To: johan.hedberg; +Cc: linux-bluetooth
In-Reply-To: <20130122081320.GA27138@x220>
Hi Johan,
>Hi Timo,
>
>On Mon, Jan 21, 2013, mail@timomueller.eu wrote:
>> A MITM protected SSP associaton model can be used for pairing if both
>> local and remote IO capabilities are set to something other than
>> NoInputNoOutput.
>>
>> With these IO capabilities a MITM protected SSP association model is
>> used if we are initiating the pairing process (initiating LM).
>>
>> When responding to a pairing request - remote device is the initiating
>> LM - the pairing should also be proteced against MITM attacks.
>>
>> Signed-off-by: Timo Mueller <timo.mueller@bmw-carit.de>
>> ---
>> When we were testing the iPhone 5 we noticed that the association
>> model changes depending on which side initiates the pairing. For
>> example if we paired from the phone "Just Works" was used while if the
>> phone was the responding LM a "Numeric Comparison" was used instead.
>>
>> We'd like to enforce MITM protection in our cars whenever possible.
>> That is why we want to set the MITM protection even when being the
>> responding LM. The patch proposes this policy as the default approach.
>>
>> Expected SSP accociation model:
>> |-------------------------------------------|
>> | Device | SSP assocation model |
>> |===========================================|
>> | KeyboardDisplay | Numeric Comparison |
>> | ------------------------------------------|
>> | NoInputNoOutput | Just Works |
>> | ------------------------------------------|
>> | KeyboardOnly | Passkey Entry |
>> |-------------------------------------------|
>>
>> Tested Devices:
>> KeyboardDisplay:
>> iPhone 4 (iOS4), iPhone 5 (iOS6), Nokia N9, HTC One S,
>> Samsung Galaxy (CM 10.1), Nexus 4, Nokia 6313 Classic,
>> BlueZ 5 - Simple Agent
>>
>> NoInputNoOutput:
>> BlueZ 5 - Simple Agent
>>
>> KeyboardOnly:
>> Logitech Keyboard Case, BlueZ 5 - Simple Agent
>>
>> I've also tested this patch with the following kernels:
>> 3.8-rc4
>> 3.4
>>
>> Best regards,
>> Timo
>>
>> net/bluetooth/hci_conn.c | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
>> index 25bfce0..806583b 100644
>> --- a/net/bluetooth/hci_conn.c
>> +++ b/net/bluetooth/hci_conn.c
>> @@ -357,11 +357,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev,
>int type, bdaddr_t *dst)
>> conn->type = type;
>> conn->mode = HCI_CM_ACTIVE;
>> conn->state = BT_OPEN;
>> - conn->auth_type = HCI_AT_GENERAL_BONDING;
>> conn->io_capability = hdev->io_capability;
>> conn->remote_auth = 0xff;
>> conn->key_type = 0xff;
>>
>> + if (hdev->io_capability == 0x03)
>> + conn->auth_type = HCI_AT_GENERAL_BONDING;
>> + else
>> + conn->auth_type = HCI_AT_GENERAL_BONDING_MITM;
>> +
>> set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
>> conn->disc_timeout = HCI_DISCONN_TIMEOUT;
>
>The question that could equally be asked is why does iOS *not* set the
>MITM flag when initiating pairing to a remote device. If it did this
>issue would not exist.
Welcome in the IOP WORLD :-D
>
>Since the over all sequence of the IO capability negotiation with iOS
>devices
>when we're on the acceptor side might be a bit unclear to people by just
>reading your commit message and patch I'll provide here a HCI trace of it:
>
> > HCI Event: IO Capability Response (0x32) plen 9
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> IO capability: DisplayYesNo (0x01)
> OOB data: Authentication data not present (0x00)
> Authentication: General Bonding - MITM not required (0x04)
> > HCI Event: IO Capability Request (0x31) plen 6
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> < HCI Command: IO Capability Request Reply (0x01|0x002b) plen 9
> Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
> IO capability: DisplayYesNo (0x01)
> OOB data: Authentication data not present (0x00)
> Authentication: General Bonding - MITM not required (0x04)
>
>Basically the BlueZ side has so far given the remote initiator the
>choice whether to do just-works or not. However, I do agree that it's
>good to strive for a best-possible security in the pairing (within the
>limits of the available IO capabilities) so setting the MITM flag on our
>side should be fine.
>
>The one thing that I'd still consider improving is to make the setting
>of the MITM flag also dependent on the remote IO capability and not just
>the local one, since we do know the remote one before we need to give
>our own value when we are on the acceptor side of the pairing. Thoughts?
>
>Johan
>--
>To unsubscribe from this list: send the line "unsubscribe linux-bluetooth"
>in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC] Bluetooth: Fix missing MITM protection when being responding LM
From: Johan Hedberg @ 2013-01-22 8:13 UTC (permalink / raw)
To: mail; +Cc: linux-bluetooth, Timo Mueller
In-Reply-To: <1358789748-318-1-git-send-email-mail@timomueller.eu>
Hi Timo,
On Mon, Jan 21, 2013, mail@timomueller.eu wrote:
> A MITM protected SSP associaton model can be used for pairing if both
> local and remote IO capabilities are set to something other than
> NoInputNoOutput.
>
> With these IO capabilities a MITM protected SSP association model is
> used if we are initiating the pairing process (initiating LM).
>
> When responding to a pairing request - remote device is the initiating
> LM - the pairing should also be proteced against MITM attacks.
>
> Signed-off-by: Timo Mueller <timo.mueller@bmw-carit.de>
> ---
> When we were testing the iPhone 5 we noticed that the association
> model changes depending on which side initiates the pairing. For
> example if we paired from the phone "Just Works" was used while if the
> phone was the responding LM a "Numeric Comparison" was used instead.
>
> We'd like to enforce MITM protection in our cars whenever possible.
> That is why we want to set the MITM protection even when being the
> responding LM. The patch proposes this policy as the default approach.
>
> Expected SSP accociation model:
> |-------------------------------------------|
> | Device | SSP assocation model |
> |===========================================|
> | KeyboardDisplay | Numeric Comparison |
> | ------------------------------------------|
> | NoInputNoOutput | Just Works |
> | ------------------------------------------|
> | KeyboardOnly | Passkey Entry |
> |-------------------------------------------|
>
> Tested Devices:
> KeyboardDisplay:
> iPhone 4 (iOS4), iPhone 5 (iOS6), Nokia N9, HTC One S,
> Samsung Galaxy (CM 10.1), Nexus 4, Nokia 6313 Classic,
> BlueZ 5 - Simple Agent
>
> NoInputNoOutput:
> BlueZ 5 - Simple Agent
>
> KeyboardOnly:
> Logitech Keyboard Case, BlueZ 5 - Simple Agent
>
> I've also tested this patch with the following kernels:
> 3.8-rc4
> 3.4
>
> Best regards,
> Timo
>
> net/bluetooth/hci_conn.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 25bfce0..806583b 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -357,11 +357,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
> conn->type = type;
> conn->mode = HCI_CM_ACTIVE;
> conn->state = BT_OPEN;
> - conn->auth_type = HCI_AT_GENERAL_BONDING;
> conn->io_capability = hdev->io_capability;
> conn->remote_auth = 0xff;
> conn->key_type = 0xff;
>
> + if (hdev->io_capability == 0x03)
> + conn->auth_type = HCI_AT_GENERAL_BONDING;
> + else
> + conn->auth_type = HCI_AT_GENERAL_BONDING_MITM;
> +
> set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
> conn->disc_timeout = HCI_DISCONN_TIMEOUT;
The question that could equally be asked is why does iOS *not* set the
MITM flag when initiating pairing to a remote device. If it did this
issue would not exist.
Since the over all sequence of the IO capability negotiation with iOS devices
when we're on the acceptor side might be a bit unclear to people by just
reading your commit message and patch I'll provide here a HCI trace of it:
> HCI Event: IO Capability Response (0x32) plen 9
Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
IO capability: DisplayYesNo (0x01)
OOB data: Authentication data not present (0x00)
Authentication: General Bonding - MITM not required (0x04)
> HCI Event: IO Capability Request (0x31) plen 6
Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
< HCI Command: IO Capability Request Reply (0x01|0x002b) plen 9
Address: 04:F7:E4:xx:xx:xx (OUI 04-F7-E4)
IO capability: DisplayYesNo (0x01)
OOB data: Authentication data not present (0x00)
Authentication: General Bonding - MITM not required (0x04)
Basically the BlueZ side has so far given the remote initiator the
choice whether to do just-works or not. However, I do agree that it's
good to strive for a best-possible security in the pairing (within the
limits of the available IO capabilities) so setting the MITM flag on our
side should be fine.
The one thing that I'd still consider improving is to make the setting
of the MITM flag also dependent on the remote IO capability and not just
the local one, since we do know the remote one before we need to give
our own value when we are on the acceptor side of the pairing. Thoughts?
Johan
^ permalink raw reply
* Simultaneous data transfer on ACL and BLE links
From: Ajay @ 2013-01-21 21:04 UTC (permalink / raw)
To: linux-bluetooth
[-- Attachment #1: Type: text/plain, Size: 1431 bytes --]
Hi ,
Kernel has set some restrictions in simultaneous L2cap data
transfer on BLE and ACL links on a common device .
My test scenario is as follows
Device A has both LE and ACL link with device B and C
respectively . when i start sending l2cap data on ACL link (A to B),
it work fine . But when i starts sending BLE data along with ACL data
(A to C ) , it fails with error "send failed : interrupt system call"
. when i count the LE packets that had reached upto hci layer , it
shows some 367 packets.
Later i noticed that LE data is getting queued at hci
layer and it is not scheduled properly . following piece of code
tells us the order of scheduling.. (hci_core.c)
static void hci_tx_task(unsigned long arg)
{
struct hci_dev *hdev = (struct hci_dev *) arg;
struct sk_buff *skb;
read_lock(&hci_task_lock);
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
hdev->sco_cnt, hdev->le_cnt);
/* Schedule queues and send stuff to HCI driver */
hci_sched_acl(hdev);
hci_sched_sco(hdev);
hci_sched_esco(hdev);
hci_sched_le(hdev);
......
Here both acl and le scheduling are using same count as
reference (hdev->acl_cnt) and they have never make use of hdev->le_cnt.
please kindly go through this issue.
--
Thanks & Regards
AJAY KV
GlobalEdge software Ltd
8892753703
[-- Attachment #2: ajay_kv.vcf --]
[-- Type: text/x-vcard, Size: 74 bytes --]
begin:vcard
fn:AJAY KV
n:;AJAY
tel;cell:8892753703
version:2.1
end:vcard
^ permalink raw reply
* [PATCH] tools: Fix glib assert in mgmt-tester if hci_vhci module is not loaded
From: Szymon Janc @ 2013-01-21 20:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
Print descriptive warning on HCI emulation setup failure and fail test
gracefully. This is better comparing to glib assertion when not fully
initialized HCI emulation is being unreferenced.
---
src/shared/hciemu.c | 31 ++++++++++++++++++++++---------
tools/mgmt-tester.c | 4 ++++
2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/src/shared/hciemu.c b/src/shared/hciemu.c
index d523743..a15102d 100644
--- a/src/shared/hciemu.c
+++ b/src/shared/hciemu.c
@@ -188,7 +188,7 @@ static guint create_source_btdev(int fd, struct btdev *btdev)
return source;
}
-static void create_vhci(struct hciemu *hciemu)
+static bool create_vhci(struct hciemu *hciemu)
{
struct btdev *btdev;
uint8_t bdaddr[6];
@@ -197,7 +197,7 @@ static void create_vhci(struct hciemu *hciemu)
btdev = btdev_create(BTDEV_TYPE_BREDR, 0x00);
if (!btdev)
- return;
+ return false;
str = hciemu_get_address(hciemu);
@@ -210,15 +210,17 @@ static void create_vhci(struct hciemu *hciemu)
fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
btdev_destroy(btdev);
- return;
+ return false;
}
hciemu->master_dev = btdev;
hciemu->master_source = create_source_btdev(fd, btdev);
+
+ return true;
}
-static void create_stack(struct hciemu *hciemu)
+static bool create_stack(struct hciemu *hciemu)
{
struct btdev *btdev;
struct bthost *bthost;
@@ -226,12 +228,12 @@ static void create_stack(struct hciemu *hciemu)
btdev = btdev_create(BTDEV_TYPE_BREDR, 0x00);
if (!btdev)
- return;
+ return false;
bthost = bthost_create();
if (!bthost) {
btdev_destroy(btdev);
- return;
+ return false;
}
btdev_set_command_handler(btdev, client_command_callback, hciemu);
@@ -240,7 +242,7 @@ static void create_stack(struct hciemu *hciemu)
0, sv) < 0) {
bthost_destroy(bthost);
btdev_destroy(btdev);
- return;
+ return false;
}
hciemu->client_dev = btdev;
@@ -248,6 +250,8 @@ static void create_stack(struct hciemu *hciemu)
hciemu->client_source = create_source_btdev(sv[0], btdev);
hciemu->host_source = create_source_bthost(sv[1], bthost);
+
+ return true;
}
static gboolean start_stack(gpointer user_data)
@@ -267,8 +271,17 @@ struct hciemu *hciemu_new(void)
if (!hciemu)
return NULL;
- create_vhci(hciemu);
- create_stack(hciemu);
+ if (!create_vhci(hciemu)) {
+ g_free(hciemu);
+ return NULL;
+ }
+
+ if (!create_stack(hciemu)) {
+ g_source_remove(hciemu->master_source);
+ btdev_destroy(hciemu->master_dev);
+ g_free(hciemu);
+ return NULL;
+ }
g_idle_add(start_stack, hciemu);
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index bf61f9d..63e7714 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -217,6 +217,10 @@ static void read_index_list_callback(uint8_t status, uint16_t length,
index_removed_callback, NULL, NULL);
data->hciemu = hciemu_new();
+ if (!data->hciemu) {
+ tester_warn("Failed to setup HCI emulation");
+ tester_pre_setup_failed();
+ }
}
static void test_pre_setup(const void *test_data)
--
1.8.1
^ permalink raw reply related
* Re: [PATCH] monitor: Fix possible crash on unknown command
From: Marcel Holtmann @ 2013-01-21 19:39 UTC (permalink / raw)
To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1358781467-32172-1-git-send-email-szymon.janc@tieto.com>
Hi Szymon,
> If uknown command is received monitor would crash due to dereference
> of NULL opcode_data pointer.
> ---
> monitor/packet.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
good catch. Patch has been applied.
Regards
Marcel
^ permalink raw reply
* [RFC] Bluetooth: Fix missing MITM protection when being responding LM
From: mail @ 2013-01-21 17:35 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Timo Mueller
From: Timo Mueller <timo.mueller@bmw-carit.de>
A MITM protected SSP associaton model can be used for pairing if both
local and remote IO capabilities are set to something other than
NoInputNoOutput.
With these IO capabilities a MITM protected SSP association model is
used if we are initiating the pairing process (initiating LM).
When responding to a pairing request - remote device is the initiating
LM - the pairing should also be proteced against MITM attacks.
Signed-off-by: Timo Mueller <timo.mueller@bmw-carit.de>
---
When we were testing the iPhone 5 we noticed that the association
model changes depending on which side initiates the pairing. For
example if we paired from the phone "Just Works" was used while if the
phone was the responding LM a "Numeric Comparison" was used instead.
We'd like to enforce MITM protection in our cars whenever possible.
That is why we want to set the MITM protection even when being the
responding LM. The patch proposes this policy as the default approach.
Expected SSP accociation model:
|-------------------------------------------|
| Device | SSP assocation model |
|===========================================|
| KeyboardDisplay | Numeric Comparison |
| ------------------------------------------|
| NoInputNoOutput | Just Works |
| ------------------------------------------|
| KeyboardOnly | Passkey Entry |
|-------------------------------------------|
Tested Devices:
KeyboardDisplay:
iPhone 4 (iOS4), iPhone 5 (iOS6), Nokia N9, HTC One S,
Samsung Galaxy (CM 10.1), Nexus 4, Nokia 6313 Classic,
BlueZ 5 - Simple Agent
NoInputNoOutput:
BlueZ 5 - Simple Agent
KeyboardOnly:
Logitech Keyboard Case, BlueZ 5 - Simple Agent
I've also tested this patch with the following kernels:
3.8-rc4
3.4
Best regards,
Timo
net/bluetooth/hci_conn.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 25bfce0..806583b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -357,11 +357,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->type = type;
conn->mode = HCI_CM_ACTIVE;
conn->state = BT_OPEN;
- conn->auth_type = HCI_AT_GENERAL_BONDING;
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
conn->key_type = 0xff;
+ if (hdev->io_capability == 0x03)
+ conn->auth_type = HCI_AT_GENERAL_BONDING;
+ else
+ conn->auth_type = HCI_AT_GENERAL_BONDING_MITM;
+
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
--
1.7.11.7
^ permalink raw reply related
* Re: [PATCH] Check uuid in disconnect profile.
From: Luiz Augusto von Dentz @ 2013-01-21 16:07 UTC (permalink / raw)
To: Alexandros Antonopoulos; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1358775868-9963-1-git-send-email-alexandros.antonopoulos@oss.bmw-carit.de>
Hi Alexandros,
On Mon, Jan 21, 2013 at 3:44 PM, Alexandros Antonopoulos
<alexandros.antonopoulos@oss.bmw-carit.de> wrote:
> If the user calls Device1.DisconnectProfile with an invalid profile
> uuid disconnect_profile still tries to parse the uuid resulting in
> a SIGSEGV
>
> ---
> src/device.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/src/device.c b/src/device.c
> index 3675616..1771c0f 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -1270,6 +1270,9 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
>
> uuid = bt_name2string(pattern);
>
> + if (uuid == NULL)
> + return btd_error_invalid_args(msg);
> +
> p = find_connectable_profile(dev, uuid);
> g_free(uuid);
>
> --
> 1.8.1
All four patches are now upstream, Ive changed the commit message for
this one to include the prefix and also please next time state it is a
fix and provide the backtrace so we know the severity of the problem.
btw, thanks for the patches.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH BlueZ 6/6] tools: Emit Seeked signal if Position changes
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358781718-386-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
MPRIS spec says PropertiesChanged is not emitted for Position, which is
probably to make clear that progress is done using the rate, so instead
Seeked should be emitted.
---
tools/mpris-player.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 448050d..b8de9a1 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -1646,6 +1646,8 @@ static void player_property_changed(GDBusProxy *proxy, const char *name,
{
struct player *player;
const char *property;
+ uint32_t position;
+ uint64_t value;
player = find_player(proxy);
if (player == NULL)
@@ -1658,6 +1660,18 @@ static void player_property_changed(GDBusProxy *proxy, const char *name,
g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
MPRIS_PLAYER_INTERFACE,
property);
+
+ if (strcasecmp(name, "Position") != 0)
+ return;
+
+ dbus_message_iter_get_basic(iter, &position);
+
+ value = position * 1000;
+
+ g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_PLAYER_INTERFACE, "Seeked",
+ DBUS_TYPE_INT64, &value,
+ DBUS_TYPE_INVALID);
}
static void transport_property_changed(GDBusProxy *proxy, const char *name,
--
1.8.0.2
^ permalink raw reply related
* [PATCH BlueZ 5/6] tools: Add volume support for mpris-player
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358781718-386-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This uses MediaTransport1 to track the Volume changes.
---
tools/mpris-player.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 178 insertions(+), 9 deletions(-)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 232e3c0..448050d 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -43,6 +43,7 @@
#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
#define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
@@ -55,6 +56,7 @@ static DBusConnection *sys = NULL;
static DBusConnection *session = NULL;
static GDBusClient *client = NULL;
static GSList *players = NULL;
+static GSList *transports = NULL;
struct player {
char *name;
@@ -62,6 +64,7 @@ struct player {
DBusConnection *conn;
GDBusProxy *proxy;
GDBusProxy *device;
+ GDBusProxy *transport;
};
typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
@@ -793,6 +796,9 @@ static void player_free(void *data)
g_dbus_proxy_unref(player->device);
g_dbus_proxy_unref(player->proxy);
+ if (player->transport)
+ g_dbus_proxy_unref(player->transport);
+
g_free(player->name);
g_free(player->bus_name);
g_free(player);
@@ -1277,6 +1283,30 @@ static gboolean get_enable(const GDBusPropertyTable *property,
}
+static gboolean get_volume(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ double value = 0.0;
+ uint16_t volume;
+ DBusMessageIter var;
+
+ if (player->transport == NULL)
+ goto done;
+
+ if (!g_dbus_proxy_get_property(player->transport, "Volume", &var))
+ goto done;
+
+ dbus_message_iter_get_basic(&var, &volume);
+
+ value = (double) volume / 127;
+
+done:
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+ return TRUE;
+}
+
static const GDBusMethodTable player_methods[] = {
{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
@@ -1301,7 +1331,7 @@ static const GDBusPropertyTable player_properties[] = {
{ "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
{ "Position", "x", get_position, NULL, position_exists },
{ "Metadata", "a{sv}", get_track, NULL, track_exists },
- { "Volume", "d", get_double, NULL, NULL },
+ { "Volume", "d", get_volume, NULL, NULL },
{ "CanGoNext", "b", get_enable, NULL, NULL },
{ "CanGoPrevious", "b", get_enable, NULL, NULL },
{ "CanPlay", "b", get_enable, NULL, NULL },
@@ -1353,12 +1383,33 @@ static char *mpris_busname(const char *name)
return g_strdelimit(bus_name, " ’", '_');
}
+static GDBusProxy *find_transport_by_path(const char *path)
+{
+ GSList *l;
+
+ for (l = transports; l; l = l->next) {
+ GDBusProxy *transport = l->data;
+ DBusMessageIter iter;
+ const char *value;
+
+ if (!g_dbus_proxy_get_property(transport, "Device", &iter))
+ continue;
+
+ dbus_message_iter_get_basic(&iter, &value);
+
+ if (strcmp(path, value) == 0)
+ return transport;
+ }
+
+ return NULL;
+}
+
static void register_player(GDBusProxy *proxy)
{
struct player *player;
DBusMessageIter iter;
const char *path, *name;
- GDBusProxy *device;
+ GDBusProxy *device, *transport;
if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
return;
@@ -1412,6 +1463,10 @@ static void register_player(GDBusProxy *proxy)
goto fail;
}
+ transport = find_transport_by_path(path);
+ if (transport)
+ player->transport = g_dbus_proxy_ref(transport);
+
return;
fail:
@@ -1419,6 +1474,47 @@ fail:
player_free(player);
}
+static struct player *find_player_by_device(const char *device)
+{
+ GSList *l;
+
+ for (l = players; l; l = l->next) {
+ struct player *player = l->data;
+ const char *path = g_dbus_proxy_get_path(player->device);
+
+ if (g_strcmp0(device, path) == 0)
+ return player;
+ }
+
+ return NULL;
+}
+
+static void register_transport(GDBusProxy *proxy)
+{
+ struct player *player;
+ DBusMessageIter iter;
+ const char *path;
+
+ if (g_slist_find(transports, proxy) != NULL)
+ return;
+
+ if (!g_dbus_proxy_get_property(proxy, "Volume", &iter))
+ return;
+
+ if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+ return;
+
+ dbus_message_iter_get_basic(&iter, &path);
+
+ transports = g_slist_append(transports, proxy);
+
+ player = find_player_by_device(path);
+ if (player == NULL || player->transport != NULL)
+ return;
+
+ player->transport = g_dbus_proxy_ref(proxy);
+}
+
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -1437,6 +1533,10 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
printf("Bluetooth Player %s found\n",
g_dbus_proxy_get_path(proxy));
register_player(proxy);
+ } else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
+ printf("Bluetooth Transport %s found\n",
+ g_dbus_proxy_get_path(proxy));
+ register_transport(proxy);
}
}
@@ -1462,6 +1562,37 @@ static struct player *find_player(GDBusProxy *proxy)
return NULL;
}
+static struct player *find_player_by_transport(GDBusProxy *proxy)
+{
+ GSList *l;
+
+ for (l = players; l; l = l->next) {
+ struct player *player = l->data;
+
+ if (player->transport == proxy)
+ return player;
+ }
+
+ return NULL;
+}
+
+static void unregister_transport(GDBusProxy *proxy)
+{
+ struct player *player;
+
+ if (g_slist_find(transports, proxy) == NULL)
+ return;
+
+ transports = g_slist_remove(transports, proxy);
+
+ player = find_player_by_transport(proxy);
+ if (player == NULL)
+ return;
+
+ g_dbus_proxy_unref(player->transport);
+ player->transport = NULL;
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -1487,6 +1618,10 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
printf("Bluetooth Player %s removed\n",
g_dbus_proxy_get_path(proxy));
unregister_player(player);
+ } else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
+ printf("Bluetooth Transport %s removed\n",
+ g_dbus_proxy_get_path(proxy));
+ unregister_transport(proxy);
}
}
@@ -1506,16 +1641,11 @@ static const char *property_to_mpris(const char *property)
return NULL;
}
-static void property_changed(GDBusProxy *proxy, const char *name,
+static void player_property_changed(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
struct player *player;
- const char *interface, *property;
-
- interface = g_dbus_proxy_get_interface(proxy);
-
- if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) != 0)
- return;
+ const char *property;
player = find_player(proxy);
if (player == NULL)
@@ -1530,6 +1660,45 @@ static void property_changed(GDBusProxy *proxy, const char *name,
property);
}
+static void transport_property_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct player *player;
+ DBusMessageIter var;
+ const char *path;
+
+ if (strcasecmp(name, "Volume") != 0)
+ return;
+
+ if (!g_dbus_proxy_get_property(proxy, "Device", &var))
+ return;
+
+ dbus_message_iter_get_basic(&var, &path);
+
+ player = find_player_by_device(path);
+ if (player == NULL)
+ return;
+
+ g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_PLAYER_INTERFACE,
+ name);
+}
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *interface;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0)
+ return player_property_changed(proxy, name, iter, user_data);
+
+ if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0)
+ return transport_property_changed(proxy, name, iter,
+ user_data);
+}
+
int main(int argc, char *argv[])
{
guint owner_watch, properties_watch;
--
1.8.0.2
^ permalink raw reply related
* [PATCH BlueZ 4/6] tools: Make mpris-player to export org.mpris.MediaPlayer2
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358781718-386-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for org.mpris.MediaPlayer2 interface whic is also
mandatory accourding to MPRIS spec.
---
tools/mpris-player.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 31da384..232e3c0 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -44,6 +44,7 @@
#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
+#define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
#define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
@@ -1275,6 +1276,7 @@ static gboolean get_enable(const GDBusPropertyTable *property,
return TRUE;
}
+
static const GDBusMethodTable player_methods[] = {
{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
@@ -1309,6 +1311,40 @@ static const GDBusPropertyTable player_properties[] = {
{ }
};
+static gboolean get_disable(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ dbus_bool_t value = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &player->name);
+
+ return TRUE;
+}
+
+static const GDBusMethodTable mpris_methods[] = {
+ { }
+};
+
+static const GDBusPropertyTable mpris_properties[] = {
+ { "CanQuit", "b", get_disable, NULL, NULL },
+ { "Fullscreen", "b", get_disable, NULL, NULL },
+ { "CanSetFullscreen", "b", get_disable, NULL, NULL },
+ { "CanRaise", "b", get_disable, NULL, NULL },
+ { "HasTrackList", "b", get_disable, NULL, NULL },
+ { "Identity", "s", get_name, NULL, NULL },
+ { }
+};
+
static char *mpris_busname(const char *name)
{
char *bus_name;
@@ -1355,6 +1391,17 @@ static void register_player(GDBusProxy *proxy)
}
if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_INTERFACE,
+ mpris_methods,
+ NULL,
+ mpris_properties,
+ player, NULL)) {
+ fprintf(stderr, "Could not register interface %s",
+ MPRIS_INTERFACE);
+ goto fail;
+ }
+
+ if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
MPRIS_PLAYER_INTERFACE,
player_methods,
player_signals,
--
1.8.0.2
^ permalink raw reply related
* [PATCH BlueZ 3/6] tools: Convert player's properties changed signals to MPRIS
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358781718-386-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thsi convert properties changed signals from org.bluez.MediaPlayer1 to
org.mpris.MediaPlayer2.Player interface.
---
tools/mpris-player.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 8e2d85f..31da384 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -1443,6 +1443,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
}
}
+static const char *property_to_mpris(const char *property)
+{
+ if (strcasecmp(property, "Repeat") == 0)
+ return "LoopStatus";
+ else if (strcasecmp(property, "Shuffle") == 0)
+ return "Shuffle";
+ else if (strcasecmp(property, "Status") == 0)
+ return "PlaybackStatus";
+ else if (strcasecmp(property, "Position") == 0)
+ return "Position";
+ else if (strcasecmp(property, "Track") == 0)
+ return "Metadata";
+
+ return NULL;
+}
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct player *player;
+ const char *interface, *property;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) != 0)
+ return;
+
+ player = find_player(proxy);
+ if (player == NULL)
+ return;
+
+ property = property_to_mpris(name);
+ if (property == NULL)
+ return;
+
+ g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_PLAYER_INTERFACE,
+ property);
+}
+
int main(int argc, char *argv[])
{
guint owner_watch, properties_watch;
@@ -1498,7 +1538,7 @@ int main(int argc, char *argv[])
g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
- NULL, NULL);
+ property_changed, NULL);
g_main_loop_run(main_loop);
--
1.8.0.2
^ permalink raw reply related
* [PATCH BlueZ 2/6] tools: Add support for setting Shuffle and LoopStatus to mpris-player
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1358781718-386-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This add write support for Shuffle and LoopStatus properties of
org.mpris.MediaPlayer2.Player interface.
---
tools/mpris-player.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 1871ae9..8e2d85f 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -992,6 +992,58 @@ static gboolean get_repeat(const GDBusPropertyTable *property,
return TRUE;
}
+static const char *loopstatus_to_repeat(const char *value)
+{
+ if (strcasecmp(value, "None") == 0)
+ return "off";
+ else if (strcasecmp(value, "Track") == 0)
+ return "singletrack";
+ else if (strcasecmp(value, "Playlist") == 0)
+ return "alltracks";
+
+ return NULL;
+}
+
+static void property_result(const DBusError *err, void *user_data)
+{
+ GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data);
+
+ if (!dbus_error_is_set(err))
+ return g_dbus_pending_property_success(id);
+
+ g_dbus_pending_property_error(id, err->name, err->message);
+}
+
+static void set_repeat(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, GDBusPendingPropertySet id,
+ void *data)
+{
+ struct player *player = data;
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &value);
+
+ value = loopstatus_to_repeat(value);
+ if (value == NULL) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ g_dbus_proxy_set_property_basic(player->proxy, "Repeat",
+ DBUS_TYPE_STRING, value,
+ property_result, GUINT_TO_POINTER(id),
+ NULL);
+}
+
static gboolean get_double(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -1030,6 +1082,30 @@ static gboolean get_shuffle(const GDBusPropertyTable *property,
return TRUE;
}
+static void set_shuffle(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, GDBusPendingPropertySet id,
+ void *data)
+{
+ struct player *player = data;
+ dbus_bool_t shuffle;
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &shuffle);
+ value = shuffle ? "alltracks" : "off";
+
+ g_dbus_proxy_set_property_basic(player->proxy, "Shuffle",
+ DBUS_TYPE_STRING, value,
+ property_result, GUINT_TO_POINTER(id),
+ NULL);
+}
+
static gboolean position_exists(const GDBusPropertyTable *property, void *data)
{
DBusMessageIter iter;
@@ -1216,11 +1292,11 @@ static const GDBusSignalTable player_signals[] = {
static const GDBusPropertyTable player_properties[] = {
{ "PlaybackStatus", "s", get_status, NULL, status_exists },
- { "LoopStatus", "s", get_repeat, NULL, repeat_exists },
+ { "LoopStatus", "s", get_repeat, set_repeat, repeat_exists },
{ "Rate", "d", get_double, NULL, NULL },
{ "MinimumRate", "d", get_double, NULL, NULL },
{ "MaximumRate", "d", get_double, NULL, NULL },
- { "Shuffle", "b", get_shuffle, NULL, shuffle_exists },
+ { "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
{ "Position", "x", get_position, NULL, position_exists },
{ "Metadata", "a{sv}", get_track, NULL, track_exists },
{ "Volume", "d", get_double, NULL, NULL },
--
1.8.0.2
^ permalink raw reply related
* [PATCH BlueZ 1/6] tools: Make mpris-player to export players using MPRIS interface
From: Luiz Augusto von Dentz @ 2013-01-21 15:21 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Players found are exported in private connections using
org.mpris.MediaPlayer2.<device name> on the session bus.
---
tools/mpris-player.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 618 insertions(+), 21 deletions(-)
diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 246791a..1871ae9 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -32,6 +32,7 @@
#include <signal.h>
#include <getopt.h>
#include <string.h>
+#include <inttypes.h>
#include <dbus/dbus.h>
#include <glib.h>
@@ -41,13 +42,29 @@
#define BLUEZ_PATH "/org/bluez"
#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
+#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
#define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
+#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
static GMainLoop *main_loop;
static GDBusProxy *adapter = NULL;
static DBusConnection *sys = NULL;
static DBusConnection *session = NULL;
+static GDBusClient *client = NULL;
+static GSList *players = NULL;
+
+struct player {
+ char *name;
+ char *bus_name;
+ DBusConnection *conn;
+ GDBusProxy *proxy;
+ GDBusProxy *device;
+};
+
+typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
+ DBusMessageIter *metadata);
static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
void *val);
@@ -190,7 +207,8 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
return 0;
}
-static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
+static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata,
+ parse_metadata_func func)
{
DBusMessageIter dict;
int ctype;
@@ -216,7 +234,7 @@ static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
- if (parse_metadata_entry(&entry, key, metadata) < 0)
+ if (func(&entry, key, metadata) < 0)
return -EINVAL;
dbus_message_iter_next(&dict);
@@ -225,7 +243,8 @@ static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
return 0;
}
-static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict)
+static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict,
+ parse_metadata_func func)
{
DBusMessageIter value, metadata;
@@ -237,7 +256,7 @@ static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict)
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
- parse_metadata(dict, &metadata);
+ parse_metadata(dict, &metadata, func);
dbus_message_iter_close_container(&value, &metadata);
dbus_message_iter_close_container(iter, &value);
@@ -260,7 +279,7 @@ static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
if (strcasecmp(key, "Metadata") == 0)
- append_metadata(&entry, val);
+ append_metadata(&entry, val, parse_metadata_entry);
else
append_variant(&entry, type, val);
@@ -509,21 +528,29 @@ done:
static void remove_player(DBusConnection *conn, const char *sender)
{
DBusMessage *msg;
- char *path;
+ char *path, *owner;
if (!adapter)
return;
+ path = sender2path(sender);
+ dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+ if (owner == NULL) {
+ g_free(path);
+ return;
+ }
+
msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
g_dbus_proxy_get_path(adapter),
BLUEZ_MEDIA_INTERFACE,
"UnregisterPlayer");
if (!msg) {
fprintf(stderr, "Can't allocate new method call\n");
+ g_free(path);
return;
}
- path = sender2path(sender);
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
@@ -540,10 +567,16 @@ static gboolean properties_changed(DBusConnection *conn,
{
DBusMessageIter iter;
const char *iface;
- char *path;
+ char *path, *owner;
dbus_message_iter_init(msg, &iter);
+ path = sender2path(dbus_message_get_sender(msg));
+ dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+ if (owner == NULL)
+ goto done;
+
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -553,14 +586,28 @@ static gboolean properties_changed(DBusConnection *conn,
dbus_message_iter_next(&iter);
- path = sender2path(dbus_message_get_sender(msg));
parse_properties(conn, path, &iter, NULL);
+done:
g_free(path);
return TRUE;
}
+static struct player *find_player_by_bus_name(const char *name)
+{
+ GSList *l;
+
+ for (l = players; l; l = l->next) {
+ struct player *player = l->data;
+
+ if (strcmp(player->bus_name, name) == 0)
+ return player;
+ }
+
+ return NULL;
+}
+
static gboolean name_owner_changed(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -581,7 +628,7 @@ static gboolean name_owner_changed(DBusConnection *conn,
if (*new == '\0') {
printf("player %s at %s disappear\n", name, old);
remove_player(conn, old);
- } else {
+ } else if (find_player_by_bus_name(name) == NULL) {
printf("player %s at %s found\n", name, new);
add_player(conn, name, new);
}
@@ -733,23 +780,565 @@ static void disconnect_handler(DBusConnection *connection, void *user_data)
printf("org.bluez disappeared\n");
}
-static void proxy_added(GDBusProxy *proxy, void *user_data)
+static void player_free(void *data)
{
- const char *interface;
+ struct player *player = data;
+
+ if (player->conn) {
+ dbus_connection_close(player->conn);
+ dbus_connection_unref(player->conn);
+ }
+
+ g_dbus_proxy_unref(player->device);
+ g_dbus_proxy_unref(player->proxy);
+
+ g_free(player->name);
+ g_free(player->bus_name);
+ g_free(player);
+}
+
+struct pending_call {
+ struct player *player;
+ DBusMessage *msg;
+};
+
+static void pending_call_free(void *data)
+{
+ struct pending_call *p = data;
+
+ if (p->msg)
+ dbus_message_unref(p->msg);
+
+ g_free(p);
+}
+
+static void player_reply(DBusMessage *message, void *user_data)
+{
+ struct pending_call *p = user_data;
+ struct player *player = p->player;
+ DBusMessage *msg = p->msg;
+ DBusMessage *reply;
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ fprintf(stderr, "error: %s", err.name);
+ reply = g_dbus_create_error(msg, err.name, err.message);
+ dbus_error_free(&err);
+ } else
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ g_dbus_send_message(player->conn, reply);
+}
+
+static void player_control(struct player *player, DBusMessage *msg,
+ const char *name)
+{
+ struct pending_call *p;
+
+ p = g_new0(struct pending_call, 1);
+ p->player = player;
+ p->msg = dbus_message_ref(msg);
+
+ g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply,
+ p, pending_call_free);
+}
+
+static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+ DBusMessageIter value;
+ const char *status;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Status", &value))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&value, &status);
+
+ if (strcasecmp(status, "Playing") == 0)
+ player_control(player, msg, "Pause");
+ else
+ player_control(player, msg, "Play");
+
+ return NULL;
+}
+
+static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+
+ player_control(player, msg, "Play");
+
+ return NULL;
+}
+
+static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+
+ player_control(player, msg, "Pause");
+
+ return NULL;
+}
+
+static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+
+ player_control(player, msg, "Stop");
+
+ return NULL;
+}
+
+static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+
+ player_control(player, msg, "Next");
+
+ return NULL;
+}
+
+static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct player *player = data;
+
+ player_control(player, msg, "Previous");
+
+ return NULL;
+}
+
+static gboolean status_exists(const GDBusPropertyTable *property, void *data)
+{
+ DBusMessageIter iter;
+ struct player *player = data;
+
+ return g_dbus_proxy_get_property(player->proxy, "Status", &iter);
+}
+
+static const char *status_to_playback(const char *status)
+{
+ if (strcasecmp(status, "playing") == 0)
+ return "Playing";
+ else if (strcasecmp(status, "paused") == 0)
+ return "Paused";
+ else
+ return "Stopped";
+}
+
+static gboolean get_status(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ DBusMessageIter value;
+ const char *status;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Status", &value))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&value, &status);
+
+ status = status_to_playback(status);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+ return TRUE;
+}
+
+static gboolean repeat_exists(const GDBusPropertyTable *property, void *data)
+{
+ DBusMessageIter iter;
+ struct player *player = data;
+
+ return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter);
+}
+
+static const char *repeat_to_loopstatus(const char *value)
+{
+ if (strcasecmp(value, "off") == 0)
+ return "None";
+ else if (strcasecmp(value, "singletrack") == 0)
+ return "Track";
+ else if (strcasecmp(value, "alltracks") == 0)
+ return "Playlist";
+
+ return NULL;
+}
+
+static gboolean get_repeat(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ DBusMessageIter value;
+ const char *status;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&value, &status);
+
+ status = repeat_to_loopstatus(status);
+ if (status == NULL)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+ return TRUE;
+}
+
+static gboolean get_double(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ double value = 1.0;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+ return TRUE;
+}
+
+static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data)
+{
+ DBusMessageIter iter;
+ struct player *player = data;
+
+ return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter);
+}
+
+static gboolean get_shuffle(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ DBusMessageIter value;
+ const char *string;
+ dbus_bool_t shuffle;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&value, &string);
+
+ shuffle = strcmp(string, "off") != 0;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle);
+
+ return TRUE;
+}
+
+static gboolean position_exists(const GDBusPropertyTable *property, void *data)
+{
+ DBusMessageIter iter;
+ struct player *player = data;
+
+ return g_dbus_proxy_get_property(player->proxy, "Position", &iter);
+}
+
+static gboolean get_position(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ DBusMessageIter var;
+ uint32_t position;
+ int64_t value;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Position", &var))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&var, &position);
+
+ value = position * 1000;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value);
+
+ return TRUE;
+}
+
+static gboolean track_exists(const GDBusPropertyTable *property, void *data)
+{
+ DBusMessageIter iter;
+ struct player *player = data;
+
+ return g_dbus_proxy_get_property(player->proxy, "Track", &iter);
+}
+
+static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key,
+ DBusMessageIter *metadata)
+{
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+
+ dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value);
+
+ return TRUE;
+}
+
+static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key,
+ DBusMessageIter *metadata)
+{
+ char **value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return FALSE;
+
+ value = dbus_malloc0(sizeof(char *));
+
+ dbus_message_iter_get_basic(iter, &(value[0]));
+
+ dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1);
+
+ dbus_free(value);
+
+ return TRUE;
+}
+
+static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key,
+ DBusMessageIter *metadata)
+{
+ uint32_t duration;
+ int64_t value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &duration);
+
+ value = duration * 1000;
+
+ dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value);
+
+ return TRUE;
+}
+
+static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key,
+ DBusMessageIter *metadata)
+{
+ uint32_t value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+
+ dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value);
+
+ return TRUE;
+}
+
+static int parse_track_entry(DBusMessageIter *entry, const char *key,
+ DBusMessageIter *metadata)
+{
+ DBusMessageIter var;
+
+ printf("metadata %s found\n", key);
+
+ if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(entry, &var);
+
+ if (strcasecmp(key, "Title") == 0) {
+ if (!parse_string_metadata(&var, "xesam:title", metadata))
+ return -EINVAL;
+ } else if (strcasecmp(key, "Artist") == 0) {
+ if (!parse_array_metadata(&var, "xesam:artist", metadata))
+ return -EINVAL;
+ } else if (strcasecmp(key, "Album") == 0) {
+ if (!parse_string_metadata(&var, "xesam:album", metadata))
+ return -EINVAL;
+ } else if (strcasecmp(key, "Genre") == 0) {
+ if (!parse_array_metadata(&var, "xesam:genre", metadata))
+ return -EINVAL;
+ } else if (strcasecmp(key, "Duration") == 0) {
+ if (!parse_int64_metadata(&var, "mpris:length", metadata))
+ return -EINVAL;
+ } else if (strcasecmp(key, "TrackNumber") == 0) {
+ if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static gboolean get_track(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct player *player = data;
+ DBusMessageIter var, metadata;
+
+ if (!g_dbus_proxy_get_property(player->proxy, "Track", &var))
+ return FALSE;
+
+ 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, &metadata);
+
+ parse_metadata(&var, &metadata, parse_track_entry);
+
+ dbus_message_iter_close_container(iter, &metadata);
+
+ return TRUE;
+}
+
+static gboolean get_enable(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ dbus_bool_t value = TRUE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+ return TRUE;
+}
+
+static const GDBusMethodTable player_methods[] = {
+ { GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
+ { GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
+ { GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) },
+ { GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) },
+ { GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) },
+ { GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) },
+ { }
+};
+
+static const GDBusSignalTable player_signals[] = {
+ { GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) },
+ { }
+};
+
+static const GDBusPropertyTable player_properties[] = {
+ { "PlaybackStatus", "s", get_status, NULL, status_exists },
+ { "LoopStatus", "s", get_repeat, NULL, repeat_exists },
+ { "Rate", "d", get_double, NULL, NULL },
+ { "MinimumRate", "d", get_double, NULL, NULL },
+ { "MaximumRate", "d", get_double, NULL, NULL },
+ { "Shuffle", "b", get_shuffle, NULL, shuffle_exists },
+ { "Position", "x", get_position, NULL, position_exists },
+ { "Metadata", "a{sv}", get_track, NULL, track_exists },
+ { "Volume", "d", get_double, NULL, NULL },
+ { "CanGoNext", "b", get_enable, NULL, NULL },
+ { "CanGoPrevious", "b", get_enable, NULL, NULL },
+ { "CanPlay", "b", get_enable, NULL, NULL },
+ { "CanPause", "b", get_enable, NULL, NULL },
+ { "CanSeek", "b", get_enable, NULL, NULL },
+ { "CanControl", "b", get_enable, NULL, NULL },
+ { }
+};
+
+static char *mpris_busname(const char *name)
+{
+ char *bus_name;
+
+ bus_name = g_strconcat(MPRIS_BUS_NAME, name, NULL);
+ return g_strdelimit(bus_name, " ’", '_');
+}
+
+static void register_player(GDBusProxy *proxy)
+{
+ struct player *player;
+ DBusMessageIter iter;
+ const char *path, *name;
+ GDBusProxy *device;
+
+ if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+ return;
+
+ dbus_message_iter_get_basic(&iter, &path);
- if (adapter != NULL)
+ device = g_dbus_proxy_new(client, path, "org.bluez.Device1");
+ if (device == NULL)
return;
+ if (!g_dbus_proxy_get_property(device, "Name", &iter))
+ return;
+
+ dbus_message_iter_get_basic(&iter, &name);
+
+ player = g_new0(struct player, 1);
+ player->name = g_strdup(name);
+ player->bus_name = mpris_busname(name);
+ player->proxy = g_dbus_proxy_ref(proxy);
+ player->device = device;
+
+ players = g_slist_prepend(players, player);
+
+ player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name,
+ NULL);
+ if (!session) {
+ fprintf(stderr, "Could not register bus name %s",
+ player->bus_name);
+ goto fail;
+ }
+
+ if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_PLAYER_INTERFACE,
+ player_methods,
+ player_signals,
+ player_properties,
+ player, player_free)) {
+ fprintf(stderr, "Could not register interface %s",
+ MPRIS_PLAYER_INTERFACE);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ players = g_slist_remove(players, player);
+ player_free(player);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+ const char *interface;
+
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) {
+ if (adapter != NULL)
+ return;
+
printf("Bluetooth Adapter %s found\n",
g_dbus_proxy_get_path(proxy));
adapter = proxy;
list_names(session);
+ } else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
+ printf("Bluetooth Player %s found\n",
+ g_dbus_proxy_get_path(proxy));
+ register_player(proxy);
}
}
+static void unregister_player(struct player *player)
+{
+ players = g_slist_remove(players, player);
+
+ g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+ MPRIS_PLAYER_INTERFACE);
+}
+
+static struct player *find_player(GDBusProxy *proxy)
+{
+ GSList *l;
+
+ for (l = players; l; l = l->next) {
+ struct player *player = l->data;
+
+ if (player->proxy == proxy)
+ return player;
+ }
+
+ return NULL;
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -759,19 +1348,27 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
interface = g_dbus_proxy_get_interface(proxy);
- if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE))
- return;
+ if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) {
+ if (adapter != proxy)
+ return;
+ printf("Bluetooth Adapter %s removed\n",
+ g_dbus_proxy_get_path(proxy));
+ adapter = NULL;
+ } else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) {
+ struct player *player;
- if (adapter != proxy)
- return;
+ player = find_player(proxy);
+ if (player == NULL)
+ return;
- printf("Bluetooth Adapter %s removed\n", g_dbus_proxy_get_path(proxy));
- adapter = NULL;
+ printf("Bluetooth Player %s removed\n",
+ g_dbus_proxy_get_path(proxy));
+ unregister_player(player);
+ }
}
int main(int argc, char *argv[])
{
- GDBusClient *client;
guint owner_watch, properties_watch;
struct sigaction sa;
int opt;
@@ -825,7 +1422,7 @@ int main(int argc, char *argv[])
g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
- NULL, NULL);
+ NULL, NULL);
g_main_loop_run(main_loop);
--
1.8.0.2
^ permalink raw reply related
* [PATCH] monitor: Fix possible crash on unknown command
From: Szymon Janc @ 2013-01-21 15:17 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
If uknown command is received monitor would crash due to dereference
of NULL opcode_data pointer.
---
monitor/packet.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/monitor/packet.c b/monitor/packet.c
index 19139f8..58a5a97 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -4266,7 +4266,7 @@ static void cmd_complete_evt(const void *data, uint8_t size)
print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
" (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd);
- if (!opcode_data->rsp_func) {
+ if (!opcode_data || !opcode_data->rsp_func) {
packet_hexdump(data + 3, size - 3);
return;
}
--
1.8.0.3
^ permalink raw reply related
* Re: [PATCH BlueZ 5/9 v3] audio: Fix handling of A2DP abort indication
From: Chan-yeol Park @ 2013-01-21 14:53 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth, SYAM SIDHARDHAN
In-Reply-To: <1339771301-24032-5-git-send-email-luiz.dentz@gmail.com>
Hi Luiz.
Regarding your patch that was applied already, I think
finalize_setup_errno() function missed NULL argument.
This is reported by Syam Sidhardhan <s.syam@samsung.com>
On 06/15/2012 11:41 PM, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> When an abort is received all setup callbacks should return an error.
> ---
> audio/a2dp.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/audio/a2dp.c b/audio/a2dp.c
> index 5139f61..d9dcead 100644
> --- a/audio/a2dp.c
> +++ b/audio/a2dp.c
> @@ -1182,6 +1182,7 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
> void *user_data)
> {
> struct a2dp_sep *a2dp_sep = user_data;
> + struct a2dp_setup *setup;
>
> if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
> DBG("Sink %p: Abort_Ind", sep);
> @@ -1190,6 +1191,14 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
>
> a2dp_sep->stream = NULL;
>
> + setup = find_setup_by_session(session);
> + if (!setup)
> + return TRUE;
> +
> + finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
> + finalize_resume,
> + finalize_config);
> +
Here.
Could you give us your opinion?
Regards
Chanyeol
^ permalink raw reply
* [PATCH v4 4/4] Bluetooth: Fix stop discovery while in STARTING state
From: Jaganath Kanakkassery @ 2013-01-21 14:13 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Jaganath Kanakkassery
In-Reply-To: <1358777619-27018-1-git-send-email-jaganath.k@samsung.com>
If stop_discovery() is called when discovery state is STARTING, it
will be failed currently. This patch fixes this.
Signed-off-by: Jaganath Kanakkassery <jaganath.k@samsung.com>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_event.c | 14 ++++++++++++--
net/bluetooth/mgmt.c | 12 +++++++++++-
3 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index f20da05..0a7aa1f 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -64,6 +64,7 @@ struct discovery_state {
DISCOVERY_RESOLVING,
DISCOVERY_STOPPING,
} state;
+ bool discovering;
struct list_head all; /* All devices found during inquiry */
struct list_head unknown; /* Name state not known */
struct list_head resolve; /* Name needs to be resolved */
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 97b4828..c616cbf 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1259,7 +1259,12 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ if (hdev->discovery.state == DISCOVERY_STOPPING) {
+ hci_cancel_le_scan(hdev);
+ mgmt_start_discovery_cancelled(hdev);
+ } else {
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ }
hci_dev_unlock(hdev);
break;
@@ -1375,7 +1380,12 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
set_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ if (hdev->discovery.state == DISCOVERY_STOPPING) {
+ hci_cancel_inquiry(hdev);
+ mgmt_start_discovery_cancelled(hdev);
+ } else {
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ }
hci_dev_unlock(hdev);
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ba5ca81..d9b1aa1 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2443,7 +2443,8 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
- if (!hci_discovery_active(hdev)) {
+ if (hdev->discovery.state != DISCOVERY_STARTING &&
+ !hci_discovery_active(hdev)) {
err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
MGMT_STATUS_REJECTED, &mgmt_cp->type,
sizeof(mgmt_cp->type));
@@ -2491,6 +2492,10 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
break;
+ case DISCOVERY_STARTING:
+ err = 0;
+ break;
+
default:
BT_DBG("unknown discovery state %u", hdev->discovery.state);
err = -EFAULT;
@@ -3777,6 +3782,11 @@ int mgmt_discovering(struct hci_dev *hdev, bool discovering)
mgmt_pending_remove(cmd);
}
+ if (hdev->discovery.discovering == discovering)
+ return 0;
+
+ hdev->discovery.discovering = discovering;
+
memset(&ev, 0, sizeof(ev));
ev.type = hdev->discovery.type;
ev.discovering = discovering;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 3/4] Bluetooth: Change type of "discovering" from u8 to bool
From: Jaganath Kanakkassery @ 2013-01-21 14:13 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Jaganath Kanakkassery
In-Reply-To: <1358777619-27018-1-git-send-email-jaganath.k@samsung.com>
Since the only possible values of discovering is 0 and 1, bool is
more appropriate
Signed-off-by: Jaganath Kanakkassery <jaganath.k@samsung.com>
---
include/net/bluetooth/hci_core.h | 2 +-
net/bluetooth/hci_core.c | 4 ++--
net/bluetooth/mgmt.c | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d8f68c7..f20da05 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1114,7 +1114,7 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_start_discovery_cancelled(struct hci_dev *hdev);
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
+int mgmt_discovering(struct hci_dev *hdev, bool discovering);
int mgmt_interleaved_discovery(struct hci_dev *hdev);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 596660d..ce6a696 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -326,12 +326,12 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
switch (state) {
case DISCOVERY_STOPPED:
if (hdev->discovery.state != DISCOVERY_STARTING)
- mgmt_discovering(hdev, 0);
+ mgmt_discovering(hdev, false);
break;
case DISCOVERY_STARTING:
break;
case DISCOVERY_FINDING:
- mgmt_discovering(hdev, 1);
+ mgmt_discovering(hdev, true);
break;
case DISCOVERY_RESOLVING:
break;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 3527095..ba5ca81 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3757,7 +3757,7 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
return err;
}
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+int mgmt_discovering(struct hci_dev *hdev, bool discovering)
{
struct mgmt_ev_discovering ev;
struct pending_cmd *cmd;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 2/4] Bluetooth: Add mgmt_start_discovery_cancelled()
From: Jaganath Kanakkassery @ 2013-01-21 14:13 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Jaganath Kanakkassery
In-Reply-To: <1358777619-27018-1-git-send-email-jaganath.k@samsung.com>
This function can be used to inform userspace that start discovery
is cancelled
Signed-off-by: Jaganath Kanakkassery <jaganath.k@samsung.com>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/mgmt.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 014a2ea..d8f68c7 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1112,6 +1112,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
+int mgmt_start_discovery_cancelled(struct hci_dev *hdev);
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_interleaved_discovery(struct hci_dev *hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a7865ad..3527095 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3722,6 +3722,25 @@ int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
return err;
}
+int mgmt_start_discovery_cancelled(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+ u8 type;
+ int err;
+
+ cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+ if (!cmd)
+ return -ENOENT;
+
+ type = hdev->discovery.type;
+
+ err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, MGMT_STATUS_CANCELLED,
+ &type, sizeof(type));
+ mgmt_pending_remove(cmd);
+
+ return err;
+}
+
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
--
1.7.9.5
^ permalink raw reply related
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