Medius - Rust LibraryControl transfers

Control transfers

Run a control request against the real device, read its answer

transfer 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 pair

The 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 answer
fn 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

ParameterTypeDescription
epu80 for EP0, or a control endpoint number the device declares.
setupSetupThe 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.

EXAMPLE
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 answer

AsyncDevice makes transfer and transfer_timeout futures, awaited like any query, since each waits for the device's answer.

EXAMPLE
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());
}