Skip to content

Commit 2c7ee4c

Browse files
Implement reset strategies (#487)
* feat: Implement reset strategies * refactor: Use libc instead of nix * feat: Add debug message when reset fails * fix: Fix clippy lint * docs: Update changelog
1 parent 2e73c4f commit 2c7ee4c

File tree

7 files changed

+309
-96
lines changed

7 files changed

+309
-96
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
[Unreleased]
99

1010
### Added
11+
- Added reset strategies (#487)
1112

1213
- Read esp-println generated defmt messages (#466)
1314
- Add --target-app-partition argument to flash command (#461)

Cargo.lock

Lines changed: 1 addition & 0 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: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,11 @@ name = "cargo-espflash"
33
version = "3.0.0-dev"
44
edition = "2021"
55
rust-version = "1.70"
6-
description = "Cargo subcommand for flashing Espressif devices"
7-
repository = "https://github.com/esp-rs/espflash"
8-
license = "MIT OR Apache-2.0"
9-
keywords = [
10-
"cargo",
11-
"cli",
12-
"embedded",
13-
"esp",
14-
]
15-
categories = [
16-
"command-line-utilities",
17-
"development-tools",
18-
"development-tools::cargo-plugins",
19-
"embedded",
20-
]
6+
description = "Cargo subcommand for flashing Espressif devices"
7+
repository = "https://github.com/esp-rs/espflash"
8+
license = "MIT OR Apache-2.0"
9+
keywords = ["cargo", "cli", "embedded", "esp"]
10+
categories = ["command-line-utilities", "development-tools", "development-tools::cargo-plugins", "embedded"]
2111

2212
[package.metadata.binstall]
2313
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }"

espflash/Cargo.toml

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,11 @@ name = "espflash"
33
version = "3.0.0-dev"
44
edition = "2021"
55
rust-version = "1.70"
6-
description = "A command-line tool for flashing Espressif devices"
7-
repository = "https://github.com/esp-rs/espflash"
8-
license = "MIT OR Apache-2.0"
9-
keywords = [
10-
"cli",
11-
"embedded",
12-
"esp",
13-
]
14-
categories = [
15-
"command-line-utilities",
16-
"development-tools",
17-
"embedded",
18-
]
6+
description = "A command-line tool for flashing Espressif devices"
7+
repository = "https://github.com/esp-rs/espflash"
8+
license = "MIT OR Apache-2.0"
9+
keywords = ["cli", "embedded", "esp"]
10+
categories = ["command-line-utilities", "development-tools", "embedded"]
1911

2012
[package.metadata.binstall]
2113
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }"
@@ -26,8 +18,8 @@ pkg-fmt = "zip"
2618
rustdoc-args = ["--cfg", "docsrs"]
2719

2820
[[bin]]
29-
name = "espflash"
30-
path = "./src/bin/espflash.rs"
21+
name = "espflash"
22+
path = "./src/bin/espflash.rs"
3123
required-features = ["cli"]
3224

3325
[dependencies]
@@ -64,7 +56,10 @@ strum = { version = "0.25.0", features = ["derive"] }
6456
thiserror = "1.0.49"
6557
toml = "0.8.2"
6658
update-informer = { version = "1.1.0", optional = true }
67-
xmas-elf = "0.9.0"
59+
xmas-elf = "0.9.0"
60+
61+
[target.'cfg(unix)'.dependencies]
62+
libc = "0.2.101"
6863

6964
[features]
7065
default = ["cli"]
Lines changed: 48 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,29 @@
44
//! sending/decoding of commands, and provides higher-level operations with the
55
//! device.
66
7-
use std::{io::BufWriter, thread::sleep, time::Duration};
7+
use std::{io::BufWriter, iter::zip, thread::sleep, time::Duration};
88

99
use binrw::{io::Cursor, BinRead, BinReaderExt};
10-
use log::{debug, info};
10+
use log::debug;
1111
use serialport::UsbPortInfo;
1212
use slip_codec::SlipDecoder;
1313

14-
use self::encoder::SlipEncoder;
14+
#[cfg(unix)]
15+
use self::reset::UnixTightReset;
16+
use self::{
17+
encoder::SlipEncoder,
18+
reset::{construct_reset_strategy_sequence, ClassicReset, ResetStrategy, UsbJtagSerialReset},
19+
};
1520
use crate::{
1621
command::{Command, CommandType},
1722
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
1823
interface::Interface,
1924
};
2025

21-
const DEFAULT_CONNECT_ATTEMPTS: usize = 7;
26+
mod reset;
27+
28+
const MAX_CONNECT_ATTEMPTS: usize = 7;
29+
const MAX_SYNC_ATTEMPTS: usize = 5;
2230
pub(crate) const USB_SERIAL_JTAG_PID: u16 = 0x1001;
2331

2432
/// A response from a target device following a command
@@ -50,29 +58,30 @@ impl Connection {
5058

5159
/// Initialize a connection with a device
5260
pub fn begin(&mut self) -> Result<(), Error> {
53-
let mut extra_delay = false;
54-
for _ in 0..DEFAULT_CONNECT_ATTEMPTS {
55-
if self.connect_attempt(extra_delay).is_err() {
56-
extra_delay = !extra_delay;
57-
58-
info!(
59-
"Unable to connect, retrying with {} delay...",
60-
if extra_delay { "extra" } else { "default" }
61-
);
62-
} else {
63-
return Ok(());
61+
let port_name = self.serial.serial_port().name().unwrap_or_default();
62+
let reset_sequence = construct_reset_strategy_sequence(&port_name, self.port_info.pid);
63+
64+
for (_, reset_strategy) in zip(0..MAX_CONNECT_ATTEMPTS, reset_sequence.iter().cycle()) {
65+
match self.connect_attempt(reset_strategy) {
66+
Ok(_) => {
67+
return Ok(());
68+
}
69+
Err(e) => {
70+
debug!("Failed to reset, error {:#?}, retrying", e);
71+
}
6472
}
6573
}
6674

6775
Err(Error::Connection(ConnectionError::ConnectionFailed))
6876
}
6977

7078
/// Try to connect to a device
71-
fn connect_attempt(&mut self, extra_delay: bool) -> Result<(), Error> {
72-
self.reset_to_flash(extra_delay)?;
73-
74-
for _ in 0..5 {
79+
#[allow(clippy::borrowed_box)]
80+
fn connect_attempt(&mut self, reset_strategy: &Box<dyn ResetStrategy>) -> Result<(), Error> {
81+
reset_strategy.reset(&mut self.serial)?;
82+
for _ in 0..MAX_SYNC_ATTEMPTS {
7583
self.flush()?;
84+
7685
if self.sync().is_ok() {
7786
return Ok(());
7887
}
@@ -86,12 +95,14 @@ impl Connection {
8695
self.with_timeout(CommandType::Sync.timeout(), |connection| {
8796
connection.command(Command::Sync)?;
8897
connection.flush()?;
98+
8999
sleep(Duration::from_millis(10));
90-
for _ in 0..7 {
100+
101+
for _ in 0..MAX_CONNECT_ATTEMPTS {
91102
match connection.read_response()? {
92103
Some(response) if response.return_op == CommandType::Sync as u8 => {
93104
if response.status == 1 {
94-
let _error = connection.flush();
105+
connection.flush().ok();
95106
return Err(Error::RomError(RomError::new(
96107
CommandType::Sync,
97108
RomErrorKind::from(response.error),
@@ -115,47 +126,26 @@ impl Connection {
115126

116127
// Reset the device
117128
pub fn reset(&mut self) -> Result<(), Error> {
118-
let pid = self.port_info.pid;
119-
Ok(reset_after_flash(&mut self.serial, pid)?)
129+
reset_after_flash(&mut self.serial, self.port_info.pid)?;
130+
131+
Ok(())
120132
}
121133

122134
// Reset the device to flash mode
123135
pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
124136
if self.port_info.pid == USB_SERIAL_JTAG_PID {
125-
self.serial.write_data_terminal_ready(false)?;
126-
self.serial.write_request_to_send(false)?;
127-
128-
sleep(Duration::from_millis(100));
129-
130-
self.serial.write_data_terminal_ready(true)?;
131-
self.serial.write_request_to_send(false)?;
132-
133-
sleep(Duration::from_millis(100));
134-
135-
self.serial.write_request_to_send(true)?;
136-
self.serial.write_data_terminal_ready(false)?;
137-
self.serial.write_request_to_send(true)?;
138-
139-
sleep(Duration::from_millis(100));
140-
141-
self.serial.write_data_terminal_ready(false)?;
142-
self.serial.write_request_to_send(false)?;
137+
UsbJtagSerialReset.reset(&mut self.serial)
143138
} else {
144-
self.serial.write_data_terminal_ready(false)?;
145-
self.serial.write_request_to_send(true)?;
146-
147-
sleep(Duration::from_millis(100));
148-
149-
self.serial.write_data_terminal_ready(true)?;
150-
self.serial.write_request_to_send(false)?;
151-
152-
let millis = if extra_delay { 500 } else { 50 };
153-
sleep(Duration::from_millis(millis));
139+
#[cfg(unix)]
140+
if UnixTightReset::new(extra_delay)
141+
.reset(&mut self.serial)
142+
.is_ok()
143+
{
144+
return Ok(());
145+
}
154146

155-
self.serial.write_data_terminal_ready(false)?;
147+
ClassicReset::new(extra_delay).reset(&mut self.serial)
156148
}
157-
158-
Ok(())
159149
}
160150

161151
/// Set timeout for the serial port
@@ -177,11 +167,10 @@ impl Connection {
177167
}
178168

179169
/// Run a command with a timeout defined by the command type
180-
pub fn with_timeout<T, F: FnMut(&mut Connection) -> Result<T, Error>>(
181-
&mut self,
182-
timeout: Duration,
183-
mut f: F,
184-
) -> Result<T, Error> {
170+
pub fn with_timeout<T, F>(&mut self, timeout: Duration, mut f: F) -> Result<T, Error>
171+
where
172+
F: FnMut(&mut Connection) -> Result<T, Error>,
173+
{
185174
let old_timeout = {
186175
let serial = self.serial.serial_port_mut();
187176
let old_timeout = serial.timeout();

0 commit comments

Comments
 (0)