2022-12-02 01:06:29

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 1/8] 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-12-02 01:07:08

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 7/8] bap: Fix not waiting Endpoint.SelectProperties

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

If there are multiple Endpoint.SelectProperties wait them to complete
before attempting to proceed to configure a stream otherwise streams
may not be linked properly and may end up creating multiple CIS
instead.
---
profiles/audio/bap.c | 58 +++++++++++++++++++++++++++++++-----------
profiles/audio/media.c | 27 ++++++++------------
2 files changed, 54 insertions(+), 31 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 9cee9fd030da..f28843ae6b38 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -83,6 +83,7 @@ struct bap_data {
struct queue *snks;
struct queue *streams;
GIOChannel *listen_io;
+ int selecting;
};

static struct queue *sessions;
@@ -503,7 +504,8 @@ static void ep_free(void *data)

bap_io_close(ep);

- free(ep->caps);
+ util_iov_free(ep->caps, 1);
+ util_iov_free(ep->metadata, 1);
free(ep->path);
free(ep);
}
@@ -566,20 +568,14 @@ static struct bap_ep *ep_register(struct btd_service *service,
return ep;
}

-static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps,
- struct iovec *metadata, struct bt_bap_qos *qos,
- void *user_data)
+static void bap_config(void *data, void *user_data)
{
- struct bap_ep *ep = user_data;
+ struct bap_ep *ep = data;

- if (err) {
- error("err %d", err);
+ DBG("ep %p caps %p metadata %p", ep, ep->caps, ep->metadata);
+
+ if (!ep->caps)
return;
- }
-
- ep->caps = caps;
- ep->metadata = metadata;
- ep->qos = *qos;

/* TODO: Check if stream capabilities match add support for Latency
* and PHY.
@@ -594,13 +590,43 @@ static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps,

if (!ep->stream) {
DBG("Unable to config stream");
- free(ep->caps);
+ util_iov_free(ep->caps, 1);
ep->caps = NULL;
+ util_iov_free(ep->metadata, 1);
+ ep->metadata = NULL;
}

bt_bap_stream_set_user_data(ep->stream, ep->path);
}

+static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps,
+ struct iovec *metadata, struct bt_bap_qos *qos,
+ void *user_data)
+{
+ struct bap_ep *ep = user_data;
+
+ if (err) {
+ error("err %d", err);
+ return;
+ }
+
+ ep->caps = util_iov_dup(caps, 1);
+
+ if (metadata && metadata->iov_base && metadata->iov_len)
+ ep->metadata = util_iov_dup(metadata, 1);
+
+ ep->qos = *qos;
+
+ DBG("selecting %d", ep->data->selecting);
+ ep->data->selecting--;
+
+ if (ep->data->selecting)
+ return;
+
+ queue_foreach(ep->data->srcs, bap_config, NULL);
+ queue_foreach(ep->data->snks, bap_config, NULL);
+}
+
static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
void *user_data)
{
@@ -616,8 +642,10 @@ static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
}

/* TODO: Cache LRU? */
- if (btd_service_is_initiator(service))
- bt_bap_select(lpac, rpac, select_cb, ep);
+ if (btd_service_is_initiator(service)) {
+ if (!bt_bap_select(lpac, rpac, select_cb, ep))
+ ep->data->selecting++;
+ }

return true;
}
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 6947cf96392e..fbb350889564 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -725,24 +725,21 @@ struct pac_select_data {
void *user_data;
};

-static int parse_array(DBusMessageIter *iter, struct iovec **iov)
+static int parse_array(DBusMessageIter *iter, struct iovec *iov)
{
DBusMessageIter array;

if (!iov)
return 0;

- if (!(*iov))
- *iov = new0(struct iovec, 1);
-
dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &(*iov)->iov_base,
- (int *)&(*iov)->iov_len);
+ dbus_message_iter_get_fixed_array(&array, &iov->iov_base,
+ (int *)&iov->iov_len);
return 0;
}

