Apply relevant Rust API guidelines (#915)

* Derive more traits on public types in `image_format` module

* Don't leak `PartitionTable` type in public API

* Derive more traits on public types in `command` module

* fixups

* impl Hash for things

* fmt

* wording consistency

* feat: Udpate docstrings and derives

* feat: Implement C-CONV

* Derive more traits on public types in `flasher` module

* docs: Add missing module documentation

* docs: Udpate changelog

* docs: Fix changelog

---------

Co-authored-by: Scott Mabin <scott@mabez.dev>
Co-authored-by: Sergio Gasquez <sergio.gasquez@gmail.com>
This commit is contained in:
Jesse Braham 2025-07-01 10:35:32 +02:00 committed by GitHub
parent de3bbdc649
commit c35728a790
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 206 additions and 157 deletions

View File

@ -31,11 +31,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add a `check-app-descriptor` bool option to `ImageArgs` and add the flag to `flash` command (#872)
- `Connection::into_serial` to get the underlying port from the connection (#882)
- All methods on the now removed `Target` & `ReadEFuse`, `UsbOtg` and `RtcWdtReset` traits have been implemented directly on (#891)
- Update checks can now be skipped by setting the the `ESPFLASH_SKIP_UPDATE_CHECK` environment variable (#900)
- Update checks can now be skipped by setting the `ESPFLASH_SKIP_UPDATE_CHECK` environment variable (#900)
- `flash_write_size` and `max_ram_block_size` functions no longer take a connection parameter and return a Result type (#903)
- `DefaultProgressCallback` which implements `ProgressCallbacks` but all methods are no-ops (#904)
- Update checks can now be skipped by setting the `ESPFLASH_SKIP_UPDATE_CHECK` environment variable (#900)
- `ProgressCallbacks` now has a `verifying` method to notify when post-flash checksum checking has begun (#908)
- Implement `From<Connection> for Port` and both `From<Flasher> for Connection` and `Port` conversions (#915)
### Changed
@ -79,7 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed typos in error variant names (#782)
- Fix `read-flash` which didn't work with some lengths (#804)
- espflash can now flash an ESP32-S2 in download mode over USB (#813)
- Fixed a case where esplash transformed the firmware elf in a way that made it unbootable (#831)
- Fixed a case where espflash transformed the firmware ELF in a way that made it unbootable (#831)
- The app descriptor is now correctly placed in the front of the binary (#835)
- espflash now extracts the MMU page size from the app descriptor (#835)
- `ResetBeforeOperation` & `ResetAfterOperation` are now public, to allow the creation of a `Connection` (#895)

View File

@ -15,11 +15,7 @@ use espflash::{
*,
},
flasher::FlashSize,
image_format::{
ImageFormat,
ImageFormatKind,
idf::{check_idf_bootloader, parse_partition_table},
},
image_format::{ImageFormat, ImageFormatKind, idf::check_idf_bootloader},
logging::initialize_logger,
target::{Chip, XtalFrequency},
update::check_for_update,
@ -419,7 +415,7 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> {
monitor_args.elf = Some(build_ctx.artifact_path);
monitor(
flasher.into_connection().into_serial(),
flasher.into(),
Some(&elf_data),
pid,
monitor_args,

View File

@ -10,11 +10,7 @@ use espflash::{
*,
},
flasher::FlashSize,
image_format::{
ImageFormat,
ImageFormatKind,
idf::{check_idf_bootloader, parse_partition_table},
},
image_format::{ImageFormat, ImageFormatKind, idf::check_idf_bootloader},
logging::initialize_logger,
target::{Chip, XtalFrequency},
update::check_for_update,
@ -332,7 +328,7 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> {
monitor_args.elf = Some(args.image);
monitor(
flasher.into_connection().into_serial(),
flasher.into(),
Some(&elf_data),
pid,
monitor_args,

View File

@ -50,12 +50,7 @@ use crate::{
FlashSize,
Flasher,
},
image_format::{
ImageFormat,
ImageFormatKind,
Metadata,
idf::{IdfBootloaderFormat, parse_partition_table},
},
image_format::{ImageFormat, ImageFormatKind, Metadata, idf::IdfBootloaderFormat},
target::{Chip, ProgressCallbacks, XtalFrequency},
};
@ -660,7 +655,7 @@ pub fn serial_monitor(args: MonitorArgs, config: &Config) -> Result<()> {
}
monitor(
flasher.into_connection().into_serial(),
flasher.into(),
elf.as_deref(),
pid,
monitor_args,
@ -996,6 +991,13 @@ pub fn partition_table(args: PartitionTableArgs) -> Result<()> {
Ok(())
}
/// Parse a [PartitionTable] from the provided path
pub fn parse_partition_table(path: &Path) -> Result<PartitionTable, Error> {
let data = fs::read(path).map_err(|e| Error::FileOpenError(path.display().to_string(), e))?;
Ok(PartitionTable::try_from(data)?)
}
/// Pretty print a partition table
fn pretty_print(table: PartitionTable) {
let mut pretty = Table::new();
@ -1162,7 +1164,7 @@ pub fn write_bin(args: WriteBinArgs, config: &Config) -> Result<()> {
monitor_args.monitor_baud = 74_880;
}
monitor(
flasher.into_connection().into_serial(),
flasher.into(),
None,
pid,
monitor_args,

View File

@ -3,6 +3,7 @@
use std::{io::Write, mem::size_of, time::Duration};
use bytemuck::{Pod, Zeroable, bytes_of};
use serde::{Deserialize, Serialize};
use strum::Display;
use crate::{
@ -30,7 +31,7 @@ const SYNC_FRAME: [u8; 36] = [
/// Types of commands that can be sent to a target device
///
/// <https://docs.espressif.com/projects/esptool/en/latest/esp32c3/advanced-topics/serial-protocol.html#supported-by-stub-loader-and-rom-loader>
#[derive(Copy, Clone, Debug, Display)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Deserialize, Serialize)]
#[non_exhaustive]
#[repr(u8)]
pub enum CommandType {
@ -95,7 +96,7 @@ pub enum CommandType {
}
/// The value of a command response.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub enum CommandResponseValue {
/// A 32-bit value.
ValueU32(u32),
@ -154,7 +155,7 @@ impl TryInto<Vec<u8>> for CommandResponseValue {
}
/// A response from a target device following a command.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct CommandResponse {
/// The response byte.
pub resp: u8,
@ -215,7 +216,7 @@ impl CommandType {
/// Available commands
///
/// See <https://docs.espressif.com/projects/esptool/en/latest/esp32c6/advanced-topics/serial-protocol.html#commands>
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[non_exhaustive]
pub enum Command<'a> {
/// Begin flash download

View File

@ -65,7 +65,7 @@ pub struct Connection {
}
impl Connection {
/// Create a new connection with a target device.
/// Creates a new connection with a target device.
pub fn new(
serial: Port,
port_info: UsbPortInfo,
@ -84,7 +84,7 @@ impl Connection {
}
}
/// Initialize a connection with a device.
/// Initializes a connection with a device.
pub fn begin(&mut self) -> Result<(), Error> {
let port_name = self.serial.name().unwrap_or_default();
let reset_sequence = construct_reset_strategy_sequence(
@ -109,7 +109,7 @@ impl Connection {
)))
}
/// Try to connect to a device.
/// Connects to a device.
fn connect_attempt(&mut self, reset_strategy: &dyn ResetStrategy) -> Result<(), Error> {
// If we're doing no_sync, we're likely communicating as a pass through
// with an intermediate device to the ESP32
@ -187,7 +187,7 @@ impl Connection {
)))
}
/// Try to sync with the device for a given timeout.
/// Syncs with a device.
pub(crate) fn sync(&mut self) -> Result<(), Error> {
self.with_timeout(CommandType::Sync.timeout(), |connection| {
connection.command(Command::Sync)?;
@ -221,14 +221,14 @@ impl Connection {
Ok(())
}
/// Reset the device.
/// Resets the device.
pub fn reset(&mut self) -> Result<(), Error> {
reset_after_flash(&mut self.serial, self.port_info.pid)?;
Ok(())
}
/// Reset the device taking into account the reset after argument.
/// Resets the device taking into account the reset after argument.
pub fn reset_after(&mut self, is_stub: bool, chip: Chip) -> Result<(), Error> {
let pid = self.usb_pid();
@ -291,7 +291,7 @@ impl Connection {
}
}
/// Reset the device to flash mode.
/// Resets the device to flash mode.
pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
if self.is_using_usb_serial_jtag() {
UsbJtagSerialReset.reset(&mut self.serial)
@ -308,25 +308,25 @@ impl Connection {
}
}
/// Set timeout for the serial port.
/// Sets the timeout for the serial port.
pub fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
self.serial.set_timeout(timeout)?;
Ok(())
}
/// Set baud rate for the serial port.
/// Sets the baud rate for the serial port.
pub fn set_baud(&mut self, baud: u32) -> Result<(), Error> {
self.serial.set_baud_rate(baud)?;
self.baud = baud;
Ok(())
}
/// Get the current baud rate of the serial port.
/// Returns the current baud rate of the serial port.
pub fn baud(&self) -> Result<u32, Error> {
Ok(self.serial.baud_rate()?)
}
/// Run a command with a timeout defined by the command type.
/// Runs a command with a timeout defined by the command type.
pub fn with_timeout<T, F>(&mut self, timeout: Duration, mut f: F) -> Result<T, Error>
where
F: FnMut(&mut Connection) -> Result<T, Error>,
@ -346,7 +346,7 @@ impl Connection {
result
}
/// Read the response from a serial port.
/// Reads the response from a serial port.
pub fn read_flash_response(&mut self) -> Result<Option<CommandResponse>, Error> {
let mut response = Vec::new();
@ -369,7 +369,7 @@ impl Connection {
Ok(Some(header))
}
/// Read the response from a serial port.
/// Reads the response from a serial port.
pub fn read_response(&mut self) -> Result<Option<CommandResponse>, Error> {
match self.read(10)? {
None => Ok(None),
@ -432,7 +432,7 @@ impl Connection {
}
}
/// Write raw data to the serial port.
/// Writes raw data to the serial port.
pub fn write_raw(&mut self, data: u32) -> Result<(), Error> {
let mut binding = Box::new(&mut self.serial);
let serial = binding.as_mut();
@ -445,7 +445,7 @@ impl Connection {
Ok(())
}
/// Write a command to the serial port.
/// Writes a command to the serial port.
pub fn write_command(&mut self, command: Command<'_>) -> Result<(), Error> {
debug!("Writing command: {command:02x?}");
let mut binding = Box::new(&mut self.serial);
@ -460,7 +460,7 @@ impl Connection {
Ok(())
}
/// Write a command and reads the response.
/// Writes a command and reads the response.
pub fn command(&mut self, command: Command<'_>) -> Result<CommandResponseValue, Error> {
let ty = command.command_type();
self.write_command(command).for_command(ty)?;
@ -495,7 +495,7 @@ impl Connection {
)))
}
/// Read a register command with a timeout.
/// Reads a register command with a timeout.
pub fn read_reg(&mut self, addr: u32) -> Result<u32, Error> {
let resp = self.with_timeout(CommandType::ReadReg.timeout(), |connection| {
connection.command(Command::ReadReg { address: addr })
@ -504,7 +504,7 @@ impl Connection {
resp.try_into()
}
/// Write a register command with a timeout.
/// Writes a register command with a timeout.
pub fn write_reg(&mut self, addr: u32, value: u32, mask: Option<u32>) -> Result<(), Error> {
self.with_timeout(CommandType::WriteReg.timeout(), |connection| {
connection.command(Command::WriteReg {
@ -517,7 +517,7 @@ impl Connection {
Ok(())
}
/// Read a register command with a timeout.
/// Reads a register command with a timeout.
pub(crate) fn read(&mut self, len: usize) -> Result<Option<Vec<u8>>, Error> {
let mut tmp = Vec::with_capacity(1024);
loop {
@ -528,18 +528,18 @@ impl Connection {
}
}
/// Flush the serial port.
/// Flushes the serial port.
pub fn flush(&mut self) -> Result<(), Error> {
self.serial.flush()?;
Ok(())
}
/// Turn a serial port into a [Port].
/// Turns a serial port into a [Port].
pub fn into_serial(self) -> Port {
self.serial
}
/// Get the USB PID of the serial port.
/// Returns the USB PID of the serial port.
pub fn usb_pid(&self) -> u16 {
self.port_info.pid
}
@ -559,7 +559,7 @@ impl Connection {
self.before_operation
}
/// Detect which chip is connected to this connection
/// Detects which chip is connected to this connection.
pub fn detect_chip(
&mut self,
use_stub: bool,
@ -580,15 +580,24 @@ impl Connection {
}
}
impl From<Connection> for Port {
fn from(conn: Connection) -> Self {
conn.into_serial()
}
}
mod encoder {
use std::io::Write;
use serde::Serialize;
const END: u8 = 0xC0;
const ESC: u8 = 0xDB;
const ESC_END: u8 = 0xDC;
const ESC_ESC: u8 = 0xDD;
/// Encoder for the SLIP protocol.
#[derive(Debug, PartialEq, Eq, Serialize, Hash)]
pub struct SlipEncoder<'a, W: Write> {
writer: &'a mut W,
len: usize,
@ -601,7 +610,7 @@ mod encoder {
Ok(Self { writer, len })
}
/// Finish the encoding.
/// Finishes the encoding.
pub fn finish(mut self) -> std::io::Result<usize> {
self.len += self.writer.write(&[END])?;
Ok(self.len)

View File

@ -1,3 +1,7 @@
//! Reset strategies for resetting a target device.
//!
//! This module defines the traits and types used for resetting a target device.
// Most of this module is copied from `esptool.py`:
// https://github.com/espressif/esptool/blob/a8586d0/esptool/reset.py
@ -8,6 +12,7 @@ use std::{thread::sleep, time::Duration};
#[cfg(unix)]
use libc::ioctl;
use log::debug;
use serde::{Deserialize, Serialize};
use serialport::SerialPort;
use strum::{Display, EnumIter, EnumString, VariantNames};
@ -18,12 +23,12 @@ use crate::{
flasher::FLASH_WRITE_SIZE,
};
/// Default time to wait before releasing the boot pin after a reset
/// Default time to wait before releasing the boot pin after a reset.
const DEFAULT_RESET_DELAY: u64 = 50; // ms
/// Amount of time to wait if the default reset delay does not work
/// Amount of time to wait if the default reset delay does not work.
const EXTRA_RESET_DELAY: u64 = 500; // ms
/// Some strategy for resting a target device
/// Reset strategies for resetting a target device.
pub trait ResetStrategy {
fn reset(&self, serial_port: &mut Port) -> Result<(), Error>;
@ -74,7 +79,7 @@ pub trait ResetStrategy {
}
/// Classic reset sequence, sets DTR and RTS sequentially.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Serialize, Hash, Deserialize)]
pub struct ClassicReset {
delay: u64,
}
@ -124,7 +129,7 @@ impl ResetStrategy for ClassicReset {
/// UNIX-only reset sequence with custom implementation, which allows setting
/// DTR and RTS lines at the same time.
#[cfg(unix)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Serialize, Hash, Deserialize)]
pub struct UnixTightReset {
delay: u64,
}
@ -170,7 +175,7 @@ impl ResetStrategy for UnixTightReset {
/// Custom reset sequence, which is required when the device is connecting via
/// its USB-JTAG-Serial peripheral.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Serialize, Hash, Deserialize)]
pub struct UsbJtagSerialReset;
impl ResetStrategy for UsbJtagSerialReset {
@ -200,7 +205,7 @@ impl ResetStrategy for UsbJtagSerialReset {
}
}
/// Reset the target device
/// Resets the target device.
pub fn reset_after_flash(serial: &mut Port, pid: u16) -> Result<(), serialport::Error> {
sleep(Duration::from_millis(100));
@ -227,7 +232,7 @@ pub fn reset_after_flash(serial: &mut Port, pid: u16) -> Result<(), serialport::
Ok(())
}
/// Reset sequence for hard resetting the chip.
/// Performs a hard reset of the chip.
pub fn hard_reset(serial_port: &mut Port, pid: u16) -> Result<(), Error> {
debug!("Using HardReset reset strategy");
@ -239,7 +244,7 @@ pub fn hard_reset(serial_port: &mut Port, pid: u16) -> Result<(), Error> {
Ok(())
}
/// Perform a soft reset of the device.
/// Performs a soft reset of the device.
pub fn soft_reset(
connection: &mut Connection,
stay_in_bootloader: bool,
@ -295,7 +300,7 @@ pub fn soft_reset(
Ok(())
}
/// Construct a sequence of reset strategies based on the OS and chip.
/// Constructs a sequence of reset strategies based on the OS and chip.
///
/// Returns a [Vec] containing one or more reset strategies to be attempted
/// sequentially.
@ -331,7 +336,19 @@ pub fn construct_reset_strategy_sequence(
/// Enum to represent different reset behaviors before an operation.
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Display, EnumIter, EnumString, VariantNames,
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
Display,
EnumIter,
EnumString,
VariantNames,
Hash,
Serialize,
Deserialize,
)]
#[non_exhaustive]
#[strum(serialize_all = "lowercase")]
@ -346,14 +363,26 @@ pub enum ResetBeforeOperation {
/// Skips DTR/RTS control signal assignments and also skips the serial
/// synchronization command.
NoResetNoSync,
/// Reset sequence for USB-JTAG-Serial peripheral
/// Reset sequence for USB-JTAG-Serial peripheral.
UsbReset,
}
/// Enum to represent different reset behaviors after an operation.
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Display, EnumIter, EnumString, VariantNames,
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
Display,
EnumIter,
EnumString,
VariantNames,
Hash,
Serialize,
Deserialize,
)]
#[non_exhaustive]
pub enum ResetAfterOperation {

View File

@ -19,6 +19,8 @@ use object::{Endianness, read::elf::ElfFile32 as ElfFile};
use serde::{Deserialize, Serialize};
use strum::{Display, EnumIter, IntoEnumIterator, VariantNames};
#[cfg(feature = "serialport")]
use crate::connection::Port;
#[cfg(feature = "serialport")]
use crate::target::{DefaultProgressCallback, ProgressCallbacks};
use crate::{
@ -52,7 +54,7 @@ pub(crate) const FLASH_SECTOR_SIZE: usize = 0x1000;
pub(crate) const FLASH_WRITE_SIZE: usize = 0x400;
/// Security Info Response containing
#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct SecurityInfo {
/// 32 bits flags
pub flags: u32,
@ -284,7 +286,9 @@ impl FlashFrequency {
/// Supported flash modes
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[derive(Copy, Clone, Debug, Default, VariantNames, Serialize, Deserialize)]
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Hash, VariantNames, Serialize, Deserialize,
)]
#[non_exhaustive]
#[strum(serialize_all = "lowercase")]
#[serde(rename_all = "lowercase")]
@ -305,17 +309,18 @@ pub enum FlashMode {
/// Note that not all sizes are supported by each target device.
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Display,
VariantNames,
EnumIter,
Serialize,
Deserialize,
Serialize,
)]
#[non_exhaustive]
#[repr(u8)]
@ -436,7 +441,7 @@ impl FromStr for FlashSize {
}
/// Flash settings to use when flashing a device.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FlashSettings {
/// Flash mode.
@ -470,7 +475,7 @@ impl FlashSettings {
}
/// Flash data and configuration
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[non_exhaustive]
pub struct FlashData {
/// Flash settings.
@ -507,7 +512,7 @@ impl FlashData {
/// Parameters of the attached SPI flash chip (sizes, etc).
///
/// See: <https://github.com/espressif/esptool/blob/da31d9d/esptool.py#L655>
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[repr(C)]
pub struct SpiSetParams {
/// Flash chip ID
@ -551,7 +556,7 @@ impl SpiSetParams {
}
/// Parameters for attaching to a target devices SPI flash
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[repr(C)]
pub struct SpiAttachParams {
clk: u8,
@ -603,7 +608,7 @@ impl SpiAttachParams {
}
/// Information about the connected device
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct DeviceInfo {
/// The chip being used
pub chip: Chip,
@ -1379,3 +1384,19 @@ fn detect_sdm(connection: &mut Connection) {
connection.secure_download_mode = true;
}
}
#[cfg(feature = "serialport")]
impl From<Flasher> for Connection {
fn from(flasher: Flasher) -> Self {
flasher.into_connection()
}
}
#[cfg(feature = "serialport")]
impl From<Flasher> for Port {
fn from(flasher: Flasher) -> Self {
// Enables `monitor(flasher.into(), …)`
let connection: Connection = flasher.into();
connection.into()
}
}

View File

@ -1,3 +1,5 @@
//! Stub loader module.
use std::time::Duration;
use base64::{Engine as _, engine::general_purpose};
@ -7,7 +9,7 @@ use crate::target::Chip;
/// Flash stub object (deserialized from TOML, converted from JSON as used by
/// `esptool.py`)
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FlashStub {
/// Entry point (address)
entry: u32,

View File

@ -23,6 +23,7 @@ use object::{
ObjectSymbol,
read::elf::ElfFile32 as ElfFile,
};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use super::{Segment, ram_segments, rom_segments};
@ -242,7 +243,7 @@ impl AppDescriptor {
/// Image format for ESP32 family chips using the second-stage bootloader from
/// ESP-IDF
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct IdfBootloaderFormat<'a> {
boot_addr: u32,
bootloader: Cow<'a, [u8]>,
@ -266,7 +267,9 @@ impl<'a> IdfBootloaderFormat<'a> {
let elf = ElfFile::parse(elf_data)?;
let partition_table = if let Some(partition_table_path) = partition_table_path {
parse_partition_table(partition_table_path)?
let data = fs::read(partition_table_path)
.map_err(|e| Error::FileOpenError(partition_table_path.display().to_string(), e))?;
PartitionTable::try_from(data)?
} else {
default_partition_table(
flash_data.chip,
@ -787,13 +790,6 @@ fn update_checksum(data: &[u8], mut checksum: u8) -> u8 {
checksum
}
/// Parse a [PartitionTable] from the provided path
pub fn parse_partition_table(path: &Path) -> Result<PartitionTable, Error> {
let data = fs::read(path).map_err(|e| Error::FileOpenError(path.display().to_string(), e))?;
Ok(PartitionTable::try_from(data)?)
}
fn encode_hex<T>(data: T) -> String
where
T: AsRef<[u8]>,

View File

@ -1,9 +1,12 @@
//! Image format metadata.
use std::{collections::HashMap, error::Error};
use object::{File, Object, ObjectSection, ObjectSymbol};
use serde::{Deserialize, Serialize};
/// Image format metadata.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Metadata {
symbols: HashMap<String, Vec<u8>>,
}

View File

@ -37,7 +37,7 @@ pub enum ImageFormatKind {
}
/// Binary application image format data
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[non_exhaustive]
pub enum ImageFormat<'a> {
/// ESP-IDF application image format
@ -76,7 +76,7 @@ impl<'a> From<IdfBootloaderFormat<'a>> for ImageFormat<'a> {
}
/// A segment of code from the source ELF
#[derive(Default, Clone, Eq)]
#[derive(Default, Clone, Eq, Deserialize, Serialize)]
pub struct Segment<'a> {
/// Base address of the code segment
pub addr: u32,

View File

@ -30,7 +30,8 @@ pub struct EfuseField {
}
impl EfuseField {
const fn new(block: u32, word: u32, bit_start: u32, bit_count: u32) -> Self {
/// Creates a new eFuse field definition.
pub const fn new(block: u32, word: u32, bit_start: u32, bit_count: u32) -> Self {
Self {
block,
word,

View File

@ -1,3 +1,8 @@
//! ESP32 flash target module.
//!
//! This module defines the traits and types used for flashing operations on a
//! target device's flash memory.
use std::io::Write;
use flate2::{
@ -22,7 +27,7 @@ use crate::{
};
/// Applications running from an ESP32's (or variant's) flash
#[derive(Debug)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Esp32Target {
chip: Chip,
spi_attach_params: SpiAttachParams,

View File

@ -1,8 +1,17 @@
pub use self::{esp32::Esp32Target, ram::RamTarget};
use crate::{Error, connection::Connection, image_format::Segment};
//! Flash target module.
//!
//! This module defines the traits and types used for flashing operations on a
//! target device.
//!
//! This module include an `FlashTarget` trait impl for `Esp32Target` and
//! `RamTarget`, enabling the writing of firmware images to the target device's
//! flash memory or static memory (SRAM). It also provides a `ProgressCallbacks`
//! trait which allows for progress updates during the flashing process.`
mod esp32;
mod ram;
pub use self::{esp32::Esp32Target, ram::RamTarget};
use crate::{Error, connection::Connection, image_format::Segment};
/// Operations for interacting with a flash target.
pub trait FlashTarget {

View File

@ -1,3 +1,8 @@
//! RAM target module.
//!
//! This module defines the traits and types used for flashing operations on a
//! target device's RAM.
use crate::{Error, image_format::Segment, target::MAX_RAM_BLOCK_SIZE};
#[cfg(feature = "serialport")]
use crate::{
@ -8,7 +13,7 @@ use crate::{
};
/// Applications running in the target device's RAM.
#[derive(Debug)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct RamTarget {
entry: Option<u32>,
block_size: usize,

View File

@ -62,8 +62,22 @@ pub enum XtalFrequency {
/// All supported devices
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, EnumIter, EnumString, VariantNames)]
#[derive(
Debug,
Clone,
Copy,
Hash,
PartialEq,
Eq,
Display,
EnumIter,
EnumString,
VariantNames,
Deserialize,
Serialize,
)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum Chip {
/// ESP32
@ -305,7 +319,7 @@ impl Chip {
}
}
/// Returns the default flash frequency for the [Chip]
/// Returns the default flash frequency for the [Chip].
pub fn default_flash_frequency(&self) -> FlashFrequency {
match self {
Chip::Esp32
@ -320,7 +334,7 @@ impl Chip {
}
}
/// Returns the default crystal frequency for the [Chip]
/// Returns the default crystal frequency for the [Chip].
pub fn default_xtal_frequency(&self) -> XtalFrequency {
match self {
Chip::Esp32c5 => XtalFrequency::_48Mhz,
@ -367,7 +381,8 @@ impl Chip {
}
}
/// Returns the offset of BLOCK0 relative to the eFuse base register address
/// Returns the offset of BLOCK0 relative to the eFuse base register
/// address.
pub fn block0_offset(&self) -> u32 {
match self {
Chip::Esp32 => 0x0,
@ -382,7 +397,7 @@ impl Chip {
}
}
/// Returns the size of the specified block for the implementing target
/// Returns the size of the specified block for the implementing target.
/// device
pub fn block_size(&self, block: usize) -> u32 {
match self {
@ -398,7 +413,8 @@ impl Chip {
}
}
/// Given an active connection, read the specified field of the eFuse region
/// Given an active connection, read the specified field of the eFuse
/// region.
#[cfg(feature = "serialport")]
pub fn read_efuse(&self, connection: &mut Connection, field: EfuseField) -> Result<u32, Error> {
let mask = if field.bit_count == 32 {
@ -416,7 +432,7 @@ impl Chip {
}
/// Read the raw word in the specified eFuse block, without performing any
/// bit-shifting or masking of the read value
/// bit-shifting or masking of the read value.
#[cfg(feature = "serialport")]
pub fn read_efuse_raw(
&self,
@ -436,7 +452,7 @@ impl Chip {
connection.read_reg(addr)
}
/// Is the provided address `addr` in flash?
/// Returns whether the provided address `addr` in flash.
pub fn addr_is_flash(&self, addr: u32) -> bool {
match self {
Chip::Esp32 => {
@ -505,7 +521,7 @@ impl Chip {
}
}
/// Enumerate the chip's features, read from eFuse
/// Enumerate the chip's features.
#[cfg(feature = "serialport")]
pub fn chip_features(&self, connection: &mut Connection) -> Result<Vec<&str>, Error> {
match self {
@ -828,43 +844,12 @@ impl Chip {
mosi_length_offset: Some(0x28),
miso_length_offset: Some(0x2c),
},
Chip::Esp32c2 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
usr2_offset: 0x20,
w0_offset: 0x58,
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
Chip::Esp32c3 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
usr2_offset: 0x20,
w0_offset: 0x58,
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
Chip::Esp32c5 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
usr2_offset: 0x20,
w0_offset: 0x58,
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
Chip::Esp32c6 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
usr2_offset: 0x20,
w0_offset: 0x58,
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
Chip::Esp32h2 => SpiRegisters {
Chip::Esp32c2
| Chip::Esp32c3
| Chip::Esp32c5
| Chip::Esp32c6
| Chip::Esp32h2
| Chip::Esp32s3 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
@ -891,15 +876,6 @@ impl Chip {
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
Chip::Esp32s3 => SpiRegisters {
base: 0x6000_3000,
usr_offset: 0x18,
usr1_offset: 0x1c,
usr2_offset: 0x20,
w0_offset: 0x58,
mosi_length_offset: Some(0x24),
miso_length_offset: Some(0x28),
},
}
}
@ -923,10 +899,8 @@ impl Chip {
self.supported_build_targets().contains(&target)
}
// Helper methods for chip-specific functionality
#[cfg(feature = "serialport")]
/// Return the package version based on the eFuses for ESP32
/// Returns the package version based on the eFuses for ESP32
fn esp32_package_version(&self, connection: &mut Connection) -> Result<u32, Error> {
let word3 = self.read_efuse_raw(connection, 0, 3)?;
@ -937,31 +911,31 @@ impl Chip {
}
#[cfg(feature = "serialport")]
/// Return the block2 version based on eFuses for ESP32-S2
/// Returns the block2 version based on eFuses for ESP32-S2
fn esp32s2_block2_version(&self, connection: &mut Connection) -> Result<u32, Error> {
self.read_efuse(connection, efuse::esp32s2::BLK_VERSION_MINOR)
}
#[cfg(feature = "serialport")]
/// Return the flash version based on eFuses for ESP32-S2
/// Returns the flash version based on eFuses for ESP32-S2
fn esp32s2_flash_version(&self, connection: &mut Connection) -> Result<u32, Error> {
self.read_efuse(connection, efuse::esp32s2::FLASH_VERSION)
}
#[cfg(feature = "serialport")]
/// Return the PSRAM version based on eFuses for ESP32-S2
/// Returns the PSRAM version based on eFuses for ESP32-S2
fn esp32s2_psram_version(&self, connection: &mut Connection) -> Result<u32, Error> {
self.read_efuse(connection, efuse::esp32s2::PSRAM_VERSION)
}
#[cfg(feature = "serialport")]
/// Return the major BLK version based on eFuses for ESP32-S3
/// Returns the major BLK version based on eFuses for ESP32-S3
fn esp32s3_blk_version_major(&self, connection: &mut Connection) -> Result<u32, Error> {
self.read_efuse(connection, efuse::esp32s3::BLK_VERSION_MAJOR)
}
#[cfg(feature = "serialport")]
/// Return the minor BLK version based on eFuses for ESP32-S3
/// Returns the minor BLK version based on eFuses for ESP32-S3
fn esp32s3_blk_version_minor(&self, connection: &mut Connection) -> Result<u32, Error> {
self.read_efuse(connection, efuse::esp32s3::BLK_VERSION_MINOR)
}
@ -989,7 +963,7 @@ impl TryFrom<u16> for Chip {
}
/// SPI register addresses
#[derive(Debug)]
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
pub struct SpiRegisters {
base: u32,
usr_offset: u32,