Skip to content

Commit abd8208

Browse files
authored
Replace --erase-otadata with --erase-parts and --erase-data-parts (#273)
* Replace --erase-otadata with --erase-parts and --erase-data-parts * Update esp-idf-part to v0.1.1
1 parent c29b1dd commit abd8208

File tree

7 files changed

+151
-76
lines changed

7 files changed

+151
-76
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-espflash/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ cargo_toml = "0.12.2"
3535
clap = { version = "4.0.14", features = ["derive"] }
3636
env_logger = "0.9.1"
3737
espflash = { version = "=2.0.0-dev", path = "../espflash" }
38+
esp-idf-part = "0.1.1"
3839
log = "0.4.17"
3940
miette = { version = "5.3.0", features = ["fancy"] }
4041
serde = { version = "1.0.145", features = ["derive"] }

cargo-espflash/src/main.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
collections::HashMap,
23
fs,
34
path::PathBuf,
45
process::{exit, Command, ExitStatus, Stdio},
@@ -8,10 +9,11 @@ use cargo_metadata::Message;
89
use clap::{Args, Parser, Subcommand};
910
use espflash::{
1011
cli::{
11-
self, board_info, clap_enum_variants, config::Config, connect, flash_elf_image,
12-
monitor::monitor, partition_table, save_elf_as_image, serial_monitor, ConnectArgs,
13-
FlashConfigArgs, PartitionTableArgs,
12+
self, board_info, clap_enum_variants, config::Config, connect, erase_partition,
13+
flash_elf_image, monitor::monitor, parse_partition_table, partition_table,
14+
save_elf_as_image, serial_monitor, ConnectArgs, FlashConfigArgs, PartitionTableArgs,
1415
},
16+
error::{MissingPartition, MissingPartitionTable},
1517
image_format::ImageFormatKind,
1618
logging::initialize_logger,
1719
targets::Chip,
@@ -157,17 +159,11 @@ struct BuildContext {
157159
}
158160

159161
fn flash(
160-
mut args: FlashArgs,
162+
args: FlashArgs,
161163
config: &Config,
162164
cargo_config: &CargoConfig,
163165
metadata: &CargoEspFlashMeta,
164166
) -> Result<()> {
165-
// The `erase_otadata` argument requires `use_stub`, which is implicitly
166-
// enabled here.
167-
if args.flash_args.erase_otadata {
168-
args.connect_args.use_stub = true;
169-
}
170-
171167
let mut flasher = connect(&args.connect_args, config)?;
172168

173169
let build_ctx = build(&args.build_args, cargo_config, flasher.chip())
@@ -198,6 +194,54 @@ fn flash(
198194
.or(metadata.partition_table.as_deref())
199195
.or(build_ctx.partition_table_path.as_deref());
200196

197+
let partition_table = match partition_table {
198+
Some(path) => Some(parse_partition_table(path)?),
199+
None => None,
200+
};
201+
202+
if args.flash_args.erase_parts.is_some() || args.flash_args.erase_data_parts.is_some() {
203+
let partition_table = match &partition_table {
204+
Some(partition_table) => partition_table,
205+
None => return Err((MissingPartitionTable {}).into()),
206+
};
207+
208+
// Using a hashmap to deduplicate entries
209+
let mut parts_to_erase = None;
210+
211+
// Look for any part with specific label
212+
if let Some(part_labels) = args.flash_args.erase_parts {
213+
for label in part_labels {
214+
let part = partition_table
215+
.find(label.as_str())
216+
.ok_or(MissingPartition::from(label))?;
217+
parts_to_erase
218+
.get_or_insert(HashMap::new())
219+
.insert(part.offset(), part);
220+
}
221+
}
222+
// Look for any data partitions with specific data subtype
223+
// There might be multiple partition of the same subtype, e.g. when using multiple FAT partitions
224+
if let Some(partition_types) = args.flash_args.erase_data_parts {
225+
for ty in partition_types {
226+
for part in partition_table.partitions() {
227+
if part.ty() == esp_idf_part::Type::Data
228+
&& part.subtype() == esp_idf_part::SubType::Data(ty)
229+
{
230+
parts_to_erase
231+
.get_or_insert(HashMap::new())
232+
.insert(part.offset(), part);
233+
}
234+
}
235+
}
236+
}
237+
238+
if let Some(parts) = parts_to_erase {
239+
parts
240+
.iter()
241+
.try_for_each(|(_, p)| erase_partition(&mut flasher, p))?;
242+
}
243+
}
244+
201245
flash_elf_image(
202246
&mut flasher,
203247
&elf_data,
@@ -207,7 +251,6 @@ fn flash(
207251
args.build_args.flash_config_args.flash_mode,
208252
args.build_args.flash_config_args.flash_size,
209253
args.build_args.flash_config_args.flash_freq,
210-
args.flash_args.erase_otadata,
211254
)?;
212255
}
213256

espflash/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ comfy-table = "6.1.0"
4545
crossterm = { version = "0.25.0", optional = true }
4646
dialoguer = { version = "0.10.2", optional = true }
4747
directories-next = "2.0.0"
48-
esp-idf-part = "0.1.0"
48+
esp-idf-part = "0.1.1"
4949
env_logger = { version = "0.9.1", optional = true }
5050
flate2 = "1.0.24"
5151
indicatif = "0.17.1"

espflash/src/bin/espflash.rs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
collections::HashMap,
23
fs::{self, File},
34
io::Read,
45
num::ParseIntError,
@@ -8,10 +9,11 @@ use std::{
89
use clap::{Args, Parser, Subcommand};
910
use espflash::{
1011
cli::{
11-
self, board_info, clap_enum_variants, config::Config, connect, flash_elf_image,
12-
monitor::monitor, partition_table, save_elf_as_image, serial_monitor, ConnectArgs,
13-
FlashConfigArgs, PartitionTableArgs,
12+
self, board_info, clap_enum_variants, config::Config, connect, erase_partition,
13+
flash_elf_image, monitor::monitor, parse_partition_table, partition_table,
14+
save_elf_as_image, serial_monitor, ConnectArgs, FlashConfigArgs, PartitionTableArgs,
1415
},
16+
error::{MissingPartition, MissingPartitionTable},
1517
image_format::ImageFormatKind,
1618
logging::initialize_logger,
1719
update::check_for_update,
@@ -114,13 +116,7 @@ fn main() -> Result<()> {
114116
}
115117
}
116118

117-
fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
118-
// The `erase_otadata` argument requires `use_stub`, which is implicitly
119-
// enabled here.
120-
if args.flash_args.erase_otadata {
121-
args.connect_args.use_stub = true;
122-
}
123-
119+
fn flash(args: FlashArgs, config: &Config) -> Result<()> {
124120
let mut flasher = connect(&args.connect_args, config)?;
125121
flasher.board_info()?;
126122

@@ -131,7 +127,53 @@ fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
131127
flasher.load_elf_to_ram(&elf_data)?;
132128
} else {
133129
let bootloader = args.flash_args.bootloader.as_deref();
134-
let partition_table = args.flash_args.partition_table.as_deref();
130+
let partition_table = match args.flash_args.partition_table.as_deref() {
131+
Some(path) => Some(parse_partition_table(path)?),
132+
None => None,
133+
};
134+
135+
if args.flash_args.erase_parts.is_some() || args.flash_args.erase_data_parts.is_some() {
136+
let partition_table = match &partition_table {
137+
Some(partition_table) => partition_table,
138+
None => return Err((MissingPartitionTable {}).into()),
139+
};
140+
141+
// Using a hashmap to deduplicate entries
142+
let mut parts_to_erase = None;
143+
144+
// Look for any part with specific label
145+
if let Some(part_labels) = args.flash_args.erase_parts {
146+
for label in part_labels {
147+
let part = partition_table
148+
.find(label.as_str())
149+
.ok_or(MissingPartition::from(label))?;
150+
parts_to_erase
151+
.get_or_insert(HashMap::new())
152+
.insert(part.offset(), part);
153+
}
154+
}
155+
// Look for any data partitions with specific data subtype
156+
// There might be multiple partition of the same subtype, e.g. when using multiple FAT partitions
157+
if let Some(partition_types) = args.flash_args.erase_data_parts {
158+
for ty in partition_types {
159+
for part in partition_table.partitions() {
160+
if part.ty() == esp_idf_part::Type::Data
161+
&& part.subtype() == esp_idf_part::SubType::Data(ty)
162+
{
163+
parts_to_erase
164+
.get_or_insert(HashMap::new())
165+
.insert(part.offset(), part);
166+
}
167+
}
168+
}
169+
}
170+
171+
if let Some(parts) = parts_to_erase {
172+
parts
173+
.iter()
174+
.try_for_each(|(_, p)| erase_partition(&mut flasher, p))?;
175+
}
176+
}
135177

136178
flash_elf_image(
137179
&mut flasher,
@@ -142,7 +184,6 @@ fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
142184
args.flash_config_args.flash_mode,
143185
args.flash_config_args.flash_size,
144186
args.flash_config_args.flash_freq,
145-
args.flash_args.erase_otadata,
146187
)?;
147188
}
148189

espflash/src/cli/mod.rs

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use std::{
1313
path::{Path, PathBuf},
1414
};
1515

16-
use clap::Args;
16+
use clap::{builder::ArgPredicate, Args};
1717
use comfy_table::{modifiers, presets::UTF8_FULL, Attribute, Cell, Color, Table};
18-
use esp_idf_part::{DataType, PartitionTable, SubType, Type};
18+
use esp_idf_part::{DataType, Partition, PartitionTable};
1919
use log::{debug, info};
2020
use miette::{IntoDiagnostic, Result, WrapErr};
2121
use serialport::{SerialPortType, UsbPortInfo};
@@ -24,7 +24,6 @@ use strum::VariantNames;
2424
use self::{config::Config, monitor::monitor, serial::get_serial_port_info};
2525
use crate::{
2626
elf::ElfFirmwareImage,
27-
error::{MissingPartitionTable, NoOtadataError},
2827
flasher::{FlashFrequency, FlashMode, FlashSize, Flasher},
2928
image_format::ImageFormatKind,
3029
interface::Interface,
@@ -74,7 +73,7 @@ pub struct ConnectArgs {
7473
#[cfg_attr(feature = "raspberry", clap(long))]
7574
pub rts: Option<u8>,
7675
/// Use RAM stub for loading
77-
#[arg(long)]
76+
#[arg(long, default_value_ifs([("erase_parts", ArgPredicate::IsPresent, Some("true")), ("erase_data_parts", ArgPredicate::IsPresent, Some("true"))]))]
7877
pub use_stub: bool,
7978
}
8079

@@ -99,11 +98,17 @@ pub struct FlashArgs {
9998
/// Path to a binary (.bin) bootloader file
10099
#[arg(long, value_name = "FILE")]
101100
pub bootloader: Option<PathBuf>,
102-
/// Erase the OTA data partition
103-
/// This is useful when using multiple OTA partitions and still wanting to
104-
/// be able to reflash via cargo-espflash or espflash
105-
#[arg(long)]
106-
pub erase_otadata: bool,
101+
/// Erase partitions by label
102+
#[arg(
103+
long,
104+
requires = "partition_table",
105+
value_name = "LABELS",
106+
value_delimiter = ','
107+
)]
108+
pub erase_parts: Option<Vec<String>>,
109+
/// Erase specified data partitions
110+
#[arg(long, requires = "partition_table", value_name = "PARTS", value_parser = clap_enum_variants!(DataType), value_delimiter = ',')]
111+
pub erase_data_parts: Option<Vec<DataType>>,
107112
/// Image format to flash
108113
#[arg(long, value_parser = clap_enum_variants!(ImageFormatKind))]
109114
pub format: Option<ImageFormatKind>,
@@ -339,12 +344,11 @@ pub fn flash_elf_image(
339344
flasher: &mut Flasher,
340345
elf_data: &[u8],
341346
bootloader: Option<&Path>,
342-
partition_table: Option<&Path>,
347+
partition_table: Option<PartitionTable>,
343348
image_format: Option<ImageFormatKind>,
344349
flash_mode: Option<FlashMode>,
345350
flash_size: Option<FlashSize>,
346351
flash_freq: Option<FlashFrequency>,
347-
erase_otadata: bool,
348352
) -> Result<()> {
349353
// If the '--bootloader' option is provided, load the binary file at the
350354
// specified path.
@@ -357,39 +361,6 @@ pub fn flash_elf_image(
357361
None
358362
};
359363

360-
// If the '--partition-table' option is provided, load the partition table from
361-
// the CSV or binary file at the specified path.
362-
let partition_table = if let Some(path) = partition_table {
363-
let path = fs::canonicalize(path).into_diagnostic()?;
364-
365-
let data = fs::read(path)
366-
.into_diagnostic()
367-
.wrap_err("Failed to open partition table")?;
368-
let table = PartitionTable::try_from(data).into_diagnostic()?;
369-
370-
Some(table)
371-
} else {
372-
None
373-
};
374-
375-
if erase_otadata {
376-
let partition_table = match &partition_table {
377-
Some(partition_table) => partition_table,
378-
None => return Err((MissingPartitionTable {}).into()),
379-
};
380-
381-
let otadata =
382-
match partition_table.find_by_subtype(Type::Data, SubType::Data(DataType::Ota)) {
383-
Some(otadata) => otadata,
384-
None => return Err((NoOtadataError {}).into()),
385-
};
386-
387-
let offset = otadata.offset();
388-
let size = otadata.size();
389-
390-
flasher.erase_region(offset, size)?;
391-
}
392-
393364
// Load the ELF data, optionally using the provider bootloader/partition
394365
// table/image format, to the device's flash memory.
395366
flasher.load_elf_to_flash_with_format(
@@ -406,11 +377,24 @@ pub fn flash_elf_image(
406377
Ok(())
407378
}
408379

380+
pub fn parse_partition_table(path: &Path) -> Result<PartitionTable> {
381+
let data = fs::read(path)
382+
.into_diagnostic()
383+
.wrap_err("Failed to open partition table")?;
384+
PartitionTable::try_from(data).into_diagnostic()
385+
}
386+
387+
pub fn erase_partition(flasher: &mut Flasher, part: &Partition) -> Result<()> {
388+
log::info!("Erasing {} ({:?})...", part.name(), part.subtype());
389+
let offset = part.offset();
390+
let size = part.size();
391+
flasher.erase_region(offset, size).into_diagnostic()
392+
}
393+
409394
/// Convert and display CSV and binary partition tables
410395
pub fn partition_table(args: PartitionTableArgs) -> Result<()> {
411396
if args.to_binary {
412-
let input = fs::read_to_string(&args.partition_table).into_diagnostic()?;
413-
let table = PartitionTable::try_from_str(input).into_diagnostic()?;
397+
let table = parse_partition_table(&args.partition_table)?;
414398

415399
// Use either stdout or a file if provided for the output.
416400
let mut writer: Box<dyn Write> = if let Some(output) = args.output {

espflash/src/error.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,13 +395,18 @@ impl<T> ResultExt for Result<T, Error> {
395395
}
396396

397397
#[derive(Debug, Error, Diagnostic)]
398-
#[error("No otadata partition was found")]
398+
#[error("Missing partition")]
399399
#[diagnostic(
400-
code(espflash::partition_table::no_otadata),
401-
help("Partition table must contain an otadata partition when trying to erase it")
400+
code(espflash::partition_table::missing_partition),
401+
help("Partition table must contain the partition of type `{0}` that is going to be erased")
402402
)]
403+
pub struct MissingPartition(String);
403404

404-
pub struct NoOtadataError;
405+
impl From<String> for MissingPartition {
406+
fn from(part: String) -> Self {
407+
MissingPartition(part)
408+
}
409+
}
405410

406411
#[derive(Debug, Error, Diagnostic)]
407412
#[error("Missing partition table")]

0 commit comments

Comments
 (0)