1
0
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation

With a little bit of trial and error (and a crash-course in how the heck
HID even works), I figured out how to get the dial to provide haptic
feedback!

Along the way, I also learned that you can take advantage of the
(incorrectly named) Resolution Multiplier field to customize how many
"steps" the dial should have, offloading the work to the device itself!

Very cool!!
This commit is contained in:
Daniel Prilik
2020-10-30 19:59:23 -04:00
parent 858209484f
commit e6fa6845fe
16 changed files with 1139 additions and 365 deletions

39
Cargo.lock generated
View File

@@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "arc-swap"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
[[package]]
name = "arrayref"
version = "0.3.6"
@@ -148,6 +154,17 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "hidapi"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6ffb97f2ec5835ec73bcea5256fc2cd57a13c5958230778ef97f11900ba661"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -299,6 +316,26 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "signal-hook"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [
"arc-swap",
"libc",
]
[[package]]
name = "strum"
version = "0.8.0"
@@ -320,7 +357,9 @@ name = "surface-dial-daemon"
version = "0.1.0"
dependencies = [
"evdev-rs",
"hidapi",
"notify-rust",
"signal-hook",
]
[[package]]

View File

@@ -7,4 +7,6 @@ edition = "2018"
[dependencies]
# master includes a PR that implements `Send` for `Device` and `UInputDevice`
evdev-rs = { git = "https://github.com/ndesh26/evdev-rs.git" }
hidapi = { version = "1.2.3", default-features = false, features = ["linux-shared-hidraw"] }
notify-rust = "4"
signal-hook = "0.1.16"

View File

