Rewrite
Match traffic in flight and change itREWRITE manages up to 32 rules on the device chip. A rule matches packets at one surface and passes, drops, patches, replaces, answers or refuses them before they reach the game PC or the real device.
real device game PC
report --> [ HID_IN ] --> input pipeline --> [ EMIT ] -------------->
^
inject, render, clip entries ---+
vendor IN --> [ VEND_INTR, VEND_BULK ] -------------------------------->
IN reply --> [ CONTROL, reply side ] --------------------------------->
OUT packet <-- [ HID_OUT, VEND_INTR, VEND_BULK ] <-----------------------
request <-- [ CONTROL, request side ] <-------------------------------
|
+-- ANSWER, STALL and NAK end the request hereA surface is a CATCH traffic class, addressed by the same (class, id, dir).
| Name | Value | id is | Carries | Head |
|---|---|---|---|---|
HID_IN | 4 | an interface number; 0xFFFF = every HID interface | IN | the device's report as it arrived, report ID first |
HID_OUT | 5 | an endpoint number; 0xFFFF = every HID interrupt-OUT endpoint | OUT | a report the PC writes to the device |
VEND_INTR | 6 | an endpoint number; 0xFFFF = every vendor interrupt endpoint | IN, OUT | a relayed vendor interrupt packet |
VEND_BULK | 7 | an endpoint number; 0xFFFF = every vendor bulk endpoint | IN, OUT | a relayed vendor bulk packet |
CONTROL | 8 | an endpoint number, 0 = EP0; 0xFFFF = every control endpoint | IN, OUT | the 8 SETUP bytes, then the first 8 OUT data bytes |
EMIT | 9 | an endpoint number; 0xFFFF = every cloned HID interrupt-IN endpoint | IN | the report going on the wire |
ANY | 0xFF | not compared | IN, OUT | the head at each surface a packet crosses, so a mouse report meets the rule at HID_IN and again at EMIT |
Rules need OPTION(IMPERFECT). Otherwise the box discards every REWRITE but the whole-table clear; turning it off empties the table.
REWRITE
Install, overwrite, or remove one ruleA rule is keyed by (cls, id, dir, mlen, match, mask); setting one whose key exists overwrites it. Opcode 0x1C.
REWRITE 0x1C · payload 9 + 2 x mlen + plen bytes
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | cls | u8 | surface, 4-9 or 0xFF, as the table above |
| 1 | id | u16 | class address, little-endian; 0xFFFF = every id in the class |
| 3 | dir | u8 | 0 both, 1 IN, 2 OUT; on CONTROL, the request's direction |
| 4 | state | u8 | 1 set (add or overwrite), 0 remove the keyed rule |
| 5 | action | u8 | rule action, 0-8 |
| 6 | off | u16 | byte offset a PATCH or REPLY_PATCH writes at, little-endian |
| 8 | mlen | u8 | match length, 0-16; 0 takes every packet on the address |
| 9 | match | u8[] | mlen bytes compared against the packet head |
| 9+mlen | mask | u8[] | mlen bytes: a head byte ANDed with its mask byte must equal the match byte |
| 9+2 x mlen | payload | u8[] | rest of the frame, plen bytes: what the action writes or replies with |
state = 0 removes the rule under that key and ignores action, off and the payload. cls = 0xFF, id = 0xFFFF, state = 0 clears the whole table.
| Refused when | Why |
|---|---|
OPTION(IMPERFECT) off, for any frame but the whole-table clear | the advanced layer needs the opt-in |
frame shorter than 9 + 2 x mlen | malformed |
11 + 2 x mlen + plen above 512 | a rule must fit its own readback in one frame |
cls is not 4-9 or 0xFF | other classes carry no packet |
dir above 2 | a packet travels IN or OUT |
mlen above 16 | the head compare reads at most 16 bytes |
action invalid on cls | the action table lists each action's classes |
off + plen of a PATCH or REPLY_PATCH passes 64 on a report class or ANY, or 2056 on CONTROL | the write lands past the largest packet the surface carries |
a REPLACE payload above 64 bytes on a report class or ANY | a report is at most 64 bytes |
| all rule payloads would pass 2048 bytes | one shared pool; an overwrite is costed with its old payload returned, and a refused one keeps the old rule; RESP(REWRITE) sets its full flag |
| a 33rd rule | nothing is evicted; RESP(REWRITE) sets its full flag |
state = 0 with no rule under that key | nothing to remove |
A rule applies from the next packet at its surface. A refused frame changes nothing, so compare QUERY(REWRITE) with what was sent.
Drop every report the clone emits on endpoint 1: cls = 9, id = 1, dir = 1, action = 1 (DROP), mlen = 0:
+--------+--------+--------+--------+--------+--------+ | A5 | 1C | 00 | 09 00 | 09 | 01 00 | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | cls | id | +--------+--------+--------+--------+--------+--------+ +--------+--------+--------+--------+--------+--------+ | 01 | 01 | 01 | 00 00 | 00 | lo hi | +--------+--------+--------+--------+--------+--------+ | dir | state | action | off | mlen | CRC16 | +--------+--------+--------+--------+--------+--------+
Answer every HID GET_REPORT on EP0 with report ID 7's four bytes: cls = 8, dir = 1, action = 4, and a two-byte match on bmRequestType and bRequest under mask FF FF:
+--------+--------+--------+--------+--------+--------+ | A5 | 1C | 01 | 11 00 | 08 | 00 00 | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | cls | id | +--------+--------+--------+--------+--------+--------+ +--------+--------+--------+--------+--------+--------+ | 01 | 01 | 04 | 00 00 | 02 | A1 01 | +--------+--------+--------+--------+--------+--------+ | dir | state | action | off | mlen | match | +--------+--------+--------+--------+--------+--------+ +--------+--------------+--------+ | FF FF | 07 01 00 00 | lo hi | +--------+--------------+--------+ | mask | payload | CRC16 | +--------+--------------+--------+
Library bindings: set_rewrite, remove_rewrite, and clear_rewrite.
Actions
Top-ranked rule's effectReport classes are 4-7 and 9.
| Name | Value | Effect |
|---|---|---|
PASS | 0 | Any class. The packet passes unchanged, and a broader rule it outranks doesn't act. |
DROP | 1 | Report classes. The packet is dropped. |
PATCH | 2 | Any class. Writes the payload at off and keeps the length; a write past the packet's end is left unapplied. |
REPLACE | 3 | Any class. A report becomes the payload, length included. |
ANSWER | 4 | CONTROL. The box completes the request itself. |
STALL | 5 | CONTROL. The request ends in a STALL handshake. |
NAK | 6 | CONTROL. EP0 NAKs until the PC times out; a control endpoint above 0 STALLs. |
REPLY_PATCH | 7 | CONTROL. Writes the payload into the device's IN reply at off, unapplied past the reply's end. |
REPLY_REPLACE | 8 | CONTROL. The IN reply becomes the payload, cut to wLength. |
On CONTROL an action depends on the request's direction. The reply actions apply only when the device completed the request.
| Action | IN request | OUT request |
|---|---|---|
PASS | proxied unchanged | proxied unchanged |
PATCH | proxied unchanged | the data stage is patched at off before the device gets it |
REPLACE | proxied unchanged | the payload overwrites the start of the data stage; wLength is kept |
ANSWER | the payload is the reply, cut to wLength | the status stage is ACKed and the data goes no further |
STALL, NAK | refused | refused |
REPLY_PATCH, REPLY_REPLACE | the device's reply is rewritten | proxied unchanged |
On EP0 the table matches class and vendor requests. The clone serves standard requests such as GET_DESCRIPTOR itself; change a descriptor with PATCH. A control endpoint above 0 passes every request to the table.
Matching
Which rule a packet reachesA packet shorter than mlen does not match. A match bit outside its mask, or a dir the class never carries, is stored and matches nothing.
Only the highest-ranked match applies to a packet.
rank 1 exact (cls, id) over id = 0xFFFF over cls = 0xFF
2 more mask bits set over fewer
3 dir 1 or 2 over dir 0
4 lower table index
table (every rule dir 1) action
#0 EMIT id 0xFFFF mlen 0 PASS
#1 EMIT id 1 mlen 0 DROP
#2 EMIT id 1 match 02 mask FF PATCH
report 02 ... on endpoint 1
+- #2 exact id, 8 mask bits top-ranked --> patched
+- #1 exact id, 0 mask bits outranked
+- #0 id 0xFFFF outranked
report 01 ... on endpoint 1
+- #2 01 AND FF is not 02 no match
+- #1 exact id top-ranked --> dropped
+- #0 id 0xFFFF outranked
report on endpoint 2
+- #0 id 0xFFFF top-ranked --> passedAn overwrite moves the rule to the end of the table with its hits at 0, so an equal-ranked rule installed earlier now outranks it. A remove or an overwrite shifts every later rule down one index; re-read the list before a QUERY(REWRITE_ENTRY).
A rule counts one hit per packet it matches as top-ranked, PASS included. A cls = 0xFF rule counts, and its PATCH applies, at each surface it matches. RESP(REWRITE) reports hits saturated at 65535.
Order
Clip triggers, pipeline, RAWEach surface runs its packets through two tables before delivery.
packet at a surface
|
v
[ clip packet triggers ] a consuming trigger stops the packet here
|
v
[ rewrite table ] the top-ranked match counts a hit and applies
|
v
delivered to the game PC (IN) or the real device (OUT)| Name | Behaviour |
|---|---|
| packet triggers | A packet can fire a trigger and then match a rule. A report a trigger consumes reaches no rule and counts no hit. |
HID_IN | Buttons, keys, media and a secondary mouse's report are read from the rewritten bytes by LOCK, TRANSFORM and injection. A DROP removes the native report; injection still emits on the frame clock. |
EMIT | Acts last, on native, injected and rendered reports and a clip's entries. A rewritten report that carries no event against the last one sent is suppressed, unless the device reports every poll. |
RAW | Goes straight to the endpoint, past every rule and trigger, as a clip's raw items do. |
TRANSFER | Runs on its own messages to the device, past every rule. |
The host chip weighs the bound mouse's relative axes from the report as it arrived. Whenever a scale, injection or rendering changes them, the host chip's values replace a HID_IN rewrite of those bytes. Rewrite motion at EMIT.
Each CATCH tap sits at a fixed side of the table, and flags bit 7 marks a packet a rule acted on: changed, dropped, answered or refused. A PASS leaves it clear. A CONTROL event is the transaction the game PC received, on every control endpoint; each class is in rules and taps.
Lifecycle
Rules are PC-owned session stateThe table holds until one of these empties it, on the same terms as injection and locks.
remove a REWRITE with state = 0 and the rule's key clear state 0 with cls = 0xFF and id = 0xFFFF silence ~1 s with no control-PC frame RESET a RESET command link loss the inter-chip link drops detach the real device goes away re-clone the box clones the device again: a replug, or a patch presentation opt-in off OPTION(IMPERFECT) turned off
Any valid frame resets the silence timer, so a keepalive holds the table. The library re-asserts its held rules on keepalive and across a control-link reconnect, as for LOCK.
Every clear here but remove and clear moves the session count.
| Event | Effect |
|---|---|
| a rule added, overwritten or removed | gen goes up by one, wrapping at 255. |
| a re-send identical to the stored rule | gen stays. |
| a clear or silence that empties a non-empty table | gen goes up by one. |
RESET, link loss, detach, re-clone, opt-in off | The table and gen both return to 0. |
QUERY(REWRITE) returns gen, the full flag and a line per rule; QUERY(REWRITE_ENTRY) returns one rule in this frame's shape. HEALTH sets REWRITE_ON (0x0100) while the table is non-empty.
Clear the whole table:
+--------+--------+--------+--------+--------+--------+ | A5 | 1C | 02 | 09 00 | FF | FF FF | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | cls | id | +--------+--------+--------+--------+--------+--------+ +--------+--------+--------+--------+--------+--------+ | 00 | 00 | 00 | 00 00 | 00 | lo hi | +--------+--------+--------+--------+--------+--------+ | dir | state | action | off | mlen | CRC16 | +--------+--------+--------+--------+--------+--------+
Library bindings: query_rewrite and query_rewrite_entry.