You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Touchless SWD flash of the physically-attached NXP LPC845-BRK (VID:PID 1FC9:0132, LPC-Link2 v1.0.7 firmware) works end-to-end via a locally-built probe-rs from the FastLED fork, using the STOCK factory debugger firmware — no button press, no reflash. Acceptance:
exits 0, reads back correct flash contents, and the board runs the flashed sketch after probe-rs reset --chip LPC845M301JBD48 --probe 1fc9:0132.
Related: #935 is the hardware-validation umbrella and closes automatically when this issue closes with the LPC845-BRK deploying touchless.
Background — everything an agent needs to know before starting
Root cause (finally correctly diagnosed). The v1.0.7 firmware on the LPC-Link2 debug chip is a compiled build of open-source ARMmbed/DAPLink + ARM's CMSIS-5 reference DAP firmware. Reading source/daplink/cmsis-dap/DAP.c in DAPLink confirms:
The firmware is NOT silently dropping DAP commands. That was our wrong hypothesis for months. The commands never reach the chip because probe-rs is talking to the wrong USB endpoint (or failing to open one at all) on Windows via hidapi.
What MCUXpresso does differently — it uses direct WinUSB, not hidapi. No proprietary protocol, no secret commands, no reverse-engineering needed — just a different USB stack on the host side that avoids Windows hidapi's composite-HID interface-picker bug.
Prior work that already landed (do NOT re-do these):
FastLED/probe-rs PR Esp32-s3 PSRAM use #1 (merged): DAP_Info sub-command fallbacks (PacketSize / PacketCount / Capabilities) with spec defaults on timeout. Preserved — improves resilience against slow probes.
Cross-compile CI on the tools branch of FastLED/framework-arduino-lpc8xx (six targets, cached by source-tree SHA256). Runs on tag push fastled-v* and publishes GitHub Releases.
Local interface-picker patch (already committed at ~/dev/probe-rs/probe-rs/src/probe/cmsisdap/tools.rs): rejects interface_number > 0 for VID 0x1fc9 PIDs 0x0090/0x0132, mirrors OpenOCD cmsis_dap_usb_hid.c:107-109. Compiles + passes the filter step, but hid_api.open_device() itself still returns NotFound on Windows for this composite HID.
Why the current patches don't finish the job. hidapi on Windows cannot reliably open the CMSIS-DAP HID interface of NXP's composite device — the interface-picker patch got us past the "which candidate to select" step, but the subsequent HidDevice::open_path() call fails at the Windows HID.dll level. Symptom: Failed to open probe → NotFound.
The fix — replace hidapi with nusb for CMSIS-DAP v1 HID transport
probe-rs already uses nusb for the CMSIS-DAP v2 (bulk) transport (probe/cmsisdap/tools.rs::open_v2_device). Extend the same pattern to v1: talk to interrupt endpoints on interface 0 directly through nusb, skip hidapi entirely on all platforms.
CMSIS-DAP v1 over HID is just interrupt IN + OUT endpoints. Nothing hidapi does is load-bearing — it's a thin wrapper over what nusb already exposes as Interface::interrupt_in_queue / interrupt_out_queue.
Concrete file map
File
Change
probe-rs/src/probe/cmsisdap/tools.rs
Add open_v1_nusb_device() mirroring open_v2_device. Drop hidapi fallback for supported paths. Keep hidapi behind a feature flag / graceful-degrade for niche cases.
probe-rs/src/probe/cmsisdap/commands/mod.rs
Add a CmsisDapDevice::V1Nusb { handle, ep_in, ep_out, packet_size } variant (or teach the existing V1 variant to hold a nusb Interface + endpoint numbers instead of a HidDevice). Route send_command / receive_response through nusb interrupt_in_queue.submit / next_complete and interrupt_out_queue.submit / next_complete.
probe-rs/Cargo.toml
Move hidapi to an optional dep (retain for Linux hidraw-only edge cases if any). Confirm nusb version already vendored covers all six cross-compile targets.
Reference implementation to mirror
Read open_v2_device in probe-rs/src/probe/cmsisdap/tools.rs — it opens a nusb interface, claims it, and hands endpoint numbers to a V2 variant of CmsisDapDevice. Do the same for v1 but with interrupt endpoints instead of bulk.
For CMSIS-DAP v1 HID framing: on the wire it's just packets, no HID report ID prefix (see DAPLink's usbd_LPC11Uxx.c). But hidapi historically requires a leading 0x00 "no report ID" byte on Windows writes — that byte is a hidapi convention, NOT something the firmware expects. When you cut over to nusb, drop the leading 0x00 padding; write DAP command bytes directly starting with the command ID.
Pitfalls learned the hard way
HID Report ID. DAPLink's LPC11U35 HID descriptor has no Report ID (single unnamed report). hidapi's leading 0x00 on Windows is spurious for nusb — do NOT prepend it.
Interface number. Interface 0 is the CMSIS-DAP HID. Interface 1 is the CDC VCOM (UART bridge). Interface 2 is CDC data. Do not claim interfaces 1/2 — they're already claimed by usbccgp / VCOM driver.
Windows WinUSB driver binding. nusb on Windows uses WinUSB via the composite driver stack. The LPC-Link2's interface 0 (HID class) currently binds to HidUsb, which may prevent nusb from claiming it. You may need to detach HidUsb first (WinUSB substitution via libwdi or a device-installer INF). Investigate first — if this is a blocker, document exactly what INF the user has to install once, and stage that install into ~/.fbuild/tools/ (parallels the dfu-util scaffolding in crates/fbuild-deploy/src/lpc_debugger_reflash.rs).
Endpoint packet size. DAPLink's LPC11U35 config hard-codes DAP_PACKET_SIZE = 64. Read this via DAP_Info(0xFF) after connect (already handled by PR Esp32-s3 PSRAM use #1's fallback); don't assume.
The v1.0.7 firmware doesn't populate per-interface iStrings — only the top-level product_string is "LPC11U3x CMSIS-DAP v1.0.7". Don't filter interfaces by iString presence.
Hardware acceptance test
Physically-attached LPC845-BRK on the maintainer's Windows machine (COM10, VID:PID 1FC9:0132, serial 0B03400A). Test project already exists at tests/platform/lpc845brk/ with a compiled firmware.elf.
Verification steps:
probe-rs list shows the LPC-Link2 (already works today).
probe-rs info --chip LPC845M301JBD48 --probe 1fc9:0132 returns IDCODE 0x0BC11477 (Cortex-M0+ ROM table) and does NOT error at "USB device could not be opened."
probe-rs download … firmware.elf writes flash and exits 0.
probe-rs reset --chip … --probe … puts the target into the flashed sketch.
All FOUR of the above complete without touching SW3 (ISP) or SW4 (RESET) on the board.
Definition of done
probe-rs download flashes firmware.elf to the LPC845-BRK on the maintainer's Windows box, touchless, from the FastLED-fork build.
Same test also passes on Linux x86_64 (via WSL2 usbipd or a native Linux box), to confirm the nusb path is cross-platform.
A cross-compiled Windows binary published via the tools-branch fastled-release-cross.yml GitHub Action reproduces the hardware test locally on the maintainer's box (no local soldr cargo build required).
fbuild's LpcDeployer (crates/fbuild-deploy/src/lpc.rs) gains an optional dispatch to probe-rs SWD when the LPC-Link2 is present, falling back to lpc21isp UART ISP when it isn't. Guarded behind a --via probe-rs flag until stable.
Round-2 agent finding — OpenOCD cmsis_dap_usb_hid.c:107-109 LPC-Link2 quirk (already ported into probe-rs but insufficient because hidapi can't open the interface on Windows anyway).
If this Option B path hits a blocker (WinUSB driver-binding, endpoint discovery, etc.), the reflash-to-DAPLink escape hatch is scaffolded already at crates/fbuild-deploy/src/lpc_debugger_reflash.rs. Don't confuse the two paths — this issue is Option B (touchless with STOCK firmware). Option D lives in its own follow-up.
Also: the DFU jumper on the LPC845-BRK is P5, not JP1 — earlier notes had the wrong pin. Only relevant if this path has to fall back.
Goal (satisfaction criteria)
Touchless SWD flash of the physically-attached NXP LPC845-BRK (VID:PID
1FC9:0132, LPC-Link2 v1.0.7 firmware) works end-to-end via a locally-builtprobe-rsfrom the FastLED fork, using the STOCK factory debugger firmware — no button press, no reflash. Acceptance:exits 0, reads back correct flash contents, and the board runs the flashed sketch after
probe-rs reset --chip LPC845M301JBD48 --probe 1fc9:0132.Related: #935 is the hardware-validation umbrella and closes automatically when this issue closes with the LPC845-BRK deploying touchless.
Background — everything an agent needs to know before starting
Root cause (finally correctly diagnosed). The v1.0.7 firmware on the LPC-Link2 debug chip is a compiled build of open-source ARMmbed/DAPLink + ARM's CMSIS-5 reference DAP firmware. Reading
source/daplink/cmsis-dap/DAP.cin DAPLink confirms:DAP_Info(PacketSize=0xFF)handler EXISTS unconditionally, returns hard-coded 64 bytes (DAP_PACKET_SIZEinsource/hic_hal/nxp/lpc11u35/DAP_config.h).DAP_Info(Capabilities=0xF0)handler EXISTS unconditionally, returns 0x01 (SWD-only).DAP_Connecthandler EXISTS unconditionally.The firmware is NOT silently dropping DAP commands. That was our wrong hypothesis for months. The commands never reach the chip because probe-rs is talking to the wrong USB endpoint (or failing to open one at all) on Windows via hidapi.
What MCUXpresso does differently — it uses direct WinUSB, not hidapi. No proprietary protocol, no secret commands, no reverse-engineering needed — just a different USB stack on the host side that avoids Windows hidapi's composite-HID interface-picker bug.
Prior work that already landed (do NOT re-do these):
DAP_Infosub-command fallbacks (PacketSize / PacketCount / Capabilities) with spec defaults on timeout. Preserved — improves resilience against slow probes.DAP_Connectexplicit-SWD + 4× retry. Preserved.toolsbranch ofFastLED/framework-arduino-lpc8xx(six targets, cached by source-tree SHA256). Runs on tag pushfastled-v*and publishes GitHub Releases.~/dev/probe-rs/probe-rs/src/probe/cmsisdap/tools.rs): rejectsinterface_number > 0for VID0x1fc9PIDs0x0090/0x0132, mirrors OpenOCDcmsis_dap_usb_hid.c:107-109. Compiles + passes the filter step, buthid_api.open_device()itself still returnsNotFoundon Windows for this composite HID.Why the current patches don't finish the job. hidapi on Windows cannot reliably open the CMSIS-DAP HID interface of NXP's composite device — the interface-picker patch got us past the "which candidate to select" step, but the subsequent
HidDevice::open_path()call fails at the Windows HID.dll level. Symptom:Failed to open probe → NotFound.The fix — replace hidapi with nusb for CMSIS-DAP v1 HID transport
probe-rs already uses nusb for the CMSIS-DAP v2 (bulk) transport (
probe/cmsisdap/tools.rs::open_v2_device). Extend the same pattern to v1: talk to interrupt endpoints on interface 0 directly through nusb, skip hidapi entirely on all platforms.CMSIS-DAP v1 over HID is just interrupt IN + OUT endpoints. Nothing hidapi does is load-bearing — it's a thin wrapper over what nusb already exposes as
Interface::interrupt_in_queue/interrupt_out_queue.Concrete file map
probe-rs/src/probe/cmsisdap/tools.rsopen_v1_nusb_device()mirroringopen_v2_device. Drop hidapi fallback for supported paths. Keephidapibehind a feature flag / graceful-degrade for niche cases.probe-rs/src/probe/cmsisdap/commands/mod.rsCmsisDapDevice::V1Nusb { handle, ep_in, ep_out, packet_size }variant (or teach the existingV1variant to hold a nusbInterface+ endpoint numbers instead of aHidDevice). Routesend_command/receive_responsethrough nusbinterrupt_in_queue.submit / next_completeandinterrupt_out_queue.submit / next_complete.probe-rs/Cargo.tomlhidapito an optional dep (retain for Linux hidraw-only edge cases if any). Confirm nusb version already vendored covers all six cross-compile targets.Reference implementation to mirror
Read
open_v2_deviceinprobe-rs/src/probe/cmsisdap/tools.rs— it opens a nusb interface, claims it, and hands endpoint numbers to aV2variant ofCmsisDapDevice. Do the same for v1 but with interrupt endpoints instead of bulk.For CMSIS-DAP v1 HID framing: on the wire it's just packets, no HID report ID prefix (see DAPLink's
usbd_LPC11Uxx.c). But hidapi historically requires a leading0x00"no report ID" byte on Windows writes — that byte is a hidapi convention, NOT something the firmware expects. When you cut over to nusb, drop the leading 0x00 padding; write DAP command bytes directly starting with the command ID.Pitfalls learned the hard way
0x00on Windows is spurious for nusb — do NOT prepend it.claiminterfaces 1/2 — they're already claimed byusbccgp/ VCOM driver.HidUsb, which may prevent nusb from claiming it. You may need to detachHidUsbfirst (WinUSB substitution vialibwdior a device-installer INF). Investigate first — if this is a blocker, document exactly what INF the user has to install once, and stage that install into~/.fbuild/tools/(parallels the dfu-util scaffolding incrates/fbuild-deploy/src/lpc_debugger_reflash.rs).DAP_PACKET_SIZE = 64. Read this viaDAP_Info(0xFF)after connect (already handled by PR Esp32-s3 PSRAM use #1's fallback); don't assume.product_stringis"LPC11U3x CMSIS-DAP v1.0.7". Don't filter interfaces by iString presence.Hardware acceptance test
Physically-attached LPC845-BRK on the maintainer's Windows machine (COM10, VID:PID
1FC9:0132, serial0B03400A). Test project already exists attests/platform/lpc845brk/with a compiledfirmware.elf.Verification steps:
probe-rs listshows the LPC-Link2 (already works today).probe-rs info --chip LPC845M301JBD48 --probe 1fc9:0132returns IDCODE0x0BC11477(Cortex-M0+ ROM table) and does NOT error at "USB device could not be opened."probe-rs download … firmware.elfwrites flash and exits 0.probe-rs reset --chip … --probe …puts the target into the flashed sketch.Definition of done
probe-rs downloadflashesfirmware.elfto the LPC845-BRK on the maintainer's Windows box, touchless, from the FastLED-fork build.tools-branchfastled-release-cross.ymlGitHub Action reproduces the hardware test locally on the maintainer's box (no localsoldr cargo buildrequired).LpcDeployer(crates/fbuild-deploy/src/lpc.rs) gains an optional dispatch to probe-rs SWD when the LPC-Link2 is present, falling back to lpc21isp UART ISP when it isn't. Guarded behind a--via probe-rsflag until stable.probe-rs/probe-rsPR opened (mirrors the fork PR body — draft under the same title).Prior-session reference material — links to preserve
FastLED/framework-arduino-lpc8xxtoolsbranch — patched probe-rs source + cross-compile CI. Retire theFastLED/probe-rsstandalone fork.cmsis_dap_usb_hid.c:107-109LPC-Link2 quirk (already ported into probe-rs but insufficient because hidapi can't open the interface on Windows anyway).source/daplink/cmsis-dap/DAP.c— firmware source proving the DAP handlers exist.source/hic_hal/nxp/lpc11u35/DAP_config.h— LPC11U35 hard-coded capabilities.Note on Option D (reflash) as a fallback
If this Option B path hits a blocker (WinUSB driver-binding, endpoint discovery, etc.), the reflash-to-DAPLink escape hatch is scaffolded already at
crates/fbuild-deploy/src/lpc_debugger_reflash.rs. Don't confuse the two paths — this issue is Option B (touchless with STOCK firmware). Option D lives in its own follow-up.Also: the DFU jumper on the LPC845-BRK is P5, not JP1 — earlier notes had the wrong pin. Only relevant if this path has to fall back.