Skip to content

Commit 2f3d110

Browse files
Add before and after reset arguments (#561)
* feat: Initial implementation of reset args * feat: Add after reset methods * feat: Allow monitoring without reseting * style: Fix clippy warnings * feat: Use reset_after when possible to take into account `--after` flag * feat: Add boot_mode and download mode checks * docs: Update changelog * chore: Cleanup comments * style: Use info! instead of println!
1 parent 9721049 commit 2f3d110

File tree

10 files changed

+331
-29
lines changed

10 files changed

+331
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Add `serialport` feature. (#535)
2323
- Add support for 26 MHz bootloader for ESP32 and ESP32-C2 (#553)
2424
- Add CI check to verify that CHANGELOG is updated (#560)
25+
- Add `--before` and `--after` reset arguments (#561)
2526

2627
### Fixed
2728

cargo-espflash/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,11 @@ pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
252252
};
253253

254254
info!("Erasing the following partitions: {:?}", args.erase_parts);
255+
let chip: Chip = flash.chip();
255256
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
256-
flash.connection().reset()?;
257+
flash
258+
.connection()
259+
.reset_after(!args.connect_args.no_stub, chip)?;
257260

258261
Ok(())
259262
}

espflash/src/bin/espflash.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,11 @@ pub fn erase_parts(args: ErasePartsArgs, config: &Config) -> Result<()> {
195195
};
196196

197197
info!("Erasing the following partitions: {:?}", args.erase_parts);
198+
let chip = flash.chip();
198199
erase_partitions(&mut flash, partition_table, Some(args.erase_parts), None)?;
199-
flash.connection().reset()?;
200+
flash
201+
.connection()
202+
.reset_after(!args.connect_args.no_stub, chip)?;
200203

201204
Ok(())
202205
}

espflash/src/cli/mod.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use clap_complete::Shell;
1818
use comfy_table::{modifiers, presets::UTF8_FULL, Attribute, Cell, Color, Table};
1919
use esp_idf_part::{DataType, Partition, PartitionTable};
2020
use indicatif::{style::ProgressStyle, HumanCount, ProgressBar};
21-
use log::{debug, info};
21+
use log::{debug, info, warn};
2222
use miette::{IntoDiagnostic, Result, WrapErr};
2323
use serialport::{SerialPortType, UsbPortInfo};
2424

@@ -28,6 +28,7 @@ use self::{
2828
serial::get_serial_port_info,
2929
};
3030
use crate::{
31+
connection::reset::{ResetAfterOperation, ResetBeforeOperation},
3132
elf::ElfFirmwareImage,
3233
error::{Error, MissingPartition, MissingPartitionTable},
3334
flasher::{
@@ -48,9 +49,15 @@ mod serial;
4849
#[derive(Debug, Args)]
4950
#[non_exhaustive]
5051
pub struct ConnectArgs {
52+
/// Reset operation to perform after connecting to the target
53+
#[arg(short = 'a', long, default_value = "hard-reset")]
54+
pub after: ResetAfterOperation,
5155
/// Baud rate at which to communicate with target device
52-
#[arg(short = 'b', long, env = "ESPFLASH_BAUD")]
56+
#[arg(short = 'B', long, env = "ESPFLASH_BAUD")]
5357
pub baud: Option<u32>,
58+
/// Reset operation to perform before connecting to the target
59+
#[arg(short = 'b', long, default_value = "default-reset")]
60+
pub before: ResetBeforeOperation,
5461
/// Target device
5562
#[arg(short = 'c', long)]
5663
pub chip: Option<Chip>,
@@ -263,6 +270,15 @@ pub fn connect(
263270
no_verify: bool,
264271
no_skip: bool,
265272
) -> Result<Flasher> {
273+
if args.before == ResetBeforeOperation::NoReset
274+
|| args.before == ResetBeforeOperation::NoResetNoSync
275+
{
276+
warn!(
277+
"Pre-connection option '{:#?}' was selected. Connection may fail if the chip is not in bootloader or flasher stub mode.",
278+
args.before
279+
);
280+
}
281+
266282
let port_info = get_serial_port_info(args, config)?;
267283

268284
// Attempt to open the serial port and set its initial baud rate.
@@ -305,6 +321,8 @@ pub fn connect(
305321
!no_verify,
306322
!no_skip,
307323
args.chip,
324+
args.after,
325+
args.before,
308326
)?)
309327
}
310328

@@ -547,8 +565,10 @@ pub fn erase_flash(args: EraseFlashArgs, config: &Config) -> Result<()> {
547565

548566
info!("Erasing Flash...");
549567
flash.erase_flash()?;
550-
551-
flash.connection().reset()?;
568+
let chip = flash.chip();
569+
flash
570+
.connection()
571+
.reset_after(!args.connect_args.no_stub, chip)?;
552572

553573
Ok(())
554574
}
@@ -565,7 +585,10 @@ pub fn erase_region(args: EraseRegionArgs, config: &Config) -> Result<()> {
565585
args.addr, args.size
566586
);
567587
flash.erase_region(args.addr, args.size)?;
568-
flash.connection().reset()?;
588+
let chip = flash.chip();
589+
flash
590+
.connection()
591+
.reset_after(!args.connect_args.no_stub, chip)?;
569592

570593
Ok(())
571594
}

espflash/src/command.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub enum CommandType {
4242
// Some commands supported by stub only
4343
EraseFlash = 0xd0,
4444
EraseRegion = 0xd1,
45+
RunUserCode = 0xd3,
4546
}
4647

4748
impl CommandType {
@@ -165,6 +166,7 @@ pub enum Command<'a> {
165166
offset: u32,
166167
size: u32,
167168
},
169+
RunUserCode,
168170
}
169171

170172
impl<'a> Command<'a> {
@@ -191,6 +193,7 @@ impl<'a> Command<'a> {
191193
Command::EraseFlash { .. } => CommandType::EraseFlash,
192194
Command::EraseRegion { .. } => CommandType::EraseRegion,
193195
Command::FlashMd5 { .. } => CommandType::FlashMd5,
196+
Command::RunUserCode { .. } => CommandType::RunUserCode,
194197
}
195198
}
196199

@@ -379,6 +382,9 @@ impl<'a> Command<'a> {
379382
writer.write_all(&(0u32.to_le_bytes()))?;
380383
writer.write_all(&(0u32.to_le_bytes()))?;
381384
}
385+
Command::RunUserCode => {
386+
write_basic(writer, &[], 0)?;
387+
}
382388
};
383389
Ok(())
384390
}

