Skip to content

Commit

Permalink
Add erase-flash, erase-region, and erase-parts subcommands (#462)
Browse files Browse the repository at this point in the history
* Add command to erase flash. (For #460)

* Add `erase-parts` command to erase named partitions.

* Fix timeout logic when erasing regions.

* Reset device after `erase_flash` or `erase_parts` commands.

* Add support for `erase-region` subcommand.

* Avoid changes to `cli/mod.rs`

* Address PR comments.  Add subcommands to `cargo-espflash` too.

* Fix clippy violation.

* Factor out error for erase failure when `--no-stub` is specified.

* Improve guidance around StubRequiredToEraseFlash

Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com>

---------

Co-authored-by: Sergio Gasquez Arcos <sergio.gasquez@gmail.com>
  • Loading branch information
jnross and SergioGasquez committed Sep 29, 2023
1 parent 1c75bce commit 59a8243
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 17 deletions.
2 changes: 1 addition & 1 deletion cargo-espflash/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub enum Error {
#[error("No package could be located in the current workspace")]
#[diagnostic(
code(cargo_espflash::no_package),
help("Ensure that you are executing from a valid package, and that the specified package name\
help("Ensure that you are executing from a valid package, and that the specified package name \
exists in the current workspace.")
)]
NoPackage,
Expand Down
60 changes: 59 additions & 1 deletion cargo-espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ use std::{

use cargo_metadata::Message;
use clap::{Args, CommandFactory, Parser, Subcommand};
use espflash::cli::{erase_flash, erase_region, EraseFlashArgs, EraseRegionArgs};
use espflash::{
cli::{
self, board_info, completions, config::Config, connect, erase_partitions, flash_elf_image,
monitor::monitor, parse_partition_table, partition_table, print_board_info,
save_elf_as_image, serial_monitor, CompletionsArgs, ConnectArgs, EspflashProgress,
FlashConfigArgs, MonitorArgs, PartitionTableArgs,
},
error::Error as EspflashError,
image_format::ImageFormatKind,
logging::initialize_logger,
targets::Chip,
update::check_for_update,
};
use log::{debug, LevelFilter};
use log::{debug, info, LevelFilter};
use miette::{IntoDiagnostic, Result, WrapErr};

use crate::{
Expand Down Expand Up @@ -66,6 +68,12 @@ enum Commands {
/// depending on which shell is being used; consult your shell's
/// documentation to determine the appropriate path.
Completions(CompletionsArgs),
/// Erase Flash entirely
EraseFlash(EraseFlashArgs),
/// Erase specified partitions
EraseParts(ErasePartsArgs),
/// Erase specified region
EraseRegion(EraseRegionArgs),
/// Flash an application in ELF format to a target device
///
/// First convert the ELF file produced by cargo into the appropriate
Expand Down Expand Up @@ -137,6 +145,26 @@ struct BuildArgs {
pub flash_config_args: FlashConfigArgs,
}

/// Erase named partitions based on provided partition table
#[derive(Debug, Args)]
pub struct ErasePartsArgs {
/// Connection configuration
#[clap(flatten)]
pub connect_args: ConnectArgs,

/// Labels of the partitions to be erased
#[arg(value_name = "LABELS", value_delimiter = ',')]
pub erase_parts: Vec<String>,

/// Input partition table
#[arg(long, value_name = "FILE")]
pub partition_table: Option<PathBuf>,

/// Specify a (binary) package within a workspace which may provide a partition table
#[arg(long)]
pub package: Option<String>,
}

/// Build and flash an application to a target device
#[derive(Debug, Args)]
struct FlashArgs {
Expand Down Expand Up @@ -182,6 +210,9 @@ fn main() -> Result<()> {
match args {
Commands::BoardInfo(args) => board_info(&args, &config),
Commands::Completions(args) => completions(&args, &mut Cli::command(), "cargo"),
Commands::EraseFlash(args) => erase_flash(args, &config),
Commands::EraseParts(args) => erase_parts(args, &config),
Commands::EraseRegion(args) => erase_region(args, &config),
Commands::Flash(args) => flash(args, &config),
Commands::Monitor(args) => serial_monitor(args, &config),
Commands::PartitionTable(args) => partition_table(args),
Expand All @@ -196,6 +227,33 @@ struct BuildContext {
pub partition_table_path: Option<PathBuf>,
}

pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
if args.connect_args.no_stub {
return Err(EspflashError::StubRequiredToEraseFlash).into_diagnostic();
}

let metadata_partition_table = PackageMetadata::load(&args.package)
.ok()
.and_then(|m| m.partition_table);

let partition_table = args
.partition_table
.as_deref()
.or(metadata_partition_table.as_deref());

let mut flash = connect(&args.connect_args, config)?;
let partition_table = match partition_table {
Some(path) => Some(parse_partition_table(path)?),
None => None,
};

info!("Erasing the following partitions: {:?}", args.erase_parts);
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
flash.connection().reset()?;

Ok(())
}

fn flash(args: FlashArgs, config: &Config) -> Result<()> {
let metadata = PackageMetadata::load(&args.build_args.package)?;
let cargo_config = CargoConfig::load(&metadata.workspace_root, &metadata.package_root);
Expand Down
61 changes: 50 additions & 11 deletions espflash/src/bin/espflash.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
use std::{
fs::{self, File},
io::Read,
num::ParseIntError,
path::PathBuf,
};

use clap::{Args, CommandFactory, Parser, Subcommand};
use espflash::{
cli::{
self, board_info, completions, config::Config, connect, erase_partitions, flash_elf_image,
monitor::monitor, parse_partition_table, partition_table, print_board_info,
save_elf_as_image, serial_monitor, CompletionsArgs, ConnectArgs, EspflashProgress,
FlashConfigArgs, MonitorArgs, PartitionTableArgs,
self, board_info, completions, config::Config, connect, erase_flash, erase_partitions,
erase_region, flash_elf_image, monitor::monitor, parse_partition_table, parse_uint32,
partition_table, print_board_info, save_elf_as_image, serial_monitor, CompletionsArgs,
ConnectArgs, EraseFlashArgs, EraseRegionArgs, EspflashProgress, FlashConfigArgs,
MonitorArgs, PartitionTableArgs,
},
error::Error,
image_format::ImageFormatKind,
logging::initialize_logger,
targets::Chip,
update::check_for_update,
};
use log::{debug, LevelFilter};
use log::{debug, info, LevelFilter};
use miette::{IntoDiagnostic, Result, WrapErr};

#[derive(Debug, Parser)]
Expand All @@ -42,6 +43,12 @@ enum Commands {
/// depending on which shell is being used; consult your shell's
/// documentation to determine the appropriate path.
Completions(CompletionsArgs),
/// Erase Flash entirely
EraseFlash(EraseFlashArgs),
/// Erase specified partitions
EraseParts(ErasePartsArgs),
/// Erase specified region
EraseRegion(EraseRegionArgs),
/// Flash an application in ELF format to a connected target device
///
/// Given a path to an ELF file, first convert it into the appropriate
Expand Down Expand Up @@ -78,6 +85,22 @@ enum Commands {
WriteBin(WriteBinArgs),
}

/// Erase named partitions based on provided partition table
#[derive(Debug, Args)]
pub struct ErasePartsArgs {
/// Connection configuration
#[clap(flatten)]
pub connect_args: ConnectArgs,

/// Labels of the partitions to be erased
#[arg(value_name = "LABELS", value_delimiter = ',')]
pub erase_parts: Vec<String>,

/// Input partition table
#[arg(long, value_name = "FILE")]
pub partition_table: Option<PathBuf>,
}

#[derive(Debug, Args)]
struct FlashArgs {
/// Connection configuration
Expand Down Expand Up @@ -121,11 +144,6 @@ struct WriteBinArgs {
connect_args: ConnectArgs,
}

/// Parses a string as a 32-bit unsigned integer.
fn parse_uint32(input: &str) -> Result<u32, ParseIntError> {
parse_int::parse(input)
}

fn main() -> Result<()> {
miette::set_panic_hook();
initialize_logger(LevelFilter::Info);
Expand All @@ -148,6 +166,9 @@ fn main() -> Result<()> {
match args {
Commands::BoardInfo(args) => board_info(&args, &config),
Commands::Completions(args) => completions(&args, &mut Cli::command(), "espflash"),
Commands::EraseFlash(args) => erase_flash(args, &config),
Commands::EraseParts(args) => erase_parts(args, &config),
Commands::EraseRegion(args) => erase_region(args, &config),
Commands::Flash(args) => flash(args, &config),
Commands::Monitor(args) => serial_monitor(args, &config),
Commands::PartitionTable(args) => partition_table(args),
Expand All @@ -156,6 +177,24 @@ fn main() -> Result<()> {
}
}

pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
if args.connect_args.no_stub {
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
}

let mut flash = connect(&args.connect_args, config)?;
let partition_table = match args.partition_table {
Some(path) => Some(parse_partition_table(&path)?),
None => None,
};

info!("Erasing the following partitions: {:?}", args.erase_parts);
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
flash.connection().reset()?;

Ok(())
}

fn flash(args: FlashArgs, config: &Config) -> Result<()> {
let mut flasher = connect(&args.connect_args, config)?;

Expand Down
64 changes: 63 additions & 1 deletion espflash/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! [cargo-espflash]: https://crates.io/crates/cargo-espflash
//! [espflash]: https://crates.io/crates/espflash

use std::num::ParseIntError;
use std::{
collections::HashMap,
fs,
Expand All @@ -26,7 +27,7 @@ use serialport::{SerialPortType, UsbPortInfo};
use self::{config::Config, monitor::monitor, serial::get_serial_port_info};
use crate::{
elf::ElfFirmwareImage,
error::{MissingPartition, MissingPartitionTable},
error::{Error, MissingPartition, MissingPartitionTable},
flasher::{FlashFrequency, FlashMode, FlashSize, Flasher, ProgressCallbacks},
image_format::ImageFormatKind,
interface::Interface,
Expand Down Expand Up @@ -67,6 +68,30 @@ pub struct CompletionsArgs {
pub shell: Shell,
}

/// Erase entire flash of target device
#[derive(Debug, Args)]
pub struct EraseFlashArgs {
/// Connection configuration
#[clap(flatten)]
pub connect_args: ConnectArgs,
}

/// Erase specified region of flash
#[derive(Debug, Args)]
pub struct EraseRegionArgs {
/// Connection configuration
#[clap(flatten)]
pub connect_args: ConnectArgs,

/// Offset to start erasing from
#[arg(value_name = "OFFSET", value_parser = parse_uint32)]
pub addr: u32,

/// Size of the region to erase
#[arg(value_name = "SIZE", value_parser = parse_uint32)]
pub size: u32,
}

/// Configure communication with the target device's flash
#[derive(Debug, Args)]
pub struct FlashConfigArgs {
Expand Down Expand Up @@ -445,6 +470,38 @@ impl ProgressCallbacks for EspflashProgress {
}
}

pub fn erase_flash(args: EraseFlashArgs, config: &Config) -> Result<()> {
if args.connect_args.no_stub {
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
}

let mut flash = connect(&args.connect_args, config)?;

info!("Erasing Flash...");
flash.erase_flash()?;

flash.connection().reset()?;

Ok(())
}

pub fn erase_region(args: EraseRegionArgs, config: &Config) -> Result<()> {
if args.connect_args.no_stub {
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
}

let mut flash = connect(&args.connect_args, config)?;

info!(
"Erasing region at 0x{:08x} ({} bytes)",
args.addr, args.size
);
flash.erase_region(args.addr, args.size)?;
flash.connection().reset()?;

Ok(())
}

/// Write an ELF image to a target device's flash
pub fn flash_elf_image(
flasher: &mut Flasher,
Expand Down Expand Up @@ -637,3 +694,8 @@ fn pretty_print(table: PartitionTable) {

println!("{pretty}");
}

/// Parses a string as a 32-bit unsigned integer.
pub fn parse_uint32(input: &str) -> Result<u32, ParseIntError> {
parse_int::parse(input)
}
7 changes: 7 additions & 0 deletions espflash/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ pub enum Error {
)]
NoSerial,

#[error("Erase commands require using the RAM stub")]
#[diagnostic(
code(espflash::stub_required_to_erase_flash),
help("Don't use the `--no-stub` option with erase commands")
)]
StubRequiredToEraseFlash,

#[error("Incorrect serial port configuration")]
#[diagnostic(
code(espflash::serial_config),
Expand Down
19 changes: 16 additions & 3 deletions espflash/src/flasher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,12 +855,25 @@ impl Flasher {
pub fn erase_region(&mut self, offset: u32, size: u32) -> Result<(), Error> {
debug!("Erasing region of 0x{:x}B at 0x{:08x}", size, offset);

self.connection.with_timeout(
CommandType::EraseRegion.timeout_for_size(size),
|connection| connection.command(Command::EraseRegion { offset, size }),
)?;
std::thread::sleep(Duration::from_secs_f32(0.05));
self.connection.flush()?;
Ok(())
}

pub fn erase_flash(&mut self) -> Result<(), Error> {
debug!("Erasing the entire flash");

self.connection
.with_timeout(CommandType::EraseRegion.timeout(), |connection| {
connection.command(Command::EraseRegion { offset, size })
.with_timeout(CommandType::EraseFlash.timeout(), |connection| {
connection.command(Command::EraseFlash)
})?;
std::thread::sleep(Duration::from_secs_f32(0.05));
sleep(Duration::from_secs_f32(0.05));
self.connection.flush()?;

Ok(())
}

Expand Down

0 comments on commit 59a8243

Please sign in to comment.