Skip to content
This repository was archived by the owner on Oct 30, 2026. It is now read-only.

Reset the USB port when the device does not return after checkm8 - #11

Closed
dl-alexandre wants to merge 1 commit into
OrchardLinux:mainfrom
dl-alexandre:linux-port-reset-after-checkm8
Closed

dl-alexandre wants to merge 1 commit into
OrchardLinux:mainfrom
dl-alexandre:linux-port-reset-after-checkm8

Conversation

@dl-alexandre

@dl-alexandre dl-alexandre commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

After Checkmate! the device drops off USB and has to come back in download mode within palera1n's 20-second window. On my x86_64 Arch laptop it often never comes back by itself. Orchard's instruction ("disconnect and reconnect the cable at the iPad once") is then the only way through, and it's easy to miss: four attempts today timed out, three of them in a row. In the host's kernel log there was no USB event at all between the post-checkmate disconnect and the device rebooting into iPadOS about 25 s later:

07:30:39.666 usb 1-4: USB disconnect      (after Checkmate!)
07:31:05.395 usb 1-4: idProduct=12ab      (iPadOS, 20 s window long gone)

Change

Linux only, and only when the port can be controlled:

  • setup-host's rules gain one line. When a device enters DFU (05ac:1227), udev makes its hub port's disable attribute group-writable (/sys%p/port/disable). udevadm verify accepts the generated file.
  • New apple/replug.py. Before palera1n starts, port_control() resolves the DFU device's port control (the device's sysfs node is gone after checkm8) and returns None if it's missing or not writable. PortReset then follows palera1n's log. If the device hasn't returned (Entered download mode / Download mode device found) 2.5 s after Checkmate! → DFU mode device disconnected, it writes 1 then 0 to the control and waits up to 5 s, at most three times. It always re-enables the port, and it stops with the bootstrap.
  • apple/boot.py runs it around run_process_group and reports the number of resets. When a control exists, the cable instruction says Orchard resets the port and to replug only if the logo stays about 15 s. Otherwise the message and behavior are unchanged (macOS, or Linux without the rule).

The RAM run path has its own palera1n call (#10). I left it alone here to avoid conflicting with that PR, and it can reuse PortReset afterwards.

Evidence

One full orchard launch on a J71b with a standalone prototype of the same logic (root-run script, same timings), and no manual replug:

09:15:15.973 checkmate; device dropped off USB
09:15:18.477 no download mode yet; toggling …/usb1-port2 (attempt 1)
09:15:22     palera1n: Entered download mode → Booting PongoOS

The launch then completed (Pongo → recovery → installed system). That's one run. None of the four unassisted attempts that day recovered by itself within the 20 s window, so the reset looks like the cause, but more runs on more hosts would settle it. A port reset re-runs USB enumeration without cutting VBUS, which was enough here. Hosts that need a real power cycle would need uhubctl-style power switching instead.

Testing

  • New tests/test_replug.py:

    • the control is found only when writable;
    • no reset when the device returns by itself;
    • a reset, then a stop once the device is back;
    • giving up after N attempts, with the port re-enabled;
    • stopping before checkm8 does nothing.

    It passed 5 out of 5 runs.

  • tests.test_host_setup passes. test_installed_wheel_runs_with_all_source_access_blocked fails the same way on main in my editable dev environment.

🤖 Generated with Claude Code

After "Checkmate!" the device drops off USB and must come back in
download mode. On some Linux hosts it never does until the cable is
replugged at the device within palera1n's 20-second window. Disabling
and re-enabling the hub port forces the re-detection: on a J71b the
device came back 4 s after a reset where three bare attempts never did.

setup-host's udev rule makes the port's disable control writable for
the USB access group when a device enters DFU. During the Pongo
bootstrap, Orchard watches palera1n's log and resets that port up to
three times if the device has not returned 2.5 s after checkm8. Hosts
without the control (macOS, or no rule) behave as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dl-alexandre
dl-alexandre force-pushed the linux-port-reset-after-checkm8 branch from 34a5b99 to d3cad49 Compare October 29, 2026 18:54
@dl-alexandre

Copy link
Copy Markdown
Contributor Author

Rebased onto main after #9/#10 and the shared start_pongo(). The port-reset watcher now lives in start_pongo(), so orchard run RAM boots get it too, not only installs; the hint wording follows main ("at the device"). 242 tests run; the only failures are the 2 that also fail on main in an editable dev venv (source-access guard).

henrikklee pushed a commit that referenced this pull request Oct 29, 2026
After "Checkmate!" the device drops off USB and must come back in
download mode. On some Linux hosts it never does until the cable is
replugged at the device within palera1n's 20-second window. Disabling
and re-enabling the hub port forces the re-detection: on a J71b the
device came back 4 s after a reset where three bare attempts never did.

setup-host's udev rule makes the port's disable control writable for
the USB access group when a device enters DFU. During the Pongo
bootstrap, Orchard watches palera1n's log and resets that port up to
three times if the device has not returned 2.5 s after checkm8. Hosts
without the control (macOS, or no rule) behave as before.

[Orchard: from #11 (34a5b99); wired into start_pongo so
 every boot path gets it; port_control ignores macOS's numeric locations
 instead of crashing; the port is re-enabled even if the wait is
 interrupted; the stop event no longer shadows Thread._stop, which broke
 join() on Python 3.12 and older]
@henrikklee

Copy link
Copy Markdown
Contributor

Thanks! Merged to main as 1b17d8c with you as author, wired into the new shared start_pongo() so every boot path (install, launch, orchard run) gets the reset. Three fixes on top: port_control returns None for macOS's numeric USB locations instead of crashing, the port is re-enabled even if the wait is interrupted, and the stop event is renamed because self._stop shadowed Thread._stop and broke join() on Python ≤3.12.

@henrikklee henrikklee closed this Oct 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants