Skip to content

flash: fix retry corruption for compressed writes and cap ESP32 baud rate - #59

Merged
deadprogram merged 2 commits into
mainfrom
esp32-baud-limit
Jul 26, 2026
Merged

flash: fix retry corruption for compressed writes and cap ESP32 baud rate#59
deadprogram merged 2 commits into
mainfrom
esp32-baud-limit

Conversation

@deadprogram

@deadprogram deadprogram commented Jul 25, 2026

Copy link
Copy Markdown
Member

After a timeout during a compressed flash write, the retry's SLIP frame (starting with 0xC0) would terminate the stub's partial frame and then deliver the full retry data in the same write. The stub processed both as separate commands: it emitted a stale error for the truncated frame, then silently decompressed and wrote the retry block then advancing the zlib decompressor state. The host only read the stale error, flushed the real ACK, and retried with the same block, which the decompressor could no longer handle, causing all subsequent retries to hang.

Fix by sending a bare 0xC0 to terminate the partial frame as a separate write, then waiting 50ms for the stale error response to arrive before flushing and retrying. This keeps the retry data out of the cleanup sequence and preserves consistent decompressor state.

Also:

  • Add MaxUARTFlashBaud field to chipDef; set to 230400 for ESP32 to prevent UART RX FIFO overflow on USB-UART bridges (CH340, CP2102)
  • Increase flash block retries from 3 to 5 to provide headroom after the stale-frame cleanup cycle consumes one attempt

UPDATE: added a second commit to handle retry of serial Write and Drain on EINTR to fix a "send command 0x12: interrupted system call" failures observed during compressed flash download on ESP32-C3 (USB-JTAG/Serial).

…rate

After a timeout during a compressed flash write, the retry's SLIP frame
(starting with 0xC0) would terminate the stub's partial frame and then
deliver the full retry data in the same write. The stub processed both as
separate commands: it emitted a stale error for the truncated frame, then
silently decompressed and wrote the retry block then advancing the zlib
decompressor state. The host only read the stale error, flushed the real
ACK, and retried with the same block, which the decompressor could no
longer handle, causing all subsequent retries to hang.

Fix by sending a bare 0xC0 to terminate the partial frame as a separate
write, then waiting 50ms for the stale error response to arrive before
flushing and retrying. This keeps the retry data out of the cleanup
sequence and preserves consistent decompressor state.

Also:
- Add MaxUARTFlashBaud field to chipDef; set to 230400 for ESP32 to
  prevent UART RX FIFO overflow on USB-UART bridges (CH340, CP2102)
- Increase flash block retries from 3 to 5 to provide headroom after
  the stale-frame cleanup cycle consumes one attempt

Signed-off-by: deadprogram <ron@hybridgroup.com>
On Linux, signals such as SIGWINCH (terminal resize) can interrupt
write(2) and tcdrain(3) on serial file descriptors, causing them to
return EINTR before any bytes are transferred. The go.bug.st/serial
library does not retry internally, so the error bubbles up as
"interrupted system call" and aborts the flash.

Wrap port.Write and port.Drain in sendCommand with EINTR retry loops
so transient signal interruptions are handled transparently.

Fixes a "send command 0x12: interrupted system call" failure observed
during compressed flash download on ESP32-C3 (USB-JTAG/Serial).

Signed-off-by: deadprogram <ron@hybridgroup.com>
@deadprogram
deadprogram merged commit 37c8b85 into main Jul 26, 2026
3 checks passed
@deadprogram
deadprogram deleted the esp32-baud-limit branch July 26, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant