raphnet: query Windows HidP_GetCaps for feature-report buffer size#132
Merged
raphnet: query Windows HidP_GetCaps for feature-report buffer size#132
Conversation
…on Windows
Pulls in JRickey/libultraship#raphnet-win-hidcaps (commit c53d401f).
Fixes intermittent native Raphnet failure on Windows for raphnet
v3.6.1 firmware (vid=0x289b pid=0x0061, "N64 to USB v3.6"):
- hid_get_feature_report no longer fails with ERROR_INVALID_PARAMETER
when the device's caps-reported FeatureReportByteLength differs from
our hardcoded 64. We query the HID stack directly via
HidD_GetPreparsedData + HidP_GetCaps and use the OS-reported size.
- 50 ms post-hid_open_path settle delay (Windows only) covers the HID
handle initialization window where the first DeviceIoControl could
return INVALID_PARAMETER on a freshly-opened handle.
Linux/macOS: unchanged. Auto-fallback to SDL when polling fails 60
times in a row continues to work as a safety net for any device that
still can't be polled natively.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pulls in JRickey/libultraship#raphnet-win-hidcaps commit 9b7f3f99 so testers' release-build BattleShip.log captures every step of the Raphnet Open() sequence (HidP_GetCaps fields, each command's reply bytes, firmware version, FIRST successful poll). Default release log level is WARN+ so the existing INFO-level narration was being dropped. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes intermittent native Raphnet failure for users with gc_n64_usb-v3.6.1 firmware (vid=0x289b pid=0x0061, "N64 to USB v3.6") on Windows, plus adds diagnostic logging so the next failure (if any) is fully captured in release-build BattleShip.log.
The original v0.7.3-beta tester's logs show three intermittent failure modes across ~12 launches:
hid_get_feature_reportreturns -1 /ERROR_INVALID_PARAMETER(0x57) on the first auto-negotiation probe; both 63 and 32 byte attempts fail identically. Open() returns false → SDL fallback claims the controller.SUSPEND_POLLING(0)probe succeeds (auto-negotiation passes), thenGET_VERSIONread times out withn=0, err=Successafter 1 s.Open()succeeds, everyRAW_SIpoll then times out and the auto-fallback drops to SDL after 60 frames.The auto-fallback (PR #111) consistently delivered them a working SDL-mapped controller within 1 s in all three modes, but the "Native Raphnet enabled" banner never stuck.
Root cause (Mode A)
Asymmetric size handling inside hidapi 0.14.0 on Windows:
hid_send_feature_reportauto-pads the caller's buffer up tocaps.FeatureReportByteLength(windows/hid.c:1189-1204, explicit comment: "Windows expects at least caps.FeatureReportByteLength bytes passed to HidD_SetFeature(), even if the report is shorter. Any less sent and the function fails with error ERROR_INVALID_PARAMETER set.").hid_get_feature_reportdoes not auto-pad — it passes the buffer length straight toDeviceIoControl(IOCTL_HID_GET_FEATURE).So any caller-supplied size that doesn't exactly match
caps.FeatureReportByteLengthsilently sends fine but reads fail with INVALID_PARAMETER. Our hardcoded 64-byte buffer matches the firmware's REPORT_COUNT 63 + 1 report-id byte in the common case, but driver/OS-version interactions can produce a different OS-reported caps value, or transiently fail until the HID handle settles. The auto-negotiation can't recover because the 32-byte fallback is also wrong size, so it fails identically.Modes B and C are likely the same root cause expressed at different points in the command stream — short replies (
SUSPEND_POLLINGecho, version string) sometimes squeak through a wrong-size buffer while the longer 7-byteRAW_SIreply does not.Fix
libultraship/src/ship/controller/raphnet/RaphnetTransport.cpp:HidD_GetPreparsedData+HidP_GetCapsand usecaps.FeatureReportByteLengthas the authoritative buffer size. Skip the auto-negotiation probe entirely — the OS knows the exact size, so don't guess. (gcn64tools'rntlib/raphnetadapter.cdoes effectively the same thing internally.)hid_open_pathsettle delay (Windows only) — Mode B's symptom (read times out after the first command works) and intermittent Mode A failures across launches are consistent with the HID handle not being fully ready for IOCTLs immediately afterhid_open_pathreturns. Invisible to the user, covers the stack initialization window.Diagnostic logging (second commit)
Release builds default the spdlog level to WARN (Context.h:73), so existing INFO-level instrumentation in the Open path was silently dropped from BattleShip.log. When a tester reports Native Raphnet not working we currently see only the failure ERRORs — not the values that would explain why.
Promote the load-bearing Open-time logs to WARN behind a
[raphnet-diag]prefix so the next tester's log captures the whole flow:HidP_GetCapsdumps every relevant field (UsagePage,Usage,InputReportByteLength,OutputReportByteLength,FeatureReportByteLength, NumberLink/Button/Value caps). On any failure path log theGetLastErroror NTSTATUS so we can distinguish "device not exposed yet" from "preparsed data corrupt" from "caps unsupported".n,rep[0], and the raw reply bytes — covers the defensiveSUSPEND_POLLING(0),GET_VERSION,SUSPEND_POLLING(1)steps individually.mReportSizeso we know whether failure was inside Open or later during Poll.buffer_sizeand (for get)retry_countso Mode A's INVALID_PARAMETER can be correlated against the caps-reportedFeatureReportByteLengthimmediately above in the log.mReportSizefor cross-reference.Mode C (RAW_SI silently drops after Open succeeds) isn't addressed by code change — but with the new diagnostics, if it persists post-fix the next log will show the full caps fields, every Exchange reply, and the exact
mReportSizeused, which should be enough to pinpoint whether it's a residual size mismatch, a firmware timing constraint on the SI bus, or something else. The existing auto-fallback continues to handle Mode C correctly: returns the user to a working SDL-mapped controller within 1 s.Companion libultraship PR
JRickey/libultraship#raphnet-win-hidcaps (commits
c53d401fand9b7f3f99)Sources
dataHidReport.c:20-30— REPORT_COUNT 63, no Report ID; was 40 pre-v3.4rntlib/raphnetadapter.c:81— PID 0x0061 caps row, interface 1, bio_support enabledrntlib/raphnetadapter.c:416,525— default report size 63, send/recv buffer = report_size+1 = 64windows/hid.c:1189-1204— asymmetric padding (root cause)Test plan
[raphnet-diag] HidP_GetCaps OK: ... FeatureReportByteLength=Nappears in BattleShip.log on next launch[raphnet-diag]trail showing exactly where the new flow still fails