@@ -11,23 +11,29 @@ use std::{
11
11
time:: Duration ,
12
12
} ;
13
13
14
- use log:: debug;
14
+ use log:: { debug, info} ;
15
+ use regex:: Regex ;
15
16
use serialport:: UsbPortInfo ;
16
17
use slip_codec:: SlipDecoder ;
17
18
18
19
#[ cfg( unix) ]
19
20
use self :: reset:: UnixTightReset ;
20
21
use self :: {
21
22
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
+ } ,
23
27
} ;
24
28
use crate :: {
25
29
command:: { Command , CommandType } ,
30
+ connection:: reset:: soft_reset,
26
31
error:: { ConnectionError , Error , ResultExt , RomError , RomErrorKind } ,
27
32
interface:: Interface ,
33
+ targets:: Chip ,
28
34
} ;
29
35
30
- mod reset;
36
+ pub mod reset;
31
37
32
38
const MAX_CONNECT_ATTEMPTS : usize = 7 ;
33
39
const MAX_SYNC_ATTEMPTS : usize = 5 ;
@@ -77,21 +83,34 @@ pub struct Connection {
77
83
serial : Interface ,
78
84
port_info : UsbPortInfo ,
79
85
decoder : SlipDecoder ,
86
+ after_operation : ResetAfterOperation ,
87
+ before_operation : ResetBeforeOperation ,
80
88
}
81
89
82
90
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 {
84
97
Connection {
85
98
serial,
86
99
port_info,
87
100
decoder : SlipDecoder :: new ( ) ,
101
+ after_operation,
102
+ before_operation,
88
103
}
89
104
}
90
105
91
106
/// Initialize a connection with a device
92
107
pub fn begin ( & mut self ) -> Result < ( ) , Error > {
93
108
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
+ ) ;
95
114
96
115
for ( _, reset_strategy) in zip ( 0 ..MAX_CONNECT_ATTEMPTS , reset_sequence. iter ( ) . cycle ( ) ) {
97
116
match self . connect_attempt ( reset_strategy) {
@@ -110,7 +129,47 @@ impl Connection {
110
129
/// Try to connect to a device
111
130
#[ allow( clippy:: borrowed_box) ]
112
131
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
+
114
173
for _ in 0 ..MAX_SYNC_ATTEMPTS {
115
174
self . flush ( ) ?;
116
175
@@ -119,6 +178,16 @@ impl Connection {
119
178
}
120
179
}
121
180
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
+
122
191
Err ( Error :: Connection ( ConnectionError :: ConnectionFailed ) )
123
192
}
124
193
@@ -163,6 +232,28 @@ impl Connection {
163
232
Ok ( ( ) )
164
233
}
165
234
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
+
166
257
// Reset the device to flash mode
167
258
pub fn reset_to_flash ( & mut self , extra_delay : bool ) -> Result < ( ) , Error > {
168
259
if self . port_info . pid == USB_SERIAL_JTAG_PID {
0 commit comments