2012-04-27 10:59:42

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 0/5] Multiple Bluetooth SCO connections (userspace)

This second version integrates the review from Luiz and drops patches v0 1 and 7. The first one is not needed due to recent Kernel changes (it's now possible to bind several SCO sockets to same address), and patch 7 needs further research (but is in practice needed for testing).

After these changes, it's possible to have two simultaneous SCO links. The question whether this should be enabled by default or not has not been addressed here.

>From previous cover letter:

This patch series includes patches that have been useful to connect two HCI-based SCO links simultaneously. This can be used for example to connect to HSP headsets at the same time.

The patch series is divided in three groups: kernel patches, BlueZ userspace patches and PulseAudio patches for module-bluetooth-device.

The kernel patches include some code cleanup and more importantly a dynamically changing alternate setting in btusb driver. These ideas have been taken from the patches I found in [1]. The last patch, “Bluetooth: Remove outgoing MTU check” should be considered with care, since there probably are better approaches to solve this (WIP).

The BlueZ userspace patches add some necessary infrastructure to support such use-cases.

The PulseAudio patches provide some changes to be able to test the rest of the code. The first three patches have been reused from a previously submitted patch series, and only the last two patches are relevant for this purpose. They provide some simple workarounds and should not be considered a proper solution.

As I said, the easiest may to test these patches is by using two Bluetooth headsets. You should use the Media API (Enable=Media in audio.conf), connect both headsets, and use pacmd to set their profile to hsp.

[1] http://bluetooth-alsa.sourceforge.net/future.html

Mikel Astiz (5):
audio: Fix gateway state check
audio: Add multiple device search to manager
media: Support multiple transports per endpoint
media: Create multiple transports if needed
media: Enable parallel requests to endpoint

audio/device.c | 2 +-
audio/manager.c | 25 +++++++-
audio/manager.h | 6 ++
audio/media.c | 164 +++++++++++++++++++++++++++++++++++-------------------
4 files changed, 135 insertions(+), 62 deletions(-)

--
1.7.7.6



2012-04-27 11:45:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v1 4/5] media: Create multiple transports if needed

Hi Mikel,

On Fri, Apr 27, 2012 at 1:59 PM, Mikel Astiz <[email protected]> wrote:

> -static struct media_transport *get_unique_transport(
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct media_endpoint *endpoint)
> -{
> - ? ? ? if (endpoint->transports == NULL)
> - ? ? ? ? ? ? ? return NULL;
> -
> - ? ? ? if (endpoint->transports->next != NULL)
> - ? ? ? ? ? ? ? return NULL;
> -
> - ? ? ? return endpoint->transports->data;
> -}

This shouldn't be here in the first place.

> ? ? ? ?} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?strcasecmp(uuid, HSP_AG_UUID) == 0) {
> - ? ? ? ? ? ? ? struct audio_device *dev;
> + ? ? ? ? ? ? ? GSList *list;
> + ? ? ? ? ? ? ? GSList *l;
>
> ? ? ? ? ? ? ? ?endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?endpoint);
> - ? ? ? ? ? ? ? dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
> + ? ? ? ? ? ? ? list = manager_find_devices(NULL, &adapter->src, BDADDR_ANY,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AUDIO_HEADSET_INTERFACE, TRUE);
> - ? ? ? ? ? ? ? if (dev)
> +
> + ? ? ? ? ? ? ? for (l = list; l != NULL; l = l->next) {
> + ? ? ? ? ? ? ? ? ? ? ? struct audio_device *dev = l->data;
> +
> ? ? ? ? ? ? ? ? ? ? ? ?set_configuration(endpoint, dev, NULL, 0,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?headset_setconf_cb, dev, NULL);
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? g_slist_free(list);
> ? ? ? ?} else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?strcasecmp(uuid, HSP_HS_UUID) == 0) {
> - ? ? ? ? ? ? ? struct audio_device *dev;
> + ? ? ? ? ? ? ? GSList *list;
> + ? ? ? ? ? ? ? GSList *l;
>
> ? ? ? ? ? ? ? ?endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?endpoint);
> - ? ? ? ? ? ? ? dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
> + ? ? ? ? ? ? ? list = manager_find_devices(NULL, &adapter->src, BDADDR_ANY,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AUDIO_GATEWAY_INTERFACE, TRUE);
> - ? ? ? ? ? ? ? if (dev)
> +
> + ? ? ? ? ? ? ? for (l = list; l != NULL; l = l->next) {
> + ? ? ? ? ? ? ? ? ? ? ? struct audio_device *dev = l->data;
> +
> ? ? ? ? ? ? ? ? ? ? ? ?set_configuration(endpoint, dev, NULL, 0,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?gateway_setconf_cb, dev, NULL);
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? g_slist_free(list);
> ? ? ? ?} else {
> ? ? ? ? ? ? ? ?if (err)
> ? ? ? ? ? ? ? ? ? ? ? ?*err = -EINVAL;

This function is becoming too big/complex, please split it per
uuid/role whenever possible.


--
Luiz Augusto von Dentz

2012-04-27 10:59:46

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 4/5] media: Create multiple transports if needed

