Medius - Native APIRequests

Requests

Query the box for state

QUERY is the only command that gets a reply, a RESP. Pick what to read with the what selector: the firmware version, the box's health, the cloned device info, device capabilities, rate, delivery stats, the active input locks, the catch subscription, a persistent box option, or the buffered input clip.

QUERY

Ask the box for state

QUERY asks for one piece of state, named by its what byte. Opcode 0x05.

QUERY 0x05 · payload 1 byte (2 for OPTIONS)

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu8which state to read (see below)
1idu8only for what = 9 (OPTIONS): which option to read; omitted otherwise
SELECTORS
whatReadsReply
0The firmware version.VERSION
1The box's health.HEALTH
2The cloned device's USB identity, kind, and product.DEVICE_INFO
3The whole device's capabilities (mouse + keyboard).CAPS
4The native report rate.RATE
5Delivery and telemetry counters.STATS
6The active input locks.LOCKS
7The active catch subscription.CATCH
8reserved-
9A persistent box option, by id.OPTIONS
10The buffered-clip ring depth, playback state, and config.CLIP
EFFECT

The box replies with a RESP carrying the same what and the requested data, one per selector in the table above. QUERY is the only command that gets a reply; everything else is fire-and-forget. Library bindings: query_version, query_health, device_info, caps, query_rate, query_stats, query_locks, query_catch, query_imperfect, query_movement_riding, query_emit_pace, and the clip status query.

EXAMPLE

what = 0 (read the version):

+--------+--------+--------+--------+--------+--------+
| A5     | 05     | 00     | 01 00  | 00     | lo hi  |
+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | CRC16  |
+--------+--------+--------+--------+--------+--------+

RESP

The box's reply

RESP is the box's reply to a QUERY. Opcode 0x06.

RESP 0x06 · box → PC

Reply

PAYLOAD
OffsetFieldTypeNotes
0whatu8echoes the request's selector
1..datavariesthe layout for the requested what (see the selectors)
EFFECT

You get exactly one RESP per QUERY. Its SEQ matches the request's and what echoes the selector, so you can pair a reply with its request and tell which kind it is. Each selector's payload is laid out below, with an example frame.

VERSION

RESP payload, what = 0

The RESP payload when what = 0. proto_ver is the protocol version (this documentation describes 3); the box reports its own firmware version, then its base mac, a stable per-box id, then a length-delimited ASCII name tail (a synthesized default when unset). The name is additive, so proto_ver stays 3: an older box just sends an empty tail.

QUERY what = 0 · RESP 11-byte header + name

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x00
1proto_veru8protocol version, expected 3
2fw_majoru8firmware major
3fw_minoru8firmware minor
4fw_patchu8firmware patch
5macu8[6]the device chip's base MAC; the stable per-box id, rendered as 12 lowercase hex digits
11..nameasciithe box's human-readable name, filling the rest of the payload (the frame LEN delimits it); a synthesized Medius-XXXX default when unset, set via OPTION(NAME)
EFFECT

The box also sends this unprompted at startup, as a ready signal. The mac stays fixed for a box, so it identifies the same box across replugs and port renumbering; the name is its readable label. Library binding: query_version.

EXAMPLE

Firmware 3.0.0, protocol 3, MAC 123456789abc, name "Loki":

+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 0F 00  | 00     | 03     | 03     | 00     | 00     | ...    |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | proto  | major  | minor  | patch  | ...    |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| ...    | 12 34 56 78 9A BC  | 4C 6F 6B 69  | lo hi  |
+--------+--------------------+--------------+--------+
| ...    | mac (6 bytes)      | "Loki"       | CRC16  |
+--------+--------------------+--------------+--------+

HEALTH

RESP payload, what = 1

The RESP payload when what = 1: a single flags byte, each bit an independent status.

QUERY what = 1 · RESP 2 bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x01
1flagsu8the status bits below
FLAGS
BitMaskSet when
b00x01the link to the host chip is up
b10x02a real mouse is attached
b20x04the PC has set up the cloned mouse
b30x08injection is active
b40x10RATE_CONFIDENT: the native-rate estimator window is full, so the RATE value is trustworthy
b50x20LOCK_ON: at least one input LOCK is active
b60x40CATCH_ON: a CATCH subscription is active, physical-input events are streaming
b70x80KBD_ATT: a keyboard is attached on the host chip, cloned and injectable
EFFECT

The first three bits set means the box is ready for input to reach the PC. Library binding: query_health.

EXAMPLE

Ready, with link, mouse, and clone all up (flags = 0x07):

+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 02 00  | 01     | 07     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | flags  | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+

DEVICE_INFO

RESP payload, what = 2

