feat(firmware): attempt JMP_TO_APP soft-reset recovery on failed bootloader health check - #312
Conversation
…loader health check A dirty HID bootloader handle left behind by another program (e.g. Microchip's HID bootloader PC tool) can make the Connecting-state connect/version health check fail even though the device is physically present and enumerated. Before giving up, RunPic32UpdateAsync now issues one best-effort JMP_TO_APP soft reset, waits for the bootloader to re-enumerate, and retries the connect + version check once. Nothing has been erased yet at this point, so this is a pure automatic-recovery improvement with no change to the erase-eligible failure paths. Closes #298
PR Summary by QodoFirmware: soft-reset recovery on PIC32 bootloader health-check failure
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
|
/agentic_review |
Code Review by Qodo
Context used 1.
|
…fallthrough throw originalFailure; resets the exception's stack trace to the recovery method, hiding where the connect/version health check actually failed. Use ExceptionDispatchInfo.Capture(...).Throw() instead so the FirmwareUpdateException wrapping it still points at the true failure site. Addresses Qodo review feedback on PR #312.
Summary
On a PIC32 firmware-update failure during
Connecting(the HID bootloader connect +RequestBootloaderVersionAsynchealth check),FirmwareUpdateService.RunPic32UpdateAsyncpreviously only retried the disconnect/reconnect loop before giving up. It never attempted aJMP_TO_APPsoft reset to recover an unhealthy/dirty bootloader session, even thoughPic32BootloaderProtocol.CreateJumpToApplicationMessage()already exists and Core already uses it on the success path (JumpToApplicationAndReconnectAsync).The observed real-world failure mode (daqifi-desktop#630) is a dirty HID bootloader handle left behind by another program (Microchip's HID bootloader PC tool, or historically desktop's own HID device finder). Once dirty,
RequestBootloaderVersionAsyncfails or returns garbage even though the physical device is present and enumerated.Closes #298
Changes
RunPic32UpdateAsync: the initial connect + version health check inConnectingis now wrapped in a try/catch. On failure (bad connect or a garbage/Errorversion response), it calls a newRecoverBootloaderHealthWithSoftResetAsyncbefore giving up.RecoverBootloaderHealthWithSoftResetAsync:CreateJumpToApplicationMessage()to the current HID handle, best-effort (guarded byIsConnected; a write failure falls through to the original failure rather than throwing a new unhandled exception).WaitForBootloaderDeviceAsync(bounded by the existingWaitingForBootloaderTimeout).ConnectToBootloaderWithRetryAsync) and retries the version health check exactly once.Connecting-state failure/guidance path and cleanup-eligibility logic (ConnectingstaysNotEligible— nothing has been erased yet) are completely unchanged.ErasingFlash/Programming/Verifying).Test plan
dotnet build Daqifi.Core.sln -c Release— 0 warnings/errorsdotnet test Daqifi.Core.sln -c Release— 1394/1396 passed, 2 skipped (real-hardware-only tests); no new failures. (ContinuousDeviceFinderTests.Start_DiscoversDevice_RaisesDeviceDiscoveredAndPopulatesDevicesis a known pre-existing intermittent flake, reproduces identically onmainwithout this change.)FirmwareUpdateServiceTests:UpdateFirmwareAsync_WhenBootloaderHealthCheckFails_SoftResetsAndCompletes— health check fails → soft reset issued → re-check succeeds → update proceeds through toComplete.UpdateFirmwareAsync_WhenSoftResetRecoveryAlsoFails_FallsThroughToFailedWithGuidance— health check fails → soft reset recovery also fails → falls through to today'sFailedbehavior, original exception andConnectingrecovery guidance preserved, noCleaningUp/Recovereddetour.DaqifiCoreProjectPath) and ran a full--fw-update-latestPIC32 update against a real Nyquist (COM3, FW 3.7.2). State tracePreparingDevice → WaitingForBootloader → Connecting → ErasingFlash → Programming (43,207 records) → Verifying (3 CRC regions) → JumpingToApp → Complete; the device re-enumerated healthy afterward at the same firmware version. The HID handle was clean going in, so this confirms the modifiedConnectingstep introduces no regression on the normal path (the new soft-reset branch is skipped entirely when the first health check succeeds) — it does not exercise the soft-reset recovery branch itself, which is covered deterministically by the two unit tests above instead.🤖 Generated with Claude Code