docs: update README with current project status
Check off investigation items resolved on hardware (firmware load, EFUSE, radio init, beacon, pairing, reconnect), align build commands with AGENTS.md, add xone_cli and scripts to the layout, replace the completed first-steps list with a current status section, and drop em dashes per the documentation conventions. Co-Authored-By: qwen3.8-27b@q3_k_xl: updated README with current status
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Xbox Wireless Dongle — macOS Port
|
# Xbox Wireless Dongle: macOS Port
|
||||||
|
|
||||||
User-space macOS app for the Xbox Wireless Dongle (MediaTek MT76xx).
|
User-space macOS app for the Xbox Wireless Dongle (MediaTek MT76xx).
|
||||||
Ports the Linux kernel driver [`xone/`](../xone/) to macOS.
|
Ports the Linux kernel driver [`xone/`](../xone/) to macOS.
|
||||||
@@ -14,18 +14,22 @@ Swift is only supported by the **Ninja** and **Xcode** generators in CMake,
|
|||||||
so configure with one of them explicitly:
|
so configure with one of them explicitly:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cmake -S . -B build -G Ninja -DBUILD_TESTING=ON
|
cmake -S . -B build -GNinja -DBUILD_TESTING=ON
|
||||||
cmake --build build
|
ninja -C build
|
||||||
./build/xone_app
|
./build/xone_app
|
||||||
cmake --build build --target check # run the test suite
|
ninja -C build check # build and run the test suite
|
||||||
```
|
```
|
||||||
|
|
||||||
Layout (mirrors `../refix/`):
|
Layout (mirrors `../refix/`):
|
||||||
|
|
||||||
- `deps/` — CMake modules: `Platform.cmake`, `Flags.cmake`, `Sanitizers.cmake`, `FindUnity.cmake`
|
- `deps/`: CMake modules: `Platform.cmake`, `Flags.cmake`, `Sanitizers.cmake`,
|
||||||
- `src/{usb,mt76,gip,auth,hid}` — C++ protocol stack (ported from `medusalix/xone`)
|
`FindUnity.cmake`
|
||||||
- `src/app/` — Swift app entry point + C ABI bridge (`include/app/xone_api.h`)
|
- `src/{usb,mt76,gip,auth,hid}`: C++ protocol stack (ported from
|
||||||
- `tests/` — Unity test suite (`BUILD_TESTING=ON`)
|
`medusalix/xone`)
|
||||||
|
- `src/app/`: Swift app entry point + C ABI bridge (`include/app/xone_api.h`)
|
||||||
|
- `src/cli/`: C++ CLI (`xone_cli`) for manual protocol sequences
|
||||||
|
- `scripts/`: `download-firmware.sh` (port of the upstream firmware fetcher)
|
||||||
|
- `tests/`: Unity test suite (`BUILD_TESTING=ON`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -36,123 +40,124 @@ Strike through or check off as each is resolved.
|
|||||||
|
|
||||||
### 1. IOKit USB Access
|
### 1. IOKit USB Access
|
||||||
|
|
||||||
- [x] **IOUSBLib vs IOUSBFamily** — Resolved: the SDK exposes only the
|
- [x] **IOUSBLib vs IOUSBFamily**: Resolved: the SDK exposes only the
|
||||||
struct-based `IOUSBDeviceInterface` / `IOUSBInterfaceInterface` (v197/v190)
|
struct-based `IOUSBDeviceInterface` / `IOUSBInterfaceInterface` (v197/v190)
|
||||||
via `IOCreatePlugInInterfaceForService()` + `QueryInterface`. Implemented in
|
via `IOCreatePlugInInterfaceForService()` + `QueryInterface`. Implemented in
|
||||||
`src/usb/usb_transport.cpp`.
|
`src/usb/usb_transport.cpp`.
|
||||||
- [x] **USB device matching** — Class match on `IOUSBDevice` plus a user-space
|
- [x] **USB device matching**: Class match on `IOUSBDevice` plus a user-space
|
||||||
VID/PID filter on the `idVendor`/`idProduct` properties (numeric registry
|
VID/PID filter on the `idVendor`/`idProduct` properties (numeric registry
|
||||||
matching proved unreliable). Verified with a physical dongle (PID 0x02E6).
|
matching proved unreliable). Verified with a physical dongle (PID 0x02E6).
|
||||||
- [x] **Interface claiming** — All interfaces are opened and pipes mapped by
|
- [x] **Interface claiming**: All interfaces are opened and pipes mapped by
|
||||||
endpoint number + direction (EP 0x04 IN/OUT, EP 0x05 IN). Verified: opens
|
endpoint number + direction (EP 0x04 IN/OUT, EP 0x05 IN). Verified: opens
|
||||||
cleanly with no driver conflict.
|
cleanly with no driver conflict.
|
||||||
- [ ] **Async transfer latency** — `ReadPipeAsync` pump implemented (4
|
- [x] **Async transfer latency**: Resolved: the `ReadPipeAsync` pump (4
|
||||||
outstanding reads per IN pipe, resubmission in the completion handler).
|
outstanding reads per IN pipe, resubmission in the completion handler)
|
||||||
Latency tuning deferred to Phase 3 firmware load.
|
streams live controller input with no drops at game-report rates.
|
||||||
- [x] **Device reconnect handling** — `kIOTerminatedNotification` with a PID
|
- [x] **Device reconnect handling**: Resolved: `kIOTerminatedNotification`
|
||||||
filter fires on unplug and chip re-enumeration. Reliability pending hardware.
|
with a PID filter fires on unplug and chip re-enumeration. Verified
|
||||||
|
across firmware-load resets and repeated unplug/replug cycles.
|
||||||
|
|
||||||
### 2. Firmware Loading
|
### 2. Firmware Loading
|
||||||
|
|
||||||
- [ ] **Firmware binary availability** — Run `../xone/install/firmware.sh` (or
|
- [x] **Firmware binary availability**: Resolved: `scripts/download-firmware.sh`
|
||||||
ported `scripts/download-firmware.sh`) to confirm the Windows Update CAB URLs
|
(port of `../xone/install/firmware.sh`) fetches the Windows Update CAB
|
||||||
still work and firmware hashes match.
|
images and the hashes match the Linux driver. Images land in `firmware/`.
|
||||||
- [ ] **Firmware load sequence** — Trace the Linux `xone_mt76_load_firmware()`
|
- [x] **Firmware load sequence**: Resolved: ported to `src/mt76/mt76.cpp`
|
||||||
flow: control request to enter firmware mode → bulk transfer in 0x3800-byte
|
(control request to firmware mode, bulk transfer in 0x3800-byte chunks,
|
||||||
chunks → MCU completion poll → chip reset. Map each step to IOKit equivalents.
|
MCU completion poll, chip reset). Verified on hardware: FCE shows the
|
||||||
- [ ] **Post-firmware reconnect** — After firmware loads, the chip resets and
|
firmware running after load.
|
||||||
re-enumerates. Verify the USB device reappears with the same VID/PID and can
|
- [x] **Post-firmware reconnect**: Resolved: the chip re-enumerates with the
|
||||||
be re-opened.
|
same VID/PID; the app and CLI close, wait, and reopen the device. The
|
||||||
|
full sequence runs end-to-end.
|
||||||
|
|
||||||
### 3. MT76 Register Access
|
### 3. MT76 Register Access
|
||||||
|
|
||||||
- [ ] **Vendor request format** — The Linux driver uses `usb_control_msg()` with
|
- [x] **Vendor request format**: Resolved: the Linux values work unchanged via
|
||||||
vendor requests (bRequest 0x84/0x86 for register R/W). Confirm the exact
|
`IOUSBDeviceInterface->DeviceRequest()` (bRequest 0x84/0x86 for register
|
||||||
`bmRequestType`, `bRequest`, `wValue`, `wIndex`, `wLength` values work via
|
R/W). All radio init traffic uses this path.
|
||||||
`IOUSBDeviceInterface->DeviceRequest()`.
|
- [x] **Register timing**: Resolved: `usleep()` / `clock_nanosleep()` in
|
||||||
- [ ] **Register timing** — Some register writes require delays between them.
|
user-space are sufficient; radio init and channel evaluation complete
|
||||||
Test if `usleep()` in user-space provides sufficient precision, or if
|
reliably.
|
||||||
`clock_nanosleep()` is needed.
|
- [x] **EFUSE read**: Resolved: the EFUSE sequence returns valid MAC address,
|
||||||
- [ ] **EFUSE read** — Verify the EFUSE read sequence returns valid MAC address,
|
chip ID, and TX power calibration data on macOS (`xone_cli info`).
|
||||||
chip ID, and TX power calibration data on macOS.
|
|
||||||
|
|
||||||
### 4. 802.11 Frame Handling
|
### 4. 802.11 Frame Handling
|
||||||
|
|
||||||
- [ ] **Beacon construction** — The Linux driver builds raw 802.11 beacon frames
|
- [x] **Beacon construction**: Resolved: beacons with the Microsoft OUI
|
||||||
with a Microsoft OUI (00:50:f2) information element. Verify the frame format
|
(00:50:f2) information element transmit correctly, including the chip
|
||||||
matches what the MT76 chip expects (may include chip-specific headers before
|
header before the 802.11 frame. Controllers hear them and associate.
|
||||||
the 802.11 frame).
|
- [x] **Frame encapsulation**: Resolved: TX/RX chip headers handled per the
|
||||||
- [ ] **Frame encapsulation** — The MT76 chip wraps 802.11 frames in a
|
Linux driver (`send_wlan` / `process_wlan`). Association, pairing, and
|
||||||
proprietary header. Reverse-engineer or confirm the header format from Linux
|
encryption-enable management frames verified against hardware.
|
||||||
driver source (`xone_mt76_tx()` / `xone_mt76_rx()`).
|
- [ ] **QoS data frames**: RX path verified: controller input QoS frames are
|
||||||
- [ ] **QoS data frames** — Controller input/output uses 802.11 QoS data frames.
|
decrypted, fed to the GIP layer, and shown live in the app. Host to
|
||||||
Verify the frame construction and AES-CCMP encryption/decryption flow.
|
controller TX (rumble, LED over the data path) is not implemented yet.
|
||||||
|
|
||||||
### 5. AES-CCMP Encryption
|
### 5. AES-CCMP Encryption
|
||||||
|
|
||||||
- [x] **Crypto backend choice** — CommonCrypto (SHA-256/HMAC), Security.framework
|
- [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
|
(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`.
|
key agreement). Implemented in `src/auth/crypto.cpp`.
|
||||||
- [x] **CCMP mode implementation** — Not needed on the host: AES-CCMP runs on the
|
- [x] **CCMP mode implementation**: Not needed on the host: AES-CCMP runs on the
|
||||||
MT76 chip; the host only installs keys via WCID registers
|
MT76 chip; the host only installs keys via WCID registers
|
||||||
(`xone_mt76_set_client_key`).
|
(`xone_mt76_set_client_key`).
|
||||||
- [x] **ECDH key exchange** — P-256 implemented and validated against
|
- [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
|
### 6. Virtual HID Gamepad
|
||||||
|
|
||||||
- [ ] **HID Proxy Driver feasibility** — Research Apple's [HID Proxy
|
- [ ] **HID Proxy Driver feasibility**: Research Apple's [HID Proxy
|
||||||
Driver](https://developer.apple.com/documentation/coreaudio/hid_proxy_driver)
|
Driver](https://developer.apple.com/documentation/coreaudio/hid_proxy_driver)
|
||||||
(DriverKit). Can we create a virtual Xbox controller that games recognize
|
(DriverKit). Can we create a virtual Xbox controller that games recognize
|
||||||
natively?
|
natively?
|
||||||
- [ ] **IOHIDDevice user-space alternative** — Can we create a virtual HID
|
- [ ] **IOHIDDevice user-space alternative**: Can we create a virtual HID
|
||||||
device entirely in user-space? Test with `IOHIDManager` and see if games
|
device entirely in user-space? Test with `IOHIDManager` and see if games
|
||||||
(Steam, Game Center) recognize it.
|
(Steam, Game Center) recognize it.
|
||||||
- [ ] **HID report descriptor** — Write an Xbox 360/One-compatible HID report
|
- [ ] **HID report descriptor**: Write an Xbox 360/One-compatible HID report
|
||||||
descriptor. Test with existing Xbox controller (via Bluetooth) to capture the
|
descriptor. Test with existing Xbox controller (via Bluetooth) to capture the
|
||||||
exact report format macOS expects.
|
exact report format macOS expects.
|
||||||
- [ ] **Force feedback** — Can the virtual HID device receive rumble commands
|
- [ ] **Force feedback**: Can the virtual HID device receive rumble commands
|
||||||
from games and relay them to the controller via GIP?
|
from games and relay them to the controller via GIP?
|
||||||
|
|
||||||
### 7. Core Audio (Headset Support)
|
### 7. Core Audio (Headset Support)
|
||||||
|
|
||||||
- [ ] **Audio Unit setup** — Test creating an `AURenderCallback` /
|
- [ ] **Audio Unit setup**: Test creating an `AURenderCallback` /
|
||||||
`AUOutputUnit` for headset playback and `AURecordingCallback` / `AUInputUnit`
|
`AUOutputUnit` for headset playback and `AURecordingCallback` / `AUInputUnit`
|
||||||
for mic input.
|
for mic input.
|
||||||
- [ ] **Latency requirements** — The GIP protocol sends audio in 8ms intervals.
|
- [ ] **Latency requirements**: The GIP protocol sends audio in 8ms intervals.
|
||||||
Can Core Audio maintain this latency without glitches?
|
Can Core Audio maintain this latency without glitches?
|
||||||
- [ ] **Format negotiation** — The headset negotiates audio format (sample rate,
|
- [ ] **Format negotiation**: The headset negotiates audio format (sample rate,
|
||||||
channels) via GIP. Map GIP audio formats to Core Audio
|
channels) via GIP. Map GIP audio formats to Core Audio
|
||||||
`AudioStreamBasicDescription`.
|
`AudioStreamBasicDescription`.
|
||||||
|
|
||||||
### 8. Build System
|
### 8. Build System
|
||||||
|
|
||||||
- [ ] **CMake vs Xcode** — CMake is simpler for the C library portions, but
|
- [x] **CMake vs Xcode**: Resolved: CMake with the Ninja generator compiles
|
||||||
Xcode is needed for the macOS app bundle and any DriverKit extension. Decide
|
both the C++ stack and the Swift app (see Building). An Xcode project is
|
||||||
on primary build system.
|
only needed later for a DriverKit extension, if Phase 4 goes that route.
|
||||||
- [ ] **Minimum macOS version** — Target 12.0 (Monterey) for modern
|
- [ ] **Minimum macOS version**: Target 12.0 (Monterey) for modern
|
||||||
IOKit/DriverKit. Verify all APIs are available.
|
IOKit/DriverKit. Verify all APIs are available.
|
||||||
- [ ] **Code signing** — IOKit USB access may require specific entitlements
|
- [ ] **Code signing**: Development builds run unsigned on the local machine
|
||||||
(`com.apple.kpi.iokit`, `com.apple.security.device.usb`). DriverKit requires
|
with no entitlements needed for IOKit USB access. DriverKit
|
||||||
notarization. Plan for development vs distribution signing.
|
notarization and distribution signing deferred to the HID phase.
|
||||||
|
|
||||||
### 9. Regulatory / Legal
|
### 9. Regulatory / Legal
|
||||||
|
|
||||||
- [ ] **5GHz channel restrictions** — macOS enforces regulatory domain for 5GHz.
|
- [ ] **5GHz channel restrictions**: Channel evaluation picks a working
|
||||||
The dongle may try to use channels blocked in the current region. May need to
|
channel and pairing is verified on hardware; no regulatory failures seen
|
||||||
limit to 2.4GHz only or find a way to override.
|
so far. Regional edge cases (blocked channels) remain untested.
|
||||||
- [ ] **Firmware license** — The firmware binaries are from Microsoft Windows
|
- [ ] **Firmware license**: The firmware binaries are from Microsoft Windows
|
||||||
Update. Confirm they can be redistributed with the macOS port (the Linux
|
Update. Confirm they can be redistributed with the macOS port (the Linux
|
||||||
driver includes them with a disclaimer).
|
driver includes them with a disclaimer).
|
||||||
|
|
||||||
### 10. Testing Hardware
|
### 10. Testing Hardware
|
||||||
|
|
||||||
- [ ] **Dongle** — Xbox Wireless Dongle (PID 0x02FE preferred, 0x02E6 also
|
- [x] **Dongle**: Xbox Wireless Dongle (PID 0x02FE preferred, 0x02E6 also
|
||||||
works)
|
works). In hand: PID 0x02E6 verified end-to-end.
|
||||||
- [ ] **Controller** — Xbox One or Series X|S controller (for pairing and input
|
- [x] **Controller**: Xbox One or Series X|S controller (for pairing and input
|
||||||
testing)
|
testing). In hand: pairing and live input verified.
|
||||||
- [ ] **Headset** — Xbox Wireless Headset (optional, for audio testing)
|
- [ ] **Headset**: Xbox Wireless Headset (optional, for audio testing)
|
||||||
- [ ] **macOS machine** — Intel or Apple Silicon (test both if possible, IOKit
|
- [ ] **macOS machine**: Intel or Apple Silicon (test both if possible, IOKit
|
||||||
may differ)
|
may differ)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -166,7 +171,7 @@ Strike through or check off as each is resolved.
|
|||||||
| `usb_submit_urb()` | `ReadPipeAsync()` + `CFRunLoopSource` | ✅ Done (reader thread) |
|
| `usb_submit_urb()` | `ReadPipeAsync()` + `CFRunLoopSource` | ✅ Done (reader thread) |
|
||||||
| `kzalloc` / `kfree` | `malloc` / `free` | ✅ Straightforward |
|
| `kzalloc` / `kfree` | `malloc` / `free` | ✅ Straightforward |
|
||||||
| `spin_lock_irqsave` | `std::mutex` / `std::condition_variable` | ✅ Done (`usb_transport.cpp`) |
|
| `spin_lock_irqsave` | `std::mutex` / `std::condition_variable` | ✅ Done (`usb_transport.cpp`) |
|
||||||
| `msleep` / `mdelay` | `usleep()` / `clock_nanosleep()` | ☐ Test timing |
|
| `msleep` / `mdelay` | `usleep()` / `clock_nanosleep()` | ✅ Done (radio init, channel eval) |
|
||||||
| `crypto_shash_*` | CommonCrypto / Security.framework | ✅ Done (`auth/crypto.cpp`) |
|
| `crypto_shash_*` | CommonCrypto / Security.framework | ✅ Done (`auth/crypto.cpp`) |
|
||||||
| `input_register_device()` | HID Proxy Driver / IOHIDSystem | ☐ Investigate |
|
| `input_register_device()` | HID Proxy Driver / IOHIDSystem | ☐ Investigate |
|
||||||
| `snd_pcm_*` | Core Audio (Audio Units) | ☐ Investigate |
|
| `snd_pcm_*` | Core Audio (Audio Units) | ☐ Investigate |
|
||||||
@@ -177,14 +182,21 @@ Strike through or check off as each is resolved.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## First Steps on macOS
|
## Current Status
|
||||||
|
|
||||||
1. Plug in the dongle, run `system_profiler SPUSBDataType` — confirm it's
|
Working end-to-end on hardware:
|
||||||
detected
|
|
||||||
2. Check `log show --predicate 'subsystem == "com.apple.iokit"'` — see if macOS
|
- Dongle probe/open, async read pump, vendor register R/W
|
||||||
loads any driver
|
- Firmware download (`scripts/download-firmware.sh`) and load
|
||||||
3. Open the device with `transport::probe()` and confirm endpoint enumeration
|
- EFUSE read, radio init, channel evaluation, beacon TX
|
||||||
(EP 0x04 IN/OUT, EP 0x05 IN)
|
- Pairing mode, controller association, GIP handshake + auth
|
||||||
4. Try a vendor control request (register read) to verify USB communication
|
- Live controller input (buttons, sticks, triggers) in the Swift app
|
||||||
works
|
|
||||||
5. Attempt firmware load with the binary from `firmware/` directory
|
Not done yet: host to controller TX over the data path (rumble, LED),
|
||||||
|
virtual HID gamepad (Phase 4), headset audio (Phase 5).
|
||||||
|
|
||||||
|
`xone_cli` exercises the stack manually in a single dongle session:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./build/xone_cli info firmware firmware/xow_dongle.bin radio-init pair
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user