The RESP payload when what = 2: the USB identity, kind, and product string the box read from the real device. The clone shows up on the game PC, not here, so this is the only way the control PC can read it. The header is fixed at 11 bytes, then a length-delimited UTF-8 product tail (which may be empty). Every field is zero when nothing is attached.

QUERY what = 2 · RESP 11-byte header + product

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x02
1vidu16idVendor, little-endian
3pidu16idProduct, little-endian
5bcd_deviceu16bcdDevice, the device release
7bcd_usbu16bcdUSB, e.g. 0x0200 or 0x0201
9flagsu8the bits below
10primary_kindu8the cloned device's kind, from its Boot-interface protocol (see below)
11..productUTF-8the product string, filling the rest of the payload; may be empty
FLAGS
BitMaskSet when
b00x01HAS_SERIAL: the clone serves a serial string
b10x02HAS_BOS: the clone serves a BOS descriptor
PRIMARY_KIND
ValueKind
0unknown
1keyboard
2mouse
EFFECT

A vid of 0 means nothing is attached yet. Library binding: device_info.

EXAMPLE

A Logitech G502 (046D:C08B), USB 2.01, serial and BOS served, kind mouse, product "G502":

+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 0F 00  | 02     | 6D 04  | 8B C0  | ...    |
+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | vid    | pid    | ...    |
+--------+--------+--------+--------+--------+--------+--------+--------+
| ...    | 10 01  | 01 02  | 03     | 02     | 47 35 30 32  | lo hi  |
+--------+--------+--------+--------+--------+--------------+--------+
| ...    | bcdDev | bcdUSB | flags  | kind   | "G502"       | CRC16  |
+--------+--------+--------+--------+--------+--------------+--------+

CAPS

RESP payload, what = 3

The RESP payload when what = 3: one summary of the whole cloned device, mouse and keyboard, read from its HID report descriptors. Counts and yes/no flags only, never raw HID field offsets. Use it to check before you act: an INJECT for a usage the device lacks is silently ignored, so the counts tell you what is real. A class that is not present reads all-zero.

QUERY what = 3 · RESP 7 bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x03
1n_buttonsu8buttons the mouse report carries
2axis_flagsu8mouse axes, the bits below
3n_hidu8cloned HID interfaces; >1 = composite
4n_keysu8keycode-array slots, or 0xFF for NKRO; 0 = no keyboard
5kbd_flagsu8keyboard, the bits below
6change_drivenu8per class: b0 mouse (continuous, 0), b1 keyboard/media (change-driven, 1 when bound)
AXIS_FLAGS
BitMaskSet when
b00x01X: the report carries an X axis
b10x02Y: the report carries a Y axis
b20x04WHEEL: the report carries a wheel
b30x08REPORT_ID: the mouse report sits behind a HID report ID
KBD_FLAGS
BitMaskSet when
b00x01NKRO: the keyboard reports an NKRO bitmap
b10x02CONSUMER: a Consumer collection is present, so media keys are injectable
b20x04SYSTEM: a system-control collection is present (passthrough-only)
b30x08REPORT_ID: the keyboard report sits behind a HID report ID
EFFECT

Library binding: caps.

EXAMPLE

A 5-button mouse (X/Y/wheel, one interface) plus a 6-key Consumer keyboard (axis_flags = 0x07, kbd_flags = 0x02, keyboard change-driven):

+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 07 00  | 03     | 05     | 07     | 01     | 06     | 02     | 02     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | n_btn  | axis   | n_hid  | n_keys | kbdfl  | chgdrv | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+

RATE

RESP payload, what = 4

The RESP payload when what = 4: how fast the active input reports, plus the poll period the clone advertises. The answer is class-aware, so read the field that fits the input kind:

Input kindCHANGE_DRIVENReadGives
continuous (moving mouse)0native_period_usHz = 1e6 / period; reads 0 until learned
change-driven (keyboard, media)1poll_period_usno steady cadence, so native_period_us is 0
QUERY what = 4 · RESP 6 bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x04
1native_period_usu16realised native period in µs; 0 = not learned, or change-driven (see flags), Hz = 1e6/period
3poll_period_usu16cloned inject-endpoint bInterval poll period in µs; the honest figure for a change-driven input
5flagsu8the bits below
FLAGS
BitMaskSet when
b00x01CONFIDENT: the estimator window is full, same source as HEALTH RATE_CONFIDENT
b10x02CHANGE_DRIVEN: the active input is event-driven (keyboard / media), so there is no continuous cadence and native_period_us is 0
EFFECT

A 1 kHz mouse reads ~1000 µs once learned. Library binding: query_rate.

EXAMPLE

