|
41 | 41 |
|
42 | 42 | if TYPE_CHECKING: |
43 | 43 | from buffer_types import AnyBuffer, AnyBytes |
44 | | - from typing import Any, Awaitable, Callable |
| 44 | + from typing import Any, Awaitable, Callable, Generator |
45 | 45 |
|
46 | 46 | from trezor.messages import ThpPairingCredential |
47 | 47 | from trezor.wire import WireInterface |
@@ -436,49 +436,56 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: AnyBytes) -> No |
436 | 436 |
|
437 | 437 | header = PacketHeader(ctrl_byte, self.get_channel_id_int(), payload_len) |
438 | 438 |
|
439 | | - async def _write_loop() -> None: |
440 | | - """Send the payload and wait for an ACK with retransmissions.""" |
| 439 | + ack_latency_ms = self.channel_cache.get_int(CHANNEL_ACK_LATENCY_MS) or 0 |
441 | 440 |
|
442 | | - ack_latency_ms = self.channel_cache.get_int(CHANNEL_ACK_LATENCY_MS) or 0 |
443 | | - if __debug__: |
444 | | - self._log(f"Sending {len(payload)} bytes, latency: {ack_latency_ms} ms") |
| 441 | + # ACK is needed before sending more data |
| 442 | + ABP.set_sending_allowed(self.channel_cache, False) |
445 | 443 |
|
446 | | - # ACK is needed before sending more data |
447 | | - ABP.set_sending_allowed(self.channel_cache, False) |
| 444 | + # allows preempting this channel, if another channel becomes active |
| 445 | + self.last_write_ms = utime.ticks_ms() |
448 | 446 |
|
449 | | - # allows preempting this channel, if another channel becomes active |
450 | | - self.last_write_ms = utime.ticks_ms() |
| 447 | + def _write_loop() -> Generator[Any, Any, None]: |
| 448 | + """ |
| 449 | + Retransmit the payload (with increasing delay), raising `Timeout` in the end. |
451 | 450 |
|
452 | | - for i in range(_MAX_RETRANSMISSION_COUNT): |
453 | | - await self._write_payload_once(header, payload) |
| 451 | + This task is spawned concurrently with `_wait_for_ack()` using `loop.race()`, |
| 452 | + so it will be cancelled when the expected ACK is received. |
| 453 | + """ |
| 454 | + if __debug__: |
| 455 | + self._log(f"Sending {len(payload)} bytes, latency: {ack_latency_ms} ms") |
454 | 456 |
|
| 457 | + for i in range(_MAX_RETRANSMISSION_COUNT): |
| 458 | + # Try to send the payload (split into packets), or raise if transport is blocked |
| 459 | + yield from self._write_payload_once(header, payload) |
455 | 460 | # Channel's estimated latency + a variable delay (from 200ms till ~3.52s) |
456 | | - timeout_ms = ack_latency_ms + round(10300 - 1010000 / (100 + i)) |
457 | | - try: |
458 | | - # wait and return after receiving an ACK, or raise in case of an unexpected message. |
459 | | - await self.recv_payload( |
460 | | - expected_ctrl_byte=None, timeout_ms=timeout_ms |
461 | | - ) |
462 | | - except Timeout: |
463 | | - if __debug__: |
464 | | - log.warning(__name__, "Retransmit after %d ms", timeout_ms) |
465 | | - continue |
466 | | - |
467 | | - ack_latency_ms = utime.ticks_diff(utime.ticks_ms(), self.last_write_ms) |
468 | | - # Limit estimated latency to avoid integer overflows and too long delays |
469 | | - ack_latency_ms = max(0, min(800, ack_latency_ms)) |
470 | | - self.channel_cache.set_int(CHANNEL_ACK_LATENCY_MS, ack_latency_ms) |
471 | | - |
472 | | - # `ABP.set_sending_allowed()` will be called after a valid ACK |
473 | | - if ABP.is_sending_allowed(self.channel_cache): |
474 | | - return |
| 461 | + delay_ms = ack_latency_ms + round(10300 - 1010000 / (100 + i)) |
| 462 | + yield from sleep(delay_ms) |
| 463 | + if __debug__: |
| 464 | + log.warning(__name__, "Retransmit after %d ms", delay_ms) |
475 | 465 |
|
476 | 466 | # restart event loop due to unresponsive channel |
477 | 467 | raise Timeout("THP retransmission timeout") |
478 | 468 |
|
| 469 | + def _wait_for_ack() -> Generator[Any, Any, None]: |
| 470 | + """ |
| 471 | + Wait for the expected ACK to be received. |
| 472 | +
|
| 473 | + This task is spawned concurrently with `_write_loop()` using `loop.race()`, |
| 474 | + so it will be cancelled when retransmission loop is over. |
| 475 | + """ |
| 476 | + while not ABP.is_sending_allowed(self.channel_cache): |
| 477 | + # `ABP.set_sending_allowed()` will be called after a valid ACK |
| 478 | + yield from self.recv_payload(expected_ctrl_byte=None) |
| 479 | + |
479 | 480 | try: |
480 | | - return await _write_loop() |
| 481 | + # wait and return after receiving an ACK, or raise in case of an unexpected message / retransmission timeout. |
| 482 | + await race(_wait_for_ack(), _write_loop()) |
481 | 483 | finally: |
| 484 | + ack_latency_ms = utime.ticks_diff(utime.ticks_ms(), self.last_write_ms) |
| 485 | + # Limit estimated latency to avoid integer overflows and too long delays |
| 486 | + ack_latency_ms = max(0, min(800, ack_latency_ms)) |
| 487 | + self.channel_cache.set_int(CHANNEL_ACK_LATENCY_MS, ack_latency_ms) |
| 488 | + |
482 | 489 | # Make sure to use the next `seq_bit` for the next payload |
483 | 490 | ABP.set_send_seq_bit_to_opposite(self.channel_cache) |
484 | 491 |
|
|
0 commit comments