From: Mikel Astiz <[email protected]>

During endpoint registration one than one device might be connected.
Thus all matching devices should have one transport each.
---
audio/media.c | 43 ++++++++++++++++++++++---------------------
1 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/audio/media.c b/audio/media.c
index 23c3217..5cbf4a2 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -589,27 +589,14 @@ static void clear_config(struct a2dp_sep *sep, void *user_data)
clear_endpoint(endpoint);
}

-static struct media_transport *get_unique_transport(
- struct media_endpoint *endpoint)
-{
- if (endpoint->transports == NULL)
- return NULL;
-
- if (endpoint->transports->next != NULL)
- return NULL;
-
- return endpoint->transports->data;
-}
-
static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
{
struct media_endpoint *endpoint = user_data;
- struct media_transport *transport = get_unique_transport(endpoint);

- if (transport == NULL)
+ if (endpoint->transports == NULL)
return;

- media_transport_update_delay(transport, delay);
+ media_transport_update_delay(endpoint->transports->data, delay);
}

static struct a2dp_endpoint a2dp_endpoint = {
@@ -713,26 +700,40 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
goto failed;
} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
strcasecmp(uuid, HSP_AG_UUID) == 0) {
- struct audio_device *dev;
+ GSList *list;
+ GSList *l;

endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
endpoint);
- dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
+ list = manager_find_devices(NULL, &adapter->src, BDADDR_ANY,
AUDIO_HEADSET_INTERFACE, TRUE);
- if (dev)
+
+ for (l = list; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+
set_configuration(endpoint, dev, NULL, 0,
headset_setconf_cb, dev, NULL);
+ }
+
+ g_slist_free(list);
} else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
strcasecmp(uuid, HSP_HS_UUID) == 0) {
- struct audio_device *dev;
+ GSList *list;
+ GSList *l;

endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
endpoint);
- dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
+ list = manager_find_devices(NULL, &adapter->src, BDADDR_ANY,
AUDIO_GATEWAY_INTERFACE, TRUE);
- if (dev)
+
+ for (l = list; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+
set_configuration(endpoint, dev, NULL, 0,
gateway_setconf_cb, dev, NULL);
+ }
+
+ g_slist_free(list);
} else {
if (err)
*err = -EINVAL;
--
1.7.7.6


2012-04-27 10:59:45

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 3/5] media: Support multiple transports per endpoint

From: Mikel Astiz <[email protected]>

Several transports may exist for each endpoint, for example if several
HFGW are connected. This should be exposed to the endpoint as one
transport each.
---
audio/media.c | 111 ++++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/audio/media.c b/audio/media.c
index c0fd0c3..23c3217 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -86,8 +86,8 @@ struct media_endpoint {
guint ag_watch;
guint watch;
struct endpoint_request *request;
- struct media_transport *transport;
struct media_adapter *adapter;
+ GSList *transports;
};

struct media_player {
@@ -153,8 +153,8 @@ static void media_endpoint_destroy(struct media_endpoint *endpoint)
if (endpoint->request)
media_endpoint_cancel(endpoint);

- if (endpoint->transport)
- media_transport_destroy(endpoint->transport);
+ g_slist_free_full(endpoint->transports,
+ (GDestroyNotify) media_transport_destroy);

g_dbus_remove_watch(adapter->conn, endpoint->watch);
g_free(endpoint->capabilities);
@@ -200,18 +200,12 @@ static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret,
headset_shutdown(dev);
}

-static void clear_configuration(struct media_endpoint *endpoint)
+static void clear_configuration(struct media_endpoint *endpoint,
+ struct media_transport *transport)
{
DBusConnection *conn;
DBusMessage *msg;
const char *path;
- struct media_transport *transport = endpoint->transport;
-
- if (endpoint->transport == NULL)
- return;
-
- if (endpoint->request)
- media_endpoint_cancel(endpoint);

conn = endpoint->adapter->conn;

@@ -223,15 +217,24 @@ static void clear_configuration(struct media_endpoint *endpoint)
goto done;
}

