diff --git a/lib/ble/ble_transport.c b/lib/ble/ble_transport.c index 89b3f8ca..7d341595 100644 --- a/lib/ble/ble_transport.c +++ b/lib/ble/ble_transport.c @@ -38,7 +38,7 @@ struct ble_connection { uint16_t tx_next, tx_acked, rx_last, flight_head, flight_count, flight_bytes, flight_sizes[FLOW_SLOTS]; uint64_t flight_deadline, ack_due, ack_sent; void* ack_timer; - int rx_flow_seen, ack_dirty, pending_ack; + int rx_flow_seen, ack_dirty, pending_ack, ack_yield; enum ble_channel pending_channel, next_channel; int outgoing, opened, ready, pending, control_pending; uint8_t control[17]; @@ -176,7 +176,18 @@ static void pump(struct ble_transport* t, struct ble_connection* c) { uint8_t value[BLE_VALUE_MAX]; size_t len; c->pending_bytes = 0; + /* После ACK дать один слот payload: медленный ATT completion не должен вытеснять DATA при непрерывном RX. */ + int payload_ready = c->control_pending && BLE_TX_WINDOW_BYTES - c->flight_bytes >= 17 + FLOW_HEADER && c->flight_count < FLOW_SLOTS; + if (c->ready && !c->control_pending) for (int i = 0; i < BLE_CHANNEL_COUNT; i++) { + struct ble_channel_state* ch = &c->channels[i]; + if (i == BLE_CHANNEL_DATA && (BLE_TX_WINDOW_BYTES - c->flight_bytes < FLOW_MIN_VALUE || c->flight_count == FLOW_SLOTS)) continue; + if (ch->current || queue_entry_count(ch->tx)) payload_ready = 1; + } c->pending_ack = c->ack_dirty && get_time_us() >= c->ack_due; + if (c->pending_ack && c->ack_yield && payload_ready) { + DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "BLE ACK yields payload slot link=%llu flight_bytes=%u", (unsigned long long)c->id, c->flight_bytes); + c->pending_ack = 0; + } c->pending_channel = BLE_CHANNEL_DATA; if (c->pending_ack) { write16(value, c->rx_last); len = 2; } else if (c->control_pending) { @@ -216,6 +227,7 @@ static void pump(struct ble_transport* t, struct ble_connection* c) { int result = ble_backend_send(t->backend, c->id, wire, value, len); if (result == BLE_OK) { c->pending = 1; c->submitted = get_time_us(); c->deadline = c->submitted + DEADLINE_US; + if (wire != BLE_WIRE_ACK) c->ack_yield = 0; 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->ack_sent = c->submitted; c->stats.ack_tx++; @@ -526,7 +538,7 @@ void ble_backend_sent(struct ble_transport* t, ble_link_id id, int success) { c->stats.fragments++; c->stats.wait_us += waited; if (waited > c->stats.max_wait_us) c->stats.max_wait_us = waited; c->pending = 0; - if (c->pending_ack) { c->pending_ack = 0; pump(t, c); return; } + if (c->pending_ack) { c->pending_ack = 0; c->ack_yield = 1; pump(t, c); return; } struct ble_channel_state* ch = &c->channels[channel]; if (!c->pending_bytes) c->control_pending = 0; else if (ch->current) {