2022-11-29 20:47:55

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 1/5] shared/bap: Fix not reading all instances of PAC Sinks/Sources

From: Luiz Augusto von Dentz <[email protected]>

Both PAC Sink and Source are allowed to have multiple instances:

- The server wanted to support a smaller maximum transmission unit
(ATT_MTU, as defined in Volume 3, Part F, Section 3.2.8 in [2]) size.
Exposing all supported PAC records in a single Sink PAC characteristic
would require the server to increase its supported Maximum
Transmission Unit (MTU) size to a value the server considered
excessive.
- The server wanted to expose support for proprietary audio
capabilities (such as vendor-specific audio codecs, as denoted by the
Codec_ID parameter value) separately from support for
non-vendor-specific audio capabilities and used separate Sink PAC
characteristics to expose such support.
- The server wanted to minimize the amount of data to be transferred,
when sending notifications to a client that the Sink PAC
characteristic value changed, by exposing the audio capabilities
likely to change quicker than others in separate Sink PAC
characteristics.
---
src/shared/bap.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 21aa8aa6c5ca..7a24824a71fc 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -2908,10 +2908,12 @@ static void foreach_pacs_char(struct gatt_db_attribute *attr, void *user_data)
DBG(bap, "Sink PAC found: handle 0x%04x", value_handle);

pacs = bap_get_pacs(bap);
- if (!pacs || pacs->sink)
+ if (!pacs)
return;

- pacs->sink = attr;
+ if (!pacs->sink)
+ pacs->sink = attr;
+
bap_read_value(bap, value_handle, read_sink_pac, bap);
}

@@ -2919,10 +2921,12 @@ static void foreach_pacs_char(struct gatt_db_attribute *attr, void *user_data)
DBG(bap, "Source PAC found: handle 0x%04x", value_handle);

pacs = bap_get_pacs(bap);
- if (!pacs || pacs->source)
+ if (!pacs)
return;

- pacs->source = attr;
+ if (!pacs->source)
+ pacs->source = attr;
+
bap_read_value(bap, value_handle, read_source_pac, NULL);
}

--
2.37.3


2022-11-29 20:47:56

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 2/5] shared/bap: Fix initiating QoS and Enable procedures as server

From: Luiz Augusto von Dentz <[email protected]>

According to Table 3.2: ASE state machine transition these procedures
can only be initated by clients.
---
src/shared/bap.c | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 7a24824a71fc..f4812a4b9f51 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -4166,14 +4166,12 @@ unsigned int bt_bap_stream_qos(struct bt_bap_stream *stream,
struct bt_ascs_qos qos;
struct bt_bap_req *req;

- if (!bap_stream_valid(stream))
+ /* Table 3.2: ASE state machine transition
+ * Initiating device - client Only
+ */
+ if (!bap_stream_valid(stream) || !stream->client)
return 0;

- if (!stream->client) {
- stream_qos(stream, data, NULL);
- return 0;
- }
-
memset(&qos, 0, sizeof(qos));

/* TODO: Figure out how to pass these values around */
@@ -4259,14 +4257,12 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
{
int ret;

- if (!bap_stream_valid(stream))
+ /* Table 3.2: ASE state machine transition
+ * Initiating device - client Only
+ */
+ if (!bap_stream_valid(stream) || !stream->client)
return 0;

- if (!stream->client) {
- stream_enable(stream, metadata, NULL);
- return 0;
- }
-
ret = bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, func,
user_data);
if (!ret || !enable_links)
--
2.37.3

2022-11-29 20:47:56

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 3/5] client/player: Add support for custom preset

From: Luiz Augusto von Dentz <[email protected]>

This adds support for a custom preset which asks the user to enter its
configuration:

[bluetooth]# endpoint.presets 00002bc9-0000-1000-8000-00805f9b34fb custom
[Codec] Enter frequency: 0x08
[Codec] Enter frame duration: 0x01
[Codec] Enter channel allocation: 0x00000003
[Codec] Enter frame length: 100
[QoS] Enter SDU Interval: 10000
[QoS] Enter Framing: 0x00
[QoS] Enter PHY: 2M
[QoS] Enter Max SDU: 100
[QoS] Enter RTN: 2
[QoS] Enter Max Transport Latency: 20
[QoS] Enter Presentation Delay: 10000
---
client/player.c | 305 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 261 insertions(+), 44 deletions(-)

