flashthing/lib.rs
1//! # flashthing
2//!
3//! A Rust library for flashing custom firmware to the Spotify Car Thing device.
4//!
5//! This library provides a comprehensive toolset for interacting with the Amlogic
6//! SoC on the Spotify Car Thing, allowing users to flash custom firmware, read/write
7//! partitions, and execute other low-level operations on the device.
8//!
9//! ## Main Features
10//!
11//! - Device detection and mode switching
12//! - Memory reading and writing
13//! - Partition management and restoration
14//! - Custom firmware flashing via JSON configuration
15//! - Progress reporting and event callbacks
16//! - Error handling and recovery (including unbricking)
17//!
18//! ## Usage Example
19//!
20//! ```no_run
21//! use flashthing::{AmlogicSoC, Flasher, Event};
22//! use std::{path::PathBuf, sync::Arc};
23//!
24//! // Set up USB access for the device (on Linux, but no-op for other OSes so fine to call)
25//! AmlogicSoC::host_setup().unwrap();
26//!
27//! // Create a callback to handle events
28//! let callback = Arc::new(|event: Event| {
29//! match event {
30//! Event::FlashProgress(progress) => {
31//! println!("Progress: {:.1}%, ETA: {:.1}s", progress.percent, progress.eta / 1000.0);
32//! },
33//! Event::Step(step_index, step) => {
34//! println!("Step {}: {:?}", step_index, step);
35//! },
36//! Event::DeviceMode(mode) => {
37//! println!("Device mode: {:?}", mode);
38//! },
39//! _ => {}
40//! }
41//! });
42//!
43//! // Flash firmware from a directory
44//! let mut flasher = Flasher::from_directory(
45//! PathBuf::from("/path/to/firmware"),
46//! Some(callback.clone())
47//! ).unwrap();
48//!
49//! // Start the flashing process
50//! flasher.flash().unwrap();
51//! ```
52//!
53//! ## Device Connection
54//!
55//! To use this library, the Spotify Car Thing must be connected via USB and placed
56//! in USB Mode by holding buttons 1 & 4 during power-on.
57//!
58//! ## Configuration Format
59//!
60//! The flashing process is guided by a `meta.json` file that specifies a sequence
61//! of operations to perform. See the schema documentation for details on the format.
62
63mod aml;
64mod flash;
65mod partitions;
66mod setup;
67
68/// Configuration types for the flashing process
69pub mod config;
70
71use std::sync::Arc;
72
73pub use aml::*;
74use config::FlashStep;
75pub use flash::{FlashProgress, Flasher};
76
77/// Callback type for receiving flash events
78///
79/// This is used to handle events during the flashing process, such as
80/// progress updates, device connection status, and step transitions.
81pub type Callback = Arc<dyn Fn(Event) + Send + Sync>;
82
83/// Events emitted during the flashing process
84///
85/// These events are sent to the callback function to notify about
86/// the progress and status of the flashing procedure.
87#[derive(Debug)]
88pub enum Event {
89 /// Indicates the tool is searching for a connected device
90 FindingDevice,
91 /// Indicates the device was found and reports its current mode
92 DeviceMode(DeviceMode),
93 /// Indicates the tool is attempting to connect to the device
94 Connecting,
95 /// Indicates a successful connection to the device
96 Connected,
97 /// Indicates the BL2 boot process has started
98 Bl2Boot,
99 /// Indicates the device is being reset
100 Resetting,
101 /// Indicates movement to a new flashing step
102 ///
103 /// Parameters: (step_index, step_details)
104 Step(usize, FlashStep),
105 /// Provides progress information for the current flashing step
106 FlashProgress(FlashProgress),
107}
108
109/// Result type used throughout the crate
110pub type Result<T> = std::result::Result<T, Error>;
111
112/// Error types that can occur during the flashing process
113#[derive(thiserror::Error, Debug)]
114pub enum Error {
115 /// Error from the USB subsystem
116 #[error("USB error: {0}")]
117 UsbError(#[from] rusb::Error),
118
119 /// I/O related error
120 #[error("IO error: {0}")]
121 IoError(#[from] std::io::Error),
122
123 /// Error converting slices
124 #[error("slice conversion error: {0}")]
125 Bytes(#[from] std::array::TryFromSliceError),
126
127 /// Error when an operation is invalid in the current context
128 #[error("Invalid operation: {0}")]
129 InvalidOperation(String),
130
131 /// UTF-8 conversion error
132 #[error("UTF8 conversion error: {0}")]
133 Utf8Error(#[from] std::string::FromUtf8Error),
134
135 /// Error when the device is not found
136 #[error("device not found!")]
137 NotFound,
138
139 /// Error when the device is in an incompatible mode
140 #[error("device in wrong mode!")]
141 WrongMode,
142
143 /// Error when a bulk command fails
144 #[error("bulkcmd failed: {0}")]
145 BulkCmdFailed(String),
146
147 /// Error when the meta.json version is not supported
148 #[error("unsupported `meta.json` version: {0}")]
149 UnsupportedVersion(usize),
150
151 /// Error when a feature in meta.json is not supported
152 #[error("unsupported `meta.json` feature: {:?}", 0)]
153 UnsupportedFeature(config::FlashStep),
154
155 /// JSON deserialization error
156 #[error("failed to deserialize json: {0}")]
157 Json(#[from] serde_json::Error),
158
159 /// Error when a path expected to be a directory is not
160 #[error("{0} is not a directory")]
161 NotDir(std::path::PathBuf),
162
163 /// Error when the required meta.json file is not found
164 #[error("could not find required `meta.json` at {0}")]
165 NoMeta(std::path::PathBuf),
166
167 /// Error when a required file is missing
168 #[error("required file does not exist at {0}")]
169 FileMissing(std::path::PathBuf),
170
171 /// Zip archive error
172 #[error("zip error: {0}")]
173 Zip(#[from] zip::result::ZipError),
174
175 #[cfg(target_os = "linux")]
176 /// whoami error
177 #[error("whoami error: {0}")]
178 Whoami(#[from] whoami::Error),
179}
180
181const SUPPORTED_META_VERSION_MIN: usize = 1;
182const SUPPORTED_META_VERSION_MAX: usize = 2;
183
184const BL2_BIN: &[u8] = include_bytes!("../resources/superbird.bl2.encrypted.bin");
185const BOOTLOADER_BIN: &[u8] = include_bytes!("../resources/superbird.bootloader.img");
186const UNBRICK_BIN_ZIP: &[u8] = include_bytes!("../resources/unbrick.bin.zip");
187const STOCK_META: &[u8] = include_bytes!("../resources/stock-meta.json");
188
189const VENDOR_ID: u16 = 0x1b8e;
190const PRODUCT_ID: u16 = 0xc003;
191
192#[allow(dead_code)]
193const VENDOR_ID_BOOTED: u16 = 0x1d6b;
194#[allow(dead_code)]
195const PRODUCT_ID_BOOTED: u16 = 0x1014;
196
197const ADDR_BL2: u32 = 0xfffa0000;
198const TRANSFER_SIZE_THRESHOLD: usize = 8 * 1024 * 1024;
199const ADDR_TMP: u32 = 0x1080000;
200
201// all requests
202const REQ_WRITE_MEM: u8 = 0x01;
203const REQ_READ_MEM: u8 = 0x02;
204#[allow(dead_code)]
205const REQ_FILL_MEM: u8 = 0x03;
206#[allow(dead_code)]
207const REQ_MODIFY_MEM: u8 = 0x04;
208const REQ_RUN_IN_ADDR: u8 = 0x05;
209#[allow(dead_code)]
210const REQ_WRITE_AUX: u8 = 0x06;
211#[allow(dead_code)]
212const REQ_READ_AUX: u8 = 0x07;
213
214const REQ_WR_LARGE_MEM: u8 = 0x11;
215#[allow(dead_code)]
216const REQ_RD_LARGE_MEM: u8 = 0x12;
217const REQ_IDENTIFY_HOST: u8 = 0x20;
218
219#[allow(dead_code)]
220const REQ_TPL_CMD: u8 = 0x30;
221#[allow(dead_code)]
222const REQ_TPL_STAT: u8 = 0x31;
223
224#[allow(dead_code)]
225const REQ_WRITE_MEDIA: u8 = 0x32;
226#[allow(dead_code)]
227const REQ_READ_MEDIA: u8 = 0x33;
228
229const REQ_BULKCMD: u8 = 0x34;
230
231#[allow(dead_code)]
232const REQ_PASSWORD: u8 = 0x35;
233#[allow(dead_code)]
234const REQ_NOP: u8 = 0x36;
235
236const REQ_GET_AMLC: u8 = 0x50;
237const REQ_WRITE_AMLC: u8 = 0x60;
238
239const FLAG_KEEP_POWER_ON: u32 = 0x10;
240
241const AMLC_AMLS_BLOCK_LENGTH: usize = 0x200;
242const AMLC_MAX_BLOCK_LENGTH: usize = 0x4000;
243const AMLC_MAX_TRANSFER_LENGTH: usize = 65536;
244
245#[allow(dead_code)]
246const WRITE_MEDIA_CHEKSUM_ALG_NONE: u16 = 0x00ee;
247#[allow(dead_code)]
248const WRITE_MEDIA_CHEKSUM_ALG_ADDSUM: u16 = 0x00ef;
249#[allow(dead_code)]
250const WRITE_MEDIA_CHEKSUM_ALG_CRC32: u16 = 0x00f0;
251
252// Constants for partition operations
253const PART_SECTOR_SIZE: usize = 512; // bytes, size of sectors used in partition table
254const TRANSFER_BLOCK_SIZE: usize = 8 * PART_SECTOR_SIZE; // 4KB data transferred into memory one block at a time