diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ae7b4b..9cfe46d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 for Port` and both `From 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) diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index 8683865..8186dbe 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -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, diff --git a/espflash/src/bin/espflash.rs b/espflash/src/bin/espflash.rs index 482d2cc..dbf0698 100644 --- a/espflash/src/bin/espflash.rs +++ b/espflash/src/bin/espflash.rs @@ -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, diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index a8d9109..b564ce3 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -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 { + 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, diff --git a/espflash/src/command.rs b/espflash/src/command.rs index 2f36565..2a70280 100644 --- a/espflash/src/command.rs +++ b/espflash/src/command.rs @@ -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 /// /// -#[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> 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 -#[derive(Copy, Clone, Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] #[non_exhaustive] pub enum Command<'a> { /// Begin flash download diff --git a/espflash/src/connection/mod.rs b/espflash/src/connection/mod.rs index b9066da..03d09a7 100644 --- a/espflash/src/connection/mod.rs +++ b/espflash/src/connection/mod.rs @@ -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 { 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(&mut self, timeout: Duration, mut f: F) -> Result where F: FnMut(&mut Connection) -> Result, @@ -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, 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, 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 { 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 { 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) -> 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>, 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 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 { self.len += self.writer.write(&[END])?; Ok(self.len) diff --git a/espflash/src/connection/reset.rs b/espflash/src/connection/reset.rs index 7b9db2a..f225cb4 100644 --- a/espflash/src/connection/reset.rs +++ b/espflash/src/connection/reset.rs @@ -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 { diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index d0ceb98..8cb0356 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -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: -#[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 for Connection { + fn from(flasher: Flasher) -> Self { + flasher.into_connection() + } +} + +#[cfg(feature = "serialport")] +impl From for Port { + fn from(flasher: Flasher) -> Self { + // Enables `monitor(flasher.into(), …)` + let connection: Connection = flasher.into(); + connection.into() + } +} diff --git a/espflash/src/flasher/stubs.rs b/espflash/src/flasher/stubs.rs index d652df1..2955846 100644 --- a/espflash/src/flasher/stubs.rs +++ b/espflash/src/flasher/stubs.rs @@ -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, diff --git a/espflash/src/image_format/idf.rs b/espflash/src/image_format/idf.rs index 73641a4..7604578 100644 --- a/espflash/src/image_format/idf.rs +++ b/espflash/src/image_format/idf.rs @@ -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 { - let data = fs::read(path).map_err(|e| Error::FileOpenError(path.display().to_string(), e))?; - - Ok(PartitionTable::try_from(data)?) -} - fn encode_hex(data: T) -> String where T: AsRef<[u8]>, diff --git a/espflash/src/image_format/metadata.rs b/espflash/src/image_format/metadata.rs index 38d8728..4e0c06e 100644 --- a/espflash/src/image_format/metadata.rs +++ b/espflash/src/image_format/metadata.rs @@ -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>, } diff --git a/espflash/src/image_format/mod.rs b/espflash/src/image_format/mod.rs index 59bf4a7..9717a85 100644 --- a/espflash/src/image_format/mod.rs +++ b/espflash/src/image_format/mod.rs @@ -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> 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, diff --git a/espflash/src/target/efuse/mod.rs b/espflash/src/target/efuse/mod.rs index 09900b9..d71cae7 100644 --- a/espflash/src/target/efuse/mod.rs +++ b/espflash/src/target/efuse/mod.rs @@ -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, diff --git a/espflash/src/target/flash_target/esp32.rs b/espflash/src/target/flash_target/esp32.rs index dbdd220..697ce77 100644 --- a/espflash/src/target/flash_target/esp32.rs +++ b/espflash/src/target/flash_target/esp32.rs @@ -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, diff --git a/espflash/src/target/flash_target/mod.rs b/espflash/src/target/flash_target/mod.rs index 107eed9..e9fd08d 100644 --- a/espflash/src/target/flash_target/mod.rs +++ b/espflash/src/target/flash_target/mod.rs @@ -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 { diff --git a/espflash/src/target/flash_target/ram.rs b/espflash/src/target/flash_target/ram.rs index 0e69dd1..629b204 100644 --- a/espflash/src/target/flash_target/ram.rs +++ b/espflash/src/target/flash_target/ram.rs @@ -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, block_size: usize, diff --git a/espflash/src/target/mod.rs b/espflash/src/target/mod.rs index 2b852dd..209d754 100644 --- a/espflash/src/target/mod.rs +++ b/espflash/src/target/mod.rs @@ -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 { 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, 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 { 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 { 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 { 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 { 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 { 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 { self.read_efuse(connection, efuse::esp32s3::BLK_VERSION_MINOR) } @@ -989,7 +963,7 @@ impl TryFrom for Chip { } /// SPI register addresses -#[derive(Debug)] +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)] pub struct SpiRegisters { base: u32, usr_offset: u32,