Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions captures/glorious-o2-pro-4k8k-wired/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Glorious Model O 2 PRO 4K/8K, wired

Reference material for `src/glorious-core2/index.ts` and
`src/drivers/glorious/core2-hid.ts`. From a community USBPcap capture
(ticket-0160, Windows, 2026-10-08): Glorious CORE talking to a Model O 2 PRO
4K/8K on its cable (USB `0x258a:0x201b`) while the owner stepped through its
settings. Only the mouse's configuration interface and its DPI button reports
are included. The other USB devices on that bus (two ASUS lighting controllers)
and all pointer traffic were left out. No serial numbers or personal data.

| File | Trust | What it is |
|-|-|-|
| `core-session.hex` | Vendor capture | Every frame on interface 2 (usage page `0xffff`, usage 0, unnumbered 64-byte feature reports): 116 requests (22 distinct) and the 41 replies CORE read. `src/drivers/glorious/core2-protocol.test.ts` re-encodes all 116 requests byte for byte. |
| `dpi-button-reports.hex` | Vendor capture | 11 input reports (report 4) on interface 1, sent by the mouse when its DPI button was pressed in the first 41 s, before CORE wrote anything. Layout `01, stage (1-based), DPI X (u16 BE), DPI Y (u16 BE), 00 00`. The test checks every one against the stage table CORE wrote later. |

## What the owner did

The capture has no labels, so this is read off the frames. CORE re-sends its
whole performance block after every change (7 requests, 30 ms apart), so only
the field that moved says what the owner touched:

