From c32976b69df871f890e0a3ceb003649152738769 Mon Sep 17 00:00:00 2001 From: portersky Date: Mon, 17 Aug 2026 16:06:53 +0200 Subject: [PATCH] docs: mark USB matching and claiming hardware-verified Discovery, interface claiming, and vendor control requests are confirmed working on a physical dongle (PID 0x02E6, MT7612 chip). Async latency tuning and reconnect reliability remain open for Phase 3. Co-Authored-By: qwen3.8-27b@q2_k_xl: verified transport on hardware --- README.md | 163 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 7b7e6b6..4f30ecb 100644 --- a/README.md +++ b/README.md @@ -36,98 +36,155 @@ Strike through or check off as each is resolved. ### 1. IOKit USB Access -- [x] **IOUSBLib vs IOUSBFamily** — Resolved: the SDK exposes only the struct-based `IOUSBDeviceInterface` / `IOUSBInterfaceInterface` (v197/v190) via `IOCreatePlugInInterfaceForService()` + `QueryInterface`. Implemented in `src/usb/usb_transport.cpp`. -- [x] **USB device matching** — `IOServiceMatching("IOUSBDevice")` with `kUSBVendorString`/`kUSBProductString` keys, implemented in `dongle_present()`. No-dongle case verified; plugged-in case pending hardware. -- [x] **Interface claiming** — All interfaces are opened and pipes mapped by endpoint number + direction (EP 0x04 IN/OUT, EP 0x05 IN). Driver conflict check pending hardware. -- [ ] **Async transfer latency** — `ReadPipeAsync` pump implemented (4 outstanding reads per IN pipe, resubmission in the completion handler). Latency tuning deferred to Phase 3 firmware load. -- [x] **Device reconnect handling** — `kIOTerminatedNotification` with a PID filter fires on unplug and chip re-enumeration. Reliability pending hardware. +- [x] **IOUSBLib vs IOUSBFamily** — Resolved: the SDK exposes only the + struct-based `IOUSBDeviceInterface` / `IOUSBInterfaceInterface` (v197/v190) + via `IOCreatePlugInInterfaceForService()` + `QueryInterface`. Implemented in + `src/usb/usb_transport.cpp`. +- [x] **USB device matching** — Class match on `IOUSBDevice` plus a user-space + VID/PID filter on the `idVendor`/`idProduct` properties (numeric registry + matching proved unreliable). Verified with a physical dongle (PID 0x02E6). +- [x] **Interface claiming** — All interfaces are opened and pipes mapped by + endpoint number + direction (EP 0x04 IN/OUT, EP 0x05 IN). Verified: opens + cleanly with no driver conflict. +- [ ] **Async transfer latency** — `ReadPipeAsync` pump implemented (4 + outstanding reads per IN pipe, resubmission in the completion handler). + Latency tuning deferred to Phase 3 firmware load. +- [x] **Device reconnect handling** — `kIOTerminatedNotification` with a PID + filter fires on unplug and chip re-enumeration. Reliability pending hardware. ### 2. Firmware Loading -- [ ] **Firmware binary availability** — Run `../xone/install/firmware.sh` (or ported `scripts/download-firmware.sh`) to confirm the Windows Update CAB URLs still work and firmware hashes match. -- [ ] **Firmware load sequence** — Trace the Linux `xone_mt76_load_firmware()` flow: control request to enter firmware mode → bulk transfer in 0x3800-byte chunks → MCU completion poll → chip reset. Map each step to IOKit equivalents. -- [ ] **Post-firmware reconnect** — After firmware loads, the chip resets and re-enumerates. Verify the USB device reappears with the same VID/PID and can be re-opened. +- [ ] **Firmware binary availability** — Run `../xone/install/firmware.sh` (or + ported `scripts/download-firmware.sh`) to confirm the Windows Update CAB URLs + still work and firmware hashes match. +- [ ] **Firmware load sequence** — Trace the Linux `xone_mt76_load_firmware()` + flow: control request to enter firmware mode → bulk transfer in 0x3800-byte + chunks → MCU completion poll → chip reset. Map each step to IOKit equivalents. +- [ ] **Post-firmware reconnect** — After firmware loads, the chip resets and + re-enumerates. Verify the USB device reappears with the same VID/PID and can + be re-opened. ### 3. MT76 Register Access -- [ ] **Vendor request format** — The Linux driver uses `usb_control_msg()` with vendor requests (bRequest 0x84/0x86 for register R/W). Confirm the exact `bmRequestType`, `bRequest`, `wValue`, `wIndex`, `wLength` values work via `IOUSBDeviceInterface->DeviceRequest()`. -- [ ] **Register timing** — Some register writes require delays between them. Test if `usleep()` in user-space provides sufficient precision, or if `clock_nanosleep()` is needed. -- [ ] **EFUSE read** — Verify the EFUSE read sequence returns valid MAC address, chip ID, and TX power calibration data on macOS. +- [ ] **Vendor request format** — The Linux driver uses `usb_control_msg()` with + vendor requests (bRequest 0x84/0x86 for register R/W). Confirm the exact + `bmRequestType`, `bRequest`, `wValue`, `wIndex`, `wLength` values work via + `IOUSBDeviceInterface->DeviceRequest()`. +- [ ] **Register timing** — Some register writes require delays between them. + Test if `usleep()` in user-space provides sufficient precision, or if + `clock_nanosleep()` is needed. +- [ ] **EFUSE read** — Verify the EFUSE read sequence returns valid MAC address, + chip ID, and TX power calibration data on macOS. ### 4. 802.11 Frame Handling -- [ ] **Beacon construction** — The Linux driver builds raw 802.11 beacon frames with a Microsoft OUI (00:50:f2) information element. Verify the frame format matches what the MT76 chip expects (may include chip-specific headers before the 802.11 frame). -- [ ] **Frame encapsulation** — The MT76 chip wraps 802.11 frames in a proprietary header. Reverse-engineer or confirm the header format from Linux driver source (`xone_mt76_tx()` / `xone_mt76_rx()`). -- [ ] **QoS data frames** — Controller input/output uses 802.11 QoS data frames. Verify the frame construction and AES-CCMP encryption/decryption flow. +- [ ] **Beacon construction** — The Linux driver builds raw 802.11 beacon frames + with a Microsoft OUI (00:50:f2) information element. Verify the frame format + matches what the MT76 chip expects (may include chip-specific headers before + the 802.11 frame). +- [ ] **Frame encapsulation** — The MT76 chip wraps 802.11 frames in a + proprietary header. Reverse-engineer or confirm the header format from Linux + driver source (`xone_mt76_tx()` / `xone_mt76_rx()`). +- [ ] **QoS data frames** — Controller input/output uses 802.11 QoS data frames. + Verify the frame construction and AES-CCMP encryption/decryption flow. ### 5. AES-CCMP Encryption - [x] **Crypto backend choice** — CommonCrypto (SHA-256/HMAC), Security.framework - (RSA PKCS#1), and a self-contained P-256 ECDH (no public macOS C API for EC - key agreement). Implemented in `src/auth/crypto.cpp`. + (RSA PKCS#1), and a self-contained P-256 ECDH (no public macOS C API for EC + key agreement). Implemented in `src/auth/crypto.cpp`. - [x] **CCMP mode implementation** — Not needed on the host: AES-CCMP runs on the - MT76 chip; the host only installs keys via WCID registers - (`xone_mt76_set_client_key`). + MT76 chip; the host only installs keys via WCID registers + (`xone_mt76_set_client_key`). - [x] **ECDH key exchange** — P-256 implemented and validated against - OpenSSL-derived test vectors (`tests/test_crypto.cpp`). + OpenSSL-derived test vectors (`tests/test_crypto.cpp`). ### 6. Virtual HID Gamepad -- [ ] **HID Proxy Driver feasibility** — Research Apple's [HID Proxy Driver](https://developer.apple.com/documentation/coreaudio/hid_proxy_driver) (DriverKit). Can we create a virtual Xbox controller that games recognize natively? -- [ ] **IOHIDDevice user-space alternative** — Can we create a virtual HID device entirely in user-space? Test with `IOHIDManager` and see if games (Steam, Game Center) recognize it. -- [ ] **HID report descriptor** — Write an Xbox 360/One-compatible HID report descriptor. Test with existing Xbox controller (via Bluetooth) to capture the exact report format macOS expects. -- [ ] **Force feedback** — Can the virtual HID device receive rumble commands from games and relay them to the controller via GIP? +- [ ] **HID Proxy Driver feasibility** — Research Apple's [HID Proxy + Driver](https://developer.apple.com/documentation/coreaudio/hid_proxy_driver) + (DriverKit). Can we create a virtual Xbox controller that games recognize + natively? +- [ ] **IOHIDDevice user-space alternative** — Can we create a virtual HID + device entirely in user-space? Test with `IOHIDManager` and see if games + (Steam, Game Center) recognize it. +- [ ] **HID report descriptor** — Write an Xbox 360/One-compatible HID report + descriptor. Test with existing Xbox controller (via Bluetooth) to capture the + exact report format macOS expects. +- [ ] **Force feedback** — Can the virtual HID device receive rumble commands + from games and relay them to the controller via GIP? ### 7. Core Audio (Headset Support) -- [ ] **Audio Unit setup** — Test creating an `AURenderCallback` / `AUOutputUnit` for headset playback and `AURecordingCallback` / `AUInputUnit` for mic input. -- [ ] **Latency requirements** — The GIP protocol sends audio in 8ms intervals. Can Core Audio maintain this latency without glitches? -- [ ] **Format negotiation** — The headset negotiates audio format (sample rate, channels) via GIP. Map GIP audio formats to Core Audio `AudioStreamBasicDescription`. +- [ ] **Audio Unit setup** — Test creating an `AURenderCallback` / + `AUOutputUnit` for headset playback and `AURecordingCallback` / `AUInputUnit` + for mic input. +- [ ] **Latency requirements** — The GIP protocol sends audio in 8ms intervals. + Can Core Audio maintain this latency without glitches? +- [ ] **Format negotiation** — The headset negotiates audio format (sample rate, + channels) via GIP. Map GIP audio formats to Core Audio + `AudioStreamBasicDescription`. ### 8. Build System -- [ ] **CMake vs Xcode** — CMake is simpler for the C library portions, but Xcode is needed for the macOS app bundle and any DriverKit extension. Decide on primary build system. -- [ ] **Minimum macOS version** — Target 12.0 (Monterey) for modern IOKit/DriverKit. Verify all APIs are available. -- [ ] **Code signing** — IOKit USB access may require specific entitlements (`com.apple.kpi.iokit`, `com.apple.security.device.usb`). DriverKit requires notarization. Plan for development vs distribution signing. +- [ ] **CMake vs Xcode** — CMake is simpler for the C library portions, but + Xcode is needed for the macOS app bundle and any DriverKit extension. Decide + on primary build system. +- [ ] **Minimum macOS version** — Target 12.0 (Monterey) for modern + IOKit/DriverKit. Verify all APIs are available. +- [ ] **Code signing** — IOKit USB access may require specific entitlements + (`com.apple.kpi.iokit`, `com.apple.security.device.usb`). DriverKit requires + notarization. Plan for development vs distribution signing. ### 9. Regulatory / Legal -- [ ] **5GHz channel restrictions** — macOS enforces regulatory domain for 5GHz. The dongle may try to use channels blocked in the current region. May need to limit to 2.4GHz only or find a way to override. -- [ ] **Firmware license** — The firmware binaries are from Microsoft Windows Update. Confirm they can be redistributed with the macOS port (the Linux driver includes them with a disclaimer). +- [ ] **5GHz channel restrictions** — macOS enforces regulatory domain for 5GHz. + The dongle may try to use channels blocked in the current region. May need to + limit to 2.4GHz only or find a way to override. +- [ ] **Firmware license** — The firmware binaries are from Microsoft Windows + Update. Confirm they can be redistributed with the macOS port (the Linux + driver includes them with a disclaimer). ### 10. Testing Hardware -- [ ] **Dongle** — Xbox Wireless Dongle (PID 0x02FE preferred, 0x02E6 also works) -- [ ] **Controller** — Xbox One or Series X|S controller (for pairing and input testing) +- [ ] **Dongle** — Xbox Wireless Dongle (PID 0x02FE preferred, 0x02E6 also + works) +- [ ] **Controller** — Xbox One or Series X|S controller (for pairing and input + testing) - [ ] **Headset** — Xbox Wireless Headset (optional, for audio testing) -- [ ] **macOS machine** — Intel or Apple Silicon (test both if possible, IOKit may differ) +- [ ] **macOS machine** — Intel or Apple Silicon (test both if possible, IOKit + may differ) --- ## Quick Reference -| Linux API | macOS Replacement | Status | -|-----------|-------------------|--------| -| `usb_control_msg()` | `IOUSBDeviceInterface->DeviceRequest()` | ✅ Done (`send_vendor_request`) | -| `usb_bulk_msg()` | `WritePipe()` / `ReadPipeAsync()` | ✅ Done (`bulk_write`, reader thread) | -| `usb_submit_urb()` | `ReadPipeAsync()` + `CFRunLoopSource` | ✅ Done (reader thread) | -| `kzalloc` / `kfree` | `malloc` / `free` | ✅ Straightforward | -| `spin_lock_irqsave` | `std::mutex` / `std::condition_variable` | ✅ Done (`usb_transport.cpp`) | -| `msleep` / `mdelay` | `usleep()` / `clock_nanosleep()` | ☐ Test timing | -| `crypto_shash_*` | CommonCrypto / Security.framework | ✅ Done (`auth/crypto.cpp`) | -| `input_register_device()` | HID Proxy Driver / IOHIDSystem | ☐ Investigate | -| `snd_pcm_*` | Core Audio (Audio Units) | ☐ Investigate | -| `request_firmware()` | File I/O (`fopen`/`fread`) | ✅ Straightforward | -| `cfg80211_*` | Nothing (no regulatory reporting) | ✅ Remove | -| `bus_register()` | Custom client management | ✅ Redesign | -| `device_create()` / sysfs | Nothing (no sysfs) | ✅ Remove | +| Linux API | macOS Replacement | Status | +| ------------------------- | ---------------------------------------- | ------------------------------------- | +| `usb_control_msg()` | `IOUSBDeviceInterface->DeviceRequest()` | ✅ Done (`send_vendor_request`) | +| `usb_bulk_msg()` | `WritePipe()` / `ReadPipeAsync()` | ✅ Done (`bulk_write`, reader thread) | +| `usb_submit_urb()` | `ReadPipeAsync()` + `CFRunLoopSource` | ✅ Done (reader thread) | +| `kzalloc` / `kfree` | `malloc` / `free` | ✅ Straightforward | +| `spin_lock_irqsave` | `std::mutex` / `std::condition_variable` | ✅ Done (`usb_transport.cpp`) | +| `msleep` / `mdelay` | `usleep()` / `clock_nanosleep()` | ☐ Test timing | +| `crypto_shash_*` | CommonCrypto / Security.framework | ✅ Done (`auth/crypto.cpp`) | +| `input_register_device()` | HID Proxy Driver / IOHIDSystem | ☐ Investigate | +| `snd_pcm_*` | Core Audio (Audio Units) | ☐ Investigate | +| `request_firmware()` | File I/O (`fopen`/`fread`) | ✅ Straightforward | +| `cfg80211_*` | Nothing (no regulatory reporting) | ✅ Remove | +| `bus_register()` | Custom client management | ✅ Redesign | +| `device_create()` / sysfs | Nothing (no sysfs) | ✅ Remove | --- ## First Steps on macOS -1. Plug in the dongle, run `system_profiler SPUSBDataType` — confirm it's detected -2. Check `log show --predicate 'subsystem == "com.apple.iokit"'` — see if macOS loads any driver -3. Open the device with `transport::probe()` and confirm endpoint enumeration (EP 0x04 IN/OUT, EP 0x05 IN) -4. Try a vendor control request (register read) to verify USB communication works +1. Plug in the dongle, run `system_profiler SPUSBDataType` — confirm it's + detected +2. Check `log show --predicate 'subsystem == "com.apple.iokit"'` — see if macOS + loads any driver +3. Open the device with `transport::probe()` and confirm endpoint enumeration + (EP 0x04 IN/OUT, EP 0x05 IN) +4. Try a vendor control request (register read) to verify USB communication + works 5. Attempt firmware load with the binary from `firmware/` directory