Files
xone_macos/PLAN.md
T
portersky 95e958a7e8 docs: add AGENTS.md coding conventions
Adapt the refix AGENTS.md to this project: build commands, module
layout, C++/Swift/C-bridge style rules, and commit guidelines. C++
headers use .hpp and C headers use .h; the PLAN.md port table is
updated to match.

Co-Authored-By: qwen (qwen/qwen3.8-27b@q2_k_xl): wrote coding conventions
2026-08-17 14:18:06 +02:00

11 KiB

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/kfreemalloc/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_putmalloc/buffer management
  • msleep/mdelayusleep/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.hpp Clean up kernel types
transport/mt76_defs.h include/mt76/mt76_defs.hpp Mostly copy (register defs)
bus/protocol.c src/gip/protocol.c Replace kernel alloc/lock/debug
bus/protocol.h include/gip/protocol.hpp 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.hpp Copy
auth/crypto.c src/auth/crypto.cpp AES-CCMP, mostly copy
auth/crypto.h include/auth/crypto.hpp 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