Skip to content

Commit 59a8243

Browse files
Add erase-flash, erase-region, and erase-parts subcommands (#462)
* 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 <[email protected]> --------- Co-authored-by: Sergio Gasquez Arcos <[email protected]>
1 parent 1c75bce commit 59a8243

File tree

6 files changed

+196
-17
lines changed

6 files changed

+196
-17
lines changed

cargo-espflash/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub enum Error {
5454
#[error("No package could be located in the current workspace")]
5555
#[diagnostic(
5656
code(cargo_espflash::no_package),
57-
help("Ensure that you are executing from a valid package, and that the specified package name\
57+
help("Ensure that you are executing from a valid package, and that the specified package name \
5858
exists in the current workspace.")
5959
)]
6060
NoPackage,

cargo-espflash/src/main.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@ use std::{
66

77
use cargo_metadata::Message;
88
use clap::{Args, CommandFactory, Parser, Subcommand};
9+
use espflash::cli::{erase_flash, erase_region, EraseFlashArgs, EraseRegionArgs};
910
use espflash::{
1011
cli::{
1112
self, board_info, completions, config::Config, connect, erase_partitions, flash_elf_image,
1213
monitor::monitor, parse_partition_table, partition_table, print_board_info,
1314
save_elf_as_image, serial_monitor, CompletionsArgs, ConnectArgs, EspflashProgress,
1415
FlashConfigArgs, MonitorArgs, PartitionTableArgs,
1516
},
17+
error::Error as EspflashError,
1618
image_format::ImageFormatKind,
1719
logging::initialize_logger,
1820
targets::Chip,
1921
update::check_for_update,
2022
};
21-
use log::{debug, LevelFilter};
23+
use log::{debug, info, LevelFilter};
2224
use miette::{IntoDiagnostic, Result, WrapErr};
2325

2426
use crate::{
@@ -66,6 +68,12 @@ enum Commands {
6668
/// depending on which shell is being used; consult your shell's
6769
/// documentation to determine the appropriate path.
6870
Completions(CompletionsArgs),
71+
/// Erase Flash entirely
72+
EraseFlash(EraseFlashArgs),
73+
/// Erase specified partitions
74+
EraseParts(ErasePartsArgs),
75+
/// Erase specified region
76+
EraseRegion(EraseRegionArgs),
6977
/// Flash an application in ELF format to a target device
7078
///
7179
/// First convert the ELF file produced by cargo into the appropriate
@@ -137,6 +145,26 @@ struct BuildArgs {
137145
pub flash_config_args: FlashConfigArgs,
138146
}
139147

148+
/// Erase named partitions based on provided partition table
149+
#[derive(Debug, Args)]
150+
pub struct ErasePartsArgs {
151+
/// Connection configuration
152+
#[clap(flatten)]
153+
pub connect_args: ConnectArgs,
154+
155+
/// Labels of the partitions to be erased
156+
#[arg(value_name = "LABELS", value_delimiter = ',')]
157+
pub erase_parts: Vec<String>,
158+
159+
/// Input partition table
160+
#[arg(long, value_name = "FILE")]
161+
pub partition_table: Option<PathBuf>,
162+
163+
/// Specify a (binary) package within a workspace which may provide a partition table
164+
#[arg(long)]
165+
pub package: Option<String>,
166+
}
167+
140168
/// Build and flash an application to a target device
141169
#[derive(Debug, Args)]
142170
struct FlashArgs {
@@ -182,6 +210,9 @@ fn main() -> Result<()> {
182210
match args {
183211
Commands::BoardInfo(args) => board_info(&args, &config),
184212
Commands::Completions(args) => completions(&args, &mut Cli::command(), "cargo"),
213+
Commands::EraseFlash(args) => erase_flash(args, &config),
214+
Commands::EraseParts(args) => erase_parts(args, &config),
215+
Commands::EraseRegion(args) => erase_region(args, &config),
185216
Commands::Flash(args) => flash(args, &config),
186217
Commands::Monitor(args) => serial_monitor(args, &config),
187218
Commands::PartitionTable(args) => partition_table(args),
@@ -196,6 +227,33 @@ struct BuildContext {
196227
pub partition_table_path: Option<PathBuf>,
197228
}
198229

230+
pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
231+
if args.connect_args.no_stub {
232+
return Err(EspflashError::StubRequiredToEraseFlash).into_diagnostic();
233+
}
234+
235+
let metadata_partition_table = PackageMetadata::load(&args.package)
236+
.ok()
237+
.and_then(|m| m.partition_table);
238+
239+
let partition_table = args
240+
.partition_table
241+
.as_deref()
242+
.or(metadata_partition_table.as_deref());
243+
244+
let mut flash = connect(&args.connect_args, config)?;
245+
let partition_table = match partition_table {
246+
Some(path) => Some(parse_partition_table(path)?),
247+
None => None,
248+
};
249+
250+
info!("Erasing the following partitions: {:?}", args.erase_parts);
251+
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
252+
flash.connection().reset()?;
253+
254+
Ok(())
255+
}
256+
199257
fn flash(args: FlashArgs, config: &Config) -> Result<()> {
200258
let metadata = PackageMetadata::load(&args.build_args.package)?;
201259
let cargo_config = CargoConfig::load(&metadata.workspace_root, &metadata.package_root);

espflash/src/bin/espflash.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
use std::{
22
fs::{self, File},
33
io::Read,
4-
num::ParseIntError,
54
path::PathBuf,
65
};
76

87
use clap::{Args, CommandFactory, Parser, Subcommand};
98
use espflash::{
109
cli::{
11-
self, board_info, completions, config::Config, connect, erase_partitions, flash_elf_image,
12-
monitor::monitor, parse_partition_table, partition_table, print_board_info,
13-
save_elf_as_image, serial_monitor, CompletionsArgs, ConnectArgs, EspflashProgress,
14-
FlashConfigArgs, MonitorArgs, PartitionTableArgs,
10+
self, board_info, completions, config::Config, connect, erase_flash, erase_partitions,
11+
erase_region, flash_elf_image, monitor::monitor, parse_partition_table, parse_uint32,
12+
partition_table, print_board_info, save_elf_as_image, serial_monitor, CompletionsArgs,
13+
ConnectArgs, EraseFlashArgs, EraseRegionArgs, EspflashProgress, FlashConfigArgs,
14+
MonitorArgs, PartitionTableArgs,
1515
},
16+
error::Error,
1617
image_format::ImageFormatKind,
1718
logging::initialize_logger,
1819
targets::Chip,
1920
update::check_for_update,
2021
};
21-
use log::{debug, LevelFilter};
22+
use log::{debug, info, LevelFilter};
2223
use miette::{IntoDiagnostic, Result, WrapErr};
2324

2425
#[derive(Debug, Parser)]
@@ -42,6 +43,12 @@ enum Commands {
4243
/// depending on which shell is being used; consult your shell's
4344
/// documentation to determine the appropriate path.
4445
Completions(CompletionsArgs),
46+
/// Erase Flash entirely
47+
EraseFlash(EraseFlashArgs),
48+
/// Erase specified partitions
49+
EraseParts(ErasePartsArgs),
50+
/// Erase specified region
51+
EraseRegion(EraseRegionArgs),
4552
/// Flash an application in ELF format to a connected target device
4653
///
4754
/// Given a path to an ELF file, first convert it into the appropriate
@@ -78,6 +85,22 @@ enum Commands {
7885
WriteBin(WriteBinArgs),
7986
}
8087

88+
/// Erase named partitions based on provided partition table
89+
#[derive(Debug, Args)]
90+
pub struct ErasePartsArgs {
91+
/// Connection configuration
92+
#[clap(flatten)]
93+
pub connect_args: ConnectArgs,
94+
95+
/// Labels of the partitions to be erased
96+
#[arg(value_name = "LABELS", value_delimiter = ',')]
97+
pub erase_parts: Vec<String>,
98+
99+
/// Input partition table
100+
#[arg(long, value_name = "FILE")]
101+
pub partition_table: Option<PathBuf>,
102+
}
103+
81104
#[derive(Debug, Args)]
82105
struct FlashArgs {
83106
/// Connection configuration
@@ -121,11 +144,6 @@ struct WriteBinArgs {
121144
connect_args: ConnectArgs,
122145
}
123146

124-
/// Parses a string as a 32-bit unsigned integer.
125-
fn parse_uint32(input: &str) -> Result<u32, ParseIntError> {
126-
parse_int::parse(input)
127-
}
128-
129147
fn main() -> Result<()> {
130148
miette::set_panic_hook();
131149
initialize_logger(LevelFilter::Info);
@@ -148,6 +166,9 @@ fn main() -> Result<()> {
148166
match args {
149167
Commands::BoardInfo(args) => board_info(&args, &config),
150168
Commands::Completions(args) => completions(&args, &mut Cli::command(), "espflash"),
169+
Commands::EraseFlash(args) => erase_flash(args, &config),
170+
Commands::EraseParts(args) => erase_parts(args, &config),
171+
Commands::EraseRegion(args) => erase_region(args, &config),
151172
Commands::Flash(args) => flash(args, &config),
152173
Commands::Monitor(args) => serial_monitor(args, &config),
153174
Commands::PartitionTable(args) => partition_table(args),
@@ -156,6 +177,24 @@ fn main() -> Result<()> {
156177
}
157178
}
158179

180+
pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
181+
if args.connect_args.no_stub {
182+
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
183+
}
184+
185+
let mut flash = connect(&args.connect_args, config)?;
186+
let partition_table = match args.partition_table {
187+
Some(path) => Some(parse_partition_table(&path)?),
188+
None => None,
189+
};
190+
191+
info!("Erasing the following partitions: {:?}", args.erase_parts);
192+
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
193+
flash.connection().reset()?;
194+
195+
Ok(())
196+
}
197+
159198
fn flash(args: FlashArgs, config: &Config) -> Result<()> {
160199
let mut flasher = connect(&args.connect_args, config)?;
161200

espflash/src/cli/mod.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! [cargo-espflash]: https://crates.io/crates/cargo-espflash
88
//! [espflash]: https://crates.io/crates/espflash
99
10+
use std::num::ParseIntError;
1011
use std::{
1112
collections::HashMap,
1213
fs,
@@ -26,7 +27,7 @@ use serialport::{SerialPortType, UsbPortInfo};
2627
use self::{config::Config, monitor::monitor, serial::get_serial_port_info};
2728
use crate::{
2829
elf::ElfFirmwareImage,
29-
error::{MissingPartition, MissingPartitionTable},
30+
error::{Error, MissingPartition, MissingPartitionTable},
3031
flasher::{FlashFrequency, FlashMode, FlashSize, Flasher, ProgressCallbacks},
3132
image_format::ImageFormatKind,
3233
interface::Interface,
@@ -67,6 +68,30 @@ pub struct CompletionsArgs {
6768
pub shell: Shell,
6869
}
6970

71+
/// Erase entire flash of target device
72+
#[derive(Debug, Args)]
73+
pub struct EraseFlashArgs {
74+
/// Connection configuration
75+
#[clap(flatten)]
76+
pub connect_args: ConnectArgs,
77+
}
78+
79+
/// Erase specified region of flash
80+
#[derive(Debug, Args)]
81+
pub struct EraseRegionArgs {
82+
/// Connection configuration
83+
#[clap(flatten)]
84+
pub connect_args: ConnectArgs,
85+
86+
/// Offset to start erasing from
87+
#[arg(value_name = "OFFSET", value_parser = parse_uint32)]
88+
pub addr: u32,
89+
90+
/// Size of the region to erase
91+
#[arg(value_name = "SIZE", value_parser = parse_uint32)]
92+
pub size: u32,
93+
}
94+
7095
/// Configure communication with the target device's flash
7196
#[derive(Debug, Args)]
7297
pub struct FlashConfigArgs {
@@ -445,6 +470,38 @@ impl ProgressCallbacks for EspflashProgress {
445470
}
446471
}
447472

473+
pub fn erase_flash(args: EraseFlashArgs, config: &Config) -> Result<()> {
474+
if args.connect_args.no_stub {
475+
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
476+
}
477+
478+
let mut flash = connect(&args.connect_args, config)?;
479+
480+
info!("Erasing Flash...");
481+
flash.erase_flash()?;
482+
483+
flash.connection().reset()?;
484+
485+
Ok(())
486+
}
487+
488+
pub fn erase_region(args: EraseRegionArgs, config: &Config) -> Result<()> {
489+
if args.connect_args.no_stub {
490+
return Err(Error::StubRequiredToEraseFlash).into_diagnostic();
491+
}
492+
493+
let mut flash = connect(&args.connect_args, config)?;
494+
495+
info!(
496+
"Erasing region at 0x{:08x} ({} bytes)",
497+
args.addr, args.size
498+
);
499+
flash.erase_region(args.addr, args.size)?;
500+
flash.connection().reset()?;
501+
502+
Ok(())
503+
}
504+
448505
/// Write an ELF image to a target device's flash
449506
pub fn flash_elf_image(
450507
flasher: &mut Flasher,
@@ -637,3 +694,8 @@ fn pretty_print(table: PartitionTable) {
637694

638695
println!("{pretty}");
639696
}
697+
698+
/// Parses a string as a 32-bit unsigned integer.
699+
pub fn parse_uint32(input: &str) -> Result<u32, ParseIntError> {
700+
parse_int::parse(input)
701+
}

espflash/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ pub enum Error {
8383
)]
8484
NoSerial,
8585

86+
#[error("Erase commands require using the RAM stub")]
87+
#[diagnostic(
88+
code(espflash::stub_required_to_erase_flash),
89+
help("Don't use the `--no-stub` option with erase commands")
90+
)]
91+
StubRequiredToEraseFlash,
92+
8693
#[error("Incorrect serial port configuration")]
8794
#[diagnostic(
8895
code(espflash::serial_config),

espflash/src/flasher/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -855,12 +855,25 @@ impl Flasher {
855855
pub fn erase_region(&mut self, offset: u32, size: u32) -> Result<(), Error> {
856856
debug!("Erasing region of 0x{:x}B at 0x{:08x}", size, offset);
857857

858+
self.connection.with_timeout(
859+
CommandType::EraseRegion.timeout_for_size(size),
860+
|connection| connection.command(Command::EraseRegion { offset, size }),
861+
)?;
862+
std::thread::sleep(Duration::from_secs_f32(0.05));
863+
self.connection.flush()?;
864+
Ok(())
865+
}
866+
867+
pub fn erase_flash(&mut self) -> Result<(), Error> {
868+
debug!("Erasing the entire flash");
869+
858870
self.connection
859-
.with_timeout(CommandType::EraseRegion.timeout(), |connection| {
860-
connection.command(Command::EraseRegion { offset, size })
871+
.with_timeout(CommandType::EraseFlash.timeout(), |connection| {
872+
connection.command(Command::EraseFlash)
861873
})?;
862-
std::thread::sleep(Duration::from_secs_f32(0.05));
874+
sleep(Duration::from_secs_f32(0.05));
863875
self.connection.flush()?;
876+
864877
Ok(())
865878
}
866879

0 commit comments

Comments
 (0)