- path = media_transport_get_path(endpoint->transport);
+ path = media_transport_get_path(transport);
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
g_dbus_send_message(conn, msg);
done:
- endpoint->transport = NULL;
+ endpoint->transports = g_slist_remove(endpoint->transports, transport);
media_transport_destroy(transport);
}

+static void clear_endpoint(struct media_endpoint *endpoint)
+{
+ if (endpoint->request)
+ media_endpoint_cancel(endpoint);
+
+ while (endpoint->transports != NULL)
+ clear_configuration(endpoint, endpoint->transports->data);
+}
+
static void endpoint_reply(DBusPendingCall *call, void *user_data)
{
struct media_endpoint *endpoint = user_data;
@@ -256,7 +259,7 @@ static void endpoint_reply(DBusPendingCall *call, void *user_data)
if (request->cb)
request->cb(endpoint, NULL, size,
request->user_data);
- clear_configuration(endpoint);
+ clear_endpoint(endpoint);
dbus_message_unref(reply);
dbus_error_free(&err);
return;
@@ -369,6 +372,31 @@ static gboolean select_configuration(struct media_endpoint *endpoint,
destroy);
}

+static gint transport_device_cmp(gconstpointer data, gconstpointer user_data)
+{
+ struct media_transport *transport = (struct media_transport *) data;
+ const struct audio_device *device = user_data;
+
+ if (device == media_transport_get_dev(transport))
+ return 0;
+
+ return -1;
+}
+
+static struct media_transport *find_device_transport(
+ struct media_endpoint *endpoint,
+ struct audio_device *device)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(endpoint->transports, device,
+ transport_device_cmp);
+ if (match == NULL)
+ return NULL;
+
+ return match->data;
+}
+
static gboolean set_configuration(struct media_endpoint *endpoint,
struct audio_device *device,
uint8_t *configuration, size_t size,
@@ -380,15 +408,18 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
DBusMessage *msg;
const char *path;
DBusMessageIter iter;
+ struct media_transport *transport;
+
+ transport = find_device_transport(endpoint, device);

- if (endpoint->transport != NULL || endpoint->request != NULL)
+ if (transport != NULL || endpoint->request != NULL)
return FALSE;

conn = endpoint->adapter->conn;

- endpoint->transport = media_transport_create(conn, endpoint, device,
+ transport = media_transport_create(conn, endpoint, device,
configuration, size);
- if (endpoint->transport == NULL)
+ if (transport == NULL)
return FALSE;

msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
@@ -396,15 +427,18 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
"SetConfiguration");
if (msg == NULL) {
error("Couldn't allocate D-Bus message");
+ media_transport_destroy(transport);
return FALSE;
}

+ endpoint->transports = g_slist_append(endpoint->transports, transport);
+
dbus_message_iter_init_append(msg, &iter);

- path = media_transport_get_path(endpoint->transport);
+ path = media_transport_get_path(transport);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);

- transport_get_properties(endpoint->transport, &iter);
+ transport_get_properties(transport, &iter);

