All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/3] a2dp: Store last used endpoint
@ 2019-04-29 12:02 Luiz Augusto von Dentz
  2019-04-29 12:02 ` [PATCH v3 2/3] doc/settings-storage: Document LastUsed Endpoints entry Luiz Augusto von Dentz
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-04-29 12:02 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This stores the last used endpoint whenever it is considered open and
then prefer to use it when attempting to reconnect.
---
 profiles/audio/a2dp.c | 105 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 92 insertions(+), 13 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 8f141739c..a23abdd97 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -147,6 +147,7 @@ struct a2dp_channel {
 	unsigned int auth_id;
 	struct avdtp *session;
 	struct queue *seps;
+	struct a2dp_remote_sep *last_used;
 };
 
 static GSList *servers = NULL;
@@ -860,6 +861,60 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	return TRUE;
 }
 
+static bool match_remote_sep(const void *data, const void *user_data)
+{
+	const struct a2dp_remote_sep *sep = data;
+	const struct avdtp_remote_sep *rsep = user_data;
+
+	return sep->sep == rsep;
+}
+
+static void store_last_used(struct a2dp_channel *chan,
+					struct avdtp_remote_sep *rsep)
+{
+	GKeyFile *key_file;
+	char filename[PATH_MAX];
+	char dst_addr[18];
+	char value[4];
+	char *data;
+	size_t len = 0;
+
+	ba2str(device_get_address(chan->device), dst_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s",
+		btd_adapter_get_storage_dir(device_get_adapter(chan->device)),
+		dst_addr);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	sprintf(value, "%02hhx", avdtp_get_seid(rsep));
+
+	g_key_file_set_string(key_file, "Endpoints", "LastUsed", value);
+
+	data = g_key_file_to_data(key_file, &len, NULL);
+	g_file_set_contents(filename, data, len, NULL);
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void update_last_used(struct a2dp_channel *chan,
+						struct avdtp_stream *stream)
+{
+	struct avdtp_remote_sep *rsep;
+	struct a2dp_remote_sep *sep;
+
+	rsep = avdtp_stream_get_remote_sep(stream);
+
+	/* Update last used */
+	sep = queue_find(chan->seps, match_remote_sep, rsep);
+	if (chan->last_used == sep)
+		return;
+
+	chan->last_used = sep;
+	store_last_used(chan, rsep);
+}
+
 static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			struct avdtp_stream *stream, struct avdtp_error *err,
 			void *user_data)
@@ -884,7 +939,8 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 		setup->err = err;
 		if (setup->start)
 			finalize_resume(setup);
-	}
+	} else if (setup->chan)
+		update_last_used(setup->chan, stream);
 
 	finalize_config(setup);
 
@@ -1077,14 +1133,6 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	return TRUE;
 }
 
-static bool match_remote_sep(const void *data, const void *user_data)
-{
-	const struct a2dp_remote_sep *sep = data;
-	const struct avdtp_remote_sep *rsep = user_data;
-
-	return sep->sep == rsep;
-}
-
 static struct a2dp_remote_sep *find_remote_sep(struct a2dp_channel *chan,
 						struct a2dp_sep *sep)
 {
@@ -1791,19 +1839,28 @@ done:
 	queue_push_tail(chan->seps, sep);
 }
 
+static bool match_seid(const void *data, const void *user_data)
+{
+	const struct a2dp_remote_sep *sep = data;
+	const uint8_t *seid = user_data;
+
+	return avdtp_get_seid(sep->sep) == *seid;
+}
+
 static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
 								char **seids)
 {
 	struct avdtp_remote_sep *sep;
+	uint8_t seid;
+	char *value;
 
 	if (!seids)
 		return;
 
 	for (; *seids; seids++) {
-		uint8_t seid;
 		uint8_t type;
 		uint8_t codec;
-		char *value, caps[256];
+		char caps[256];
 		uint8_t data[128];
 		int i, size;
 		GSList *l = NULL;
@@ -1847,6 +1904,15 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
 
 		register_remote_sep(sep, chan);
 	}
+
+	value = g_key_file_get_string(key_file, "Endpoints", "LastUsed", NULL);
+	if (!value)
+		return;
+
+	if (sscanf(value, "%02hhx", &seid) != 1)
+		return;
+
+	chan->last_used = queue_find(chan->seps, match_seid, &seid);
 }
 
 static void load_remote_seps(struct a2dp_channel *chan)
@@ -2355,8 +2421,12 @@ done:
 static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
 					const char *sender)
 {
+	struct a2dp_sep *first = NULL;
+	struct a2dp_channel *chan = find_channel(session);
+
 	for (; list; list = list->next) {
 		struct a2dp_sep *sep = list->data;
+		struct avdtp_remote_sep *rsep;
 
 		/* Use sender's endpoint if available */
 		if (sender) {
@@ -2370,14 +2440,23 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
 				continue;
 		}
 
-		if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+		rsep = avdtp_find_remote_sep(session, sep->lsep);
+		if (!rsep)
 			continue;
 
+		/* Locate last used if set */
+		if (chan->last_used) {
+			if (chan->last_used->sep == rsep)
+				return sep;
+			first = sep;
+			continue;
+		}
+
 		return sep;
 
 	}
 
-	return NULL;
+	return first;
 }
 
 static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 2/3] doc/settings-storage: Document LastUsed Endpoints entry
  2019-04-29 12:02 [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
@ 2019-04-29 12:02 ` Luiz Augusto von Dentz
  2019-04-29 12:02 ` [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints Luiz Augusto von Dentz
  2019-04-30 12:41 ` [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
  2 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-04-29 12:02 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Document the use of LastUsed entry inside Endpoints group.
---
 doc/settings-storage.txt | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 44b0b4961..f595f9817 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -169,7 +169,9 @@ In "Attributes" group GATT database is stored using attribute handle as key
 all data required to re-create given attribute. ":" is used to separate fields.
 
 In "Endpoints" group A2DP remote endpoints are stored using the seid as key
-(hexadecimal format) and ":" is used to separate fields.
+(hexadecimal format) and ":" is used to separate fields. It may also contain
+an entry which key is set to "LastUsed" which represented the last endpoint
+used.
 
 [General] group contains:
 
@@ -220,6 +222,9 @@ Sample Attributes section:
 					followed by codec type and its
 					capabilies as hexadecimal encoded
 					string.
+	LastUsed:<xx>		String	LastUsed has one field which is the
+					endpoint ID as hexadecimal encoded
+					string.
 
 Info file format
 ================
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-29 12:02 [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
  2019-04-29 12:02 ` [PATCH v3 2/3] doc/settings-storage: Document LastUsed Endpoints entry Luiz Augusto von Dentz
@ 2019-04-29 12:02 ` Luiz Augusto von Dentz
  2019-04-29 16:35   ` Luiz Augusto von Dentz
  2019-04-30 12:41 ` [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
  2 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-04-29 12:02 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Endpoint may not be able to select a valid configuration but there could
be other endpoints available that could be used, so instead of just
using the first match this collects all the matching endpoints and put
them into a queue (ordered by priority) then proceed to next endpoint
only failing if none of the available endpoits can select a valid
configuration.
---
 profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
 1 file changed, 50 insertions(+), 27 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index a23abdd97..4d378a91a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -105,6 +105,7 @@ struct a2dp_setup_cb {
 struct a2dp_setup {
 	struct a2dp_channel *chan;
 	struct avdtp *session;
+	struct queue *eps;
 	struct a2dp_sep *sep;
 	struct a2dp_remote_sep *rsep;
 	struct avdtp_stream *stream;
@@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
 
 static void select_cb(struct a2dp_setup *setup, void *ret, int size)
 {
-	if (size < 0) {
-		DBG("Endpoint replied an invalid configuration");
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	int err;
+
+	if (size) {
+		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
 		goto done;
 	}
 
-	caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
+	setup->sep = queue_pop_head(setup->eps);
+	if (!setup->sep) {
+		error("Unable to select a valid configuration");
+		queue_destroy(setup->eps, NULL);
+		goto done;
+	}
+
+	setup->rsep = find_remote_sep(setup->chan, setup->sep);
+	service = avdtp_get_codec(setup->rsep->sep);
+	codec = (struct avdtp_media_codec_capability *) service->data;
+
+	err = setup->sep->endpoint->select_configuration(setup->sep,
+					codec->data,
+					service->length - sizeof(*codec),
+					setup_ref(setup),
+					select_cb, setup->sep->user_data);
+	if (err == 0)
+		return;
 
 done:
 	finalize_select(setup);
 	setup_unref(setup);
 }
 
-static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
 					const char *sender)
 {
-	struct a2dp_sep *first = NULL;
 	struct a2dp_channel *chan = find_channel(session);
+	struct queue *seps = NULL;
 
 	for (; list; list = list->next) {
 		struct a2dp_sep *sep = list->data;
@@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
 		if (!rsep)
 			continue;
 
-		/* Locate last used if set */
-		if (chan->last_used) {
-			if (chan->last_used->sep == rsep)
-				return sep;
-			first = sep;
-			continue;
-		}
+		if (!seps)
+			seps = queue_new();
 
-		return sep;
+		/* Prepend last used so it is preferred over others */
+		if (chan->last_used && chan->last_used->sep == rsep)
+			queue_push_head(seps, sep);
+		else
+			queue_push_tail(seps, sep);
 
 	}
 
-	return first;
+	return seps;
 }
 
-static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
 					const char *sender)
 {
 	struct a2dp_server *server;
-	struct a2dp_sep *sep;
+	struct queue *seps;
 	GSList *l;
 
 	server = find_server(servers, avdtp_get_adapter(session));
@@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
 	l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
 
 	/* Check sender's seps first */
-	sep = a2dp_find_sep(session, l, sender);
-	if (sep != NULL)
-		return sep;
+	seps = a2dp_find_eps(session, l, sender);
+	if (seps != NULL)
+		return seps;
 
-	return a2dp_find_sep(session, l, NULL);
+	return a2dp_find_eps(session, l, NULL);
 }
 
 static void store_remote_sep(void *data, void *user_data)
@@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
 {
 	struct a2dp_setup *setup;
 	struct a2dp_setup_cb *cb_data;
-	struct a2dp_sep *sep;
+	struct queue *eps;
 	struct avdtp_service_capability *service;
 	struct avdtp_media_codec_capability *codec;
 	int err;
 
-	sep = a2dp_select_sep(session, type, sender);
-	if (!sep) {
+	eps = a2dp_select_eps(session, type, sender);
+	if (!eps) {
 		error("Unable to select SEP");
 		return 0;
 	}
@@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
 	cb_data->select_cb = cb;
 	cb_data->user_data = user_data;
 
-	setup->sep = sep;
-	setup->rsep = find_remote_sep(setup->chan, sep);
+	setup->eps = eps;
+	setup->sep = queue_pop_head(eps);
+	setup->rsep = find_remote_sep(setup->chan, setup->sep);
 
 	if (setup->rsep == NULL) {
 		error("Could not find remote sep");
@@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
 	service = avdtp_get_codec(setup->rsep->sep);
 	codec = (struct avdtp_media_codec_capability *) service->data;
 
-	err = sep->endpoint->select_configuration(sep, codec->data,
+	err = setup->sep->endpoint->select_configuration(setup->sep,
+					codec->data,
 					service->length - sizeof(*codec),
 					setup_ref(setup),
-					select_cb, sep->user_data);
+					select_cb, setup->sep->user_data);
 	if (err == 0)
 		return cb_data->id;
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-29 12:02 ` [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints Luiz Augusto von Dentz
@ 2019-04-29 16:35   ` Luiz Augusto von Dentz
  2019-04-29 16:39     ` Pali Rohár
  0 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-04-29 16:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pali Rohár

Hi Pali,

On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> Endpoint may not be able to select a valid configuration but there could
> be other endpoints available that could be used, so instead of just
> using the first match this collects all the matching endpoints and put
> them into a queue (ordered by priority) then proceed to next endpoint
> only failing if none of the available endpoits can select a valid
> configuration.
> ---
>  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
>  1 file changed, 50 insertions(+), 27 deletions(-)
>
> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> index a23abdd97..4d378a91a 100644
> --- a/profiles/audio/a2dp.c
> +++ b/profiles/audio/a2dp.c
> @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
>  struct a2dp_setup {
>         struct a2dp_channel *chan;
>         struct avdtp *session;
> +       struct queue *eps;
>         struct a2dp_sep *sep;
>         struct a2dp_remote_sep *rsep;
>         struct avdtp_stream *stream;
> @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
>
>  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
>  {
> -       if (size < 0) {
> -               DBG("Endpoint replied an invalid configuration");
> +       struct avdtp_service_capability *service;
> +       struct avdtp_media_codec_capability *codec;
> +       int err;
> +
> +       if (size) {
> +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
>                 goto done;
>         }
>
> -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> +       setup->sep = queue_pop_head(setup->eps);
> +       if (!setup->sep) {
> +               error("Unable to select a valid configuration");
> +               queue_destroy(setup->eps, NULL);
> +               goto done;
> +       }
> +
> +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> +       service = avdtp_get_codec(setup->rsep->sep);
> +       codec = (struct avdtp_media_codec_capability *) service->data;
> +
> +       err = setup->sep->endpoint->select_configuration(setup->sep,
> +                                       codec->data,
> +                                       service->length - sizeof(*codec),
> +                                       setup_ref(setup),
> +                                       select_cb, setup->sep->user_data);
> +       if (err == 0)
> +               return;
>
>  done:
>         finalize_select(setup);
>         setup_unref(setup);
>  }
>
> -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
>                                         const char *sender)
>  {
> -       struct a2dp_sep *first = NULL;
>         struct a2dp_channel *chan = find_channel(session);
> +       struct queue *seps = NULL;
>
>         for (; list; list = list->next) {
>                 struct a2dp_sep *sep = list->data;
> @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
>                 if (!rsep)
>                         continue;
>
> -               /* Locate last used if set */
> -               if (chan->last_used) {
> -                       if (chan->last_used->sep == rsep)
> -                               return sep;
> -                       first = sep;
> -                       continue;
> -               }
> +               if (!seps)
> +                       seps = queue_new();
>
> -               return sep;
> +               /* Prepend last used so it is preferred over others */
> +               if (chan->last_used && chan->last_used->sep == rsep)
> +                       queue_push_head(seps, sep);
> +               else
> +                       queue_push_tail(seps, sep);
>
>         }
>
> -       return first;
> +       return seps;
>  }
>
> -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
>                                         const char *sender)
>  {
>         struct a2dp_server *server;
> -       struct a2dp_sep *sep;
> +       struct queue *seps;
>         GSList *l;
>
>         server = find_server(servers, avdtp_get_adapter(session));
> @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
>         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
>
>         /* Check sender's seps first */
> -       sep = a2dp_find_sep(session, l, sender);
> -       if (sep != NULL)
> -               return sep;
> +       seps = a2dp_find_eps(session, l, sender);
> +       if (seps != NULL)
> +               return seps;
>
> -       return a2dp_find_sep(session, l, NULL);
> +       return a2dp_find_eps(session, l, NULL);
>  }
>
>  static void store_remote_sep(void *data, void *user_data)
> @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
>  {
>         struct a2dp_setup *setup;
>         struct a2dp_setup_cb *cb_data;
> -       struct a2dp_sep *sep;
> +       struct queue *eps;
>         struct avdtp_service_capability *service;
>         struct avdtp_media_codec_capability *codec;
>         int err;
>
> -       sep = a2dp_select_sep(session, type, sender);
> -       if (!sep) {
> +       eps = a2dp_select_eps(session, type, sender);
> +       if (!eps) {
>                 error("Unable to select SEP");
>                 return 0;
>         }
> @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
>         cb_data->select_cb = cb;
>         cb_data->user_data = user_data;
>
> -       setup->sep = sep;
> -       setup->rsep = find_remote_sep(setup->chan, sep);
> +       setup->eps = eps;
> +       setup->sep = queue_pop_head(eps);
> +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
>
>         if (setup->rsep == NULL) {
>                 error("Could not find remote sep");
> @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
>         service = avdtp_get_codec(setup->rsep->sep);
>         codec = (struct avdtp_media_codec_capability *) service->data;
>
> -       err = sep->endpoint->select_configuration(sep, codec->data,
> +       err = setup->sep->endpoint->select_configuration(setup->sep,
> +                                       codec->data,
>                                         service->length - sizeof(*codec),
>                                         setup_ref(setup),
> -                                       select_cb, sep->user_data);
> +                                       select_cb, setup->sep->user_data);
>         if (err == 0)
>                 return cb_data->id;
>
> --
> 2.20.1

Le me know if you find any problem with this change, my headsets seems
to always succeed the first try so I cannot really reproduce the
problem.


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-29 16:35   ` Luiz Augusto von Dentz
@ 2019-04-29 16:39     ` Pali Rohár
  2019-04-30 10:24       ` Sebastian Reichel
  2019-05-02 20:41       ` Pali Rohár
  0 siblings, 2 replies; 14+ messages in thread
From: Pali Rohár @ 2019-04-29 16:39 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 7975 bytes --]

On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> Hi Pali,
> 
> On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
> >
> > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> >
> > Endpoint may not be able to select a valid configuration but there could
> > be other endpoints available that could be used, so instead of just
> > using the first match this collects all the matching endpoints and put
> > them into a queue (ordered by priority) then proceed to next endpoint
> > only failing if none of the available endpoits can select a valid
> > configuration.
> > ---
> >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> >  1 file changed, 50 insertions(+), 27 deletions(-)
> >
> > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > index a23abdd97..4d378a91a 100644
> > --- a/profiles/audio/a2dp.c
> > +++ b/profiles/audio/a2dp.c
> > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> >  struct a2dp_setup {
> >         struct a2dp_channel *chan;
> >         struct avdtp *session;
> > +       struct queue *eps;
> >         struct a2dp_sep *sep;
> >         struct a2dp_remote_sep *rsep;
> >         struct avdtp_stream *stream;
> > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> >
> >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> >  {
> > -       if (size < 0) {
> > -               DBG("Endpoint replied an invalid configuration");
> > +       struct avdtp_service_capability *service;
> > +       struct avdtp_media_codec_capability *codec;
> > +       int err;
> > +
> > +       if (size) {
> > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> >                 goto done;
> >         }
> >
> > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > +       setup->sep = queue_pop_head(setup->eps);
> > +       if (!setup->sep) {
> > +               error("Unable to select a valid configuration");
> > +               queue_destroy(setup->eps, NULL);
> > +               goto done;
> > +       }
> > +
> > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > +       service = avdtp_get_codec(setup->rsep->sep);
> > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > +
> > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > +                                       codec->data,
> > +                                       service->length - sizeof(*codec),
> > +                                       setup_ref(setup),
> > +                                       select_cb, setup->sep->user_data);
> > +       if (err == 0)
> > +               return;
> >
> >  done:
> >         finalize_select(setup);
> >         setup_unref(setup);
> >  }
> >
> > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> >                                         const char *sender)
> >  {
> > -       struct a2dp_sep *first = NULL;
> >         struct a2dp_channel *chan = find_channel(session);
> > +       struct queue *seps = NULL;
> >
> >         for (; list; list = list->next) {
> >                 struct a2dp_sep *sep = list->data;
> > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> >                 if (!rsep)
> >                         continue;
> >
> > -               /* Locate last used if set */
> > -               if (chan->last_used) {
> > -                       if (chan->last_used->sep == rsep)
> > -                               return sep;
> > -                       first = sep;
> > -                       continue;
> > -               }
> > +               if (!seps)
> > +                       seps = queue_new();
> >
> > -               return sep;
> > +               /* Prepend last used so it is preferred over others */
> > +               if (chan->last_used && chan->last_used->sep == rsep)
> > +                       queue_push_head(seps, sep);
> > +               else
> > +                       queue_push_tail(seps, sep);
> >
> >         }
> >
> > -       return first;
> > +       return seps;
> >  }
> >
> > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> >                                         const char *sender)
> >  {
> >         struct a2dp_server *server;
> > -       struct a2dp_sep *sep;
> > +       struct queue *seps;
> >         GSList *l;
> >
> >         server = find_server(servers, avdtp_get_adapter(session));
> > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> >
> >         /* Check sender's seps first */
> > -       sep = a2dp_find_sep(session, l, sender);
> > -       if (sep != NULL)
> > -               return sep;
> > +       seps = a2dp_find_eps(session, l, sender);
> > +       if (seps != NULL)
> > +               return seps;
> >
> > -       return a2dp_find_sep(session, l, NULL);
> > +       return a2dp_find_eps(session, l, NULL);
> >  }
> >
> >  static void store_remote_sep(void *data, void *user_data)
> > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> >  {
> >         struct a2dp_setup *setup;
> >         struct a2dp_setup_cb *cb_data;
> > -       struct a2dp_sep *sep;
> > +       struct queue *eps;
> >         struct avdtp_service_capability *service;
> >         struct avdtp_media_codec_capability *codec;
> >         int err;
> >
> > -       sep = a2dp_select_sep(session, type, sender);
> > -       if (!sep) {
> > +       eps = a2dp_select_eps(session, type, sender);
> > +       if (!eps) {
> >                 error("Unable to select SEP");
> >                 return 0;
> >         }
> > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> >         cb_data->select_cb = cb;
> >         cb_data->user_data = user_data;
> >
> > -       setup->sep = sep;
> > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > +       setup->eps = eps;
> > +       setup->sep = queue_pop_head(eps);
> > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> >
> >         if (setup->rsep == NULL) {
> >                 error("Could not find remote sep");
> > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> >         service = avdtp_get_codec(setup->rsep->sep);
> >         codec = (struct avdtp_media_codec_capability *) service->data;
> >
> > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > +                                       codec->data,
> >                                         service->length - sizeof(*codec),
> >                                         setup_ref(setup),
> > -                                       select_cb, sep->user_data);
> > +                                       select_cb, setup->sep->user_data);
> >         if (err == 0)
> >                 return cb_data->id;
> >
> > --
> > 2.20.1
> 
> Le me know if you find any problem with this change, my headsets seems
> to always succeed the first try so I cannot really reproduce the
> problem.

Ok, I will try it in next days. I can register PA endpoints which always
"fails" so I can test if this is working correctly as expected.

Btw, for future patches please directly CC them to me as I'm not
subscribed to list and either extracting them from '> ' quotes or
finding them in web archive is quite impractical.

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-29 16:39     ` Pali Rohár
@ 2019-04-30 10:24       ` Sebastian Reichel
  2019-04-30 10:26         ` Pali Rohár
  2019-05-02 20:41       ` Pali Rohár
  1 sibling, 1 reply; 14+ messages in thread
From: Sebastian Reichel @ 2019-04-30 10:24 UTC (permalink / raw)
  To: Pali Rohár; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 509 bytes --]

Hi,

On Mon, Apr 29, 2019 at 06:39:16PM +0200, Pali Rohár wrote:
> Btw, for future patches please directly CC them to me as I'm not
> subscribed to list and either extracting them from '> ' quotes or
> finding them in web archive is quite impractical.

FYI (not against your point, just making your life a bit easier):
linux-bluetooth is on lore.kernel.org, which provides an mbox
download:

https://lore.kernel.org/linux-bluetooth/20190429120259.17880-1-luiz.dentz@gmail.com/T/#t

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-30 10:24       ` Sebastian Reichel
@ 2019-04-30 10:26         ` Pali Rohár
  0 siblings, 0 replies; 14+ messages in thread
From: Pali Rohár @ 2019-04-30 10:26 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: linux-bluetooth

On Tuesday 30 April 2019 12:24:32 Sebastian Reichel wrote:
> Hi,
> 
> On Mon, Apr 29, 2019 at 06:39:16PM +0200, Pali Rohár wrote:
> > Btw, for future patches please directly CC them to me as I'm not
> > subscribed to list and either extracting them from '> ' quotes or
> > finding them in web archive is quite impractical.
> 
> FYI (not against your point, just making your life a bit easier):
> linux-bluetooth is on lore.kernel.org, which provides an mbox
> download:
> 
> https://lore.kernel.org/linux-bluetooth/20190429120259.17880-1-luiz.dentz@gmail.com/T/#t

I have not know about it. I used marc.info, there is "Download RAW":
https://marc.info/?l=linux-bluetooth&m=155653938929963&w=2

-- 
Pali Rohár
pali.rohar@gmail.com

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 1/3] a2dp: Store last used endpoint
  2019-04-29 12:02 [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
  2019-04-29 12:02 ` [PATCH v3 2/3] doc/settings-storage: Document LastUsed Endpoints entry Luiz Augusto von Dentz
  2019-04-29 12:02 ` [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints Luiz Augusto von Dentz
@ 2019-04-30 12:41 ` Luiz Augusto von Dentz
  2 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-04-30 12:41 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This stores the last used endpoint whenever it is considered open and
> then prefer to use it when attempting to reconnect.
> ---
>  profiles/audio/a2dp.c | 105 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 92 insertions(+), 13 deletions(-)
>
> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> index 8f141739c..a23abdd97 100644
> --- a/profiles/audio/a2dp.c
> +++ b/profiles/audio/a2dp.c
> @@ -147,6 +147,7 @@ struct a2dp_channel {
>         unsigned int auth_id;
>         struct avdtp *session;
>         struct queue *seps;
> +       struct a2dp_remote_sep *last_used;
>  };
>
>  static GSList *servers = NULL;
> @@ -860,6 +861,60 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
>         return TRUE;
>  }
>
> +static bool match_remote_sep(const void *data, const void *user_data)
> +{
> +       const struct a2dp_remote_sep *sep = data;
> +       const struct avdtp_remote_sep *rsep = user_data;
> +
> +       return sep->sep == rsep;
> +}
> +
> +static void store_last_used(struct a2dp_channel *chan,
> +                                       struct avdtp_remote_sep *rsep)
> +{
> +       GKeyFile *key_file;
> +       char filename[PATH_MAX];
> +       char dst_addr[18];
> +       char value[4];
> +       char *data;
> +       size_t len = 0;
> +
> +       ba2str(device_get_address(chan->device), dst_addr);
> +
> +       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s",
> +               btd_adapter_get_storage_dir(device_get_adapter(chan->device)),
> +               dst_addr);
> +       key_file = g_key_file_new();
> +       g_key_file_load_from_file(key_file, filename, 0, NULL);
> +
> +       sprintf(value, "%02hhx", avdtp_get_seid(rsep));
> +
> +       g_key_file_set_string(key_file, "Endpoints", "LastUsed", value);
> +
> +       data = g_key_file_to_data(key_file, &len, NULL);
> +       g_file_set_contents(filename, data, len, NULL);
> +
> +       g_free(data);
> +       g_key_file_free(key_file);
> +}
> +
> +static void update_last_used(struct a2dp_channel *chan,
> +                                               struct avdtp_stream *stream)
> +{
> +       struct avdtp_remote_sep *rsep;
> +       struct a2dp_remote_sep *sep;
> +
> +       rsep = avdtp_stream_get_remote_sep(stream);
> +
> +       /* Update last used */
> +       sep = queue_find(chan->seps, match_remote_sep, rsep);
> +       if (chan->last_used == sep)
> +               return;
> +
> +       chan->last_used = sep;
> +       store_last_used(chan, rsep);
> +}
> +
>  static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
>                         struct avdtp_stream *stream, struct avdtp_error *err,
>                         void *user_data)
> @@ -884,7 +939,8 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
>                 setup->err = err;
>                 if (setup->start)
>                         finalize_resume(setup);
> -       }
> +       } else if (setup->chan)
> +               update_last_used(setup->chan, stream);
>
>         finalize_config(setup);
>
> @@ -1077,14 +1133,6 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
>         return TRUE;
>  }
>
> -static bool match_remote_sep(const void *data, const void *user_data)
> -{
> -       const struct a2dp_remote_sep *sep = data;
> -       const struct avdtp_remote_sep *rsep = user_data;
> -
> -       return sep->sep == rsep;
> -}
> -
>  static struct a2dp_remote_sep *find_remote_sep(struct a2dp_channel *chan,
>                                                 struct a2dp_sep *sep)
>  {
> @@ -1791,19 +1839,28 @@ done:
>         queue_push_tail(chan->seps, sep);
>  }
>
> +static bool match_seid(const void *data, const void *user_data)
> +{
> +       const struct a2dp_remote_sep *sep = data;
> +       const uint8_t *seid = user_data;
> +
> +       return avdtp_get_seid(sep->sep) == *seid;
> +}
> +
>  static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
>                                                                 char **seids)
>  {
>         struct avdtp_remote_sep *sep;
> +       uint8_t seid;
> +       char *value;
>
>         if (!seids)
>                 return;
>
>         for (; *seids; seids++) {
> -               uint8_t seid;
>                 uint8_t type;
>                 uint8_t codec;
> -               char *value, caps[256];
> +               char caps[256];
>                 uint8_t data[128];
>                 int i, size;
>                 GSList *l = NULL;
> @@ -1847,6 +1904,15 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
>
>                 register_remote_sep(sep, chan);
>         }
> +
> +       value = g_key_file_get_string(key_file, "Endpoints", "LastUsed", NULL);
> +       if (!value)
> +               return;
> +
> +       if (sscanf(value, "%02hhx", &seid) != 1)
> +               return;
> +
> +       chan->last_used = queue_find(chan->seps, match_seid, &seid);
>  }
>
>  static void load_remote_seps(struct a2dp_channel *chan)
> @@ -2355,8 +2421,12 @@ done:
>  static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
>                                         const char *sender)
>  {
> +       struct a2dp_sep *first = NULL;
> +       struct a2dp_channel *chan = find_channel(session);
> +
>         for (; list; list = list->next) {
>                 struct a2dp_sep *sep = list->data;
> +               struct avdtp_remote_sep *rsep;
>
>                 /* Use sender's endpoint if available */
>                 if (sender) {
> @@ -2370,14 +2440,23 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
>                                 continue;
>                 }
>
> -               if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
> +               rsep = avdtp_find_remote_sep(session, sep->lsep);
> +               if (!rsep)
>                         continue;
>
> +               /* Locate last used if set */
> +               if (chan->last_used) {
> +                       if (chan->last_used->sep == rsep)
> +                               return sep;
> +                       first = sep;
> +                       continue;
> +               }
> +
>                 return sep;
>
>         }
>
> -       return NULL;
> +       return first;
>  }
>
>  static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> --
> 2.20.1