A 1 kHz mouse, 1000 µs poll, estimator confident (flags = 0x01):

+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 06 00  | 04     | E8 03  | E8 03  | 01     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | native | poll   | flags  | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+

STATS

RESP payload, what = 5

The RESP payload when what = 5: counters the box keeps about whether your commands were delivered. Commands are fire-and-forget with no acknowledgement, so these counters are the only way to tell that everything you sent landed. A nonzero tx_drops or tx_wedges means delivery slipped under load. Wide counters clamp at their max instead of wrapping around.

QUERY what = 5 · RESP 17 bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x05
1inject_emitsu32pure-injection reports emitted, little-endian
5tx_dropsu16reports dropped on TX-queue overflow; should stay 0
7tx_mergesu16backed-up reports merged instead of queued
9tx_maxdepthu8deepest the TX queue has reached
10tx_wedgesu8wedged-endpoint recoveries
11wakeupsu16remote-wakeups issued
13reset_countu16USB bus resets seen
15config_countu16SET_CONFIGURATION events (re-enumerations)
EFFECT

inject_emits counts the no-halving / 1 kHz path. The rest are merge, queue, wakeup, reset, and reconfig counters. Library binding: query_stats.

EXAMPLE

4096 emits, no drops, no wedges:

+--------+--------+--------+--------+--------+--------------+
| A5     | 06     | 00     | 11 00  | 05     | 00 10 00 00  |
+--------+--------+--------+--------+--------+--------------+
| SOF    | TYPE   | SEQ    | LEN    | what   | inject_emits |
+--------+--------+--------+--------+--------+--------------+
| 00 00  | 00 00  | 00     | 00     | 00 00  | 00 00  | 00 00  | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+
| drops  | merges | maxdep | wedges | wakeup | resets | config | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+

LOCKS

RESP payload, what = 6

The RESP payload when what = 6: which physical inputs are currently locked by LOCK, as a variable list of entries, one per locked field across every class, so keyboard and media locks read the same as mouse ones. An empty list (n = 0) means nothing is locked.

QUERY what = 6 · RESP 2 + 4n bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x06
1nu8number of lock entries that follow
+classu8per entry: 0=button 1=key 2=media 3=axis (as LOCK)
+idu16the locked field's id, or 0xFFFF for a whole-class blanket, little-endian
+dirbitsu8which edges are locked, the bits below
DIRBITS
BitMaskSet when
b00x01the positive / press edge is locked
b10x02the negative / release edge is locked
EFFECT

Read it to confirm a lock landed, or to mirror the box's lock state in a UI. Library binding: query_locks.

EXAMPLE

One entry: the wheel's negative (scroll-down) sign locked (class = 3 axis, id = 2 wheel, dirbits = 0x02):

+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 06 00  | 06     | 01     | 03     | 02 00  | 02     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | n      | class  | id     | dirbits| CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+

CATCH

RESP payload, what = 7

The RESP payload when what = 7: the active CATCH subscription mask, plus the box-side count of event frames dropped under back-pressure. A zero mask means nothing is subscribed. Mirrors the CATCH_ON health bit.

QUERY what = 7 · RESP 6 bytes

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x07
1masku8subscribed event classes; bits Motion 0x01, Wheel 0x02, Buttons 0x04, Keys 0x08, Media 0x10
2droppedu32events dropped box-side under back-pressure, little-endian
EFFECT

Read it to confirm a subscription landed, or to check whether you're losing events. Library binding: query_catch.

EXAMPLE

Motion and buttons subscribed, no drops (mask = 0x05):

+--------+--------+--------+--------+--------+--------+--------------+--------+
| A5     | 06     | 00     | 06 00  | 07     | 05     | 00 00 00 00  | lo hi  |
+--------+--------+--------+--------+--------+--------+--------------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | mask   | dropped      | CRC16  |
+--------+--------+--------+--------+--------+--------+--------------+--------+

OPTIONS

RESP payload, what = 9

The RESP payload when what = 9: the value of one persistent box OPTION, echoing the queried id. The value is id-specific, so the query takes the option's id as a second byte and each option is read on its own. An unknown id gets no reply.

QUERY what = 9, id · RESP varies

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu80x09
1idu8which option this value is for
2..valuevariesid-specific, mirroring the matching OPTION value
IMPERFECT VALUE

The IMPERFECT opt-in (id 0) plus two derived clone-status bytes. Each is 0 or 1; a faithful clone reads all-zero.

OffsetFieldNotes
2allowedthe opt-in toggle; 1 = cloning an over-capacity device is allowed
3over_capacitythe attached device needs an interrupt-IN endpoint the box can't service
4clone_imperfectthe live clone is over-capacity and was cloned anyway, so one interface is dead