return media_endpoint_async_call(conn, msg, endpoint, cb, user_data,
destroy);
@@ -440,16 +474,17 @@ static void headset_state_changed(struct audio_device *dev,
void *user_data)
{
struct media_endpoint *endpoint = user_data;
+ struct media_transport *transport;

DBG("");

switch (new_state) {
case HEADSET_STATE_DISCONNECTED:
- if (endpoint->transport &&
- media_transport_get_dev(endpoint->transport) == dev) {
+ transport = find_device_transport(endpoint, dev);

+ if (transport != NULL) {
DBG("Clear endpoint %p", endpoint);
- clear_configuration(endpoint);
+ clear_configuration(endpoint, transport);
}
break;
case HEADSET_STATE_CONNECTING:
@@ -551,17 +586,30 @@ static void clear_config(struct a2dp_sep *sep, void *user_data)
{
struct media_endpoint *endpoint = user_data;

- clear_configuration(endpoint);
+ clear_endpoint(endpoint);
+}
+
+static struct media_transport *get_unique_transport(
+ struct media_endpoint *endpoint)
+{
+ if (endpoint->transports == NULL)
+ return NULL;
+
+ if (endpoint->transports->next != NULL)
+ return NULL;
+
+ return endpoint->transports->data;
}

static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
{
struct media_endpoint *endpoint = user_data;
+ struct media_transport *transport = get_unique_transport(endpoint);

- if (endpoint->transport == NULL)
+ if (transport == NULL)
return;

- media_transport_update_delay(endpoint->transport, delay);
+ media_transport_update_delay(transport, delay);
}

static struct a2dp_endpoint a2dp_endpoint = {
@@ -577,10 +625,7 @@ static void a2dp_destroy_endpoint(void *user_data)
{
struct media_endpoint *endpoint = user_data;

- if (endpoint->transport) {
- media_transport_destroy(endpoint->transport);
- endpoint->transport = NULL;
- }
+ clear_endpoint(endpoint);

endpoint->sep = NULL;
release_endpoint(endpoint);
@@ -603,16 +648,16 @@ static void gateway_state_changed(struct audio_device *dev,
void *user_data)
{
struct media_endpoint *endpoint = user_data;
+ struct media_transport *transport;

DBG("");

switch (new_state) {
case GATEWAY_STATE_DISCONNECTED:
- if (endpoint->transport &&
- media_transport_get_dev(endpoint->transport) == dev) {
-
+ transport = find_device_transport(endpoint, dev);
+ if (transport != NULL) {
DBG("Clear endpoint %p", endpoint);
- clear_configuration(endpoint);
+ clear_configuration(endpoint, transport);
}
break;
case GATEWAY_STATE_CONNECTING:
--
1.7.7.6


2012-04-27 10:59:47

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 5/5] media: Enable parallel requests to endpoint

From: Mikel Astiz <[email protected]>

If endpoint is handling several devices, it should be able to handle
multiple requests, one per each.
---
audio/media.c | 46 ++++++++++++++++++++++++----------------------
1 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/audio/media.c b/audio/media.c
index 5cbf4a2..15f0a6e 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -67,6 +67,7 @@ struct media_adapter {
};

struct endpoint_request {
+ struct media_endpoint *endpoint;
DBusMessage *msg;
DBusPendingCall *call;
media_endpoint_cb_t cb;
@@ -85,7 +86,7 @@ struct media_endpoint {
guint hs_watch;
guint ag_watch;
guint watch;
- struct endpoint_request *request;
+ GSList *requests;
struct media_adapter *adapter;
GSList *transports;
};
@@ -127,15 +128,22 @@ static void endpoint_request_free(struct endpoint_request *request)
g_free(request);
}

-static void media_endpoint_cancel(struct media_endpoint *endpoint)
+static void media_endpoint_cancel(struct endpoint_request *request)
{
- struct endpoint_request *request = endpoint->request;
+ struct media_endpoint *endpoint = request->endpoint;

if (request->call)
dbus_pending_call_cancel(request->call);

+ endpoint->requests = g_slist_remove(endpoint->requests, request);
+
endpoint_request_free(request);
- endpoint->request = NULL;
+}
+
+static void media_endpoint_cancel_all(struct media_endpoint *endpoint)
+{
+ while (endpoint->requests != NULL)
+ media_endpoint_cancel(endpoint->requests->data);
}

static void media_endpoint_destroy(struct media_endpoint *endpoint)
@@ -150,8 +158,7 @@ static void media_endpoint_destroy(struct media_endpoint *endpoint)
if (endpoint->ag_watch)
gateway_remove_state_cb(endpoint->ag_watch);

- if (endpoint->request)
- media_endpoint_cancel(endpoint);
+ media_endpoint_cancel_all(endpoint);

g_slist_free_full(endpoint->transports,
(GDestroyNotify) media_transport_destroy);
@@ -228,8 +235,7 @@ done:

static void clear_endpoint(struct media_endpoint *endpoint)
{
- if (endpoint->request)
- media_endpoint_cancel(endpoint);
+ media_endpoint_cancel_all(endpoint);

while (endpoint->transports != NULL)
clear_configuration(endpoint, endpoint->transports->data);
@@ -237,8 +243,8 @@ static void clear_endpoint(struct media_endpoint *endpoint)

static void endpoint_reply(DBusPendingCall *call, void *user_data)
{
- struct media_endpoint *endpoint = user_data;
- struct endpoint_request *request = endpoint->request;
+ struct endpoint_request *request = user_data;
+ struct media_endpoint *endpoint = request->endpoint;
DBusMessage *reply;
DBusError err;
gboolean value;
@@ -299,9 +305,8 @@ done:
if (request->cb)
request->cb(endpoint, ret, size, request->user_data);

- if (endpoint->request)
- endpoint_request_free(endpoint->request);
- endpoint->request = NULL;
+ endpoint->requests = g_slist_remove(endpoint->requests, request);
+ endpoint_request_free(request);
}

static gboolean media_endpoint_async_call(DBusConnection *conn,
@@ -313,9 +318,6 @@ static gboolean media_endpoint_async_call(DBusConnection *conn,
{
struct endpoint_request *request;

- if (endpoint->request)
- return FALSE;
-
request = g_new0(struct endpoint_request, 1);

/* Timeout should be less than avdtp request timeout (4 seconds) */
@@ -326,13 +328,16 @@ static gboolean media_endpoint_async_call(DBusConnection *conn,
return FALSE;
}

- dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
+ dbus_pending_call_set_notify(request->call, endpoint_reply, request,
+ NULL);

+ request->endpoint = endpoint;
request->msg = msg;
request->cb = cb;
request->destroy = destroy;
request->user_data = user_data;
- endpoint->request = request;
+
+ endpoint->requests = g_slist_append(endpoint->requests, request);

DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
dbus_message_get_destination(msg),
@@ -351,9 +356,6 @@ static gboolean select_configuration(struct media_endpoint *endpoint,
DBusConnection *conn;
DBusMessage *msg;

- if (endpoint->request != NULL)
- return FALSE;
-
conn = endpoint->adapter->conn;

msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
@@ -412,7 +414,7 @@ static gboolean set_configuration(struct media_endpoint *endpoint,

transport = find_device_transport(endpoint, device);

- if (transport != NULL || endpoint->request != NULL)
+ if (transport != NULL)
return FALSE;

conn = endpoint->adapter->conn;
--
1.7.7.6


2012-04-27 10:59:43

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 1/5] audio: Fix gateway state check

From: Mikel Astiz <[email protected]>

Gateway should be considered active also if connecting or playing.

This could for example lead to manager_find_device() not returning a
device that is connecting, and thus the corresponding endpoint would
never be created in the Media API.
---
audio/device.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/audio/device.c b/audio/device.c
index a9d35f9..ee1ade1 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -701,7 +701,7 @@ gboolean audio_device_is_active(struct audio_device *dev,
control_is_active(dev))
return TRUE;
else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
- gateway_is_connected(dev))
+ gateway_is_active(dev))
return TRUE;

return FALSE;
--
1.7.7.6


2012-04-27 10:59:44

by Mikel Astiz

[permalink] [raw]
Subject: [PATCH BlueZ v1 2/5] audio: Add multiple device search to manager

From: Mikel Astiz <[email protected]>

This method is useful to search or than one device fulfulling certain
criteria.
---
audio/manager.c | 25 ++++++++++++++++++++++---
audio/manager.h | 6 ++++++
2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/audio/manager.c b/audio/manager.c
index 20453e6..9c7d0d3 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -1300,12 +1300,13 @@ void audio_manager_exit(void)
btd_unregister_device_driver(&audio_driver);
}

-struct audio_device *manager_find_device(const char *path,
+GSList *manager_find_devices(const char *path,
const bdaddr_t *src,
const bdaddr_t *dst,
const char *interface,
gboolean connected)
{
+ GSList *result = NULL;
GSList *l;

for (l = devices; l != NULL; l = l->next) {
@@ -1343,10 +1344,28 @@ struct audio_device *manager_find_device(const char *path,
if (connected && !audio_device_is_active(dev, interface))
continue;

- return dev;
+ result = g_slist_append(result, dev);
}

- return NULL;
+ return result;
+}
+
+struct audio_device *manager_find_device(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected)
+{
+ struct audio_device *result;
+ GSList *l;
+
+ l = manager_find_devices(path, src, dst, interface, connected);
+ if (l == NULL)
+ return NULL;
+
+ result = l->data;
+ g_slist_free(l);
+ return result;
}

struct audio_device *manager_get_device(const bdaddr_t *src,
diff --git a/audio/manager.h b/audio/manager.h
index cfc646c..f1d3021 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -46,6 +46,12 @@ struct audio_device *manager_find_device(const char *path,
const char *interface,
gboolean connected);

+GSList *manager_find_devices(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected);
+
struct audio_device *manager_get_device(const bdaddr_t *src,
const bdaddr_t *dst,
gboolean create);
--
1.7.7.6