Applied.


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-04-29 16:39     ` Pali Rohár
  2019-04-30 10:24       ` Sebastian Reichel
@ 2019-05-02 20:41       ` Pali Rohár
  2019-05-02 20:53         ` Pali Rohár
  1 sibling, 1 reply; 14+ messages in thread
From: Pali Rohár @ 2019-05-02 20:41 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 10161 bytes --]

On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > Hi Pali,
> > 
> > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > <luiz.dentz@gmail.com> wrote:
> > >
> > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > >
> > > Endpoint may not be able to select a valid configuration but there could
> > > be other endpoints available that could be used, so instead of just
> > > using the first match this collects all the matching endpoints and put
> > > them into a queue (ordered by priority) then proceed to next endpoint
> > > only failing if none of the available endpoits can select a valid
> > > configuration.
> > > ---
> > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > >
> > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > index a23abdd97..4d378a91a 100644
> > > --- a/profiles/audio/a2dp.c
> > > +++ b/profiles/audio/a2dp.c
> > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > >  struct a2dp_setup {
> > >         struct a2dp_channel *chan;
> > >         struct avdtp *session;
> > > +       struct queue *eps;
> > >         struct a2dp_sep *sep;
> > >         struct a2dp_remote_sep *rsep;
> > >         struct avdtp_stream *stream;
> > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > >
> > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > >  {
> > > -       if (size < 0) {
> > > -               DBG("Endpoint replied an invalid configuration");
> > > +       struct avdtp_service_capability *service;
> > > +       struct avdtp_media_codec_capability *codec;
> > > +       int err;
> > > +
> > > +       if (size) {
> > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > >                 goto done;
> > >         }
> > >
> > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > +       setup->sep = queue_pop_head(setup->eps);
> > > +       if (!setup->sep) {
> > > +               error("Unable to select a valid configuration");
> > > +               queue_destroy(setup->eps, NULL);
> > > +               goto done;
> > > +       }
> > > +
> > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > +
> > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > +                                       codec->data,
> > > +                                       service->length - sizeof(*codec),
> > > +                                       setup_ref(setup),
> > > +                                       select_cb, setup->sep->user_data);
> > > +       if (err == 0)
> > > +               return;
> > >
> > >  done:
> > >         finalize_select(setup);
> > >         setup_unref(setup);
> > >  }
> > >
> > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > >                                         const char *sender)
> > >  {
> > > -       struct a2dp_sep *first = NULL;
> > >         struct a2dp_channel *chan = find_channel(session);
> > > +       struct queue *seps = NULL;
> > >
> > >         for (; list; list = list->next) {
> > >                 struct a2dp_sep *sep = list->data;
> > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > >                 if (!rsep)
> > >                         continue;
> > >
> > > -               /* Locate last used if set */
> > > -               if (chan->last_used) {
> > > -                       if (chan->last_used->sep == rsep)
> > > -                               return sep;
> > > -                       first = sep;
> > > -                       continue;
> > > -               }
> > > +               if (!seps)
> > > +                       seps = queue_new();
> > >
> > > -               return sep;
> > > +               /* Prepend last used so it is preferred over others */
> > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > +                       queue_push_head(seps, sep);
> > > +               else
> > > +                       queue_push_tail(seps, sep);
> > >
> > >         }
> > >
> > > -       return first;
> > > +       return seps;
> > >  }
> > >
> > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > >                                         const char *sender)
> > >  {
> > >         struct a2dp_server *server;
> > > -       struct a2dp_sep *sep;
> > > +       struct queue *seps;
> > >         GSList *l;
> > >
> > >         server = find_server(servers, avdtp_get_adapter(session));
> > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > >
> > >         /* Check sender's seps first */
> > > -       sep = a2dp_find_sep(session, l, sender);
> > > -       if (sep != NULL)
> > > -               return sep;
> > > +       seps = a2dp_find_eps(session, l, sender);
> > > +       if (seps != NULL)
> > > +               return seps;
> > >
> > > -       return a2dp_find_sep(session, l, NULL);
> > > +       return a2dp_find_eps(session, l, NULL);
> > >  }
> > >
> > >  static void store_remote_sep(void *data, void *user_data)
> > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > >  {
> > >         struct a2dp_setup *setup;
> > >         struct a2dp_setup_cb *cb_data;
> > > -       struct a2dp_sep *sep;
> > > +       struct queue *eps;
> > >         struct avdtp_service_capability *service;
> > >         struct avdtp_media_codec_capability *codec;
> > >         int err;
> > >
> > > -       sep = a2dp_select_sep(session, type, sender);
> > > -       if (!sep) {
> > > +       eps = a2dp_select_eps(session, type, sender);
> > > +       if (!eps) {
> > >                 error("Unable to select SEP");
> > >                 return 0;
> > >         }
> > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > >         cb_data->select_cb = cb;
> > >         cb_data->user_data = user_data;
> > >
> > > -       setup->sep = sep;
> > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > +       setup->eps = eps;
> > > +       setup->sep = queue_pop_head(eps);
> > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > >
> > >         if (setup->rsep == NULL) {
> > >                 error("Could not find remote sep");
> > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > >         service = avdtp_get_codec(setup->rsep->sep);
> > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > >
> > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > +                                       codec->data,
> > >                                         service->length - sizeof(*codec),
> > >                                         setup_ref(setup),
> > > -                                       select_cb, sep->user_data);
> > > +                                       select_cb, setup->sep->user_data);
> > >         if (err == 0)
> > >                 return cb_data->id;
> > >
> > > --
> > > 2.20.1
> > 
> > Le me know if you find any problem with this change, my headsets seems
> > to always succeed the first try so I cannot really reproduce the
> > problem.
> 
> Ok, I will try it in next days. I can register PA endpoints which always
> "fails" so I can test if this is working correctly as expected.

Nope, this your patch does not work. It cause segfaulting of bluetoothd
daemon itself. Here is stacktrace of current git master branch (where is
your patch too):

Program received signal SIGSEGV, Segmentation fault.
__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
(gdb) bt
#0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
#1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
#2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
#3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
#4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
#5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
#6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
#7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
#11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
#12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729

In syslog for bluetoothd is this last line:
bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments

pulseaudio refused only one SelectConfiguration call.

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-05-02 20:41       ` Pali Rohár
@ 2019-05-02 20:53         ` Pali Rohár
  2019-05-02 20:58           ` Pali Rohár
  0 siblings, 1 reply; 14+ messages in thread
From: Pali Rohár @ 2019-05-02 20:53 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 11355 bytes --]

On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > Hi Pali,
> > > 
> > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > <luiz.dentz@gmail.com> wrote:
> > > >
> > > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > > >
> > > > Endpoint may not be able to select a valid configuration but there could
> > > > be other endpoints available that could be used, so instead of just
> > > > using the first match this collects all the matching endpoints and put
> > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > only failing if none of the available endpoits can select a valid
> > > > configuration.
> > > > ---
> > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > >
> > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > index a23abdd97..4d378a91a 100644
> > > > --- a/profiles/audio/a2dp.c
> > > > +++ b/profiles/audio/a2dp.c
> > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > >  struct a2dp_setup {
> > > >         struct a2dp_channel *chan;
> > > >         struct avdtp *session;
> > > > +       struct queue *eps;
> > > >         struct a2dp_sep *sep;
> > > >         struct a2dp_remote_sep *rsep;
> > > >         struct avdtp_stream *stream;
> > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > >
> > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > >  {
> > > > -       if (size < 0) {
> > > > -               DBG("Endpoint replied an invalid configuration");
> > > > +       struct avdtp_service_capability *service;
> > > > +       struct avdtp_media_codec_capability *codec;
> > > > +       int err;
> > > > +
> > > > +       if (size) {
> > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > >                 goto done;
> > > >         }
> > > >
> > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > +       if (!setup->sep) {
> > > > +               error("Unable to select a valid configuration");
> > > > +               queue_destroy(setup->eps, NULL);
> > > > +               goto done;
> > > > +       }
> > > > +
> > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > +
> > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > +                                       codec->data,
> > > > +                                       service->length - sizeof(*codec),
> > > > +                                       setup_ref(setup),
> > > > +                                       select_cb, setup->sep->user_data);
> > > > +       if (err == 0)
> > > > +               return;
> > > >
> > > >  done:
> > > >         finalize_select(setup);
> > > >         setup_unref(setup);
> > > >  }
> > > >
> > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > >                                         const char *sender)
> > > >  {
> > > > -       struct a2dp_sep *first = NULL;
> > > >         struct a2dp_channel *chan = find_channel(session);
> > > > +       struct queue *seps = NULL;
> > > >
> > > >         for (; list; list = list->next) {
> > > >                 struct a2dp_sep *sep = list->data;
> > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > >                 if (!rsep)
> > > >                         continue;
> > > >
> > > > -               /* Locate last used if set */
> > > > -               if (chan->last_used) {
> > > > -                       if (chan->last_used->sep == rsep)
> > > > -                               return sep;
> > > > -                       first = sep;
> > > > -                       continue;
> > > > -               }
> > > > +               if (!seps)
> > > > +                       seps = queue_new();
> > > >
> > > > -               return sep;
> > > > +               /* Prepend last used so it is preferred over others */
> > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > +                       queue_push_head(seps, sep);
> > > > +               else
> > > > +                       queue_push_tail(seps, sep);
> > > >
> > > >         }
> > > >
> > > > -       return first;
> > > > +       return seps;
> > > >  }
> > > >
> > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > >                                         const char *sender)
> > > >  {
> > > >         struct a2dp_server *server;
> > > > -       struct a2dp_sep *sep;
> > > > +       struct queue *seps;
> > > >         GSList *l;
> > > >
> > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > >
> > > >         /* Check sender's seps first */
> > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > -       if (sep != NULL)
> > > > -               return sep;
> > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > +       if (seps != NULL)
> > > > +               return seps;
> > > >
> > > > -       return a2dp_find_sep(session, l, NULL);
> > > > +       return a2dp_find_eps(session, l, NULL);
> > > >  }
> > > >
> > > >  static void store_remote_sep(void *data, void *user_data)
> > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >  {
> > > >         struct a2dp_setup *setup;
> > > >         struct a2dp_setup_cb *cb_data;
> > > > -       struct a2dp_sep *sep;
> > > > +       struct queue *eps;
> > > >         struct avdtp_service_capability *service;
> > > >         struct avdtp_media_codec_capability *codec;
> > > >         int err;
> > > >
> > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > -       if (!sep) {
> > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > +       if (!eps) {
> > > >                 error("Unable to select SEP");
> > > >                 return 0;
> > > >         }
> > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >         cb_data->select_cb = cb;
> > > >         cb_data->user_data = user_data;
> > > >
> > > > -       setup->sep = sep;
> > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > +       setup->eps = eps;
> > > > +       setup->sep = queue_pop_head(eps);
> > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > >
> > > >         if (setup->rsep == NULL) {
> > > >                 error("Could not find remote sep");
> > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > >
> > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > +                                       codec->data,
> > > >                                         service->length - sizeof(*codec),
> > > >                                         setup_ref(setup),
> > > > -                                       select_cb, sep->user_data);
> > > > +                                       select_cb, setup->sep->user_data);
> > > >         if (err == 0)
> > > >                 return cb_data->id;
> > > >
> > > > --
> > > > 2.20.1
> > > 
> > > Le me know if you find any problem with this change, my headsets seems
> > > to always succeed the first try so I cannot really reproduce the
> > > problem.
> > 
> > Ok, I will try it in next days. I can register PA endpoints which always
> > "fails" so I can test if this is working correctly as expected.
> 
> Nope, this your patch does not work. It cause segfaulting of bluetoothd
> daemon itself. Here is stacktrace of current git master branch (where is
> your patch too):
> 
> Program received signal SIGSEGV, Segmentation fault.
> __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> (gdb) bt
> #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> 
> In syslog for bluetoothd is this last line:
> bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> 
> pulseaudio refused only one SelectConfiguration call.

In profiles/audio/a2dp.c is:

static void select_cb(struct a2dp_setup *setup, void *ret, int size)
{
...
	if (size) {
		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
...

and then there is:

tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
							size_t size)
{
...
	memcpy(cap->data, caps, size);
...

When select_cb() is called with size=-1 then is calls caps_add_codec()
as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
is NULL too. Seems like wrong error handling in select_cb() function.

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-05-02 20:53         ` Pali Rohár
@ 2019-05-02 20:58           ` Pali Rohár
  2019-05-02 21:10             ` Pali Rohár
  0 siblings, 1 reply; 14+ messages in thread
From: Pali Rohár @ 2019-05-02 20:58 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 12027 bytes --]

