Raw injection
Put a report byte-for-byte on a cloned endpointraw 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 presentsThe 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-forgetfn raw(&self, ep: u8, direction: Direction, bytes: &[u8]) -> Result<()>
Fire-and-forget
| Parameter | Type | Description |
|---|---|---|
ep | u8 | The cloned endpoint number, 0 to 15. |
direction | Direction | IN 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. |
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 1Packet size
What the endpoint carries decides what fitsThe report is bounded by the endpoint it goes on, and the two transfer types the box relays treat an over-long payload differently.
| Endpoint type | A payload over the limit |
|---|---|
| Interrupt | Past the endpoint's wMaxPacketSize the report is dropped box-side. Keep a report within one packet. |
| Bulk | Split 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 pointA 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.
| Aspect | inject / move_rel | raw |
|---|---|---|
| Addresses | An axis or usage, by semantic id | An endpoint, by number and direction |
| On the wire | Merged into the native report, clamped to the field width, paced to the native rate | The bytes as given, one report |
| State | Held until cleared; rides the native stream | Stateless; the next native report overwrites it |
| Rewrite rules | Apply | Bypassed |
| Gate | Always available | Imperfect-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 layerThe 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.
| Error | Returned on |
|---|---|
ImperfectRequired | The 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 gateAsyncDevice makes raw a future: it awaits the imperfect-clone opt-in check, then the send itself is fire-and-forget.
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