<!-- Source: https://medius.k4tech.net/library/advanced/transfer -->
# 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`](/library/types/structs.md#transfer-outcome).

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
```

> **Warning**
>
> The advanced control layer is gated on the imperfect-clone opt-in. With [`allow_imperfect_clones`](/library/options.md#allow-imperfect-clones) off, the box answers `Refused` rather than reaching the device.

## transfer

_One control transfer, blocking on the answer_

```text
fn transfer(&self, ep: u8, setup: Setup, out: &[u8]) -> Result<TransferOutcome>
```

```text
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`](/library/types/structs.md#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`](/library/types/enums.md#transfer-status) other than `Ok` comes back inside the [`TransferOutcome`](/library/types/structs.md#transfer-outcome), not as an error.

> **Note**
>
> `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

```rust
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`](/library/features/async.md) makes `transfer` and `transfer_timeout` futures, awaited like any query, since each waits for the device's answer.

#### EXAMPLE

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