From e0076d5d1068945f97abcbcac317b7e2d0033b9e Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 04:34:54 +0100 Subject: [PATCH] Repeat cumulative BLE acknowledgements after unreliable delivery --- lib/ble/ble_transport.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/ble/ble_transport.c b/lib/ble/ble_transport.c index 8df5f60d..1c3cc7ad 100644 --- a/lib/ble/ble_transport.c +++ b/lib/ble/ble_transport.c @@ -35,7 +35,7 @@ struct ble_connection { unsigned int capacity; uint16_t pending_bytes; /* ID монотонен по модулю 65536; кольцо хранит только размеры, payload остаётся у ETCP. */ - uint16_t tx_next, tx_acked, rx_last, flight_head, flight_count, flight_bytes, flight_sizes[FLOW_SLOTS]; + uint16_t tx_next, tx_acked, rx_last, ack_sent, flight_head, flight_count, flight_bytes, flight_sizes[FLOW_SLOTS]; uint64_t flight_deadline, ack_due, ack_completed; void* ack_timer; int rx_flow_seen, ack_dirty, pending_ack; @@ -156,14 +156,18 @@ static void wake_writer(struct ble_transport* t, struct ble_connection* c, enum } static void ack_timer(void* arg) { struct ble_connection* c = arg; c->ack_timer = NULL; + c->ack_dirty = 1; pump(c->owner, c); } -/* Один отложенный ACK на направление; новые RX заменяют ID, не отодвигая срок. */ -static int schedule_ack(struct ble_transport* t, struct ble_connection* c) { +/* Свежий ID объединяем до 15ms, последний повторяем раз в 100ms даже при полном окне отправителя. */ +static int schedule_ack(struct ble_transport* t, struct ble_connection* c, int fresh) { + if (fresh && c->ack_dirty) return 1; + if (fresh) c->ack_dirty = 1; uint64_t now = get_time_us(); - if (!c->ack_dirty) c->ack_due = c->ack_completed ? c->ack_completed + BLE_ACK_INTERVAL_US : now + BLE_ACK_INTERVAL_US; - c->ack_dirty = 1; - if (c->ack_timer || (c->pending && now >= c->ack_due)) return 1; + c->ack_due = c->ack_completed ? c->ack_completed + (c->ack_dirty ? BLE_ACK_INTERVAL_US : BLE_ACK_REPEAT_US) : + now + BLE_ACK_INTERVAL_US; + if (c->ack_timer) { uasync_cancel_timeout(t->ua, c->ack_timer); c->ack_timer = NULL; } + if (c->pending && now >= c->ack_due) return 1; /* uasync использует усечённые TB: дополнительный TB исключает срабатывание раньше 15ms. */ uint64_t delay = c->ack_due > now ? (c->ack_due - now + 199) / 100 : 0; c->ack_timer = uasync_set_timeout(t->ua, (int)delay, c, ack_timer, "ble_ack"); @@ -219,7 +223,9 @@ static void pump(struct ble_transport* t, struct ble_connection* c) { if (wire == BLE_WIRE_ACK) { if (c->ack_timer) { uasync_cancel_timeout(t->ua, c->ack_timer); c->ack_timer = NULL; } c->ack_dirty = 0; c->stats.ack_tx++; - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "BLE ACK TX link=%llu id=%u", (unsigned long long)c->id, c->rx_last); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "BLE ACK TX link=%llu id=%u repeat=%d", + (unsigned long long)c->id, c->rx_last, c->ack_sent == c->rx_last); + c->ack_sent = c->rx_last; } else if (wire == BLE_WIRE_DATA) { if (!c->flight_count) c->flight_deadline = c->submitted + DEADLINE_US; c->flight_sizes[(c->flight_head + c->flight_count++) % FLOW_SLOTS] = len; @@ -383,8 +389,9 @@ void ble_backend_opened(struct ble_transport* t, ble_link_id id, unsigned int mt c->opened = 1; DEBUG_INFO(CAT, "BLE GATT open link=%llu mtu=%u fragment_payload=%u outgoing=%d DATA=without_response/notify INVITE=request/indicate tx_limit=%u", (unsigned long long)id, mtu, c->capacity - HEADER - FLOW_HEADER, c->outgoing, t->config.tx_limit); - DEBUG_INFO(CAT, "BLE flow configured link=%llu rate_bits=%u window_ms=%u window_bytes=%u ACK_interval_us=%u id_bits=16", - (unsigned long long)id, BLE_TX_RATE_BITS, BLE_TX_WINDOW_MS, BLE_TX_WINDOW_BYTES, BLE_ACK_INTERVAL_US); + DEBUG_INFO(CAT, "BLE flow configured link=%llu rate_bits=%u window_ms=%u window_bytes=%u " + "ACK=without_response/notify ACK_interval_us=%u ACK_repeat_us=%u id_bits=16", + (unsigned long long)id, BLE_TX_RATE_BITS, BLE_TX_WINDOW_MS, BLE_TX_WINDOW_BYTES, BLE_ACK_INTERVAL_US, BLE_ACK_REPEAT_US); if (c->outgoing) { c->control[0] = HELLO; write64(c->control + 1, t->config.node_id); write64(c->control + 9, c->peer); c->control_pending = 1; @@ -438,12 +445,12 @@ void ble_backend_received(struct ble_transport* t, ble_link_id id, enum ble_wire (unsigned long long)id, seq, data[FLOW_HEADER], len, c->rx_last, advance); if (!advance || advance >= 0x8000) { DEBUG_WARN(CAT, "BLE duplicate/stale DATA link=%llu id=%u last=%u", (unsigned long long)id, seq, c->rx_last); - if (c->rx_flow_seen) schedule_ack(t, c); return; + if (c->rx_flow_seen) schedule_ack(t, c, 1); return; } if (advance != 1) DEBUG_WARN(CAT, "BLE DATA flow gap link=%llu expected=%u received=%u missing=%u", (unsigned long long)id, (uint16_t)(c->rx_last + 1), seq, advance - 1); c->rx_last = seq; c->rx_flow_seen = 1; - if (!schedule_ack(t, c)) return; + if (!schedule_ack(t, c, 1)) return; data += FLOW_HEADER; len -= FLOW_HEADER; } struct ble_channel_state* ch = &c->channels[channel]; @@ -530,12 +537,9 @@ void ble_backend_sent(struct ble_transport* t, ble_link_id id, int success) { c->pending = 0; if (c->pending_ack) { c->pending_ack = 0; c->ack_completed = get_time_us(); - /* Даже при медленном ACK дать DATA/INVITE следующие 15ms, иначе непрерывный RX вытесняет ответы. */ - if (c->ack_dirty) { - if (c->ack_timer) { uasync_cancel_timeout(t->ua, c->ack_timer); c->ack_timer = NULL; } - c->ack_due = c->ack_completed + BLE_ACK_INTERVAL_US; - if (!schedule_ack(t, c)) return; - } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "BLE ACK sent link=%llu id=%u elapsed_us=%llu latest=%u dirty=%d", + (unsigned long long)id, c->ack_sent, (unsigned long long)waited, c->rx_last, c->ack_dirty); + if (!schedule_ack(t, c, 0)) return; pump(t, c); return; } struct ble_channel_state* ch = &c->channels[channel];