From d0df49446c60245d0f9df62e5f6c291d6d3cb1ae Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 04:35:12 +0100 Subject: [PATCH] Send BlueZ flow acknowledgements without ATT responses --- lib/ble/ble_backend.h | 2 +- lib/ble/ble_bluez.c | 13 +++++++------ lib/ble/tests/test_ble_bluez.c | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/ble/ble_backend.h b/lib/ble/ble_backend.h index cee2df4d6..c68249d63 100644 --- a/lib/ble/ble_backend.h +++ b/lib/ble/ble_backend.h @@ -1,6 +1,6 @@ /* Внутренний платформенный контракт. Все вызовы/события в uasync, send/close не вызывают события синхронно. * ACCEPTED send копирует значение и даёт ровно один sent, либо closed отменяет все операции соединения. - * DATA sent означает локальное завершение; INVITE/ACK используют Request/Indication. + * DATA/ACK sent означает локальное завершение Without Response/Notify; только INVITE использует Request/Indication. * close/destroy отменяют callbacks; transport создаёт идентификаторы, backend не хранит указатели core-link. * Реализации не создают неограниченных очередей: максимум одна операция send на link. */ #ifndef VIBECHAT_BLE_BACKEND_H diff --git a/lib/ble/ble_bluez.c b/lib/ble/ble_bluez.c index 34d03670d..77cde8dab 100644 --- a/lib/ble/ble_bluez.c +++ b/lib/ble/ble_bluez.c @@ -146,7 +146,7 @@ static void notify_read(int fd, void* arg) { link_error(l, "notification socket read failed"); return; } if (!n) { link_error(l, "notification subscription closed"); return; } - if (n != 1 || data[0] != 1 || l->channel == BLE_WIRE_DATA || fd != l->fd[l->channel] || + if (n != 1 || data[0] != 1 || l->channel != BLE_WIRE_INVITE || fd != l->fd[l->channel] || !l->sending || l->buffered) { DEBUG_WARN(CAT, "BLE indication completion invalid fd=%d expected_fd=%d bytes=%zd sending=%d buffered=%d", fd, l->fd[l->channel], n, l->sending, l->buffered); @@ -166,7 +166,7 @@ static void notify_write(int fd, void* arg) { link_error(l, "notification socket write failed"); return; } l->buffered = 0; uasync_set_socket_write(l->backend->ua, l->io[l->channel], 0); - if (l->channel == BLE_WIRE_DATA) { + if (l->channel != BLE_WIRE_INVITE) { l->sending = 0; ble_backend_sent(l->backend->owner, l->id, 1); } @@ -232,7 +232,7 @@ static int acquire_notify(sd_bus_message* m, void* arg, sd_bus_error* error) { link_error(l, "AcquireNotify reply failed"); return r; } DEBUG_DEBUG(CAT, "BLE BlueZ subscription link=%llu channel=%d mode=%s mtu=%u", - (unsigned long long)l->id, channel, channel == BLE_WIRE_DATA ? "notification" : "indication", mtu); + (unsigned long long)l->id, channel, channel == BLE_WIRE_INVITE ? "indication" : "notification", mtu); if (l->fd[0] >= 0 && l->fd[1] >= 0 && l->fd[2] >= 0 && !l->opened) { l->opened = 1; ble_backend_opened(b->owner, l->id, mtu); } @@ -249,7 +249,8 @@ static int get_char(sd_bus* bus, const char* path, const char* interface, const if (!strcmp(property, "UUID")) return sd_bus_message_append(reply, "s", ctx->tx == 3 ? BLE_ACK_UUID : ctx->tx == 2 ? BLE_INVITE_UUID : ctx->tx ? BLE_TX_UUID : BLE_RX_UUID); if (!strcmp(property, "Service")) return sd_bus_message_append(reply, "o", SERVICE); - if (!strcmp(property, "Flags") && ctx->tx >= 2) return sd_bus_message_append(reply, "as", 2, "write", "indicate"); + if (!strcmp(property, "Flags") && ctx->tx == 2) return sd_bus_message_append(reply, "as", 2, "write", "indicate"); + if (!strcmp(property, "Flags") && ctx->tx == 3) return sd_bus_message_append(reply, "as", 2, "write-without-response", "notify"); if (!strcmp(property, "Flags")) return sd_bus_message_append(reply, "as", 1, ctx->tx ? "notify" : "write-without-response"); return sd_bus_message_append(reply, "b", 0); /* NotifyAcquired объявляет поддержку адресного fd API. */ } @@ -635,11 +636,11 @@ int ble_backend_send(struct ble_backend* b, ble_link_id id, enum ble_wire_channe const char* path = channel == BLE_WIRE_ACK ? l->ack : channel == BLE_WIRE_INVITE ? l->invite : l->rx; int r = sd_bus_message_new_method_call(b->bus, &m, "org.bluez", path, CHAR_IF, "WriteValue"); if (r >= 0) r = sd_bus_message_append_array(m, 'y', data, len); - if (r >= 0) r = sd_bus_message_append(m, "a{sv}", 1, "type", "s", channel == BLE_WIRE_DATA ? "command" : "request"); + if (r >= 0) r = sd_bus_message_append(m, "a{sv}", 1, "type", "s", channel == BLE_WIRE_INVITE ? "request" : "command"); if (r >= 0) r = sd_bus_call_async(b->bus, &l->write, m, written, l, 10000000); sd_bus_message_unref(m); if (r < 0) { DEBUG_ERROR(CAT, "BLE WriteValue request: %s", strerror(-r)); return BLE_ERROR; } DEBUG_TRACE(CAT, "BLE WriteValue submitted link=%llu channel=%d type=%s bytes=%zu", (unsigned long long)id, channel, - channel == BLE_WIRE_DATA ? "command" : "request", len); + channel == BLE_WIRE_INVITE ? "request" : "command", len); l->sending = 1; bus_update(b); return BLE_OK; } diff --git a/lib/ble/tests/test_ble_bluez.c b/lib/ble/tests/test_ble_bluez.c index 12d295277..a2e426754 100644 --- a/lib/ble/tests/test_ble_bluez.c +++ b/lib/ble/tests/test_ble_bluez.c @@ -1,4 +1,4 @@ -/* Private D-Bus + настоящий backend: DATA без ATT ACK, адресные INVITE/ACK indications, два клиента. */ +/* Private D-Bus + настоящий backend: DATA/ACK без ATT ACK, адресные INVITE indications, два клиента. */ #include "ble_backend.h" #include "u_async.h" #include "mem.h" @@ -31,7 +31,7 @@ static void poll_all(void) { for (int j = 0; j < 2; j++) if (ack_fds[j] >= 0) { uint8_t ack[8]; ssize_t n = recv(ack_fds[j], ack, sizeof(ack), MSG_DONTWAIT); if (n < 0) { CHECK(errno == EAGAIN); continue; } - CHECK(n == 2); ack_values++; CHECK(send(ack_fds[j], "\1", 1, MSG_NOSIGNAL) == 1); + CHECK(n == 2); ack_values++; /* ACK notification не требует ATT confirmation. */ } } } @@ -78,7 +78,7 @@ static int remote_write(sd_bus_message* m, void* arg, sd_bus_error* e) { CHECK(sd_bus_message_read(m, "s", &type) > 0); int invitation = strstr(sd_bus_message_get_path(m), "/invite") != NULL; int ack = strstr(sd_bus_message_get_path(m), "/ack") != NULL; - CHECK(!strcmp(type, invitation || ack ? "request" : "command")); + CHECK(!strcmp(type, invitation ? "request" : "command")); if (ack) { CHECK(len == 2); ack_requests++; } else if (invitation) requests++; else commands++; int r = sd_bus_reply_method_return(m, ""); if (!invitation && !ack && len == 19 && ((const uint8_t*)data)[2] == 1) { @@ -235,11 +235,11 @@ int main(void) { poll_all(); CHECK(ready_links == 3 && commands == 1); CHECK(!ble_tx_put(transport, links[3], frame(0xe5))); CHECK(!ble_channel_tx_put(transport, links[3], BLE_CHANNEL_INVITE, frame(0xe6))); - poll_all(); CHECK(commands == 2 && requests == 1 && ack_requests >= 1); /* MTU517 и ACK Write Request. */ + poll_all(); CHECK(commands == 2 && requests == 1 && ack_requests >= 1); /* MTU517 и ACK Write Command. */ ble_destroy(transport); uasync_destroy(ua, 0); for (int i = 0; i < 5; i++) sd_bus_slot_unref(remote[i]); sd_bus_slot_unref(manager); sd_bus_slot_unref(adapter); sd_bus_slot_unref(gatt); sd_bus_slot_unref(adv); sd_bus_close_unref(bus); CHECK(u_get_allocated_count() == before); - puts("BlueZ private-bus tests PASS (DATA commands, addressed INVITE/ACK indications, mandatory ACK, two clients, teardown)"); + puts("BlueZ private-bus tests PASS (DATA/ACK commands and notifications, INVITE indications, two clients, teardown)"); return 0; }