Skip to content

hid

tested on simulator

Tier Tier 1
Protocol USB HID 1.11, Bluetooth HID through the operating system
Covers macropads and button boxes, knobs and jog wheels, foot switches, USB status lights, custom HID gadgets on RP2040, ESP32-S2/S3 and ATmega32U4 boards
Hazard default low
Conformance control
Tested with fake hidapi module with a simulated macropad and a real report descriptor

Estimated reach. Thousands of models. HID needs no vendor driver on Linux, macOS and Windows, so it is the default class for small USB input and indicator devices and for homemade gadgets built with TinyUSB or the Arduino HID library. Any device whose report layout is documented or readable from its report descriptor can be described in a device file. Keyboards and mice that the operating system claims exclusively, and devices that need a vendor command protocol on top of HID, are not covered.

The hid driver reads input and feature reports from USB HID devices and writes output and feature reports, through hidapi. The driver suits macropads, knobs, foot switches, status lights and homemade gadgets with a documented report layout. Fields are bools, numbers or enums at any byte offset, with an optional bit mask.

Setup

uv tool install --compile-bytecode "halkit[hid] @ git+https://github.com/sohan-bhat/OpenHAL"

On Linux a regular user needs a udev rule such as /etc/udev/rules.d/70-openhal-hid.rules containing KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="0001", MODE="0660", TAG+="uaccess", then sudo udevadm control --reload and a replug. On macOS, devices that send keyboard reports may need Input Monitoring permission. discover() lists interfaces without opening any.

ParameterDefaultMeaning
nameautoPart of the product string, any case. hid: auto is shorthand
vid, pidfrom a single match.usb entryVendor and product id, decimal or 0x hex
serialnonePicks one of several identical devices
pathnoneExact hidapi path from discover()
usage_pagenonePicks one interface of a composite device
interfacenoneUSB interface number, for the same purpose
report_length64Largest report read, in bytes after the report id
timeout1 sFeature read and write deadline

Exactly one interface must match, or connect fails listing the candidates.

Channel maps

read:
  key_1: { type: bool, stream: true, map: { report: 1, byte: 0, mask: 0x01 } }
  knob: { unit: percent, stream: true, map: { report: 1, byte: 1, scale: 0.392156862745098 } }
  mode: { unit: dimensionless, map: { report: 3, kind: feature } }
write:
  key_1_led: { type: bool, map: { report: 2, kind: output, byte: 0, mask: 0x01, length: 2 } }
  brightness: { unit: dimensionless, min: 0, max: 255, step: 1, map: { report: 2, kind: output, byte: 1 } }
KeyDefaultMeaning
report0Report id, 0 for devices without ids
kindinputinput or feature for reads, output or feature for writes
byte0Offset after the report id byte
formatuint8uint8, int8, uint16, int16, uint32, int32
byteorderlittleSome vendor reports are big-endian
masknoneBit mask, with the value shifted down to the mask's lowest bit
scale, offset1, 0value = raw × scale + offset
on, off1, 0Raw bool values
valuesnoneEnum word to raw value, required for enums
lengthend of the furthest fieldOutput or feature payload length
fillzerosStarting bytes, for reports with a command byte

A write changes only the channel's bits and sends the whole report. Output channels read back the last value sent. Input reads return the newest report, so a button never pressed reads as unknown.

Describe

describe() parses the report descriptor and suggests a channel per whole-byte or single-bit field, named from the usage tables or field_r<report>_b<bit>, with the logical range as range or limits. Feature and odd-sized fields go in the notes.

Example

openhal/drivers/hid/examples/diy-macropad.hal.yaml describes a homemade macropad with four keys, a knob and key LEDs.

Errors and health

  • DriverError for no match, several matches, a device held elsewhere or blocked by permissions, short reports, unmapped enum values, values too big for the field and reports the device fails.
  • A failed read marks the device gone in health(), and the hub reopens the device on return.

Limitations

  • describe() skips long items and fields that are neither whole bytes nor single bits.
  • Keyboards, mice and gamepads the OS claims, especially keyboards on macOS and Windows, may not open.
  • No actions.

Hazard default and safe state

Low. safe_state_support is file, sent as ordinary reports.

Sources