Skip to content

Commit e90e6db

Browse files
authored
Merge pull request #71 from esp-rs/command-rework
Rework command writing to move encoding to a command enum
2 parents 65f7873 + 574ba62 commit e90e6db

File tree

10 files changed

+551
-382
lines changed

10 files changed

+551
-382
lines changed

espflash/src/command.rs

Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
use crate::flasher::{checksum, SpiAttachParams, CHECKSUM_INIT};
2+
use bytemuck::{bytes_of, Pod, Zeroable};
3+
use std::io::Write;
4+
use std::mem::size_of;
5+
use std::time::Duration;
6+
use strum_macros::Display;
7+
8+
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
9+
const ERASE_REGION_TIMEOUT_PER_MB: Duration = Duration::from_secs(30);
10+
const ERASE_WRITE_TIMEOUT_PER_MB: Duration = Duration::from_secs(40);
11+
const MEM_END_TIMEOUT: Duration = Duration::from_millis(50);
12+
const SYNC_TIMEOUT: Duration = Duration::from_millis(100);
13+
14+
#[derive(Copy, Clone, Debug, Display)]
15+
#[allow(dead_code)]
16+
#[repr(u8)]
17+
#[non_exhaustive]
18+
pub enum CommandType {
19+
Unknown = 0,
20+
FlashBegin = 0x02,
21+
FlashData = 0x03,
22+
FlashEnd = 0x04,
23+
MemBegin = 0x05,
24+
MemEnd = 0x06,
25+
MemData = 0x07,
26+
Sync = 0x08,
27+
WriteReg = 0x09,
28+
ReadReg = 0x0a,
29+
SpiSetParams = 0x0B,
30+
SpiAttach = 0x0D,
31+
ChangeBaud = 0x0F,
32+
FlashDeflateBegin = 0x10,
33+
FlashDeflateData = 0x11,
34+
FlashDeflateEnd = 0x12,
35+
FlashMd5 = 0x13,
36+
FlashDetect = 0x9f,
37+
}
38+
39+
impl CommandType {
40+
pub fn timeout(&self) -> Duration {
41+
match self {
42+
CommandType::MemEnd => MEM_END_TIMEOUT,
43+
CommandType::Sync => SYNC_TIMEOUT,
44+
_ => DEFAULT_TIMEOUT,
45+
}
46+
}
47+
48+
pub fn timeout_for_size(&self, size: u32) -> Duration {
49+
fn calc_timeout(timeout_per_mb: Duration, size: u32) -> Duration {
50+
let mb = size as f64 / 1_000_000.0;
51+
std::cmp::max(
52+
DEFAULT_TIMEOUT,
53+
Duration::from_millis((timeout_per_mb.as_millis() as f64 * mb) as u64),
54+
)
55+
}
56+
match self {
57+
CommandType::FlashBegin | CommandType::FlashDeflateBegin => {
58+
calc_timeout(ERASE_REGION_TIMEOUT_PER_MB, size)
59+
}
60+
CommandType::FlashData | CommandType::FlashDeflateData => {
61+
calc_timeout(ERASE_WRITE_TIMEOUT_PER_MB, size)
62+
}
63+
_ => self.timeout(),
64+
}
65+
}
66+
}
67+
68+
#[derive(Copy, Clone, Debug)]
69+
pub enum Command<'a> {
70+
FlashBegin {
71+
size: u32,
72+
blocks: u32,
73+
block_size: u32,
74+
offset: u32,
75+
supports_encryption: bool,
76+
},
77+
FlashData {
78+
data: &'a [u8],
79+
pad_to: usize,
80+
pad_byte: u8,
81+
sequence: u32,
82+
},
83+
FlashEnd {
84+
reboot: bool,
85+
},
86+
MemBegin {
87+
size: u32,
88+
blocks: u32,
89+
block_size: u32,
90+
offset: u32,
91+
supports_encryption: bool,
92+
},
93+
MemData {
94+
data: &'a [u8],
95+
pad_to: usize,
96+
pad_byte: u8,
97+
sequence: u32,
98+
},
99+
MemEnd {
100+
no_entry: bool,
101+
entry: u32,
102+
},
103+
Sync,
104+
WriteReg {
105+
address: u32,
106+
value: u32,
107+
mask: Option<u32>,
108+
},
109+
ReadReg {
110+
address: u32,
111+
},
112+
SpiAttach {
113+
spi_params: SpiAttachParams,
114+
},
115+
ChangeBaud {
116+
speed: u32,
117+
},
118+
FlashDeflateBegin {
119+
size: u32,
120+
blocks: u32,
121+
block_size: u32,
122+
offset: u32,
123+
supports_encryption: bool,
124+
},
125+
FlashDeflateData {
126+
data: &'a [u8],
127+
pad_to: usize,
128+
pad_byte: u8,
129+
sequence: u32,
130+
},
131+
FlashDeflateEnd {
132+
reboot: bool,
133+
},
134+
FlashDetect,
135+
}
136+
137+
impl<'a> Command<'a> {
138+
pub fn command_type(&self) -> CommandType {
139+
match self {
140+
Command::FlashBegin { .. } => CommandType::FlashBegin,
141+
Command::FlashData { .. } => CommandType::FlashData,
142+
Command::FlashEnd { .. } => CommandType::FlashEnd,
143+
Command::MemBegin { .. } => CommandType::MemBegin,
144+
Command::MemData { .. } => CommandType::MemData,
145+
Command::MemEnd { .. } => CommandType::MemEnd,
146+
Command::Sync => CommandType::Sync,
147+
Command::WriteReg { .. } => CommandType::WriteReg,
148+
Command::ReadReg { .. } => CommandType::ReadReg,
149+
Command::SpiAttach { .. } => CommandType::SpiAttach,
150+
Command::ChangeBaud { .. } => CommandType::ChangeBaud,
151+
Command::FlashDeflateBegin { .. } => CommandType::FlashDeflateBegin,
152+
Command::FlashDeflateData { .. } => CommandType::FlashDeflateData,
153+
Command::FlashDeflateEnd { .. } => CommandType::FlashDeflateEnd,
154+
Command::FlashDetect => CommandType::FlashDetect,
155+
}
156+
}
157+
158+
pub fn timeout_for_size(&self, size: u32) -> Duration {
159+
self.command_type().timeout_for_size(size)
160+
}
161+
162+
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
163+
writer.write_all(&[0, self.command_type() as u8])?;
164+
match *self {
165+
Command::FlashBegin {
166+
size,
167+
blocks,
168+
block_size,
169+
offset,
170+
supports_encryption,
171+
} => {
172+
begin_command(
173+
writer,
174+
size,
175+
blocks,
176+
block_size,
177+
offset,
178+
supports_encryption,
179+
)?;
180+
}
181+
Command::FlashData {
182+
pad_to,
183+
pad_byte,
184+
data,
185+
sequence,
186+
} => {
187+
data_command(writer, data, pad_to, pad_byte, sequence)?;
188+
}
189+
Command::FlashEnd { reboot } => {
190+
write_basic(writer, &[if reboot { 0 } else { 1 }], 0)?;
191+
}
192+
Command::MemBegin {
193+
size,
194+
blocks,
195+
block_size,
196+
offset,
197+
supports_encryption,
198+
} => {
199+
begin_command(
200+
writer,
201+
size,
202+
blocks,
203+
block_size,
204+
offset,
205+
supports_encryption,
206+
)?;
207+
}
208+
Command::MemData {
209+
pad_to,
210+
pad_byte,
211+
data,
212+
sequence,
213+
} => {
214+
data_command(writer, data, pad_to, pad_byte, sequence)?;
215+
}
216+
Command::MemEnd {
217+
no_entry: reboot,
218+
entry,
219+
} => {
220+
#[derive(Zeroable, Pod, Copy, Clone)]
221+
#[repr(C)]
222+
struct EntryParams {
223+
no_entry: u32,
224+
entry: u32,
225+
}
226+
let params = EntryParams {
227+
no_entry: if reboot { 1 } else { 0 },
228+
entry,
229+
};
230+
write_basic(writer, bytes_of(&params), 0)?;
231+
}
232+
Command::Sync => {
233+
write_basic(
234+
writer,
235+
&[
236+
0x07, 0x07, 0x12, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
237+
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
238+
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
239+
],
240+
0,
241+
)?;
242+
}
243+
Command::WriteReg {
244+
address,
245+
value,
246+
mask,
247+
} => {
248+
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
249+
#[repr(C)]
250+
struct WriteRegParams {
251+
addr: u32,
252+
value: u32,
253+
mask: u32,
254+
delay_us: u32,
255+
}
256+
let params = WriteRegParams {
257+
addr: address,
258+
value,
259+
mask: mask.unwrap_or(0xFFFFFFFF),
260+
delay_us: 0,
261+
};
262+
write_basic(writer, bytes_of(&params), 0)?;
263+
}
264+
Command::ReadReg { address } => {
265+
write_basic(writer, &address.to_le_bytes(), 0)?;
266+
}
267+
Command::SpiAttach { spi_params } => {
268+
write_basic(writer, &spi_params.encode(), 0)?;
269+
}
270+
Command::ChangeBaud { speed } => {
271+
// length
272+
writer.write_all(&(8u16.to_le_bytes()))?;
273+
// checksum
274+
writer.write_all(&(0u32.to_le_bytes()))?;
275+
// data
276+
writer.write_all(&speed.to_le_bytes())?;
277+
writer.write_all(&0u32.to_le_bytes())?;
278+
}
279+
Command::FlashDeflateBegin {
280+
size,
281+
blocks,
282+
block_size,
283+
offset,
284+
supports_encryption,
285+
} => {
286+
begin_command(
287+
writer,
288+
size,
289+
blocks,
290+
block_size,
291+
offset,
292+
supports_encryption,
293+
)?;
294+
}
295+
Command::FlashDeflateData {
296+
pad_to,
297+
pad_byte,
298+
data,
299+
sequence,
300+
} => {
301+
data_command(writer, data, pad_to, pad_byte, sequence)?;
302+
}
303+
Command::FlashDeflateEnd { reboot } => {
304+
write_basic(writer, &[if reboot { 0 } else { 1 }], 0)?;
305+
}
306+
Command::FlashDetect => {
307+
write_basic(writer, &[], 0)?;
308+
}
309+
};
310+
Ok(())
311+
}
312+
}
313+
314+
fn write_basic<W: Write>(mut writer: W, data: &[u8], checksum: u32) -> std::io::Result<()> {
315+
writer.write_all(&((data.len() as u16).to_le_bytes()))?;
316+
writer.write_all(&(checksum.to_le_bytes()))?;
317+
writer.write_all(data)?;
318+
Ok(())
319+
}
320+
321+
fn begin_command<W: Write>(
322+
writer: W,
323+
size: u32,
324+
blocks: u32,
325+
block_size: u32,
326+
offset: u32,
327+
supports_encryption: bool,
328+
) -> std::io::Result<()> {
329+
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
330+
#[repr(C)]
331+
struct BeginParams {
332+
size: u32,
333+
blocks: u32,
334+
block_size: u32,
335+
offset: u32,
336+
encrypted: u32,
337+
}
338+
let params = BeginParams {
339+
size,
340+
blocks,
341+
block_size,
342+
offset,
343+
encrypted: 0,
344+
};
345+
346+
let bytes = bytes_of(&params);
347+
let data = if !supports_encryption {
348+
// The ESP32 and ESP8266 do not take the `encrypted` field, so truncate the last
349+
// 4 bytes of the slice where it resides.
350+
let end = bytes.len() - 4;
351+
&bytes[0..end]
352+
} else {
353+
bytes
354+
};
355+
write_basic(writer, data, 0)
356+
}
357+
358+
fn data_command<W: Write>(
359+
mut writer: W,
360+
block_data: &[u8],
361+
pad_to: usize,
362+
pad_byte: u8,
363+
sequence: u32,
364+
) -> std::io::Result<()> {
365+
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
366+
#[repr(C)]
367+
struct BlockParams {
368+
size: u32,
369+
sequence: u32,
370+
dummy1: u32,
371+
dummy2: u32,
372+
}
373+
374+
let pad_length = pad_to.saturating_sub(block_data.len());
375+
376+
let params = BlockParams {
377+
size: (block_data.len() + pad_length) as u32,
378+
sequence,
379+
dummy1: 0,
380+
dummy2: 0,
381+
};
382+
383+
let mut check = checksum(block_data, CHECKSUM_INIT);
384+
385+
for _ in 0..pad_length {
386+
check = checksum(&[pad_byte], check);
387+
}
388+
389+
let total_length = size_of::<BlockParams>() + block_data.len() + pad_length;
390+
writer.write_all(&((total_length as u16).to_le_bytes()))?;
391+
writer.write_all(&((check as u32).to_le_bytes()))?;
392+
writer.write_all(bytes_of(&params))?;
393+
writer.write_all(block_data)?;
394+
for _ in 0..pad_length {
395+
writer.write_all(&[pad_byte])?;
396+
}
397+
Ok(())
398+
}

0 commit comments

Comments
 (0)