mirror of
https://github.com/esp-rs/espflash.git
synced 2026-03-14 01:47:47 +00:00
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:
parent
de3bbdc649
commit
c35728a790
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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]>,
|
||||
|
||||
@ -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>>,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user