Control transfers
Run a control request against the real device, read its answertransfer runs one USB control transfer against the real device on the host chip and returns its answer as a TransferOutcome.
It rides its own inter-chip link pair, not the game PC's EP0 proxy, and is single-outstanding. Read a descriptor, string, or vendor value straight from the device.
native device the box (host chip | device chip = the clone) game PC
HID report ---IN---> [ HID_IN ]--> renderer --> [ EMIT ]---interrupt-IN---> reads report
relayed <--OUT--- [ HID_OUT ]<-- relay <---------------- interrupt-OUT <-- writes report
control <-- EP0 -> [ CONTROL ]<-- proxy ------------------- EP0 <--------> GET_DESCRIPTOR, SET_*
^
+-- transfer(ep, setup) <== your own control request, its own link pairThe advanced control layer is gated on the imperfect-clone opt-in. With allow_imperfect_clones off, the box answers Refused rather than reaching the device.
transfer
One control transfer, blocking on the answerfn transfer(&self, ep: u8, setup: Setup, out: &[u8]) -> Result<TransferOutcome>
fn transfer_timeout(&self, ep: u8, setup: Setup, out: &[u8], timeout: Duration) -> Result<TransferOutcome>
Blocks
| Parameter | Type | Description |
|---|---|---|
ep | u8 | 0 for EP0, or a control endpoint number the device declares. |
setup | Setup | The eight-byte setup packet. |
out | &[u8] | The OUT data stage: the bytes carried after the setup packet. Empty for an IN transfer. |
Ok(_) means the box answered at all: a TransferStatus other than Ok comes back inside the TransferOutcome, not as an error.
transfer uses DEFAULT_TRANSFER_TIMEOUT (1.5 s). The box gives up after its own ~800 ms window; keep transfer_timeout at or above that.
use medius::{Device, Setup, TransferStatus};
let device = Device::find()?;
device.allow_imperfect_clones(true)?;
let reply = device.transfer(0, Setup::new(0x80, 0x06, 0x0100, 0x0000, 18), &[])?;
if reply.status == TransferStatus::Ok {
println!("device descriptor: {:02x?}", reply.data());
}On AsyncDevice
transfer awaits the device's answerAsyncDevice makes transfer and transfer_timeout futures, awaited like any query, since each waits for the device's answer.
use futures::executor::block_on;
use medius::{AsyncDevice, Setup, TransferStatus};
let device = AsyncDevice::open("/dev/ttyACM0")?;
device.allow_imperfect_clones(true)?;
let reply = block_on(device.transfer(0, Setup::new(0x80, 0x06, 0x0100, 0x0000, 18), &[]))?;
if reply.status == TransferStatus::Ok {
println!("device descriptor: {:02x?}", reply.data());
}