1#[cfg(feature = "serialport")]
4use std::fmt::{Display, Formatter};
5
6use miette::Diagnostic;
7use std::io;
8use strum::VariantNames;
9use thiserror::Error;
10
11#[cfg(feature = "cli")]
12use crate::cli::monitor::parser::esp_defmt::DefmtError;
13#[cfg(feature = "serialport")]
14use crate::command::CommandType;
15use crate::{
16 flasher::{FlashFrequency, FlashSize},
17 targets::Chip,
18};
19#[cfg(feature = "serialport")]
20use slip_codec::SlipError;
21
22#[derive(Debug, Diagnostic, Error)]
24#[non_exhaustive]
25pub enum Error {
26 #[error("App partition not found")]
27 #[diagnostic(code(espflash::app_partition_not_found))]
28 AppPartitionNotFound,
29
30 #[error("Operation was cancelled by the user")]
31 #[diagnostic(code(espflash::cancelled))]
32 Cancelled,
33
34 #[error("Unrecognized magic value: {0:#x}")]
35 #[diagnostic(
36 code(espflash::chip_detect_error),
37 help("Supported chips are: {}\n\
38 If your chip is supported, try hard-resetting the device and try again",
39 Chip::VARIANTS.join(", "))
40 )]
41 ChipDetectError(u32),
42
43 #[error("Chip provided ({0}) with `-c/--chip` does not match the detected chip ({1})")]
44 #[diagnostic(
45 code(espflash::chip_missmatch),
46 help("Ensure that the correct chip is selected, or remove the `-c/--chip` option to autodetect the chip")
47 )]
48 ChipMismatch(String, String),
49
50 #[error("Chip not argument provided, this is required when using the `--before no-reset-no-sync` option")]
51 #[diagnostic(
52 code(espflash::chip_not_provided),
53 help("Ensure that you provide the `-c/--chip` option with the proper chip")
54 )]
55 ChipNotProvided,
56
57 #[error("Corrupt data, expected {0:2x?} bytes but receved {1:2x?} bytes")]
58 #[diagnostic(code(espflash::read_flash::corrupt_data))]
59 CorruptData(usize, usize),
60
61 #[error("MD5 digest missmatch: expected {0:2x?}, received: {1:2x?}")]
62 #[diagnostic(code(espflash::read_flash::digest_missmatch))]
63 DigestMissmatch(Vec<u8>, Vec<u8>),
64
65 #[error("Supplied ELF image can not be run from RAM, as it includes segments mapped to ROM addresses")]
66 #[diagnostic(
67 code(espflash::not_ram_loadable),
68 help("Either build the binary to be all in RAM, or remove the `--ram` option to load the image to flash")
69 )]
70 ElfNotRamLoadable,
71
72 #[error(
73 "Supplied ELF image of {0}B is too big, and doesn't fit configured app partition of {1}B"
74 )]
75 #[diagnostic(
76 code(espflash::image_too_big),
77 help("Reduce the size of the binary or increase the size of the app partition."),
78 url("https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#built-in-partition-tables")
79 )]
80 ElfTooBig(u32, u32),
81
82 #[error("Failed to connect to on-device flash")]
83 #[diagnostic(code(espflash::flash_connect))]
84 FlashConnect,
85
86 #[error("Expected MD5 digest (16 bytes), received: {0:#x} bytes")]
87 #[diagnostic(code(espflash::read_flash::incorrect_digest_length))]
88 IncorrectDigestLength(usize),
89
90 #[error("Incorrect response from the sutb/ROM loader")]
91 #[diagnostic(code(espflash::read_flash::incorrect_response))]
92 IncorrectReposnse,
93
94 #[error("The provided bootloader binary is invalid")]
95 InvalidBootloader,
96
97 #[error("Specified bootloader path is not a .bin file")]
98 #[diagnostic(code(espflash::invalid_bootloader_path))]
99 InvalidBootloaderPath,
100
101 #[error("The flash size '{0}' is invalid")]
102 #[diagnostic(
103 code(espflash::invalid_flash_size),
104 help("The accepted values are: {:?}", FlashSize::VARIANTS)
105 )]
106 InvalidFlashSize(String),
107
108 #[cfg(not(feature = "serialport"))]
109 #[error(transparent)]
110 IoError(#[from] io::Error),
111
112 #[error("Specified partition table path is not a .bin or .csv file")]
113 #[diagnostic(code(espflash::invalid_partition_table_path))]
114 InvalidPartitionTablePath,
115
116 #[error("No serial ports could be detected")]
117 #[diagnostic(
118 code(espflash::no_serial),
119 help("Make sure you have connected a device to the host system. If the device is connected but not listed, try using the `--list-all-ports` flag.")
120 )]
121 NoSerial,
122
123 #[error("Read more bytes than expected")]
124 #[diagnostic(code(espflash::read_flash::read_more_than_expected))]
125 ReadMoreThanExpected,
126
127 #[error("This command requires using the RAM stub")]
128 #[diagnostic(
129 code(espflash::stub_required),
130 help("Don't use the `--no-stub` option with the command")
131 )]
132 StubRequired,
133
134 #[error("The serial port '{0}' could not be found")]
135 #[diagnostic(
136 code(espflash::serial_not_found),
137 help("Make sure the correct device is connected to the host system")
138 )]
139 SerialNotFound(String),
140
141 #[error("The {chip} does not support {feature}")]
142 #[diagnostic(code(espflash::unsupported_feature))]
143 UnsupportedFeature { chip: Chip, feature: String },
144
145 #[error("Flash chip not supported, unrecognized flash ID: {0:#x}")]
146 #[diagnostic(code(espflash::unrecognized_flash))]
147 UnsupportedFlash(u8),
148
149 #[error("The specified flash frequency '{frequency}' is not supported by the {chip}")]
150 #[diagnostic(code(espflash::unsupported_flash_frequency))]
151 UnsupportedFlashFrequency {
152 chip: Chip,
153 frequency: FlashFrequency,
154 },
155
156 #[error(
157 "Minimum supported revision is {major}.{minor}, connected device's revision is {found_major}.{found_minor}"
158 )]
159 #[diagnostic(code(espflash::unsupported_chip_revision))]
160 UnsupportedChipRevision {
161 major: u16,
162 minor: u16,
163 found_major: u16,
164 found_minor: u16,
165 },
166
167 #[error("Failed to parse chip revision: {chip_rev}. Chip revision must be in the format `major.minor`")]
168 #[diagnostic(code(espflash::cli::parse_chip_rev_error))]
169 ParseChipRevError { chip_rev: String },
170
171 #[error("Error while connecting to device")]
172 #[diagnostic(transparent)]
173 Connection(#[source] ConnectionError),
174
175 #[error("Communication error while flashing device")]
176 #[diagnostic(transparent)]
177 Flashing(#[source] ConnectionError),
178
179 #[error("Supplied ELF image is not valid")]
180 #[diagnostic(
181 code(espflash::invalid_elf),
182 help("Try running `cargo clean` and rebuilding the image")
183 )]
184 InvalidElf(#[from] ElfError),
185
186 #[error("The bootloader returned an error")]
187 #[cfg(feature = "serialport")]
188 #[diagnostic(transparent)]
189 RomError(#[from] RomError),
190
191 #[cfg(feature = "cli")]
192 #[error(transparent)]
193 #[diagnostic(transparent)]
194 Defmt(#[from] DefmtError),
195
196 #[error("Verification of flash content failed")]
197 #[diagnostic(code(espflash::verify_failed))]
198 VerifyFailed,
199
200 #[cfg(feature = "cli")]
201 #[error(transparent)]
202 #[diagnostic(code(espflash::dialoguer_error))]
203 DialoguerError(#[from] dialoguer::Error),
204
205 #[error("Internal Error")]
206 InternalError,
207
208 #[error("Failed to open file: {0}")]
209 FileOpenError(String, #[source] io::Error),
210
211 #[error("Failed to parse partition table")]
212 Partition(#[from] esp_idf_part::Error),
213}
214
215#[cfg(feature = "serialport")]
216impl From<io::Error> for Error {
217 fn from(err: io::Error) -> Self {
218 Self::Connection(err.into())
219 }
220}
221
222#[cfg(feature = "serialport")]
223#[cfg_attr(docsrs, doc(cfg(feature = "serialport")))]
224impl From<serialport::Error> for Error {
225 fn from(err: serialport::Error) -> Self {
226 Self::Connection(err.into())
227 }
228}
229
230#[cfg(feature = "serialport")]
231impl From<SlipError> for Error {
232 fn from(err: SlipError) -> Self {
233 Self::Connection(err.into())
234 }
235}
236
237#[derive(Debug, Diagnostic, Error)]
239#[non_exhaustive]
240pub enum ConnectionError {
241 #[error("Failed to connect to the device")]
242 #[diagnostic(
243 code(espflash::connection_failed),
244 help("Ensure that the device is connected and the reset and boot pins are not being held down")
245 )]
246 ConnectionFailed,
247
248 #[error("Serial port not found")]
249 #[diagnostic(
250 code(espflash::connection_failed),
251 help("Ensure that the device is connected and your host recognizes the serial adapter")
252 )]
253 DeviceNotFound,
254
255 #[error("Received packet has invalid SLIP framing")]
256 #[diagnostic(
257 code(espflash::slip_framing),
258 help("Try hard-resetting the device and try again, if the error persists your ROM may be corrupted")
259 )]
260 FramingError,
261
262 #[error("Invalid stub handshake response received")]
263 InvalidStubHandshake,
264
265 #[error("Download mode successfully detected, but getting no sync reply")]
266 #[diagnostic(
267 code(espflash::no_sync_reply),
268 help("The serial TX path seems to be down")
269 )]
270 NoSyncReply,
271
272 #[error("Received packet to large for buffer")]
273 #[diagnostic(
274 code(espflash::oversized_packet),
275 help("Try hard-resetting the device and try again, if the error persists your ROM may be corrupted")
276 )]
277 OverSizedPacket,
278
279 #[error("Failed to read the available bytes on the serial port. Available bytes: {0}, Read bytes: {1}")]
280 #[diagnostic(code(espflash::read_missmatch))]
281 ReadMissmatch(u32, u32),
282
283 #[cfg(feature = "serialport")]
284 #[error("Timeout while running {0}command")]
285 #[diagnostic(code(espflash::timeout))]
286 Timeout(TimedOutCommand),
287
288 #[cfg(feature = "serialport")]
289 #[error("IO error while using serial port: {0}")]
290 #[diagnostic(code(espflash::serial_error))]
291 Serial(#[source] serialport::Error),
292
293 #[error("Wrong boot mode detected ({0})! The chip needs to be in download mode.")]
294 #[diagnostic(code(espflash::wrong_boot_mode))]
295 WrongBootMode(String),
296}
297
298#[cfg(feature = "serialport")]
299impl From<io::Error> for ConnectionError {
300 fn from(err: io::Error) -> Self {
301 from_error_kind(err.kind(), err)
302 }
303}
304
305#[cfg(feature = "serialport")]
306#[cfg_attr(docsrs, doc(cfg(feature = "serialport")))]
307impl From<serialport::Error> for ConnectionError {
308 fn from(err: serialport::Error) -> Self {
309 use serialport::ErrorKind;
310
311 match err.kind() {
312 ErrorKind::Io(kind) => from_error_kind(kind, err),
313 ErrorKind::NoDevice => ConnectionError::DeviceNotFound,
314 _ => ConnectionError::Serial(err),
315 }
316 }
317}
318
319#[cfg(feature = "serialport")]
320impl From<SlipError> for ConnectionError {
321 fn from(err: SlipError) -> Self {
322 match err {
323 SlipError::FramingError => Self::FramingError,
324 SlipError::OversizedPacket => Self::OverSizedPacket,
325 SlipError::ReadError(io) => Self::from(io),
326 SlipError::EndOfStream => Self::FramingError,
327 }
328 }
329}
330
331#[derive(Clone, Debug, Default)]
333#[cfg(feature = "serialport")]
334pub struct TimedOutCommand {
335 command: Option<CommandType>,
336}
337
338#[cfg(feature = "serialport")]
339impl Display for TimedOutCommand {
340 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
341 match &self.command {
342 Some(command) => write!(f, "{} ", command),
343 None => Ok(()),
344 }
345 }
346}
347
348#[cfg(feature = "serialport")]
349impl From<CommandType> for TimedOutCommand {
350 fn from(ct: CommandType) -> Self {
351 TimedOutCommand { command: Some(ct) }
352 }
353}
354
355#[derive(Clone, Copy, Debug, Default, Diagnostic, Error, strum::FromRepr)]
357#[non_exhaustive]
358#[repr(u8)]
359#[cfg(feature = "serialport")]
360pub enum RomErrorKind {
361 #[error("Invalid message received")]
362 #[diagnostic(code(espflash::rom::invalid_message))]
363 InvalidMessage = 0x05,
364
365 #[error("Bootloader failed to execute command")]
366 #[diagnostic(code(espflash::rom::failed))]
367 FailedToAct = 0x06,
368
369 #[error("Received message has invalid CRC")]
370 #[diagnostic(code(espflash::rom::crc))]
371 InvalidCrc = 0x07,
372
373 #[error("Bootloader failed to write to flash")]
374 #[diagnostic(code(espflash::rom::flash_write))]
375 FlashWriteError = 0x08,
376
377 #[error("Bootloader failed to read from flash")]
378 #[diagnostic(code(espflash::rom::flash_read))]
379 FlashReadError = 0x09,
380
381 #[error("Invalid length for flash read")]
382 #[diagnostic(code(espflash::rom::flash_read_length))]
383 FlashReadLengthError = 0x0a,
384
385 #[error("Malformed compressed data received")]
386 #[diagnostic(code(espflash::rom::deflate))]
387 DeflateError = 0x0b,
388
389 #[error("Bad data length")]
390 #[diagnostic(code(espflash::rom::data_len))]
391 BadDataLen = 0xc0,
392
393 #[error("Bad data checksum")]
394 #[diagnostic(code(espflash::rom::data_crc))]
395 BadDataChecksum = 0xc1,
396
397 #[error("Bad block size")]
398 #[diagnostic(code(espflash::rom::block_size))]
399 BadBlocksize = 0xc2,
400
401 #[error("Invalid command")]
402 #[diagnostic(code(espflash::rom::cmd))]
403 InvalidCommand = 0xc3,
404
405 #[error("SPI operation failed")]
406 #[diagnostic(code(espflash::rom::spi))]
407 FailedSpiOp = 0xc4,
408
409 #[error("SPI unlock failed")]
410 #[diagnostic(code(espflash::rom::spi_unlock))]
411 FailedSpiUnlock = 0xc5,
412
413 #[error("Not in flash mode")]
414 #[diagnostic(code(espflash::rom::flash_mode))]
415 NotInFlashMode = 0xc6,
416
417 #[error("Error when uncompressing the data")]
418 #[diagnostic(code(espflash::rom::inflate))]
419 InflateError = 0xc7,
420
421 #[error("Didn't receive enough data")]
422 #[diagnostic(code(espflash::rom::not_enough))]
423 NotEnoughData = 0xc8,
424
425 #[error("Received too much data")]
426 #[diagnostic(code(espflash::rom::too_much_data))]
427 TooMuchData = 0xc9,
428
429 #[default]
430 #[error("Other")]
431 #[diagnostic(code(espflash::rom::other))]
432 Other = 0xff,
433}
434
435#[cfg(feature = "serialport")]
436impl From<u8> for RomErrorKind {
437 fn from(raw: u8) -> Self {
438 Self::from_repr(raw).unwrap_or_default()
439 }
440}
441
442#[derive(Clone, Copy, Debug, Diagnostic, Error)]
444#[error("Error while running {command} command")]
445#[cfg(feature = "serialport")]
446#[non_exhaustive]
447pub struct RomError {
448 command: CommandType,
449 #[source]
450 kind: RomErrorKind,
451}
452
453#[cfg(feature = "serialport")]
454impl RomError {
455 pub fn new(command: CommandType, kind: RomErrorKind) -> RomError {
456 RomError { command, kind }
457 }
458}
459
460#[derive(Debug, Diagnostic, Error)]
462#[error("Missing partition")]
463#[diagnostic(
464 code(espflash::partition_table::missing_partition),
465 help("Partition table must contain the partition of type `{0}` to be erased")
466)]
467pub struct MissingPartition(String);
468
469impl From<String> for MissingPartition {
470 fn from(part: String) -> Self {
471 MissingPartition(part)
472 }
473}
474
475#[derive(Debug, Error, Diagnostic)]
477#[error("No partition table could be found")]
478#[diagnostic(
479 code(espflash::partition_table::missing_partition_table),
480 help("Try providing a CSV or binary paritition table with the `--partition-table` argument.")
481)]
482pub struct MissingPartitionTable;
483
484#[derive(Debug, Error)]
486#[error("{0}")]
487pub struct ElfError(&'static str);
488
489impl From<&'static str> for ElfError {
490 fn from(err: &'static str) -> Self {
491 ElfError(err)
492 }
493}
494
495#[cfg(feature = "serialport")]
496pub(crate) trait ResultExt {
497 fn flashing(self) -> Self;
499 fn for_command(self, command: CommandType) -> Self;
501}
502
503#[cfg(feature = "serialport")]
504impl<T> ResultExt for Result<T, Error> {
505 fn flashing(self) -> Self {
506 match self {
507 Err(Error::Connection(err)) => Err(Error::Flashing(err)),
508 res => res,
509 }
510 }
511
512 fn for_command(self, command: CommandType) -> Self {
513 match self {
514 Err(Error::Connection(ConnectionError::Timeout(_))) => {
515 Err(Error::Connection(ConnectionError::Timeout(command.into())))
516 }
517 Err(Error::Flashing(ConnectionError::Timeout(_))) => {
518 Err(Error::Flashing(ConnectionError::Timeout(command.into())))
519 }
520 res => res,
521 }
522 }
523}
524
525#[cfg(feature = "serialport")]
526#[cfg_attr(docsrs, doc(cfg(feature = "serialport")))]
527fn from_error_kind<E>(kind: io::ErrorKind, err: E) -> ConnectionError
528where
529 E: Into<serialport::Error>,
530{
531 use io::ErrorKind;
532
533 match kind {
534 ErrorKind::TimedOut => ConnectionError::Timeout(TimedOutCommand::default()),
535 ErrorKind::NotFound => ConnectionError::DeviceNotFound,
536 _ => ConnectionError::Serial(err.into()),
537 }
538}