Medius - Rust LibraryRaw injection

Raw injection

Put a report byte-for-byte on a cloned endpoint

raw puts bytes verbatim on one cloned endpoint, named by number and direction: IN emits toward the game PC, OUT relays to the real device. It carries no semantic model and no merge with native motion.

The write is stateless: the next native report overwrites it, and raw bypasses the rewrite rules.

  native device          the box  (host chip  |  device chip = the clone)         game PC

  HID report  ---IN--->  [ HID_IN ]--> renderer --> [ EMIT ]---interrupt-IN--->  reads report
                                                                    ^
                                                                    +-- raw(n, IN)   <== a report toward the PC

  relayed     <--OUT---  [ HID_OUT ]<-- relay <---------------- interrupt-OUT <--  writes report
                         (VEND_INTR / VEND_BULK)                 ^
                                                                 +-- raw(n, OUT)  <== a report toward the device

  control     <-- EP0 -> [ CONTROL ]<-- proxy ------------------- EP0 <-------->  GET_DESCRIPTOR, SET_*
  enumerate   descriptor patches overwrite what the clone presents

The whole advanced control layer is gated on the imperfect-clone opt-in. With allow_imperfect_clones off, raw returns Error::ImperfectRequired rather than sending a frame the box would drop.

raw

One report on one endpoint, fire-and-forget
fn raw(&self, ep: u8, direction: Direction, bytes: &[u8]) -> Result<()>

Fire-and-forget

ParameterTypeDescription
epu8The cloned endpoint number, 0 to 15.
directionDirectionIN emits toward the game PC, OUT relays to the real device. Any other is Error::RawDirection.
bytes&[u8]The report, on the wire as given. At most one interrupt endpoint's wMaxPacketSize; a bulk endpoint takes up to the 512-byte frame limit.
EXAMPLE
use medius::{Device, Direction};

let device = Device::find()?;
device.allow_imperfect_clones(true)?;
device.raw(1, Direction::IN, &[0x00, 0x01, 0x00, 0x00])?;  // one report on interrupt-IN endpoint 1

Packet size

What the endpoint carries decides what fits

The report is bounded by the endpoint it goes on, and the two transfer types the box relays treat an over-long payload differently.

Endpoint typeA payload over the limit
InterruptPast the endpoint's wMaxPacketSize the report is dropped box-side. Keep a report within one packet.
BulkSplit at the packet size and terminated with a short packet, or a zero-length packet when the payload is an exact multiple, the way a bulk transfer ends on the wire.

A single advanced control layer frame carries up to 512 payload bytes (the frame limit), the bound the C and Python buffers (MEDIUS_MAX_DEV_PAYLOAD) are sized to.

Raw against injection

When the bytes are the point

A standard input in the device's own report belongs in inject and move_rel, not here. Those describe an input and let the box render it into a native-faithful report; raw describes bytes and leaves them untouched.

Aspectinject / move_relraw
AddressesAn axis or usage, by semantic idAn endpoint, by number and direction
On the wireMerged into the native report, clamped to the field width, paced to the native rateThe bytes as given, one report
StateHeld until cleared; rides the native streamStateless; the next native report overwrites it
Rewrite rulesApplyBypassed
GateAlways availableImperfect-clone opt-in

Reach for raw when the report itself is the point: a vendor packet no HID field describes, a byte sequence a device expects on an OUT endpoint, a report shape the semantic core does not model.

The imperfect-clone gate

One opt-in admits the whole layer

The box admits the advanced control layer under the imperfect-clone opt-in and nothing else. The crate reads that state before it sends, so an off opt-in is a real error rather than a frame the box silently drops.

ErrorReturned on
ImperfectRequiredThe box reports the opt-in off. Turn it on with allow_imperfect_clones(true).

The opt-in is a persistent box option, read back with query_imperfect. It is the same gate the native OPTION(IMPERFECT) sets.

On AsyncDevice

raw awaits the opt-in gate

AsyncDevice makes raw a future: it awaits the imperfect-clone opt-in check, then the send itself is fire-and-forget.

EXAMPLE
use futures::executor::block_on;
use medius::{AsyncDevice, Direction};

let device = AsyncDevice::open("/dev/ttyACM0")?;
device.allow_imperfect_clones(true)?;
block_on(device.raw(1, Direction::IN, &[0x00, 0x01, 0x00, 0x00]))?;  // awaits the opt-in gate