* [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable
@ 2026-11-03 20:30 Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events Luiz Augusto von Dentz
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
When acting as a HID device, GATT Notify Multiple adds up to 10 ms of
latency to notifications since they are buffered to be aggregated into
a single PDU. This set makes the timeout configurable with the new
[GATT] NotifyMultiple option in main.conf:
false: Disable Notify Multiple, always send regular notifications.
true: Enable Notify Multiple with 0 ms timeout (default), so only the
notifications queued in the same mainloop cycle are aggregated.
<ms>: Enable Notify Multiple with the given timeout in milliseconds.
Since timeouts of 0 are run as mainloop idle callbacks, these are also
fixed to free removed idles without waiting for other events, and to
not overflow their IDs.
Note that with the lower timeout the ASE Control Point and ASE state
notifications of BAP are no longer aggregated, which exposes a race on
BAP clients, so the BAP enhancements set should be applied first.
Luiz Augusto von Dentz (5):
shared/mainloop: Free removed idles without waiting for events
shared/mainloop: Fix idle ID overflow
shared/gatt-server: Add bt_gatt_server_set_mnotify_timeout
unit/test-gatt: Add Notify Multiple server tests
main.conf: Add NotifyMultiple option to GATT section
src/btd.h | 1 +
src/device.c | 2 +
src/main.c | 32 +++++++++++++
src/main.conf | 12 +++++
src/shared/gatt-server.c | 87 ++++++++++++++++++++++-------------
src/shared/gatt-server.h | 3 ++
src/shared/mainloop.c | 39 ++++++++++++++--
unit/test-gatt.c | 99 ++++++++++++++++++++++++++++++++++++++++
8 files changed, 238 insertions(+), 37 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
@ 2026-11-03 20:30 ` Luiz Augusto von Dentz
2026-11-03 22:42 ` Make Notify Multiple timeout configurable bluez.test.bot
2026-11-03 20:30 ` [PATCH BlueZ v1 2/5] shared/mainloop: Fix idle ID overflow Luiz Augusto von Dentz
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Removing the last armed idle left it in the list without any idle
pending, so mainloop_run() would block in epoll_wait() and the idle and
its destroy callback would not be released until another event.
Fix this by considering removed idles as pending so they are freed on
the next iteration.
---
src/shared/mainloop.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/shared/mainloop.c b/src/shared/mainloop.c
index 653f22db70e6..27de2b02ae09 100644
--- a/src/shared/mainloop.c
+++ b/src/shared/mainloop.c
@@ -361,8 +361,11 @@ static bool idle_pending(void)
{
struct idle_data *idle;
+ /* Removed idles also count so they are freed without waiting for
+ * another event.
+ */
for (idle = idle_list; idle; idle = idle->next) {
- if (idle->armed && !idle->removed)
+ if (idle->armed || idle->removed)
return true;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 2/5] shared/mainloop: Fix idle ID overflow
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events Luiz Augusto von Dentz
@ 2026-11-03 20:30 ` Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 3/5] shared/gatt-server: Add bt_gatt_server_set_mnotify_timeout Luiz Augusto von Dentz
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Idle IDs were allocated from a signed counter that was never recycled,
so it would eventually overflow and wrap negative, after which
run_idles() would skip the pending idles, since it used the IDs to tell
the ones added during the pass, and timeout_add() would reject the new
IDs.
Fix this by recycling the IDs within the idle range and marking the
idles to be run at the start of each pass instead.
---
src/shared/mainloop.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/src/shared/mainloop.c b/src/shared/mainloop.c
index 27de2b02ae09..c3db4932c7df 100644
--- a/src/shared/mainloop.c
+++ b/src/shared/mainloop.c
@@ -21,6 +21,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
+#include <limits.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
@@ -335,6 +336,7 @@ struct idle_data {
int id;
bool deferred;
bool armed;
+ bool ready;
bool removed;
mainloop_timeout_func callback;
mainloop_destroy_func destroy;
@@ -357,6 +359,18 @@ static struct idle_data *find_idle(int id)
return NULL;
}
+static bool idle_in_use(int id)
+{
+ struct idle_data *idle;
+
+ for (idle = idle_list; idle; idle = idle->next) {
+ if (idle->id == id)
+ return true;
+ }
+
+ return false;
+}
+
static bool idle_pending(void)
{
struct idle_data *idle;
@@ -407,13 +421,19 @@ static void remove_all_idles(void)
static void run_idles(bool deferred)
{
struct idle_data *idle;
- int last = idle_next_id;
+
+ /* Mark the ones armed before this pass, so the ones added or armed by
+ * the callbacks only run on the next pass.
+ */
+ for (idle = idle_list; idle; idle = idle->next)
+ idle->ready = idle->armed && !idle->removed &&
+ idle->deferred == deferred;
for (idle = idle_list; idle; idle = idle->next) {
- if (idle->removed || !idle->armed || idle->id >= last ||
- idle->deferred != deferred)
+ if (!idle->ready || idle->removed)
continue;
+ idle->ready = false;
idle->armed = false;
idle->callback(idle->id, idle->user_data);
}
@@ -434,7 +454,13 @@ static int add_idle(bool deferred, mainloop_timeout_func callback,
return -ENOMEM;
memset(idle, 0, sizeof(*idle));
- idle->id = idle_next_id++;
+
+ /* Recycle the IDs within the idle range, skipping the ones in use */
+ do {
+ idle->id = idle_next_id;
+ idle_next_id = idle_next_id < INT_MAX ? idle_next_id + 1 :
+ IDLE_ID_BASE;
+ } while (idle_in_use(idle->id));
idle->deferred = deferred;
idle->armed = true;
idle->callback = callback;
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 3/5] shared/gatt-server: Add bt_gatt_server_set_mnotify_timeout
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 2/5] shared/mainloop: Fix idle ID overflow Luiz Augusto von Dentz
@ 2026-11-03 20:30 ` Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 4/5] unit/test-gatt: Add Notify Multiple server tests Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 5/5] main.conf: Add NotifyMultiple option to GATT section Luiz Augusto von Dentz
4 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds bt_gatt_server_set_mnotify_timeout which can be used to
control the timeout used to aggregate notifications into a Notify
Multiple PDU, a negative value disables Notify Multiple so regular
notifications are always used.
The default timeout is changed from 10 ms to 0 so only notifications
queued in the same mainloop cycle are aggregated, since the former adds
latency which may be undesirable for e.g. HID.
While at it, rename the nfy_mult/notify_multiple identifiers to mnotify.
---
src/shared/gatt-server.c | 87 +++++++++++++++++++++++++---------------
src/shared/gatt-server.h | 3 ++
2 files changed, 58 insertions(+), 32 deletions(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index fde791aae867..15944bd1478f 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -32,7 +32,7 @@
*/
#define DEFAULT_MAX_PREP_QUEUE_LEN 30
-#define NFY_MULT_TIMEOUT 10
+#define MNOTIFY_TIMEOUT 0
#define DBG(_server, _format, arg...) \
gatt_log(_server, "%s:%s() " _format, __FILE__, __func__, ## arg)
@@ -73,7 +73,7 @@ static void prep_write_data_destroy(void *user_data)
free(data);
}
-struct nfy_mult_data {
+struct mnotify_data {
unsigned int id;
uint8_t *pdu;
uint16_t offset;
@@ -114,20 +114,21 @@ struct bt_gatt_server {
bt_gatt_server_authorize_cb_t authorize;
void *authorize_data;
- struct nfy_mult_data *nfy_mult;
+ struct mnotify_data *mnotify;
+ int mnotify_timeout;
};
-static void notify_multiple_free(struct bt_gatt_server *server)
+static void mnotify_free(struct bt_gatt_server *server)
{
- if (!server->nfy_mult)
+ if (!server->mnotify)
return;
- if (server->nfy_mult->id)
- timeout_remove(server->nfy_mult->id);
+ if (server->mnotify->id)
+ timeout_remove(server->mnotify->id);
- free(server->nfy_mult->pdu);
- free(server->nfy_mult);
- server->nfy_mult = NULL;
+ free(server->mnotify->pdu);
+ free(server->mnotify);
+ server->mnotify = NULL;
}
static void bt_gatt_server_free(struct bt_gatt_server *server)
@@ -135,7 +136,7 @@ static void bt_gatt_server_free(struct bt_gatt_server *server)
if (server->debug_destroy)
server->debug_destroy(server->debug_data);
- notify_multiple_free(server);
+ mnotify_free(server);
bt_att_unregister(server->att, server->mtu_id);
bt_att_unregister(server->att, server->read_by_grp_type_id);
@@ -1681,6 +1682,7 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN;
server->prep_queue = queue_new();
server->min_enc_size = min_enc_size;
+ server->mnotify_timeout = MNOTIFY_TIMEOUT;
if (!gatt_server_register_att_handlers(server)) {
bt_gatt_server_free(server);
@@ -1753,31 +1755,48 @@ bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
return true;
}
-static void notify_multiple_timeout_remove(struct bt_gatt_server *server)
+static void mnotify_timeout_remove(struct bt_gatt_server *server)
{
- if (!server->nfy_mult->id)
+ if (!server->mnotify->id)
return;
- timeout_remove(server->nfy_mult->id);
- server->nfy_mult->id = 0;
+ timeout_remove(server->mnotify->id);
+ server->mnotify->id = 0;
}
-static bool notify_multiple(void *user_data)
+static bool mnotify_send(void *user_data)
{
struct bt_gatt_server *server = user_data;
- server->nfy_mult->id = 0;
+ server->mnotify->id = 0;
bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY_MULT,
- server->nfy_mult->pdu, server->nfy_mult->offset, NULL,
+ server->mnotify->pdu, server->mnotify->offset, NULL,
NULL, NULL);
- notify_multiple_free(server);
+ mnotify_free(server);
return false;
}
-static bool notify_append_le16(struct nfy_mult_data *data, uint16_t value)
+bool bt_gatt_server_set_mnotify_timeout(struct bt_gatt_server *server,
+ int timeout)
+{
+ if (!server)
+ return false;
+
+ server->mnotify_timeout = timeout;
+
+ /* Flush any pending data if Notify Multiple is being disabled */
+ if (timeout < 0 && server->mnotify) {
+ mnotify_timeout_remove(server);
+ mnotify_send(server);
+ }
+
+ return true;
+}
+
+static bool notify_append_le16(struct mnotify_data *data, uint16_t value)
{
if (data->offset + sizeof(value) > data->len)
return false;
@@ -1792,27 +1811,31 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
uint16_t length, bool multiple)
{
- struct nfy_mult_data *data = NULL;
+ struct mnotify_data *data = NULL;
bool result;
if (!server || (length && !value))
return false;
+ /* Notify Multiple disabled, send as regular notification */
+ if (server->mnotify_timeout < 0)
+ multiple = false;
+
if (multiple) {
- data = server->nfy_mult;
+ data = server->mnotify;
/* flush buffered data if this request hits buffer size limit */
if (data && data->offset > 0 &&
data->len - data->offset < 4 + length) {
- notify_multiple_timeout_remove(server);
- notify_multiple(server);
- /* data has been freed by notify_multiple */
+ mnotify_timeout_remove(server);
+ mnotify_send(server);
+ /* data has been freed by mnotify_send */
data = NULL;
}
}
if (!data) {
- data = new0(struct nfy_mult_data, 1);
+ data = new0(struct mnotify_data, 1);
data->len = bt_att_get_mtu(server->att) - 1;
data->pdu = malloc(data->len);
}
@@ -1834,13 +1857,13 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
data->offset += length;
if (multiple) {
- if (!server->nfy_mult)
- server->nfy_mult = data;
+ if (!server->mnotify)
+ server->mnotify = data;
- if (!server->nfy_mult->id)
- server->nfy_mult->id = timeout_add(NFY_MULT_TIMEOUT,
- notify_multiple, server,
- NULL);
+ if (!server->mnotify->id)
+ server->mnotify->id = timeout_add(
+ server->mnotify_timeout,
+ mnotify_send, server, NULL);
return true;
}
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
index ea49f2960d71..b024258cd186 100644
--- a/src/shared/gatt-server.h
+++ b/src/shared/gatt-server.h
@@ -43,6 +43,9 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
uint16_t length, bool multiple);
+bool bt_gatt_server_set_mnotify_timeout(struct bt_gatt_server *server,
+ int timeout);
+
bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
uint16_t handle, const uint8_t *value,
uint16_t length,
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 4/5] unit/test-gatt: Add Notify Multiple server tests
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
` (2 preceding siblings ...)
2026-11-03 20:30 ` [PATCH BlueZ v1 3/5] shared/gatt-server: Add bt_gatt_server_set_mnotify_timeout Luiz Augusto von Dentz
@ 2026-11-03 20:30 ` Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 5/5] main.conf: Add NotifyMultiple option to GATT section Luiz Augusto von Dentz
4 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds server tests for sending Notify Multiple with the default and
a positive timeout, disabling it while a notification is pending, and
sending with it disabled.
---
unit/test-gatt.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 535baafc6f70..6d6d0c73ed40 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2336,6 +2336,70 @@ static const struct test_step test_notification_server_1 = {
.length = 0x03,
};
+static void test_server_mnotify(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ bt_gatt_server_send_notification(context->server, step->handle,
+ step->value, step->length, true);
+ bt_gatt_server_send_notification(context->server, step->handle,
+ step->value, step->length, true);
+}
+
+static const struct test_step test_mnotify_server_1 = {
+ .handle = 0x0003,
+ .func = test_server_mnotify,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static void test_server_mnotify_timeout(struct context *context)
+{
+ bt_gatt_server_set_mnotify_timeout(context->server, 1);
+ test_server_mnotify(context);
+}
+
+static const struct test_step test_mnotify_server_2 = {
+ .handle = 0x0003,
+ .func = test_server_mnotify_timeout,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static void test_server_mnotify_flush(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ bt_gatt_server_send_notification(context->server, step->handle,
+ step->value, step->length, true);
+
+ /* Disabling sends the pending notification right away */
+ bt_gatt_server_set_mnotify_timeout(context->server, -1);
+}
+
+static const struct test_step test_mnotify_server_3 = {
+ .handle = 0x0003,
+ .func = test_server_mnotify_flush,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static void test_server_mnotify_disabled(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ bt_gatt_server_set_mnotify_timeout(context->server, -1);
+ bt_gatt_server_send_notification(context->server, step->handle,
+ step->value, step->length, true);
+}
+
+static const struct test_step test_mnotify_server_4 = {
+ .handle = 0x0003,
+ .func = test_server_mnotify_disabled,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
static uint8_t indication_received;
static void test_indication_cb(void *user_data)
@@ -3337,6 +3401,41 @@ int main(int argc, char *argv[])
raw_pdu(),
raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+ define_test_server("/robustness/mnotify", test_server, ts_small_db,
+ &test_mnotify_server_1,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03,
+ 0x03, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_server("/robustness/mnotify-timeout", test_server,
+ ts_small_db, &test_mnotify_server_2,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03,
+ 0x03, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_server("/robustness/mnotify-flush", test_server,
+ ts_small_db, &test_mnotify_server_3,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x03, 0x00, 0x01, 0x02,
+ 0x03));
+
+ define_test_server("/robustness/mnotify-disabled", test_server,
+ ts_small_db, &test_mnotify_server_4,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+
define_test_server("/TP/GAI/SR/BV-01-C", test_server, ts_small_db,
&test_indication_server_1,
raw_pdu(0x03, 0x00, 0x02),
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH BlueZ v1 5/5] main.conf: Add NotifyMultiple option to GATT section
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
` (3 preceding siblings ...)
2026-11-03 20:30 ` [PATCH BlueZ v1 4/5] unit/test-gatt: Add Notify Multiple server tests Luiz Augusto von Dentz
@ 2026-11-03 20:30 ` Luiz Augusto von Dentz
4 siblings, 0 replies; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 20:30 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds NotifyMultiple option which can be used to control the use
of GATT Notify Multiple:
false: Disable Notify Multiple, always send regular notifications.
true: Enable Notify Multiple with 0 ms timeout (default).
<ms>: Enable Notify Multiple with the given timeout in milliseconds.
---
src/btd.h | 1 +
src/device.c | 2 ++
src/main.c | 32 ++++++++++++++++++++++++++++++++
src/main.conf | 12 ++++++++++++
4 files changed, 47 insertions(+)
diff --git a/src/btd.h b/src/btd.h
index d70276b1dca0..b059ab872764 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -165,6 +165,7 @@ struct btd_opts {
uint16_t gatt_mtu;
uint8_t gatt_channels;
bool gatt_client;
+ int gatt_mnotify;
enum bt_gatt_export_t gatt_export;
bool gatt_seclevel;
enum mps_mode_t mps;
diff --git a/src/device.c b/src/device.c
index 137970637777..6d1e78b0ead2 100644
--- a/src/device.c
+++ b/src/device.c
@@ -6547,6 +6547,8 @@ static void gatt_server_init(struct btd_device *device,
bt_gatt_server_set_permissions(device->server, false);
bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
+ bt_gatt_server_set_mnotify_timeout(device->server,
+ btd_opts.gatt_mnotify);
btd_gatt_database_server_connected(database, device->server);
}
diff --git a/src/main.c b/src/main.c
index c7a55d30f61b..8e58b6b58ae2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -151,6 +151,7 @@ static const char *gatt_options[] = {
"ExchangeMTU",
"Channels",
"Client",
+ "NotifyMultiple",
"ExportClaimedServices",
"Security",
NULL
@@ -1190,6 +1191,35 @@ static void parse_gatt_seclevel(GKeyFile *config)
g_free(str);
}
+static void parse_gatt_mnotify(GKeyFile *config)
+{
+ char *str = NULL;
+ char *endptr = NULL;
+ long val;
+
+ parse_config_string(config, "GATT", "NotifyMultiple", &str);
+ if (!str)
+ return;
+
+ if (!strcmp(str, "false") || !strcmp(str, "no") ||
+ !strcmp(str, "off")) {
+ btd_opts.gatt_mnotify = -1;
+ } else if (!strcmp(str, "true") || !strcmp(str, "yes") ||
+ !strcmp(str, "on")) {
+ btd_opts.gatt_mnotify = 0;
+ } else {
+ val = strtol(str, &endptr, 0);
+ if (!endptr || *endptr != '\0' || val < 0 || val > INT_MAX)
+ error("Invalid value for NotifyMultiple=%s", str);
+ else
+ btd_opts.gatt_mnotify = val;
+ }
+
+ DBG("NotifyMultiple=%d", btd_opts.gatt_mnotify);
+
+ g_free(str);
+}
+
static void parse_gatt(GKeyFile *config)
{
parse_gatt_cache(config);
@@ -1199,6 +1229,7 @@ static void parse_gatt(GKeyFile *config)
parse_config_u8(config, "GATT", "Channels", &btd_opts.gatt_channels,
1, 6);
parse_config_bool(config, "GATT", "Client", &btd_opts.gatt_client);
+ parse_gatt_mnotify(config);
parse_gatt_export(config);
parse_gatt_seclevel(config);
}
@@ -1425,6 +1456,7 @@ static void init_defaults(void)
btd_opts.gatt_mtu = BT_ATT_MAX_LE_MTU;
btd_opts.gatt_channels = 1;
btd_opts.gatt_client = true;
+ btd_opts.gatt_mnotify = 0;
btd_opts.gatt_export = BT_GATT_EXPORT_READ_ONLY;
btd_opts.gatt_seclevel = BT_ATT_SECURITY_AUTO;
diff --git a/src/main.conf b/src/main.conf
index 1d8e97dc6f1a..12028e162c1d 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -270,6 +270,18 @@
# Defaults to 'true'.
#Client = true
+# Controls the use of GATT Notify Multiple when acting as a server and the
+# remote client supports it. Multiple notifications are buffered for up to
+# the given timeout so they can be sent in a single PDU, this improves
+# throughput but adds latency which may be undesirable for e.g. HID.
+# Possible values:
+# false: Disable Notify Multiple, always send regular notifications.
+# true: Enable Notify Multiple with 0 ms timeout (only notifications queued
+# in the same mainloop iteration are aggregated).
+# <ms>: Enable Notify Multiple with the given timeout in milliseconds.
+# Defaults to true
+#NotifyMultiple = true
+
# Minimum required Encryption Key Size for accessing secured characteristics.
# Possible values: 0 and 7-16. 0 means don't care.
# Defaults to 0
--
2.56.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* RE: Make Notify Multiple timeout configurable
2026-11-03 20:30 ` [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events Luiz Augusto von Dentz
@ 2026-11-03 22:42 ` bluez.test.bot
0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-11-03 22:42 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
[-- Attachment #1: Type: text/plain, Size: 3293 bytes --]
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/series/1200849/
---Test result---
Test Summary:
CheckPatch PASS 9.64 seconds
GitLint PASS 1.92 seconds
BuildEll PASS 19.09 seconds
BluezMake PASS 286.88 seconds
MakeCheck PASS 14.47 seconds
MakeDistcheck PASS 146.61 seconds
CheckValgrind PASS 242.67 seconds
CheckSmatch WARNING 274.74 seconds
bluezmakeextell PASS 95.48 seconds
TestFunctional FAIL 1217.49 seconds
IncrementalBuild PASS 409.32 seconds
ScanBuild PASS 1061.38 seconds
Details
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/main.c: note: in included file (through src/device.h):
##############################
Test: TestFunctional - FAIL
Desc: Run test-functional
Output:
FAIL functional.test_bap::test_bap_unicast_transport_resume[0-hosts4-vm2]: pytest_bluezenv.rpc.RemoteError: ('Acquire', [('/org/bluez/hci0/dev_00_AA_01_01_00_42/pac_sink0/fd1', 'org.bluez.Error.Failed: Input/output error'), ('/org/bluez/hci0/dev_00_AA_01_01_00_42/pac_sink0/fd0', 'org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.')])
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 256, in call_plugin
return getattr(self.plugins[name], method)(*a, **kw)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/host_plugins.py", line 108, in __call__
return func(*a, **kw)
File "/home/runner/work/bluez/bluez/src/src/test/functional/test_bap.py", line 406, in resume_transports
operation("Acquire")
~~~~~~~~~^^^^^^^^^^^
File "/home/runner/work/bluez/bluez/src/src/test/functional/test_bap.py", line 377, in operation
assert not errors, (method, errors)
^^^^^^^^^^
AssertionError: ('Acquire', [('/org/bluez/hci0/dev_00_AA_01_01_00_42/pac_sink0/fd1', 'org.bluez.Error.Failed: Input/output error'), ('/org/bluez/hci0/dev_00_AA_01_01_00_42/pac_sink0/fd0', 'org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.')])
https://github.com/bluez/bluez/pull/2755
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-03 22:42 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03 20:30 [PATCH BlueZ v1 0/5] Make Notify Multiple timeout configurable Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 1/5] shared/mainloop: Free removed idles without waiting for events Luiz Augusto von Dentz
2026-11-03 22:42 ` Make Notify Multiple timeout configurable bluez.test.bot
2026-11-03 20:30 ` [PATCH BlueZ v1 2/5] shared/mainloop: Fix idle ID overflow Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 3/5] shared/gatt-server: Add bt_gatt_server_set_mnotify_timeout Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 4/5] unit/test-gatt: Add Notify Multiple server tests Luiz Augusto von Dentz
2026-11-03 20:30 ` [PATCH BlueZ v1 5/5] main.conf: Add NotifyMultiple option to GATT section Luiz Augusto von Dentz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox