4
4
import asyncio
5
5
import binascii
6
6
from collections .abc import Coroutine
7
+ import contextlib
7
8
import dataclasses
8
9
import enum
9
10
import logging
@@ -62,7 +63,7 @@ class Reserved(enum.IntEnum):
62
63
# Maximum number of consecutive timeouts allowed while waiting to receive an ACK before
63
64
# going to the FAILED state. The value 0 prevents the NCP from entering the error state
64
65
# due to timeouts.
65
- ACK_TIMEOUTS = 4
66
+ ACK_TIMEOUTS = 5
66
67
67
68
68
69
def generate_random_sequence (length : int ) -> bytes :
@@ -368,14 +369,26 @@ def connection_made(self, transport):
368
369
self ._ezsp_protocol .connection_made (self )
369
370
370
371
def connection_lost (self , exc ):
372
+ self ._transport = None
373
+ self ._cancel_pending_data_frames ()
371
374
self ._ezsp_protocol .connection_lost (exc )
372
375
373
376
def eof_received (self ):
374
377
self ._ezsp_protocol .eof_received ()
375
378
379
+ def _cancel_pending_data_frames (
380
+ self , exc : BaseException = RuntimeError ("Connection has been closed" )
381
+ ):
382
+ for fut in self ._pending_data_frames .values ():
383
+ if not fut .done ():
384
+ fut .set_exception (exc )
385
+
376
386
def close (self ):
387
+ self ._cancel_pending_data_frames ()
388
+
377
389
if self ._transport is not None :
378
390
self ._transport .close ()
391
+ self ._transport = None
379
392
380
393
@staticmethod
381
394
def _stuff_bytes (data : bytes ) -> bytes :
@@ -399,7 +412,9 @@ def _unstuff_bytes(data: bytes) -> bytes:
399
412
for c in data :
400
413
if escaped :
401
414
byte = c ^ 0b00100000
402
- assert byte in RESERVED_BYTES
415
+ if byte not in RESERVED_BYTES :
416
+ raise ParsingError (f"Invalid escaped byte: 0x{ byte :02X} " )
417
+
403
418
out .append (byte )
404
419
escaped = False
405
420
elif c == Reserved .ESCAPE :
@@ -417,7 +432,7 @@ def data_received(self, data: bytes) -> None:
417
432
_LOGGER .debug (
418
433
"Truncating buffer to %s bytes, it is growing too fast" , MAX_BUFFER_SIZE
419
434
)
420
- self ._buffer = self ._buffer [: MAX_BUFFER_SIZE ]
435
+ self ._buffer = self ._buffer [- MAX_BUFFER_SIZE : ]
421
436
422
437
while self ._buffer :
423
438
if self ._discarding_until_next_flag :
@@ -447,14 +462,19 @@ def data_received(self, data: bytes) -> None:
447
462
if not frame_bytes :
448
463
continue
449
464
450
- data = self ._unstuff_bytes (frame_bytes )
451
-
452
465
try :
466
+ data = self ._unstuff_bytes (frame_bytes )
453
467
frame = parse_frame (data )
454
468
except Exception :
455
469
_LOGGER .debug (
456
470
"Failed to parse frame %r" , frame_bytes , exc_info = True
457
471
)
472
+
473
+ with contextlib .suppress (NcpFailure ):
474
+ self ._write_frame (
475
+ NakFrame (res = 0 , ncp_ready = 0 , ack_num = self ._rx_seq ),
476
+ prefix = (Reserved .CANCEL ,),
477
+ )
458
478
else :
459
479
self .frame_received (frame )
460
480
elif reserved_byte == Reserved .CANCEL :
@@ -479,7 +499,7 @@ def data_received(self, data: bytes) -> None:
479
499
f"Unexpected reserved byte found: 0x{ reserved_byte :02X} "
480
500
) # pragma: no cover
481
501
482
- def _handle_ack (self , frame : DataFrame | AckFrame ) -> None :
502
+ def _handle_ack (self , frame : DataFrame | AckFrame | NakFrame ) -> None :
483
503
# Note that ackNum is the number of the next frame the receiver expects and it
484
504
# is one greater than the last frame received.
485
505
for ack_num_offset in range (- TX_K , 0 ):
@@ -494,14 +514,19 @@ def _handle_ack(self, frame: DataFrame | AckFrame) -> None:
494
514
def frame_received (self , frame : AshFrame ) -> None :
495
515
_LOGGER .debug ("Received frame %r" , frame )
496
516
517
+ # If a frame has ACK information (DATA, ACK, or NAK), it should be used even if
518
+ # the frame is out of sequence or invalid
497
519
if isinstance (frame , DataFrame ):
520
+ self ._handle_ack (frame )
498
521
self .data_frame_received (frame )
499
- elif isinstance (frame , RStackFrame ):
500
- self .rstack_frame_received (frame )
501
522
elif isinstance (frame , AckFrame ):
523
+ self ._handle_ack (frame )
502
524
self .ack_frame_received (frame )
503
525
elif isinstance (frame , NakFrame ):
526
+ self ._handle_ack (frame )
504
527
self .nak_frame_received (frame )
528
+ elif isinstance (frame , RStackFrame ):
529
+ self .rstack_frame_received (frame )
505
530
elif isinstance (frame , RstFrame ):
506
531
self .rst_frame_received (frame )
507
532
elif isinstance (frame , ErrorFrame ):
@@ -513,7 +538,6 @@ def data_frame_received(self, frame: DataFrame) -> None:
513
538
# The Host may not piggyback acknowledgments and should promptly send an ACK
514
539
# frame when it receives a DATA frame.
515
540
if frame .frm_num == self ._rx_seq :
516
- self ._handle_ack (frame )
517
541
self ._rx_seq = (frame .frm_num + 1 ) % 8
518
542
self ._write_frame (AckFrame (res = 0 , ncp_ready = 0 , ack_num = self ._rx_seq ))
519
543
@@ -536,14 +560,10 @@ def rstack_frame_received(self, frame: RStackFrame) -> None:
536
560
self ._ezsp_protocol .reset_received (frame .reset_code )
537
561
538
562
def ack_frame_received (self , frame : AckFrame ) -> None :
539
- self . _handle_ack ( frame )
563
+ pass
540
564
541
565
def nak_frame_received (self , frame : NakFrame ) -> None :
542
- err = NotAcked (frame = frame )
543
-
544
- for fut in self ._pending_data_frames .values ():
545
- if not fut .done ():
546
- fut .set_exception (err )
566
+ self ._cancel_pending_data_frames (NotAcked (frame = frame ))
547
567
548
568
def rst_frame_received (self , frame : RstFrame ) -> None :
549
569
self ._ncp_reset_code = None
@@ -558,12 +578,8 @@ def error_frame_received(self, frame: ErrorFrame) -> None:
558
578
self ._enter_failed_state (self ._ncp_reset_code )
559
579
560
580
def _enter_failed_state (self , reset_code : t .NcpResetCode ) -> None :
561
- exc = NcpFailure (code = reset_code )
562
-
563
- for fut in self ._pending_data_frames .values ():
564
- if not fut .done ():
565
- fut .set_exception (exc )
566
-
581
+ self ._ncp_state = NcpState .FAILED
582
+ self ._cancel_pending_data_frames (NcpFailure (code = reset_code ))
567
583
self ._ezsp_protocol .reset_received (reset_code )
568
584
569
585
def _write_frame (
@@ -573,6 +589,9 @@ def _write_frame(
573
589
prefix : tuple [Reserved ] = (),
574
590
suffix : tuple [Reserved ] = (Reserved .FLAG ,),
575
591
) -> None :
592
+ if self ._transport is None or self ._transport .is_closing ():
593
+ raise NcpFailure ("Transport is closed, cannot send frame" )
594
+
576
595
if _LOGGER .isEnabledFor (logging .DEBUG ):
577
596
prefix_str = "" .join ([f"{ r .name } + " for r in prefix ])
578
597
suffix_str = "" .join ([f" + { r .name } " for r in suffix ])
@@ -631,7 +650,9 @@ async def _send_data_frame(self, frame: AshFrame) -> None:
631
650
await ack_future
632
651
except NotAcked :
633
652
_LOGGER .debug (
634
- "NCP responded with NAK. Retrying (attempt %d)" , attempt + 1
653
+ "NCP responded with NAK to %r. Retrying (attempt %d)" ,
654
+ frame ,
655
+ attempt + 1 ,
635
656
)
636
657
637
658
# For timing purposes, NAK can be treated as an ACK
@@ -650,9 +671,10 @@ async def _send_data_frame(self, frame: AshFrame) -> None:
650
671
raise
651
672
except asyncio .TimeoutError :
652
673
_LOGGER .debug (
653
- "No ACK received in %0.2fs (attempt %d)" ,
674
+ "No ACK received in %0.2fs (attempt %d) for %r " ,
654
675
self ._t_rx_ack ,
655
676
attempt + 1 ,
677
+ frame ,
656
678
)
657
679
# If a DATA frame acknowledgement is not received within the
658
680
# current timeout value, then t_rx_ack is doubled.
0 commit comments