-static int parse_select_properties(DBusMessageIter *props, struct iovec **caps,
- struct iovec **metadata,
+static int parse_select_properties(DBusMessageIter *props, struct iovec *caps,
+ struct iovec *metadata,
struct bt_bap_qos *qos)
{
const char *key;
@@ -845,11 +842,6 @@ static int parse_select_properties(DBusMessageIter *props, struct iovec **caps,
fail:
DBG("Failed parsing %s", key);

- if (*caps) {
- free(*caps);
- *caps = NULL;
- }
-
return -EINVAL;
}

@@ -859,7 +851,7 @@ static void pac_select_cb(struct media_endpoint *endpoint, void *ret, int size,
struct pac_select_data *data = user_data;
DBusMessageIter *iter = ret;
int err;
- struct iovec *caps = NULL, *metadata = NULL;
+ struct iovec caps, meta;
struct bt_bap_qos qos;

if (!ret) {
@@ -881,12 +873,15 @@ static void pac_select_cb(struct media_endpoint *endpoint, void *ret, int size,
qos.cig_id = BT_ISO_QOS_CIG_UNSET;
qos.cis_id = BT_ISO_QOS_CIS_UNSET;

- err = parse_select_properties(iter, &caps, &metadata, &qos);
+ memset(&caps, 0, sizeof(caps));
+ memset(&meta, 0, sizeof(meta));
+
+ err = parse_select_properties(iter, &caps, &meta, &qos);
if (err < 0)
DBG("Unable to parse properties");

done:
- data->cb(data->pac, err, caps, metadata, &qos, data->user_data);
+ data->cb(data->pac, err, &caps, &meta, &qos, data->user_data);
}

static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
--
2.37.3

2022-12-02 01:07:10

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 3/8] 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 (Khz): 48
[Codec] Enter frame duration (ms): 10
[Codec] Enter channel allocation: 0x000000003
[Codec] Enter frame length: 100
[QoS] Enter Target Latency (Low, Balance, High): Low
[QoS] Enter SDU Interval (us): 10000
[QoS] Enter Framing (Unframed, Framed): Unframed
[QoS] Enter PHY (1M, 2M): 2M
[QoS] Enter Max SDU: 200
[QoS] Enter RTN: 3
[QoS] Enter Max Transport Latency (ms): 20
[QoS] Enter Presentation Delay (us): 10000
---
client/player.c | 414 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 366 insertions(+), 48 deletions(-)

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

#define SBC_PRESET(_name, _data) \
@@ -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)),
@@ -1368,7 +1360,7 @@ static struct codec_preset sbc_presets[] = {
.name = _name, \
.data = _data, \
.qos = _qos, \
- .latency = 0x02, \
+ .target_latency = 0x02, \
}

#define LC3_PRESET_HR(_name, _data, _qos) \
@@ -1376,16 +1368,7 @@ static struct codec_preset sbc_presets[] = {
.name = _name, \
.data = _data, \
.qos = _qos, \
- .latency = 0x03, \
- }
-
-#define LC3_DEFAULT_PRESET(_name, _data, _qos) \
- { \
- .name = _name, \
- .data = _data, \
- .is_default = true, \
- .qos = _qos, \
- .latency = 0x02, \
+ .target_latency = 0x03, \
}

static struct codec_preset lc3_presets[] = {
@@ -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);
@@ -1757,7 +1747,7 @@ static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep,

/* Copy capabilities */
iov_append(&cfg->caps, preset->data.iov_base, preset->data.iov_len);
- cfg->target_latency = preset->latency;
+ cfg->target_latency = preset->target_latency;

if (preset->qos.phy)
/* Set QoS parameters */
@@ -2348,6 +2338,325 @@ 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 (us):",
+ 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 (ms):",
+ 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;
+
+ if (!strcasecmp(input, "Unframed"))
+ qos->framing = 0x00;
+ else if (!strcasecmp(input, "Framed"))
+ qos->framing = 0x01;
+ else {
+ 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 (1M, 2M):", 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 (Unframed, Framed):",
+ custom_framing, user_data);
+}
+
+static void custom_target_latency(const char *input, void *user_data)
+{
+ struct codec_preset *p = user_data;
+
+ if (!strcasecmp(input, "Low"))
+ p->target_latency = 0x01;
+ else if (!strcasecmp(input, "Balance"))
+ p->target_latency = 0x02;
+ else if (!strcasecmp(input, "High"))
+ p->target_latency = 0x02;
+ else {
+ char *endptr = NULL;
+
+ p->target_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 SDU Interval (us):",
+ custom_interval, 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 Target Latency "
+ "(Low, Balance, High):",
+ custom_target_latency, 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 uint8_t val2duration(uint32_t val)
+{
+ switch (val) {
+ case 7:
+ return 0x00;
+ case 10:
+ return 0x01;
+ default:
+ return 0xff;
+ }
+}
+
+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;
+ uint32_t val;
+
+ val = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (strncmp(input, "0x", 2))
+ ltv[2] = val2duration(val);
+ else
+ ltv[2] = val;
+
+ if (ltv[2] == 0xff) {
+ 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 uint8_t val2freq(uint32_t val)
+{
+ switch (val) {
+ case 8:
+ return 0x01;
+ case 11:
+ return 0x02;
+ case 16:
+ return 0x03;
+ case 22:
+ return 0x04;
+ case 24:
+ return 0x05;
+ case 32:
+ return 0x06;
+ case 44:
+ return 0x07;
+ case 48:
+ return 0x08;
+ case 88:
+ return 0x09;
+ case 96:
+ return 0x0a;
+ case 174:
+ return 0x0b;
+ case 192:
+ return 0x0c;
+ case 384:
+ return 0x0d;
+ default:
+ return 0x00;
+ }
+}
+
+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 };
+ uint32_t val;
+ char *endptr = NULL;
+
+ val = strtol(input, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (strncmp(input, "0x", 2))
+ ltv[2] = val2freq(val);
+ else
+ ltv[2] = val;
+
+ if (!ltv[2]) {
+ 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 (ms):",
+ custom_duration, user_data);
+}
+
static void cmd_presets_endpoint(int argc, char *argv[])
{
size_t i;
@@ -2359,32 +2668,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 (Khz):",
+ custom_frequency, default_preset);
+ return;
+ }
+
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}