espflash/src/connection/mod.rs

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,29 @@ use std::{
1111
time::Duration,
1212
};
1313

14-
use log::debug;
14+
use log::{debug, info};
15+
use regex::Regex;
1516
use serialport::UsbPortInfo;
1617
use slip_codec::SlipDecoder;
1718

1819
#[cfg(unix)]
1920
use self::reset::UnixTightReset;
2021
use self::{
2122
encoder::SlipEncoder,
22-
reset::{construct_reset_strategy_sequence, ClassicReset, ResetStrategy, UsbJtagSerialReset},
23+
reset::{
24+
construct_reset_strategy_sequence, ClassicReset, HardReset, ResetAfterOperation,
25+
ResetBeforeOperation, ResetStrategy, UsbJtagSerialReset,
26+
},
2327
};
2428
use crate::{
2529
command::{Command, CommandType},
30+
connection::reset::soft_reset,
2631
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
2732
interface::Interface,
33+
targets::Chip,
2834
};
2935

30-
mod reset;
36+
pub mod reset;
3137

3238
const MAX_CONNECT_ATTEMPTS: usize = 7;
3339
const MAX_SYNC_ATTEMPTS: usize = 5;
@@ -77,21 +83,34 @@ pub struct Connection {
7783
serial: Interface,
7884
port_info: UsbPortInfo,
7985
decoder: SlipDecoder,
86+
after_operation: ResetAfterOperation,
87+
before_operation: ResetBeforeOperation,
8088
}
8189