On Thursday 02 May 2019 22:53:49 Pali Rohár wrote:
> On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> > On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > > Hi Pali,
> > > > 
> > > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > > <luiz.dentz@gmail.com> wrote:
> > > > >
> > > > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > > > >
> > > > > Endpoint may not be able to select a valid configuration but there could
> > > > > be other endpoints available that could be used, so instead of just
> > > > > using the first match this collects all the matching endpoints and put
> > > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > > only failing if none of the available endpoits can select a valid
> > > > > configuration.
> > > > > ---
> > > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > > >
> > > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > > index a23abdd97..4d378a91a 100644
> > > > > --- a/profiles/audio/a2dp.c
> > > > > +++ b/profiles/audio/a2dp.c
> > > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > > >  struct a2dp_setup {
> > > > >         struct a2dp_channel *chan;
> > > > >         struct avdtp *session;
> > > > > +       struct queue *eps;
> > > > >         struct a2dp_sep *sep;
> > > > >         struct a2dp_remote_sep *rsep;
> > > > >         struct avdtp_stream *stream;
> > > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > > >
> > > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > > >  {
> > > > > -       if (size < 0) {
> > > > > -               DBG("Endpoint replied an invalid configuration");
> > > > > +       struct avdtp_service_capability *service;
> > > > > +       struct avdtp_media_codec_capability *codec;
> > > > > +       int err;
> > > > > +
> > > > > +       if (size) {
> > > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > >                 goto done;
> > > > >         }
> > > > >
> > > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > > +       if (!setup->sep) {
> > > > > +               error("Unable to select a valid configuration");
> > > > > +               queue_destroy(setup->eps, NULL);
> > > > > +               goto done;
> > > > > +       }
> > > > > +
> > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > +
> > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > +                                       codec->data,
> > > > > +                                       service->length - sizeof(*codec),
> > > > > +                                       setup_ref(setup),
> > > > > +                                       select_cb, setup->sep->user_data);
> > > > > +       if (err == 0)
> > > > > +               return;
> > > > >
> > > > >  done:
> > > > >         finalize_select(setup);
> > > > >         setup_unref(setup);
> > > > >  }
> > > > >
> > > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > > >                                         const char *sender)
> > > > >  {
> > > > > -       struct a2dp_sep *first = NULL;
> > > > >         struct a2dp_channel *chan = find_channel(session);
> > > > > +       struct queue *seps = NULL;
> > > > >
> > > > >         for (; list; list = list->next) {
> > > > >                 struct a2dp_sep *sep = list->data;
> > > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > >                 if (!rsep)
> > > > >                         continue;
> > > > >
> > > > > -               /* Locate last used if set */
> > > > > -               if (chan->last_used) {
> > > > > -                       if (chan->last_used->sep == rsep)
> > > > > -                               return sep;
> > > > > -                       first = sep;
> > > > > -                       continue;
> > > > > -               }
> > > > > +               if (!seps)
> > > > > +                       seps = queue_new();
> > > > >
> > > > > -               return sep;
> > > > > +               /* Prepend last used so it is preferred over others */
> > > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > > +                       queue_push_head(seps, sep);
> > > > > +               else
> > > > > +                       queue_push_tail(seps, sep);
> > > > >
> > > > >         }
> > > > >
> > > > > -       return first;
> > > > > +       return seps;
> > > > >  }
> > > > >
> > > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > > >                                         const char *sender)
> > > > >  {
> > > > >         struct a2dp_server *server;
> > > > > -       struct a2dp_sep *sep;
> > > > > +       struct queue *seps;
> > > > >         GSList *l;
> > > > >
> > > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > > >
> > > > >         /* Check sender's seps first */
> > > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > > -       if (sep != NULL)
> > > > > -               return sep;
> > > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > > +       if (seps != NULL)
> > > > > +               return seps;
> > > > >
> > > > > -       return a2dp_find_sep(session, l, NULL);
> > > > > +       return a2dp_find_eps(session, l, NULL);
> > > > >  }
> > > > >
> > > > >  static void store_remote_sep(void *data, void *user_data)
> > > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > >  {
> > > > >         struct a2dp_setup *setup;
> > > > >         struct a2dp_setup_cb *cb_data;
> > > > > -       struct a2dp_sep *sep;
> > > > > +       struct queue *eps;
> > > > >         struct avdtp_service_capability *service;
> > > > >         struct avdtp_media_codec_capability *codec;
> > > > >         int err;
> > > > >
> > > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > > -       if (!sep) {
> > > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > > +       if (!eps) {
> > > > >                 error("Unable to select SEP");
> > > > >                 return 0;
> > > > >         }
> > > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > >         cb_data->select_cb = cb;
> > > > >         cb_data->user_data = user_data;
> > > > >
> > > > > -       setup->sep = sep;
> > > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > > +       setup->eps = eps;
> > > > > +       setup->sep = queue_pop_head(eps);
> > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > >
> > > > >         if (setup->rsep == NULL) {
> > > > >                 error("Could not find remote sep");
> > > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > > >
> > > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > +                                       codec->data,
> > > > >                                         service->length - sizeof(*codec),
> > > > >                                         setup_ref(setup),
> > > > > -                                       select_cb, sep->user_data);
> > > > > +                                       select_cb, setup->sep->user_data);
> > > > >         if (err == 0)
> > > > >                 return cb_data->id;
> > > > >
> > > > > --
> > > > > 2.20.1
> > > > 
> > > > Le me know if you find any problem with this change, my headsets seems
> > > > to always succeed the first try so I cannot really reproduce the
> > > > problem.
> > > 
> > > Ok, I will try it in next days. I can register PA endpoints which always
> > > "fails" so I can test if this is working correctly as expected.
> > 
> > Nope, this your patch does not work. It cause segfaulting of bluetoothd
> > daemon itself. Here is stacktrace of current git master branch (where is
> > your patch too):
> > 
> > Program received signal SIGSEGV, Segmentation fault.
> > __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> > (gdb) bt
> > #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> > #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> > #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> > #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> > #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> > #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> > #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> > 
> > In syslog for bluetoothd is this last line:
> > bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> > 
> > pulseaudio refused only one SelectConfiguration call.
> 
> In profiles/audio/a2dp.c is:
> 
> static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> {
> ...
> 	if (size) {
> 		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> ...
> 
> and then there is:
> 
> tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
> 							size_t size)
> {
> ...
> 	memcpy(cap->data, caps, size);
> ...
> 
> When select_cb() is called with size=-1 then is calls caps_add_codec()
> as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
> as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
> there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
> is NULL too. Seems like wrong error handling in select_cb() function.

And seems that ret=NULL and size=-1 comes from media.c endpoint_reply()
functions. As those values are defaults... Maybe it is a bug here?

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-05-02 20:58           ` Pali Rohár
@ 2019-05-02 21:10             ` Pali Rohár
  2019-05-03  8:55               ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 14+ messages in thread
From: Pali Rohár @ 2019-05-02 21:10 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 13490 bytes --]

On Thursday 02 May 2019 22:58:36 Pali Rohár wrote:
> On Thursday 02 May 2019 22:53:49 Pali Rohár wrote:
> > On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> > > On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > > > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > > > Hi Pali,
> > > > > 
> > > > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > > > <luiz.dentz@gmail.com> wrote:
> > > > > >
> > > > > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > > > > >
> > > > > > Endpoint may not be able to select a valid configuration but there could
> > > > > > be other endpoints available that could be used, so instead of just
> > > > > > using the first match this collects all the matching endpoints and put
> > > > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > > > only failing if none of the available endpoits can select a valid
> > > > > > configuration.
> > > > > > ---
> > > > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > > > >
> > > > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > > > index a23abdd97..4d378a91a 100644
> > > > > > --- a/profiles/audio/a2dp.c
> > > > > > +++ b/profiles/audio/a2dp.c
> > > > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > > > >  struct a2dp_setup {
> > > > > >         struct a2dp_channel *chan;
> > > > > >         struct avdtp *session;
> > > > > > +       struct queue *eps;
> > > > > >         struct a2dp_sep *sep;
> > > > > >         struct a2dp_remote_sep *rsep;
> > > > > >         struct avdtp_stream *stream;
> > > > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > > > >
> > > > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > > > >  {
> > > > > > -       if (size < 0) {
> > > > > > -               DBG("Endpoint replied an invalid configuration");
> > > > > > +       struct avdtp_service_capability *service;
> > > > > > +       struct avdtp_media_codec_capability *codec;
> > > > > > +       int err;
> > > > > > +
> > > > > > +       if (size) {
> > > > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > >                 goto done;
> > > > > >         }
> > > > > >
> > > > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > > > +       if (!setup->sep) {
> > > > > > +               error("Unable to select a valid configuration");
> > > > > > +               queue_destroy(setup->eps, NULL);
> > > > > > +               goto done;
> > > > > > +       }
> > > > > > +
> > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > > +
> > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > +                                       codec->data,
> > > > > > +                                       service->length - sizeof(*codec),
> > > > > > +                                       setup_ref(setup),
> > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > > +       if (err == 0)
> > > > > > +               return;
> > > > > >
> > > > > >  done:
> > > > > >         finalize_select(setup);
> > > > > >         setup_unref(setup);
> > > > > >  }
> > > > > >
> > > > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > > > >                                         const char *sender)
> > > > > >  {
> > > > > > -       struct a2dp_sep *first = NULL;
> > > > > >         struct a2dp_channel *chan = find_channel(session);
> > > > > > +       struct queue *seps = NULL;
> > > > > >
> > > > > >         for (; list; list = list->next) {
> > > > > >                 struct a2dp_sep *sep = list->data;
> > > > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > >                 if (!rsep)
> > > > > >                         continue;
> > > > > >
> > > > > > -               /* Locate last used if set */
> > > > > > -               if (chan->last_used) {
> > > > > > -                       if (chan->last_used->sep == rsep)
> > > > > > -                               return sep;
> > > > > > -                       first = sep;
> > > > > > -                       continue;
> > > > > > -               }
> > > > > > +               if (!seps)
> > > > > > +                       seps = queue_new();
> > > > > >
> > > > > > -               return sep;
> > > > > > +               /* Prepend last used so it is preferred over others */
> > > > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > > > +                       queue_push_head(seps, sep);
> > > > > > +               else
> > > > > > +                       queue_push_tail(seps, sep);
> > > > > >
> > > > > >         }
> > > > > >
> > > > > > -       return first;
> > > > > > +       return seps;
> > > > > >  }
> > > > > >
> > > > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > > > >                                         const char *sender)
> > > > > >  {
> > > > > >         struct a2dp_server *server;
> > > > > > -       struct a2dp_sep *sep;
> > > > > > +       struct queue *seps;
> > > > > >         GSList *l;
> > > > > >
> > > > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > > > >
> > > > > >         /* Check sender's seps first */
> > > > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > > > -       if (sep != NULL)
> > > > > > -               return sep;
> > > > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > > > +       if (seps != NULL)
> > > > > > +               return seps;
> > > > > >
> > > > > > -       return a2dp_find_sep(session, l, NULL);
> > > > > > +       return a2dp_find_eps(session, l, NULL);
> > > > > >  }
> > > > > >
> > > > > >  static void store_remote_sep(void *data, void *user_data)
> > > > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > >  {
> > > > > >         struct a2dp_setup *setup;
> > > > > >         struct a2dp_setup_cb *cb_data;
> > > > > > -       struct a2dp_sep *sep;
> > > > > > +       struct queue *eps;
> > > > > >         struct avdtp_service_capability *service;
> > > > > >         struct avdtp_media_codec_capability *codec;
> > > > > >         int err;
> > > > > >
> > > > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > > > -       if (!sep) {
> > > > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > > > +       if (!eps) {
> > > > > >                 error("Unable to select SEP");
> > > > > >                 return 0;
> > > > > >         }
> > > > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > >         cb_data->select_cb = cb;
> > > > > >         cb_data->user_data = user_data;
> > > > > >
> > > > > > -       setup->sep = sep;
> > > > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > > > +       setup->eps = eps;
> > > > > > +       setup->sep = queue_pop_head(eps);
> > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > >
> > > > > >         if (setup->rsep == NULL) {
> > > > > >                 error("Could not find remote sep");
> > > > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > >
> > > > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > +                                       codec->data,
> > > > > >                                         service->length - sizeof(*codec),
> > > > > >                                         setup_ref(setup),
> > > > > > -                                       select_cb, sep->user_data);
> > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > >         if (err == 0)
> > > > > >                 return cb_data->id;
> > > > > >
> > > > > > --
> > > > > > 2.20.1
> > > > > 
> > > > > Le me know if you find any problem with this change, my headsets seems
> > > > > to always succeed the first try so I cannot really reproduce the
> > > > > problem.
> > > > 
> > > > Ok, I will try it in next days. I can register PA endpoints which always
> > > > "fails" so I can test if this is working correctly as expected.
> > > 
> > > Nope, this your patch does not work. It cause segfaulting of bluetoothd
> > > daemon itself. Here is stacktrace of current git master branch (where is
> > > your patch too):
> > > 
> > > Program received signal SIGSEGV, Segmentation fault.
> > > __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> > > (gdb) bt
> > > #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> > > #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> > > #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> > > #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> > > #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> > > #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> > > #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> > > 
> > > In syslog for bluetoothd is this last line:
> > > bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> > > 
> > > pulseaudio refused only one SelectConfiguration call.
> > 
> > In profiles/audio/a2dp.c is:
> > 
> > static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > {
> > ...
> > 	if (size) {
> > 		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > ...
> > 
> > and then there is:
> > 
> > tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
> > 							size_t size)
> > {
> > ...
> > 	memcpy(cap->data, caps, size);
> > ...
> > 
> > When select_cb() is called with size=-1 then is calls caps_add_codec()
> > as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
> > as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
> > there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
> > is NULL too. Seems like wrong error handling in select_cb() function.
> 
> And seems that ret=NULL and size=-1 comes from media.c endpoint_reply()
> functions. As those values are defaults... Maybe it is a bug here?

Here is patch which fixes above problem with crashing:

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index d0676b577..b06eafae0 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1703,7 +1703,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
 	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
 		return btd_error_invalid_args(msg);
 
-	if (parse_properties(&props, &caps, &size) < 0)
+	if (parse_properties(&props, &caps, &size) < 0 || size <= 0)
 		return btd_error_invalid_args(msg);
 
 	err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size,
@@ -2418,7 +2418,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
 	struct avdtp_media_codec_capability *codec;
 	int err;
 
-	if (size) {
+	if (ret && size > 0) {
 		caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
 		goto done;
 	}

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-05-02 21:10             ` Pali Rohár
@ 2019-05-03  8:55               ` Luiz Augusto von Dentz
  2019-05-03  9:18                 ` Pali Rohár
  0 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2019-05-03  8:55 UTC (permalink / raw)
  To: Pali Rohár; +Cc: linux-bluetooth

Hi Pali,

On Fri, May 3, 2019 at 12:10 AM Pali Rohár <pali.rohar@gmail.com> wrote:
>
> On Thursday 02 May 2019 22:58:36 Pali Rohár wrote:
> > On Thursday 02 May 2019 22:53:49 Pali Rohár wrote:
> > > On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> > > > On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > > > > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > > > > Hi Pali,
> > > > > >
> > > > > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > > > > <luiz.dentz@gmail.com> wrote:
> > > > > > >
> > > > > > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > > > > > >
> > > > > > > Endpoint may not be able to select a valid configuration but there could
> > > > > > > be other endpoints available that could be used, so instead of just
> > > > > > > using the first match this collects all the matching endpoints and put
> > > > > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > > > > only failing if none of the available endpoits can select a valid
> > > > > > > configuration.
> > > > > > > ---
> > > > > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > > > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > > > > >
> > > > > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > > > > index a23abdd97..4d378a91a 100644
> > > > > > > --- a/profiles/audio/a2dp.c
> > > > > > > +++ b/profiles/audio/a2dp.c
> > > > > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > > > > >  struct a2dp_setup {
> > > > > > >         struct a2dp_channel *chan;
> > > > > > >         struct avdtp *session;
> > > > > > > +       struct queue *eps;
> > > > > > >         struct a2dp_sep *sep;
> > > > > > >         struct a2dp_remote_sep *rsep;
> > > > > > >         struct avdtp_stream *stream;
> > > > > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > > > > >
> > > > > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > > > > >  {
> > > > > > > -       if (size < 0) {
> > > > > > > -               DBG("Endpoint replied an invalid configuration");
> > > > > > > +       struct avdtp_service_capability *service;
> > > > > > > +       struct avdtp_media_codec_capability *codec;
> > > > > > > +       int err;
> > > > > > > +
> > > > > > > +       if (size) {
> > > > > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > > >                 goto done;
> > > > > > >         }
> > > > > > >
> > > > > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > > > > +       if (!setup->sep) {
> > > > > > > +               error("Unable to select a valid configuration");
> > > > > > > +               queue_destroy(setup->eps, NULL);
> > > > > > > +               goto done;
> > > > > > > +       }
> > > > > > > +
> > > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > > > +
> > > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > > +                                       codec->data,
> > > > > > > +                                       service->length - sizeof(*codec),
> > > > > > > +                                       setup_ref(setup),
> > > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > > > +       if (err == 0)
> > > > > > > +               return;
> > > > > > >
> > > > > > >  done:
> > > > > > >         finalize_select(setup);
> > > > > > >         setup_unref(setup);
> > > > > > >  }
> > > > > > >
> > > > > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > > > > >                                         const char *sender)
> > > > > > >  {
> > > > > > > -       struct a2dp_sep *first = NULL;
> > > > > > >         struct a2dp_channel *chan = find_channel(session);
> > > > > > > +       struct queue *seps = NULL;
> > > > > > >
> > > > > > >         for (; list; list = list->next) {
> > > > > > >                 struct a2dp_sep *sep = list->data;
> > > > > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > > >                 if (!rsep)
> > > > > > >                         continue;
> > > > > > >
> > > > > > > -               /* Locate last used if set */
> > > > > > > -               if (chan->last_used) {
> > > > > > > -                       if (chan->last_used->sep == rsep)
> > > > > > > -                               return sep;
> > > > > > > -                       first = sep;
> > > > > > > -                       continue;
> > > > > > > -               }
> > > > > > > +               if (!seps)
> > > > > > > +                       seps = queue_new();
> > > > > > >
> > > > > > > -               return sep;
> > > > > > > +               /* Prepend last used so it is preferred over others */
> > > > > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > > > > +                       queue_push_head(seps, sep);
> > > > > > > +               else
> > > > > > > +                       queue_push_tail(seps, sep);
> > > > > > >
> > > > > > >         }
> > > > > > >
> > > > > > > -       return first;
> > > > > > > +       return seps;
> > > > > > >  }
> > > > > > >
> > > > > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > > > > >                                         const char *sender)
> > > > > > >  {
> > > > > > >         struct a2dp_server *server;
> > > > > > > -       struct a2dp_sep *sep;
> > > > > > > +       struct queue *seps;
> > > > > > >         GSList *l;
> > > > > > >
> > > > > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > > > > >
> > > > > > >         /* Check sender's seps first */
> > > > > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > > > > -       if (sep != NULL)
> > > > > > > -               return sep;
> > > > > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > > > > +       if (seps != NULL)
> > > > > > > +               return seps;
> > > > > > >
> > > > > > > -       return a2dp_find_sep(session, l, NULL);
> > > > > > > +       return a2dp_find_eps(session, l, NULL);
> > > > > > >  }
> > > > > > >
> > > > > > >  static void store_remote_sep(void *data, void *user_data)
> > > > > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > >  {
> > > > > > >         struct a2dp_setup *setup;
> > > > > > >         struct a2dp_setup_cb *cb_data;
> > > > > > > -       struct a2dp_sep *sep;
> > > > > > > +       struct queue *eps;
> > > > > > >         struct avdtp_service_capability *service;
> > > > > > >         struct avdtp_media_codec_capability *codec;
> > > > > > >         int err;
> > > > > > >
> > > > > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > > > > -       if (!sep) {
> > > > > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > > > > +       if (!eps) {
> > > > > > >                 error("Unable to select SEP");
> > > > > > >                 return 0;
> > > > > > >         }
> > > > > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > >         cb_data->select_cb = cb;
> > > > > > >         cb_data->user_data = user_data;
> > > > > > >
> > > > > > > -       setup->sep = sep;
> > > > > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > > > > +       setup->eps = eps;
> > > > > > > +       setup->sep = queue_pop_head(eps);
> > > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > > >
> > > > > > >         if (setup->rsep == NULL) {
> > > > > > >                 error("Could not find remote sep");
> > > > > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > > > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > > >
> > > > > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > > +                                       codec->data,
> > > > > > >                                         service->length - sizeof(*codec),
> > > > > > >                                         setup_ref(setup),
> > > > > > > -                                       select_cb, sep->user_data);
> > > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > > >         if (err == 0)
> > > > > > >                 return cb_data->id;
> > > > > > >
> > > > > > > --
> > > > > > > 2.20.1
> > > > > >
> > > > > > Le me know if you find any problem with this change, my headsets seems
> > > > > > to always succeed the first try so I cannot really reproduce the
> > > > > > problem.
> > > > >
> > > > > Ok, I will try it in next days. I can register PA endpoints which always
> > > > > "fails" so I can test if this is working correctly as expected.
> > > >
> > > > Nope, this your patch does not work. It cause segfaulting of bluetoothd
> > > > daemon itself. Here is stacktrace of current git master branch (where is
> > > > your patch too):
> > > >
> > > > Program received signal SIGSEGV, Segmentation fault.
> > > > __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > > 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> > > > (gdb) bt
> > > > #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > > #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> > > > #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> > > > #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> > > > #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > > #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > > #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> > > > #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> > > > #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> > > > #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> > > >
> > > > In syslog for bluetoothd is this last line:
> > > > bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> > > >
> > > > pulseaudio refused only one SelectConfiguration call.
> > >
> > > In profiles/audio/a2dp.c is:
> > >
> > > static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > {
> > > ...
> > >     if (size) {
> > >             caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > ...
> > >
> > > and then there is:
> > >
> > > tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
> > >                                                     size_t size)
> > > {
> > > ...
> > >     memcpy(cap->data, caps, size);
> > > ...
> > >
> > > When select_cb() is called with size=-1 then is calls caps_add_codec()
> > > as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
> > > as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
> > > there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
> > > is NULL too. Seems like wrong error handling in select_cb() function.
> >
> > And seems that ret=NULL and size=-1 comes from media.c endpoint_reply()
> > functions. As those values are defaults... Maybe it is a bug here?
>
> Here is patch which fixes above problem with crashing:
>
> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> index d0676b577..b06eafae0 100644
> --- a/profiles/audio/a2dp.c
> +++ b/profiles/audio/a2dp.c
> @@ -1703,7 +1703,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
>         if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
>                 return btd_error_invalid_args(msg);
>
> -       if (parse_properties(&props, &caps, &size) < 0)
> +       if (parse_properties(&props, &caps, &size) < 0 || size <= 0)
>                 return btd_error_invalid_args(msg);
>
>         err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size,
> @@ -2418,7 +2418,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
>         struct avdtp_media_codec_capability *codec;
>         int err;
>
> -       if (size) {
> +       if (ret && size > 0) {
>                 caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
>                 goto done;
>         }

Ive sent a similar fix, there is also a patch to abort if stream
cannot be closed when switching streams. Regarding the endpoint
selection it seems to be working correctly at least it does choose the
endpoints in the order they are registered.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints
  2019-05-03  8:55               ` Luiz Augusto von Dentz
@ 2019-05-03  9:18                 ` Pali Rohár
  0 siblings, 0 replies; 14+ messages in thread
From: Pali Rohár @ 2019-05-03  9:18 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

[-- Attachment #1: Type: text/plain, Size: 15192 bytes --]

On Friday 03 May 2019 11:55:38 Luiz Augusto von Dentz wrote:
> Hi Pali,
> 
> On Fri, May 3, 2019 at 12:10 AM Pali Rohár <pali.rohar@gmail.com> wrote:
> >
> > On Thursday 02 May 2019 22:58:36 Pali Rohár wrote:
> > > On Thursday 02 May 2019 22:53:49 Pali Rohár wrote:
> > > > On Thursday 02 May 2019 22:41:55 Pali Rohár wrote:
> > > > > On Monday 29 April 2019 18:39:16 Pali Rohár wrote:
> > > > > > On Monday 29 April 2019 19:35:24 Luiz Augusto von Dentz wrote:
> > > > > > > Hi Pali,
> > > > > > >
> > > > > > > On Mon, Apr 29, 2019 at 3:03 PM Luiz Augusto von Dentz
> > > > > > > <luiz.dentz@gmail.com> wrote:
> > > > > > > >
> > > > > > > > From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > > > > > > >
> > > > > > > > Endpoint may not be able to select a valid configuration but there could
> > > > > > > > be other endpoints available that could be used, so instead of just
> > > > > > > > using the first match this collects all the matching endpoints and put
> > > > > > > > them into a queue (ordered by priority) then proceed to next endpoint
> > > > > > > > only failing if none of the available endpoits can select a valid
> > > > > > > > configuration.
> > > > > > > > ---
> > > > > > > >  profiles/audio/a2dp.c | 77 ++++++++++++++++++++++++++++---------------
> > > > > > > >  1 file changed, 50 insertions(+), 27 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > > > > > > > index a23abdd97..4d378a91a 100644
> > > > > > > > --- a/profiles/audio/a2dp.c
> > > > > > > > +++ b/profiles/audio/a2dp.c
> > > > > > > > @@ -105,6 +105,7 @@ struct a2dp_setup_cb {
> > > > > > > >  struct a2dp_setup {
> > > > > > > >         struct a2dp_channel *chan;
> > > > > > > >         struct avdtp *session;
> > > > > > > > +       struct queue *eps;
> > > > > > > >         struct a2dp_sep *sep;
> > > > > > > >         struct a2dp_remote_sep *rsep;
> > > > > > > >         struct avdtp_stream *stream;
> > > > > > > > @@ -2406,23 +2407,44 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
> > > > > > > >
> > > > > > > >  static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > > > > > >  {
> > > > > > > > -       if (size < 0) {
> > > > > > > > -               DBG("Endpoint replied an invalid configuration");
> > > > > > > > +       struct avdtp_service_capability *service;
> > > > > > > > +       struct avdtp_media_codec_capability *codec;
> > > > > > > > +       int err;
> > > > > > > > +
> > > > > > > > +       if (size) {
> > > > > > > > +               caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > > > >                 goto done;
> > > > > > > >         }
> > > > > > > >
> > > > > > > > -       caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > > > > > +       setup->sep = queue_pop_head(setup->eps);
> > > > > > > > +       if (!setup->sep) {
> > > > > > > > +               error("Unable to select a valid configuration");
> > > > > > > > +               queue_destroy(setup->eps, NULL);
> > > > > > > > +               goto done;
> > > > > > > > +       }
> > > > > > > > +
> > > > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > > > > +       service = avdtp_get_codec(setup->rsep->sep);
> > > > > > > > +       codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > > > > +
> > > > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > > > +                                       codec->data,
> > > > > > > > +                                       service->length - sizeof(*codec),
> > > > > > > > +                                       setup_ref(setup),
> > > > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > > > > +       if (err == 0)
> > > > > > > > +               return;
> > > > > > > >
> > > > > > > >  done:
> > > > > > > >         finalize_select(setup);
> > > > > > > >         setup_unref(setup);
> > > > > > > >  }
> > > > > > > >
> > > > > > > > -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > > > > +static struct queue *a2dp_find_eps(struct avdtp *session, GSList *list,
> > > > > > > >                                         const char *sender)
> > > > > > > >  {
> > > > > > > > -       struct a2dp_sep *first = NULL;
> > > > > > > >         struct a2dp_channel *chan = find_channel(session);
> > > > > > > > +       struct queue *seps = NULL;
> > > > > > > >
> > > > > > > >         for (; list; list = list->next) {
> > > > > > > >                 struct a2dp_sep *sep = list->data;
> > > > > > > > @@ -2444,26 +2466,25 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
> > > > > > > >                 if (!rsep)
> > > > > > > >                         continue;
> > > > > > > >
> > > > > > > > -               /* Locate last used if set */
> > > > > > > > -               if (chan->last_used) {
> > > > > > > > -                       if (chan->last_used->sep == rsep)
> > > > > > > > -                               return sep;
> > > > > > > > -                       first = sep;
> > > > > > > > -                       continue;
> > > > > > > > -               }
> > > > > > > > +               if (!seps)
> > > > > > > > +                       seps = queue_new();
> > > > > > > >
> > > > > > > > -               return sep;
> > > > > > > > +               /* Prepend last used so it is preferred over others */
> > > > > > > > +               if (chan->last_used && chan->last_used->sep == rsep)
> > > > > > > > +                       queue_push_head(seps, sep);
> > > > > > > > +               else
> > > > > > > > +                       queue_push_tail(seps, sep);
> > > > > > > >
> > > > > > > >         }
> > > > > > > >
> > > > > > > > -       return first;
> > > > > > > > +       return seps;
> > > > > > > >  }
> > > > > > > >
> > > > > > > > -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > > > > +static struct queue *a2dp_select_eps(struct avdtp *session, uint8_t type,
> > > > > > > >                                         const char *sender)
> > > > > > > >  {
> > > > > > > >         struct a2dp_server *server;
> > > > > > > > -       struct a2dp_sep *sep;
> > > > > > > > +       struct queue *seps;
> > > > > > > >         GSList *l;
> > > > > > > >
> > > > > > > >         server = find_server(servers, avdtp_get_adapter(session));
> > > > > > > > @@ -2473,11 +2494,11 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
> > > > > > > >         l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
> > > > > > > >
> > > > > > > >         /* Check sender's seps first */
> > > > > > > > -       sep = a2dp_find_sep(session, l, sender);
> > > > > > > > -       if (sep != NULL)
> > > > > > > > -               return sep;
> > > > > > > > +       seps = a2dp_find_eps(session, l, sender);
> > > > > > > > +       if (seps != NULL)
> > > > > > > > +               return seps;
> > > > > > > >
> > > > > > > > -       return a2dp_find_sep(session, l, NULL);
> > > > > > > > +       return a2dp_find_eps(session, l, NULL);
> > > > > > > >  }
> > > > > > > >
> > > > > > > >  static void store_remote_sep(void *data, void *user_data)
> > > > > > > > @@ -2580,13 +2601,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > > >  {
> > > > > > > >         struct a2dp_setup *setup;
> > > > > > > >         struct a2dp_setup_cb *cb_data;
> > > > > > > > -       struct a2dp_sep *sep;
> > > > > > > > +       struct queue *eps;
> > > > > > > >         struct avdtp_service_capability *service;
> > > > > > > >         struct avdtp_media_codec_capability *codec;
> > > > > > > >         int err;
> > > > > > > >
> > > > > > > > -       sep = a2dp_select_sep(session, type, sender);
> > > > > > > > -       if (!sep) {
> > > > > > > > +       eps = a2dp_select_eps(session, type, sender);
> > > > > > > > +       if (!eps) {
> > > > > > > >                 error("Unable to select SEP");
> > > > > > > >                 return 0;
> > > > > > > >         }
> > > > > > > > @@ -2599,8 +2620,9 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > > >         cb_data->select_cb = cb;
> > > > > > > >         cb_data->user_data = user_data;
> > > > > > > >
> > > > > > > > -       setup->sep = sep;
> > > > > > > > -       setup->rsep = find_remote_sep(setup->chan, sep);
> > > > > > > > +       setup->eps = eps;
> > > > > > > > +       setup->sep = queue_pop_head(eps);
> > > > > > > > +       setup->rsep = find_remote_sep(setup->chan, setup->sep);
> > > > > > > >
> > > > > > > >         if (setup->rsep == NULL) {
> > > > > > > >                 error("Could not find remote sep");
> > > > > > > > @@ -2610,10 +2632,11 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
> > > > > > > >         service = avdtp_get_codec(setup->rsep->sep);
> > > > > > > >         codec = (struct avdtp_media_codec_capability *) service->data;
> > > > > > > >
> > > > > > > > -       err = sep->endpoint->select_configuration(sep, codec->data,
> > > > > > > > +       err = setup->sep->endpoint->select_configuration(setup->sep,
> > > > > > > > +                                       codec->data,
> > > > > > > >                                         service->length - sizeof(*codec),
> > > > > > > >                                         setup_ref(setup),
> > > > > > > > -                                       select_cb, sep->user_data);
> > > > > > > > +                                       select_cb, setup->sep->user_data);
> > > > > > > >         if (err == 0)
> > > > > > > >                 return cb_data->id;
> > > > > > > >
> > > > > > > > --
> > > > > > > > 2.20.1
> > > > > > >
> > > > > > > Le me know if you find any problem with this change, my headsets seems
> > > > > > > to always succeed the first try so I cannot really reproduce the
> > > > > > > problem.
> > > > > >
> > > > > > Ok, I will try it in next days. I can register PA endpoints which always
> > > > > > "fails" so I can test if this is working correctly as expected.
> > > > >
> > > > > Nope, this your patch does not work. It cause segfaulting of bluetoothd
> > > > > daemon itself. Here is stacktrace of current git master branch (where is
> > > > > your patch too):
> > > > >
> > > > > Program received signal SIGSEGV, Segmentation fault.
> > > > > __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > > > 416     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Adresár alebo súbor neexistuje.
> > > > > (gdb) bt
> > > > > #0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
> > > > > #1  0x0000557d2d2f3164 in caps_add_codec (l=0x557d2f1a9708, codec=<optimized out>, caps=0x0, size=18446744073709551615) at profiles/audio/a2dp.c:1519
> > > > > #2  0x0000557d2d2f626a in select_cb (setup=0x557d2f1a96c0, ret=<optimized out>, size=<optimized out>) at profiles/audio/a2dp.c:2422
> > > > > #3  0x0000557d2d2fe016 in endpoint_reply (call=<optimized out>, user_data=0x557d2f187810) at profiles/audio/media.c:316
> > > > > #4  0x00007fd2dc6aa002 in ?? () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > > > #5  0x00007fd2dc6ada5f in dbus_connection_dispatch () from /lib/x86_64-linux-gnu/libdbus-1.so.3
> > > > > #6  0x0000557d2d36f320 in message_dispatch (data=0x557d2f184560) at gdbus/mainloop.c:72
> > > > > #7  0x00007fd2dc9326aa in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > > #8  0x00007fd2dc932a60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > > #9  0x00007fd2dc932d82 in g_main_loop_run () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
> > > > > #10 0x0000557d2d384a45 in mainloop_run () at src/shared/mainloop-glib.c:79
> > > > > #11 0x0000557d2d384e0f in mainloop_run_with_signal (func=<optimized out>, user_data=0x0) at src/shared/mainloop-notify.c:201
> > > > > #12 0x0000557d2d2eb0a8 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:729
> > > > >
> > > > > In syslog for bluetoothd is this last line:
> > > > > bluetoothd[12605]: Endpoint replied with an error: org.bluez.Error.InvalidArguments
> > > > >
> > > > > pulseaudio refused only one SelectConfiguration call.
> > > >
> > > > In profiles/audio/a2dp.c is:
> > > >
> > > > static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> > > > {
> > > > ...
> > > >     if (size) {
> > > >             caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> > > > ...
> > > >
> > > > and then there is:
> > > >
> > > > tatic void caps_add_codec(GSList **l, uint8_t codec, uint8_t *caps,
> > > >                                                     size_t size)
> > > > {
> > > > ...
> > > >     memcpy(cap->data, caps, size);
> > > > ...
> > > >
> > > > When select_cb() is called with size=-1 then is calls caps_add_codec()
> > > > as size is non-zero and cast signed 32bit int -1 to unsigned 64bit int
> > > > as value 2^64-1. Later is called memcpy with size 2^64-1 and crash is
> > > > there. Moreover caps is NULLL pointer, therefore void *ret in select_cb
> > > > is NULL too. Seems like wrong error handling in select_cb() function.
> > >
> > > And seems that ret=NULL and size=-1 comes from media.c endpoint_reply()
> > > functions. As those values are defaults... Maybe it is a bug here?
> >
> > Here is patch which fixes above problem with crashing:
> >
> > diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
> > index d0676b577..b06eafae0 100644
> > --- a/profiles/audio/a2dp.c
> > +++ b/profiles/audio/a2dp.c
> > @@ -1703,7 +1703,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> >         if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
> >                 return btd_error_invalid_args(msg);
> >
> > -       if (parse_properties(&props, &caps, &size) < 0)
> > +       if (parse_properties(&props, &caps, &size) < 0 || size <= 0)
> >                 return btd_error_invalid_args(msg);
> >
> >         err = a2dp_reconfig(chan, sender, lsep, rsep, caps, size,
> > @@ -2418,7 +2418,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
> >         struct avdtp_media_codec_capability *codec;
> >         int err;
> >
> > -       if (size) {
> > +       if (ret && size > 0) {
> >                 caps_add_codec(&setup->caps, setup->sep->codec, ret, size);
> >                 goto done;
> >         }
> 
> Ive sent a similar fix, there is also a patch to abort if stream
> cannot be closed when switching streams. Regarding the endpoint
> selection it seems to be working correctly at least it does choose the
> endpoints in the order they are registered.

Ok, seems that crashing with those 3 patches on ML is fixed.

-- 
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2019-05-03  9:19 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-29 12:02 [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz
2019-04-29 12:02 ` [PATCH v3 2/3] doc/settings-storage: Document LastUsed Endpoints entry Luiz Augusto von Dentz
2019-04-29 12:02 ` [PATCH v3 3/3] a2dp: Fix not calling SelectConfiguration on other available endpoints Luiz Augusto von Dentz
2019-04-29 16:35   ` Luiz Augusto von Dentz
2019-04-29 16:39     ` Pali Rohár
2019-04-30 10:24       ` Sebastian Reichel
2019-04-30 10:26         ` Pali Rohár
2019-05-02 20:41       ` Pali Rohár
2019-05-02 20:53         ` Pali Rohár
2019-05-02 20:58           ` Pali Rohár
2019-05-02 21:10             ` Pali Rohár
2019-05-03  8:55               ` Luiz Augusto von Dentz
2019-05-03  9:18                 ` Pali Rohár
2019-04-30 12:41 ` [PATCH v3 1/3] a2dp: Store last used endpoint Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.