feat(hid): exclusive HID open for held bootloader sessions - #274
Conversation
Add HidLibraryTransport.ExclusiveAccess (default false). When set, the HID device is opened exclusively — Windows dwShareMode=0 via HidSharp OpenConfiguration, macOS IOKit kIOHIDOptionsTypeSeizeDevice — so no other user-mode opener can open or write to the device while the handle is held. Best-effort: a refused exclusive open falls back to a shared open so existing flows are never regressed. IHidTransportDevice.Open() becomes Open(bool exclusive) across both platform backends and the test fakes. Enables the desktop to grab and hold a sitting PIC32 HID bootloader and to lock out other openers during a flash (the bootloader's CRC gate is disabled, so a stray frame from another opener could be mis-parsed as an ERASE).
PR Summary by QodoAdd best-effort exclusive HID opens for bootloader flash sessions Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
…s a throwing TryOpen The shared-open fallback only ran when the exclusive TryOpen returned false; if it threw, ConnectAsync failed hard, contradicting the documented best-effort fallback. Wrap the exclusive TryOpen so a throw also falls through to the shared open.
|
/improve |
|
/agentic_review |
PR Code Suggestions ✨Latest suggestions up to e141c34 Warning
Previous suggestionsSuggestions up to commit 8e24081
Suggestions up to commit 9d266d0
|
|||||||||||||||||||||||||||||||||||
|
Code review by qodo was updated up to the latest commit 9d266d0 |
When both the exclusive and shared opens fail, surface the root cause: - HidSharp: capture the exclusive-open exception and chain it as the inner exception of the final IOException. - macOS: include both the seize and shared-open IOReturn codes in the message.
|
/improve |
|
/agentic_review |
PR Code Suggestions ✨Warning
No code suggestions found for the PR. |
|
Code review by qodo was updated up to the latest commit 60e4fcc |
The shared-open fallback could itself throw, bypassing the normalized IOException and dropping the captured exclusive-open cause. Wrap it and, on a double failure, chain both causes via AggregateException so neither is lost.
|
/improve |
|
/agentic_review |
|
Persistent suggestions updated to latest commit 8e24081 |
|
Code review by qodo was updated up to the latest commit 8e24081 |
…dy macOS codes - HidSharp: check the TryOpen bool on both the exclusive and shared opens (a false return with a non-null stream was misclassified as success); dispose any partial stream so it isn't leaked. - macOS: only populate seizeResult during an exclusive attempt so the error message never reports a misleading seize code on a shared-only open.
|
/improve |
|
/agentic_review |
|
Persistent suggestions updated to latest commit e141c34 |
|
Code review by qodo was updated up to the latest commit e141c34 |
|
Re: The bug pass ( This last suggestion proposes synthesizing
Treating the bug pass (0) as the convergence signal here. |
What
Adds
HidLibraryTransport.ExclusiveAccess(defaultfalse). When set, the HID device is opened exclusively so no other user-mode opener can open or write to it while this transport holds the handle:dwShareMode=0via HidSharpOpenConfiguration+OpenOption.Exclusive.kIOHIDOptionsTypeSeizeDevice.IHidTransportDevice.Open()becomesOpen(bool exclusive)across both platform backends and the test fakes.Best-effort: a refused exclusive open falls back to a shared open, so any flow that works today is never regressed.
Why
Enables the desktop to grab and hold a sitting PIC32 HID bootloader and to lock other openers out for the duration of a flash. The bootloader's inbound CRC gate is disabled in fielded firmware, so a stray
SOH…EOTframe from another opener (e.g. a discovery loop) could be mis-parsed as anERASE; the exclusive handle guards against that. A vendor-defined top-level collection (Usage Page 0xFF00) permits exclusive access, unlike the system keyboard/mouse collectionshidclasskeeps shared.This is the minimal Core change that the upcoming desktop grab-and-hold-the-bootloader feature depends on.
Testing
🤖 Generated with Claude Code