From 0812deac9357d8a0d71f3ed948fd206c3d3330a6 Mon Sep 17 00:00:00 2001 From: Amar Suchak Date: Mon, 27 Jul 2026 12:50:11 -0700 Subject: [PATCH] feat: support Xbox Elite Series 2 over wired USB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Elite Series 2 (045e:0b00) speaks the same 18-byte GIP input frame over USB as the already-supported Xbox One S (045e:02ea), but its PID was not in XBOX_GIP_PIDS, so it fell through to the generic driver and produced no input events at all. Adding the PID is sufficient — parseXboxGipReport needed no changes. Verified against a live pad: doctor reports 17/17 buttons and full range on all six axes. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 6 + CONTROLLERS.md | 1 + src/controller/xbox-driver.ts | 5 +- test/fixtures/controllers/045e-0b00-usb.json | 159 +++++++++++++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/controllers/045e-0b00-usb.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5693486..e474da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] + +### Added + +- **Xbox Elite Series 2 wired support.** The pad (`045e:0b00`) speaks the same 18-byte GIP input frame over USB as the already-supported Xbox One S, but its PID was unlisted, so it fell through to the `generic` driver and produced no input. Adding it to `XBOX_GIP_PIDS` is sufficient; no parser changes were needed. + ## [1.4.0] - 2026-07-23 ### Added diff --git a/CONTROLLERS.md b/CONTROLLERS.md index 8bbfd2b..e5acc71 100644 --- a/CONTROLLERS.md +++ b/CONTROLLERS.md @@ -12,6 +12,7 @@ Every controller below has a committed `openmicro doctor` report. CI replays cap | ------------------------------- | ------------------------------- | --------- | ---------- | --------- | -------------- | ------------- | ------- | | GameSir Cyclone 2 (DS4 mode) | DUALSHOCK 4 Wireless Controller | 054c:05c4 | bluetooth | ds4 | 17/17 | none | ✅ full | | GameSir G7 Pro | GameSir-G7 Pro | 3537:1022 | bluetooth | gamesir | 17/17 | none | ✅ full | +| Microsoft Xbox Elite Series 2 | Controller | 045e:0b00 | usb | xbox | 17/17 | none | ✅ full | | Microsoft Xbox One S Controller | Controller | 045e:02ea | usb | xbox | 17/17 | none | ✅ full | | Microsoft Xbox One S Controller | Xbox Wireless Controller | 045e:0b20 | bluetooth | xbox | 17/17 | none | ✅ full | | Sony DualSense | DualSense Wireless Controller | 054c:0ce6 | usb | dualsense | 17/17 | lightbar+LEDs | ✅ full | diff --git a/src/controller/xbox-driver.ts b/src/controller/xbox-driver.ts index c6b2d82..959c768 100644 --- a/src/controller/xbox-driver.ts +++ b/src/controller/xbox-driver.ts @@ -6,8 +6,9 @@ import type { ButtonId, ControllerEvent } from '../types.js' export const XBOX_VID = 0x045e export const XBOX_PIDS = [0x0b12, 0x0b13, 0x02fd, 0x02e0] // Wired pads that speak GIP over USB (macOS exposes them via HID with the raw -// GIP input frame). Verified against a live Xbox One S (0x02ea) capture. -export const XBOX_GIP_PIDS = [0x02ea] +// GIP input frame). Verified against live Xbox One S (0x02ea) and Xbox Elite +// Series 2 (0x0b00) captures. +export const XBOX_GIP_PIDS = [0x02ea, 0x0b00] // Bluetooth pads using the standard Xbox BT HID report (report ID 0x01). // Verified against a live Xbox Wireless Controller (0x0b20) capture. export const XBOX_BT_PIDS = [0x0b20] diff --git a/test/fixtures/controllers/045e-0b00-usb.json b/test/fixtures/controllers/045e-0b00-usb.json new file mode 100644 index 0000000..3a92e4a --- /dev/null +++ b/test/fixtures/controllers/045e-0b00-usb.json @@ -0,0 +1,159 @@ +{ + "schemaVersion": 1, + "openmicroVersion": "1.4.0", + "platform": "darwin", + "osVersion": "Darwin Kernel Version 25.5.0: Mon Apr 27 20:41:15 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T6041", + "controller": { + "vid": "0x045e", + "pid": "0x0b00", + "product": "Controller", + "makeModel": "Microsoft Xbox Elite Series 2", + "transport": "usb", + "driver": "xbox" + }, + "results": { + "south": { + "status": "pass" + }, + "east": { + "status": "pass", + "capture": { + "pressed": "2000e62f20000000000036000e07d0041904", + "idle": "2000da2f1000000000004d040e07d0041904" + } + }, + "west": { + "status": "pass", + "capture": { + "pressed": "2000eb2f40000000000096000e07d0041904", + "idle": "2000e62f20000000000036000e07d0041904" + } + }, + "north": { + "status": "pass", + "capture": { + "pressed": "2000ed2f80000000000096000e07d0041904", + "idle": "2000eb2f40000000000096000e07d0041904" + } + }, + "dpad_up": { + "status": "pass", + "capture": { + "pressed": "2000fc2f0001000000008dfc0e0778038e02", + "idle": "2000ed2f80000000000096000e07d0041904" + } + }, + "dpad_down": { + "status": "pass", + "capture": { + "pressed": "2000fe2f0002000000008dfc0e0778038e02", + "idle": "2000fc2f0001000000008dfc0e0778038e02" + } + }, + "dpad_left": { + "status": "pass", + "capture": { + "pressed": "2000012f0004000000008dfc0e0778038e02", + "idle": "2000fe2f0002000000008dfc0e0778038e02" + } + }, + "dpad_right": { + "status": "pass", + "capture": { + "pressed": "2000032f0008000000008dfc0e0778038e02", + "idle": "2000012f0004000000008dfc0e0778038e02" + } + }, + "l1": { + "status": "pass", + "capture": { + "pressed": "2000052f0010000000008dfc0e0778038e02", + "idle": "2000032f0008000000008dfc0e0778038e02" + } + }, + "r1": { + "status": "pass", + "capture": { + "pressed": "2000092f0020000000008dfc0e0778038e02", + "idle": "2000052f0010000000008dfc0e0778038e02" + } + }, + "l2": { + "status": "pass", + "capture": { + "pressed": "20000c2f0000100300008dfc0e0778038e02", + "idle": "2000092f0020000000008dfc0e0778038e02" + } + }, + "r2": { + "status": "pass", + "capture": { + "pressed": "2000102f0000000017018dfc0e0778038e02", + "idle": "20000c2f0000100300008dfc0e0778038e02" + } + }, + "l3": { + "status": "pass", + "capture": { + "pressed": "2000252f00400000000042fdbbf442047dfb", + "idle": "2000102f0000000017018dfc0e0778038e02" + } + }, + "r3": { + "status": "pass", + "capture": { + "pressed": "20003d2f0080000000001d0676f6a504c702", + "idle": "2000252f00400000000042fdbbf442047dfb" + } + }, + "menu": { + "status": "pass", + "capture": { + "pressed": "2000742f0400000000000f0584f796018608", + "idle": "20003d2f0080000000001d0676f6a504c702" + } + }, + "view": { + "status": "pass", + "capture": { + "pressed": "2000852f0800000000000f05a1f71202e500", + "idle": "2000742f0400000000000f0584f796018608" + } + }, + "touchpad": { + "status": "pass", + "capture": { + "pressed": "07304b02015b", + "idle": "2000852f0800000000000f05a1f71202e500" + } + } + }, + "axes": { + "left_x": { + "min": -1, + "max": 1 + }, + "left_y": { + "min": -1, + "max": 1 + }, + "right_x": { + "min": -1, + "max": 1 + }, + "right_y": { + "min": -1, + "max": 1 + }, + "l2": { + "min": 0, + "max": 1 + }, + "r2": { + "min": 0, + "max": 1 + } + }, + "output": "unsupported", + "timestamp": "2026-07-27T19:49:41.053Z" +}