8290
impl Connection {
83-
pub fn new(serial: Interface, port_info: UsbPortInfo) -> Self {
91+
pub fn new(
92+
serial: Interface,
93+
port_info: UsbPortInfo,
94+
after_operation: ResetAfterOperation,
95+
before_operation: ResetBeforeOperation,
96+
) -> Self {
8497
Connection {
8598
serial,
8699
port_info,
87100
decoder: SlipDecoder::new(),
101+
after_operation,
102+
before_operation,
88103
}
89104
}
90105

91106
/// Initialize a connection with a device
92107
pub fn begin(&mut self) -> Result<(), Error> {
93108
let port_name = self.serial.serial_port().name().unwrap_or_default();
94-
let reset_sequence = construct_reset_strategy_sequence(&port_name, self.port_info.pid);
109+
let reset_sequence = construct_reset_strategy_sequence(
110+
&port_name,
111+
self.port_info.pid,
112+
self.before_operation,
113+
);
95114

96115
for (_, reset_strategy) in zip(0..MAX_CONNECT_ATTEMPTS, reset_sequence.iter().cycle()) {
97116
match self.connect_attempt(reset_strategy) {
@@ -110,7 +129,47 @@ impl Connection {
110129
/// Try to connect to a device
111130
#[allow(clippy::borrowed_box)]
112131
fn connect_attempt(&mut self, reset_strategy: &Box<dyn ResetStrategy>) -> Result<(), Error> {
113-
reset_strategy.reset(&mut self.serial)?;
132+
// If we're doing no_sync, we're likely communicating as a pass through
133+
// with an intermediate device to the ESP32
134+
if self.before_operation == ResetBeforeOperation::NoResetNoSync {
135+
return Ok(());
136+
}
137+
let mut download_mode: bool = false;
138+
let mut boot_mode: &str = "";
139+
let mut boot_log_detected = false;
140+
let mut buff: Vec<u8>;
141+
if self.before_operation != ResetBeforeOperation::NoReset {
142+
// Reset the chip to bootloader (download mode)
143+
reset_strategy.reset(&mut self.serial)?;
144+
145+
let available_bytes = self.serial.serial_port_mut().bytes_to_read()?;
146+
buff = vec![0; available_bytes as usize];
147+
let read_bytes = self.serial.serial_port_mut().read(&mut buff)? as u32;
148+
149+
if read_bytes != available_bytes {
150+
return Err(Error::Connection(ConnectionError::ReadMissmatch(
151+
available_bytes,
152+
read_bytes,
153+
)));
154+
}
155+
156+
let read_slice = std::str::from_utf8(&buff[..read_bytes as usize]).unwrap();
157+
158+
let pattern = Regex::new(r"boot:(0x[0-9a-fA-F]+)(.*waiting for download)?").unwrap();
159+
160+
// Search for the pattern in the read data
161+
if let Some(data) = pattern.captures(read_slice) {
162+
boot_log_detected = true;
163+
// Boot log detected
164+
boot_mode = data.get(1).map(|m| m.as_str()).unwrap_or_default();
165+
download_mode = data.get(2).is_some();
166+
167+
// Further processing or printing the results
168+
debug!("Boot Mode: {}", boot_mode);
169+
debug!("Download Mode: {}", download_mode);
170+
};
171+
}
172+
114173
for _ in 0..MAX_SYNC_ATTEMPTS {
115174
self.flush()?;
116175

@@ -119,6 +178,16 @@ impl Connection {
119178
}
120179
}
121180

181+
if boot_log_detected {
182+
if download_mode {
183+
return Err(Error::Connection(ConnectionError::NoSyncReply));
184+
} else {
185+
return Err(Error::Connection(ConnectionError::WrongBootMode(
186+
boot_mode.to_string(),
187+
)));
188+
}
189+
}
190+
122191
Err(Error::Connection(ConnectionError::ConnectionFailed))
123192
}
124193

@@ -163,6 +232,28 @@ impl Connection {
163232
Ok(())
164233
}
165234

235+
// Reset the device taking into account the reset after argument
236+
pub fn reset_after(&mut self, is_stub: bool, chip: Chip) -> Result<(), Error> {
237+
match self.after_operation {
238+
ResetAfterOperation::HardReset => HardReset.reset(&mut self.serial),
239+
ResetAfterOperation::SoftReset => {
240+
info!("Soft resetting");
241+
soft_reset(self, false, is_stub, chip)?;
242+
Ok(())
243+
}
244+
ResetAfterOperation::NoReset => {
245+
info!("Staying in bootloader");
246+
soft_reset(self, true, is_stub, chip)?;
247+
248+
Ok(())
249+
}
250+
ResetAfterOperation::NoResetNoStub => {
251+
info!("Staying in flasher stub");
252+
Ok(())
253+
}
254+
}
255+
}
256+
166257
// Reset the device to flash mode
167258
pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
168259
if self.port_info.pid == USB_SERIAL_JTAG_PID {

0 commit comments

Comments
 (0)