Skip to content

Fix standalone mkII flows on OCDEN-unprogrammed targets - #118

Open
wexi wants to merge 3 commits into
avrdudes:mainfrom
wexi:pr-mkii-leave-progmode
Open

Fix standalone mkII flows on OCDEN-unprogrammed targets#118
wexi wants to merge 3 commits into
avrdudes:mainfrom
wexi:pr-mkii-leave-progmode

Conversation

@wexi

@wexi wexi commented Jul 19, 2026

Copy link
Copy Markdown

Standalone programming-only flows (--read-fuses, --erase, --program) against a JTAGICE mkII fail on any part whose OCDEN fuse is unprogrammed — the required state of every shipped product. The failure is confusing to diagnose because three separate defects stack:

  1. The mkII couples CMND_LEAVE_PROGMODE with re-attaching on-chip debugging; with OCDEN unprogrammed that attach cannot succeed and the ICE answers RSP_FAILED, although every memory operation completed. doSimpleJtagCommand() treated that definitive refusal like a lost frame and re-sent the same command until MAX_JTAG_COMM_ATTEMPS, converting the real response code into a generic "JTAG communication failed" exception. (Isolated on an AT90CAN128: identical command frames, only the stored OCDEN fuse value decides OK vs FAILED; lock bits ruled out.)
  2. main() caught jtag_exception silently, so the only visible symptom was secondary noise: the USB reader thread's "USB bulk read error: Bad file descriptor (-9)" when cleanup closed the device under it — and, timing-dependent, an fd_set fortify abort out of the same shutdown race.
  3. ~jtag2() sent CMND_SIGN_OFF outside any try block, so a failing sign-off during exception unwinding called std::terminate() (SIGABRT).

The three commits address these bottom-up:

  • doSimpleJtagCommand() now throws jtag_io_exception with the real response code on a definitive failure answer instead of retrying; disableProgramming() catches the leave-progmode case and releases the target with CMND_RESET (flag 0x01), which the ICE accepts and which leaves the target running. The destructor's sign-off is guarded.
  • main() reports the exception's what() before exiting.
  • The USB reader thread exits quietly on EBADF/ENODEV, which is the normal shutdown signal, not an error.

Bench result on the AT90CAN128 with OCDEN unprogrammed: avarice --jtag usb --mkII --part at90can128 --read-fuses went from always failing (silently, or with the abort) to printing all three fuse bytes and exiting 0, repeatably; with OCDEN programmed the behavior is unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Mh2GBGBjSKBTr3Lrxo2fa

wexi and others added 3 commits July 19, 2026 19:33
…ures

doSimpleJtagCommand() re-sent the command whenever the ICE answered
RSP_FAILED, exhausting MAX_JTAG_COMM_ATTEMPS and converting the real
response code into a generic "JTAG communication failed". A definitive
answer means the link works; re-asking cannot change the ICE's mind.

This surfaced on every programming-only flow (--read-fuses, --erase,
--program) against a part with the OCDEN fuse unprogrammed: the mkII
couples CMND_LEAVE_PROGMODE with re-attaching on-chip debugging and
answers RSP_FAILED when it cannot, although all memory operations
completed. Catch that case in disableProgramming() and release the
target with CMND_RESET; bench-verified on an AT90CAN128, fuse reads
now complete with the target left running.

The faster failure path also reaches the destructor's CMND_SIGN_OFF,
which ran outside any try block; guard it so a failing sign-off during
exception unwinding cannot call std::terminate().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mh2GBGBjSKBTr3Lrxo2fa
main() caught jtag_exception and exited 1 without a word, leaving only
misleading secondary noise (a USB teardown message from the reader
thread) as the visible symptom. Print e.what() so the primary failure
is the one the user sees.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mh2GBGBjSKBTr3Lrxo2fa
Normal shutdown closes the USB device from the main thread while the
reader thread is blocked in usb_bulk_read(), which then fails with
EBADF/ENODEV and printed "USB bulk read error" on every clean exit.
Treat those codes as the shutdown signal they are.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mh2GBGBjSKBTr3Lrxo2fa
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