Files
xone_macos/PLAN.md
T
2026-08-04 17:12:54 +02:00

242 lines
11 KiB
Markdown

# 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