Skip to content

Commit d64ff68

Browse files
authored
Add support for erasing otadata (#229)
* Add support for erasing otadata * Fix formatting * Look for proper OTA partition by type and subtype
1 parent 1b23940 commit d64ff68

File tree

8 files changed

+103
-5
lines changed

8 files changed

+103
-5
lines changed

cargo-espflash/src/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,15 @@ fn main() -> Result<()> {
143143
.init();
144144

145145
// Extract subcommand
146-
let CargoSubCommand::Espflash(opts) = opts.subcommand;
146+
let CargoSubCommand::Espflash(mut opts) = opts.subcommand;
147147

148148
debug!("subcommand options: {:?}", opts);
149149

150+
// `erase_otadata` requires `use_stub`
151+
if opts.flash_opts.erase_otadata {
152+
opts.connect_opts.use_stub = true;
153+
}
154+
150155
// Load configuration and metadata
151156
let config = Config::load()?;
152157
let metadata = CargoEspFlashMeta::load("Cargo.toml")?;
@@ -226,6 +231,7 @@ fn flash(
226231
opts.build_opts.flash_config_opts.flash_mode,
227232
opts.build_opts.flash_config_opts.flash_size,
228233
opts.build_opts.flash_config_opts.flash_freq,
234+
opts.flash_opts.erase_otadata,
229235
)?;
230236
}
231237

espflash/src/cli/mod.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ use crate::{
2121
cli::monitor::monitor,
2222
cli::serial::get_serial_port_info,
2323
elf::{ElfFirmwareImage, FlashFrequency, FlashMode},
24-
error::Error,
24+
error::{Error, NoOtadataError},
2525
flasher::FlashSize,
26-
Chip, Flasher, ImageFormatId, InvalidPartitionTable, PartitionTable,
26+
partition_table, Chip, Flasher, ImageFormatId, InvalidPartitionTable, MissingPartitionTable,
27+
PartitionTable,
2728
};
2829

2930
pub mod config;
@@ -59,6 +60,10 @@ pub struct FlashOpts {
5960
/// Open a serial monitor after flashing
6061
#[clap(long)]
6162
pub monitor: bool,
63+
/// Erase the OTADATA partition
64+
/// This is useful when using multiple OTA partitions and still wanting to be able to reflash via espflash
65+
#[clap(long)]
66+
pub erase_otadata: bool,
6267
}
6368

6469
#[derive(Debug, Clone, Parser)]
@@ -283,6 +288,7 @@ pub fn flash_elf_image(
283288
flash_mode: Option<FlashMode>,
284289
flash_size: Option<FlashSize>,
285290
flash_freq: Option<FlashFrequency>,
291+
erase_otadata: bool,
286292
) -> Result<()> {
287293
// If the '--bootloader' option is provided, load the binary file at the
288294
// specified path.
@@ -310,6 +316,26 @@ pub fn flash_elf_image(
310316
None
311317
};
312318

319+
if erase_otadata {
320+
let partition_table = match &partition_table {
321+
Some(partition_table) => partition_table,
322+
None => return Err((MissingPartitionTable {}).into()),
323+
};
324+
325+
let otadata = match partition_table.find_by_subtype(
326+
partition_table::Type::CoreType(partition_table::CoreType::Data),
327+
partition_table::SubType::Data(partition_table::DataType::Ota),
328+
) {
329+
Some(otadata) => otadata,
330+
None => return Err((NoOtadataError {}).into()),
331+
};
332+
333+
let offset = otadata.offset();
334+
let size = otadata.size();
335+
336+
flasher.erase_region(offset, size)?;
337+
}
338+
313339
// Load the ELF data, optionally using the provider bootloader/partition
314340
// table/image format, to the device's flash memory.
315341
flasher.load_elf_to_flash_with_format(

espflash/src/command.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use strum_macros::Display;
88
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
99
const ERASE_REGION_TIMEOUT_PER_MB: Duration = Duration::from_secs(30);
1010
const ERASE_WRITE_TIMEOUT_PER_MB: Duration = Duration::from_secs(40);
11+
const ERASE_CHIP_TIMEOUT: Duration = Duration::from_secs(120);
1112
const MEM_END_TIMEOUT: Duration = Duration::from_millis(50);
1213
const SYNC_TIMEOUT: Duration = Duration::from_millis(100);
1314

@@ -34,13 +35,17 @@ pub enum CommandType {
3435
FlashDeflateEnd = 0x12,
3536
FlashMd5 = 0x13,
3637
FlashDetect = 0x9f,
38+
// Some commands supported by stub only
39+
EraseFlash = 0xd0,
40+
EraseRegion = 0xd1,
3741
}
3842

3943
impl CommandType {
4044
pub fn timeout(&self) -> Duration {
4145
match self {
4246
CommandType::MemEnd => MEM_END_TIMEOUT,
4347
CommandType::Sync => SYNC_TIMEOUT,
48+
CommandType::EraseFlash => ERASE_CHIP_TIMEOUT,
4449
_ => DEFAULT_TIMEOUT,
4550
}
4651
}
@@ -54,7 +59,7 @@ impl CommandType {
5459
)
5560
}
5661
match self {
57-
CommandType::FlashBegin | CommandType::FlashDeflateBegin => {
62+
CommandType::FlashBegin | CommandType::FlashDeflateBegin | CommandType::EraseRegion => {
5863
calc_timeout(ERASE_REGION_TIMEOUT_PER_MB, size)
5964
}
6065
CommandType::FlashData | CommandType::FlashDeflateData => {
@@ -138,6 +143,11 @@ pub enum Command<'a> {
138143
reboot: bool,
139144
},
140145
FlashDetect,
146+
EraseFlash,
147+
EraseRegion {
148+
offset: u32,
149+
size: u32,
150+
},
141151
}
142152

143153
impl<'a> Command<'a> {
@@ -159,6 +169,8 @@ impl<'a> Command<'a> {
159169
Command::FlashDeflateData { .. } => CommandType::FlashDeflateData,
160170
Command::FlashDeflateEnd { .. } => CommandType::FlashDeflateEnd,
161171
Command::FlashDetect => CommandType::FlashDetect,
172+
Command::EraseFlash { .. } => CommandType::EraseFlash,
173+
Command::EraseRegion { .. } => CommandType::EraseRegion,
162174
}
163175
}
164176

@@ -319,6 +331,18 @@ impl<'a> Command<'a> {
319331
Command::FlashDetect => {
320332
write_basic(writer, &[], 0)?;
321333
}
334+
Command::EraseFlash => {
335+
write_basic(writer, &[], 0)?;
336+
}
337+
Command::EraseRegion { offset, size } => {
338+
// length
339+
writer.write_all(&(8u16.to_le_bytes()))?;
340+
// checksum
341+
writer.write_all(&(0u32.to_le_bytes()))?;
342+
// data
343+
writer.write_all(&offset.to_le_bytes())?;
344+
writer.write_all(&size.to_le_bytes())?;
345+
}
322346
};
323347
Ok(())
324348
}

espflash/src/error.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ pub enum PartitionTableError {
368368
#[error(transparent)]
369369
#[diagnostic(transparent)]
370370
InvalidPartitionTable(#[from] InvalidPartitionTable),
371+
#[error(transparent)]
372+
#[diagnostic(transparent)]
373+
MissingPartitionTable(#[from] MissingPartitionTable),
371374
}
372375

373376
#[derive(Debug, Error, Diagnostic)]
@@ -538,7 +541,14 @@ impl NoAppError {
538541
}
539542
}
540543
}
544+
#[derive(Debug, Error, Diagnostic)]
545+
#[error("No otadata partition was found")]
546+
#[diagnostic(
547+
code(espflash::partition_table::no_otadata),
548+
help("Partition table must contain an otadata partition when trying to erase it")
549+
)]
541550

551+
pub struct NoOtadataError;
542552
#[derive(Debug, Error, Diagnostic)]
543553
#[error("Unaligned partition")]
544554
#[diagnostic(code(espflash::partition_table::unaligned))]
@@ -578,6 +588,11 @@ pub struct NoEndMarker;
578588
#[diagnostic(code(espflash::partition_table::invalid_partition_table))]
579589
pub struct InvalidPartitionTable;
580590

591+
#[derive(Debug, Error, Diagnostic)]
592+
#[error("Missing partition table")]
593+
#[diagnostic(code(espflash::partition_table::missing_partition_table))]
594+
pub struct MissingPartitionTable;
595+
581596
#[derive(Debug, Error)]
582597
#[error("{0}")]
583598
pub struct ElfError(&'static str);

espflash/src/flasher.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,18 @@ impl Flasher {
641641
pub fn get_usb_pid(&self) -> Result<u16, Error> {
642642
self.connection.get_usb_pid()
643643
}
644+
645+
pub fn erase_region(&mut self, offset: u32, size: u32) -> Result<(), Error> {
646+
debug!("Erasing region of 0x{:x}B at 0x{:08x}", size, offset);
647+
648+
self.connection
649+
.with_timeout(CommandType::EraseRegion.timeout(), |connection| {
650+
connection.command(Command::EraseRegion { offset, size })
651+
})?;
652+
std::thread::sleep(Duration::from_secs_f32(0.05));
653+
self.connection.flush()?;
654+
Ok(())
655+
}
644656
}
645657

646658
pub(crate) fn get_erase_size(offset: usize, size: usize) -> usize {

espflash/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub use chip::Chip;
22
pub use cli::config::Config;
33
pub use elf::{FlashFrequency, FlashMode};
4-
pub use error::{Error, InvalidPartitionTable};
4+
pub use error::{Error, InvalidPartitionTable, MissingPartitionTable};
55
pub use flasher::{FlashSize, Flasher};
66
pub use image_format::ImageFormatId;
77
pub use partition_table::PartitionTable;

espflash/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ fn main() -> Result<()> {
9595

9696
debug!("options: {:?}", opts);
9797

98+
if opts.flash_opts.erase_otadata {
99+
opts.connect_opts.use_stub = true;
100+
}
101+
98102
// Setup logging
99103
tracing_subscriber::fmt()
100104
.with_env_filter(EnvFilter::from_default_env().add_directive(opts.log_level.into()))
@@ -167,6 +171,7 @@ fn flash(opts: Opts, config: Config) -> Result<()> {
167171
opts.flash_config_opts.flash_mode,
168172
opts.flash_config_opts.flash_size,
169173
opts.flash_config_opts.flash_freq,
174+
opts.flash_opts.erase_otadata,
170175
)?;
171176
}
172177

espflash/src/partition_table.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,12 @@ impl PartitionTable {
418418
self.partitions.iter().find(|&p| p.ty == ty)
419419
}
420420

421+
pub fn find_by_subtype(&self, ty: Type, sub_type: SubType) -> Option<&Partition> {
422+
self.partitions
423+
.iter()
424+
.find(|&p| p.ty == ty && p.sub_type == sub_type)
425+
}
426+
421427
fn validate(&self, source: &str) -> Result<(), PartitionTableError> {
422428
for partition in &self.partitions {
423429
if let Some(line) = &partition.line {
@@ -662,6 +668,10 @@ impl Partition {
662668
self.offset
663669
}
664670

671+
pub fn size(&self) -> u32 {
672+
self.size
673+
}
674+
665675
pub fn flags(&self) -> Option<Flags> {
666676
self.flags
667677
}

0 commit comments

Comments
 (0)