Skip to content
Closed
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix Windows connection issue by aligning reset sequence with `esptool` including RTS/DTR workaround (#999)
- Fix board-info misreporting the crystal frequency of ESP32-C5 (#1005)
- Fix `esptool flash` getting stuck on `Sync` command
Comment thread
Dzuchun marked this conversation as resolved.
Outdated

### Removed

Expand Down
11 changes: 11 additions & 0 deletions espflash/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const SYNC_TIMEOUT: Duration = Duration::from_millis(100);
const FLASH_DEFLATE_END_TIMEOUT: Duration = Duration::from_secs(10);
const FLASH_MD5_TIMEOUT_PER_MB: Duration = Duration::from_secs(8);

const SYNC_MAX_LEN: u64 = 44;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a comment about why you chose 44

const DEFAULT_MAX_LEN: u64 = 8 * 1024 * 1024; // 8Mi by default

/// Input data for SYNC command (36 bytes: 0x07 0x07 0x12 0x20, followed by
/// 32 x 0x55)
const SYNC_FRAME: [u8; 36] = [
Expand Down Expand Up @@ -211,6 +214,14 @@ impl CommandType {
_ => self.timeout(),
}
}

/// Return a max response length for the given [`CommandType`]
pub fn max_response_len(&self) -> u64 {
Comment thread
SergioGasquez marked this conversation as resolved.
Outdated
match self {
CommandType::Sync => SYNC_MAX_LEN,
_ => DEFAULT_MAX_LEN,
}
}
}

/// Available commands
Expand Down
34 changes: 28 additions & 6 deletions espflash/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ impl Connection {
sleep(Duration::from_millis(10));

for _ in 0..MAX_CONNECT_ATTEMPTS {
match connection.read_response()? {
match connection.read_response_for_command(CommandType::Sync)? {
Some(response) if response.return_op == CommandType::Sync as u8 => {
if response.status == 1 {
connection.flush().ok();
Expand Down Expand Up @@ -542,9 +542,21 @@ impl Connection {
Ok(Some(header))
}

/// Reads the response from a serial port for a [`CommandType`].
pub fn read_response_for_command(
&mut self,
ty: CommandType,
) -> Result<Option<CommandResponse>, Error> {
self.read_response_bounded(ty.max_response_len())
.for_command(ty)
}

/// Reads the response from a serial port.
pub fn read_response(&mut self) -> Result<Option<CommandResponse>, Error> {
match self.read(10)? {
pub fn read_response_bounded(
&mut self,
max_len: u64,
) -> Result<Option<CommandResponse>, Error> {
match self.read_bounded(10, max_len)? {
None => Ok(None),
Some(response) => {
// Here is what esptool does: https://github.com/espressif/esptool/blob/81b2eaee261aed0d3d754e32c57959d6b235bfed/esptool/loader.py#L518
Expand Down Expand Up @@ -631,7 +643,7 @@ impl Connection {
let ty = command.command_type();
self.write_command(command).for_command(ty)?;
for _ in 0..100 {
match self.read_response().for_command(ty)? {
match self.read_response_for_command(ty)? {
Some(response) if response.return_op == ty as u8 => {
return if response.status != 0 {
let _error = self.flush();
Expand Down Expand Up @@ -693,11 +705,21 @@ impl Connection {
self.write_reg(addr, masked_old_value | masked_new_value, None)
}

/// Reads a register command with a timeout.
/// Reads a register command.
pub(crate) fn read(&mut self, len: usize) -> Result<Option<Vec<u8>>, Error> {
self.read_bounded(len, u64::MAX)
}

/// Reads a register command at most `max_len` bytes long.
pub(crate) fn read_bounded(
&mut self,
len: usize,
max_len: u64,
) -> Result<Option<Vec<u8>>, Error> {
let mut tmp = Vec::with_capacity(1024);
let mut serial = (&mut self.serial).take(max_len);
loop {
self.decoder.decode(&mut self.serial, &mut tmp)?;
self.decoder.decode(&mut serial, &mut tmp)?;
if tmp.len() >= len {
return Ok(Some(tmp));
}
Expand Down
Loading