Read it to tell why a clone is missing (over_capacity = 1, allowed = 0), or to confirm an imperfect clone is live (clone_imperfect = 1). Library binding: query_imperfect.

MOVE_RIDE VALUE

The current MOVE_RIDE window (id 1).

OffsetFieldNotes
2timeoutu16, little-endian; the ride window in ms, 0 = off

Library binding: query_movement_riding.

EMIT VALUE

The current EMIT pacing (id 2): the mode, the configured fixed rate, and the rate actually in effect.

OffsetFieldNotes
2mode0 learnt, 1 interval, 2 fixed
3fixed_hzu16, little-endian; the configured fixed rate
5resolved_hzu16, little-endian; the ceiling in effect, 0 = learnt/adaptive or no device yet

Library binding: query_emit_pace.

EXAMPLE

Reading id = 0: opted in, an over-capacity device attached and cloned imperfectly:

+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 06     | 00     | 05 00  | 09     | 00     | 01     | 01     | 01     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | what   | id     | allow  | overcap| imperf | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+

CLIP

RESP payload, what = 10

The RESP payload when what = 10: the buffered-clip ring depth, playback state, and full config, for host flow-control. A fixed 25-byte prefix, then the clip's held-usage snapshot (the same class-tagged list a USAGE_EVENT carries), then the config tail (autolock, flags, and the bound triggers). Read free before a CLIP_APPEND to avoid an overrun, and state to see a fault or that playback finished. Backs ClipHandle::query_status and query_config.

QUERY what = 10 · RESP 25-byte prefix + held usages + config

Returns RESP

PAYLOAD
OffsetFieldTypeNotes
0whatu810
1stateu80 idle / 1 playing / 2 paused / 3 faulted (recover with CLEAR)
2freeu32ring bytes free; pace top-ups off this, little-endian
6totalu32retained clip size in bytes (streaming: buffered-but-undrained bytes)
10playedu32bytes played from the clip start (retained progress; ~0 while streaming)
14ticksu32content ticks emitted since start (diagnostic)
18underrunsu16empty-ring episodes
20overrunsu16appends dropped whole because the ring was full
22seq_gapsu16dropped CLIP_APPEND frames detected (SEQ gaps)
24held_nu8number of held usages that follow
+classu8per held usage: 0=button 1=key 2=media
+idu16the held usage's id (button id, HID keycode, or Consumer usage), little-endian
+autolocku8config: the CLIP_SET autolock bitmask (CLIP_LOCK_*)
+flagsu8config: b0 loop, b1 retain, b2 finalized
+n_trigu8config: number of bound triggers that follow
+classu8per trigger: 0=button 1=key 2=media 0xFF=any
+idu16per trigger: the usage id, 0xFFFF=any, little-endian
+edgeu8per trigger: 0 both / 1 press / 2 release
+actionu8per trigger: the CLIP_CTRL op 0..5 (start/stop/pause/resume/restart/toggle)
+consumeu8per trigger: 1 = swallow the triggering edge from the host
FLAGS
BitMaskSet when
b00x01LOOP: playback restarts from the top on drain instead of stopping
b10x02RETAIN: the buffered content survives a stop instead of clearing
b20x04FINALIZED: the clip is sealed, no more CLIP_APPEND accepted
EFFECT

The held snapshot lists the usages the clip is currently forcing down, one class-tagged entry each (3 bytes), so buttons, keys, and media are reported one way. The config tail mirrors what CLIP_SET and CLIP_TRIGGER set, so a UI can read back the whole clip in one query. Library bindings: query_status (ClipStatus) and query_config (ClipSettings).

EXAMPLE

Idle, empty ring, no held usages, no autolock, no triggers (state = 0, free = 1024):

+--------+--------+--------+--------+--------+--------+--------------+
| A5     | 06     | 00     | 1C 00  | 0A     | 00     | 00 04 00 00  |
+--------+--------+--------+--------+--------+--------+--------------+
| SOF    | TYPE   | SEQ    | LEN    | what   | state  | free         |
+--------+--------+--------+--------+--------+--------+--------------+
| 00 04 00 00  | 00 00 00 00  | 00 00 00 00  | 00 00  | 00 00  | 00 00  |
+--------------+--------------+--------------+--------+--------+--------+
| total        | played       | ticks        | undrun | ovrrun | seqgap |
+--------------+--------------+--------------+--------+--------+--------+
| 00     | 00     | 00     | 00     | lo hi  |
+--------+--------+--------+--------+--------+
| held_n | autolk | flags  | n_trig | CRC16  |
+--------+--------+--------+--------+--------+