Add definitions for new MGMT Controller Information settings bits,
indicating adapter Connected Isochronous Stream - Central/Peripheral
feature support.
The Set Quality Report command was removed in
commit 0454e2d09570 ("mgmt: Add support for Mesh in the kernel"),
but the settings bit was not removed. It's also not implemented on
kernel side, so remove it now.
---
Notes:
Was the quality report setting bit reserved on purpose?
From the commit log it looks like it was forgotten to remove from the
docs, but this is not clear.
doc/mgmt-api.txt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 90d612ed8..58395dc90 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -332,7 +332,8 @@ Read Controller Information Command
15 Static Address
16 PHY Configuration
17 Wideband Speech
- 18 Quality Report
+ 18 Connected Isochronous Stream - Central
+ 19 Connected Isochronous Stream - Peripheral
This command generates a Command Complete event on success or
a Command Status event on failure.
@@ -2925,7 +2926,8 @@ Read Extended Controller Information Command
15 Static Address
16 PHY Configuration
17 Wideband Speech
- 18 Quality Report
+ 18 Connected Isochronous Stream - Central
+ 19 Connected Isochronous Stream - Peripheral
The EIR_Data field contains information about class of device,
local name and other values. Not all of them might be present. For
--
2.39.1
---
tools/btmgmt.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 29f86091f..323c26712 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -353,6 +353,8 @@ static const char *settings_str[] = {
"static-addr",
"phy-configuration",
"wide-band-speech",
+ "cis-central",
+ "cis-peripheral",
};
static const char *settings2str(uint32_t settings)
--
2.39.1
---
monitor/packet.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/monitor/packet.c b/monitor/packet.c
index 44f1941bd..d9e8abf41 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -12649,6 +12649,8 @@ static const struct bitfield_data mgmt_settings_table[] = {
{ 15, "Static Address" },
{ 16, "PHY Configuration" },
{ 17, "Wideband Speech" },
+ { 18, "CIS Central" },
+ { 19, "CIS Peripheral" },
{ }
};
--
2.39.1
---
lib/mgmt.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/mgmt.h b/lib/mgmt.h
index 796190cd9..efbdfb4b1 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -96,6 +96,8 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_STATIC_ADDRESS 0x00008000
#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000
#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000
+#define MGMT_SETTING_CIS_CENTRAL 0x00040000
+#define MGMT_SETTING_CIS_PERIPHERAL 0x00080000
#define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info {
--
2.39.1
---
src/adapter.c | 16 ++++++++++++++++
src/adapter.h | 3 +++
2 files changed, 19 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index aadad4016..4f06bce53 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -9033,6 +9033,22 @@ bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
return false;
}
+bool btd_adapter_cis_central_capable(struct btd_adapter *adapter)
+{
+ if (adapter->current_settings & MGMT_SETTING_CIS_CENTRAL)
+ return true;
+
+ return false;
+}
+
+bool btd_adapter_cis_peripheral_capable(struct btd_adapter *adapter)
+{
+ if (adapter->current_settings & MGMT_SETTING_CIS_PERIPHERAL)
+ return true;
+
+ return false;
+}
+
void btd_adapter_set_oob_handler(struct btd_adapter *adapter,
struct oob_handler *handler)
{
diff --git a/src/adapter.h b/src/adapter.h
index 78eb069ae..3fcee30bc 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -226,6 +226,9 @@ void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
bool btd_adapter_ssp_enabled(struct btd_adapter *adapter);
+bool btd_adapter_cis_central_capable(struct btd_adapter *adapter);
+bool btd_adapter_cis_peripheral_capable(struct btd_adapter *adapter);
+
int adapter_connect_list_add(struct btd_adapter *adapter,
struct btd_device *device);
void adapter_connect_list_remove(struct btd_adapter *adapter,
--
2.39.1
When BT adapter is not CIS Peripheral capable, use the shared/bap code
in its central-only mode, and don't register anything in the local GATT
database.
When BT adapter is not CIS Central capable, ignore the remote device
GATT database, so that we work purely in peripheral mode.
If BT adapter supports neither feature, don't do anything with BAP.
---
profiles/audio/bap.c | 18 ++++++++++++++++--
profiles/audio/media.c | 11 ++++++-----
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index e5ffb7230..28c0d139a 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -1254,6 +1254,8 @@ static int bap_probe(struct btd_service *service)
struct btd_adapter *adapter = device_get_adapter(device);
struct btd_gatt_database *database = btd_adapter_get_database(adapter);
struct bap_data *data = btd_service_get_user_data(service);
+ struct bt_bap_db *ldb;
+ struct gatt_db *device_db;
char addr[18];
ba2str(device_get_address(device), addr);
@@ -1264,17 +1266,29 @@ static int bap_probe(struct btd_service *service)
return -ENOTSUP;
}
+ if (!btd_adapter_cis_central_capable(adapter) &&
+ !btd_adapter_cis_peripheral_capable(adapter)) {
+ DBG("BAP requires CIS features, unsupported by adapter");
+ return -ENOTSUP;
+ }
+
/* Ignore, if we were probed for this device already */
if (data) {
error("Profile probed twice for the same device!");
return -EINVAL;
}
+ if (btd_adapter_cis_central_capable(adapter))
+ device_db = btd_device_get_gatt_db(device);
+ else
+ device_db = NULL;
+
data = bap_data_new(device);
data->service = service;
- data->bap = bt_bap_new(btd_gatt_database_get_db(database),
- btd_device_get_gatt_db(device));
+ ldb = bt_bap_get_local_db(btd_gatt_database_get_db(database),
+ btd_adapter_cis_peripheral_capable(adapter));
+ data->bap = bt_bap_new(ldb, device_db);
if (!data->bap) {
error("Unable to create BAP instance");
free(data);
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index d68085514..6f83b03b5 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1105,8 +1105,9 @@ static void bap_debug(const char *str, void *user_data)
static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
int *err)
{
+ struct btd_adapter *adapter = endpoint->adapter->btd_adapter;
struct btd_gatt_database *database;
- struct gatt_db *db;
+ struct bt_bap_db *ldb;
struct iovec data;
char *name;
@@ -1116,7 +1117,7 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
return false;
}
- database = btd_adapter_get_database(endpoint->adapter->btd_adapter);
+ database = btd_adapter_get_database(adapter);
if (!database) {
error("Adapter database not found");
return false;
@@ -1128,8 +1129,6 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
return false;
}
- db = btd_gatt_database_get_db(database);
-
data.iov_base = endpoint->capabilities;
data.iov_len = endpoint->size;
@@ -1141,7 +1140,9 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
return false;
}
- endpoint->pac = bt_bap_add_pac(db, name, type, endpoint->codec,
+ ldb = bt_bap_get_local_db(btd_gatt_database_get_db(database),
+ btd_adapter_cis_peripheral_capable(adapter));
+ endpoint->pac = bt_bap_add_pac(ldb, name, type, endpoint->codec,
&endpoint->qos, &data, NULL);
if (!endpoint->pac) {
error("Unable to create PAC");
--
2.39.1
Support central-only case, where no local endpoints are created, and
nothing is registered in the local GATT database.
In this case, we keep track of the local PACs ("sinks" and "sources"),
but there are no PACS and ASCS registrations or endpoints.
---
src/shared/bap.c | 58 ++++++++++++++++++++++++++----------------------
src/shared/bap.h | 9 +++++---
unit/test-bap.c | 4 +++-
3 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 22f2e6714..24113bb34 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -110,11 +110,13 @@ struct bt_ascs {
struct bt_bap_db {
struct gatt_db *db;
- struct bt_pacs *pacs;
- struct bt_ascs *ascs;
struct queue *sinks;
struct queue *sources;
+
+ /* The following are NULL if central-only (no GATT) */
struct queue *endpoints;
+ struct bt_pacs *pacs;
+ struct bt_ascs *ascs;
};
struct bt_bap_req {
@@ -565,6 +567,7 @@ static void bap_disconnected(int err, void *user_data)
static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
{
const struct queue_entry *entry;
+ struct bt_bap_db *ldb;
struct bt_bap *bap;
for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
@@ -574,7 +577,8 @@ static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
return bap;
}
- bap = bt_bap_new(db, NULL);
+ ldb = bt_bap_get_local_db(db, true);
+ bap = bt_bap_new(ldb, NULL);
bap->att = att;
bt_bap_attach(bap, NULL);
@@ -620,7 +624,7 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct bt_bap_db *db,
{
struct bt_bap_endpoint *ep;
- if (!db || !attr)
+ if (!db || !attr || !db->endpoints)
return NULL;
ep = queue_find(db->endpoints, bap_endpoint_match, attr);
@@ -652,7 +656,7 @@ static struct bt_bap_endpoint *bap_get_endpoint_id(struct bt_bap *bap,
struct gatt_db_attribute *attr = NULL;
size_t i;
- if (!bap || !db)
+ if (!bap || !db || !db->endpoints)
return NULL;
ep = queue_find(db->endpoints, bap_endpoint_match_id, UINT_TO_PTR(id));
@@ -2170,7 +2174,7 @@ static struct bt_ascs *ascs_new(struct gatt_db *db)
return ascs;
}
-static struct bt_bap_db *bap_db_new(struct gatt_db *db)
+static struct bt_bap_db *bap_db_new(struct gatt_db *db, bool peripheral)
{
struct bt_bap_db *bdb;
@@ -2181,23 +2185,26 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
bdb->db = gatt_db_ref(db);
bdb->sinks = queue_new();
bdb->sources = queue_new();
- bdb->endpoints = queue_new();
if (!bap_db)
bap_db = queue_new();
- bdb->pacs = pacs_new(db);
- bdb->pacs->bdb = bdb;
+ if (peripheral) {
+ bdb->endpoints = queue_new();
- bdb->ascs = ascs_new(db);
- bdb->ascs->bdb = bdb;
+ bdb->pacs = pacs_new(db);
+ bdb->pacs->bdb = bdb;
+
+ bdb->ascs = ascs_new(db);
+ bdb->ascs->bdb = bdb;
+ }
queue_push_tail(bap_db, bdb);
return bdb;
}
-static struct bt_bap_db *bap_get_db(struct gatt_db *db)
+struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral)
{
struct bt_bap_db *bdb;
@@ -2205,7 +2212,7 @@ static struct bt_bap_db *bap_get_db(struct gatt_db *db)
if (bdb)
return bdb;
- return bap_db_new(db);
+ return bap_db_new(db, peripheral);
}
static struct bt_pacs *bap_get_pacs(struct bt_bap *bap)
@@ -2328,6 +2335,9 @@ static void bap_add_sink(struct bt_bap_pac *pac)
queue_push_tail(pac->bdb->sinks, pac);
+ if (!pac->bdb->endpoints)
+ return;
+
memset(value, 0, sizeof(value));
iov.iov_base = value;
@@ -2346,6 +2356,9 @@ static void bap_add_source(struct bt_bap_pac *pac)
queue_push_tail(pac->bdb->sources, pac);
+ if (!pac->bdb->endpoints)
+ return;
+
memset(value, 0, sizeof(value));
iov.iov_base = value;
@@ -2373,21 +2386,16 @@ static void notify_session_pac_added(void *data, void *user_data)
queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
}
-struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
+struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
const char *name, uint8_t type,
uint8_t id, uint16_t cid, uint16_t vid,
struct bt_bap_pac_qos *qos,
struct iovec *data,
struct iovec *metadata)
{
- struct bt_bap_db *bdb;
struct bt_bap_pac *pac;
struct bt_bap_codec codec;
- if (!db)
- return NULL;
-
- bdb = bap_get_db(db);
if (!bdb)
return NULL;
@@ -2417,13 +2425,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
return pac;
}
-struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
+struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
uint8_t type, uint8_t id,
struct bt_bap_pac_qos *qos,
struct iovec *data,
struct iovec *metadata)
{
- return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
+ return bt_bap_add_vendor_pac(bdb, name, type, id, 0x0000, 0x0000, qos,
data, metadata);
}
@@ -2635,7 +2643,7 @@ static void bap_attached(void *data, void *user_data)
cb->attached(bap, cb->user_data);
}
-struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
+struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb)
{
struct bt_bap *bap;
struct bt_bap_db *bdb;
@@ -2643,12 +2651,8 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
if (!ldb)
return NULL;
- bdb = bap_get_db(ldb);
- if (!bdb)
- return NULL;
-
bap = new0(struct bt_bap, 1);
- bap->ldb = bdb;
+ bap->ldb = ldb;
bap->reqs = queue_new();
bap->pending = queue_new();
bap->notify = queue_new();
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 47a15636c..a4e8549a5 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -34,6 +34,7 @@
#define BT_BAP_CONFIG_PHY_CODEC 0x03
struct bt_bap;
+struct bt_bap_db;
struct bt_bap_pac;
struct bt_bap_stream;
@@ -98,14 +99,14 @@ struct bt_bap_pac_qos {
uint32_t ppd_max;
};
-struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
+struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
const char *name, uint8_t type,
uint8_t id, uint16_t cid, uint16_t vid,
struct bt_bap_pac_qos *qos,
struct iovec *data,
struct iovec *metadata);
-struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
+struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
uint8_t type, uint8_t id,
struct bt_bap_pac_qos *qos,
struct iovec *data,
@@ -135,7 +136,9 @@ unsigned int bt_bap_register(bt_bap_func_t added, bt_bap_func_t removed,
void *user_data);
bool bt_bap_unregister(unsigned int id);
-struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb);
+struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral);
+
+struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb);
bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data);
diff --git a/unit/test-bap.c b/unit/test-bap.c
index afeefac84..b677f5197 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
@@ -311,6 +311,7 @@ static void test_client(const void *user_data)
{
struct test_data *data = (void *)user_data;
struct io *io;
+ struct bt_bap_db *ldb;
struct gatt_db *db;
io = tester_setup_io(data->iov, data->iovcnt);
@@ -321,7 +322,8 @@ static void test_client(const void *user_data)
db = gatt_db_new();
g_assert(db);
- data->bap = bt_bap_new(db, bt_gatt_client_get_db(data->client));
+ ldb = bt_bap_get_local_db(db, true);
+ data->bap = bt_bap_new(ldb, bt_gatt_client_get_db(data->client));
g_assert(data->bap);
bt_bap_set_debug(data->bap, print_debug, "bt_bap:", NULL);
--
2.39.1
Don't indicate BAP support in SupportedUUIDs, if adapter supports
neither CIS Central nor Peripheral.
---
profiles/audio/media.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index fbb350889..d68085514 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1259,6 +1259,10 @@ static bool experimental_endpoint_supported(struct btd_adapter *adapter)
if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET))
return false;
+ if (!btd_adapter_cis_central_capable(adapter) &&
+ !btd_adapter_cis_peripheral_capable(adapter))
+ return false;
+
return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
}
--
2.39.1
Hi Pauli,
On Mon, Jan 30, 2023 at 11:06 AM Pauli Virtanen <[email protected]> wrote:
>
> Support central-only case, where no local endpoints are created, and
> nothing is registered in the local GATT database.
>
> In this case, we keep track of the local PACs ("sinks" and "sources"),
> but there are no PACS and ASCS registrations or endpoints.
I'm not really following how that would work without local endpoints
since the endpoints are the interface which is used to setup the
stream with the likes of pipewire/pulseaudio we can't really remove
them, what Id would suggest doing is to have a flag indicating if the
instance is client only or not, that way bt_bap can identify if it
needs to register services or not.
> ---
> src/shared/bap.c | 58 ++++++++++++++++++++++++++----------------------
> src/shared/bap.h | 9 +++++---
> unit/test-bap.c | 4 +++-
> 3 files changed, 40 insertions(+), 31 deletions(-)
>
> diff --git a/src/shared/bap.c b/src/shared/bap.c
> index 22f2e6714..24113bb34 100644
> --- a/src/shared/bap.c
> +++ b/src/shared/bap.c
> @@ -110,11 +110,13 @@ struct bt_ascs {
>
> struct bt_bap_db {
> struct gatt_db *db;
> - struct bt_pacs *pacs;
> - struct bt_ascs *ascs;
> struct queue *sinks;
> struct queue *sources;
> +
> + /* The following are NULL if central-only (no GATT) */
> struct queue *endpoints;
> + struct bt_pacs *pacs;
> + struct bt_ascs *ascs;
> };
>
> struct bt_bap_req {
> @@ -565,6 +567,7 @@ static void bap_disconnected(int err, void *user_data)
> static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> {
> const struct queue_entry *entry;
> + struct bt_bap_db *ldb;
> struct bt_bap *bap;
>
> for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
> @@ -574,7 +577,8 @@ static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> return bap;
> }
>
> - bap = bt_bap_new(db, NULL);
> + ldb = bt_bap_get_local_db(db, true);
> + bap = bt_bap_new(ldb, NULL);
> bap->att = att;
>
> bt_bap_attach(bap, NULL);
> @@ -620,7 +624,7 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct bt_bap_db *db,
> {
> struct bt_bap_endpoint *ep;
>
> - if (!db || !attr)
> + if (!db || !attr || !db->endpoints)
> return NULL;
>
> ep = queue_find(db->endpoints, bap_endpoint_match, attr);
> @@ -652,7 +656,7 @@ static struct bt_bap_endpoint *bap_get_endpoint_id(struct bt_bap *bap,
> struct gatt_db_attribute *attr = NULL;
> size_t i;
>
> - if (!bap || !db)
> + if (!bap || !db || !db->endpoints)
> return NULL;
>
> ep = queue_find(db->endpoints, bap_endpoint_match_id, UINT_TO_PTR(id));
> @@ -2170,7 +2174,7 @@ static struct bt_ascs *ascs_new(struct gatt_db *db)
> return ascs;
> }
>
> -static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> +static struct bt_bap_db *bap_db_new(struct gatt_db *db, bool peripheral)
> {
> struct bt_bap_db *bdb;
>
> @@ -2181,23 +2185,26 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> bdb->db = gatt_db_ref(db);
> bdb->sinks = queue_new();
> bdb->sources = queue_new();
> - bdb->endpoints = queue_new();
>
> if (!bap_db)
> bap_db = queue_new();
>
> - bdb->pacs = pacs_new(db);
> - bdb->pacs->bdb = bdb;
> + if (peripheral) {
> + bdb->endpoints = queue_new();
>
> - bdb->ascs = ascs_new(db);
> - bdb->ascs->bdb = bdb;
> + bdb->pacs = pacs_new(db);
> + bdb->pacs->bdb = bdb;
> +
> + bdb->ascs = ascs_new(db);
> + bdb->ascs->bdb = bdb;
> + }
>
> queue_push_tail(bap_db, bdb);
>
> return bdb;
> }
>
> -static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral)
> {
> struct bt_bap_db *bdb;
>
> @@ -2205,7 +2212,7 @@ static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> if (bdb)
> return bdb;
>
> - return bap_db_new(db);
> + return bap_db_new(db, peripheral);
> }
>
> static struct bt_pacs *bap_get_pacs(struct bt_bap *bap)
> @@ -2328,6 +2335,9 @@ static void bap_add_sink(struct bt_bap_pac *pac)
>
> queue_push_tail(pac->bdb->sinks, pac);
>
> + if (!pac->bdb->endpoints)
> + return;
> +
> memset(value, 0, sizeof(value));
>
> iov.iov_base = value;
> @@ -2346,6 +2356,9 @@ static void bap_add_source(struct bt_bap_pac *pac)
>
> queue_push_tail(pac->bdb->sources, pac);
>
> + if (!pac->bdb->endpoints)
> + return;
> +
> memset(value, 0, sizeof(value));
>
> iov.iov_base = value;
> @@ -2373,21 +2386,16 @@ static void notify_session_pac_added(void *data, void *user_data)
> queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
> }
>
> -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> const char *name, uint8_t type,
> uint8_t id, uint16_t cid, uint16_t vid,
> struct bt_bap_pac_qos *qos,
> struct iovec *data,
> struct iovec *metadata)
> {
> - struct bt_bap_db *bdb;
> struct bt_bap_pac *pac;
> struct bt_bap_codec codec;
>
> - if (!db)
> - return NULL;
> -
> - bdb = bap_get_db(db);
> if (!bdb)
> return NULL;
>
> @@ -2417,13 +2425,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> return pac;
> }
>
> -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> uint8_t type, uint8_t id,
> struct bt_bap_pac_qos *qos,
> struct iovec *data,
> struct iovec *metadata)
> {
> - return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
> + return bt_bap_add_vendor_pac(bdb, name, type, id, 0x0000, 0x0000, qos,
> data, metadata);
> }
>
> @@ -2635,7 +2643,7 @@ static void bap_attached(void *data, void *user_data)
> cb->attached(bap, cb->user_data);
> }
>
> -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb)
> {
> struct bt_bap *bap;
> struct bt_bap_db *bdb;
> @@ -2643,12 +2651,8 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> if (!ldb)
> return NULL;
>
> - bdb = bap_get_db(ldb);
> - if (!bdb)
> - return NULL;
> -
> bap = new0(struct bt_bap, 1);
> - bap->ldb = bdb;
> + bap->ldb = ldb;
> bap->reqs = queue_new();
> bap->pending = queue_new();
> bap->notify = queue_new();
> diff --git a/src/shared/bap.h b/src/shared/bap.h
> index 47a15636c..a4e8549a5 100644
> --- a/src/shared/bap.h
> +++ b/src/shared/bap.h
> @@ -34,6 +34,7 @@
> #define BT_BAP_CONFIG_PHY_CODEC 0x03
>
> struct bt_bap;
> +struct bt_bap_db;
> struct bt_bap_pac;
> struct bt_bap_stream;
>
> @@ -98,14 +99,14 @@ struct bt_bap_pac_qos {
> uint32_t ppd_max;
> };
>
> -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> const char *name, uint8_t type,
> uint8_t id, uint16_t cid, uint16_t vid,
> struct bt_bap_pac_qos *qos,
> struct iovec *data,
> struct iovec *metadata);
>
> -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> uint8_t type, uint8_t id,
> struct bt_bap_pac_qos *qos,
> struct iovec *data,
> @@ -135,7 +136,9 @@ unsigned int bt_bap_register(bt_bap_func_t added, bt_bap_func_t removed,
> void *user_data);
> bool bt_bap_unregister(unsigned int id);
>
> -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb);
> +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral);
> +
> +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb);
>
> bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data);
>
> diff --git a/unit/test-bap.c b/unit/test-bap.c
> index afeefac84..b677f5197 100644
> --- a/unit/test-bap.c
> +++ b/unit/test-bap.c
> @@ -311,6 +311,7 @@ static void test_client(const void *user_data)
> {
> struct test_data *data = (void *)user_data;
> struct io *io;
> + struct bt_bap_db *ldb;
> struct gatt_db *db;
>
> io = tester_setup_io(data->iov, data->iovcnt);
> @@ -321,7 +322,8 @@ static void test_client(const void *user_data)
> db = gatt_db_new();
> g_assert(db);
>
> - data->bap = bt_bap_new(db, bt_gatt_client_get_db(data->client));
> + ldb = bt_bap_get_local_db(db, true);
> + data->bap = bt_bap_new(ldb, bt_gatt_client_get_db(data->client));
> g_assert(data->bap);
>
> bt_bap_set_debug(data->bap, print_debug, "bt_bap:", NULL);
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Luiz,
ma, 2023-01-30 kello 11:39 -0800, Luiz Augusto von Dentz kirjoitti:
> On Mon, Jan 30, 2023 at 11:06 AM Pauli Virtanen <[email protected]> wrote:
> >
> > Support central-only case, where no local endpoints are created, and
> > nothing is registered in the local GATT database.
> >
> > In this case, we keep track of the local PACs ("sinks" and "sources"),
> > but there are no PACS and ASCS registrations or endpoints.
>
> I'm not really following how that would work without local endpoints
> since the endpoints are the interface which is used to setup the
> stream with the likes of pipewire/pulseaudio we can't really remove
> them, what Id would suggest doing is to have a flag indicating if the
> instance is client only or not, that way bt_bap can identify if it
> needs to register services or not.
What I tried to say here is that in the central-only (or client in
other words) mode, no ASEs or PACs are registered in the local GATT DB.
The local endpoints do still exist in the Media1 API. In shared/bap.c
they are always associated with the PACs (not ASEs), so the client-only
mode does not need ASEs. So what is done is to my understanding what
you write above --- maybe I am missing something?
I've tested playback on this patchset for pipewire -> pipewire between
two machines, with the CIS Central flag forced to false on one end, and
CIS Peripheral false on the other end. Also playback to non-BlueZ
device works with CIS Peripheral flag disabled.
>
> > ---
> > src/shared/bap.c | 58 ++++++++++++++++++++++++++----------------------
> > src/shared/bap.h | 9 +++++---
> > unit/test-bap.c | 4 +++-
> > 3 files changed, 40 insertions(+), 31 deletions(-)
> >
> > diff --git a/src/shared/bap.c b/src/shared/bap.c
> > index 22f2e6714..24113bb34 100644
> > --- a/src/shared/bap.c
> > +++ b/src/shared/bap.c
> > @@ -110,11 +110,13 @@ struct bt_ascs {
> >
> > struct bt_bap_db {
> > struct gatt_db *db;
> > - struct bt_pacs *pacs;
> > - struct bt_ascs *ascs;
> > struct queue *sinks;
> > struct queue *sources;
> > +
> > + /* The following are NULL if central-only (no GATT) */
> > struct queue *endpoints;
> > + struct bt_pacs *pacs;
> > + struct bt_ascs *ascs;
> > };
> >
> > struct bt_bap_req {
> > @@ -565,6 +567,7 @@ static void bap_disconnected(int err, void *user_data)
> > static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> > {
> > const struct queue_entry *entry;
> > + struct bt_bap_db *ldb;
> > struct bt_bap *bap;
> >
> > for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
> > @@ -574,7 +577,8 @@ static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> > return bap;
> > }
> >
> > - bap = bt_bap_new(db, NULL);
> > + ldb = bt_bap_get_local_db(db, true);
> > + bap = bt_bap_new(ldb, NULL);
> > bap->att = att;
> >
> > bt_bap_attach(bap, NULL);
> > @@ -620,7 +624,7 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct bt_bap_db *db,
> > {
> > struct bt_bap_endpoint *ep;
> >
> > - if (!db || !attr)
> > + if (!db || !attr || !db->endpoints)
> > return NULL;
> >
> > ep = queue_find(db->endpoints, bap_endpoint_match, attr);
> > @@ -652,7 +656,7 @@ static struct bt_bap_endpoint *bap_get_endpoint_id(struct bt_bap *bap,
> > struct gatt_db_attribute *attr = NULL;
> > size_t i;
> >
> > - if (!bap || !db)
> > + if (!bap || !db || !db->endpoints)
> > return NULL;
> >
> > ep = queue_find(db->endpoints, bap_endpoint_match_id, UINT_TO_PTR(id));
> > @@ -2170,7 +2174,7 @@ static struct bt_ascs *ascs_new(struct gatt_db *db)
> > return ascs;
> > }
> >
> > -static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> > +static struct bt_bap_db *bap_db_new(struct gatt_db *db, bool peripheral)
> > {
> > struct bt_bap_db *bdb;
> >
> > @@ -2181,23 +2185,26 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> > bdb->db = gatt_db_ref(db);
> > bdb->sinks = queue_new();
> > bdb->sources = queue_new();
> > - bdb->endpoints = queue_new();
> >
> > if (!bap_db)
> > bap_db = queue_new();
> >
> > - bdb->pacs = pacs_new(db);
> > - bdb->pacs->bdb = bdb;
> > + if (peripheral) {
> > + bdb->endpoints = queue_new();
> >
> > - bdb->ascs = ascs_new(db);
> > - bdb->ascs->bdb = bdb;
> > + bdb->pacs = pacs_new(db);
> > + bdb->pacs->bdb = bdb;
> > +
> > + bdb->ascs = ascs_new(db);
> > + bdb->ascs->bdb = bdb;
> > + }
> >
> > queue_push_tail(bap_db, bdb);
> >
> > return bdb;
> > }
> >
> > -static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> > +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral)
> > {
> > struct bt_bap_db *bdb;
> >
> > @@ -2205,7 +2212,7 @@ static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> > if (bdb)
> > return bdb;
> >
> > - return bap_db_new(db);
> > + return bap_db_new(db, peripheral);
> > }
> >
> > static struct bt_pacs *bap_get_pacs(struct bt_bap *bap)
> > @@ -2328,6 +2335,9 @@ static void bap_add_sink(struct bt_bap_pac *pac)
> >
> > queue_push_tail(pac->bdb->sinks, pac);
> >
> > + if (!pac->bdb->endpoints)
> > + return;
> > +
> > memset(value, 0, sizeof(value));
> >
> > iov.iov_base = value;
> > @@ -2346,6 +2356,9 @@ static void bap_add_source(struct bt_bap_pac *pac)
> >
> > queue_push_tail(pac->bdb->sources, pac);
> >
> > + if (!pac->bdb->endpoints)
> > + return;
> > +
> > memset(value, 0, sizeof(value));
> >
> > iov.iov_base = value;
> > @@ -2373,21 +2386,16 @@ static void notify_session_pac_added(void *data, void *user_data)
> > queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
> > }
> >
> > -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> > const char *name, uint8_t type,
> > uint8_t id, uint16_t cid, uint16_t vid,
> > struct bt_bap_pac_qos *qos,
> > struct iovec *data,
> > struct iovec *metadata)
> > {
> > - struct bt_bap_db *bdb;
> > struct bt_bap_pac *pac;
> > struct bt_bap_codec codec;
> >
> > - if (!db)
> > - return NULL;
> > -
> > - bdb = bap_get_db(db);
> > if (!bdb)
> > return NULL;
> >
> > @@ -2417,13 +2425,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > return pac;
> > }
> >
> > -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> > +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> > uint8_t type, uint8_t id,
> > struct bt_bap_pac_qos *qos,
> > struct iovec *data,
> > struct iovec *metadata)
> > {
> > - return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
> > + return bt_bap_add_vendor_pac(bdb, name, type, id, 0x0000, 0x0000, qos,
> > data, metadata);
> > }
> >
> > @@ -2635,7 +2643,7 @@ static void bap_attached(void *data, void *user_data)
> > cb->attached(bap, cb->user_data);
> > }
> >
> > -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> > +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb)
> > {
> > struct bt_bap *bap;
> > struct bt_bap_db *bdb;
> > @@ -2643,12 +2651,8 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> > if (!ldb)
> > return NULL;
> >
> > - bdb = bap_get_db(ldb);
> > - if (!bdb)
> > - return NULL;
> > -
> > bap = new0(struct bt_bap, 1);
> > - bap->ldb = bdb;
> > + bap->ldb = ldb;
> > bap->reqs = queue_new();
> > bap->pending = queue_new();
> > bap->notify = queue_new();
> > diff --git a/src/shared/bap.h b/src/shared/bap.h
> > index 47a15636c..a4e8549a5 100644
> > --- a/src/shared/bap.h
> > +++ b/src/shared/bap.h
> > @@ -34,6 +34,7 @@
> > #define BT_BAP_CONFIG_PHY_CODEC 0x03
> >
> > struct bt_bap;
> > +struct bt_bap_db;
> > struct bt_bap_pac;
> > struct bt_bap_stream;
> >
> > @@ -98,14 +99,14 @@ struct bt_bap_pac_qos {
> > uint32_t ppd_max;
> > };
> >
> > -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> > const char *name, uint8_t type,
> > uint8_t id, uint16_t cid, uint16_t vid,
> > struct bt_bap_pac_qos *qos,
> > struct iovec *data,
> > struct iovec *metadata);
> >
> > -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> > +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> > uint8_t type, uint8_t id,
> > struct bt_bap_pac_qos *qos,
> > struct iovec *data,
> > @@ -135,7 +136,9 @@ unsigned int bt_bap_register(bt_bap_func_t added, bt_bap_func_t removed,
> > void *user_data);
> > bool bt_bap_unregister(unsigned int id);
> >
> > -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb);
> > +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral);
> > +
> > +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb);
> >
> > bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data);
> >
> > diff --git a/unit/test-bap.c b/unit/test-bap.c
> > index afeefac84..b677f5197 100644
> > --- a/unit/test-bap.c
> > +++ b/unit/test-bap.c
> > @@ -311,6 +311,7 @@ static void test_client(const void *user_data)
> > {
> > struct test_data *data = (void *)user_data;
> > struct io *io;
> > + struct bt_bap_db *ldb;
> > struct gatt_db *db;
> >
> > io = tester_setup_io(data->iov, data->iovcnt);
> > @@ -321,7 +322,8 @@ static void test_client(const void *user_data)
> > db = gatt_db_new();
> > g_assert(db);
> >
> > - data->bap = bt_bap_new(db, bt_gatt_client_get_db(data->client));
> > + ldb = bt_bap_get_local_db(db, true);
> > + data->bap = bt_bap_new(ldb, bt_gatt_client_get_db(data->client));
> > g_assert(data->bap);
> >
> > bt_bap_set_debug(data->bap, print_debug, "bt_bap:", NULL);
> > --
> > 2.39.1
> >
>
>
--
Pauli Virtanen
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=717083
---Test result---
Test Summary:
CheckPatch FAIL 2.20 seconds
GitLint FAIL 1.53 seconds
BuildEll PASS 27.04 seconds
BluezMake PASS 745.55 seconds
MakeCheck PASS 11.00 seconds
MakeDistcheck PASS 146.65 seconds
CheckValgrind PASS 238.51 seconds
CheckSmatch WARNING 317.34 seconds
bluezmakeextell PASS 95.56 seconds
IncrementalBuild PASS 3050.31 seconds
ScanBuild PASS 938.84 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,1/8] doc: add MGMT setting for CIS features, remove Quality Report
WARNING:UNKNOWN_COMMIT_ID: Unknown commit id '0454e2d09570', maybe rebased or not pulled?
#99:
commit 0454e2d09570 ("mgmt: Add support for Mesh in the kernel"),
/github/workspace/src/src/13121669.patch total: 0 errors, 1 warnings, 18 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/src/13121669.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,1/8] doc: add MGMT setting for CIS features, remove Quality Report
WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
15: B2 Line has trailing whitespace: " "
[BlueZ,4/8] tools/btmgmt: add names for MGMT setting bits for CIS feature support
WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
1: T1 Title exceeds max length (81>80): "[BlueZ,4/8] tools/btmgmt: add names for MGMT setting bits for CIS feature support"
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
monitor/packet.c: note: in included file:monitor/display.h:82:26: warning: Variable length array is used.monitor/packet.c:1799:26: warning: Variable length array is used.monitor/packet.c: note: in included file:monitor/bt.h:3551:52: warning: array of flexible structuresmonitor/bt.h:3539:40: warning: array of flexible structures
---
Regards,
Linux Bluetooth
Hi Pauli,
On Mon, Jan 30, 2023 at 10:43 AM Pauli Virtanen <[email protected]> wrote:
>
> Add definitions for new MGMT Controller Information settings bits,
> indicating adapter Connected Isochronous Stream - Central/Peripheral
> feature support.
>
> The Set Quality Report command was removed in
> commit 0454e2d09570 ("mgmt: Add support for Mesh in the kernel"),
> but the settings bit was not removed. It's also not implemented on
> kernel side, so remove it now.
Let's split this into 2 patches, one removing Quality Report, since it
was never implemented, and then another including the new flags.
> ---
>
> Notes:
> Was the quality report setting bit reserved on purpose?
>
> From the commit log it looks like it was forgotten to remove from the
> docs, but this is not clear.
>
> doc/mgmt-api.txt | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
> index 90d612ed8..58395dc90 100644
> --- a/doc/mgmt-api.txt
> +++ b/doc/mgmt-api.txt
> @@ -332,7 +332,8 @@ Read Controller Information Command
> 15 Static Address
> 16 PHY Configuration
> 17 Wideband Speech
> - 18 Quality Report
> + 18 Connected Isochronous Stream - Central
> + 19 Connected Isochronous Stream - Peripheral
>
> This command generates a Command Complete event on success or
> a Command Status event on failure.
> @@ -2925,7 +2926,8 @@ Read Extended Controller Information Command
> 15 Static Address
> 16 PHY Configuration
> 17 Wideband Speech
> - 18 Quality Report
> + 18 Connected Isochronous Stream - Central
> + 19 Connected Isochronous Stream - Peripheral
>
> The EIR_Data field contains information about class of device,
> local name and other values. Not all of them might be present. For
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 10:58 AM Pauli Virtanen <[email protected]> wrote:
>
> ---
> src/adapter.c | 16 ++++++++++++++++
> src/adapter.h | 3 +++
> 2 files changed, 19 insertions(+)
>
> diff --git a/src/adapter.c b/src/adapter.c
> index aadad4016..4f06bce53 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -9033,6 +9033,22 @@ bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
> return false;
> }
>
> +bool btd_adapter_cis_central_capable(struct btd_adapter *adapter)
> +{
> + if (adapter->current_settings & MGMT_SETTING_CIS_CENTRAL)
> + return true;
> +
> + return false;
> +}
> +
> +bool btd_adapter_cis_peripheral_capable(struct btd_adapter *adapter)
> +{
> + if (adapter->current_settings & MGMT_SETTING_CIS_PERIPHERAL)
> + return true;
> +
> + return false;
> +}
> +
> void btd_adapter_set_oob_handler(struct btd_adapter *adapter,
> struct oob_handler *handler)
> {
> diff --git a/src/adapter.h b/src/adapter.h
> index 78eb069ae..3fcee30bc 100644
> --- a/src/adapter.h
> +++ b/src/adapter.h
> @@ -226,6 +226,9 @@ void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
>
> bool btd_adapter_ssp_enabled(struct btd_adapter *adapter);
>
> +bool btd_adapter_cis_central_capable(struct btd_adapter *adapter);
> +bool btd_adapter_cis_peripheral_capable(struct btd_adapter *adapter);
Lets just have btd_adapter_capable(struct btd_adapter *adapter, int
flags) to make it simple to check multiple flags if needed.
> int adapter_connect_list_add(struct btd_adapter *adapter,
> struct btd_device *device);
> void adapter_connect_list_remove(struct btd_adapter *adapter,
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 10:48 AM Pauli Virtanen <[email protected]> wrote:
Let's have the btmon output in the description to make it easier to
visualize how this changes affect it.
> ---
> monitor/packet.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/monitor/packet.c b/monitor/packet.c
> index 44f1941bd..d9e8abf41 100644
> --- a/monitor/packet.c
> +++ b/monitor/packet.c
> @@ -12649,6 +12649,8 @@ static const struct bitfield_data mgmt_settings_table[] = {
> { 15, "Static Address" },
> { 16, "PHY Configuration" },
> { 17, "Wideband Speech" },
> + { 18, "CIS Central" },
> + { 19, "CIS Peripheral" },
> { }
> };
>
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 10:56 AM Pauli Virtanen <[email protected]> wrote:
>
> ---
> lib/mgmt.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/lib/mgmt.h b/lib/mgmt.h
> index 796190cd9..efbdfb4b1 100644
> --- a/lib/mgmt.h
> +++ b/lib/mgmt.h
> @@ -96,6 +96,8 @@ struct mgmt_rp_read_index_list {
> #define MGMT_SETTING_STATIC_ADDRESS 0x00008000
> #define MGMT_SETTING_PHY_CONFIGURATION 0x00010000
> #define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000
> +#define MGMT_SETTING_CIS_CENTRAL 0x00040000
> +#define MGMT_SETTING_CIS_PERIPHERAL 0x00080000
Perhaps now it would be a good time to convert these to use BIT macro.
> #define MGMT_OP_READ_INFO 0x0004
> struct mgmt_rp_read_info {
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 10:50 AM Pauli Virtanen <[email protected]> wrote:
Add the btmgmt output as a description showing the result of these changes.
> ---
> tools/btmgmt.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/tools/btmgmt.c b/tools/btmgmt.c
> index 29f86091f..323c26712 100644
> --- a/tools/btmgmt.c
> +++ b/tools/btmgmt.c
> @@ -353,6 +353,8 @@ static const char *settings_str[] = {
> "static-addr",
> "phy-configuration",
> "wide-band-speech",
> + "cis-central",
> + "cis-peripheral",
> };
>
> static const char *settings2str(uint32_t settings)
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 12:04 PM Pauli Virtanen <[email protected]> wrote:
>
> Hi Luiz,
>
> ma, 2023-01-30 kello 11:39 -0800, Luiz Augusto von Dentz kirjoitti:
> > On Mon, Jan 30, 2023 at 11:06 AM Pauli Virtanen <[email protected]> wrote:
> > >
> > > Support central-only case, where no local endpoints are created, and
> > > nothing is registered in the local GATT database.
> > >
> > > In this case, we keep track of the local PACs ("sinks" and "sources"),
> > > but there are no PACS and ASCS registrations or endpoints.
> >
> > I'm not really following how that would work without local endpoints
> > since the endpoints are the interface which is used to setup the
> > stream with the likes of pipewire/pulseaudio we can't really remove
> > them, what Id would suggest doing is to have a flag indicating if the
> > instance is client only or not, that way bt_bap can identify if it
> > needs to register services or not.
>
> What I tried to say here is that in the central-only (or client in
> other words) mode, no ASEs or PACs are registered in the local GATT DB.
> The local endpoints do still exist in the Media1 API. In shared/bap.c
> they are always associated with the PACs (not ASEs), so the client-only
> mode does not need ASEs. So what is done is to my understanding what
> you write above --- maybe I am missing something?
Make sense.
> I've tested playback on this patchset for pipewire -> pipewire between
> two machines, with the CIS Central flag forced to false on one end, and
> CIS Peripheral false on the other end. Also playback to non-BlueZ
> device works with CIS Peripheral flag disabled.
Alright, make sure it also works with the likes of bluetoothctl -e as
well, since that is likely how the CI will be testing this in the
future.
> >
> > > ---
> > > src/shared/bap.c | 58 ++++++++++++++++++++++++++----------------------
> > > src/shared/bap.h | 9 +++++---
> > > unit/test-bap.c | 4 +++-
> > > 3 files changed, 40 insertions(+), 31 deletions(-)
> > >
> > > diff --git a/src/shared/bap.c b/src/shared/bap.c
> > > index 22f2e6714..24113bb34 100644
> > > --- a/src/shared/bap.c
> > > +++ b/src/shared/bap.c
> > > @@ -110,11 +110,13 @@ struct bt_ascs {
> > >
> > > struct bt_bap_db {
> > > struct gatt_db *db;
> > > - struct bt_pacs *pacs;
> > > - struct bt_ascs *ascs;
> > > struct queue *sinks;
> > > struct queue *sources;
> > > +
> > > + /* The following are NULL if central-only (no GATT) */
> > > struct queue *endpoints;
> > > + struct bt_pacs *pacs;
> > > + struct bt_ascs *ascs;
> > > };
> > >
> > > struct bt_bap_req {
> > > @@ -565,6 +567,7 @@ static void bap_disconnected(int err, void *user_data)
> > > static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> > > {
> > > const struct queue_entry *entry;
> > > + struct bt_bap_db *ldb;
> > > struct bt_bap *bap;
> > >
> > > for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
> > > @@ -574,7 +577,8 @@ static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> > > return bap;
> > > }
> > >
> > > - bap = bt_bap_new(db, NULL);
> > > + ldb = bt_bap_get_local_db(db, true);
> > > + bap = bt_bap_new(ldb, NULL);
> > > bap->att = att;
> > >
> > > bt_bap_attach(bap, NULL);
> > > @@ -620,7 +624,7 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct bt_bap_db *db,
> > > {
> > > struct bt_bap_endpoint *ep;
> > >
> > > - if (!db || !attr)
> > > + if (!db || !attr || !db->endpoints)
> > > return NULL;
> > >
> > > ep = queue_find(db->endpoints, bap_endpoint_match, attr);
> > > @@ -652,7 +656,7 @@ static struct bt_bap_endpoint *bap_get_endpoint_id(struct bt_bap *bap,
> > > struct gatt_db_attribute *attr = NULL;
> > > size_t i;
> > >
> > > - if (!bap || !db)
> > > + if (!bap || !db || !db->endpoints)
> > > return NULL;
> > >
> > > ep = queue_find(db->endpoints, bap_endpoint_match_id, UINT_TO_PTR(id));
> > > @@ -2170,7 +2174,7 @@ static struct bt_ascs *ascs_new(struct gatt_db *db)
> > > return ascs;
> > > }
> > >
> > > -static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> > > +static struct bt_bap_db *bap_db_new(struct gatt_db *db, bool peripheral)
> > > {
> > > struct bt_bap_db *bdb;
> > >
> > > @@ -2181,23 +2185,26 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
> > > bdb->db = gatt_db_ref(db);
> > > bdb->sinks = queue_new();
> > > bdb->sources = queue_new();
> > > - bdb->endpoints = queue_new();
> > >
> > > if (!bap_db)
> > > bap_db = queue_new();
> > >
> > > - bdb->pacs = pacs_new(db);
> > > - bdb->pacs->bdb = bdb;
> > > + if (peripheral) {
> > > + bdb->endpoints = queue_new();
> > >
> > > - bdb->ascs = ascs_new(db);
> > > - bdb->ascs->bdb = bdb;
> > > + bdb->pacs = pacs_new(db);
> > > + bdb->pacs->bdb = bdb;
> > > +
> > > + bdb->ascs = ascs_new(db);
> > > + bdb->ascs->bdb = bdb;
> > > + }
> > >
> > > queue_push_tail(bap_db, bdb);
> > >
> > > return bdb;
> > > }
> > >
> > > -static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> > > +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral)
> > > {
> > > struct bt_bap_db *bdb;
> > >
> > > @@ -2205,7 +2212,7 @@ static struct bt_bap_db *bap_get_db(struct gatt_db *db)
> > > if (bdb)
> > > return bdb;
> > >
> > > - return bap_db_new(db);
> > > + return bap_db_new(db, peripheral);
> > > }
> > >
> > > static struct bt_pacs *bap_get_pacs(struct bt_bap *bap)
> > > @@ -2328,6 +2335,9 @@ static void bap_add_sink(struct bt_bap_pac *pac)
> > >
> > > queue_push_tail(pac->bdb->sinks, pac);
> > >
> > > + if (!pac->bdb->endpoints)
> > > + return;
> > > +
> > > memset(value, 0, sizeof(value));
> > >
> > > iov.iov_base = value;
> > > @@ -2346,6 +2356,9 @@ static void bap_add_source(struct bt_bap_pac *pac)
> > >
> > > queue_push_tail(pac->bdb->sources, pac);
> > >
> > > + if (!pac->bdb->endpoints)
> > > + return;
> > > +
> > > memset(value, 0, sizeof(value));
> > >
> > > iov.iov_base = value;
> > > @@ -2373,21 +2386,16 @@ static void notify_session_pac_added(void *data, void *user_data)
> > > queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
> > > }
> > >
> > > -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > > +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> > > const char *name, uint8_t type,
> > > uint8_t id, uint16_t cid, uint16_t vid,
> > > struct bt_bap_pac_qos *qos,
> > > struct iovec *data,
> > > struct iovec *metadata)
> > > {
> > > - struct bt_bap_db *bdb;
> > > struct bt_bap_pac *pac;
> > > struct bt_bap_codec codec;
> > >
> > > - if (!db)
> > > - return NULL;
> > > -
> > > - bdb = bap_get_db(db);
> > > if (!bdb)
> > > return NULL;
> > >
> > > @@ -2417,13 +2425,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > > return pac;
> > > }
> > >
> > > -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> > > +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> > > uint8_t type, uint8_t id,
> > > struct bt_bap_pac_qos *qos,
> > > struct iovec *data,
> > > struct iovec *metadata)
> > > {
> > > - return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
> > > + return bt_bap_add_vendor_pac(bdb, name, type, id, 0x0000, 0x0000, qos,
> > > data, metadata);
> > > }
> > >
> > > @@ -2635,7 +2643,7 @@ static void bap_attached(void *data, void *user_data)
> > > cb->attached(bap, cb->user_data);
> > > }
> > >
> > > -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> > > +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb)
> > > {
> > > struct bt_bap *bap;
> > > struct bt_bap_db *bdb;
> > > @@ -2643,12 +2651,8 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> > > if (!ldb)
> > > return NULL;
> > >
> > > - bdb = bap_get_db(ldb);
> > > - if (!bdb)
> > > - return NULL;
> > > -
> > > bap = new0(struct bt_bap, 1);
> > > - bap->ldb = bdb;
> > > + bap->ldb = ldb;
> > > bap->reqs = queue_new();
> > > bap->pending = queue_new();
> > > bap->notify = queue_new();
> > > diff --git a/src/shared/bap.h b/src/shared/bap.h
> > > index 47a15636c..a4e8549a5 100644
> > > --- a/src/shared/bap.h
> > > +++ b/src/shared/bap.h
> > > @@ -34,6 +34,7 @@
> > > #define BT_BAP_CONFIG_PHY_CODEC 0x03
> > >
> > > struct bt_bap;
> > > +struct bt_bap_db;
> > > struct bt_bap_pac;
> > > struct bt_bap_stream;
> > >
> > > @@ -98,14 +99,14 @@ struct bt_bap_pac_qos {
> > > uint32_t ppd_max;
> > > };
> > >
> > > -struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> > > +struct bt_bap_pac *bt_bap_add_vendor_pac(struct bt_bap_db *bdb,
> > > const char *name, uint8_t type,
> > > uint8_t id, uint16_t cid, uint16_t vid,
> > > struct bt_bap_pac_qos *qos,
> > > struct iovec *data,
> > > struct iovec *metadata);
> > >
> > > -struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> > > +struct bt_bap_pac *bt_bap_add_pac(struct bt_bap_db *bdb, const char *name,
> > > uint8_t type, uint8_t id,
> > > struct bt_bap_pac_qos *qos,
> > > struct iovec *data,
> > > @@ -135,7 +136,9 @@ unsigned int bt_bap_register(bt_bap_func_t added, bt_bap_func_t removed,
> > > void *user_data);
> > > bool bt_bap_unregister(unsigned int id);
> > >
> > > -struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb);
> > > +struct bt_bap_db *bt_bap_get_local_db(struct gatt_db *db, bool peripheral);
> > > +
> > > +struct bt_bap *bt_bap_new(struct bt_bap_db *ldb, struct gatt_db *rdb);
> > >
> > > bool bt_bap_set_user_data(struct bt_bap *bap, void *user_data);
> > >
> > > diff --git a/unit/test-bap.c b/unit/test-bap.c
> > > index afeefac84..b677f5197 100644
> > > --- a/unit/test-bap.c
> > > +++ b/unit/test-bap.c
> > > @@ -311,6 +311,7 @@ static void test_client(const void *user_data)
> > > {
> > > struct test_data *data = (void *)user_data;
> > > struct io *io;
> > > + struct bt_bap_db *ldb;
> > > struct gatt_db *db;
> > >
> > > io = tester_setup_io(data->iov, data->iovcnt);
> > > @@ -321,7 +322,8 @@ static void test_client(const void *user_data)
> > > db = gatt_db_new();
> > > g_assert(db);
> > >
> > > - data->bap = bt_bap_new(db, bt_gatt_client_get_db(data->client));
> > > + ldb = bt_bap_get_local_db(db, true);
> > > + data->bap = bt_bap_new(ldb, bt_gatt_client_get_db(data->client));
> > > g_assert(data->bap);
> > >
> > > bt_bap_set_debug(data->bap, print_debug, "bt_bap:", NULL);
> > > --
> > > 2.39.1
> > >
> >
> >
>
> --
> Pauli Virtanen
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Jan 30, 2023 at 11:06 AM Pauli Virtanen <[email protected]> wrote:
>
> When BT adapter is not CIS Peripheral capable, use the shared/bap code
> in its central-only mode, and don't register anything in the local GATT
> database.
>
> When BT adapter is not CIS Central capable, ignore the remote device
> GATT database, so that we work purely in peripheral mode.
>
> If BT adapter supports neither feature, don't do anything with BAP.
> ---
> profiles/audio/bap.c | 18 ++++++++++++++++--
> profiles/audio/media.c | 11 ++++++-----
> 2 files changed, 22 insertions(+), 7 deletions(-)
>
> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
> index e5ffb7230..28c0d139a 100644
> --- a/profiles/audio/bap.c
> +++ b/profiles/audio/bap.c
> @@ -1254,6 +1254,8 @@ static int bap_probe(struct btd_service *service)
> struct btd_adapter *adapter = device_get_adapter(device);
> struct btd_gatt_database *database = btd_adapter_get_database(adapter);
> struct bap_data *data = btd_service_get_user_data(service);
> + struct bt_bap_db *ldb;
> + struct gatt_db *device_db;
> char addr[18];
>
> ba2str(device_get_address(device), addr);
> @@ -1264,17 +1266,29 @@ static int bap_probe(struct btd_service *service)
> return -ENOTSUP;
> }
>
> + if (!btd_adapter_cis_central_capable(adapter) &&
> + !btd_adapter_cis_peripheral_capable(adapter)) {
> + DBG("BAP requires CIS features, unsupported by adapter");
> + return -ENOTSUP;
> + }
> +
> /* Ignore, if we were probed for this device already */
> if (data) {
> error("Profile probed twice for the same device!");
> return -EINVAL;
> }
>
> + if (btd_adapter_cis_central_capable(adapter))
> + device_db = btd_device_get_gatt_db(device);
> + else
> + device_db = NULL;
> +
> data = bap_data_new(device);
> data->service = service;
>
> - data->bap = bt_bap_new(btd_gatt_database_get_db(database),
> - btd_device_get_gatt_db(device));
> + ldb = bt_bap_get_local_db(btd_gatt_database_get_db(database),
> + btd_adapter_cis_peripheral_capable(adapter));
Don't really like the idea of having an API to access the bt_bap_db,
can we just pass NULL to ldb as before? We can work out internally in
bap.c to handle this properly.
> + data->bap = bt_bap_new(ldb, device_db);
> if (!data->bap) {
> error("Unable to create BAP instance");
> free(data);
> diff --git a/profiles/audio/media.c b/profiles/audio/media.c
> index d68085514..6f83b03b5 100644
> --- a/profiles/audio/media.c
> +++ b/profiles/audio/media.c
> @@ -1105,8 +1105,9 @@ static void bap_debug(const char *str, void *user_data)
> static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
> int *err)
> {
> + struct btd_adapter *adapter = endpoint->adapter->btd_adapter;
> struct btd_gatt_database *database;
> - struct gatt_db *db;
> + struct bt_bap_db *ldb;
> struct iovec data;
> char *name;
>
> @@ -1116,7 +1117,7 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
> return false;
> }
>
> - database = btd_adapter_get_database(endpoint->adapter->btd_adapter);
> + database = btd_adapter_get_database(adapter);
> if (!database) {
> error("Adapter database not found");
> return false;
> @@ -1128,8 +1129,6 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
> return false;
> }
>
> - db = btd_gatt_database_get_db(database);
> -
> data.iov_base = endpoint->capabilities;
> data.iov_len = endpoint->size;
>
> @@ -1141,7 +1140,9 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,
> return false;
> }
>
> - endpoint->pac = bt_bap_add_pac(db, name, type, endpoint->codec,
> + ldb = bt_bap_get_local_db(btd_gatt_database_get_db(database),
> + btd_adapter_cis_peripheral_capable(adapter));
> + endpoint->pac = bt_bap_add_pac(ldb, name, type, endpoint->codec,
> &endpoint->qos, &data, NULL);
> if (!endpoint->pac) {
> error("Unable to create PAC");
> --
> 2.39.1
>
--
Luiz Augusto von Dentz
Hi Pauli,
On Mon, Feb 6, 2023 at 5:08 PM Luiz Augusto von Dentz
<[email protected]> wrote:
>
> Hi Pauli,
>
> On Mon, Jan 30, 2023 at 10:43 AM Pauli Virtanen <[email protected]> wrote:
> >
> > Add definitions for new MGMT Controller Information settings bits,
> > indicating adapter Connected Isochronous Stream - Central/Peripheral
> > feature support.
> >
> > The Set Quality Report command was removed in
> > commit 0454e2d09570 ("mgmt: Add support for Mesh in the kernel"),
> > but the settings bit was not removed. It's also not implemented on
> > kernel side, so remove it now.
>
> Let's split this into 2 patches, one removing Quality Report, since it
> was never implemented, and then another including the new flags.
Are you still planning on updating this set?
> > ---
> >
> > Notes:
> > Was the quality report setting bit reserved on purpose?
> >
> > From the commit log it looks like it was forgotten to remove from the
> > docs, but this is not clear.
> >
> > doc/mgmt-api.txt | 6 ++++--
> > 1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
> > index 90d612ed8..58395dc90 100644
> > --- a/doc/mgmt-api.txt
> > +++ b/doc/mgmt-api.txt
> > @@ -332,7 +332,8 @@ Read Controller Information Command
> > 15 Static Address
> > 16 PHY Configuration
> > 17 Wideband Speech
> > - 18 Quality Report
> > + 18 Connected Isochronous Stream - Central
> > + 19 Connected Isochronous Stream - Peripheral
> >
> > This command generates a Command Complete event on success or
> > a Command Status event on failure.
> > @@ -2925,7 +2926,8 @@ Read Extended Controller Information Command
> > 15 Static Address
> > 16 PHY Configuration
> > 17 Wideband Speech
> > - 18 Quality Report
> > + 18 Connected Isochronous Stream - Central
> > + 19 Connected Isochronous Stream - Peripheral
> >
> > The EIR_Data field contains information about class of device,
> > local name and other values. Not all of them might be present. For
> > --
> > 2.39.1
> >
>
>
> --
> Luiz Augusto von Dentz
--
Luiz Augusto von Dentz
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:
On Mon, 30 Jan 2023 20:37:32 +0200 you wrote:
> Add definitions for new MGMT Controller Information settings bits,
> indicating adapter Connected Isochronous Stream - Central/Peripheral
> feature support.
>
> The Set Quality Report command was removed in
> commit 0454e2d09570 ("mgmt: Add support for Mesh in the kernel"),
> but the settings bit was not removed. It's also not implemented on
> kernel side, so remove it now.
>
> [...]
Here is the summary with links:
- [BlueZ,1/8] doc: add MGMT setting for CIS features, remove Quality Report
(no matching commit)
- [BlueZ,2/8] lib: Add defines for MGMT setting bits for CIS feature support
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=c35d32b19989
- [BlueZ,3/8] monitor: add names for MGMT setting bits for CIS feature support
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=0f2f7a8fe270
- [BlueZ,4/8] tools/btmgmt: add names for MGMT setting bits for CIS feature support
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=6f131929b832
- [BlueZ,5/8] adapter: add functions indicating adapter CIS capability
(no matching commit)
- [BlueZ,6/8] media: Check adapter CIS support to add BAP in SupportedUUIDs
(no matching commit)
- [BlueZ,7/8] shared/bap: handle central-only case
(no matching commit)
- [BlueZ,8/8] bap: handle adapters that are not CIS Central / Peripheral capable
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html