stage_firmwareWrite an image to a chip's spare slot

fn stage_firmware(&self, target: UpdateTarget, image: &[u8], progress: &mut dyn FnMut(UpdateProgress)) -> Result<u32>

Blocks

Blocks for the whole transfer (a few seconds per chip). The clone is disconnected from the game PC meanwhile. A chip on probation refuses another update, so this first waits for both chips to confirm their booted image. If they don't, it returns Error::Update with ON_PROBATION.

ParameterTypeDescription
targetUpdateTargetWhich chip to write: Device or Host.
image&[u8]The whole .bin. An image larger than slot_size is refused with TOO_BIG before any byte is sent.
progress&mut dyn FnMut(UpdateProgress)Called once per acknowledged window, not once per chunk.

Returns the bytes the box wrote. A box on the single-app layout replies NO_SLOT and needs one ROM download flash first.

A staged image is inert until activate_firmware. A power cut in between boots the running firmware.

EXAMPLE
use medius::{Device, UpdateTarget};

let device = Device::find()?;
let image = std::fs::read("medius_device.bin")?;

let written = device.stage_firmware(UpdateTarget::Device, &image, &mut |p| {
    print!("\r{}%", p.percent());          // one call per acknowledged window
})?;
println!("\nstaged {written} bytes, not yet booted");

activate_firmwareCommit every staged image and boot into it

fn activate_firmware(&self) -> Result<()>

Blocks

Commits every staged image, reboots into it (host chip first), then reconnects. This takes tens of seconds when the host chip is involved. With nothing staged, it returns Error::Update with NOTHING_STAGED.

The bootloader reverts a chip whose image cannot run, so a bad image costs a reboot (rollback).

If the host chip refuses, the device image stays staged and armed. The next call would commit it alone and leave the chips on different versions. Retry the whole update, or abort_update each staged target first.

EXAMPLE
device.activate_firmware()?;
let fw = device.firmware_info()?;
println!("now on ota_{} ({})", fw.device.slot, fw.device.state);

abort_updateDiscard a staged or in-flight transfer

fn abort_update(&self, target: UpdateTarget) -> Result<()>

Blocks

Drops what is staged or in flight for one target. The clone returns without a reboot, and the running slot is untouched. An abandoned session times out on the box after 10 s.

ParameterTypeDescription
targetUpdateTargetWhich chip to clear: Device or Host.

If an activate is waiting on the host chip, abort_update abandons that wait and disarms both chips, whichever target it names.

EXAMPLE
use medius::UpdateTarget;

if let Err(e) = device.activate_firmware() {
    device.abort_update(UpdateTarget::Host)?;    // disarm what the refusal left behind
    device.abort_update(UpdateTarget::Device)?;
    return Err(e);
}

update_firmwareStage and activate one image

fn update_firmware(&self, target: UpdateTarget, image: &[u8], progress: &mut dyn FnMut(UpdateProgress)) -> Result<()>

Blocks

stage_firmware then activate_firmware, for one chip. To update both chips together, call the two separately. If the activate refuses, the staged image is cleared before the error returns.

ParameterTypeDescription
targetUpdateTargetWhich chip to write: Device or Host.
image&[u8]The whole .bin.
progress&mut dyn FnMut(UpdateProgress)Called once per acknowledged window.
EXAMPLE
use medius::{Device, UpdateTarget};

let device = Device::find()?;
let image = std::fs::read("medius_device.bin")?;
device.update_firmware(UpdateTarget::Device, &image, &mut |p| {
    print!("\r{}%", p.percent());
})?;

wait_firmware_confirmedBlock until neither chip is on probation

fn wait_firmware_confirmed(&self) -> Result<FirmwareInfo>

Blocks

A chip on probation refuses another update, so stage_firmware waits on this first. The device chip confirms after 10 s of running. The host chip confirms on a clock exchange over the inter-chip link. This call keeps asking while the box reboots after an activate.

After 55 sReturns
A chip still on probationError::Update: op 0 (begin), ON_PROBATION
No reply from the boxError::QueryTimeout
EXAMPLE
device.update_firmware(UpdateTarget::Device, &image, &mut |_| {})?;
let fw = device.wait_firmware_confirmed()?;
println!("now on ota_{} ({})", fw.device.slot, fw.device.state);

On AsyncDeviceSame calls, awaitable

async fn stage_firmware(&self, target: UpdateTarget, image: &[u8], progress: &mut dyn FnMut(UpdateProgress)) -> Result<u32>
async fn activate_firmware(&self) -> Result<()>
async fn abort_update(&self, target: UpdateTarget) -> Result<()>
async fn update_firmware(&self, target: UpdateTarget, image: &[u8], progress: &mut dyn FnMut(UpdateProgress)) -> Result<()>
async fn wait_firmware_confirmed(&self) -> Result<FirmwareInfo>

Blocks

cargo add medius --features async

Each runs the synchronous transfer on a separate thread and resolves when it finishes, so the crate stays runtime-agnostic. Dropping the future doesn't stop a transfer the box has begun.

EXAMPLE
use futures::executor::block_on;
use medius::{Device, UpdateTarget};

let device = Device::find()?.into_async();
let image = std::fs::read("medius_device.bin")?;
block_on(device.update_firmware(UpdateTarget::Device, &image, &mut |_| {}))?;