From 008bafc18af3fde77dfc0692122fee6289ad0cf5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 04:34:10 +0100 Subject: [PATCH] Cover lost cumulative BLE acknowledgements with full duplex windows --- lib/ble/ble_transport.h | 2 ++ lib/ble/tests/test_ble_transport.c | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/ble/ble_transport.h b/lib/ble/ble_transport.h index 6ffad3992..de5ab2258 100644 --- a/lib/ble/ble_transport.h +++ b/lib/ble/ble_transport.h @@ -4,6 +4,7 @@ * Один сервис обслуживает независимые соединения; данные — непрозрачные кадры 1..1500 байт. * Целые кадры находятся в ll_queue. DATA: Write Without Response/Notification; INVITE: Request/Indication. * DATA имеет накопительный ACK GATT-фрагментов: ограничивает очередь ОС, без собственных ретрансляций. + * ACK: Without Response/Notification; последний ID повторяется, чтобы потеря ACK не запирала окно. * Доставку целых DATA подтверждает потребитель (ETCP); ACK не является подтверждением приложения. * Диагностика: discovery (BLE-префикс). NULL validate разрешает запрос, как и начальная политика GUI. */ #ifndef VIBECHAT_BLE_TRANSPORT_H @@ -24,6 +25,7 @@ extern "C" { #define BLE_TX_WINDOW_MS 50u #define BLE_TX_WINDOW_BYTES (BLE_TX_RATE_BITS / 8u * BLE_TX_WINDOW_MS / 1000u) #define BLE_ACK_INTERVAL_US 15000u +#define BLE_ACK_REPEAT_US 100000u /* Повтор последнего ACK при отсутствии нового DATA. */ struct ble_transport; struct UASYNC; typedef uint64_t ble_link_id; diff --git a/lib/ble/tests/test_ble_transport.c b/lib/ble/tests/test_ble_transport.c index 3e266a856..94c665998 100644 --- a/lib/ble/tests/test_ble_transport.c +++ b/lib/ble/tests/test_ble_transport.c @@ -19,6 +19,7 @@ static int hold_ack; static unsigned ack_delay_us; static unsigned ack_timeouts; static unsigned closed_count; +static unsigned drop_acks[3]; /* Потери ACK в эфире независимы от успешного локального completion. */ static uint64_t data_wire_bytes[3]; static ble_link_id incoming_ids[3]; static void run(void* arg); @@ -81,6 +82,7 @@ static void run(void* arg) { if (!f->delivered) { uint8_t data[512]; size_t len = f->len; memcpy(data, f->data, len); f->delivered = 1; int dropped = f->channel != BLE_WIRE_ACK && data[f->channel == BLE_WIRE_DATA ? 2 : 0] == 3 && drop && --drop == 0; + if (f->channel == BLE_WIRE_ACK && drop_acks[b->index]) { drop_acks[b->index]--; dropped = 1; } if (hold_ack && f->channel == BLE_WIRE_ACK) f->ack_held = 1; else if (!dropped && peer && slot(peer, other)) { if (f->channel == BLE_WIRE_ACK) f->ack_delivered = f->ack_id; @@ -229,7 +231,12 @@ static void exercise_flow(void) { ble_backend_received(a, id, BLE_WIRE_ACK, duplicate, 2); for (unsigned i = 0; i < 1000; i++) uasync_poll(ua, 0); CHECK(data_wire_bytes[0] == paused && !ble_tx_ready(a, id)); - hold_ack = 0; schedule(backends[0]); schedule(backends[1]); + /* Последний ACK потерян в обоих направлениях, а новый DATA уже не помещается в окно. */ + hold_ack = 0; + slot(backends[0], id)->ack_held = receiver->ack_held = 0; + drop_acks[0] = drop_acks[1] = 3; + uint64_t recovery_started = get_time_us(); + schedule(backends[0]); schedule(backends[1]); uint64_t deadline = get_time_us() + 5000000; while ((received[0] < BLE_QUEUE_MAX || received[1] < BLE_QUEUE_MAX) && get_time_us() < deadline) { uasync_poll(ua, 10); @@ -241,7 +248,11 @@ static void exercise_flow(void) { } } } - CHECK(received[0] == BLE_QUEUE_MAX && received[1] == BLE_QUEUE_MAX); drain(ua); + CHECK(received[0] == BLE_QUEUE_MAX && received[1] == BLE_QUEUE_MAX); + CHECK(!drop_acks[0] && !drop_acks[1] && get_time_us() - recovery_started >= 3 * BLE_ACK_REPEAT_US); + printf("BLE lost ACK recovery PASS (3 losses per direction, both windows full, elapsed_us=%llu)\n", + (unsigned long long)(get_time_us() - recovery_started)); + drain(ua); /* Больше полного оборота wire ID; новые данные действительно приходят, старые ACK не открывают окно. */ unsigned sent = 0, delivered = 0; deadline = get_time_us() + 10000000; while (delivered < 70000 && get_time_us() < deadline) {