From 1b1d60d088a3e27cd4b649d8f84f3366653ec809 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 5 Feb 2026 00:04:15 -0600 Subject: [PATCH] Increase Fault Resilience --- panda/board/obj/bootstub.panda.bin | Bin 14876 -> 14876 bytes panda/board/obj/bootstub.panda_h7.bin | Bin 16944 -> 16944 bytes panda/board/obj/panda.bin.signed | Bin 60680 -> 60680 bytes panda/board/obj/panda_h7.bin.signed | Bin 69512 -> 69512 bytes panda/python/__init__.py | 34 ++++++++++++++------------ panda/python/spi.py | 13 +++++++++- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin index 8ff046882865ac5b18c2f8ee2dbb3e5b02b38366..0688d327ff1f6a1b7d858f79b8fee14e5b788fc0 100755 GIT binary patch delta 21 ccmbPJGN)w2En|*U!{ju>BvX^kkBqI@0A->GNdN!< delta 21 ccmbPJGN)w2En^OgG)oghLo@TukBqI@0AQpC$N&HU diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin index 2ed656a52c9d4ee63ae661b731f49b117179a53b..9a5348e899c652ceb6be7875c912aa2e454c4d4c 100755 GIT binary patch delta 23 dcmdnc!ngs5X4!D08YZV1CYhRSUS!k93IJ;`2zUSh delta 23 dcmdnc!ngs5X4!C9q*2=w-VKP5ah`m`V;;KOQl59``&0Q7H&w|yVcc5L1xjQ0aqysRsOdwL z`Cm|W=ew8L#ksIJqMhv|&T0psQb2sQ-D`XCru6F*@=(TB-~o7G|5E@Tk_EhVYbB-x HtakyW`Xo=m delta 153 zcmV;K0A~M)+5?E%1F*=S2smarG%+zYH?z>6xJ-Ye>T4#b5abnB72@y~%6Hk^76Zp> zDcFH{F<}$ocOhn%h{h)@Qzl5fEe!^xlqX@{p9p*wMPp~Icq2kUd@kOI^ITb6E|6n< z?hI#{)i}sNhY)DD=2u_?p()Uqi(&La6BQ83VQHm$&P1eyq=|C<=`J7htQBu~{9u3DwPv(| zXD=cHt98#qQoHfFiXkGUWGzEB@G+b2R@K44)X$+^T*nE3o0^eSn$XOQT`1`el*_}8 z?&7xU3ytLMC8zT1B8fc6+2 0: + bs = self._handle.bulkWrite(3, tx, timeout=timeout) + if bs == 0: + retries += 1 + if retries > self.CAN_MAX_RETRIES: + logging.warning("CAN send: no progress after retries, dropping") + break + else: + retries = 0 + tx = tx[bs:] def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS): self.can_send_many([[addr, None, dat, bus]], timeout=timeout) @@ -832,13 +833,16 @@ class Panda: @ensure_can_packet_version def can_recv(self): dat = bytearray() - while True: + for _ in range(self.CAN_MAX_RETRIES): try: dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames break except (usb1.USBErrorIO, usb1.USBErrorOverflow): logging.error("CAN: BAD RECV, RETRYING") - time.sleep(0.1) + time.sleep(0.01) + else: + logging.error("CAN: recv failed after retries") + return [] msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat) return msgs diff --git a/panda/python/spi.py b/panda/python/spi.py index be4f7dcf4..681f44a56 100644 --- a/panda/python/spi.py +++ b/panda/python/spi.py @@ -27,7 +27,10 @@ NACK = 0x1F CHECKSUM_START = 0xAB MIN_ACK_TIMEOUT_MS = 100 +MAX_ACK_TIMEOUT_MS = 500 # like C++ SPI_ACK_TIMEOUT +DEFAULT_TIMEOUT_MS = 500 # default when timeout=0 MAX_XFER_RETRY_COUNT = 5 +MAX_TIMEOUT_RETRIES = 5 # like C++ XFER_SIZE = 0x40*31 @@ -152,6 +155,8 @@ class PandaSpiHandle(BaseHandle): return cksum def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes: + # Original behavior preserved - timeout=0 means wait forever within this function + # The caller (_transfer) handles the overall timeout timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3 start = time.monotonic() @@ -225,10 +230,15 @@ class PandaSpiHandle(BaseHandle): logging.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len) logging.debug("==============================================") + # Fix timeout=0 infinite loop: default to DEFAULT_TIMEOUT_MS + if timeout == 0: + timeout = DEFAULT_TIMEOUT_MS + n = 0 start_time = time.monotonic() exc = PandaSpiException() - while (timeout == 0) or (time.monotonic() - start_time) < timeout*1e-3: + # Use the timeout for the overall loop, matching original behavior but with timeout=0 fixed + while (time.monotonic() - start_time) < timeout * 1e-3: n += 1 logging.debug("\ntry #%d", n) with self.dev.acquire() as spi: @@ -238,6 +248,7 @@ class PandaSpiHandle(BaseHandle): exc = e logging.debug("SPI transfer failed, retrying", exc_info=True) + logging.error("SPI transfer failed after %d tries, %.2fms", n, (time.monotonic() - start_time) * 1000) raise exc def get_protocol_version(self) -> bytes: