Skip to main content

rpi_loader_ota/
lib.rs

1//! The over-the-air update bundle a bare-metal Raspberry Pi installs on
2//! itself: one container, packed on a host and validated on the device.
3//!
4//! A bundle carries a kernel image and the files that ship beside it —
5//! a website, assets, whatever an application serves — under a single
6//! checksum, so a board can reject a damaged transfer before it writes
7//! anything to its card.
8//!
9//! The reason this is a crate rather than a file each project keeps a copy
10//! of is that **the encoder and the decoder are the same code**. A wire
11//! format described in two places is a format that has already drifted:
12//! the two implementations this one was collected from agreed on every
13//! byte and disagreed on what they enforced, each rejecting bundles the
14//! other accepted.
15//!
16//! # How it is split
17//!
18//! | | Feature | Pulls in |
19//! | --- | --- | --- |
20//! | Reading and checking a bundle | *none* | nothing |
21//! | Building one | `alloc` *(default)* | nothing |
22//! | Installing one onto a FAT volume | `apply` | `resident-fat` |
23//!
24//! The split is not tidiness. `rpi-loader`, the host CLI that packs
25//! bundles, never installs one — without the feature it would depend on a
26//! filesystem implementation in order to write a file on a host. A device
27//! that only installs bundles is the mirror image, compiling an encoder it
28//! will never call.
29//!
30//! Reading needs no allocator at all: [`Bundle::parse`] validates in place
31//! and borrows, so a device holds one buffer — the bundle as it arrived —
32//! and nothing else.
33//!
34//! # Versions
35//!
36//! The container is at version 2 and version 1 is not readable here. The
37//! two are different enough that supporting both would be a compatibility
38//! path used twice and then deleted, which is worth less than the card
39//! reader it would save.
40//!
41//! That awkwardness is inherent rather than accidental: **a bundle is
42//! parsed by the firmware already running and installs the firmware that
43//! replaces it**, so a board can never be sent a container its current
44//! build does not understand. Changing the format means reaching the
45//! board some other way once.
46//!
47//! # What an application still supplies
48//!
49//! **The transport and the reboot.** How a bundle arrives — an HTTP route,
50//! a job handed to another core, a serial command — is application shaped,
51//! and a crate that took it would be choosing the web framework.
52//!
53//! **The measurement.** What an update costs the card is the number these
54//! projects care about most, and collecting it here would mean depending on
55//! an async runtime for a clock and on whatever wrapper is counting device
56//! commands. The install reports its progress and the caller times it.
57
58// `std` under `cfg(test)` only, because the test harness needs it. The
59// bare-metal clippy pass in `make clippy-ota` builds without `--all-targets`
60// and so compiles this crate as the device sees it: `no_std`.
61#![cfg_attr(not(test), no_std)]
62#![deny(missing_docs)]
63
64#[cfg(feature = "alloc")]
65extern crate alloc;
66
67pub mod bundle;
68
69#[cfg(feature = "apply")]
70pub mod apply;
71
72#[cfg(feature = "apply")]
73pub mod measure;
74
75pub use bundle::{Bundle, Checksum, Entry, Error, Format, Role, checksum};
76
77#[cfg(feature = "alloc")]
78pub use bundle::encode;
79
80#[cfg(feature = "apply")]
81pub use apply::{Progress, Report, apply};