From e5229e5c435fcb5baf4b8a4aa16ab95ced4f3768 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 18 Aug 2025 15:08:54 +0200 Subject: [PATCH] Fix problem with menu handling Resolves: RHEL-103965 --- 5.77-devel.patch | 3923 ---------------------------------------------- 5.83-fixes.patch | 174 ++ bluez.spec | 7 +- 3 files changed, 180 insertions(+), 3924 deletions(-) delete mode 100644 5.77-devel.patch create mode 100644 5.83-fixes.patch diff --git a/5.77-devel.patch b/5.77-devel.patch deleted file mode 100644 index e79bc77..0000000 --- a/5.77-devel.patch +++ /dev/null @@ -1,3923 +0,0 @@ -From f00d5546c9e989dd68ce0de0190cd0e043b0f1f5 Mon Sep 17 00:00:00 2001 -From: Arjan Opmeer -Date: Tue, 9 Jul 2024 13:55:41 +0200 -Subject: [PATCH 01/46] tools/btmgmt: Fix --index option for non-interactive - mode - -In non-interactive mode the --index option does not work because the -call to mgmt_set_index() is made after bt_shell_attach(). - -Fixes: https://github.com/bluez/bluez/issues/893 ---- - tools/btmgmt.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/btmgmt.c b/tools/btmgmt.c -index 9b7f851bd8c6..436c2bb21f10 100644 ---- a/tools/btmgmt.c -+++ b/tools/btmgmt.c -@@ -51,8 +51,8 @@ int main(int argc, char *argv[]) - return EXIT_FAILURE; - } - -- bt_shell_attach(fileno(stdin)); - mgmt_set_index(index_option); -+ bt_shell_attach(fileno(stdin)); - status = bt_shell_run(); - - mgmt_remove_submenu(); --- -2.45.2 - - -From 66a76c268d05583c2396054e3f63a19c6f18bb9c Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Wed, 3 Jul 2024 17:58:39 +0300 -Subject: [PATCH 02/46] doc: Add initial MediaAssistant rst - -This adds initial documentation for the MediaAssistant D-Bus API, to -be used by a Broadcast Assistant application to interract with the -BlueZ implementation (BASS Client). ---- - Makefile.am | 6 +-- - doc/org.bluez.MediaAssistant.rst | 74 ++++++++++++++++++++++++++++++++ - 2 files changed, 77 insertions(+), 3 deletions(-) - create mode 100644 doc/org.bluez.MediaAssistant.rst - -diff --git a/Makefile.am b/Makefile.am -index 0ae72111179b..46a8cfb4966f 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -362,7 +362,7 @@ man_MANS += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ - man_MANS += doc/org.bluez.Media.5 doc/org.bluez.MediaControl.5 \ - doc/org.bluez.MediaPlayer.5 doc/org.bluez.MediaFolder.5 \ - doc/org.bluez.MediaItem.5 doc/org.bluez.MediaEndpoint.5 \ -- doc/org.bluez.MediaTransport.5 -+ doc/org.bluez.MediaTransport.5 doc/org.bluez.MediaAssistant.5 - man_MANS += doc/org.bluez.GattManager.5 doc/org.bluez.GattProfile.5 \ - doc/org.bluez.GattService.5 \ - doc/org.bluez.GattCharacteristic.5 \ -@@ -395,7 +395,7 @@ manual_pages += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ - manual_pages += doc/org.bluez.Media.5 doc/org.bluez.MediaControl.5 \ - doc/org.bluez.MediaPlayer.5 doc/org.bluez.MediaFolder.5 \ - doc/org.bluez.MediaItem.5 doc/org.bluez.MediaEndpoint.5 \ -- doc/org.bluez.MediaTransport.5 -+ doc/org.bluez.MediaTransport.5 doc/org.bluez.MediaAssistant.5 - manual_pages += doc/org.bluez.GattManager.5 doc/org.bluez.GattProfile.5 \ - doc/org.bluez.GattService.5 \ - doc/org.bluez.GattCharacteristic.5 \ -@@ -475,7 +475,7 @@ EXTRA_DIST += doc/org.bluez.Adapter.rst doc/org.bluez.Device.rst \ - EXTRA_DIST += doc/org.bluez.Media.rst doc/org.bluez.MediaControl.rst \ - doc/org.bluez.MediaPlayer.rst doc/org.bluez.MediaFolder.rst \ - doc/org.bluez.MediaItem.rst doc/org.bluez.MediaEndpoint.rst \ -- doc/org.bluez.MediaTransport.rst -+ doc/org.bluez.MediaTransport.rst doc/org.bluez.MediaAssistant.rst - - EXTRA_DIST += doc/org.bluez.GattManager.rst doc/org.bluez.GattProfile.rst\ - doc/org.bluez.GattService.rst \ -diff --git a/doc/org.bluez.MediaAssistant.rst b/doc/org.bluez.MediaAssistant.rst -new file mode 100644 -index 000000000000..4aac8953619d ---- /dev/null -+++ b/doc/org.bluez.MediaAssistant.rst -@@ -0,0 +1,74 @@ -+======================== -+org.bluez.MediaAssistant -+======================== -+ -+-------------------------------------------- -+BlueZ D-Bus MediaAssistant API documentation -+-------------------------------------------- -+ -+:Version: BlueZ -+:Date: June 2024 -+:Manual section: 5 -+:Manual group: Linux System Administration -+ -+Interface -+========= -+ -+:Service: org.bluez -+:Interface: org.bluez.MediaAssistant1 -+:Object path: /org/bluez/{hci0,hci1,...}/src_XX_XX_XX_XX_XX_XX/dev_YY_YY_YY_YY_YY_YY/bisZ -+ -+Methods -+------- -+ -+void Push(dict properties) -+```````````````````````````````````````````````````````` -+ -+ Send stream information to the remote device. -+ -+ :dict properties: -+ -+ Indicate stream properties that will be sent to the peer. -+ -+ Values: -+ -+ :array{byte} Metadata [ISO only]: -+ -+ See Metadata property. -+ -+ :dict QoS [ISO only]: -+ -+ See QoS property. -+ -+Properties -+---------- -+ -+string State [readonly] -+``````````````````````` -+ -+ Indicates the state of the assistant object. Possible values are: -+ -+ :"idle": assistant object was created for the stream -+ :"pending": assistant object was pushed (stream information was sent to the peer) -+ :"requesting": remote device requires Broadcast_Code -+ :"active": remote device started receiving stream -+ -+array{byte} Metadata [readwrite, ISO Only, experimental] -+```````````````````````````````````````````````````````` -+ -+ Indicates stream Metadata. -+ -+dict QoS [readwrite, ISO only, experimental] -+````````````````````````````````````````````````````` -+ -+ Indicates stream QoS capabilities. -+ -+ Values: -+ -+ :byte Encryption: -+ -+ Indicates whether the stream is encrypted. -+ -+ :array{byte} BCode -+ -+ Indicates Broadcast_Code to decrypt stream. --- -2.45.2 - - -From 34aca9a4fbcf3d4da459f97cef4a863d7853f6e4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Antonio=20V=C3=A1zquez=20Blanco?= - -Date: Thu, 4 Jul 2024 12:11:23 +0200 -Subject: [PATCH 03/46] bdaddr: Add cypress manufacturer support - ---- - tools/bdaddr.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tools/bdaddr.c b/tools/bdaddr.c -index bc0478d461b2..de17416e9c6c 100644 ---- a/tools/bdaddr.c -+++ b/tools/bdaddr.c -@@ -303,6 +303,7 @@ static struct { - { 48, st_write_bd_addr, generic_reset_device }, - { 57, ericsson_write_bd_addr, generic_reset_device }, - { 72, mrvl_write_bd_addr, generic_reset_device }, -+ { 305, bcm_write_bd_addr, generic_reset_device }, - { 65535, NULL, NULL }, - }; - --- -2.45.2 - - -From 2748c60a2c6b1b090a7507fdd23865a598129d61 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Tue, 9 Jul 2024 11:59:03 +0300 -Subject: [PATCH 04/46] bap: Wait for BIG Info report event before creating - streams - -This makes it so that stream for each BIS is created after BIG -Info report is received. This ensures that when the stream is -created the encryption field is correctly set. ---- - profiles/audio/bap.c | 27 ++++++++++++++++++++++++--- - 1 file changed, 24 insertions(+), 3 deletions(-) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index e82a253825f1..afa938091996 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -134,6 +134,7 @@ struct bap_bcast_pa_req { - struct btd_service *service; - struct bap_setup *setup; - } data; -+ unsigned int io_id; /* io_id for BIG Info watch */ - }; - - static struct queue *sessions; -@@ -1220,7 +1221,8 @@ fail: - return ret; - } - --static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) -+static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond, -+ gpointer user_data) - { - GError *err = NULL; - struct bap_bcast_pa_req *req = user_data; -@@ -1228,7 +1230,7 @@ static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) - struct bt_iso_base base; - struct bt_iso_qos qos; - -- DBG("PA Sync done"); -+ DBG("BIG Info received"); - - bt_io_get(io, &err, - BT_IO_OPT_BASE, &base, -@@ -1238,7 +1240,8 @@ static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) - error("%s", err->message); - g_error_free(err); - g_io_channel_shutdown(io, TRUE, NULL); -- return; -+ req->io_id = 0; -+ return FALSE; - } - - /* Close the io and remove the queue request for another PA Sync */ -@@ -1255,7 +1258,21 @@ static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) - service_set_connecting(req->data.service); - - queue_remove(data->adapter->bcast_pa_requests, req); -+ req->io_id = 0; - free(req); -+ -+ return FALSE; -+} -+ -+static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) -+{ -+ struct bap_bcast_pa_req *req = user_data; -+ /* PA Sync was established, wait for BIG Info report so that the -+ * encryption flag is also available. -+ */ -+ DBG("PA Sync done"); -+ req->io_id = g_io_add_watch(io, G_IO_OUT, big_info_report_cb, -+ user_data); - } - - static bool match_data_bap_data(const void *data, const void *match_data) -@@ -3177,6 +3194,10 @@ static void bap_bcast_remove(struct btd_service *service) - */ - req = queue_remove_if(data->adapter->bcast_pa_requests, - match_service, service); -+ if (req->io_id) { -+ g_source_remove(req->io_id); -+ req->io_id = 0; -+ } - free(req); - - bap_data_remove(data); --- -2.45.2 - - -From aa6063aa66954ac8321211145d1ae6b434b2555c Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Tue, 9 Jul 2024 17:35:00 +0300 -Subject: [PATCH 05/46] health: mcap: add checks for NULL mcap_notify_error() - -It is necessary to prevent dereferencing of NULL pointers. - -Found with the SVACE static analysis tool. ---- - profiles/health/mcap.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/profiles/health/mcap.c b/profiles/health/mcap.c -index 7eceaa88a3a9..2e4214a6984f 100644 ---- a/profiles/health/mcap.c -+++ b/profiles/health/mcap.c -@@ -336,6 +336,9 @@ static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) - case MCAP_MD_CREATE_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); -+ if (!l) -+ return; -+ - mdl = l->data; - mcl->mdls = g_slist_remove(mcl->mdls, mdl); - mcap_mdl_unref(mdl); -@@ -345,6 +348,9 @@ static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) - case MCAP_MD_ABORT_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); -+ if (!l) -+ return; -+ - shutdown_mdl(l->data); - update_mcl_state(mcl); - con->cb.notify(err, con->user_data); -@@ -362,6 +368,9 @@ static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) - case MCAP_MD_RECONNECT_MDL_REQ: - st = MDL_WAITING; - l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); -+ if (!l) -+ return; -+ - shutdown_mdl(l->data); - update_mcl_state(mcl); - con->cb.op(NULL, err, con->user_data); --- -2.45.2 - - -From 11dcc9bf0dba61c83269fb3cf234579d6f9ef192 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Tue, 9 Jul 2024 17:35:01 +0300 -Subject: [PATCH 06/46] shared: prevent dereferencing of NULL pointers - -It is necessary to add checks for NULL before dereferencing pointers. - -Found with the SVACE static analysis tool. ---- - src/shared/micp.c | 4 ++++ - src/shared/vcp.c | 12 ++++++++++++ - 2 files changed, 16 insertions(+) - -diff --git a/src/shared/micp.c b/src/shared/micp.c -index b82bd92dedb8..1c34e9d0079f 100644 ---- a/src/shared/micp.c -+++ b/src/shared/micp.c -@@ -398,6 +398,10 @@ static void mics_mute_write(struct gatt_db_attribute *attrib, - } - - micp_op = iov_pull_mem(&iov, sizeof(*micp_op)); -+ if (!micp_op) { -+ DBG(micp, "iov_pull_mem() returned NULL"); -+ goto respond; -+ } - - if ((*micp_op == MICS_DISABLED) || (*micp_op != MICS_NOT_MUTED - && *micp_op != MICS_MUTED)) { -diff --git a/src/shared/vcp.c b/src/shared/vcp.c -index 06264a24146c..602d46dc1d1d 100644 ---- a/src/shared/vcp.c -+++ b/src/shared/vcp.c -@@ -925,6 +925,10 @@ static void vcs_cp_write(struct gatt_db_attribute *attrib, - } - - vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op)); -+ if (!vcp_op) { -+ DBG(vcp, "iov_pull_mem() returned NULL"); -+ goto respond; -+ } - - for (handler = vcp_handlers; handler && handler->str; handler++) { - if (handler->op != *vcp_op) -@@ -985,6 +989,10 @@ static void vocs_cp_write(struct gatt_db_attribute *attrib, - } - - vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op)); -+ if (!vcp_op) { -+ DBG(vcp, "iov_pull_mem() returned NULL"); -+ goto respond; -+ } - - for (handler = vocp_handlers; handler && handler->str; handler++) { - if (handler->op != *vcp_op) -@@ -1517,6 +1525,10 @@ static void aics_ip_cp_write(struct gatt_db_attribute *attrib, - } - - aics_op = iov_pull_mem(&iov, sizeof(*aics_op)); -+ if (!aics_op) { -+ DBG(vcp, "iov_pull_mem() returned NULL"); -+ goto respond; -+ } - - for (handler = aics_handlers; handler && handler->str; handler++) { - if (handler->op != *aics_op) --- -2.45.2 - - -From 755091581336dd6b6a710e599da9e1e52037851a Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Tue, 9 Jul 2024 17:35:02 +0300 -Subject: [PATCH 07/46] settings: limit string size in load_service() - -It is necessary to prevent buffer overflow by limiting -the maximum string length. - -Found with the SVACE static analysis tool. ---- - src/settings.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/settings.c b/src/settings.c -index b61e694f1b85..643a083dbc84 100644 ---- a/src/settings.c -+++ b/src/settings.c -@@ -193,7 +193,7 @@ static int load_service(struct gatt_db *db, char *handle, char *value) - return -EIO; - } - -- if (sscanf(value, "%[^:]:%04hx:%36s", type, &end, uuid_str) != 3) { -+ if (sscanf(value, "%36[^:]:%04hx:%36s", type, &end, uuid_str) != 3) { - DBG("Failed to parse value: %s", value); - return -EIO; - } --- -2.45.2 - - -From 4ca662fcea1604e937bde1bddd5de2c50bcb6e00 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Tue, 9 Jul 2024 17:35:03 +0300 -Subject: [PATCH 08/46] settings: limit string size in gatt_db_load() - -It is necessary to prevent buffer overflow by limiting -the maximum string length. - -Found with the SVACE static analysis tool. ---- - src/settings.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/settings.c b/src/settings.c -index 643a083dbc84..37164939573f 100644 ---- a/src/settings.c -+++ b/src/settings.c -@@ -232,7 +232,7 @@ static int gatt_db_load(struct gatt_db *db, GKeyFile *key_file, char **keys) - value = g_key_file_get_string(key_file, "Attributes", *handle, - NULL); - -- if (!value || sscanf(value, "%[^:]:", type) != 1) { -+ if (!value || sscanf(value, "%36[^:]:", type) != 1) { - g_free(value); - return -EIO; - } -@@ -255,7 +255,7 @@ static int gatt_db_load(struct gatt_db *db, GKeyFile *key_file, char **keys) - value = g_key_file_get_string(key_file, "Attributes", *handle, - NULL); - -- if (!value || sscanf(value, "%[^:]:", type) != 1) { -+ if (!value || sscanf(value, "%36[^:]:", type) != 1) { - g_free(value); - return -EIO; - } --- -2.45.2 - - -From e56fc72fc66765f407473e4cb903fdc80784a4ff Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:44 +0300 -Subject: [PATCH 09/46] gatt: add return value check of io_get_fd() to - sock_io_send() - -It is necessary to add a return value check. - -Found with the SVACE static analysis tool. ---- - src/gatt-database.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/src/gatt-database.c b/src/gatt-database.c -index 8472aac5941d..6c84b085ca29 100644 ---- a/src/gatt-database.c -+++ b/src/gatt-database.c -@@ -2630,6 +2630,7 @@ static int sock_io_send(struct io *io, const void *data, size_t len) - { - struct msghdr msg; - struct iovec iov; -+ int fd; - - iov.iov_base = (void *) data; - iov.iov_len = len; -@@ -2638,7 +2639,13 @@ static int sock_io_send(struct io *io, const void *data, size_t len) - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - -- return sendmsg(io_get_fd(io), &msg, MSG_NOSIGNAL); -+ fd = io_get_fd(io); -+ if (fd < 0) { -+ error("io_get_fd() returned %d\n", fd); -+ return fd; -+ } -+ -+ return sendmsg(fd, &msg, MSG_NOSIGNAL); - } - - static void att_disconnect_cb(int err, void *user_data) --- -2.45.2 - - -From ba70a116d97108f21a853f5549758a720fdbefb3 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:45 +0300 -Subject: [PATCH 10/46] shared/vcp: add NULL checks to foreach_aics_service() - -Make foreach_aics_service() safe for passing NULL pointers. - -Found with the SVACE static analysis tool. ---- - src/shared/vcp.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/shared/vcp.c b/src/shared/vcp.c -index 602d46dc1d1d..43ef1d18644d 100644 ---- a/src/shared/vcp.c -+++ b/src/shared/vcp.c -@@ -2729,6 +2729,9 @@ static void foreach_aics_service(struct gatt_db_attribute *attr, - struct bt_vcp *vcp = user_data; - struct bt_aics *aics = vcp_get_aics(vcp); - -+ if (!aics || !attr) -+ return; -+ - aics->service = attr; - - gatt_db_service_set_claimed(attr, true); --- -2.45.2 - - -From 12525371ef082483d524447310da7d0f5866bf91 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:46 +0300 -Subject: [PATCH 11/46] client/player: add error code handling to - transport_recv() - -It is necessary to add return value check as in sock_send(). - -Found with the SVACE static analysis tool. ---- - client/player.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/client/player.c b/client/player.c -index 584fc5e8148a..de4491b534c2 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -4514,7 +4514,13 @@ static bool transport_recv(struct io *io, void *user_data) - uint8_t buf[1024]; - int ret, len; - -- ret = read(io_get_fd(io), buf, sizeof(buf)); -+ ret = io_get_fd(io); -+ if (ret < 0) { -+ bt_shell_printf("io_get_fd() returned %d\n", ret); -+ return true; -+ } -+ -+ ret = read(ret, buf, sizeof(buf)); - if (ret < 0) { - bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno), - -errno); --- -2.45.2 - - -From 7ffc08dd78d68eff15bb77e43efbc1b606fb4fd8 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:47 +0300 -Subject: [PATCH 12/46] shared/vcp: prevent dereferencing of NULL pointers - -util_memdup() will terminate the program if memory -allocation fails. - -Found with the SVACE static analysis tool. ---- - src/shared/vcp.c | 20 ++++---------------- - 1 file changed, 4 insertions(+), 16 deletions(-) - -diff --git a/src/shared/vcp.c b/src/shared/vcp.c -index 43ef1d18644d..cfc426624875 100644 ---- a/src/shared/vcp.c -+++ b/src/shared/vcp.c -@@ -2139,14 +2139,8 @@ static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success, - return; - } - -- vocs_ao_dec_r = malloc(length+1); -- memset(vocs_ao_dec_r, 0, length+1); -- memcpy(vocs_ao_dec_r, value, length); -- -- if (!vocs_ao_dec_r) { -- DBG(vcp, "Unable to get VOCS Audio Descriptor"); -- return; -- } -+ vocs_ao_dec_r = util_memdup(value, length + 1); -+ memset(vocs_ao_dec_r + length, 0, 1); - - DBG(vcp, "VOCS Audio Descriptor: %s", vocs_ao_dec_r); - free(vocs_ao_dec_r); -@@ -2543,14 +2537,8 @@ static void read_aics_audio_ip_description(struct bt_vcp *vcp, bool success, - return; - } - -- ip_descrptn = malloc(length+1); -- memset(ip_descrptn, 0, length+1); -- memcpy(ip_descrptn, value, length); -- -- if (!ip_descrptn) { -- DBG(vcp, "Unable to get Audio Input Description"); -- return; -- } -+ ip_descrptn = util_memdup(value, length + 1); -+ memset(ip_descrptn + length, 0, 1); - - DBG(vcp, "Audio Input Description: %s", ip_descrptn); - free(ip_descrptn); --- -2.45.2 - - -From cf3d80a01f1f21538148cb9a5569b678dad0848b Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:48 +0300 -Subject: [PATCH 13/46] client/player: fix the order of args in - cmd_register_endpoint() - -Based on the function prototype, ep->cid and ep->vid should be swapped. - -Found with the SVACE static analysis tool. ---- - client/player.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/client/player.c b/client/player.c -index de4491b534c2..2480ed64b8e5 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -3388,7 +3388,7 @@ static void cmd_register_endpoint(int argc, char *argv[]) - - if (strrchr(argv[2], ':')) { - ep->codec = 0xff; -- parse_vendor_codec(argv[2], &ep->cid, &ep->vid); -+ parse_vendor_codec(argv[2], &ep->vid, &ep->cid); - ep->preset = new0(struct preset, 1); - ep->preset->custom.name = strdup("custom"); - ep->preset->default_preset = &ep->preset->custom; --- -2.45.2 - - -From 7a45038dc1e505afbaa49f8dd64fd41dab627f23 Mon Sep 17 00:00:00 2001 -From: Roman Smirnov -Date: Wed, 10 Jul 2024 14:31:49 +0300 -Subject: [PATCH 14/46] shared/gatt-client: add NULL check to - discover_secondary_cb() - -It is necessary to prevent dereferencing of a NULL pointer. - -Found with the SVACE static analysis tool. ---- - src/shared/gatt-client.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c -index b48d739fc609..9db3f52117ff 100644 ---- a/src/shared/gatt-client.c -+++ b/src/shared/gatt-client.c -@@ -1276,7 +1276,9 @@ next: - - range = queue_peek_head(op->discov_ranges); - -- client->discovery_req = bt_gatt_discover_included_services(client->att, -+ if (range) -+ client->discovery_req = bt_gatt_discover_included_services( -+ client->att, - range->start, - range->end, - discover_incl_cb, --- -2.45.2 - - -From 9cc587947b6ac56a4c94dcc880b273bc72af22a8 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Thu, 11 Jul 2024 15:11:56 -0400 -Subject: [PATCH 15/46] device: Fix overwritting current_flags - -MGMT Set Device Flags overwrites the current_flags so only the last -flags set this way would remain active which can be seem in the -following sequence when LL Privacy is enabled: - -@ MGMT Command: Set Device Flags (0x0050) plen 11 - LE Address: CF:AC:A6:79:3D:B9 (Static) - Current Flags: 0x00000001 - Remote Wakeup -@ MGMT Event: Command Complete (0x0001) plen 10 - Set Device Flags (0x0050) plen 7 - Status: Success (0x00) - LE Address: CF:AC:A6:79:3D:B9 (Static) -@ MGMT Command: Set Device Flags (0x0050) plen 11 - LE Address: CF:AC:A6:79:3D:B9 (Static) - Current Flags: 0x00000002 - Device Privacy Mode -@ MGMT Event: Command Complete (0x0001) plen 10 - Set Device Flags (0x0050) plen 7 - Status: Success (0x00) - LE Address: CF:AC:A6:79:3D:B9 (Static) - -In order to do this properly the code needs to track the pending_flags -being set and also call btd_device_flags_changed whenever a change is -complete since that event is not generated when MGMT_OP_SET_DEVICE_FLAGS -is sent by bluetoothd itself. ---- - src/adapter.c | 20 +++++++++++++++++--- - src/device.c | 20 +++++++++++++++++++- - src/device.h | 2 ++ - 3 files changed, 38 insertions(+), 4 deletions(-) - -diff --git a/src/adapter.c b/src/adapter.c -index bb49a1ecad23..85ddfc16568f 100644 ---- a/src/adapter.c -+++ b/src/adapter.c -@@ -5569,6 +5569,7 @@ void adapter_accept_list_remove(struct btd_adapter *adapter, - static void set_device_privacy_complete(uint8_t status, uint16_t length, - const void *param, void *user_data) - { -+ struct btd_device *dev = user_data; - const struct mgmt_rp_set_device_flags *rp = param; - - if (status != MGMT_STATUS_SUCCESS) { -@@ -5581,6 +5582,9 @@ static void set_device_privacy_complete(uint8_t status, uint16_t length, - error("Too small Set Device Flags complete event: %d", length); - return; - } -+ -+ btd_device_flags_changed(dev, btd_device_get_supported_flags(dev), -+ btd_device_get_pending_flags(dev)); - } - - static void add_device_complete(uint8_t status, uint16_t length, -@@ -5626,7 +5630,7 @@ static void add_device_complete(uint8_t status, uint16_t length, - adapter_set_device_flags(adapter, dev, flags | - DEVICE_FLAG_DEVICE_PRIVACY, - set_device_privacy_complete, -- NULL); -+ dev); - } - } - } -@@ -5676,6 +5680,7 @@ void adapter_set_device_flags(struct btd_adapter *adapter, - { - struct mgmt_cp_set_device_flags cp; - uint32_t supported = btd_device_get_supported_flags(device); -+ uint32_t pending = btd_device_get_pending_flags(device); - const bdaddr_t *bdaddr; - uint8_t bdaddr_type; - -@@ -5683,6 +5688,14 @@ void adapter_set_device_flags(struct btd_adapter *adapter, - (supported | flags) != supported) - return; - -+ /* Check if changing flags are pending */ -+ if (flags == (flags & pending)) -+ return; -+ -+ /* Set Device Privacy Mode if it has not set the flag yet. */ -+ if (btd_opts.device_privacy && !(flags & DEVICE_FLAG_DEVICE_PRIVACY)) -+ flags |= DEVICE_FLAG_DEVICE_PRIVACY & supported & ~pending; -+ - bdaddr = device_get_address(device); - bdaddr_type = btd_device_get_bdaddr_type(device); - -@@ -5691,8 +5704,9 @@ void adapter_set_device_flags(struct btd_adapter *adapter, - cp.addr.type = bdaddr_type; - cp.current_flags = cpu_to_le32(flags); - -- mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id, -- sizeof(cp), &cp, func, user_data, NULL); -+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id, -+ sizeof(cp), &cp, func, user_data, NULL)) -+ btd_device_set_pending_flags(device, flags); - } - - static void device_flags_changed_callback(uint16_t index, uint16_t length, -diff --git a/src/device.c b/src/device.c -index 097b1fbba37d..a1dc0750ca41 100644 ---- a/src/device.c -+++ b/src/device.c -@@ -214,6 +214,7 @@ struct btd_device { - GDBusPendingPropertySet wake_id; - - uint32_t supported_flags; -+ uint32_t pending_flags; - uint32_t current_flags; - GSList *svc_callbacks; - GSList *eir_uuids; -@@ -1569,7 +1570,7 @@ static void set_wake_allowed_complete(uint8_t status, uint16_t length, - return; - } - -- device_set_wake_allowed_complete(dev); -+ btd_device_flags_changed(dev, dev->supported_flags, dev->pending_flags); - } - - void device_set_wake_allowed(struct btd_device *device, bool wake_allowed, -@@ -7243,6 +7244,22 @@ uint32_t btd_device_get_supported_flags(struct btd_device *dev) - return dev->supported_flags; - } - -+void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags) -+{ -+ if (!dev) -+ return; -+ -+ dev->pending_flags = flags; -+} -+ -+uint32_t btd_device_get_pending_flags(struct btd_device *dev) -+{ -+ if (!dev) -+ return 0; -+ -+ return dev->pending_flags; -+} -+ - /* This event is sent immediately after add device on all mgmt sockets. - * Afterwards, it is only sent to mgmt sockets other than the one which called - * set_device_flags. -@@ -7255,6 +7272,7 @@ void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, - - dev->supported_flags = supported_flags; - dev->current_flags = current_flags; -+ dev->pending_flags = 0; - - if (!changed_flags) - return; -diff --git a/src/device.h b/src/device.h -index 0794f92d0178..3742f6028040 100644 ---- a/src/device.h -+++ b/src/device.h -@@ -191,6 +191,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services); - - uint32_t btd_device_get_current_flags(struct btd_device *dev); - uint32_t btd_device_get_supported_flags(struct btd_device *dev); -+uint32_t btd_device_get_pending_flags(struct btd_device *dev); -+void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags); - void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, - uint32_t current_flags); - --- -2.45.2 - - -From 73266377b0185c56c921b8cece257df428612d73 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Fri, 12 Jul 2024 15:27:12 -0400 -Subject: [PATCH 16/46] shared/bap: Fix ASE notification order - -When processing a CP operation the CP shall be notified ahead of -the ASE itself: - - 'If the server successfully completes a client-initiated ASE Control - operation for an ASE, the server shall send a notification of the ASE - Control Point characteristic value formatted as defined in Table 4.7. - The server shall then perform the behavior defined in Section 5.1 - through Section 5.8 for that ASE Control operation and send - notifications of any ASE characteristic values written during that - ASE Control operation.' - -So this delays the processing of notifications of ASE states so the CP -responses always appears first in the notification e.g: - -> ACL Data RX: Handle 42 flags 0x02 dlen 59 - ATT: Handle Multiple Value Notification (0x23) len 54 - Length: 0x0008 - Handle: 0x0036 Type: ASE Control Point (0x2bc6) - Data[8]: 0202030000010000 - Opcode: QoS Configuration (0x02) - Number of ASE(s): 2 - ASE: #0 - ASE ID: 0x03 - ASE Response Code: Success (0x00) - ASE Response Reason: None (0x00) - ASE: #1 - ASE ID: 0x01 - ASE Response Code: Success (0x00) - ASE Response Reason: None (0x00) - Length: 0x0011 - Handle: 0x0030 Type: Source ASE (0x2bc5) - Data[17]: 0302000010270000022800020a00409c00 - ASE ID: 3 - State: QoS Configured (0x02) - CIG ID: 0x00 - CIS ID: 0x00 - SDU Interval: 10000 usec - Framing: Unframed (0x00) - PHY: 0x02 - LE 2M PHY (0x02) - Max SDU: 40 - RTN: 2 - Max Transport Latency: 10 - Presentation Delay: 40000 us - Length: 0x0011 - Handle: 0x002a Type: Sink ASE (0x2bc4) - Data[17]: 0102000010270000025000020a00409c00 - ASE ID: 1 - State: QoS Configured (0x02) - CIG ID: 0x00 - CIS ID: 0x00 - SDU Interval: 10000 usec - Framing: Unframed (0x00) - PHY: 0x02 - LE 2M PHY (0x02) - Max SDU: 80 - RTN: 2 - Max Transport Latency: 10 - Presentation Delay: 40000 us ---- - src/shared/bap.c | 53 +++++++++++++++++++++++++++++++++++++++--------- - 1 file changed, 43 insertions(+), 10 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 3a4c1f9d3a98..d59eac8cca16 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -169,6 +169,7 @@ struct bt_bap { - unsigned int process_id; - unsigned int disconn_id; - unsigned int idle_id; -+ bool in_cp_write; - - struct queue *reqs; - struct queue *notify; -@@ -266,6 +267,7 @@ struct bt_bap_stream { - const struct bt_bap_stream_ops *ops; - uint8_t old_state; - uint8_t state; -+ unsigned int state_id; - bool client; - void *user_data; - }; -@@ -1102,6 +1104,8 @@ static void bap_stream_free(void *data) - { - struct bt_bap_stream *stream = data; - -+ timeout_remove(stream->state_id); -+ - if (stream->ep) - stream->ep->stream = NULL; - -@@ -1579,20 +1583,17 @@ static bool bap_queue_req(struct bt_bap *bap, struct bt_bap_req *req) - return true; - } - --static void bap_ucast_set_state(struct bt_bap_stream *stream, uint8_t state) -+static bool stream_notify_state(void *data) - { -+ struct bt_bap_stream *stream = data; - struct bt_bap_endpoint *ep = stream->ep; - -- ep->old_state = ep->state; -- ep->state = state; -- -- DBG(stream->bap, "stream %p dir 0x%02x: %s -> %s", stream, -- bt_bap_stream_get_dir(stream), -- bt_bap_stream_statestr(stream->ep->old_state), -- bt_bap_stream_statestr(stream->ep->state)); -+ DBG(stream->bap, "stream %p", stream); - -- if (stream->lpac->type == BT_BAP_BCAST_SINK || stream->client) -- goto done; -+ if (stream->state_id) { -+ timeout_remove(stream->state_id); -+ stream->state_id = 0; -+ } - - switch (ep->state) { - case BT_ASCS_ASE_STATE_IDLE: -@@ -1610,6 +1611,31 @@ static void bap_ucast_set_state(struct bt_bap_stream *stream, uint8_t state) - break; - } - -+ return false; -+} -+ -+static void bap_ucast_set_state(struct bt_bap_stream *stream, uint8_t state) -+{ -+ struct bt_bap_endpoint *ep = stream->ep; -+ -+ ep->old_state = ep->state; -+ ep->state = state; -+ -+ DBG(stream->bap, "stream %p dir 0x%02x: %s -> %s", stream, -+ bt_bap_stream_get_dir(stream), -+ bt_bap_stream_statestr(stream->ep->old_state), -+ bt_bap_stream_statestr(stream->ep->state)); -+ -+ if (stream->client) -+ goto done; -+ -+ if (!stream->bap->in_cp_write) -+ stream_notify_state(stream); -+ else if (!stream->state_id) -+ stream->state_id = timeout_add(BAP_PROCESS_TIMEOUT, -+ stream_notify_state, -+ stream, NULL); -+ - done: - bap_stream_state_changed(stream); - } -@@ -3069,8 +3095,15 @@ static void ascs_ase_cp_write(struct gatt_db_attribute *attrib, - - DBG(bap, "%s", handler->str); - -+ /* Set in_cp_write so ASE notification are not sent ahead of -+ * CP notifcation. -+ */ -+ bap->in_cp_write = true; -+ - for (i = 0; i < hdr->num; i++) - ret = handler->func(ascs, bap, &iov, rsp); -+ -+ bap->in_cp_write = false; - } else { - DBG(bap, "Unknown opcode 0x%02x", hdr->op); - ascs_ase_rsp_add_errno(rsp, 0x00, -ENOTSUP); --- -2.45.2 - - -From 025f07ec0d0ebfb5e83c07d2918a6c01b0ae49a6 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Fri, 12 Jul 2024 10:52:03 -0400 -Subject: [PATCH 17/46] client/player: Add support for name custom presets - -This adds support for naming custom presets instead of always having -just one "custom" codec preset which needs to be overwriten everytime -a new set of settings needs to be entered. ---- - client/player.c | 130 ++++++++++++++++++++++++++++++++---------------- - 1 file changed, 87 insertions(+), 43 deletions(-) - -diff --git a/client/player.c b/client/player.c -index 2480ed64b8e5..26190fef7bc1 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -1232,6 +1232,7 @@ struct codec_preset { - const struct iovec data; - struct bt_bap_qos qos; - uint8_t target_latency; -+ bool custom; - }; - - #define SBC_PRESET(_name, _data) \ -@@ -1448,7 +1449,6 @@ static void print_lc3_meta(void *data, int len) - { \ - .uuid = _uuid, \ - .codec = _codec, \ -- .custom = { .name = "custom" }, \ - .default_preset = &_presets[_default_index], \ - .presets = _presets, \ - .num_presets = ARRAY_SIZE(_presets), \ -@@ -1459,7 +1459,7 @@ static struct preset { - uint8_t codec; - uint16_t cid; - uint16_t vid; -- struct codec_preset custom; -+ struct queue *custom; - struct codec_preset *default_preset; - struct codec_preset *presets; - size_t num_presets; -@@ -1557,6 +1557,14 @@ static struct preset *find_presets_name(const char *uuid, const char *codec) - return find_presets(uuid, id, 0x0000, 0x0000); - } - -+static bool match_custom_name(const void *data, const void *match_data) -+{ -+ const struct codec_preset *preset = data; -+ const char *name = match_data; -+ -+ return !strcmp(preset->name, name); -+} -+ - static struct codec_preset *preset_find_name(struct preset *preset, - const char *name) - { -@@ -1567,8 +1575,6 @@ static struct codec_preset *preset_find_name(struct preset *preset, - - if (!name) - return preset->default_preset; -- else if (!strcmp(name, "custom")) -- return &preset->custom; - - for (i = 0; i < preset->num_presets; i++) { - struct codec_preset *p; -@@ -1579,19 +1585,7 @@ static struct codec_preset *preset_find_name(struct preset *preset, - return p; - } - -- return NULL; --} -- --static struct codec_preset *find_preset(const char *uuid, const char *codec, -- const char *name) --{ -- struct preset *preset; -- -- preset = find_presets_name(uuid, codec); -- if (!preset) -- return NULL; -- -- return preset_find_name(preset, name); -+ return queue_find(preset->custom, match_custom_name, name); - } - - static DBusMessage *endpoint_select_config_reply(DBusMessage *msg, -@@ -2816,10 +2810,11 @@ static void endpoint_free(void *data) - if (ep->msg) - dbus_message_unref(ep->msg); - -- if (ep->codec == 0xff) { -- free(ep->preset->custom.name); -+ queue_destroy(ep->preset->custom, free); -+ ep->preset->custom = NULL; -+ -+ if (ep->codec == 0xff) - free(ep->preset); -- } - - queue_destroy(ep->acquiring, NULL); - queue_destroy(ep->transports, free); -@@ -3365,6 +3360,36 @@ static const struct capabilities *find_capabilities(const char *uuid, - return NULL; - } - -+static struct codec_preset *codec_preset_new(const char *name) -+{ -+ struct codec_preset *codec; -+ -+ codec = new0(struct codec_preset, 1); -+ codec->name = strdup(name); -+ codec->custom = true; -+ -+ return codec; -+} -+ -+static struct codec_preset *codec_preset_add(struct preset *preset, -+ const char *name) -+{ -+ struct codec_preset *codec; -+ -+ codec = preset_find_name(preset, name); -+ if (codec) -+ return codec; -+ -+ codec = codec_preset_new(name); -+ -+ if (!preset->custom) -+ preset->custom = queue_new(); -+ -+ queue_push_tail(preset->custom, codec); -+ -+ return codec; -+} -+ - static void cmd_register_endpoint(int argc, char *argv[]) - { - struct endpoint *ep; -@@ -3390,8 +3415,8 @@ static void cmd_register_endpoint(int argc, char *argv[]) - ep->codec = 0xff; - parse_vendor_codec(argv[2], &ep->vid, &ep->cid); - ep->preset = new0(struct preset, 1); -- ep->preset->custom.name = strdup("custom"); -- ep->preset->default_preset = &ep->preset->custom; -+ ep->preset->default_preset = codec_preset_add(ep->preset, -+ "custom"); - } else { - ep->preset = find_presets_name(ep->uuid, argv[2]); - } -@@ -4060,21 +4085,27 @@ static void custom_frequency(const char *input, void *user_data) - custom_duration, user_data); - } - -+static void foreach_custom_preset_print(void *data, void *user_data) -+{ -+ struct codec_preset *p = data; -+ struct preset *preset = user_data; -+ -+ bt_shell_printf("%s%s\n", p == preset->default_preset ? "*" : "", -+ p->name); -+} -+ - static void print_presets(struct preset *preset) - { - size_t i; - struct codec_preset *p; - -- p = &preset->custom; -- -- bt_shell_printf("%s%s\n", p == preset->default_preset ? "*" : "", -- p->name); -- - for (i = 0; i < preset->num_presets; i++) { - p = &preset->presets[i]; - bt_shell_printf("%s%s\n", p == preset->default_preset ? - "*" : "", p->name); - } -+ -+ queue_foreach(preset->custom, foreach_custom_preset_print, preset); - } - - static void cmd_presets_endpoint(int argc, char *argv[]) -@@ -4082,29 +4113,42 @@ static void cmd_presets_endpoint(int argc, char *argv[]) - struct preset *preset; - struct codec_preset *default_preset = NULL; - -- if (argc > 3) { -- default_preset = find_preset(argv[1], argv[2], argv[3]); -- if (!default_preset) { -- bt_shell_printf("Preset %s not found\n", argv[3]); -- return bt_shell_noninteractive_quit(EXIT_FAILURE); -- } -- } -- - preset = find_presets_name(argv[1], argv[2]); - if (!preset) { - bt_shell_printf("No preset found\n"); - return bt_shell_noninteractive_quit(EXIT_FAILURE); - } - -- if (default_preset) { -+ if (argc > 3) { -+ default_preset = codec_preset_add(preset, argv[3]); -+ if (!default_preset) { -+ bt_shell_printf("Preset %s not found\n", argv[3]); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } - preset->default_preset = default_preset; -- goto done; -- } - -- print_presets(preset); -+ if (argc > 4) { -+ struct iovec *iov = (void *)&default_preset->data; - --done: -- if (default_preset && !strcmp(default_preset->name, "custom")) { -+ iov->iov_base = str2bytearray(argv[4], &iov->iov_len); -+ if (!iov->iov_base) { -+ bt_shell_printf("Invalid configuration %s\n", -+ argv[4]); -+ return bt_shell_noninteractive_quit( -+ EXIT_FAILURE); -+ } -+ -+ bt_shell_prompt_input("QoS", "Enter Target Latency " -+ "(Low, Balance, High):", -+ custom_target_latency, -+ default_preset); -+ -+ return; -+ } -+ } else -+ print_presets(preset); -+ -+ if (default_preset && default_preset->custom) { - bt_shell_prompt_input("Codec", "Enter frequency (Khz):", - custom_frequency, default_preset); - return; -@@ -4133,9 +4177,9 @@ static const struct bt_shell_menu endpoint_menu = { - cmd_config_endpoint, - "Configure Endpoint", - endpoint_generator }, -- { "presets", " [default]", -+ { "presets", " [preset] [config]", - cmd_presets_endpoint, -- "List available presets", -+ "List or add presets", - uuid_generator }, - {} }, - }; --- -2.45.2 - - -From 957c956112cc2bba528fe8df4a0a21d221a617ca Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Thu, 18 Jul 2024 13:58:52 -0400 -Subject: [PATCH 18/46] client/player: Fix printing errors when - transport->filename is not set - -If transport->filename is not set don't attempt to write to the -transport->fd. ---- - client/player.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/client/player.c b/client/player.c -index 26190fef7bc1..5b0b918fb8d7 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -4575,10 +4575,10 @@ static bool transport_recv(struct io *io, void *user_data) - - transport->seq++; - -- if (transport->fd >= 0) { -+ if (transport->filename) { - len = write(transport->fd, buf, ret); - if (len < 0) -- bt_shell_printf("Unable to write: %s (%d)", -+ bt_shell_printf("Unable to write: %s (%d)\n", - strerror(errno), -errno); - } - --- -2.45.2 - - -From 0bad3d5cbea84b24d53e86de7c419e893bb19a93 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 11:11:31 +0300 -Subject: [PATCH 19/46] bap: Fix crash in bap_bcast_remove - -This adds a check for the PA request dequeued in bap_bcast_remove, -to avoid accessing a member within a NULL pointer. ---- - profiles/audio/bap.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index afa938091996..16c02524f2d0 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -3194,7 +3194,7 @@ static void bap_bcast_remove(struct btd_service *service) - */ - req = queue_remove_if(data->adapter->bcast_pa_requests, - match_service, service); -- if (req->io_id) { -+ if (req && req->io_id) { - g_source_remove(req->io_id); - req->io_id = 0; - } --- -2.45.2 - - -From caa4202a7ee3423211733f7883641c77666dfbbf Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:21:58 +0300 -Subject: [PATCH 20/46] shared/bap: Add separate API to merge caps - -This moves the logic to merge L2 and L3 capabilities discovered -inside a BASE structure in a public API. ---- - src/shared/bap.c | 40 ++++++++++++++++++++++++---------------- - src/shared/bap.h | 2 ++ - 2 files changed, 26 insertions(+), 16 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index d59eac8cca16..1259ee3c90ef 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -6607,29 +6607,21 @@ static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, - return compare_data; - } - --void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, -- struct bt_bap_codec *codec, -- struct iovec *l2_caps, -- struct iovec *l3_caps, -- struct bt_bap_pac **lpac, -- struct iovec **caps) -+struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps) - { - struct bt_ltv_extract merge_data = {0}; -- struct bt_ltv_match match_data; - - if (!l2_caps) - /* Codec_Specific_Configuration parameters shall - * be present at Level 2. - */ -- return; -+ return NULL; - -- if (!l3_caps) { -+ if (!l3_caps) - /* Codec_Specific_Configuration parameters may - * be present at Level 3. - */ -- merge_data.result = util_iov_dup(l2_caps, 1); -- goto done; -- } -+ return util_iov_dup(l2_caps, 1); - - merge_data.src = l3_caps; - merge_data.result = new0(struct iovec, 1); -@@ -6642,17 +6634,33 @@ void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, - NULL, - bap_sink_check_level2_ltv, &merge_data); - --done: -+ return merge_data.result; -+} -+ -+void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, -+ struct bt_bap_codec *codec, -+ struct iovec *l2_caps, -+ struct iovec *l3_caps, -+ struct bt_bap_pac **lpac, -+ struct iovec **caps) -+{ -+ struct iovec *merged_caps; -+ struct bt_ltv_match match_data; -+ -+ merged_caps = bt_bap_merge_caps(l2_caps, l3_caps); -+ if (!merged_caps) -+ return; -+ - /* Check each BIS Codec Specific Configuration LTVs against our Codec - * Specific Capabilities and if the BIS matches create a PAC with it - */ -- match_data = bap_check_bis(bap->ldb, merge_data.result); -+ match_data = bap_check_bis(bap->ldb, merged_caps); - if (match_data.found == true) { -- *caps = merge_data.result; -+ *caps = merged_caps; - *lpac = match_data.data; - DBG(bap, "Matching BIS %i", bis_index); - } else { -- util_iov_free(merge_data.result, 1); -+ util_iov_free(merged_caps, 1); - *caps = NULL; - *lpac = NULL; - } -diff --git a/src/shared/bap.h b/src/shared/bap.h -index b35b2711edb9..e63161dca4e8 100644 ---- a/src/shared/bap.h -+++ b/src/shared/bap.h -@@ -251,6 +251,8 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac); - - struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream); - -+struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps); -+ - void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, - struct bt_bap_codec *codec, - struct iovec *l2_caps, --- -2.45.2 - - -From 679349fbc9f2eaf4216cca0ca45f25e4d2829c9d Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:21:59 +0300 -Subject: [PATCH 21/46] shared/bap: Update bt_bap_verify_bis to receive caps - -This updates bt_bap_verify_bis to receive the already merged L2 and L3 -capabilities, instead of computing it internally. ---- - profiles/audio/bap.c | 11 ++++++++--- - src/shared/bap.c | 15 ++++----------- - src/shared/bap.h | 6 ++---- - unit/test-bap.c | 7 ++++--- - 4 files changed, 18 insertions(+), 21 deletions(-) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index 16c02524f2d0..81b5051f089a 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -1191,12 +1191,17 @@ static bool parse_base(struct bap_data *bap_data, struct bt_iso_base *base, - l3_caps->iov_len, NULL, print_ltv, - func); - -+ merged_caps = bt_bap_merge_caps(l2_caps, l3_caps); -+ if (!merged_caps) { -+ free(path); -+ continue; -+ } -+ - /* Check if this BIS matches any local PAC */ - bt_bap_verify_bis(bap_data->bap, bis_index, &codec, -- l2_caps, l3_caps, &matched_lpac, -- &merged_caps); -+ merged_caps, &matched_lpac); - -- if (matched_lpac == NULL || merged_caps == NULL) { -+ if (matched_lpac == NULL) { - free(path); - continue; - } -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 1259ee3c90ef..3381ffdd43ee 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -6639,29 +6639,22 @@ struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps) - - void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, - struct bt_bap_codec *codec, -- struct iovec *l2_caps, -- struct iovec *l3_caps, -- struct bt_bap_pac **lpac, -- struct iovec **caps) -+ struct iovec *caps, -+ struct bt_bap_pac **lpac) - { -- struct iovec *merged_caps; - struct bt_ltv_match match_data; - -- merged_caps = bt_bap_merge_caps(l2_caps, l3_caps); -- if (!merged_caps) -+ if (!caps) - return; - - /* Check each BIS Codec Specific Configuration LTVs against our Codec - * Specific Capabilities and if the BIS matches create a PAC with it - */ -- match_data = bap_check_bis(bap->ldb, merged_caps); -+ match_data = bap_check_bis(bap->ldb, caps); - if (match_data.found == true) { -- *caps = merged_caps; - *lpac = match_data.data; - DBG(bap, "Matching BIS %i", bis_index); - } else { -- util_iov_free(merged_caps, 1); -- *caps = NULL; - *lpac = NULL; - } - -diff --git a/src/shared/bap.h b/src/shared/bap.h -index e63161dca4e8..3e68f00e2e29 100644 ---- a/src/shared/bap.h -+++ b/src/shared/bap.h -@@ -255,8 +255,6 @@ struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps); - - void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, - struct bt_bap_codec *codec, -- struct iovec *l2_caps, -- struct iovec *l3_caps, -- struct bt_bap_pac **lpac, -- struct iovec **caps); -+ struct iovec *caps, -+ struct bt_bap_pac **lpac); - -diff --git a/unit/test-bap.c b/unit/test-bap.c -index 9dd7a45e89c1..4b47d6363a80 100644 ---- a/unit/test-bap.c -+++ b/unit/test-bap.c -@@ -587,12 +587,13 @@ static void bsnk_pac_added(struct bt_bap_pac *pac, void *user_data) - codec.id = LC3_ID; - - for (uint8_t i = 0; i < data->cfg->streams; i++) { -- bt_bap_verify_bis(data->bap, bis_idx++, &codec, -- &data->cfg->cc, NULL, &lpac, &cc); -+ cc = bt_bap_merge_caps(&data->cfg->cc, NULL); -+ g_assert(cc); -+ -+ bt_bap_verify_bis(data->bap, bis_idx++, &codec, cc, &lpac); - - g_assert(lpac); - g_assert(pac == lpac); -- g_assert(cc); - - stream = bt_bap_stream_new(data->bap, - pac, NULL, &data->cfg->qos, cc); --- -2.45.2 - - -From bbcf4891cd46f53e35761db808155dc0fb89b175 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:00 +0300 -Subject: [PATCH 22/46] shared/bap: Remove unused param from bt_bap_verify_bis - -This removes the codec parameter from bt_bap_verify_bis, -since it is not used. ---- - profiles/audio/bap.c | 2 +- - src/shared/bap.c | 1 - - src/shared/bap.h | 1 - - unit/test-bap.c | 8 +------- - 4 files changed, 2 insertions(+), 10 deletions(-) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index 81b5051f089a..b76d5385fa10 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -1198,7 +1198,7 @@ static bool parse_base(struct bap_data *bap_data, struct bt_iso_base *base, - } - - /* Check if this BIS matches any local PAC */ -- bt_bap_verify_bis(bap_data->bap, bis_index, &codec, -+ bt_bap_verify_bis(bap_data->bap, bis_index, - merged_caps, &matched_lpac); - - if (matched_lpac == NULL) { -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 3381ffdd43ee..d2a500e486ed 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -6638,7 +6638,6 @@ struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps) - } - - void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, -- struct bt_bap_codec *codec, - struct iovec *caps, - struct bt_bap_pac **lpac) - { -diff --git a/src/shared/bap.h b/src/shared/bap.h -index 3e68f00e2e29..bf928bc2d577 100644 ---- a/src/shared/bap.h -+++ b/src/shared/bap.h -@@ -254,7 +254,6 @@ struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream); - struct iovec *bt_bap_merge_caps(struct iovec *l2_caps, struct iovec *l3_caps); - - void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, -- struct bt_bap_codec *codec, - struct iovec *caps, - struct bt_bap_pac **lpac); - -diff --git a/unit/test-bap.c b/unit/test-bap.c -index 4b47d6363a80..9cfc8c403ef0 100644 ---- a/unit/test-bap.c -+++ b/unit/test-bap.c -@@ -575,22 +575,16 @@ static void bsnk_pac_added(struct bt_bap_pac *pac, void *user_data) - struct test_data *data = user_data; - struct bt_bap_pac *lpac; - struct iovec *cc; -- struct bt_bap_codec codec = {0}; - struct bt_bap_stream *stream; - uint8_t bis_idx = 1; - - bt_bap_pac_set_ops(pac, &bcast_pac_ops, NULL); - -- if (data->cfg->vs) -- codec.id = 0xff; -- else -- codec.id = LC3_ID; -- - for (uint8_t i = 0; i < data->cfg->streams; i++) { - cc = bt_bap_merge_caps(&data->cfg->cc, NULL); - g_assert(cc); - -- bt_bap_verify_bis(data->bap, bis_idx++, &codec, cc, &lpac); -+ bt_bap_verify_bis(data->bap, bis_idx++, cc, &lpac); - - g_assert(lpac); - g_assert(pac == lpac); --- -2.45.2 - - -From 662aee4357f8975763280fec0e6cd35b2082200d Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:01 +0300 -Subject: [PATCH 23/46] shared/bap: Allow checking bis caps against peer caps - -A BAP Broadcast Assistant needs to match stream capabilities with -capabilities discovered in the Sink PAC characteristic on the peer. - -This updates bt_bap_verify_bis to check the provided stream capabilities -against local or remote capabilities, depending on the bap structure -provided: - -If the device is acting as a BAP Broadcast Sink and the bap session was -created after scanning a Broadcast Source, the stream caps will be matched -with the local broadcast sink PAC. - -If the device is acting as a Broadcast Assistant and the bap session is a -client session with a BAP Scan Delegator, the stream caps will be matched -with the PAC records populated in the rdb at service discovery. ---- - src/shared/bap.c | 26 ++++++++++++++++++++------ - 1 file changed, 20 insertions(+), 6 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index d2a500e486ed..44fb06169e4e 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -6577,7 +6577,7 @@ static void bap_sink_match_allocation(size_t i, uint8_t l, uint8_t t, - data->found = false; - } - --static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, -+static struct bt_ltv_match bap_check_bis(uint32_t sink_loc, struct queue *pacs, - struct iovec *bis_data) - { - struct bt_ltv_match compare_data = {}; -@@ -6588,10 +6588,10 @@ static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, - */ - compare_data.found = true; - -- if (ldb->pacs->sink_loc_value) { -+ if (sink_loc) { - uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; - -- compare_data.data32 = ldb->pacs->sink_loc_value; -+ compare_data.data32 = sink_loc; - util_ltv_foreach(bis_data->iov_base, bis_data->iov_len, &type, - bap_sink_match_allocation, &compare_data); - } -@@ -6600,8 +6600,7 @@ static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, - if (compare_data.found) { - compare_data.data = bis_data; - compare_data.found = false; -- queue_foreach(ldb->broadcast_sinks, check_local_pac, -- &compare_data); -+ queue_foreach(pacs, check_local_pac, &compare_data); - } - - return compare_data; -@@ -6642,14 +6641,29 @@ void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, - struct bt_bap_pac **lpac) - { - struct bt_ltv_match match_data; -+ uint32_t sink_loc; -+ struct queue *pacs; - - if (!caps) - return; - -+ /* If the bap session corresponds to a client connection with -+ * a BAP Server, bis caps should be checked against peer caps. -+ * If the bap session corresponds to a scanned broadcast source, -+ * bis caps should be checked against local broadcast sink caps. -+ */ -+ if (bap->client) { -+ sink_loc = bap->rdb->pacs->sink_loc_value; -+ pacs = bap->rdb->sinks; -+ } else { -+ sink_loc = bap->ldb->pacs->sink_loc_value; -+ pacs = bap->ldb->broadcast_sinks; -+ } -+ - /* Check each BIS Codec Specific Configuration LTVs against our Codec - * Specific Capabilities and if the BIS matches create a PAC with it - */ -- match_data = bap_check_bis(bap->ldb, caps); -+ match_data = bap_check_bis(sink_loc, pacs, caps); - if (match_data.found == true) { - *lpac = match_data.data; - DBG(bap, "Matching BIS %i", bis_index); --- -2.45.2 - - -From 2c98c478863ee9e213a4129f0f4fee2b16b678da Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:02 +0300 -Subject: [PATCH 24/46] shared/bap: Append bcast sink pacs to Sink PAC char - -It is mandatory for a BAP Broadcast Sink to support the PACS Server role. -The Sink PAC characteristic should contain PAC records that expose -supported audio capabilities for receiving both unicast and broadcast -streams. - -A BAP Broadcast Assistant acting as a GATT Client needs to discover the -Sink PAC characteristic on the BAP Scan Delegator peer (BAP Broadcast -Sink), in order to discover supported capabilities for receiving streams. - -This commit updates the callback for handling read requests for the Sink -PAC characteristic, to also append Broadcast Sink pac structures to the -read response. ---- - src/shared/bap.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 44fb06169e4e..0aa89c2781ba 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -441,6 +441,7 @@ static void pacs_sink_read(struct gatt_db_attribute *attrib, - iov.iov_len = 0; - - queue_foreach(bdb->sinks, pac_foreach, &iov); -+ queue_foreach(bdb->broadcast_sinks, pac_foreach, &iov); - - gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, - iov.iov_len); --- -2.45.2 - - -From f163913488106929081026c56c236800aa6f8269 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:03 +0300 -Subject: [PATCH 25/46] bap: Add API to get bt_bap matching device - -This adds a public BAP API to obtain a reference to the bt_bap session -with a peer device. ---- - Makefile.plugins | 2 +- - profiles/audio/bap.c | 21 +++++++++++++++++++++ - profiles/audio/bap.h | 10 ++++++++++ - 3 files changed, 32 insertions(+), 1 deletion(-) - create mode 100644 profiles/audio/bap.h - -diff --git a/Makefile.plugins b/Makefile.plugins -index 44fda45367d9..9dd8134b42c5 100644 ---- a/Makefile.plugins -+++ b/Makefile.plugins -@@ -115,7 +115,7 @@ endif - - if BAP - builtin_modules += bap --builtin_sources += profiles/audio/bap.c -+builtin_sources += profiles/audio/bap.h profiles/audio/bap.c - endif - - if BASS -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index b76d5385fa10..79e9cc52791e 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -56,6 +56,8 @@ - #include "src/log.h" - #include "src/error.h" - -+#include "bap.h" -+ - #define ISO_SOCKET_UUID "6fbaf188-05e0-496a-9885-d6ddfdb4e03e" - #define PACS_UUID_STR "00001850-0000-1000-8000-00805f9b34fb" - #define BCAAS_UUID_STR "00001852-0000-1000-8000-00805f9b34fb" -@@ -2751,6 +2753,25 @@ static void pac_removed_broadcast(struct bt_bap_pac *pac, void *user_data) - ep_unregister(ep); - } - -+static bool match_device(const void *data, const void *match_data) -+{ -+ const struct bap_data *bdata = data; -+ const struct btd_device *device = match_data; -+ -+ return bdata->device == device; -+} -+ -+struct bt_bap *bap_get_session(struct btd_device *device) -+{ -+ struct bap_data *data; -+ -+ data = queue_find(sessions, match_device, device); -+ if (!data) -+ return NULL; -+ -+ return data->bap; -+} -+ - static struct bap_data *bap_data_new(struct btd_device *device) - { - struct bap_data *data; -diff --git a/profiles/audio/bap.h b/profiles/audio/bap.h -new file mode 100644 -index 000000000000..66f8db88713a ---- /dev/null -+++ b/profiles/audio/bap.h -@@ -0,0 +1,10 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * -+ * BlueZ - Bluetooth protocol stack for Linux -+ * -+ * Copyright 2024 NXP -+ * -+ */ -+ -+struct bt_bap *bap_get_session(struct btd_device *device); --- -2.45.2 - - -From 88bf423eb525655e15890bdca84d9acb5afab122 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:04 +0300 -Subject: [PATCH 26/46] shared/bass: Add API to get GATT client reference - -Some use cases require the BASS plugin to differentiate between client and -server BASS sessions - for example, the BAP Broadcast Assistant role only -considers client BASS sessions. - -This adds a BASS API to obtain a reference to the bt_gatt_client structure -attached to the bt_bass session. ---- - src/shared/bass.c | 8 ++++++++ - src/shared/bass.h | 1 + - 2 files changed, 9 insertions(+) - -diff --git a/src/shared/bass.c b/src/shared/bass.c -index d82c043ac0db..268e3bd8613e 100644 ---- a/src/shared/bass.c -+++ b/src/shared/bass.c -@@ -1683,6 +1683,14 @@ struct bt_att *bt_bass_get_att(struct bt_bass *bass) - return bt_gatt_client_get_att(bass->client); - } - -+struct bt_gatt_client *bt_bass_get_client(struct bt_bass *bass) -+{ -+ if (!bass) -+ return NULL; -+ -+ return bass->client; -+} -+ - bool bt_bass_set_debug(struct bt_bass *bass, bt_bass_debug_func_t func, - void *user_data, bt_bass_destroy_func_t destroy) - { -diff --git a/src/shared/bass.h b/src/shared/bass.h -index c4b5b76baa53..1674146bca10 100644 ---- a/src/shared/bass.h -+++ b/src/shared/bass.h -@@ -121,6 +121,7 @@ typedef void (*bt_bass_destroy_func_t)(void *user_data); - typedef void (*bt_bass_debug_func_t)(const char *str, void *user_data); - - struct bt_att *bt_bass_get_att(struct bt_bass *bass); -+struct bt_gatt_client *bt_bass_get_client(struct bt_bass *bass); - unsigned int bt_bass_register(bt_bass_func_t attached, bt_bass_func_t detached, - void *user_data); - bool bt_bass_unregister(unsigned int id); --- -2.45.2 - - -From 77e4c0976c0d342e45f0ad1b485efd7e60863e30 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:05 +0300 -Subject: [PATCH 27/46] bass: Register MediaAssistant objects - -This adds an initial implementation of the BAP Broadcast Assistant role -in the BASS plugin, by introducing the MediaAssistant DBus object. - -The BAP plugin implements the callback to probe Broadcast Sources and -parse the BASE. This commit adds 2 BASS APIs, that will be called by the -BAP plugin to notify BISes discovered in the BASE of a broadcaster to -BASS, or to inform the BASS plugin that a broadcaster has been removed. - -For each BASS client session, the BASS plugin checks BIS caps against -the peer caps, and registers a MediaAssistant object for each match. ---- - Makefile.plugins | 2 +- - profiles/audio/bass.c | 257 ++++++++++++++++++++++++++++++++++++++++++ - profiles/audio/bass.h | 13 +++ - 3 files changed, 271 insertions(+), 1 deletion(-) - create mode 100644 profiles/audio/bass.h - -diff --git a/Makefile.plugins b/Makefile.plugins -index 9dd8134b42c5..9da29a3ce43a 100644 ---- a/Makefile.plugins -+++ b/Makefile.plugins -@@ -120,7 +120,7 @@ endif - - if BASS - builtin_modules += bass --builtin_sources += profiles/audio/bass.c -+builtin_sources += profiles/audio/bass.h profiles/audio/bass.c - endif - - if MCP -diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c -index 7952105c51bb..083988358735 100644 ---- a/profiles/audio/bass.c -+++ b/profiles/audio/bass.c -@@ -39,6 +39,7 @@ - #include "src/shared/gatt-server.h" - #include "src/adapter.h" - #include "src/shared/bass.h" -+#include "src/shared/bap.h" - - #include "src/plugin.h" - #include "src/gatt-database.h" -@@ -48,21 +49,265 @@ - #include "src/log.h" - #include "src/error.h" - -+#include "bass.h" -+#include "bap.h" -+ - #define BASS_UUID_STR "0000184f-0000-1000-8000-00805f9b34fb" - -+#define MEDIA_ASSISTANT_INTERFACE "org.bluez.MediaAssistant1" -+ -+enum assistant_state { -+ ASSISTANT_STATE_IDLE, /* Assistant object was created for -+ * the stream -+ */ -+ ASSISTANT_STATE_PENDING, /* Assistant object was pushed */ -+ ASSISTANT_STATE_REQUESTING, /* Remote device requires -+ * Broadcast_Code -+ */ -+ ASSISTANT_STATE_ACTIVE, /* Remote device started receiving -+ * stream -+ */ -+}; -+ - struct bass_data { - struct btd_device *device; - struct btd_service *service; - struct bt_bass *bass; - }; - -+struct bass_assistant { -+ struct btd_device *device; /* Broadcast source device */ -+ struct bass_data *data; /* BASS session with peer device */ -+ uint8_t sgrp; -+ uint8_t bis; -+ struct bt_iso_qos qos; -+ struct iovec *meta; -+ struct iovec *caps; -+ enum assistant_state state; -+ char *path; -+}; -+ - static struct queue *sessions; -+static struct queue *assistants; - - static void bass_debug(const char *str, void *user_data) - { - DBG_IDX(0xffff, "%s", str); - } - -+static DBusMessage *push(DBusConnection *conn, DBusMessage *msg, -+ void *user_data) -+{ -+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -+} -+ -+static const GDBusMethodTable assistant_methods[] = { -+ {GDBUS_EXPERIMENTAL_ASYNC_METHOD("Push", -+ GDBUS_ARGS({ "Props", "a{sv}" }), -+ NULL, push)}, -+ {}, -+}; -+ -+static const char *state2str(enum assistant_state state) -+{ -+ switch (state) { -+ case ASSISTANT_STATE_IDLE: -+ return "idle"; -+ case ASSISTANT_STATE_PENDING: -+ return "pending"; -+ case ASSISTANT_STATE_REQUESTING: -+ return "requesting"; -+ case ASSISTANT_STATE_ACTIVE: -+ return "active"; -+ } -+ -+ return NULL; -+} -+ -+static gboolean get_state(const GDBusPropertyTable *property, -+ DBusMessageIter *iter, void *data) -+{ -+ struct bass_assistant *assistant = data; -+ const char *state = state2str(assistant->state); -+ -+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state); -+ -+ return TRUE; -+} -+ -+static gboolean get_metadata(const GDBusPropertyTable *property, -+ DBusMessageIter *iter, void *data) -+{ -+ struct bass_assistant *assistant = data; -+ DBusMessageIter array; -+ -+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, -+ DBUS_TYPE_BYTE_AS_STRING, &array); -+ -+ if (assistant->meta) -+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, -+ &assistant->meta->iov_base, -+ assistant->meta->iov_len); -+ -+ dbus_message_iter_close_container(iter, &array); -+ -+ return TRUE; -+} -+ -+static gboolean get_qos(const GDBusPropertyTable *property, -+ DBusMessageIter *iter, void *data) -+{ -+ struct bass_assistant *assistant = data; -+ DBusMessageIter dict; -+ uint8_t *bcode = assistant->qos.bcast.bcode; -+ -+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, -+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING -+ DBUS_TYPE_STRING_AS_STRING -+ DBUS_TYPE_VARIANT_AS_STRING -+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, -+ &dict); -+ -+ dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE, -+ &assistant->qos.bcast.encryption); -+ dict_append_array(&dict, "BCode", DBUS_TYPE_BYTE, -+ &bcode, BT_BASS_BCAST_CODE_SIZE); -+ -+ dbus_message_iter_close_container(iter, &dict); -+ -+ return TRUE; -+} -+ -+static const GDBusPropertyTable assistant_properties[] = { -+ { "State", "s", get_state }, -+ { "Metadata", "ay", get_metadata, NULL, NULL, -+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, -+ { "QoS", "a{sv}", get_qos, NULL, NULL, -+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, -+ { } -+}; -+ -+static void assistant_free(void *data) -+{ -+ struct bass_assistant *assistant = data; -+ -+ g_free(assistant->path); -+ util_iov_free(assistant->meta, 1); -+ util_iov_free(assistant->caps, 1); -+ -+ free(assistant); -+} -+ -+static struct bass_assistant *assistant_new(struct btd_adapter *adapter, -+ struct btd_device *device, struct bass_data *data, -+ uint8_t sgrp, uint8_t bis, struct bt_iso_qos *qos, -+ struct iovec *meta, struct iovec *caps) -+{ -+ struct bass_assistant *assistant; -+ char src_addr[18]; -+ char dev_addr[18]; -+ -+ assistant = new0(struct bass_assistant, 1); -+ if (!assistant) -+ return NULL; -+ -+ DBG("assistant %p", assistant); -+ -+ assistant->device = device; -+ assistant->data = data; -+ assistant->sgrp = sgrp; -+ assistant->bis = bis; -+ assistant->qos = *qos; -+ assistant->meta = util_iov_dup(meta, 1); -+ assistant->caps = util_iov_dup(caps, 1); -+ -+ ba2str(device_get_address(device), src_addr); -+ ba2str(device_get_address(data->device), dev_addr); -+ -+ assistant->path = g_strdup_printf("%s/src_%s/dev_%s/bis%d", -+ adapter_get_path(adapter), src_addr, dev_addr, bis); -+ -+ g_strdelimit(assistant->path, ":", '_'); -+ -+ if (!assistants) -+ assistants = queue_new(); -+ -+ queue_push_tail(assistants, assistant); -+ -+ return assistant; -+} -+ -+void bass_add_stream(struct btd_device *device, struct iovec *meta, -+ struct iovec *caps, struct bt_iso_qos *qos, -+ uint8_t sgrp, uint8_t bis) -+{ -+ const struct queue_entry *entry; -+ struct bt_bap *bap; -+ struct bt_bap_pac *pac; -+ struct bass_assistant *assistant; -+ char addr[18]; -+ -+ for (entry = queue_get_entries(sessions); entry; entry = entry->next) { -+ struct bass_data *data = entry->data; -+ struct btd_adapter *adapter = device_get_adapter(data->device); -+ -+ if (!bt_bass_get_client(data->bass)) -+ /* Only client sessions must be handled */ -+ continue; -+ -+ bap = bap_get_session(data->device); -+ if (!bap) -+ continue; -+ -+ /* Check stream capabilities against peer caps. */ -+ bt_bap_verify_bis(bap, bis, caps, &pac); -+ -+ if (!pac) -+ /* Capabilities did not match. */ -+ continue; -+ -+ ba2str(device_get_address(device), addr); -+ -+ DBG("%s data %p BIS %d", addr, data, bis); -+ -+ assistant = assistant_new(adapter, device, data, sgrp, -+ bis, qos, meta, caps); -+ -+ if (g_dbus_register_interface(btd_get_dbus_connection(), -+ assistant->path, -+ MEDIA_ASSISTANT_INTERFACE, -+ assistant_methods, NULL, -+ assistant_properties, -+ assistant, -+ assistant_free) == FALSE) -+ DBG("Could not register path %s", assistant->path); -+ } -+} -+ -+static bool assistant_match_device(const void *data, const void *match_data) -+{ -+ const struct bass_assistant *assistant = data; -+ const struct btd_device *device = match_data; -+ -+ return (assistant->device == device); -+} -+ -+static void unregister_assistant(void *data) -+{ -+ struct bass_assistant *assistant = data; -+ -+ DBG("%p", assistant); -+ -+ g_dbus_unregister_interface(btd_get_dbus_connection(), -+ assistant->path, MEDIA_ASSISTANT_INTERFACE); -+} -+ -+void bass_remove_stream(struct btd_device *device) -+{ -+ queue_remove_all(assistants, assistant_match_device, -+ device, unregister_assistant); -+} -+ - static struct bass_data *bass_data_new(struct btd_device *device) - { - struct bass_data *data; -@@ -101,6 +346,14 @@ static bool match_data(const void *data, const void *match_data) - return bdata->bass == bass; - } - -+static bool assistant_match_data(const void *data, const void *match_data) -+{ -+ const struct bass_assistant *assistant = data; -+ const struct bass_data *bdata = match_data; -+ -+ return (assistant->data == bdata); -+} -+ - static void bass_data_free(struct bass_data *data) - { - if (data->service) { -@@ -109,6 +362,10 @@ static void bass_data_free(struct bass_data *data) - } - - bt_bass_unref(data->bass); -+ -+ queue_remove_all(assistants, assistant_match_data, -+ data, unregister_assistant); -+ - free(data); - } - -diff --git a/profiles/audio/bass.h b/profiles/audio/bass.h -new file mode 100644 -index 000000000000..5bef92946c46 ---- /dev/null -+++ b/profiles/audio/bass.h -@@ -0,0 +1,13 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * -+ * BlueZ - Bluetooth protocol stack for Linux -+ * -+ * Copyright 2024 NXP -+ * -+ */ -+ -+void bass_add_stream(struct btd_device *device, struct iovec *meta, -+ struct iovec *caps, struct bt_iso_qos *qos, -+ uint8_t sgrp, uint8_t bis); -+void bass_remove_stream(struct btd_device *device); --- -2.45.2 - - -From 22779f0bce61cfd5cd72f4e2c199aaa385067248 Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:06 +0300 -Subject: [PATCH 28/46] bap: Notify scanned BISes to BASS - -This updates the BAP implementation to also notify the BASS plugin about -scanned broadcast streams, or when a scanned broadcaster is removed. This -is needed for the BAP Broadcast Assistant role - the BASS plugin registers -MediaAssistant objects for each detected stream that matches the audio -capabilities of peer Scan Delegator devices. ---- - profiles/audio/bap.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index 79e9cc52791e..53f430d66171 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -57,6 +57,7 @@ - #include "src/error.h" - - #include "bap.h" -+#include "bass.h" - - #define ISO_SOCKET_UUID "6fbaf188-05e0-496a-9885-d6ddfdb4e03e" - #define PACS_UUID_STR "00001850-0000-1000-8000-00805f9b34fb" -@@ -1199,6 +1200,9 @@ static bool parse_base(struct bap_data *bap_data, struct bt_iso_base *base, - continue; - } - -+ bass_add_stream(bap_data->device, meta, merged_caps, -+ qos, idx, bis_index); -+ - /* Check if this BIS matches any local PAC */ - bt_bap_verify_bis(bap_data->bap, bis_index, - merged_caps, &matched_lpac); -@@ -3227,6 +3231,8 @@ static void bap_bcast_remove(struct btd_service *service) - free(req); - - bap_data_remove(data); -+ -+ bass_remove_stream(device); - } - - static int bap_probe(struct btd_service *service) --- -2.45.2 - - -From a3f9970f7a8b62b426e7a00303ddb66acb79aadd Mon Sep 17 00:00:00 2001 -From: Iulia Tanasescu -Date: Tue, 16 Jul 2024 17:22:07 +0300 -Subject: [PATCH 29/46] client: Add assistant submenu - -This adds the initial implementation for the assistant menu in -bluetoothctl, to detect and print MediaAssistant objects. - -The current BAP Broadcast Assistant implementation can be tested -by running bluetoothctl, connecting to a BASS Server, scanning -a Broadcast Source that is streaming a number of BISes with -audio capabilities matching the capabilities of the peer device, -and noticing the MediaAssistant objects being created: - -client/bluetoothctl -[bluetooth]# [CHG] Controller 00:60:37:31:7E:3F Pairable: yes -[bluetooth]# AdvertisementMonitor path registered -[bluetooth]# scan on -[bluetooth]# [NEW] Device 00:60:37:94:A6:A3 00-60-37-94-A6-A3 -[bluetooth]# connect 00:60:37:94:A6:A3 -Attempting to connect to 00:60:37:94:A6:A3 -[CHG] Device 00:60:37:94:A6:A3 Connected: yes -[00-60-37-94-A6-A3]# Connection successful -[00-60-37-94-A6-A3]# [NEW] Device 15:65:78:B6:52:F6 15-65-78-B6-52-F6 -[00-60-37-94-A6-A3]# [NEW] Assistant - /org/bluez/hci0/src_15_65_78_B6_52_F6/dev_00_60_37_94_A6_A3/bis1 -[00-60-37-94-A6-A3]# [NEW] Assistant - /org/bluez/hci0/src_15_65_78_B6_52_F6/dev_00_60_37_94_A6_A3/bis2 -[00-60-37-94-A6-A3]# scan off -[00-60-37-94-A6-A3]# Diovery stopped -[00-60-37-94-A6-A3]# disconnect -Attempting to disconnect from 00:60:37:94:A6:A3 -[00-60-37-94-A6-A3]# Successful disconnected -[CHG] Device 00:60:37:94:A6:A3 Connected: no -[bluetooth]# [DEL] Assistant - /org/bluez/hci0/src_15_65_78_B6_52_F6/dev_00_60_37_94_A6_A3/bis1 -[bluetooth]# [DEL] Assistant - /org/bluez/hci0/src_15_65_78_B6_52_F6/dev_00_60_37_94_A6_A3/bis2 ---- - Makefile.tools | 3 +- - client/assistant.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ - client/assistant.h | 13 ++++ - client/main.c | 5 +- - 4 files changed, 183 insertions(+), 2 deletions(-) - create mode 100644 client/assistant.c - create mode 100644 client/assistant.h - -diff --git a/Makefile.tools b/Makefile.tools -index 679c914bf8cd..f4f9e82dc7c4 100644 ---- a/Makefile.tools -+++ b/Makefile.tools -@@ -13,7 +13,8 @@ client_bluetoothctl_SOURCES = client/main.c \ - client/gatt.h client/gatt.c \ - client/admin.h client/admin.c \ - client/player.h client/player.c \ -- client/mgmt.h client/mgmt.c -+ client/mgmt.h client/mgmt.c \ -+ client/assistant.h client/assistant.c - client_bluetoothctl_LDADD = lib/libbluetooth-internal.la \ - gdbus/libgdbus-internal.la src/libshared-glib.la \ - $(GLIB_LIBS) $(DBUS_LIBS) -lreadline -diff --git a/client/assistant.c b/client/assistant.c -new file mode 100644 -index 000000000000..69a955c18655 ---- /dev/null -+++ b/client/assistant.c -@@ -0,0 +1,164 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * -+ * BlueZ - Bluetooth protocol stack for Linux -+ * -+ * Copyright 2024 NXP -+ * -+ * -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "gdbus/gdbus.h" -+ -+#include "lib/bluetooth.h" -+#include "lib/uuid.h" -+ -+#include "src/shared/util.h" -+#include "src/shared/shell.h" -+#include "src/shared/io.h" -+#include "src/shared/queue.h" -+#include "print.h" -+#include "assistant.h" -+ -+/* String display constants */ -+#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF -+#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF -+#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF -+ -+#define MEDIA_ASSISTANT_INTERFACE "org.bluez.MediaAssistant1" -+ -+static DBusConnection *dbus_conn; -+ -+static GList *assistants; -+ -+static char *proxy_description(GDBusProxy *proxy, const char *title, -+ const char *description) -+{ -+ const char *path; -+ -+ path = g_dbus_proxy_get_path(proxy); -+ -+ return g_strdup_printf("%s%s%s%s %s ", -+ description ? "[" : "", -+ description ? : "", -+ description ? "] " : "", -+ title, path); -+} -+ -+static void print_assistant(GDBusProxy *proxy, const char *description) -+{ -+ char *str; -+ -+ str = proxy_description(proxy, "Assistant", description); -+ -+ bt_shell_printf("%s\n", str); -+ -+ g_free(str); -+} -+ -+static void assistant_added(GDBusProxy *proxy) -+{ -+ assistants = g_list_append(assistants, proxy); -+ -+ print_assistant(proxy, COLORED_NEW); -+} -+ -+static void proxy_added(GDBusProxy *proxy, void *user_data) -+{ -+ const char *interface; -+ -+ interface = g_dbus_proxy_get_interface(proxy); -+ -+ if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) -+ assistant_added(proxy); -+} -+ -+static void assistant_removed(GDBusProxy *proxy) -+{ -+ assistants = g_list_remove(assistants, proxy); -+ -+ print_assistant(proxy, COLORED_DEL); -+} -+ -+static void proxy_removed(GDBusProxy *proxy, void *user_data) -+{ -+ const char *interface; -+ -+ interface = g_dbus_proxy_get_interface(proxy); -+ -+ if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) -+ assistant_removed(proxy); -+} -+ -+static void assistant_property_changed(GDBusProxy *proxy, const char *name, -+ DBusMessageIter *iter) -+{ -+ char *str; -+ -+ str = proxy_description(proxy, "Assistant", COLORED_CHG); -+ print_iter(str, name, iter); -+ g_free(str); -+} -+ -+static void property_changed(GDBusProxy *proxy, const char *name, -+ DBusMessageIter *iter, void *user_data) -+{ -+ const char *interface; -+ -+ interface = g_dbus_proxy_get_interface(proxy); -+ -+ if (!strcmp(interface, MEDIA_ASSISTANT_INTERFACE)) -+ assistant_property_changed(proxy, name, iter); -+} -+ -+static void assistant_unregister(void *data) -+{ -+ GDBusProxy *proxy = data; -+ -+ bt_shell_printf("Assistant %s unregistered\n", -+ g_dbus_proxy_get_path(proxy)); -+} -+ -+static void disconnect_handler(DBusConnection *connection, void *user_data) -+{ -+ g_list_free_full(assistants, assistant_unregister); -+ assistants = NULL; -+} -+ -+static GDBusClient * client; -+ -+void assistant_add_submenu(void) -+{ -+ dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); -+ if (!dbus_conn || client) -+ return; -+ -+ client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); -+ -+ g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, -+ property_changed, NULL); -+ g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); -+} -+ -+void assistant_remove_submenu(void) -+{ -+ g_dbus_client_unref(client); -+ client = NULL; -+} -+ -diff --git a/client/assistant.h b/client/assistant.h -new file mode 100644 -index 000000000000..418b0b84031f ---- /dev/null -+++ b/client/assistant.h -@@ -0,0 +1,13 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * -+ * BlueZ - Bluetooth protocol stack for Linux -+ * -+ * Copyright 2024 NXP -+ * -+ * -+ */ -+ -+void assistant_add_submenu(void); -+void assistant_remove_submenu(void); -+ -diff --git a/client/main.c b/client/main.c -index f012ddd436ad..a96a4263849d 100644 ---- a/client/main.c -+++ b/client/main.c -@@ -4,7 +4,7 @@ - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2012 Intel Corporation. All rights reserved. -- * -+ * Copyright 2024 NXP - * - */ - -@@ -34,6 +34,7 @@ - #include "admin.h" - #include "player.h" - #include "mgmt.h" -+#include "assistant.h" - - /* String display constants */ - #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF -@@ -3205,6 +3206,7 @@ int main(int argc, char *argv[]) - admin_add_submenu(); - player_add_submenu(); - mgmt_add_submenu(); -+ assistant_add_submenu(); - - client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); - -@@ -3222,6 +3224,7 @@ int main(int argc, char *argv[]) - admin_remove_submenu(); - player_remove_submenu(); - mgmt_remove_submenu(); -+ assistant_remove_submenu(); - - g_dbus_client_unref(client); - --- -2.45.2 - - -From 4c9d4ed059b539c696e1ebcc92b0cb2522e48a2d Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Mon, 22 Jul 2024 11:58:39 -0400 -Subject: [PATCH 30/46] client/player: Set number of channels based on - locations - -This sets the number of channels based on the locations set rather than -always hardcoding it to 3 which in certain case is incorrect and can -lead for the same location to be configured multiple times. ---- - client/player.c | 19 ++++++++++++------- - 1 file changed, 12 insertions(+), 7 deletions(-) - -diff --git a/client/player.c b/client/player.c -index 5b0b918fb8d7..9334a053d34d 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -1140,10 +1140,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, - .meta = _meta, \ - } - --#define LC3_DATA(_freq, _duration, _chan_count, _len_min, _len_max) \ -+#define LC3_DATA(_freq, _duration, _len_min, _len_max) \ - UTIL_IOV_INIT(0x03, LC3_FREQ, _freq, _freq >> 8, \ - 0x02, LC3_DURATION, _duration, \ -- 0x02, LC3_CHAN_COUNT, _chan_count, \ - 0x05, LC3_FRAME_LEN, _len_min, _len_min >> 8, \ - _len_max, _len_max >> 8) - -@@ -1182,11 +1181,10 @@ static const struct capabilities { - * - * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz - * Duration: 7.5 ms 10 ms -- * Channel count: 3 - * Frame length: 26-240 - */ - CODEC_CAPABILITIES("pac_snk/lc3", PAC_SINK_UUID, LC3_ID, -- LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 3u, 26, -+ LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, - 240), - UTIL_IOV_INIT()), - -@@ -1198,7 +1196,7 @@ static const struct capabilities { - * Frame length: 26-240 - */ - CODEC_CAPABILITIES("pac_src/lc3", PAC_SOURCE_UUID, LC3_ID, -- LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 3u, 26, -+ LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, - 240), - UTIL_IOV_INIT()), - -@@ -1210,7 +1208,7 @@ static const struct capabilities { - * Frame length: 26-240 - */ - CODEC_CAPABILITIES("bcaa/lc3", BCAA_SERVICE_UUID, LC3_ID, -- LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 3u, 26, -+ LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, - 240), - UTIL_IOV_INIT()), - -@@ -1222,7 +1220,7 @@ static const struct capabilities { - * Frame length: 26-240 - */ - CODEC_CAPABILITIES("baa/lc3", BAA_SERVICE_UUID, LC3_ID, -- LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 3u, 26, -+ LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY, 26, - 240), - UTIL_IOV_INIT()), - }; -@@ -3220,6 +3218,7 @@ static void endpoint_locations(const char *input, void *user_data) - struct endpoint *ep = user_data; - char *endptr = NULL; - int value; -+ uint8_t channels; - - value = strtol(input, &endptr, 0); - -@@ -3230,6 +3229,12 @@ static void endpoint_locations(const char *input, void *user_data) - - ep->locations = value; - -+ channels = __builtin_popcount(value); -+ /* Automatically set LC3_CHAN_COUNT if only 1 location is supported */ -+ if (channels == 1) -+ util_ltv_push(ep->caps, sizeof(channels), LC3_CHAN_COUNT, -+ &channels); -+ - bt_shell_prompt_input(ep->path, "Supported Context (value):", - endpoint_supported_context, ep); - } --- -2.45.2 - - -From c2312ebe318413f9e72df505fa236024b57429d4 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Fri, 19 Jul 2024 15:52:29 -0400 -Subject: [PATCH 31/46] client/player: Add support to enter alternative preset - -This adds support for alternative preset to be entered so when auto -accepting configuration a different preset can be selected following the -order given to endpoint.presets. ---- - client/player.c | 120 ++++++++++++++++++++++++++++++++++++++++-------- - 1 file changed, 101 insertions(+), 19 deletions(-) - -diff --git a/client/player.c b/client/player.c -index 9334a053d34d..3c3587f2ca3a 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -1230,7 +1230,10 @@ struct codec_preset { - const struct iovec data; - struct bt_bap_qos qos; - uint8_t target_latency; -+ uint32_t chan_alloc; - bool custom; -+ bool alt; -+ struct codec_preset *alt_preset; - }; - - #define SBC_PRESET(_name, _data) \ -@@ -1969,12 +1972,31 @@ static int parse_chan_alloc(DBusMessageIter *iter, uint32_t *location, - if (*channels) - *channels = __builtin_popcount(*location); - return 0; -+ } else if (!strcasecmp(key, "Locations")) { -+ uint32_t tmp; -+ -+ if (var != DBUS_TYPE_UINT32) -+ return -EINVAL; -+ -+ dbus_message_iter_get_basic(&value, &tmp); -+ *location &= tmp; -+ -+ if (*channels) -+ *channels = __builtin_popcount(*location); - } - - dbus_message_iter_next(iter); - } - -- return -EINVAL; -+ return *location ? 0 : -EINVAL; -+} -+ -+static void ltv_find(size_t i, uint8_t l, uint8_t t, uint8_t *v, -+ void *user_data) -+{ -+ bool *found = user_data; -+ -+ *found = true; - } - - static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, -@@ -1985,7 +2007,7 @@ static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, - DBusMessageIter iter, props; - struct endpoint_config *cfg; - struct bt_bap_io_qos *qos; -- uint32_t location = 0; -+ uint32_t location = ep->locations; - uint8_t channels = 1; - - if (!preset) -@@ -2006,13 +2028,44 @@ static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, - dbus_message_iter_recurse(&iter, &props); - - if (!parse_chan_alloc(&props, &location, &channels)) { -- uint8_t chan_alloc_ltv[] = { -- 0x05, LC3_CONFIG_CHAN_ALLOC, location & 0xff, -- location >> 8, location >> 16, location >> 24 -- }; -+ uint32_t chan_alloc = 0; -+ uint8_t type = LC3_CONFIG_CHAN_ALLOC; -+ bool found = false; -+ -+ if (preset->chan_alloc & location) -+ chan_alloc = preset->chan_alloc & location; -+ else if (preset->alt_preset && -+ preset->alt_preset->chan_alloc & -+ location) { -+ chan_alloc = preset->alt_preset->chan_alloc & location; -+ preset = preset->alt_preset; -+ -+ /* Copy alternate capabilities */ -+ util_iov_free(cfg->caps, 1); -+ cfg->caps = util_iov_dup(&preset->data, 1); -+ cfg->target_latency = preset->target_latency; -+ } else -+ chan_alloc = location; -+ -+ /* Check if Channel Allocation is present in caps */ -+ util_ltv_foreach(cfg->caps->iov_base, cfg->caps->iov_len, -+ &type, ltv_find, &found); - -- util_iov_append(cfg->caps, &chan_alloc_ltv, -+ /* If Channel Allocation has not been set directly via -+ * preset->data then attempt to set it if chan_alloc has been -+ * set. -+ */ -+ if (!found && chan_alloc) { -+ uint8_t chan_alloc_ltv[] = { -+ 0x05, LC3_CONFIG_CHAN_ALLOC, chan_alloc & 0xff, -+ chan_alloc >> 8, chan_alloc >> 16, -+ chan_alloc >> 24 -+ }; -+ -+ put_le32(chan_alloc, &chan_alloc_ltv[2]); -+ util_iov_append(cfg->caps, &chan_alloc_ltv, - sizeof(chan_alloc_ltv)); -+ } - } - - /* Copy metadata */ -@@ -2035,6 +2088,8 @@ static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, - - dbus_message_iter_init_append(reply, &iter); - -+ bt_shell_printf("selecting %s...\n", preset->name); -+ - append_properties(&iter, cfg); - - free(cfg); -@@ -2098,8 +2153,6 @@ static DBusMessage *endpoint_select_properties(DBusConnection *conn, - if (!reply) - return NULL; - -- bt_shell_printf("Auto Accepting using %s...\n", p->name); -- - return reply; - } - -@@ -3621,14 +3674,6 @@ add_meta: - endpoint_set_metadata_cfg, cfg); - } - --static void ltv_find(size_t i, uint8_t l, uint8_t t, uint8_t *v, -- void *user_data) --{ -- bool *found = user_data; -- -- *found = true; --} -- - static void config_endpoint_iso_group(const char *input, void *user_data) - { - struct endpoint_config *cfg = user_data; -@@ -4106,13 +4151,38 @@ static void print_presets(struct preset *preset) - - for (i = 0; i < preset->num_presets; i++) { - p = &preset->presets[i]; -- bt_shell_printf("%s%s\n", p == preset->default_preset ? -- "*" : "", p->name); -+ -+ if (p == preset->default_preset) -+ bt_shell_printf("*%s\n", p->name); -+ else if (preset->default_preset && -+ p == preset->default_preset->alt_preset) -+ bt_shell_printf("**%s\n", p->name); -+ else -+ bt_shell_printf("%s\n", p->name); - } - - queue_foreach(preset->custom, foreach_custom_preset_print, preset); - } - -+static void custom_chan_alloc(const char *input, void *user_data) -+{ -+ struct codec_preset *p = user_data; -+ char *endptr = NULL; -+ -+ p->chan_alloc = strtol(input, &endptr, 0); -+ if (!endptr || *endptr != '\0') { -+ bt_shell_printf("Invalid argument: %s\n", input); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ if (p->alt_preset) -+ bt_shell_prompt_input(p->alt_preset->name, -+ "Enter Channel Allocation: ", -+ custom_chan_alloc, p->alt_preset); -+ else -+ return bt_shell_noninteractive_quit(EXIT_SUCCESS); -+} -+ - static void cmd_presets_endpoint(int argc, char *argv[]) - { - struct preset *preset; -@@ -4133,8 +4203,20 @@ static void cmd_presets_endpoint(int argc, char *argv[]) - preset->default_preset = default_preset; - - if (argc > 4) { -+ struct codec_preset *alt_preset; - struct iovec *iov = (void *)&default_preset->data; - -+ /* Check if and alternative preset was given */ -+ alt_preset = preset_find_name(preset, argv[4]); -+ if (alt_preset) { -+ default_preset->alt_preset = alt_preset; -+ bt_shell_prompt_input(default_preset->name, -+ "Enter Channel Allocation: ", -+ custom_chan_alloc, -+ default_preset); -+ return; -+ } -+ - iov->iov_base = str2bytearray(argv[4], &iov->iov_len); - if (!iov->iov_base) { - bt_shell_printf("Invalid configuration %s\n", --- -2.45.2 - - -From fcf39175e35ef086ffbe4e84ed6dcd3bf4c0aeea Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Tue, 23 Jul 2024 15:49:58 -0400 -Subject: [PATCH 32/46] shared/bap: Fix bt_bap_select with multiple lpacs - -When there are multiple local PAC records of the same codec with -different locations only the first was consider, also bt_bap_select -would stop doing location matching early if the location don't match -without considering there could be more remote channels. ---- - src/shared/bap.c | 35 ++++++++++++++++++++++++++--------- - 1 file changed, 26 insertions(+), 9 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 0aa89c2781ba..499e740c9162 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -3249,25 +3249,32 @@ static void *ltv_merge(struct iovec *data, struct iovec *cont) - return util_iov_append(data, cont->iov_base, cont->iov_len); - } - --static void bap_pac_foreach_channel(size_t i, uint8_t l, uint8_t t, uint8_t *v, -- void *user_data) -+static void bap_pac_chan_add(struct bt_bap_pac *pac, uint8_t count, -+ uint32_t location) - { -- struct bt_bap_pac *pac = user_data; - struct bt_bap_chan *chan; - -- if (!v) -- return; -- - if (!pac->channels) - pac->channels = queue_new(); - - chan = new0(struct bt_bap_chan, 1); -- chan->count = *v; -- chan->location = bt_bap_pac_get_locations(pac) ? : pac->qos.location; -+ chan->count = count; -+ chan->location = location; - - queue_push_tail(pac->channels, chan); - } - -+static void bap_pac_foreach_channel(size_t i, uint8_t l, uint8_t t, uint8_t *v, -+ void *user_data) -+{ -+ struct bt_bap_pac *pac = user_data; -+ -+ if (!v) -+ return; -+ -+ bap_pac_chan_add(pac, *v, bt_bap_pac_get_locations(pac)); -+} -+ - static void bap_pac_update_channels(struct bt_bap_pac *pac, struct iovec *data) - { - uint8_t type = 0x03; -@@ -3277,6 +3284,13 @@ static void bap_pac_update_channels(struct bt_bap_pac *pac, struct iovec *data) - - util_ltv_foreach(data->iov_base, data->iov_len, &type, - bap_pac_foreach_channel, pac); -+ -+ /* If record didn't set a channel count but set a location use that as -+ * channel count. -+ */ -+ if (queue_isempty(pac->channels) && pac->qos.location) -+ bap_pac_chan_add(pac, pac->qos.location, pac->qos.location); -+ - } - - static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data, -@@ -3607,6 +3621,9 @@ uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac) - if (!pac) - return 0; - -+ if (pac->qos.location) -+ return pac->qos.location; -+ - pacs = pac->bdb->pacs; - - switch (pac->type) { -@@ -5411,7 +5428,7 @@ int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, - - /* Try matching the channel location */ - if (!(map.location & rc->location)) -- break; -+ continue; - - lpac->ops->select(lpac, rpac, map.location & - rc->location, &rpac->qos, --- -2.45.2 - - -From 549d38852f665d8251b2c0c0e8c9f3d5574ac99b Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Mon, 29 Jul 2024 12:37:36 +0100 -Subject: [PATCH 33/46] client/player: Fix not setting config target_latency - with edpoint.config - -This fixes not setting target_latency with endpoint.config command. ---- - client/player.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/client/player.c b/client/player.c -index 3c3587f2ca3a..93d6b62e24bf 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -1790,9 +1790,9 @@ static void append_ucast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) - DBUS_TYPE_UINT32, &qos->delay); - - if (cfg->target_latency) { -- bt_shell_printf("TargetLatency 0x%02x\n", qos->target_latency); -+ bt_shell_printf("TargetLatency 0x%02x\n", cfg->target_latency); - g_dbus_dict_append_entry(iter, "TargetLatency", DBUS_TYPE_BYTE, -- &qos->target_latency); -+ &cfg->target_latency); - } - - append_io_qos(iter, &qos->io_qos); -@@ -3765,6 +3765,7 @@ static void cmd_config_endpoint(int argc, char *argv[]) - /* Copy capabilities */ - util_iov_append(cfg->caps, preset->data.iov_base, - preset->data.iov_len); -+ cfg->target_latency = preset->target_latency; - - /* Set QoS parameters */ - cfg->qos = preset->qos; -@@ -3960,7 +3961,7 @@ static void custom_target_latency(const char *input, void *user_data) - else if (!strcasecmp(input, "Balance")) - p->target_latency = 0x02; - else if (!strcasecmp(input, "High")) -- p->target_latency = 0x02; -+ p->target_latency = 0x03; - else { - char *endptr = NULL; - --- -2.45.2 - - -From d7b7f3a39562ca6341254a711ce079b6b8185cd1 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:06 +0300 -Subject: [PATCH 34/46] doc/media: Add 'broadcasting' state and 'Select' method - -This adds a new state for transports created by the Broadcast -Sink. Such transports will remain in the 'idle' state until the -user calls 'Select' on them, at which point they will be moved to -'broadcasting'. This allows the user to select the desired BIS as -the audio server automatically acquires transports that are in this -state. ---- - doc/org.bluez.MediaTransport.rst | 18 +++++++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - -diff --git a/doc/org.bluez.MediaTransport.rst b/doc/org.bluez.MediaTransport.rst -index 6e95df8f2ee8..c8aca0223d24 100644 ---- a/doc/org.bluez.MediaTransport.rst -+++ b/doc/org.bluez.MediaTransport.rst -@@ -7,7 +7,7 @@ BlueZ D-Bus MediaTransport API documentation - -------------------------------------------- - - :Version: BlueZ --:Date: September 2023 -+:Date: July 2024 - :Manual section: 5 - :Manual group: Linux System Administration - -@@ -51,6 +51,20 @@ void Release() - - Releases file descriptor. - -+void Select() -+````````````` -+ -+ Applicable only for transports created by a broadcast sink. This moves -+ the transport from 'idle' to 'broadcasting'. This allows the user to -+ select which BISes he wishes to sync to via a 2 step process: -+ 1) the user calls this method, changing the transport's state to idle -+ 2) the audio server detects that the transport is in the 'broadcasting' -+ state and automatically acquires it -+ -+ Possible Errors: -+ -+ :org.bluez.Error.NotAuthorized: -+ - Properties - ---------- - -@@ -84,6 +98,8 @@ string State [readonly] - - :"idle": not streaming - :"pending": streaming but not acquired -+ :"broadcasting": streaming but not acquired, applicable only for transports -+ created by a broadcast sink - :"active": streaming and acquired - - uint16 Delay [readwrite, optional] --- -2.45.2 - - -From 9357edb87bb98c74677f4c5548a4fe2d589230f8 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:07 +0300 -Subject: [PATCH 35/46] transport: Add 'broadcasting' state - -This adds a new state for transports created by the Broadcast -Sink device as a result of scanning a Broadcast Source. Such -transports will remain in the 'idle' state until the user -selects them using 'transport.select', at which point they will -be moved to 'broadcasting'. ---- - profiles/audio/transport.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c -index 922911cf3fbc..0a890c0ac5f7 100644 ---- a/profiles/audio/transport.c -+++ b/profiles/audio/transport.c -@@ -51,6 +51,10 @@ - typedef enum { - TRANSPORT_STATE_IDLE, /* Not acquired and suspended */ - TRANSPORT_STATE_PENDING, /* Playing but not acquired */ -+ /* Playing but not acquired, applicable only for transports -+ * created by a broadcast sink -+ */ -+ TRANSPORT_STATE_BROADCASTING, - TRANSPORT_STATE_REQUESTING, /* Acquire in progress */ - TRANSPORT_STATE_ACTIVE, /* Acquired and playing */ - TRANSPORT_STATE_SUSPENDING, /* Release in progress */ -@@ -59,6 +63,7 @@ typedef enum { - static const char *str_state[] = { - "TRANSPORT_STATE_IDLE", - "TRANSPORT_STATE_PENDING", -+ "TRANSPORT_STATE_BROADCASTING", - "TRANSPORT_STATE_REQUESTING", - "TRANSPORT_STATE_ACTIVE", - "TRANSPORT_STATE_SUSPENDING", -@@ -139,6 +144,8 @@ static const char *state2str(transport_state_t state) - return "idle"; - case TRANSPORT_STATE_PENDING: - return "pending"; -+ case TRANSPORT_STATE_BROADCASTING: -+ return "broadcasting"; - case TRANSPORT_STATE_ACTIVE: - case TRANSPORT_STATE_SUSPENDING: - return "active"; -@@ -152,6 +159,7 @@ static gboolean state_in_use(transport_state_t state) - switch (state) { - case TRANSPORT_STATE_IDLE: - case TRANSPORT_STATE_PENDING: -+ case TRANSPORT_STATE_BROADCASTING: - return FALSE; - case TRANSPORT_STATE_REQUESTING: - case TRANSPORT_STATE_ACTIVE: -@@ -679,7 +687,8 @@ static DBusMessage *try_acquire(DBusConnection *conn, DBusMessage *msg, - if (transport->state >= TRANSPORT_STATE_REQUESTING) - return btd_error_not_authorized(msg); - -- if (transport->state != TRANSPORT_STATE_PENDING) -+ if ((transport->state != TRANSPORT_STATE_PENDING) && -+ (transport->state != TRANSPORT_STATE_BROADCASTING)) - return btd_error_not_available(msg); - - owner = media_owner_create(msg); -@@ -1281,7 +1290,8 @@ static void transport_update_playing(struct media_transport *transport, - str_state[transport->state], playing); - - if (playing == FALSE) { -- if (transport->state == TRANSPORT_STATE_PENDING) -+ if ((transport->state == TRANSPORT_STATE_PENDING) || -+ (transport->state == TRANSPORT_STATE_BROADCASTING)) - transport_set_state(transport, TRANSPORT_STATE_IDLE); - else if (transport->state == TRANSPORT_STATE_ACTIVE) { - /* Remove owner */ --- -2.45.2 - - -From 083d1a7b66b5c495d2545670d5d255aef340dbf9 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:08 +0300 -Subject: [PATCH 36/46] transport: Add 'Select' method - -This adds the 'Select' method for Broadcast transports. It's role -is to change the transport's state from idle to broadcasting. This -allows the user to select the desired stream when running the setup -with PipeWire since it acquires any transport that is broadcasting. ---- - profiles/audio/transport.c | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c -index 0a890c0ac5f7..bf2215a0f725 100644 ---- a/profiles/audio/transport.c -+++ b/profiles/audio/transport.c -@@ -972,6 +972,9 @@ static gboolean get_endpoint(const GDBusPropertyTable *property, - return TRUE; - } - -+static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, -+ void *data); -+ - static const GDBusMethodTable transport_methods[] = { - { GDBUS_ASYNC_METHOD("Acquire", - NULL, -@@ -984,6 +987,8 @@ static const GDBusMethodTable transport_methods[] = { - { "mtu_w", "q" }), - try_acquire) }, - { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) }, -+ { GDBUS_ASYNC_METHOD("Select", -+ NULL, NULL, select_transport) }, - { }, - }; - -@@ -1302,6 +1307,25 @@ static void transport_update_playing(struct media_transport *transport, - transport_set_state(transport, TRANSPORT_STATE_PENDING); - } - -+static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, -+ void *data) -+{ -+ struct media_transport *transport = data; -+ -+ if (transport->owner != NULL) -+ return btd_error_not_authorized(msg); -+ -+ if (transport->state >= TRANSPORT_STATE_REQUESTING) -+ return btd_error_not_authorized(msg); -+ -+ if (!strcmp(media_endpoint_get_uuid(transport->endpoint), -+ BAA_SERVICE_UUID)) { -+ transport_update_playing(transport, TRUE); -+ } -+ -+ return NULL; -+} -+ - static void sink_state_changed(struct btd_service *service, - sink_state_t old_state, - sink_state_t new_state, --- -2.45.2 - - -From 61e16e3b831754368599fc619ddac31f0db48571 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:09 +0300 -Subject: [PATCH 37/46] client/player: Expose transport 'Select' method to the - user - -This exposes the 'Select' method for Broadcast transports. This -allows the user to select the desired stream when running the setup -with PipeWire since it acquires any transport that is broadcasting. ---- - client/player.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 52 insertions(+) - -diff --git a/client/player.c b/client/player.c -index 93d6b62e24bf..c36e7ff4851b 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -4766,6 +4766,23 @@ static void acquire_reply(DBusMessage *message, void *user_data) - return bt_shell_noninteractive_quit(EXIT_FAILURE); - } - -+static void select_reply(DBusMessage *message, void *user_data) -+{ -+ DBusError error; -+ -+ dbus_error_init(&error); -+ -+ if (dbus_set_error_from_message(&error, message) == TRUE) { -+ bt_shell_printf("Failed to select: %s\n", error.name); -+ dbus_error_free(&error); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ bt_shell_printf("Select successful"); -+ -+ return bt_shell_noninteractive_quit(EXIT_SUCCESS); -+} -+ - static void prompt_acquire(const char *input, void *user_data) - { - GDBusProxy *proxy = user_data; -@@ -4987,6 +5004,38 @@ static void cmd_acquire_transport(int argc, char *argv[]) - return bt_shell_noninteractive_quit(EXIT_SUCCESS); - } - -+static void transport_select(GDBusProxy *proxy, bool prompt) -+{ -+ if (!g_dbus_proxy_method_call(proxy, "Select", NULL, -+ select_reply, proxy, NULL)) { -+ bt_shell_printf("Failed select transport\n"); -+ return; -+ } -+} -+ -+static void cmd_select_transport(int argc, char *argv[]) -+{ -+ GDBusProxy *proxy; -+ int i; -+ -+ for (i = 1; i < argc; i++) { -+ proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], -+ BLUEZ_MEDIA_TRANSPORT_INTERFACE); -+ if (!proxy) { -+ bt_shell_printf("Transport %s not found\n", argv[i]); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ if (find_transport(proxy)) { -+ bt_shell_printf("Transport %s already acquired\n", -+ argv[i]); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ transport_select(proxy, false); -+ } -+} -+ - static void release_reply(DBusMessage *message, void *user_data) - { - struct transport *transport = user_data; -@@ -5415,6 +5464,9 @@ static const struct bt_shell_menu transport_menu = { - { "volume", " [value]", cmd_volume_transport, - "Get/Set transport volume", - transport_generator }, -+ { "select", " [transport1...]", cmd_select_transport, -+ "Select Transport", -+ transport_generator }, - {} }, - }; - --- -2.45.2 - - -From 53a4078cb350f630b19f7fe6ea32dd4e1c01b7bb Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:10 +0300 -Subject: [PATCH 38/46] transport: Broadcast sink: wait for user to select - transport - -This changes the flow for transports created on broadcast sink side. -Transports are not automatically changed to pending anymore, instead -the user must first run transport.select on them which updates the -state to 'broadcasting'. This allows for the selection of the desired -stream when running the setup with PipeWire, which acquires any transport -that is broadcasting. ---- - profiles/audio/transport.c | 15 +++++++++------ - 1 file changed, 9 insertions(+), 6 deletions(-) - -diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c -index bf2215a0f725..80e4f564c861 100644 ---- a/profiles/audio/transport.c -+++ b/profiles/audio/transport.c -@@ -1303,8 +1303,14 @@ static void transport_update_playing(struct media_transport *transport, - if (transport->owner != NULL) - media_transport_remove_owner(transport); - } -- } else if (transport->state == TRANSPORT_STATE_IDLE) -- transport_set_state(transport, TRANSPORT_STATE_PENDING); -+ } else if (transport->state == TRANSPORT_STATE_IDLE) { -+ if (!strcmp(media_endpoint_get_uuid(transport->endpoint), -+ BAA_SERVICE_UUID)) -+ transport_set_state(transport, -+ TRANSPORT_STATE_BROADCASTING); -+ else -+ transport_set_state(transport, TRANSPORT_STATE_PENDING); -+ } - } - - static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, -@@ -1686,10 +1692,7 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, - bap_update_qos(transport); - else if (bt_bap_stream_io_dir(stream) != BT_BAP_BCAST_SOURCE) - bap_update_bcast_qos(transport); -- if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE) -- transport_update_playing(transport, TRUE); -- else -- transport_update_playing(transport, FALSE); -+ transport_update_playing(transport, FALSE); - return; - case BT_BAP_STREAM_STATE_DISABLING: - return; --- -2.45.2 - - -From c7e79fa8bfffff1c7b76cd32ff925ab4613ceb45 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:11 +0300 -Subject: [PATCH 39/46] doc/media: Add 'Unselect' method - -This adds the documentation for a new method, exclusive to transports -created by the Broadcast Sink. It would allow the user to terminate the -sync to a BIS, via a 2 step process. The first step is the call to this -method, which changes the transport's state to idle, with the second step -being done by the audio server which detects this change and releases -the transport. ---- - doc/org.bluez.MediaTransport.rst | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - -diff --git a/doc/org.bluez.MediaTransport.rst b/doc/org.bluez.MediaTransport.rst -index c8aca0223d24..eb3e04ae25c6 100644 ---- a/doc/org.bluez.MediaTransport.rst -+++ b/doc/org.bluez.MediaTransport.rst -@@ -57,7 +57,7 @@ void Select() - Applicable only for transports created by a broadcast sink. This moves - the transport from 'idle' to 'broadcasting'. This allows the user to - select which BISes he wishes to sync to via a 2 step process: -- 1) the user calls this method, changing the transport's state to idle -+ 1) the user calls the method, changing the transport's state to broadcasting - 2) the audio server detects that the transport is in the 'broadcasting' - state and automatically acquires it - -@@ -65,6 +65,19 @@ void Select() - - :org.bluez.Error.NotAuthorized: - -+void Unselect() -+``````````````` -+ -+ Applicable only for transports created by a broadcast sink. This moves -+ the transport from 'broadcasting' or 'active' to 'idle'. This allows the -+ user to terminate the sync to a BIS to via a 2 step process: -+ 1) the user calls this method, changing the transport's state to idle -+ 2) the audio server detects this event and releases the transport -+ -+ Possible Errors: -+ -+ :org.bluez.Error.NotAuthorized: -+ - Properties - ---------- - --- -2.45.2 - - -From 6ee75c3ec383c664cd7e7be02e951999758a6c4f Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:12 +0300 -Subject: [PATCH 40/46] transport: Add 'Unselect' method - -This adds a new method, exclusive to transports created by the Broadcast -Sink. It allows the user to terminate the sync to a BIS, via a 2 step -process. The first step is the call to this method, which changes the -transport's state to idle, with the second step being done by the audio -server which detects this change and releases the transport. ---- - profiles/audio/transport.c | 41 +++++++++++++++++++++++++++++++------- - 1 file changed, 34 insertions(+), 7 deletions(-) - -diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c -index 80e4f564c861..3001457943ac 100644 ---- a/profiles/audio/transport.c -+++ b/profiles/audio/transport.c -@@ -975,6 +975,9 @@ static gboolean get_endpoint(const GDBusPropertyTable *property, - static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, - void *data); - -+static DBusMessage *unselect_transport(DBusConnection *conn, DBusMessage *msg, -+ void *data); -+ - static const GDBusMethodTable transport_methods[] = { - { GDBUS_ASYNC_METHOD("Acquire", - NULL, -@@ -989,6 +992,8 @@ static const GDBusMethodTable transport_methods[] = { - { GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) }, - { GDBUS_ASYNC_METHOD("Select", - NULL, NULL, select_transport) }, -+ { GDBUS_ASYNC_METHOD("Unselect", -+ NULL, NULL, unselect_transport) }, - { }, - }; - -@@ -1295,13 +1300,22 @@ static void transport_update_playing(struct media_transport *transport, - str_state[transport->state], playing); - - if (playing == FALSE) { -- if ((transport->state == TRANSPORT_STATE_PENDING) || -- (transport->state == TRANSPORT_STATE_BROADCASTING)) -- transport_set_state(transport, TRANSPORT_STATE_IDLE); -- else if (transport->state == TRANSPORT_STATE_ACTIVE) { -- /* Remove owner */ -- if (transport->owner != NULL) -- media_transport_remove_owner(transport); -+ if (!strcmp(media_endpoint_get_uuid(transport->endpoint), -+ BCAA_SERVICE_UUID)) { -+ if ((transport->state == -+ TRANSPORT_STATE_BROADCASTING) || -+ (transport->state == TRANSPORT_STATE_ACTIVE)) -+ transport_set_state(transport, -+ TRANSPORT_STATE_IDLE); -+ } else { -+ if (transport->state == TRANSPORT_STATE_PENDING) -+ transport_set_state(transport, -+ TRANSPORT_STATE_IDLE); -+ else if (transport->state == TRANSPORT_STATE_ACTIVE) { -+ /* Remove owner */ -+ if (transport->owner != NULL) -+ media_transport_remove_owner(transport); -+ } - } - } else if (transport->state == TRANSPORT_STATE_IDLE) { - if (!strcmp(media_endpoint_get_uuid(transport->endpoint), -@@ -1332,6 +1346,19 @@ static DBusMessage *select_transport(DBusConnection *conn, DBusMessage *msg, - return NULL; - } - -+static DBusMessage *unselect_transport(DBusConnection *conn, DBusMessage *msg, -+ void *data) -+{ -+ struct media_transport *transport = data; -+ -+ if (!strcmp(media_endpoint_get_uuid(transport->endpoint), -+ BAA_SERVICE_UUID)) { -+ transport_update_playing(transport, FALSE); -+ } -+ -+ return NULL; -+} -+ - static void sink_state_changed(struct btd_service *service, - sink_state_t old_state, - sink_state_t new_state, --- -2.45.2 - - -From 827416638289d901fe5b2bc747fc33cff8b7db99 Mon Sep 17 00:00:00 2001 -From: Vlad Pruteanu -Date: Wed, 31 Jul 2024 09:17:13 +0300 -Subject: [PATCH 41/46] client/player: Expose transport 'Unselect' method to - the user - -This exposes the 'Unselect' method for Broadcast transports. This -allows the user to terminate the sync to a specific BIS, via a 2 -step process. The first step is the call to this method, which -changes the transport's state to idle, with the second step being -done by the audio server which detects this change and releases -the transport. ---- - client/player.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 48 insertions(+) - -diff --git a/client/player.c b/client/player.c -index c36e7ff4851b..f1cd909663eb 100644 ---- a/client/player.c -+++ b/client/player.c -@@ -4783,6 +4783,24 @@ static void select_reply(DBusMessage *message, void *user_data) - return bt_shell_noninteractive_quit(EXIT_SUCCESS); - } - -+static void unselect_reply(DBusMessage *message, void *user_data) -+{ -+ DBusError error; -+ -+ dbus_error_init(&error); -+ -+ if (dbus_set_error_from_message(&error, message) == TRUE) { -+ bt_shell_printf("Failed to unselect: %s\n", error.name); -+ dbus_error_free(&error); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ bt_shell_printf("Select successful"); -+ -+ return bt_shell_noninteractive_quit(EXIT_SUCCESS); -+} -+ -+ - static void prompt_acquire(const char *input, void *user_data) - { - GDBusProxy *proxy = user_data; -@@ -5013,6 +5031,16 @@ static void transport_select(GDBusProxy *proxy, bool prompt) - } - } - -+static void transport_unselect(GDBusProxy *proxy, bool prompt) -+{ -+ if (!g_dbus_proxy_method_call(proxy, "Unselect", NULL, -+ unselect_reply, proxy, NULL)) { -+ bt_shell_printf("Failed unselect transport\n"); -+ return; -+ } -+} -+ -+ - static void cmd_select_transport(int argc, char *argv[]) - { - GDBusProxy *proxy; -@@ -5036,6 +5064,23 @@ static void cmd_select_transport(int argc, char *argv[]) - } - } - -+static void cmd_unselect_transport(int argc, char *argv[]) -+{ -+ GDBusProxy *proxy; -+ int i; -+ -+ for (i = 1; i < argc; i++) { -+ proxy = g_dbus_proxy_lookup(transports, NULL, argv[i], -+ BLUEZ_MEDIA_TRANSPORT_INTERFACE); -+ if (!proxy) { -+ bt_shell_printf("Transport %s not found\n", argv[i]); -+ return bt_shell_noninteractive_quit(EXIT_FAILURE); -+ } -+ -+ transport_unselect(proxy, false); -+ } -+} -+ - static void release_reply(DBusMessage *message, void *user_data) - { - struct transport *transport = user_data; -@@ -5467,6 +5512,9 @@ static const struct bt_shell_menu transport_menu = { - { "select", " [transport1...]", cmd_select_transport, - "Select Transport", - transport_generator }, -+ { "unselect", " [transport1...]", cmd_unselect_transport, -+ "Unselect Transport", -+ transport_generator }, - {} }, - }; - --- -2.45.2 - - -From 720e8ec9760b8d8bfb565e535bd311bbc8273a76 Mon Sep 17 00:00:00 2001 -From: Alexander Ganslandt -Date: Wed, 31 Jul 2024 12:23:21 +0200 -Subject: [PATCH 42/46] client/gatt: Set handle before calling print functions - -The print functions (print_service, print_chrc and print_desc) all print -the handle, but the handle is never set in the struct object. This -results in the handle always printing as 0x0000. Set the handle before -calling the print function. ---- - client/gatt.c | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/client/gatt.c b/client/gatt.c -index e1d2b545d691..4dac8859060b 100644 ---- a/client/gatt.c -+++ b/client/gatt.c -@@ -165,6 +165,7 @@ static void print_service_proxy(GDBusProxy *proxy, const char *description) - DBusMessageIter iter; - const char *uuid; - dbus_bool_t primary; -+ uint16_t handle; - - if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) - return; -@@ -176,10 +177,16 @@ static void print_service_proxy(GDBusProxy *proxy, const char *description) - - dbus_message_iter_get_basic(&iter, &primary); - -+ if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) -+ return; -+ -+ dbus_message_iter_get_basic(&iter, &handle); -+ - memset(&service, 0, sizeof(service)); - service.path = (char *) g_dbus_proxy_get_path(proxy); - service.uuid = (char *) uuid; - service.primary = primary; -+ service.handle = handle; - - print_service(&service, description); - } -@@ -253,15 +260,22 @@ static void print_characteristic(GDBusProxy *proxy, const char *description) - struct chrc chrc; - DBusMessageIter iter; - const char *uuid; -+ uint16_t handle; - - if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) - return; - - dbus_message_iter_get_basic(&iter, &uuid); - -+ if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) -+ return; -+ -+ dbus_message_iter_get_basic(&iter, &handle); -+ - memset(&chrc, 0, sizeof(chrc)); - chrc.path = (char *) g_dbus_proxy_get_path(proxy); - chrc.uuid = (char *) uuid; -+ chrc.handle = handle; - - print_chrc(&chrc, description); - } -@@ -347,15 +361,22 @@ static void print_descriptor(GDBusProxy *proxy, const char *description) - struct desc desc; - DBusMessageIter iter; - const char *uuid; -+ uint16_t handle; - - if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE) - return; - - dbus_message_iter_get_basic(&iter, &uuid); - -+ if (g_dbus_proxy_get_property(proxy, "Handle", &iter) == FALSE) -+ return; -+ -+ dbus_message_iter_get_basic(&iter, &handle); -+ - memset(&desc, 0, sizeof(desc)); - desc.path = (char *) g_dbus_proxy_get_path(proxy); - desc.uuid = (char *) uuid; -+ desc.handle = handle; - - print_desc(&desc, description); - } --- -2.45.2 - - -From 8a708aa5f04613768e903d243a7261efd202ea88 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Wed, 31 Jul 2024 12:15:47 +0100 -Subject: [PATCH 43/46] monitor: Fix crash parsing notification - -This fixes the following crash caused by notify callback being NULL: - -Jump to the invalid address stated on the next line - at 0x0: ??? - by 0x1E8375: print_notify (att.c:5420) - by 0x1E9464: att_multiple_vl_rsp (att.c:5463) - by 0x20D39E: att_packet (att.c:5637) - by 0x1B2054: l2cap_frame (l2cap.c:2567) - by 0x1B4A4D: l2cap_packet (l2cap.c:2708) - by 0x19AD43: packet_hci_acldata (packet.c:12522) - by 0x19CF07: packet_monitor (packet.c:4249) - by 0x152405: data_callback (control.c:973) - by 0x2204F6: mainloop_run (mainloop.c:106) - by 0x221017: mainloop_run_with_signal (mainloop-notify.c:189) - by 0x14F387: main (main.c:298) - Address 0x0 is not stack'd, malloc'd or (recently) free'd ---- - monitor/att.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/monitor/att.c b/monitor/att.c -index a23347ef7ede..73a61658454f 100644 ---- a/monitor/att.c -+++ b/monitor/att.c -@@ -4646,7 +4646,8 @@ static void print_notify(const struct l2cap_frame *frame, uint16_t handle, - frame = &clone; - } - -- handler->notify(frame); -+ if (handler->notify) -+ handler->notify(frame); - } - - static void att_handle_value_notify(const struct l2cap_frame *frame) --- -2.45.2 - - -From fe703a0058d8271a259885d3da4e886400cf4245 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Wed, 31 Jul 2024 12:17:07 +0100 -Subject: [PATCH 44/46] shared/bap: Fix not setting metadata - -bt_bap_stream_metatada shall not send Update Metadata if the states -don't allow it, instead it shall store it so it can be send later when -enabling the stream. ---- - src/shared/bap.c | 13 +++++++++++-- - 1 file changed, 11 insertions(+), 2 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index 499e740c9162..a7217b4177e7 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -1971,8 +1971,17 @@ static unsigned int bap_ucast_metadata(struct bt_bap_stream *stream, - return 0; - } - -- return bap_stream_metadata(stream, BT_ASCS_METADATA, data, func, -- user_data); -+ switch (bt_bap_stream_get_state(stream)) { -+ /* Valid only if ASE_State field = 0x03 (Enabling) */ -+ case BT_BAP_STREAM_STATE_ENABLING: -+ /* or 0x04 (Streaming) */ -+ case BT_BAP_STREAM_STATE_STREAMING: -+ return bap_stream_metadata(stream, BT_ASCS_METADATA, data, func, -+ user_data); -+ } -+ -+ stream_metadata(stream, data, NULL); -+ return 0; - } - - static uint8_t stream_release(struct bt_bap_stream *stream, struct iovec *rsp) --- -2.45.2 - - -From 998104507ba103ae0c83641d381794bf11dd46e0 Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Wed, 31 Jul 2024 12:19:04 +0100 -Subject: [PATCH 45/46] bap: Fix not setting metatada - -Fix not using bt_bap_stream_metadata when configuring a new stream as -the endpoint/client may have set it. ---- - profiles/audio/bap.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c -index 53f430d66171..a2c5a546db47 100644 ---- a/profiles/audio/bap.c -+++ b/profiles/audio/bap.c -@@ -1523,6 +1523,10 @@ static void setup_config(void *data, void *user_data) - return; - } - -+ if (setup->metadata && setup->metadata->iov_len) -+ bt_bap_stream_metadata(setup->stream, setup->metadata, NULL, -+ NULL); -+ - bt_bap_stream_set_user_data(setup->stream, ep->path); - } - --- -2.45.2 - - -From 100c845b2d20e7f4f96b371e044b8b59944230ab Mon Sep 17 00:00:00 2001 -From: Luiz Augusto von Dentz -Date: Thu, 25 Jul 2024 15:17:39 -0400 -Subject: [PATCH 46/46] shared/bap: Fix overwriting sink attribute - -When allocating the sink and sink_ccc attribute they were being -overwriten by source and source_ccc attributes. ---- - src/shared/bap.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/shared/bap.c b/src/shared/bap.c -index a7217b4177e7..9381ebb39a7c 100644 ---- a/src/shared/bap.c -+++ b/src/shared/bap.c -@@ -559,14 +559,14 @@ static struct bt_pacs *pacs_new(struct gatt_db *db) - BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); - - bt_uuid16_create(&uuid, PAC_SOURCE_CHRC_UUID); -- pacs->sink = gatt_db_service_add_characteristic(pacs->service, &uuid, -+ pacs->source = gatt_db_service_add_characteristic(pacs->service, &uuid, - BT_ATT_PERM_READ, - BT_GATT_CHRC_PROP_READ | - BT_GATT_CHRC_PROP_NOTIFY, - pacs_source_read, NULL, - pacs); - -- pacs->sink_ccc = gatt_db_service_add_ccc(pacs->service, -+ pacs->source_ccc = gatt_db_service_add_ccc(pacs->service, - BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); - - bt_uuid16_create(&uuid, PAC_SOURCE_LOC_CHRC_UUID); --- -2.45.2 - diff --git a/5.83-fixes.patch b/5.83-fixes.patch new file mode 100644 index 0000000..a5edd3c --- /dev/null +++ b/5.83-fixes.patch @@ -0,0 +1,174 @@ +From dd718c202276165244ea4c26f99d233c8b6f4cd1 Mon Sep 17 00:00:00 2001 +From: Luiz Augusto von Dentz +Date: Thu, 3 Jul 2025 13:30:22 -0400 +Subject: [PATCH 1/2] shared/shell: Fix not calling pre_run for main menu + +When calling bt_shell_run the main menu pre_run was not being called +which cause tools with just one menu to not work as intended. + +Fixes: https://github.com/bluez/bluez/issues/1319 +--- + src/shared/shell.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/shared/shell.c b/src/shared/shell.c +index f7d237c19203..2fc02a98eda9 100644 +--- a/src/shared/shell.c ++++ b/src/shared/shell.c +@@ -1464,6 +1464,9 @@ int bt_shell_run(void) + int status; + const struct queue_entry *submenu; + ++ if (data.menu && data.menu->pre_run) ++ data.menu->pre_run(data.menu); ++ + for (submenu = queue_get_entries(data.submenus); submenu; + submenu = submenu->next) { + struct bt_shell_menu *menu = submenu->data; +-- +2.50.1 + + +From 13f7ed2e5242ac4e98710f3e97c00bf51e35c2e9 Mon Sep 17 00:00:00 2001 +From: Luiz Augusto von Dentz +Date: Mon, 7 Jul 2025 09:39:43 -0400 +Subject: [PATCH 2/2] shared/shell: Fix not running pre_run on + MODE_NON_INTERACTIVE + +If a command is given to be run in non-interactive mode the code would +not attempt to execute .pre_run first since some (sub)menus requires that +in order to properly initialize things. + +Fixes: https://github.com/bluez/bluez/issues/1394 +Fixes: https://github.com/bluez/bluez/issues/1317 +--- + src/shared/shell.c | 33 +++++++++++++++++++++++---------- + 1 file changed, 23 insertions(+), 10 deletions(-) + +diff --git a/src/shared/shell.c b/src/shared/shell.c +index 2fc02a98eda9..bdfbf2af6f21 100644 +--- a/src/shared/shell.c ++++ b/src/shared/shell.c +@@ -423,7 +423,8 @@ static void cmd_script(int argc, char *argv[]) + return bt_shell_noninteractive_quit(EXIT_SUCCESS); + } + +-static const struct bt_shell_menu_entry default_menu[] = { ++static const struct bt_shell_menu default_menu = { ++ .entries = { + { "back", NULL, cmd_back, "Return to main menu", NULL, + NULL, cmd_back_exists }, + { "menu", "", cmd_menu, "Select submenu", +@@ -437,7 +438,7 @@ static const struct bt_shell_menu_entry default_menu[] = { + { "export", NULL, cmd_export, + "Print environment variables" }, + { "script", "", cmd_script, "Run script" }, +- { } ++ {} }, + }; + + static void shell_print_help(void) +@@ -480,7 +481,7 @@ static void shell_print_menu(void) + print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : ""); + } + +- for (entry = default_menu; entry->cmd; entry++) { ++ for (entry = default_menu.entries; entry->cmd; entry++) { + if (entry->exists && !entry->exists(data.menu)) + continue; + +@@ -495,7 +496,7 @@ static void shell_print_menu_zsh_complete(void) + for (entry = data.menu->entries; entry->cmd; entry++) + printf("%s:%s\n", entry->cmd, entry->desc ? : ""); + +- for (entry = default_menu; entry->cmd; entry++) { ++ for (entry = default_menu.entries; entry->cmd; entry++) { + if (entry->exists && !entry->exists(data.menu)) + continue; + +@@ -642,9 +643,11 @@ fail: + return -EINVAL; + } + +-static int menu_exec(const struct bt_shell_menu_entry *entry, ++static int menu_exec(const struct bt_shell_menu *menu, + int argc, char *argv[]) + { ++ const struct bt_shell_menu_entry *entry = menu->entries; ++ + for (; entry->cmd; entry++) { + if (strcmp(argv[0], entry->cmd)) + continue; +@@ -657,6 +660,9 @@ static int menu_exec(const struct bt_shell_menu_entry *entry, + if (data.menu == data.main && !strcmp(entry->cmd, "back")) + continue; + ++ if (data.mode == MODE_NON_INTERACTIVE && menu->pre_run) ++ menu->pre_run(menu); ++ + return cmd_exec(entry, argc, argv); + } + +@@ -688,7 +694,7 @@ static int submenu_exec(int argc, char *argv[]) + memmove(argv[0], argv[0] + len + 1, tlen - len - 1); + memset(argv[0] + tlen - len - 1, 0, len + 1); + +- return menu_exec(submenu->entries, argc, argv); ++ return menu_exec(submenu, argc, argv); + } + + static int shell_exec(int argc, char *argv[]) +@@ -701,9 +707,9 @@ static int shell_exec(int argc, char *argv[]) + if (!argsisutf8(argc, argv)) + return -EINVAL; + +- err = menu_exec(default_menu, argc, argv); ++ err = menu_exec(&default_menu, argc, argv); + if (err == -ENOENT) { +- err = menu_exec(data.menu->entries, argc, argv); ++ err = menu_exec(data.menu, argc, argv); + if (err == -ENOENT) { + err = submenu_exec(argc, argv); + if (err == -ENOENT) { +@@ -995,7 +1001,7 @@ static char *cmd_generator(const char *text, int state) + } + + if (default_menu_enabled) { +- cmd = find_cmd(text, default_menu, &index); ++ cmd = find_cmd(text, default_menu.entries, &index); + if (cmd) { + return cmd; + } else { +@@ -1186,7 +1192,7 @@ static char **shell_completion(const char *text, int start, int end) + if (_wordexp(rl_line_buffer, &w, WRDE_NOCMD)) + return NULL; + +- matches = menu_completion(default_menu, text, w.we_wordc, ++ matches = menu_completion(default_menu.entries, text, w.we_wordc, + w.we_wordv[0]); + if (!matches) { + matches = menu_completion(data.menu->entries, text, +@@ -1464,6 +1470,12 @@ int bt_shell_run(void) + int status; + const struct queue_entry *submenu; + ++ /* Check if on non-interactive mode skip pre-run since that is on-demand ++ * by shell_exec() only for the menu in use. ++ */ ++ if (data.mode == MODE_NON_INTERACTIVE) ++ goto done; ++ + if (data.menu && data.menu->pre_run) + data.menu->pre_run(data.menu); + +@@ -1475,6 +1487,7 @@ int bt_shell_run(void) + menu->pre_run(menu); + } + ++done: + status = mainloop_run_with_signal(signal_callback, NULL); + + bt_shell_cleanup(); +-- +2.50.1 + diff --git a/bluez.spec b/bluez.spec index 3aa33b1..00b2a98 100644 --- a/bluez.spec +++ b/bluez.spec @@ -6,7 +6,7 @@ Name: bluez Version: 5.83 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Bluetooth utilities License: GPL-2.0-or-later URL: http://www.bluez.org/ @@ -21,6 +21,7 @@ Patch2: 0001-shared-shell-Free-memory-allocated-by-wordexp.patch Patch3: static-analysis-issues-6.patch # Coverity downstream patches Patch4: coverity-workarounds.patch +Patch5: 5.83-fixes.patch BuildRequires: dbus-devel >= 1.6 BuildRequires: glib2-devel @@ -342,6 +343,10 @@ install emulator/btvirt ${RPM_BUILD_ROOT}/%{_libexecdir}/bluetooth/ %{_userunitdir}/obex.service %changelog +* Mon Aug 18 2025 Bastien Nocera - 5.83-2 +- Fix problem with menu handling + Resolves: RHEL-103965 + * Fri Jun 13 2025 Bastien Nocera - 5.83-1 - Update to 5.83 - Resolves: RHEL-94817