diff --git a/client/player.c b/client/player.c
index 51c10f8e0a23..952b01e8236b 100644
--- a/client/player.c
+++ b/client/player.c
@@ -1231,7 +1231,6 @@ struct codec_preset {
const char *name;
const struct iovec data;
const struct codec_qos qos;
- bool is_default;
uint8_t latency;
};

@@ -1241,13 +1240,6 @@ struct codec_preset {
.data = _data, \
}

-#define SBC_DEFAULT_PRESET(_name, _data) \
- { \
- .name = _name, \
- .data = _data, \
- .is_default = true, \
- }
-
static struct codec_preset sbc_presets[] = {
/* Table 4.7: Recommended sets of SBC parameters in the SRC device
* Other settings: Block length = 16, Allocation method = Loudness,
@@ -1268,7 +1260,7 @@ static struct codec_preset sbc_presets[] = {
CODEC_DATA(0x28, 0x15, 2, SBC_BITPOOL_HQ_MONO_44100)),
SBC_PRESET("HQ_MONO_48",
CODEC_DATA(0x18, 0x15, 2, SBC_BITPOOL_HQ_MONO_48000)),
- SBC_DEFAULT_PRESET("HQ_STEREO_44_1",
+ SBC_PRESET("HQ_STEREO_44_1",
CODEC_DATA(0x21, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_44100)),
SBC_PRESET("HQ_STEREO_48",
CODEC_DATA(0x11, 0x15, 2, SBC_BITPOOL_HQ_JOINT_STEREO_48000)),
@@ -1379,15 +1371,6 @@ static struct codec_preset sbc_presets[] = {
.latency = 0x03, \
}

-#define LC3_DEFAULT_PRESET(_name, _data, _qos) \
- { \
- .name = _name, \
- .data = _data, \
- .is_default = true, \
- .qos = _qos, \
- .latency = 0x02, \
- }
-
static struct codec_preset lc3_presets[] = {
/* Table 4.43: QoS configuration support setting requirements */
LC3_PRESET("8_1_1",
@@ -1399,7 +1382,7 @@ static struct codec_preset lc3_presets[] = {
LC3_PRESET("16_1_1",
LC3_PRESET_16KHZ(LC3_CONFIG_DURATION_7_5, 30u),
LC3_7_5_UNFRAMED(30u, 2u, 8u, 40000u)),
- LC3_DEFAULT_PRESET("16_2_1",
+ LC3_PRESET("16_2_1",
LC3_PRESET_16KHZ(LC3_CONFIG_DURATION_10, 40u),
LC3_10_UNFRAMED(40u, 2u, 10u, 40000u)),
LC3_PRESET("24_1_1",
@@ -1465,22 +1448,26 @@ static struct codec_preset lc3_presets[] = {
LC3_10_UNFRAMED(155u, 23u, 60u, 40000u)),
};

-#define PRESET(_uuid, _presets) \
+#define PRESET(_uuid, _presets, _default_index) \
{ \
.uuid = _uuid, \
+ .custom = { .name = "custom" }, \
+ .default_preset = &_presets[_default_index], \
.presets = _presets, \
.num_presets = ARRAY_SIZE(_presets), \
}

-static const struct preset {
+static struct preset {
const char *uuid;
+ struct codec_preset custom;
+ struct codec_preset *default_preset;
struct codec_preset *presets;
size_t num_presets;
} presets[] = {
- PRESET(A2DP_SOURCE_UUID, sbc_presets),
- PRESET(A2DP_SINK_UUID, sbc_presets),
- PRESET(PAC_SINK_UUID, lc3_presets),
- PRESET(PAC_SOURCE_UUID, lc3_presets),
+ PRESET(A2DP_SOURCE_UUID, sbc_presets, 6),
+ PRESET(A2DP_SINK_UUID, sbc_presets, 6),
+ PRESET(PAC_SINK_UUID, lc3_presets, 3),
+ PRESET(PAC_SOURCE_UUID, lc3_presets, 3),
};

static struct codec_preset *find_preset(const char *uuid, const char *name)
@@ -1488,20 +1475,22 @@ static struct codec_preset *find_preset(const char *uuid, const char *name)
size_t i;

for (i = 0; i < ARRAY_SIZE(presets); i++) {
- const struct preset *preset = &presets[i];
+ struct preset *preset = &presets[i];

if (!strcasecmp(preset->uuid, uuid)) {
size_t j;

+ if (!name)
+ return preset->default_preset;
+ else if (!strcmp(name, "custom"))
+ return &preset->custom;
+
for (j = 0; j < preset->num_presets; j++) {
struct codec_preset *p;

p = &preset->presets[j];

- if (!name) {
- if (p->is_default)
- return p;
- } else if (!strcmp(p->name, name))
+ if (!strcmp(p->name, name))
return p;
}
}
@@ -1724,10 +1713,11 @@ done:
static struct iovec *iov_append(struct iovec **iov, const void *data,
size_t len)
{
- if (!*iov) {
+ if (!*iov)
*iov = new0(struct iovec, 1);
+
+ if (!((*iov)->iov_base))
(*iov)->iov_base = new0(uint8_t, UINT8_MAX);
- }

if (data && len) {
memcpy((*iov)->iov_base + (*iov)->iov_len, data, len);
@@ -2348,6 +2338,224 @@ fail:
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

+static void custom_delay(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->delay = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void custom_latency(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->latency = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_prompt_input("QoS", "Enter Presentation Delay:",
+ custom_delay, user_data);
+}
+
+static void custom_rtn(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->rtn = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_prompt_input("QoS", "Enter Max Transport Latency:",
+ custom_latency, user_data);
+}
+
+static void custom_sdu(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->sdu = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_prompt_input("QoS", "Enter RTN:", custom_rtn, user_data);
+}
+
+static void custom_phy(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+
+ if (!strcmp(input, "1M"))
+ qos->phy = "1M";
+ else if (!strcmp(input, "2M"))
+ qos->phy = "2M";
+ else {
+ char *endptr = NULL;
+ uint8_t phy = strtol(input, &endptr, 0);
+
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ switch (phy) {
+ case 0x01:
+ qos->phy = "1M";
+ break;
+ case 0x02:
+ qos->phy = "2M";
+ break;
+ default:
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ }
+
+ bt_shell_prompt_input("QoS", "Enter Max SDU:", custom_sdu, user_data);
+}
+
+static void custom_framing(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->framing = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_prompt_input("QoS", "Enter PHY:", custom_phy, user_data);
+}
+
+static void custom_interval(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct codec_qos *qos = (void *)&p->qos;
+ char *endptr = NULL;
+
+ qos->interval = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_prompt_input("QoS", "Enter Framing:",
+ custom_framing, user_data);
+}
+
+static void custom_length(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct iovec *iov = (void *)&p->data;
+ uint8_t ltv[4] = { 0x03, LC3_CONFIG_FRAME_LEN };
+ uint16_t len;
+ char *endptr = NULL;
+
+ len = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ ltv[2] = len;
+ ltv[3] = len >> 8;
+
+ iov_append(&iov, ltv, sizeof(ltv));
+
+ bt_shell_prompt_input("QoS", "Enter SDU Interval:",
+ custom_interval, user_data);
+}
+
+static void custom_location(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct iovec *iov = (void *)&p->data;
+ uint32_t location;
+ char *endptr = NULL;
+
+ location = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ /* Only add Channel Allocation if set */
+ if (location) {
+ uint8_t ltv[6] = { 0x05, LC3_CONFIG_CHAN_ALLOC };
+
+ location = cpu_to_le32(location);
+ memcpy(&ltv[2], &location, sizeof(location));
+ iov_append(&iov, ltv, sizeof(ltv));
+ }
+
+ bt_shell_prompt_input("Codec", "Enter frame length:",
+ custom_length, user_data);
+}
+
+static void custom_duration(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct iovec *iov = (void *)&p->data;
+ uint8_t ltv[3] = { 0x02, LC3_CONFIG_DURATION, 0x00 };
+ char *endptr = NULL;
+
+ ltv[2] = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ iov_append(&iov, ltv, sizeof(ltv));
+
+ bt_shell_prompt_input("Codec", "Enter channel allocation:",
+ custom_location, user_data);
+}
+
+static void custom_frequency(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+ struct iovec *iov = (void *)&p->data;
+ uint8_t ltv[3] = { 0x02, LC3_CONFIG_FREQ, 0x00 };
+ char *endptr = NULL;
+
+ ltv[2] = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ /* Reset iov to start over the codec configuration */
+ free(iov->iov_base);
+ iov->iov_base = NULL;
+ iov->iov_len = 0;
+ iov_append(&iov, ltv, sizeof(ltv));
+
+ bt_shell_prompt_input("Codec", "Enter frame duration:",
+ custom_duration, user_data);
+}
+
static void cmd_presets_endpoint(int argc, char *argv[])
{
size_t i;
@@ -2359,32 +2567,41 @@ static void cmd_presets_endpoint(int argc, char *argv[])
bt_shell_printf("Preset %s not found\n", argv[2]);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
-
- default_preset->is_default = true;
}

for (i = 0; i < ARRAY_SIZE(presets); i++) {
- const struct preset *preset = &presets[i];
+ struct preset *preset = &presets[i];

if (!strcasecmp(preset->uuid, argv[1])) {
size_t j;
+ struct codec_preset *p;
+
+ if (default_preset) {
+ preset->default_preset = default_preset;
+ break;
+ }
+
+ p = &preset->custom;
+
+ bt_shell_printf("%s%s\n", p == preset->default_preset ?
+ "*" : "", p->name);

for (j = 0; j < preset->num_presets; j++) {
- struct codec_preset *p;
-
p = &preset->presets[j];

- if (default_preset && p != default_preset)
- p->is_default = false;
-
- if (p->is_default)
- bt_shell_printf("*%s\n", p->name);
- else
- bt_shell_printf("%s\n", p->name);
+ bt_shell_printf("%s%s\n",
+ p == preset->default_preset ?
+ "*" : "", p->name);
}
}
}

+ if (default_preset && !strcmp(default_preset->name, "custom")) {
+ bt_shell_prompt_input("Codec", "Enter frequency:",
+ custom_frequency, default_preset);
+ return;
+ }
+
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}

--
2.37.3

2022-11-29 20:48:09

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 5/5] media: Fix crash when transport configuration changes

From: Luiz Augusto von Dentz <[email protected]>

In case of BAP the same transport may be reconfigured multiple times
which means it would appears multiple times on endpoint->transports
leading to a crash when disconnecting as the code would attempt to
destroy the same object multiple times.
---
profiles/audio/media.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index c9328ab9bd6e..6947cf96392e 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1057,6 +1057,8 @@ static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,

path = media_transport_get_path(transport);
bt_bap_stream_set_user_data(stream, (void *)path);
+ endpoint->transports = g_slist_append(endpoint->transports,
+ transport);
}

msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
@@ -1064,7 +1066,7 @@ static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,
"SetConfiguration");
if (msg == NULL) {
error("Couldn't allocate D-Bus message");
- media_transport_destroy(transport);
+ endpoint_remove_transport(endpoint, transport);
return FALSE;
}

@@ -1073,8 +1075,6 @@ static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,
data->cb = cb;
data->user_data = user_data;

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

path = media_transport_get_path(transport);
--
2.37.3

2022-11-29 23:14:50

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,1/5] shared/bap: Fix not reading all instances of PAC Sinks/Sources

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=700234

---Test result---

Test Summary:
CheckPatch PASS 2.95 seconds
GitLint PASS 1.87 seconds
BuildEll PASS 33.94 seconds
BluezMake PASS 1007.00 seconds
MakeCheck PASS 13.20 seconds
MakeDistcheck PASS 183.09 seconds
CheckValgrind PASS 297.60 seconds
bluezmakeextell PASS 116.65 seconds
IncrementalBuild PASS 4089.08 seconds
ScanBuild PASS 1243.14 seconds



---
Regards,
Linux Bluetooth