Device Config Reference

Device configs are TOML files in devices/<vendor>/<model>.toml.

[device]

FieldTypeRequiredDescription
namestringyesHuman-readable device name
vidintegeryesUSB vendor ID (hex literal ok: 0x054c)
pidintegeryesUSB product ID
modestringnoDevice mode identifier

[[device.interface]]

FieldTypeRequiredDescription
idintegeryesUSB interface number
classstringyes"hid" or "vendor"
ep_inintegernoIN endpoint number
ep_outintegernoOUT endpoint number

[device.init]

Optional initialization sequence sent after device open.

FieldTypeRequiredDescription
commandsstring[]yesHex byte strings sent in order
response_prefixinteger[]yesExpected response prefix bytes
enablestringnoHex byte string sent to activate extended mode (e.g. BT mode switch)
disablestringnoHex byte string sent on shutdown
interfaceintegernoInterface to send init commands on
report_sizeintegernoExpected report size after init

[[report]]

Describes one incoming HID report.

FieldTypeRequiredDescription
namestringyesReport name (unique within device)
interfaceintegeryesWhich interface this report arrives on
sizeintegeryesReport byte length

[report.match]

Disambiguates reports when multiple share an interface.

FieldTypeDescription
offsetintegerByte position to inspect
expectinteger[]Expected bytes at that offset

[report.fields]

Inline table mapping field names to their layout:

[report.fields]
left_x = { offset = 1, type = "u8", transform = "scale(-32768, 32767)" }
gyro_x = { offset = 16, type = "i16le" }
battery_level = { bits = [53, 0, 4] }
FieldTypeDescription
offsetintegerByte offset in report
typestringData type (see below)
bitsinteger[3]Sub-byte extraction: [byte_offset, bit_offset, bit_count]
transformstringComma-separated transform chain

Use offset + type for whole-byte fields. Use bits for sub-byte bit extraction (e.g. a 4-bit battery level packed within a byte).

Note: When using bits, the type field must be null, "unsigned", or "signed" — standard type strings like "u8" or "i16le" are not valid.

Data Types

u8 i8 u16le i16le u16be i16be u32le i32le u32be i32be

Transform DSL

Transforms are applied left-to-right as a comma-separated chain:

TransformDescription
scale(min, max)Linearly scale the raw value to the target range
negateNegate the value (multiply by -1)
absTake the absolute value
clampClamp to the output axis range
deadzoneApply deadzone filtering

Example: transform = "scale(-32768, 32767), negate" — scales a u8 (0–255) to -32768..32767, then negates the result.

[report.button_group]

Maps a contiguous byte range to named buttons via bit index.

[report.button_group]
source = { offset = 8, size = 3 }
map = { A = 5, B = 6, X = 4, Y = 7, LB = 8, RB = 9 }

Button names must be valid ButtonId values:

A B X Y LB RB LT RT Start Select Home Capture LS RS DPadUp DPadDown DPadLeft DPadRight M1 M2 M3 M4 Paddle1 Paddle2 Paddle3 Paddle4 TouchPad Mic C Z LM RM O

[report.checksum]

Optional integrity check on the report.

FieldTypeDescription
algostringcrc32 crc8 sum8 xor none
rangeinteger[2][start, end] byte range to checksum
seedintegerInitial seed value prepended to CRC calculation (e.g. 0xa1 for DualSense BT)
expect.offsetintegerWhere the checksum is stored in the report
expect.typestringStorage type of the checksum field

[commands.<name>]

Output command templates (rumble, LED, adaptive triggers, etc.). Template placeholders use {name:type} syntax.

[commands.rumble]
interface = 3
template = "02 01 00 {weak:u8} {strong:u8} 00 ..."

Adaptive Trigger Commands

DualSense-style adaptive triggers use a naming convention of adaptive_trigger_<mode>:

[commands.adaptive_trigger_off]
interface = 3
template = "02 0c 00 ..."

[commands.adaptive_trigger_feedback]
interface = 3
template = "02 0c 00 ... 01 {r_position:u8} {r_strength:u8} ... 01 {l_position:u8} {l_strength:u8} ..."

[commands.adaptive_trigger_weapon]
interface = 3
template = "02 0c 00 ... 02 {r_start:u8} {r_end:u8} {r_strength:u8} ... 02 {l_start:u8} {l_end:u8} {l_strength:u8} ..."

[commands.adaptive_trigger_vibration]
interface = 3
template = "02 0c 00 ... 06 {r_position:u8} {r_amplitude:u8} {r_frequency:u8} ... 06 {l_position:u8} {l_amplitude:u8} {l_frequency:u8} ..."

[output]

Declares the uinput device emitted by padctl.

FieldTypeDescription
emulatestringPreset emulation profile
namestringuinput device name
vidintegerEmulated vendor ID
pidintegerEmulated product ID

[output.axes]

[output.axes]
left_x = { code = "ABS_X", min = -32768, max = 32767, fuzz = 16, flat = 128 }

[output.buttons]

[output.buttons]
A = "BTN_SOUTH"
B = "BTN_EAST"

[output.dpad]

[output.dpad]
type = "hat"   # or "buttons"

[output.force_feedback]

[output.force_feedback]
type = "rumble"
max_effects = 16

[output.aux]

Auxiliary output device (mouse or keyboard).

FieldTypeDescription
typestring"mouse" or "keyboard"
namestringuinput device name
keyboardboolCreate keyboard capability
buttonstableButton-to-event mapping

[output.touchpad]

Touchpad output device.

FieldTypeDescription
namestringuinput device name
x_min / x_maxintegerX axis range
y_min / y_maxintegerY axis range
max_slotsintegerMaximum multitouch slots

[wasm]

WASM plugin for stateful/custom protocols (Phase 4+).

FieldTypeDescription
pluginstringPath to .wasm plugin file

[wasm.overrides]

FieldTypeDescription
process_reportboolPlugin handles report processing