--
2.37.3

2022-12-02 01:07:24

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 6/8] shared/bap: Merge PAC records of the same type/codec

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

This attempts to merge PAC records which contain the same type and
codec to simplify the matching with client endpoints so all
capabilities and metadata are match at once instead of for each PAC
record.
---
src/shared/bap.c | 65 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 62 insertions(+), 3 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index f4812a4b9f51..59ef81d11882 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -262,6 +262,12 @@ static bool bap_db_match(const void *data, const void *match_data)
return (bdb->db == db);
}

+static void *iov_append(struct iovec *iov, size_t len, const void *d)
+{
+ iov->iov_base = realloc(iov->iov_base, iov->iov_len + len);
+ return util_iov_push_mem(iov, len, d);
+}
+
unsigned int bt_bap_pac_register(bt_bap_pac_func_t added,
bt_bap_pac_func_t removed, void *user_data,
bt_bap_destroy_func_t destroy)
@@ -2236,6 +2242,52 @@ static struct bt_ascs *bap_get_ascs(struct bt_bap *bap)
return bap->rdb->ascs;
}

+static bool match_codec(const void *data, const void *user_data)
+{
+ const struct bt_bap_pac *pac = data;
+ const struct bt_bap_codec *codec = user_data;
+
+ return bap_codec_equal(&pac->codec, codec);
+}
+
+static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type,
+ struct bt_bap_codec *codec)
+{
+ switch (type) {
+ case BT_BAP_SOURCE:
+ return queue_find(bdb->sources, match_codec, codec);
+ case BT_BAP_SINK:
+ return queue_find(bdb->sinks, match_codec, codec);
+ }
+
+ return NULL;
+}
+
+static void *ltv_merge(struct iovec *data, struct iovec *cont)
+{
+ uint8_t delimiter = 0;
+
+ iov_append(data, sizeof(delimiter), &delimiter);
+
+ return iov_append(data, cont->iov_len, cont->iov_base);
+}
+
+static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
+ struct iovec *metadata)
+{
+ /* Merge data into existing record */
+ if (pac->data)
+ ltv_merge(pac->data, data);
+ else
+ pac->data = util_iov_dup(data, 1);
+
+ /* Merge metadata into existing record */
+ if (pac->metadata)
+ ltv_merge(pac->metadata, metadata);
+ else
+ pac->metadata = util_iov_dup(metadata, 1);
+}
+
static struct bt_bap_pac *bap_pac_new(struct bt_bap_db *bdb, const char *name,
uint8_t type,
struct bt_bap_codec *codec,
@@ -2742,14 +2794,21 @@ static void bap_parse_pacs(struct bt_bap *bap, uint8_t type,

util_iov_pull_mem(&iov, meta->len);

+ DBG(bap, "PAC #%u: type %u codec 0x%02x cc_len %u meta_len %u",
+ i, type, p->codec.id, p->cc_len, meta->len);
+
+ /* Check if there is already a PAC record for the codec */
+ pac = bap_pac_find(bap->rdb, type, &p->codec);
+ if (pac) {
+ bap_pac_merge(pac, &data, &metadata);
+ continue;
+ }
+
pac = bap_pac_new(bap->rdb, NULL, type, &p->codec, NULL, &data,
&metadata);
if (!pac)
continue;

- DBG(bap, "PAC #%u: type %u codec 0x%02x cc_len %u meta_len %u",
- i, type, p->codec.id, p->cc_len, meta->len);
-
queue_push_tail(queue, pac);
}
}
--
2.37.3

2022-12-02 01:07:44

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 2/8] 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-12-02 01:11:16

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 5/8] 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-12-02 04:14:32

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,v2,1/8] 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=701027

---Test result---

Test Summary:
CheckPatch PASS 4.54 seconds
GitLint PASS 2.60 seconds
BuildEll PASS 32.50 seconds
BluezMake PASS 1144.47 seconds
MakeCheck PASS 12.70 seconds
MakeDistcheck PASS 173.32 seconds
CheckValgrind PASS 287.56 seconds
bluezmakeextell PASS 114.48 seconds
IncrementalBuild PASS 7628.72 seconds
ScanBuild PASS 1199.21 seconds



---
Regards,
Linux Bluetooth