Initial commit
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# Xbox Wireless Dongle — macOS Port
|
||||
|
||||
## Goal
|
||||
|
||||
User-space macOS app that speaks to the Xbox Wireless Dongle (MT76xx chip) and exposes connected controllers as HID gamepads.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ macOS App (Swift/C++) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
|
||||
│ │ App/UI │ │ HID Proxy│ │ Core Audio (opt) │ │
|
||||
│ └────┬─────┘ └────▲─────┘ └──────────────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌────▼──────────────▼──────────────────────┐ │
|
||||
│ │ GIP Protocol Layer │ │
|
||||
│ │ (bus/protocol.c — ported, pure C) │ │
|
||||
│ └──────────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼───────────────────────┐ │
|
||||
│ │ MT76 Chip Protocol Layer │ │
|
||||
│ │ (transport/mt76.c — ported, USB calls) │ │
|
||||
│ └──────────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼───────────────────────┐ │
|
||||
│ │ USB Transport Layer (new) │ │
|
||||
│ │ (IOKit / IOUSBInterfaceInterface) │ │
|
||||
│ └──────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼───────────────────────┐ │
|
||||
│ │ Xbox Wireless Dongle │ │
|
||||
│ │ VID:045E PID:02FE (02E6, 02F9, 091E) │ │
|
||||
│ └──────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
macos/
|
||||
├── PLAN.md ← this file
|
||||
├── src/
|
||||
│ ├── usb/ ← IOKit USB transport (new)
|
||||
│ ├── mt76/ ← MT76 chip protocol (port from transport/mt76.c)
|
||||
│ ├── gip/ ← GIP protocol (port from bus/protocol.c)
|
||||
│ ├── auth/ ← Auth + crypto (port from auth/)
|
||||
│ ├── hid/ ← Virtual HID gamepad (new)
|
||||
│ └── app/ ← macOS app entry point + UI (new)
|
||||
├── include/
|
||||
│ ├── common/ ← Shared types, platform abstraction
|
||||
│ ├── usb/
|
||||
│ ├── mt76/
|
||||
│ ├── gip/
|
||||
│ ├── auth/
|
||||
│ └── hid/
|
||||
├── scripts/
|
||||
│ └── download-firmware.sh ← Port of install/firmware.sh
|
||||
├── firmware/ ← Downloaded firmware binaries (gitignored)
|
||||
└── build/ ← Build output (gitignored)
|
||||
```
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1 — Foundation (Linux → C library)
|
||||
|
||||
Extract the protocol logic from Linux kernel code into standalone C.
|
||||
|
||||
**src/gip/**
|
||||
|
||||
- Port `bus/protocol.c` → pure C, no kernel deps
|
||||
- Redefine `__le16`, `__packed`, `guid_t` for user-space
|
||||
- Replace `kzalloc`/`kfree` → `malloc`/`free`
|
||||
- Replace `spin_lock_irqsave` → pthread mutex or lock-free ring buffer
|
||||
- Replace `dev_dbg`/`gip_err` → NSLog or custom logger
|
||||
- Replace `EXPORT_SYMBOL_GPL` → nothing (static library)
|
||||
|
||||
**src/auth/**
|
||||
|
||||
- Port `auth/auth.c` + `auth/crypto.c` → AES-CCMP, authentication handshake
|
||||
- Should be mostly copy-paste, these are pure crypto
|
||||
|
||||
**include/common/**
|
||||
|
||||
- Platform types (`stdint.h` based)
|
||||
- Endianness helpers
|
||||
- Memory allocator abstraction (so we can swap malloc if needed)
|
||||
|
||||
### Phase 2 — USB Transport (new)
|
||||
|
||||
**src/usb/**
|
||||
|
||||
- IOKit device matching on `USB\VID_045E&PID_02FE` (and other PIDs)
|
||||
- Open device, claim interface (bInterfaceNumber = 1)
|
||||
- `IOUSBDevInterface` for control transfers (vendor requests)
|
||||
- `IOUSBInterfaceInterface` for bulk endpoints:
|
||||
- EP 0x04 IN — WLAN data (802.11 frames from controllers)
|
||||
- EP 0x04 OUT — Bulk out (commands to chip)
|
||||
- EP 0x05 IN — MCU commands (firmware load responses)
|
||||
- Async completion callbacks → callback/dispatch queue
|
||||
- Device disconnect handling (chip reconnects during firmware load)
|
||||
|
||||
**Key IOKit APIs:**
|
||||
|
||||
- `IOServiceMatching("IOUSBDevice")` — find dongle
|
||||
- `IOUSBDeviceOpen` / `IOUSBInterfaceOpen` — claim
|
||||
- `DeviceRequest` — control transfers (register R/W)
|
||||
- `WritePipe` / `ReadPipe` — bulk transfers
|
||||
- `CreateInterruptEndpoint` — for EP 0x05 (MCU)
|
||||
|
||||
### Phase 3 — MT76 Chip Protocol (port)
|
||||
|
||||
**src/mt76/**
|
||||
|
||||
- Port `transport/mt76.c` → replace USB calls with Phase 2 transport
|
||||
- **Firmware loading:** send binary in 0x3800-byte chunks, poll for completion
|
||||
- **EFUSE read:** MAC address, chip ID, crystal trim, TX power calibration
|
||||
- **Radio init:** ~80 hardcoded register writes (AGC, EDCA, TX power, protection)
|
||||
- **Channel evaluation:** cycle through 12 channels, pick highest power
|
||||
- **Beacon transmission:** 802.11 beacons with Microsoft OUI IE
|
||||
- **Pairing mode:** rotate channels every 2s when pairing enabled
|
||||
- **Controller association:** 802.11 assoc, WCID assignment, AES-CCMP encryption
|
||||
- **Data path:** QoS data frames to/from controllers
|
||||
|
||||
**Key replacement map:**
|
||||
|
||||
- `usb_control_msg()` → `IOUSBDevInterface->DeviceRequest()`
|
||||
- `usb_bulk_msg()` → `IOUSBInterfaceInterface->WritePipe()` / `ReadPipe()`
|
||||
- `kzalloc`/`skb_put` → `malloc`/buffer management
|
||||
- `msleep`/`mdelay` → `usleep`/`nanosleep`
|
||||
- `ieee80211_*` → custom 802.11 frame builders
|
||||
- `cfg80211_*` → nothing (no regulatory domain reporting needed)
|
||||
|
||||
### Phase 4 — Virtual HID Gamepad (new)
|
||||
|
||||
**src/hid/**
|
||||
|
||||
- Expose controller as macOS HID device so games work natively
|
||||
- Options:
|
||||
- **HID Proxy Driver (DriverKit)** — Maps USB device to virtual HID. Minimal kernel code. Preferred approach.
|
||||
- **IOHIDSystem user-space** — Create virtual HID device entirely in user-space. May not work for all games.
|
||||
- **Gamepad wrapper** — Lower-level, translate input events to HID reports.
|
||||
- Map Xbox controller buttons/sticks/triggers to standard Xbox 360/One HID report descriptor
|
||||
- Handle force feedback (rumble) — send back to dongle via GIP
|
||||
- Battery status reporting
|
||||
|
||||
### Phase 5 — macOS App (new)
|
||||
|
||||
**src/app/**
|
||||
|
||||
- Swift or C++ main app
|
||||
- Device discovery (IOKit notification when dongle plugged in)
|
||||
- Pairing UI (LED blink, controller button press)
|
||||
- Status display (connected controllers, battery)
|
||||
- Preferences (channel selection, LED mode)
|
||||
- Menubar icon for status
|
||||
- Firmware download helper (script or in-app)
|
||||
|
||||
## Dongle Initialization Sequence
|
||||
|
||||
```
|
||||
1. Detect dongle via IOKit
|
||||
2. Download/verify firmware (firmware.sh)
|
||||
3. Load firmware into chip (bulk transfer, 0x3800 byte chunks)
|
||||
4. Chip disconnects + reconnects (handle this!)
|
||||
5. Read EFUSE (MAC, chip ID, TX power cal)
|
||||
6. Initialize radio (~80 register writes)
|
||||
7. Evaluate channels (find best channel)
|
||||
8. Start beacon transmission
|
||||
9. Enter pairing mode (channel rotation)
|
||||
10. When controller associates:
|
||||
a. Parse 802.11 assoc request
|
||||
b. Assign WCID (client ID)
|
||||
c. Set up AES-CCMP encryption
|
||||
d. Send assoc response
|
||||
11. GIP handshake:
|
||||
a. Controller ANNOUNCE (vendor/product/fw version)
|
||||
b. Host sends IDENTIFY request
|
||||
c. Controller sends IDENTIFY (capabilities, interfaces, HID descriptor)
|
||||
d. Auth handshake (AES-CCMP key exchange)
|
||||
e. Status reports begin (battery, connected)
|
||||
12. Game loop:
|
||||
a. Controller → HID reports (input)
|
||||
b. Host → rumble, LED, audio control
|
||||
```
|
||||
|
||||
## Firmware
|
||||
|
||||
Firmware binaries are downloaded from Microsoft Windows Update driver catalog:
|
||||
|
||||
| PID | Dongle Type | Firmware File |
|
||||
| ---- | ---------------------- | -------------------- |
|
||||
| 02E6 | Old dongle | xone_dongle_02e6.bin |
|
||||
| 02FE | New dongle | xone_dongle_02fe.bin |
|
||||
| 02F9 | Built-in (ASUS/Lenovo) | xone_dongle_02f9.bin |
|
||||
| 091E | Surface Book 2 | xone_dongle_091e.bin |
|
||||
|
||||
Downloaded via `scripts/download-firmware.sh` (port of `install/firmware.sh`).
|
||||
|
||||
## Known Challenges
|
||||
|
||||
1. **USB timing** — MT76 is timing-sensitive. User-space USB on macOS may have different latency than Linux kernel URBs. May need careful tuning of `usleep` values.
|
||||
|
||||
2. **Chip reconnect** — During firmware load, the dongle disconnects and reconnects. IOKit needs to handle this gracefully (close, wait, reopen, re-claim).
|
||||
|
||||
3. **Virtual HID** — Games expect a real HID device. HID Proxy Driver (DriverKit) is the cleanest path but requires a minimal kernel extension.
|
||||
|
||||
4. **5GHz regulatory** — The dongle uses 5GHz channels. macOS may have regulatory restrictions. May need to limit to 2.4GHz only.
|
||||
|
||||
5. **Audio** — Headset audio is complex (real-time PCM streaming). Lower priority, tackle after gamepad works.
|
||||
|
||||
## Source Files to Port
|
||||
|
||||
| Linux source | Target | Notes |
|
||||
| ----------------------- | -------------------------- | ---------------------------------- |
|
||||
| `transport/mt76.c` | `src/mt76/mt76.c` | Replace USB calls, remove cfg80211 |
|
||||
| `transport/mt76.h` | `include/mt76/mt76.h` | Clean up kernel types |
|
||||
| `transport/mt76_defs.h` | `include/mt76/mt76_defs.h` | Mostly copy (register defs) |
|
||||
| `bus/protocol.c` | `src/gip/protocol.c` | Replace kernel alloc/lock/debug |
|
||||
| `bus/protocol.h` | `include/gip/protocol.h` | Clean up kernel types |
|
||||
| `bus/bus.c` | `src/gip/bus.c` | Client lifecycle management |
|
||||
| `auth/auth.c` | `src/auth/auth.c` | Pure crypto, mostly copy |
|
||||
| `auth/auth.h` | `include/auth/auth.h` | Copy |
|
||||
| `auth/crypto.c` | `src/auth/crypto.c` | AES-CCMP, mostly copy |
|
||||
| `auth/crypto.h` | `include/auth/crypto.h` | Copy |
|
||||
| `driver/gamepad.c` | N/A | Replaced by HID layer |
|
||||
|
||||
## Build System
|
||||
|
||||
- **CMake** or **Xcode project** — either works
|
||||
- Static library for protocol/auth layers
|
||||
- macOS app bundle for the final product
|
||||
- Minimum macOS: 12.0 (Monterey) — for modern IOKit/DriverKit support
|
||||
|
||||
## Dependencies
|
||||
|
||||
- IOKit (system)
|
||||
- CoreFoundation (system)
|
||||
- HID Proxy Driver framework (optional, for virtual gamepad)
|
||||
- Core Audio (optional, for headset support)
|
||||
- No external dependencies for protocol/auth layers
|
||||
@@ -0,0 +1,109 @@
|
||||
# Xbox Wireless Dongle — macOS Port
|
||||
|
||||
User-space macOS app for the Xbox Wireless Dongle (MediaTek MT76xx).
|
||||
Ports the Linux kernel driver [`xone/`](../xone/) to macOS.
|
||||
|
||||
See [PLAN.md](PLAN.md) for full architecture and implementation phases.
|
||||
|
||||
---
|
||||
|
||||
## Investigation Checklist
|
||||
|
||||
These items need research on macOS before implementation can begin.
|
||||
Strike through or check off as each is resolved.
|
||||
|
||||
### 1. IOKit USB Access
|
||||
|
||||
- [ ] **IOUSBLib vs IOUSBFamily** — macOS deprecated `IOUSBLib` (UserClient-based) in favor of `IOUSBFamily` direct interfaces. Determine which API set is available on target macOS version (12.0+).
|
||||
- [ ] **USB device matching** — Verify `IOServiceMatching("IOUSBDevice")` with `kUSBVendorString`/`kUSBProductString` keys works for PID `0x02FE`. Test with actual dongle plugged in.
|
||||
- [ ] **Interface claiming** — The dongle uses interface 1 (WLAN). Confirm `IOUSBInterfaceOpen()` succeeds without conflicting with any built-in macOS driver. Check if macOS auto-loads any driver for this VID/PID combo.
|
||||
- [ ] **Async transfer latency** — The MT76 chip is timing-sensitive. Measure `ReadPipeAsync`/`WritePipe` latency vs Linux `usb_bulk_msg`. May need to tune `usleep` values in firmware loading and register polling.
|
||||
- [ ] **Device reconnect handling** — During firmware load, the dongle disconnects and reconnects. Test that `IOService` notification callbacks fire correctly and that re-opening the device works reliably.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 5. AES-CCMP Encryption
|
||||
|
||||
- [ ] **Crypto backend choice** — Decide between:
|
||||
- **CommonCrypto** (system, zero deps) — `CCryptorCreate()` for AES-CTR/CBC
|
||||
- **Security.framework** (system) — `SecKeyRef` for ECDH key exchange
|
||||
- **libcrypto/OpenSSL** (Homebrew) — `EVP_*` APIs, more familiar but external dep
|
||||
- [ ] **CCMP mode implementation** — AES-CCMP = AES-CTR encryption + AES-CBC-MAC authentication. Neither CommonCrypto nor OpenSSL has a direct CCMP API. Need to implement the mode manually (encrypt then MIC, or verify MIC then decrypt).
|
||||
- [ ] **ECDH key exchange** — The authentication handshake uses ECDH (P-256 curve). Test `SecKeyCreateWithData()` + `SecKeyCopyKeyExchangeResult()` on macOS for key agreement.
|
||||
|
||||
### 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?
|
||||
|
||||
### 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`.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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).
|
||||
|
||||
### 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)
|
||||
- [ ] **Headset** — Xbox Wireless Headset (optional, for audio testing)
|
||||
- [ ] **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()` | ☐ Investigate |
|
||||
| `usb_bulk_msg()` | `IOUSBInterfaceInterface->WritePipe()` / `ReadPipe()` | ☐ Investigate |
|
||||
| `usb_submit_urb()` | `ReadPipeAsync()` + `CFRunLoopSource` | ☐ Investigate |
|
||||
| `kzalloc` / `kfree` | `malloc` / `free` | ✅ Straightforward |
|
||||
| `spin_lock_irqsave` | `pthread_mutex_t` or lock-free | ☐ Design |
|
||||
| `msleep` / `mdelay` | `usleep()` / `clock_nanosleep()` | ☐ Test timing |
|
||||
| `crypto_shash_*` | CommonCrypto / Security.framework | ☐ Choose backend |
|
||||
| `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. Write a minimal IOKit test program to open the device and read its descriptors
|
||||
4. Try a vendor control request (register read) to verify USB communication works
|
||||
5. Attempt firmware load with the binary from `firmware/` directory
|
||||
Reference in New Issue
Block a user