@@ -4,6 +4,7 @@ A Linux userspace controller for the [Microsoft Surface Dial](https://www.micros
- Uses the [`evdev`](https://en.wikipedia.org/wiki/Evdev) API + `libevdev` to read events from the surface dial.
- Uses `libevdev` to fake input via `/dev/uinput` (for keypresses / media controls)
- Uses `hidapi` to configure dial sensitivity + haptics
**DISCLAIMER: This is WIP software!**
@@ -34,33 +35,42 @@ It would be cool to create some sort of GUI overlay (similar to the Windows one)
- [x] Scrolling / Zooming
- [ ] \(meta\) Specify modes via config file(s)
- [ ] Dynamically switch between operating modes
- _currently required re-compiling the daemon_
- [ ] Context-sensitive (based on currently open application)
- [ ] Using `surface-dial-cli` application
- _currently requires re-compiling the daemon_
- [ ] Using some-sort of on-device mechanism (e.g: long-press)
- [ ] Haptic Feedback
- This is tough one, as it doesn't seem like the kernel driver exposes any haptic feedback interface...
- [ ] Using `surface-dial-cli` application
- [ ] Context-sensitive (based on currently open application)
- [x] Haptic Feedback
- https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation
- https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf
- https://www.usb.org/sites/default/files/hut1_21.pdf
- _This was tricky to figure out, but in the end, it was surprisingly straightforward! Big thanks to [Geo](https://www.linkedin.com/in/geo-palakunnel-57718245/) for pointing me in the right direction!_
- [x] Desktop Notifications
- [x] On Launch
- [x] When switching between sub-modes (e.g: scroll/zoom)
Feel free to contribute new features!
## Building
## Dependencies
Building `surface-dial-daemon` requires the following:
- Linux Kernel 4.19 or higher
- A fairly recent version of the Rust compiler
- `libevdev`
- `hidapi`
If `libevdev` is not installed, the `evdev_rs` Rust library will try to build it from source, which may require other bits of build tooling. As such, it's recommended to install `libevdev` if it's available through your distribution.
You can install Rust through [`rustup`](https://rustup.rs/).
Unless you're a cool hackerman, the easiest way to get `libevdev` and `hidapi` is via your distro's package manager.
```bash
# e.g: on ubuntu
sudo apt install libevdev-dev
sudo apt install libevdev-dev libhidapi-dev
```
Otherwise, `surface-dial-daemon` uses the bog-standard `cargo build` flow.
## Building
`surface-dial-daemon` uses the bog-standard `cargo build` flow.
```bash
cargo build -p surface-dial-daemon --release

View File

@@ -1,234 +1,234 @@
# extracted using hid-example.c + https://eleccelerator.com/usbdescreqparser/
# extracted using cat /sys/bus/hid/devices/*:045E:091B.*/report_descriptor | hidrd-convert -o spec
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x0E, // Usage (0x0E)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x05, 0x0D, // Usage Page (Digitizer)
0x09, 0x21, // Usage (Puck)
0xA1, 0x02, // Collection (Logical)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x01, // Report Count (1)
0xA1, 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Button)
0x09, 0x01, // Usage (0x01)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0D, // Usage Page (Digitizer)
0x09, 0x33, // Usage (Touch)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06, // Report Count (6)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xA1, 0x02, // Collection (Logical)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x37, // Usage (Dial)
0x16, 0x01, 0x80, // Logical Minimum (-32767)
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x10, 0x0E, // Physical Maximum (3600)
0x15, 0x00, // Logical Minimum (0)
0x26, 0x10, 0x0E, // Logical Maximum (3600)
0x09, 0x48, // Usage (0x48)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x45, 0x00, // Physical Maximum (0)
0xC0, // End Collection
0x55, 0x0E, // Unit Exponent (-2)
0x65, 0x11, // Unit (System: SI Linear, Length: Centimeter)
0x46, 0x00, 0x00, // Physical Maximum (0)
0x26, 0x00, 0x00, // Logical Maximum (0)
0x09, 0x30, // Usage (X)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x09, 0x31, // Usage (Y)
0x46, 0x00, 0x00, // Physical Maximum (0)
0x26, 0x00, 0x00, // Logical Maximum (0)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x05, 0x0D, // Usage Page (Digitizer)
0x09, 0x48, // Usage (0x48)
0x15, 0x3A, // Logical Minimum (58)
0x25, 0x3A, // Logical Maximum (58)
0x75, 0x08, // Report Size (8)
0x55, 0x0F, // Unit Exponent (-1)
0x35, 0x3A, // Physical Minimum (58)
0x45, 0x3A, // Physical Maximum (58)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x55, 0x00, // Unit Exponent (0)
0x65, 0x00, // Unit (None)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x00, // Physical Maximum (0)
0x05, 0x0E, // Usage Page (Reserved 0x0E)
0x09, 0x01, // Usage (0x01)
0xA1, 0x02, // Collection (Logical)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x24, // Usage (0x24)
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x09, 0x24, // Usage (0x24)
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x07, // Logical Maximum (7)
0x09, 0x20, // Usage (0x20)
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x09, 0x21, // Usage (0x21)
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x25, 0x0A, // Logical Maximum (10)
0x09, 0x28, // Usage (0x28)
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x75, 0x10, // Report Size (16)
0x26, 0xD0, 0x07, // Logical Maximum (2000)
0x09, 0x25, // Usage (0x25)
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x09, 0x25, // Usage (0x25)
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
0x85, 0x02, // Report ID (2)
0x75, 0x20, // Report Size (32)
0x17, 0x37, 0x00, 0x01, 0x00, // Logical Minimum (65590)
0x27, 0x37, 0x00, 0x01, 0x00, // Logical Maximum (65590)
0x09, 0x22, // Usage (0x22)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x11, // Usage (0x11)
0xA1, 0x02, // Collection (Logical)
0x05, 0x0A, // Usage Page (Ordinal)
0x95, 0x03, // Report Count (3)
0x09, 0x03, // Usage (0x03)
0x09, 0x04, // Usage (0x04)
0x09, 0x05, // Usage (0x05)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (-1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0x05, 0x0E, // Usage Page (Reserved 0x0E)
0x09, 0x10, // Usage (0x10)
0xA1, 0x02, // Collection (Logical)
0x05, 0x0A, // Usage Page (Ordinal)
0x95, 0x01, // Report Count (1)
0x15, 0x03, // Logical Minimum (3)
0x25, 0x03, // Logical Maximum (3)
0x36, 0x03, 0x10, // Physical Minimum (4099)
0x46, 0x03, 0x10, // Physical Maximum (4099)
0x09, 0x03, // Usage (0x03)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x15, 0x04, // Logical Minimum (4)
0x25, 0x04, // Logical Maximum (4)
0x36, 0x04, 0x10, // Physical Minimum (4100)
0x46, 0x04, 0x10, // Physical Maximum (4100)
0x09, 0x04, // Usage (0x04)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x15, 0x05, // Logical Minimum (5)
0x25, 0x05, // Logical Maximum (5)
0x36, 0x04, 0x10, // Physical Minimum (4100)
0x46, 0x04, 0x10, // Physical Maximum (4100)
0x09, 0x05, // Usage (0x05)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x00, // Physical Maximum (0)
0xC0, // End Collection
0xC0, // End Collection
0xC0, // End Collection
0xC0, // End Collection
0xC0, // End Collection
0x06, 0x07, 0xFF, // Usage Page (Vendor Defined 0xFF07)
0x09, 0x70, // Usage (0x70)
0xA1, 0x01, // Collection (Application)
0x85, 0x30, // Report ID (48)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (-1)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x09, 0x00, // Usage (0x00)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0x09, 0x71, // Usage (0x71)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (-1)
0x75, 0x08, // Report Size (8)
0x95, 0x48, // Report Count (72)
0x85, 0x2A, // Report ID (42)
0x09, 0xC6, // Usage (0xC6)
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
0x09, 0xC7, // Usage (0xC7)
0x92, 0x02, 0x01, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
0x95, 0x34, // Report Count (52)
0x09, 0xC8, // Usage (0xC8)
0xB2, 0x03, 0x01, // Feature (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
0x85, 0x2B, // Report ID (43)
0x09, 0xC9, // Usage (0xC9)
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
0x09, 0xCA, // Usage (0xCA)
0x92, 0x02, 0x01, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
0x09, 0xCB, // Usage (0xCB)
0xB2, 0x02, 0x01, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483649)
0x27, 0xFF, 0xFF, 0xFF, 0x7F, // Logical Maximum (2147483646)
0x75, 0x20, // Report Size (32)
0x95, 0x04, // Report Count (4)
0x85, 0x2C, // Report ID (44)
0x19, 0xCC, // Usage Minimum (0xCC)
0x29, 0xCF, // Usage Maximum (0xCF)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x04, // Report Count (4)
0x85, 0x2D, // Report ID (45)
0x19, 0xD8, // Usage Minimum (0xD8)
0x29, 0xDB, // Usage Maximum (0xDB)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x04, // Report Count (4)
0x19, 0xDC, // Usage Minimum (0xDC)
0x29, 0xDF, // Usage Maximum (0xDF)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE3, // Usage Maximum (0xE3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x2E, // Report ID (46)
0x19, 0xE4, // Usage Minimum (0xE4)
0x29, 0xE7, // Usage Maximum (0xE7)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x19, 0xE8, // Usage Minimum (0xE8)
0x29, 0xEB, // Usage Maximum (0xEB)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x0B, // Report Count (11)
0x19, 0xEC, // Usage Minimum (0xEC)
0x29, 0xEF, // Usage Maximum (0xEF)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x04, // Report Count (4)
0x85, 0x2F, // Report ID (47)
0x19, 0xF0, // Usage Minimum (0xF0)
0x29, 0xF3, // Usage Maximum (0xF3)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x19, 0xF4, // Usage Minimum (0xF4)
0x29, 0xF7, // Usage Maximum (0xF7)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x19, 0xF8, // Usage Minimum (0xF8)
0x29, 0xFB, // Usage Maximum (0xFB)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x80, // Usage (Sys Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x32, // Report ID (50)
0x09, 0x82, // Usage (Sys Sleep)
0x09, 0x83, // Usage (Sys Wake Up)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x02, // Report Count (2)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06, // Report Count (6)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x09, 0x72, // Usage (0x72)
0xA1, 0x01, // Collection (Application)
0x85, 0x31, // Report ID (49)
0x95, 0x0A, // Report Count (10)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (-1)
0x09, 0xC6, // Usage (0xC6)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0xC7, // Usage (0xC7)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (0Eh),
Collection (Application),
Report ID (1),
Usage Page (Digitizer), ; Digitizer (0Dh)
Usage (Puck), ; Puck (21h, logical collection)
Collection (Logical),
Logical Minimum (0),
Logical Maximum (1),
Report Size (1),
Report Count (1),
Collection (Physical),
Usage Page (Button), ; Button (09h)
Usage (01h),
Input (Variable),
Usage Page (Digitizer), ; Digitizer (0Dh)
Usage (Touch), ; Touch (33h, momentary control)
Input (Variable),
Report Count (6),
Input (Constant, Variable),
Collection (Logical),
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (Dial), ; Dial (37h, dynamic value)
Logical Minimum (-32767),
Logical Maximum (32767),
Report Size (16),
Report Count (1),
Input (Variable, Relative),
Physical Minimum (0),
Physical Maximum (3600),
Logical Minimum (0),
Logical Maximum (3600),
Usage (Resolution Multiplier), ; Resolution multiplier (48h, dynamic value)
Feature (Variable),
Physical Maximum (0),
End Collection,
Unit Exponent (14),
Unit (Centimeter),
Physical Maximum (0),
Logical Maximum (0),
Usage (X), ; X (30h, dynamic value)
Input (Variable, Null State),
Usage (Y), ; Y (31h, dynamic value)
Physical Maximum (0),
Logical Maximum (0),
Input (Variable, Null State),
Usage Page (Digitizer), ; Digitizer (0Dh)
Usage (Width), ; Width (48h, dynamic value)
Logical Minimum (58),
Logical Maximum (58),
Report Size (8),
Unit Exponent (15),
Physical Minimum (58),
Physical Maximum (58),
Input (Constant, Variable),
Unit Exponent (0),
Unit,
Physical Minimum (0),
Physical Maximum (0),
Usage Page (0Eh), ; 0Eh, reserved
Usage (01h),
Collection (Logical),
Logical Minimum (0),
Logical Maximum (255),
Usage (24h),
Feature (Variable, Null State),
Usage (24h),
Output (Variable, Null State),
Logical Minimum (1),
Logical Maximum (7),
Usage (20h),
Feature (Variable, Null State),
Usage (21h),
Output (Variable, Null State),
Logical Maximum (10),
Usage (28h),
Feature (Variable, Null State),
Report Size (16),
Logical Maximum (2000),
Usage (25h),
Feature (Variable, Null State),
Usage (25h),
Output (Variable, Null State),
Report ID (2),
Report Size (32),
Logical Minimum (65591),
Logical Maximum (65591),
Usage (22h),
Feature (Variable),
Usage (11h),
Collection (Logical),
Usage Page (Ordinal), ; Ordinal (0Ah)
Report Count (3),
Usage (03h),
Usage (04h),
Usage (05h),
Report Size (8),
Logical Minimum (0),
Logical Maximum (-1),
Feature (Variable),
End Collection,
Usage Page (0Eh), ; 0Eh, reserved
Usage (10h),
Collection (Logical),
Usage Page (Ordinal), ; Ordinal (0Ah)
Report Count (1),
Logical Minimum (3),
Logical Maximum (3),
Physical Minimum (4099),
Physical Maximum (4099),
Usage (03h),
Feature (Variable),
Logical Minimum (4),
Logical Maximum (4),
Physical Minimum (4100),
Physical Maximum (4100),
Usage (04h),
Feature (Variable),
Logical Minimum (5),
Logical Maximum (5),
Physical Minimum (4100),
Physical Maximum (4100),
Usage (05h),
Feature (Variable),
Physical Minimum (0),
Physical Maximum (0),
End Collection,
End Collection,
End Collection,
End Collection,
End Collection,
Usage Page (FF07h), ; FF07h, vendor-defined
Usage (70h),
Collection (Application),
Report ID (48),
Logical Minimum (0),
Logical Maximum (-1),
Report Count (1),
Report Size (8),
Usage (00h),
Output (Variable),
End Collection,
Usage (71h),
Collection (Application),
Logical Minimum (0),
Logical Maximum (-1),
Report Size (8),
Report Count (72),
Report ID (42),
Usage (C6h),
Input (Variable, Buffered Bytes),
Usage (C7h),
Output (Variable, Buffered Bytes),
Report Count (52),
Usage (C8h),
Feature (Constant, Variable, Buffered Bytes),
Report ID (43),
Usage (C9h),
Input (Variable, Buffered Bytes),
Usage (CAh),
Output (Variable, Buffered Bytes),
Usage (CBh),
Feature (Variable, Buffered Bytes),
Logical Minimum (-2147483648),
Logical Maximum (2147483647),
Report Size (32),
Report Count (4),
Report ID (44),
Usage Minimum (CCh),
Usage Maximum (CFh),
Input (Variable),
Report Count (4),
Report ID (45),
Usage Minimum (D8h),
Usage Maximum (DBh),
Input (Variable),
Report Count (4),
Usage Minimum (DCh),
Usage Maximum (DFh),
Output (Variable),
Usage Minimum (E0h),
Usage Maximum (E3h),
Feature (Variable),
Report ID (46),
Usage Minimum (E4h),
Usage Maximum (E7h),
Input (Variable),
Usage Minimum (E8h),
Usage Maximum (EBh),
Output (Variable),
Report Count (11),
Usage Minimum (ECh),
Usage Maximum (EFh),
Feature (Variable),
Report Count (4),
Report ID (47),
Usage Minimum (F0h),
Usage Maximum (F3h),
Input (Variable),
Usage Minimum (F4h),
Usage Maximum (F7h),
Output (Variable),
Usage Minimum (F8h),
Usage Maximum (FBh),
Feature (Variable),
End Collection,
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (Sys Control), ; System control (80h, application collection)
Collection (Application),
Report ID (50),
Usage (Sys Sleep), ; System sleep (82h, one-shot control)
Usage (Sys Wake Up), ; System wake up (83h, one-shot control)
Logical Minimum (0),
Logical Maximum (1),
Report Count (2),
Report Size (1),
Input (Variable),
Report Count (6),
Input (Constant, Variable),
End Collection,
Usage (72h),
Collection (Application),
Report ID (49),
Report Count (10),
Report Size (8),
Logical Minimum (0),
Logical Maximum (-1),
Usage (C6h),
Input (Variable),
Usage (C7h),
Output (Variable),
End Collection

621
notes/descriptor.c Normal file
View File

@@ -0,0 +1,621 @@
// generated by https://github.com/abend0c1/hidrdd
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
09 0E (LOCAL) USAGE 0x0001000E System Multi-axis Controller (Application Collection)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x0001000E: Page=Generic Desktop Page, Usage=System Multi-axis Controller, Type=Application Collection)
85 01 (GLOBAL) REPORT_ID 0x01 (1)
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
09 21 (LOCAL) USAGE 0x000D0021 Puck (Logical Collection)
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000D0021: Page=Digitizer Device Page, Usage=Puck, Type=Logical Collection)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
A1 00 (MAIN) COLLECTION 0x00 Physical (Usage=0x0: Page=, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CP (Physical Collection)
05 09 (GLOBAL) USAGE_PAGE 0x0009 Button Page
09 01 (LOCAL) USAGE 0x00090001 Button 1 Primary/trigger (Selector, On/Off Control, Momentary Control, or One Shot Control)
81 02 (MAIN) INPUT 0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
09 33 (LOCAL) USAGE 0x000D0033 Touch (Momentary Control)
81 02 (MAIN) INPUT 0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 06 (GLOBAL) REPORT_COUNT 0x06 (6) Number of fields
81 03 (MAIN) INPUT 0x00000003 (6 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x0: Page=, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CL (Logical Collection)
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
09 37 (LOCAL) USAGE 0x00010037 Dial (Dynamic Value)
16 0180 (GLOBAL) LOGICAL_MINIMUM 0x8001 (-32767)
26 FF7F (GLOBAL) LOGICAL_MAXIMUM 0x7FFF (32767)
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
81 06 (MAIN) INPUT 0x00000006 (1 field x 16 bits) 0=Data 1=Variable 1=Relative 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
46 100E (GLOBAL) PHYSICAL_MAXIMUM 0x0E10 (3600)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
26 100E (GLOBAL) LOGICAL_MAXIMUM 0x0E10 (3600)
09 48 (LOCAL) USAGE 0x00010048 Resolution Multiplier (Dynamic Value)
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
C0 (MAIN) END_COLLECTION Logical
55 0E (GLOBAL) UNIT_EXPONENT 0x0E (Unit Value x 10⁻²)
65 11 (GLOBAL) UNIT 0x11 Distance in metres [1 cm units] (1=System=SI Linear, 1=Length=Centimetre)
46 0000 (GLOBAL) PHYSICAL_MAXIMUM 0x0000 (0) <-- Redundant: PHYSICAL_MAXIMUM is already 0 <-- Info: Consider replacing 46 0000 with 44
26 0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000 (0) <-- Info: Consider replacing 26 0000 with 24
09 30 (LOCAL) USAGE 0x00010030 X (Dynamic Value)
81 42 (MAIN) INPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
09 31 (LOCAL) USAGE 0x00010031 Y (Dynamic Value)
46 0000 (GLOBAL) PHYSICAL_MAXIMUM 0x0000 (0) <-- Redundant: PHYSICAL_MAXIMUM is already 0 <-- Info: Consider replacing 46 0000 with 44
26 0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000 (0) <-- Redundant: LOGICAL_MAXIMUM is already 0 <-- Info: Consider replacing 26 0000 with 24
81 42 (MAIN) INPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
09 48 (LOCAL) USAGE 0x000D0048 Width (Dynamic Value)
15 3A (GLOBAL) LOGICAL_MINIMUM 0x3A (58)
25 3A (GLOBAL) LOGICAL_MAXIMUM 0x3A (58)
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
55 0F (GLOBAL) UNIT_EXPONENT 0x0F (Unit Value x 10⁻¹)
35 3A (GLOBAL) PHYSICAL_MINIMUM 0x3A (58)
45 3A (GLOBAL) PHYSICAL_MAXIMUM 0x3A (58)
81 03 (MAIN) INPUT 0x00000003 (1 field x 8 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
55 00 (GLOBAL) UNIT_EXPONENT 0x00 (Unit Value x 10⁰) <-- Info: Consider replacing 55 00 with 54
65 00 (GLOBAL) UNIT 0x00 No unit (0=None) <-- Info: Consider replacing 65 00 with 64
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
05 0E (GLOBAL) USAGE_PAGE 0x000E Haptics Page
09 01 (LOCAL) USAGE 0x000E0001 Simple Haptic Controller (Application or Logical Collection)
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0001: Page=Haptics Page, Usage=Simple Haptic Controller, Type=Application or Logical Collection)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)
09 24 (LOCAL) USAGE 0x000E0024 Repeat Count (Dynamic Value)
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
09 24 (LOCAL) USAGE 0x000E0024 Repeat Count (Dynamic Value)
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
15 01 (GLOBAL) LOGICAL_MINIMUM 0x01 (1)
25 07 (GLOBAL) LOGICAL_MAXIMUM 0x07 (7)
09 20 (LOCAL) USAGE 0x000E0020 Auto Trigger (Dynamic Value)
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
09 21 (LOCAL) USAGE 0x000E0021 Manual Trigger (Dynamic Value)
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
25 0A (GLOBAL) LOGICAL_MAXIMUM 0x0A (10)
09 28 (LOCAL) USAGE 0x000E0028 Waveform Cutoff Time (Static Value)
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
26 D007 (GLOBAL) LOGICAL_MAXIMUM 0x07D0 (2000)
09 25 (LOCAL) USAGE 0x000E0025 Retrigger Period (Dynamic Value)
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
09 25 (LOCAL) USAGE 0x000E0025 Retrigger Period (Dynamic Value)
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
85 02 (GLOBAL) REPORT_ID 0x02 (2)
75 20 (GLOBAL) REPORT_SIZE 0x20 (32) Number of bits per field
17 37000100 (GLOBAL) LOGICAL_MINIMUM 0x00010037 (65591)
27 37000100 (GLOBAL) LOGICAL_MAXIMUM 0x00010037 (65591)
09 22 (LOCAL) USAGE 0x000E0022 Auto Trigger Associated Control (Static Value)
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
09 11 (LOCAL) USAGE 0x000E0011 Duration List (Named Array Collection)
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0011: Page=Haptics Page, Usage=Duration List, Type=Named Array Collection) <-- Warning: USAGE type should be CL (Logical Collection)
05 0A (GLOBAL) USAGE_PAGE 0x000A Ordinal Page
95 03 (GLOBAL) REPORT_COUNT 0x03 (3) Number of fields
09 03 (LOCAL) USAGE 0x000A0003 Instance 3 (Usage Modifier Collection)
09 04 (LOCAL) USAGE 0x000A0004 Instance 4 (Usage Modifier Collection)
09 05 (LOCAL) USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
B1 02 (MAIN) FEATURE 0x00000002 (3 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
C0 (MAIN) END_COLLECTION Logical
05 0E (GLOBAL) USAGE_PAGE 0x000E Haptics Page
09 10 (LOCAL) USAGE 0x000E0010 Waveform List (Named Array Collection)
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0010: Page=Haptics Page, Usage=Waveform List, Type=Named Array Collection) <-- Warning: USAGE type should be CL (Logical Collection)
05 0A (GLOBAL) USAGE_PAGE 0x000A Ordinal Page
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
15 03 (GLOBAL) LOGICAL_MINIMUM 0x03 (3)
25 03 (GLOBAL) LOGICAL_MAXIMUM 0x03 (3)
36 0310 (GLOBAL) PHYSICAL_MINIMUM 0x1003 (4099)
46 0310 (GLOBAL) PHYSICAL_MAXIMUM 0x1003 (4099)
09 03 (LOCAL) USAGE 0x000A0003 Instance 3 (Usage Modifier Collection)
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 04 (GLOBAL) LOGICAL_MINIMUM 0x04 (4)
25 04 (GLOBAL) LOGICAL_MAXIMUM 0x04 (4)
36 0410 (GLOBAL) PHYSICAL_MINIMUM 0x1004 (4100)
46 0410 (GLOBAL) PHYSICAL_MAXIMUM 0x1004 (4100)
09 04 (LOCAL) USAGE 0x000A0004 Instance 4 (Usage Modifier Collection)
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 05 (GLOBAL) LOGICAL_MINIMUM 0x05 (5)
25 05 (GLOBAL) LOGICAL_MAXIMUM 0x05 (5)
36 0410 (GLOBAL) PHYSICAL_MINIMUM 0x1004 (4100) <-- Redundant: PHYSICAL_MINIMUM is already 4100
46 0410 (GLOBAL) PHYSICAL_MAXIMUM 0x1004 (4100) <-- Redundant: PHYSICAL_MAXIMUM is already 4100
09 05 (LOCAL) USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
C0 (MAIN) END_COLLECTION Logical
C0 (MAIN) END_COLLECTION Logical
C0 (MAIN) END_COLLECTION Physical
C0 (MAIN) END_COLLECTION Logical
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Generic Desktop Page featureReport 01 (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x01 (1)
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:
uint16_t GD_SystemMultiaxisControllerPuckResolutionMultiplier; // Usage 0x00010048: Resolution Multiplier, Value = 0 to 3600, Physical = Value
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRepeatCount; // Usage 0x000E0024: Repeat Count, Value = 0 to 255
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerAutoTrigger; // Usage 0x000E0020: Auto Trigger, Value = 1 to 7
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformCutoffTime; // Usage 0x000E0028: Waveform Cutoff Time, Value = 1 to 10
uint16_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRetriggerPeriod; // Usage 0x000E0025: Retrigger Period, Value = 1 to 2000
} featureReport01_t;
//--------------------------------------------------------------------------------
// Haptics Page featureReport 02 (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x02 (2)
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
uint32_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerAutoTriggerAssociatedControl; // Usage 0x000E0022: Auto Trigger Associated Control, Value = 65591 to 65591
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController CL:DurationList
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance3; // Usage 0x000A0003: Instance 3, Value = 0 to -1
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance4; // Usage 0x000A0004: Instance 4, Value = 0 to -1
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance5; // Usage 0x000A0005: Instance 5, Value = 0 to -1
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController CL:WaveformList
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance3; // Usage 0x000A0003: Instance 3, Value = 3 to 3, Physical = ((Value - 3) x 0 / 0 + 4099)
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance4; // Usage 0x000A0004: Instance 4, Value = 4 to 4, Physical = ((Value - 4) x 0 / 0 + 4100)
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance5; // Usage 0x000A0005: Instance 5, Value = 5 to 5, Physical = ((Value - 5) x 0 / 0 + 4100)
} featureReport02_t;
//--------------------------------------------------------------------------------
// Button Page inputReport 01 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x01 (1)
// Collection: CA:SystemMulti-axisController CL:Puck CP:
uint8_t BTN_SystemMultiaxisControllerPuckButton1 : 1; // Usage 0x00090001: Button 1 Primary/trigger, Value = 0 to 1
uint8_t DIG_SystemMultiaxisControllerPuckTouch : 1; // Usage 0x000D0033: Touch, Value = 0 to 1
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:
int16_t GD_SystemMultiaxisControllerPuckDial; // Usage 0x00010037: Dial, Value = -32767 to 32767
// Collection: CA:SystemMulti-axisController CL:Puck CP:
uint16_t GD_SystemMultiaxisControllerPuckX; // Usage 0x00010030: X, Value = 0 to 0, Physical = Value x 0 / 0 in 10⁻⁴ m units
uint16_t GD_SystemMultiaxisControllerPuckY; // Usage 0x00010031: Y, Value = 0 to 0, Physical = Value x 0 / 0 in 10⁻⁴ m units
uint8_t DIG_SystemMultiaxisControllerPuckWidth; // Usage 0x000D0048: Width, Value = 58 to 58, Physical = ((Value - 58) x 0 / 0 + 58) in 10⁻³ m units
} inputReport01_t;
//--------------------------------------------------------------------------------
// Haptics Page outputReport 01 (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x01 (1)
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRepeatCount; // Usage 0x000E0024: Repeat Count, Value = 0 to 255
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerManualTrigger; // Usage 0x000E0021: Manual Trigger, Value = 1 to 7
uint16_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRetriggerPeriod; // Usage 0x000E0025: Retrigger Period, Value = 1 to 2000
} outputReport01_t;
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
06 07FF (GLOBAL) USAGE_PAGE 0xFF07 Vendor-defined
09 70 (LOCAL) USAGE 0xFF070070 <-- Warning: Undocumented usage (document it by inserting 0070 into file FF07.conf) <-- Error: Usage Modifier COLLECTION item (A1 06) expected for USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0xFF070070: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
85 30 (GLOBAL) REPORT_ID 0x30 (48) '0'
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
09 00 (LOCAL) USAGE 0xFF070000 <-- Info: Consider replacing 09 00 with 08
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 30 (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x30 (48) '0'
// Collection: CA:
uint8_t VEN_0000; // Usage 0xFF070000: , Value = 0 to -1
} outputReport30_t;
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
09 71 (LOCAL) USAGE 0xFF070071 <-- Warning: Undocumented usage (document it by inserting 0071 into file FF07.conf)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0xFF070071: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1) <-- Redundant: LOGICAL_MAXIMUM is already -1
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
95 48 (GLOBAL) REPORT_COUNT 0x48 (72) Number of fields
85 2A (GLOBAL) REPORT_ID 0x2A (42)
09 C6 (LOCAL) USAGE 0xFF0700C6 <-- Warning: Undocumented usage (document it by inserting 00C6 into file FF07.conf)
82 0201 (MAIN) INPUT 0x00000102 (72 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
09 C7 (LOCAL) USAGE 0xFF0700C7 <-- Warning: Undocumented usage (document it by inserting 00C7 into file FF07.conf)
92 0201 (MAIN) OUTPUT 0x00000102 (72 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
95 34 (GLOBAL) REPORT_COUNT 0x34 (52) Number of fields
09 C8 (LOCAL) USAGE 0xFF0700C8 <-- Warning: Undocumented usage (document it by inserting 00C8 into file FF07.conf)
B2 0301 (MAIN) FEATURE 0x00000103 (52 fields x 8 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer
85 2B (GLOBAL) REPORT_ID 0x2B (43)
09 C9 (LOCAL) USAGE 0xFF0700C9 <-- Warning: Undocumented usage (document it by inserting 00C9 into file FF07.conf)
82 0201 (MAIN) INPUT 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
09 CA (LOCAL) USAGE 0xFF0700CA <-- Warning: Undocumented usage (document it by inserting 00CA into file FF07.conf)
92 0201 (MAIN) OUTPUT 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
09 CB (LOCAL) USAGE 0xFF0700CB <-- Warning: Undocumented usage (document it by inserting 00CB into file FF07.conf)
B2 0201 (MAIN) FEATURE 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
17 00000080 (GLOBAL) LOGICAL_MINIMUM 0x80000000 (-2147483648)
27 FFFFFF7F (GLOBAL) LOGICAL_MAXIMUM 0x7FFFFFFF (2147483647)
75 20 (GLOBAL) REPORT_SIZE 0x20 (32) Number of bits per field
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields
85 2C (GLOBAL) REPORT_ID 0x2C (44)
19 CC (LOCAL) USAGE_MINIMUM 0xFF0700CC <-- Warning: Undocumented usage (document it by inserting 00CC into file FF07.conf)
29 CF (LOCAL) USAGE_MAXIMUM 0xFF0700CF <-- Warning: Undocumented usage (document it by inserting 00CF into file FF07.conf)
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields <-- Redundant: REPORT_COUNT is already 4
85 2D (GLOBAL) REPORT_ID 0x2D (45)
19 D8 (LOCAL) USAGE_MINIMUM 0xFF0700D8 <-- Warning: Undocumented usage (document it by inserting 00D8 into file FF07.conf)
29 DB (LOCAL) USAGE_MAXIMUM 0xFF0700DB <-- Warning: Undocumented usage (document it by inserting 00DB into file FF07.conf)
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields <-- Redundant: REPORT_COUNT is already 4
19 DC (LOCAL) USAGE_MINIMUM 0xFF0700DC <-- Warning: Undocumented usage (document it by inserting 00DC into file FF07.conf)
29 DF (LOCAL) USAGE_MAXIMUM 0xFF0700DF <-- Warning: Undocumented usage (document it by inserting 00DF into file FF07.conf)
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
19 E0 (LOCAL) USAGE_MINIMUM 0xFF0700E0 <-- Warning: Undocumented usage (document it by inserting 00E0 into file FF07.conf)
29 E3 (LOCAL) USAGE_MAXIMUM 0xFF0700E3 <-- Warning: Undocumented usage (document it by inserting 00E3 into file FF07.conf)
B1 02 (MAIN) FEATURE 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
85 2E (GLOBAL) REPORT_ID 0x2E (46)
19 E4 (LOCAL) USAGE_MINIMUM 0xFF0700E4 <-- Warning: Undocumented usage (document it by inserting 00E4 into file FF07.conf)
29 E7 (LOCAL) USAGE_MAXIMUM 0xFF0700E7 <-- Warning: Undocumented usage (document it by inserting 00E7 into file FF07.conf)
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
19 E8 (LOCAL) USAGE_MINIMUM 0xFF0700E8 <-- Warning: Undocumented usage (document it by inserting 00E8 into file FF07.conf)
29 EB (LOCAL) USAGE_MAXIMUM 0xFF0700EB <-- Warning: Undocumented usage (document it by inserting 00EB into file FF07.conf)
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 0B (GLOBAL) REPORT_COUNT 0x0B (11) Number of fields
19 EC (LOCAL) USAGE_MINIMUM 0xFF0700EC <-- Warning: Undocumented usage (document it by inserting 00EC into file FF07.conf)
29 EF (LOCAL) USAGE_MAXIMUM 0xFF0700EF <-- Warning: Undocumented usage (document it by inserting 00EF into file FF07.conf)
B1 02 (MAIN) FEATURE 0x00000002 (11 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields
85 2F (GLOBAL) REPORT_ID 0x2F (47)
19 F0 (LOCAL) USAGE_MINIMUM 0xFF0700F0 <-- Warning: Undocumented usage (document it by inserting 00F0 into file FF07.conf)
29 F3 (LOCAL) USAGE_MAXIMUM 0xFF0700F3 <-- Warning: Undocumented usage (document it by inserting 00F3 into file FF07.conf)
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
19 F4 (LOCAL) USAGE_MINIMUM 0xFF0700F4 <-- Warning: Undocumented usage (document it by inserting 00F4 into file FF07.conf)
29 F7 (LOCAL) USAGE_MAXIMUM 0xFF0700F7 <-- Warning: Undocumented usage (document it by inserting 00F7 into file FF07.conf)
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
19 F8 (LOCAL) USAGE_MINIMUM 0xFF0700F8 <-- Warning: Undocumented usage (document it by inserting 00F8 into file FF07.conf)
29 FB (LOCAL) USAGE_MAXIMUM 0xFF0700FB <-- Warning: Undocumented usage (document it by inserting 00FB into file FF07.conf)
B1 02 (MAIN) FEATURE 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Vendor-defined featureReport 2A (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2A (42)
// Collection: CA:
uint8_t VEN_00C8[52]; // Usage 0xFF0700C8: , Value = 0 to -1
} featureReport2A_t;
//--------------------------------------------------------------------------------
// Vendor-defined featureReport 2B (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2B (43)
// Collection: CA:
uint8_t VEN_00CB[52]; // Usage 0xFF0700CB: , Value = 0 to -1
} featureReport2B_t;
//--------------------------------------------------------------------------------
// Vendor-defined featureReport 2D (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2D (45)
// Collection: CA:
int32_t VEN_00E0; // Usage 0xFF0700E0: , Value = -2147483648 to 2147483647
int32_t VEN_00E1; // Usage 0xFF0700E1: , Value = -2147483648 to 2147483647
int32_t VEN_00E2; // Usage 0xFF0700E2: , Value = -2147483648 to 2147483647
int32_t VEN_00E3; // Usage 0xFF0700E3: , Value = -2147483648 to 2147483647
} featureReport2D_t;
//--------------------------------------------------------------------------------
// Vendor-defined featureReport 2E (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2E (46)
// Collection: CA:
int32_t VEN_00EC; // Usage 0xFF0700EC: , Value = -2147483648 to 2147483647
int32_t VEN_00ED; // Usage 0xFF0700ED: , Value = -2147483648 to 2147483647
int32_t VEN_00EE; // Usage 0xFF0700EE: , Value = -2147483648 to 2147483647
int32_t VEN_00EF[8]; // Usage 0xFF0700EF: , Value = -2147483648 to 2147483647
} featureReport2E_t;
//--------------------------------------------------------------------------------
// Vendor-defined featureReport 2F (Device <-> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2F (47)
// Collection: CA:
int32_t VEN_00F8; // Usage 0xFF0700F8: , Value = -2147483648 to 2147483647
int32_t VEN_00F9; // Usage 0xFF0700F9: , Value = -2147483648 to 2147483647
int32_t VEN_00FA; // Usage 0xFF0700FA: , Value = -2147483648 to 2147483647
int32_t VEN_00FB; // Usage 0xFF0700FB: , Value = -2147483648 to 2147483647
} featureReport2F_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2A (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2A (42)
// Collection: CA:
uint8_t VEN_00C6[72]; // Usage 0xFF0700C6: , Value = 0 to -1
} inputReport2A_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2B (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2B (43)
// Collection: CA:
uint8_t VEN_00C9[52]; // Usage 0xFF0700C9: , Value = 0 to -1
} inputReport2B_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2C (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2C (44)
// Collection: CA:
int32_t VEN_00CC; // Usage 0xFF0700CC: , Value = -2147483648 to 2147483647
int32_t VEN_00CD; // Usage 0xFF0700CD: , Value = -2147483648 to 2147483647
int32_t VEN_00CE; // Usage 0xFF0700CE: , Value = -2147483648 to 2147483647
int32_t VEN_00CF; // Usage 0xFF0700CF: , Value = -2147483648 to 2147483647
} inputReport2C_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2D (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2D (45)
// Collection: CA:
int32_t VEN_00D8; // Usage 0xFF0700D8: , Value = -2147483648 to 2147483647
int32_t VEN_00D9; // Usage 0xFF0700D9: , Value = -2147483648 to 2147483647
int32_t VEN_00DA; // Usage 0xFF0700DA: , Value = -2147483648 to 2147483647
int32_t VEN_00DB; // Usage 0xFF0700DB: , Value = -2147483648 to 2147483647
} inputReport2D_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2E (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2E (46)
// Collection: CA:
int32_t VEN_00E4; // Usage 0xFF0700E4: , Value = -2147483648 to 2147483647
int32_t VEN_00E5; // Usage 0xFF0700E5: , Value = -2147483648 to 2147483647
int32_t VEN_00E6; // Usage 0xFF0700E6: , Value = -2147483648 to 2147483647
int32_t VEN_00E7; // Usage 0xFF0700E7: , Value = -2147483648 to 2147483647
} inputReport2E_t;
//--------------------------------------------------------------------------------
// Vendor-defined inputReport 2F (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2F (47)
// Collection: CA:
int32_t VEN_00F0; // Usage 0xFF0700F0: , Value = -2147483648 to 2147483647
int32_t VEN_00F1; // Usage 0xFF0700F1: , Value = -2147483648 to 2147483647
int32_t VEN_00F2; // Usage 0xFF0700F2: , Value = -2147483648 to 2147483647
int32_t VEN_00F3; // Usage 0xFF0700F3: , Value = -2147483648 to 2147483647
} inputReport2F_t;
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 2A (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2A (42)
// Collection: CA:
uint8_t VEN_00C7[72]; // Usage 0xFF0700C7: , Value = 0 to -1
} outputReport2A_t;
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 2B (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2B (43)
// Collection: CA:
uint8_t VEN_00CA[52]; // Usage 0xFF0700CA: , Value = 0 to -1
} outputReport2B_t;
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 2D (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2D (45)
// Collection: CA:
int32_t VEN_00DC; // Usage 0xFF0700DC: , Value = -2147483648 to 2147483647
int32_t VEN_00DD; // Usage 0xFF0700DD: , Value = -2147483648 to 2147483647
int32_t VEN_00DE; // Usage 0xFF0700DE: , Value = -2147483648 to 2147483647
int32_t VEN_00DF; // Usage 0xFF0700DF: , Value = -2147483648 to 2147483647
} outputReport2D_t;
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 2E (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2E (46)
// Collection: CA:
int32_t VEN_00E8; // Usage 0xFF0700E8: , Value = -2147483648 to 2147483647
int32_t VEN_00E9; // Usage 0xFF0700E9: , Value = -2147483648 to 2147483647
int32_t VEN_00EA; // Usage 0xFF0700EA: , Value = -2147483648 to 2147483647
int32_t VEN_00EB; // Usage 0xFF0700EB: , Value = -2147483648 to 2147483647
} outputReport2E_t;
//--------------------------------------------------------------------------------
// Vendor-defined outputReport 2F (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x2F (47)
// Collection: CA:
int32_t VEN_00F4; // Usage 0xFF0700F4: , Value = -2147483648 to 2147483647
int32_t VEN_00F5; // Usage 0xFF0700F5: , Value = -2147483648 to 2147483647
int32_t VEN_00F6; // Usage 0xFF0700F6: , Value = -2147483648 to 2147483647
int32_t VEN_00F7; // Usage 0xFF0700F7: , Value = -2147483648 to 2147483647
} outputReport2F_t;
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
09 80 (LOCAL) USAGE 0x00010080 System Control (Application Collection)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00010080: Page=Generic Desktop Page, Usage=System Control, Type=Application Collection)
85 32 (GLOBAL) REPORT_ID 0x32 (50) '2'
09 82 (LOCAL) USAGE 0x00010082 System Sleep (One Shot Control)
09 83 (LOCAL) USAGE 0x00010083 System Wake Up (One Shot Control)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
95 02 (GLOBAL) REPORT_COUNT 0x02 (2) Number of fields
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
81 02 (MAIN) INPUT 0x00000002 (2 fields x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
95 06 (GLOBAL) REPORT_COUNT 0x06 (6) Number of fields
81 03 (MAIN) INPUT 0x00000003 (6 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Generic Desktop Page inputReport 32 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x32 (50) '2'
// Collection: CA:SystemControl
uint8_t GD_SystemControlSystemSleep : 1; // Usage 0x00010082: System Sleep, Value = 0 to 1
uint8_t GD_SystemControlSystemWakeUp : 1; // Usage 0x00010083: System Wake Up, Value = 0 to 1
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
uint8_t : 1; // Pad
} inputReport32_t;
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
09 72 (LOCAL) USAGE 0x00010072 <-- Warning: Undocumented usage (document it by inserting 0072 into file 0001.conf)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00010072: Page=Generic Desktop Page, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CA (Application Collection)
85 31 (GLOBAL) REPORT_ID 0x31 (49) '1'
95 0A (GLOBAL) REPORT_COUNT 0x0A (10) Number of fields
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
09 C6 (LOCAL) USAGE 0x000100C6 Wireless Radio Button (On/Off Control)
81 02 (MAIN) INPUT 0x00000002 (10 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
09 C7 (LOCAL) USAGE 0x000100C7 Wireless Radio LED (On/Off Control)
91 02 (MAIN) OUTPUT 0x00000002 (10 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Generic Desktop Page inputReport 31 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x31 (49) '1'
// Collection: CA:
uint8_t GD_WirelessRadioButton[10]; // Usage 0x000100C6: Wireless Radio Button, Value = 0 to -1
} inputReport31_t;
//--------------------------------------------------------------------------------
// Generic Desktop Page outputReport 31 (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x31 (49) '1'
// Collection: CA:
uint8_t GD_WirelessRadioLed[10]; // Usage 0x000100C7: Wireless Radio LED, Value = 0 to -1
} outputReport31_t;

View File

@@ -1,38 +1,3 @@
pub enum DialDir {
Left,
Right,
}
pub struct ThresholdHelper {
sensitivity: i32,
pos: i32,
}
impl ThresholdHelper {
pub fn new(sensitivity: i32) -> ThresholdHelper {
ThresholdHelper {
sensitivity,
pos: 0,
}
}
pub fn update(&mut self, delta: i32) -> Option<DialDir> {
self.pos += delta;
if self.pos > self.sensitivity {
self.pos -= self.sensitivity;
return Some(DialDir::Right);
}
if self.pos < -self.sensitivity {
self.pos += self.sensitivity;
return Some(DialDir::Left);
}
None
}
}
use notify_rust::error::Result as NotifyResult;
use notify_rust::{Hint, Notification, NotificationHandle, Timeout};

View File

@@ -4,6 +4,7 @@ use std::thread::JoinHandle;
use std::time::Duration;
use crate::controller::ControlMode;
use crate::dial_device::DialHaptics;
use crate::fake_input::FakeInput;
use crate::DynResult;
@@ -106,12 +107,6 @@ impl Drop for DPad {
}
}
impl Default for DPad {
fn default() -> Self {
Self::new()
}
}
impl DPad {
pub fn new() -> DPad {
let (msg_tx, msg_rx) = mpsc::channel();
@@ -128,17 +123,22 @@ impl DPad {
}
impl ControlMode for DPad {
fn on_btn_press(&mut self) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
haptics.set_mode(false, Some(3600))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
eprintln!("space");
self.fake_input.key_click(&[EV_KEY::KEY_SPACE])?;
Ok(())
}
fn on_btn_release(&mut self) -> DynResult<()> {
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
Ok(())
}
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
self.msg.send(Msg::Delta(delta))?;
Ok(())
}

View File

@@ -1,47 +1,44 @@
use crate::common::{DialDir, ThresholdHelper};
use crate::controller::ControlMode;
use crate::dial_device::DialHaptics;
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
pub struct Media {
thresh: ThresholdHelper,
fake_input: FakeInput,
}
impl Media {
pub fn new(sensitivity: i32) -> Media {
pub fn new() -> Media {
Media {
thresh: ThresholdHelper::new(sensitivity),
fake_input: FakeInput::new(),
}
}
}
impl ControlMode for Media {
fn on_btn_press(&mut self) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
haptics.set_mode(false, Some(36))?;
Ok(())
}
fn on_btn_release(&mut self) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
Ok(())
}
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
Ok(())
}
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
match self.thresh.update(delta) {
Some(DialDir::Left) => {
eprintln!("next song");
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
}
Some(DialDir::Right) => {
eprintln!("last song");
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
}
None => {}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
if delta > 0 {
eprintln!("last song");
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
} else {
eprintln!("next song");
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
}
Ok(())
}

View File

@@ -1,9 +1,11 @@
mod dpad;
mod media;
mod null;
mod scroll_zoom;
mod volume;
pub use self::dpad::*;
pub use self::media::*;
pub use self::null::*;
pub use self::scroll_zoom::*;
pub use self::volume::*;

View File

@@ -0,0 +1,30 @@
use crate::controller::ControlMode;
use crate::dial_device::DialHaptics;
use crate::DynResult;
pub struct Null {}
impl Null {
pub fn new() -> Null {
Null {}
}
}
impl ControlMode for Null {
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
haptics.set_mode(false, Some(0))?;
Ok(())
}
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
Ok(())
}
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
Ok(())
}
fn on_dial(&mut self, _haptics: &DialHaptics, _delta: i32) -> DynResult<()> {
Ok(())
}
}

View File

@@ -1,21 +1,20 @@
use crate::common::{action_notification, DialDir, ThresholdHelper};
use crate::common::action_notification;
use crate::controller::ControlMode;
use crate::dial_device::DialHaptics;
use crate::fake_input::{FakeInput, ScrollStep};
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
pub struct ScrollZoom {
thresh: ThresholdHelper,
zoom: bool,
fake_input: FakeInput,
}
impl ScrollZoom {
pub fn new(sensitivity: i32) -> ScrollZoom {
pub fn new() -> ScrollZoom {
ScrollZoom {
thresh: ThresholdHelper::new(sensitivity),
zoom: false,
fake_input: FakeInput::new(),
@@ -23,46 +22,55 @@ impl ScrollZoom {
}
}
const ZOOM_SENSITIVITY: u16 = 36;
const SCROLL_SENSITIVITY: u16 = 90;
impl ControlMode for ScrollZoom {
fn on_btn_press(&mut self) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
Ok(())
}
fn on_btn_release(&mut self) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
Ok(())
}
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()> {
self.zoom = !self.zoom;
haptics.buzz(1)?;
if self.zoom {
action_notification("Zoom Mode", "zoom-in")?;
haptics.set_mode(false, Some(ZOOM_SENSITIVITY))?;
} else {
action_notification("ScrollZoom Mode", "input-mouse")?;
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
}
Ok(())
}
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
match self.thresh.update(delta) {
None => {}
Some(DialDir::Left) => {
if self.zoom {
eprintln!("zoom out");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])?;
} else {
eprintln!("scroll up");
self.fake_input.scroll_step(ScrollStep::Up)?;
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
if delta > 0 {
if self.zoom {
eprintln!("zoom in");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
} else {
eprintln!("scroll down");
self.fake_input.scroll_step(ScrollStep::Down)?;
}
Some(DialDir::Right) => {
if self.zoom {
eprintln!("zoom in");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
} else {
eprintln!("scroll down");
self.fake_input.scroll_step(ScrollStep::Down)?;
}
} else {
if self.zoom {
eprintln!("zoom out");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])?;
} else {
eprintln!("scroll up");
self.fake_input.scroll_step(ScrollStep::Up)?;
}
}
Ok(())
}
}

View File

@@ -1,53 +1,51 @@
use crate::common::{DialDir, ThresholdHelper};
use crate::controller::ControlMode;
use crate::dial_device::DialHaptics;
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
pub struct Volume {
thresh: ThresholdHelper,
fake_input: FakeInput,
}
impl Volume {
pub fn new(sensitivity: i32) -> Volume {
pub fn new() -> Volume {
Volume {
thresh: ThresholdHelper::new(sensitivity),
fake_input: FakeInput::new(),
}
}
}
impl ControlMode for Volume {
fn on_btn_press(&mut self) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
haptics.set_mode(false, Some(36 * 2))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
// TODO: support double-click to mute
Ok(())
}
fn on_btn_release(&mut self) -> DynResult<()> {
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
eprintln!("play/pause");
// self.fake_input.mute()?
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
Ok(())
}
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
match self.thresh.update(delta) {
Some(DialDir::Left) => {
eprintln!("volume down");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?
}
Some(DialDir::Right) => {
eprintln!("volume up");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?
}
None => {}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
if delta > 0 {
eprintln!("volume up");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?;
} else {
eprintln!("volume down");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?;
}
Ok(())
}
}

View File

@@ -1,13 +1,14 @@
use crate::DynResult;
use crate::dial_device::{DialDevice, DialEventKind};
use crate::dial_device::{DialDevice, DialEventKind, DialHaptics};
pub mod controls;
pub trait ControlMode {
fn on_btn_press(&mut self) -> DynResult<()>;
fn on_btn_release(&mut self) -> DynResult<()>;
fn on_dial(&mut self, delta: i32) -> DynResult<()>;
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_btn_press(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_dial(&mut self, haptics: &DialHaptics, delta: i32) -> DynResult<()>;
}
pub struct DialController {
@@ -26,16 +27,20 @@ impl DialController {
}
pub fn run(&mut self) -> DynResult<()> {
let haptics = self.device.haptics();
self.mode.on_start(haptics)?;
loop {
let evt = self.device.next_event()?;
// TODO: press and hold + rotate to switch between modes
// TODO: press and hold (+ rotate?) to switch between modes
match evt.kind {
DialEventKind::Ignored => {}
DialEventKind::ButtonPress => self.mode.on_btn_press()?,
DialEventKind::ButtonRelease => self.mode.on_btn_release()?,
DialEventKind::Dial(delta) => self.mode.on_dial(delta)?,
DialEventKind::ButtonPress => self.mode.on_btn_press(haptics)?,
DialEventKind::ButtonRelease => self.mode.on_btn_release(haptics)?,
DialEventKind::Dial(delta) => self.mode.on_dial(haptics, delta)?,
}
}
}

View File

@@ -2,6 +2,7 @@ use std::fs;
use std::time::Duration;
use evdev_rs::{Device, InputEvent};
use hidapi::{HidApi, HidDevice};
use crate::error::Error;
@@ -9,6 +10,7 @@ pub struct DialDevice {
// TODO: explore what the control channel can be used for...
_control: Device,
axis: Device,
haptics: DialHaptics,
}
#[derive(Debug)]
@@ -30,6 +32,7 @@ impl DialDevice {
let mut control = None;
let mut axis = None;
// discover the evdev devices
for e in fs::read_dir("/dev/input/").map_err(Error::OpenDevInputDir)? {
let e = e.map_err(Error::OpenDevInputDir)?;
if !e.file_name().to_str().unwrap().starts_with("event") {
@@ -62,6 +65,7 @@ impl DialDevice {
Ok(DialDevice {
_control: control.ok_or(Error::MissingDial)?,
axis: axis.ok_or(Error::MissingDial)?,
haptics: DialHaptics::new()?,
})
}
@@ -79,6 +83,10 @@ impl DialDevice {
Ok(event)
}
pub fn haptics(&self) -> &DialHaptics {
&self.haptics
}
}
impl DialEvent {
@@ -110,3 +118,67 @@ impl DialEvent {
Some(evt)
}
}
pub struct DialHaptics {
hid_device: HidDevice,
}
impl DialHaptics {
fn new() -> Result<DialHaptics, Error> {
let api = HidApi::new().map_err(Error::HidError)?;
let hid_device = api.open(0x045e, 0x091b).map_err(|_| Error::MissingDial)?;
// let mut buf = [0; 256];
// buf[0] = 1;
// let len = device
// .get_feature_report(&mut buf)
// .map_err(Error::HidError)?;
// eprintln!("1: {:02x?}", &buf[..len]);
// buf[0] = 2;
// let len = device
// .get_feature_report(&mut buf)
// .map_err(Error::HidError)?;
// eprintln!("2: {:02x?}", &buf[..len]);
Ok(DialHaptics { hid_device })
}
/// `steps` should be a value between 0 and 3600, which corresponds to the
/// number of subdivisions the dial should use. If left unspecified, this
/// defaults to 36 (an arbitrary choice that "feels good" most of the time)
pub fn set_mode(&self, haptics: bool, steps: Option<u16>) -> Result<(), Error> {
let steps = steps.unwrap_or(36);
assert!(steps <= 3600);
let steps_lo = steps & 0xff;
let steps_hi = (steps >> 8) & 0xff;
let mut buf = [0; 8];
buf[0] = 1;
buf[1] = steps_lo as u8; // steps
buf[2] = steps_hi as u8; // steps
buf[3] = 0x00; // Repeat Count
buf[4] = if haptics { 0x03 } else { 0x02 }; // auto trigger
buf[5] = 0x00; // Waveform Cutoff Time
buf[6] = 0x00; // retrigger period
buf[7] = 0x00; // retrigger period
self.hid_device
.send_feature_report(&buf[..8])
.map_err(Error::HidError)?;
Ok(())
}
pub fn buzz(&self, repeat: u8) -> Result<(), Error> {
let mut buf = [0; 5];
buf[0] = 0x01; // Report ID
buf[1] = repeat; // RepeatCount
buf[2] = 0x03; // ManualTrigger
buf[3] = 0x00; // RetriggerPeriod (lo)
buf[4] = 0x00; // RetriggerPeriod (hi)
self.hid_device.write(&buf).map_err(Error::HidError)?;
Ok(())
}
}

View File

@@ -7,6 +7,7 @@ use evdev_rs::InputEvent;
pub enum Error {
OpenDevInputDir(io::Error),
OpenEventFile(std::path::PathBuf, io::Error),
HidError(hidapi::HidError),
MissingDial,
MultipleDials,
UnexpectedEvt(InputEvent),
@@ -19,6 +20,7 @@ impl fmt::Display for Error {
match self {
Error::OpenDevInputDir(e) => write!(f, "Could not open /dev/input directory: {}", e),
Error::OpenEventFile(path, e) => write!(f, "Could not open {:?}: {}", path, e),
Error::HidError(e) => write!(f, "HID API Error: {}", e),
Error::MissingDial => write!(f, "Could not find the Surface Dial"),
Error::MultipleDials => write!(f, "Found multiple dials"),
Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt),

View File

@@ -1,3 +1,5 @@
#![allow(clippy::collapsible_if, clippy::new_without_default)]
mod common;
pub mod controller;
mod dial_device;
@@ -10,6 +12,9 @@ use crate::controller::DialController;
use crate::dial_device::DialDevice;
use crate::error::Error;
use notify_rust::{Hint, Notification, Timeout};
use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
fn main() {
if let Err(e) = true_main() {
println!("{}", e);
@@ -22,11 +27,29 @@ fn true_main() -> DynResult<()> {
let dial = DialDevice::new()?;
println!("Found the dial.");
common::action_notification("Active!", "input-mouse")?;
std::thread::spawn(move || {
let active_notification = Notification::new()
.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()))
.timeout(Timeout::Never)
.summary("Surface Dial")
.body("Active!")
.icon("input-mouse")
.show()
.expect("failed to send notification");
let default_mode = Box::new(controller::controls::ScrollZoom::new(30));
// let default_mode = Box::new(controller::controls::Volume::new(30));
// let default_mode = Box::new(controller::controls::Media::new(50));
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
for sig in signals.forever() {
eprintln!("received signal {:?}", sig);
active_notification.close();
std::process::exit(1);
}
});
// let default_mode = Box::new(controller::controls::Null::new());
let default_mode = Box::new(controller::controls::ScrollZoom::new());
// let default_mode = Box::new(controller::controls::Volume::new());
// let default_mode = Box::new(controller::controls::Media::new());
// let default_mode = Box::new(controller::controls::DPad::new());
let mut controller = DialController::new(dial, default_mode);