|
20 | 20 | conditionally_replace_channel,
|
21 | 21 | is_there_a_channel_to_replace,
|
22 | 22 | )
|
23 |
| -from trezor import protobuf, utils, workflow |
| 23 | +from trezor import loop, protobuf, utils, workflow |
24 | 24 | from trezor.loop import Timeout
|
25 | 25 |
|
26 | 26 | from ..protocol_common import Message
|
|
51 | 51 | _MAX_RETRANSMISSION_COUNT = const(50)
|
52 | 52 | _MIN_RETRANSMISSION_COUNT = const(2)
|
53 | 53 |
|
| 54 | +_WRITE_TIMEOUT_MS = const(5_000) |
| 55 | + |
54 | 56 |
|
55 | 57 | class Reassembler:
|
56 | 58 | def __init__(self, cid: int, read_buf: ThpBuffer) -> None:
|
@@ -395,16 +397,27 @@ async def write_encrypted_payload(self, ctrl_byte: int, payload: bytes) -> None:
|
395 | 397 | # ACK is needed before sending more data
|
396 | 398 | ABP.set_sending_allowed(self.channel_cache, False)
|
397 | 399 |
|
| 400 | + write_timeout = loop.sleep(_WRITE_TIMEOUT_MS) |
| 401 | + |
398 | 402 | for i in range(_MAX_RETRANSMISSION_COUNT):
|
399 |
| - await self.ctx.write_payload(header, payload) |
| 403 | + res = await loop.race( |
| 404 | + self.ctx.write_payload(header, payload), write_timeout |
| 405 | + ) |
| 406 | + if isinstance(res, int): |
| 407 | + # When using USB, the writer can get stuck if there is no matching reader. |
| 408 | + # Let's stop retransmission in this case, to allow handling other channels. |
| 409 | + if __debug__: |
| 410 | + self._log("Sending is stuck", logger=log.error) |
| 411 | + break |
| 412 | + |
400 | 413 | # starting from 100ms till ~3.42s
|
401 | 414 | timeout_ms = round(10200 - 1010000 / (100 + i))
|
402 | 415 | try:
|
403 | 416 | # wait and return after receiving an ACK, or raise in case of an unexpected message.
|
404 | 417 | await self.recv_payload(expected_ctrl_byte=None, timeout_ms=timeout_ms)
|
405 | 418 | except Timeout:
|
406 | 419 | if __debug__:
|
407 |
| - log.warning(__name__, "Retransmit after %d ms", timeout_ms) |
| 420 | + self._log(f"Retransmit after {timeout_ms} ms", logger=log.warning) |
408 | 421 | continue
|
409 | 422 | # `ABP.set_sending_allowed()` will be called after a valid ACK
|
410 | 423 | if ABP.is_sending_allowed(self.channel_cache):
|
|
0 commit comments