Cover lost cumulative BLE acknowledgements with full duplex windows

This commit is contained in:
evgeny committed 2026-10-30 04:34:10 +01:00
1 parent f4bcc6eae9
commit 008bafc18a
2 files changed
+15 -2

No files matched your search

+2
View File
@@ -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;
+13 -2
View File
@@ -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) {