| Seconds | What changed |
|-|-|
| 5.8 | CORE starts: firmware read. Battery read every 10 s from here, 100 % and not charging. |
| 14 to 41 | DPI button pressed on the mouse, stages 1 to 4 cycle. |
| 57 to 114 | Polling rate stepped 125, 250, 500, 1000, 2000, 4000, 8000 Hz (`08, 04, 02, 01, 20, 40, 80` in both polling bytes). |
| 120 | Polling bytes become `80 40`: 8000 Hz wired, 4000 Hz wireless. |
| 124 to 133 | Motion sync off, then on again (register `0x09` goes 1, 0, 1). |
| 141 to 160 | Debounce stepped 4, 8, 12, 16 ms (register `0x08`, first byte after the profile). |
| 168 | Advanced debounce switched on: `00 00 0a 0a 08` (CORE's defaults 0, 0, 10, 10, 8). |

Every request carries profile `2`: CORE had its second profile selected. A
capture from a Model D2 Pro 4K in the GloriousAutoPollingRate project carries
profile `1`.

## What the registers are

The 7 requests of a burst, with what each one is. The frame layout is in the
header comment of `src/glorious-core2/index.ts`. The names come from the code of
Glorious CORE 2.1.21 itself (`MouseV2ProDeviceHandler`, which also drives the
Model O/D Wireless that korkje/mxw documents); the capture fixes the bytes, the
code fixes the meaning.

| Order | Bank, register | Data after the profile byte |
|-|-|-|
| 1 | 1, `0x01` | DPI stage count, then X and Y as u16 BE per stage |
| 2 | 2, `0x01` | six RGB triplets, the stage LED colors |
| 3 | 1, `0x0b` | lift-off value; CORE writes 1 for both its 1.0 mm and 2.0 mm options, so the value to distance map is unknown |
| 4 | 1, `0x02` | active DPI stage, 1-based |
| 5 | 1, `0x0a` | polling code for the cable, polling code for 2.4 GHz |
| 6 | 0, `0x08` | debounce: `ms, 0, 0, 0, 0, 0`, or in advanced mode `beforePress, beforeRelease, afterPress, afterRelease, liftOffPress, 0` |
| 7 | 1, `0x09` | motion sync, 1 on, 0 off |

Reads: bank 0 register `0x81` answers firmware `1.0.15.0` then the wired
product id `0x201b`; register `0x83` answers charging flag and percent. The
reply to a write is the request echoed with status `0xa1`, and CORE only reads
it after the last frame of a burst.

## Not captured yet

- **Any read of a setting.** CORE never reads one, and no command for it is
known, so the driver shows the last values it wrote.
- **The HID report descriptors.** The capture started after enumeration. The
driver finds the config channel by shape (a feature report with id 0 on usage
page `0xffff`), which is how CORE's own device table describes it.
- **The receiver (`0x2035`).** No traffic. The frames are the same ones CORE
sends over either link (GloriousAutoPollingRate captured the D2 Pro 4K
receiver doing so); only the firmware read differs, target byte 0 instead
of 2, from CORE's code.
- **The profile select frame** (bank 0, register `0x05`), which CORE and mxw
agree on. CORE had already selected profile 2.
- **Lighting, buttons, macros and auto sleep.** Not exercised.
- **8000 Hz on the 2.4 GHz link.** CORE wrote it once (`80 80`) and then
settled on `80 40`; the product page gives 4000 Hz as the wireless maximum.
163 changes: 163 additions & 0 deletions captures/glorious-o2-pro-4k8k-wired/core-session.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Glorious Model O 2 PRO 4K/8K, wired (USB 0x258a:0x201b), ticket-0160.
# Glorious CORE on Windows, USBPcap, 2026-10-08. Config interface only: interface 2,
# usage page 0xffff usage 0, unnumbered 64-byte feature reports (report id 0). Format:
# <seconds since the first packet of this device> <dir> <hex>, where > is SET_REPORT (host to
# device) and < is the GET_REPORT reply, full 64-byte frames with trailing zeros dropped.
# CORE has no label per action; what the owner did is inferred in README.md.
5.811 > 00 00 02 03 00 81
5.868 < a1 00 02 06 00 81 01 00 0f 00 20 1b
15.822 > 00 00 02 02 00 83
15.884 < a1 00 02 02 00 83 00 64
25.829 > 00 00 02 02 00 83
25.891 < a1 00 02 02 00 83 00 64
35.839 > 00 00 02 02 00 83
35.901 < a1 00 02 02 00 83 00 64
45.838 > 00 00 02 02 00 83
45.899 < a1 00 02 02 00 83 00 64
55.846 > 00 00 02 02 00 83
55.907 < a1 00 02 02 00 83 00 64
57.331 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
57.378 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
57.424 > 00 00 02 02 01 0b 02 01
57.471 > 00 00 02 02 01 02 02 01
57.597 > 00 00 02 03 01 0a 02 08 08
57.643 > 00 00 02 07 00 08 02
57.646 > 00 00 02 02 01 09 02 01
57.705 < a1 00 02 02 01 09 02 01
57.708 < a1 00 02 02 01 09 02 01
65.860 > 00 00 02 02 00 83
65.922 < a1 00 02 02 00 83 00 64
69.450 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
69.490 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
69.537 > 00 00 02 02 01 0b 02 01
69.584 > 00 00 02 02 01 02 02 01
69.707 > 00 00 02 03 01 0a 02 04 04
69.755 > 00 00 02 07 00 08 02
69.758 > 00 00 02 02 01 09 02 01
69.818 < a1 00 02 02 01 09 02 01
69.821 < a1 00 02 02 01 09 02 01
78.708 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
78.742 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
78.774 > 00 00 02 02 01 0b 02 01
78.820 > 00 00 02 02 01 02 02 01
78.946 > 00 00 02 03 01 0a 02 02 02
78.992 > 00 00 02 07 00 08 02
78.995 > 00 00 02 02 01 09 02 01
79.055 < a1 00 02 02 01 09 02 01
79.058 < a1 00 02 02 01 09 02 01
89.420 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
89.454 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
89.486 > 00 00 02 02 01 0b 02 01
89.532 > 00 00 02 02 01 02 02 01
89.658 > 00 00 02 03 01 0a 02 01 01
89.705 > 00 00 02 07 00 08 02
89.708 > 00 00 02 02 01 09 02 01
89.769 < a1 00 02 02 01 09 02 01
89.772 < a1 00 02 02 01 09 02 01
95.985 > 00 00 02 02 00 83
96.046 < a1 00 02 02 00 83 00 64
97.386 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
97.432 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
97.477 > 00 00 02 02 01 0b 02 01
97.524 > 00 00 02 02 01 02 02 01
97.647 > 00 00 02 03 01 0a 02 20 20
97.694 > 00 00 02 07 00 08 02
97.697 > 00 00 02 02 01 09 02 01
97.756 < a1 00 02 02 01 09 02 01
97.759 < a1 00 02 02 01 09 02 01
104.919 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
104.963 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
105.009 > 00 00 02 02 01 0b 02 01
105.041 > 00 00 02 02 01 02 02 01
105.166 > 00 00 02 03 01 0a 02 40 40
105.213 > 00 00 02 07 00 08 02
105.216 > 00 00 02 02 01 09 02 01
105.275 < a1 00 02 02 01 09 02 01
105.278 < a1 00 02 02 01 09 02 01
114.102 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
114.144 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
114.191 > 00 00 02 02 01 0b 02 01
114.237 > 00 00 02 02 01 02 02 01
114.374 > 00 00 02 03 01 0a 02 80 80
114.421 > 00 00 02 07 00 08 02
114.424 > 00 00 02 02 01 09 02 01
114.484 < a1 00 02 02 01 09 02 01
114.487 < a1 00 02 02 01 09 02 01
120.129 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
120.174 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
120.221 > 00 00 02 02 01 0b 02 01
120.269 > 00 00 02 02 01 02 02 01
120.397 > 00 00 02 03 01 0a 02 80 40
120.444 > 00 00 02 07 00 08 02
120.447 > 00 00 02 02 01 09 02 01
120.506 < a1 00 02 02 01 09 02 01
120.509 < a1 00 02 02 01 09 02 01
123.562 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
123.597 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
123.644 > 00 00 02 02 01 0b 02 01
123.691 > 00 00 02 02 01 02 02 01
123.817 > 00 00 02 03 01 0a 02 80 40
123.864 > 00 00 02 07 00 08 02
123.867 > 00 00 02 02 01 09 02
123.925 < a1 00 02 02 01 09 02
123.928 < a1 00 02 02 01 09 02
126.125 > 00 00 02 02 00 83
126.366 < a1 00 02 02 00 83 00 64
132.737 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
132.772 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
132.818 > 00 00 02 02 01 0b 02 01
132.865 > 00 00 02 02 01 02 02 01
132.989 > 00 00 02 03 01 0a 02 80 40
133.036 > 00 00 02 07 00 08 02
133.039 > 00 00 02 02 01 09 02 01
133.098 < a1 00 02 02 01 09 02 01
133.101 < a1 00 02 02 01 09 02 01
141.160 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
141.202 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
141.249 > 00 00 02 02 01 0b 02 01
141.295 > 00 00 02 02 01 02 02 01
141.421 > 00 00 02 03 01 0a 02 80 40
141.468 > 00 00 02 07 00 08 02 04
141.471 > 00 00 02 02 01 09 02 01
141.531 < a1 00 02 02 01 09 02 01
141.534 < a1 00 02 02 01 09 02 01
147.456 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
147.489 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
147.536 > 00 00 02 02 01 0b 02 01
147.582 > 00 00 02 02 01 02 02 01
147.705 > 00 00 02 03 01 0a 02 80 40
147.752 > 00 00 02 07 00 08 02 08
147.755 > 00 00 02 02 01 09 02 01
147.814 < a1 00 02 02 01 09 02 01
147.817 < a1 00 02 02 01 09 02 01
152.004 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
152.051 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
152.098 > 00 00 02 02 01 0b 02 01
152.130 > 00 00 02 02 01 02 02 01
152.256 > 00 00 02 03 01 0a 02 80 40
152.303 > 00 00 02 07 00 08 02 0c
152.306 > 00 00 02 02 01 09 02 01
152.366 < a1 00 02 02 01 09 02 01
152.369 < a1 00 02 02 01 09 02 01
156.429 > 00 00 02 02 00 83
156.493 < a1 00 02 02 00 83 00 64
159.448 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
159.485 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
159.532 > 00 00 02 02 01 0b 02 01
159.564 > 00 00 02 02 01 02 02 01
159.691 > 00 00 02 03 01 0a 02 80 40
159.738 > 00 00 02 07 00 08 02 10
159.741 > 00 00 02 02 01 09 02 01
159.801 < a1 00 02 02 01 09 02 01
159.804 < a1 00 02 02 01 09 02 01
167.941 > 00 00 02 12 01 01 02 04 01 90 01 90 03 20 03 20 06 40 06 40 0c 80 0c 80
167.988 > 00 00 02 13 02 01 02 ff a4 0d 26 b4 ff ff 26 26 18 b3 0a
168.036 > 00 00 02 02 01 0b 02 01
168.083 > 00 00 02 02 01 02 02 01
168.208 > 00 00 02 03 01 0a 02 80 40
168.254 > 00 00 02 07 00 08 02 00 00 0a 0a 08
168.257 > 00 00 02 02 01 09 02 01
168.317 < a1 00 02 02 01 09 02 01
168.320 < a1 00 02 02 01 09 02 01
186.559 > 00 00 02 02 00 83
186.623 < a1 00 02 02 00 83 00 64
14 changes: 14 additions & 0 deletions captures/glorious-o2-pro-4k8k-wired/dpi-button-reports.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Same capture: interface 1 input report 4 (8 bytes after the report id), sent by the mouse when
# the DPI button was pressed during the first 41 s, before CORE wrote anything. Format:
# <seconds since the first packet of this device> < <hex>. Layout: 01, stage (1-based), DPI X (u16 BE), DPI Y (u16 BE), 00 00.
14.207 < 01 02 03 20 03 20 00 00
14.315 < 01 03 06 40 06 40 00 00
20.879 < 01 04 0c 80 0c 80 00 00
26.487 < 01 01 01 90 01 90 00 00
26.575 < 01 02 03 20 03 20 00 00
31.469 < 01 04 0c 80 0c 80 00 00
31.525 < 01 01 01 90 01 90 00 00
33.657 < 01 02 03 20 03 20 00 00
33.733 < 01 03 06 40 06 40 00 00
40.937 < 01 04 0c 80 0c 80 00 00
41.050 < 01 01 01 90 01 90 00 00
110 changes: 110 additions & 0 deletions docs/glorious-o2-pro-4k8k.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Glorious Model O 2 PRO 4K/8K

Requested in the OpenMouse Discord (ticket 0160). The driver was written from a
USBPcap capture of Glorious CORE on a wired unit
(`captures/glorious-o2-pro-4k8k-wired/`) and from the code of CORE 2.1.21,
which names every register. It has not run on hardware.

## Which mouse

| Path | VID:PID | Collection |
|-|-|-|
| USB cable | `258a:201b` | usage page `0xffff`, usage 0, unnumbered 64-byte feature report |
| 2.4 GHz receiver | `258a:2035` | same |

CORE's device table calls it "MODEL O 2 PRO 4k/8kHz Edition" and gives both ids.
Interface 1 has another `0xffff` collection (usage 1, no feature report). The
picker may list it as a second entry; the driver claims only the one with the
feature report.

Wired it polls up to 8000 Hz, through the receiver up to 4000 Hz. The product
page gives DPI 100 to 26000, debounce 4 to 16 ms (10 ms default), lift-off
1 to 2 mm (1 mm default).

The classic Glorious driver used to claim both ids with a reduced feature set:
battery, plus RGB and debounce methods the app never surfaced. The ids moved
out of `GLORIOUS_CLASSIC_PRODUCTS` so that exactly one driver claims them. Its
debounce frame is shorter than the one CORE sends (length 1 against 7), which
was never tried on this mouse. CORE builds this mouse's RGB frames with the
classic layout, so RGB can be added to the new client later; it is not
captured here.

## Frame

Unnumbered 64-byte feature reports. A request is `SET_REPORT`; a reply is the
`GET_REPORT` about 60 ms later. No checksum.

```
00 00 02 len bank register data... profile id first in every per-profile register
```

`len` is the data length, `bank` is 0 system, 1 per-profile settings, 2
per-profile lighting. A reply echoes the request with status `0xa1` in byte 0.
The full register table is in `captures/glorious-o2-pro-4k8k-wired/README.md`.

## Profiles

The mouse keeps three profiles and every setting is written with a profile
number: the position of the profile selected in CORE. The mouse cannot report
which one is active and no read of any setting is known. The driver assumes
profile 1 until the user picks one in the Profile card; picking sends CORE's own
select frame (`00 00 02 01 00 05 <n>`), after which writes land in that profile.
The values the panel shows are the driver's last writes, kept per profile in
localStorage (one set per model, shared by the cable and the receiver), so a fresh
browser shows the factory values (400, 800, 1600, 3200
DPI, 1000 Hz, 10 ms, motion sync on).

## What the driver does

Identity and firmware, battery, DPI stages (value, count up to 6, LED color,
active stage), polling rate (125 to 8000 Hz; the receiver stops at 4000 Hz),
debounce (simple mode, 4 to 16 ms), motion sync, and profile select.

A single DPI edit rewrites the whole stage table, as CORE does, from the values
shown. Frames are sent 30 ms apart and 120 ms after the active stage, CORE's own
pauses. The driver does not read the acknowledgement of a write.

Not done, with the reason:

- Lift-off. CORE writes the same value for its 1.0 mm and 2.0 mm options, so
the value to distance map is unknown.
- Auto sleep (bank 0 register `0x07`, seconds as u16 BE, `0xffff` for never),
lighting, buttons, macros, advanced debounce. Not captured; the codec has the
advanced debounce encoder because the capture contains it.
- Reading the receiver's own firmware: the frame (target byte 0) is CORE's, not
captured.

## To test

1. Connect in Chrome through control.openmouse.app, on the cable. The status
should show the model, battery, firmware `1.0.15.0` on the unit that was
captured, and "Profile 1".
2. Polling: set 1000 Hz, then 8000 Hz, and measure with a polling rate checker.
Turn Motion Sync off at 8000 Hz, as Glorious advises.
3. DPI: edit a stage, press the DPI button and check the LED color and the
measured DPI; recolor a stage.
4. Debounce and Motion Sync: set each and check the mouse still clicks.
5. If nothing changes, the mouse is on another profile: pick the profile you
use in CORE in the Profile card and repeat.
6. Repeat 2 to 4 on the receiver; 8000 Hz must not be offered there.
7. Report the console log and which profile CORE shows as active.

## Sources

- The capture above; no serial numbers or personal data in it.
- Glorious CORE 2.1.21 (Windows installer from gloriousgaming.com), read for
its device table and `MouseV2ProDeviceHandler` frame builders. Nothing from it
is committed.
- https://github.com/korkje/mxw (profile select, firmware and battery reads)
and https://github.com/AMarcinkiewicz/GloriousAutoPollingRate (polling codes,
measured on a Model D2 Pro 4K, whose receiver `258a:2036` takes the same
frame).
- https://www.gloriousgaming.com/pages/pro-mice-4k8k for the limits.

## Open

- What the mouse does with 8000 Hz in the wireless byte. CORE wrote it once and
corrected itself.
- Whether the same frames work unchanged on the Model D2 Pro 4K/8K
(`258a:201c`, `258a:2036`). CORE gives it the same handler, but no capture of
it exists here, so it stays on the reduced classic driver.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@
"types": "./dist/glorious-classic/index.d.ts",
"import": "./dist/glorious-classic/index.js"
},
"./glorious-core2": {
"types": "./dist/glorious-core2/index.d.ts",
"import": "./dist/glorious-core2/index.js"
},
"./ksnake": {
"types": "./dist/ksnake/index.d.ts",
"import": "./dist/ksnake/index.js"
Expand Down
Loading
Loading