1use std::cell::Cell;
2use std::convert::TryInto;
3
4use hidapi::HidDevice;
5
6#[allow(unused)]
7pub struct PartInfo {
8 pub name: &'static str,
9 pub flash_size: u32,
10}
11
12macro_rules! pdid_map {
13 { $( $name:ident { $pdid:expr, $size:expr }, ) + } =>
14 ( |pdid| ( match pdid { $(
15 $pdid => Some((stringify!($name), $size)),
16 ) * _ => None } ).map(|(name, flash_size)| PartInfo{name, flash_size}) );
17}
18
19pub fn get_partinfo(pdid: u32) -> Option<PartInfo> {
21 let pdid_map = pdid_map! {
22 NUC123ZC2AN { 0x00012345, 36*1024 },
25 NUC123ZD4AN { 0x00012355, 68*1024 },
26 NUC123LC2AN { 0x00012325, 36*1024 },
27 NUC123LD4AN { 0x00012335, 68*1024 },
28 NUC123SC2AN { 0x00012305, 36*1024 },
29 NUC123SD4AN { 0x00012315, 68*1024 },
30 NUC123ZC2AE { 0x10012345, 36*1024 },
31 NUC123ZD4AE { 0x10012355, 68*1024 },
32 NUC123LC2AE { 0x10012325, 36*1024 },
33 NUC123LD4AE { 0x10012335, 68*1024 },
34 NUC123SC2AE { 0x10012305, 36*1024 },
35 NUC123SD4AE { 0x10012315, 68*1024 },
36 NUC126NE4AE { 0x00C05206, 128*1024 },
38 NUC126LE4AE { 0x00C05205, 128*1024 },
39 NUC126LG4AE { 0x00C05204, 256*1024 },
40 NUC126SE4AE { 0x00C05213, 128*1024 },
41 NUC126SG4AE { 0x00C05212, 256*1024 },
42 NUC126VG4AE { 0x00C05231, 256*1024 },
43 NUC126KG4AE { 0x00C05230, 256*1024 },
44 NUC029LGE { 0x00295C50, 256*1024 },
46 NUC029SGE { 0x00295C51, 256*1024 },
47 NUC029KGE { 0x00295C52, 256*1024 },
48 M032FC1AE { 0x01132CB0, 32*1024 },
50 M032EC1AE { 0x01132CA0, 32*1024 },
51 M032TC1AE { 0x01132CE0, 32*1024 },
52 M032TC2AE { 0x01132DE1, 32*1024 },
53 M032TD2AE { 0x01132DE0, 64*1024 },
54 M032LC2AE { 0x01132D01, 32*1024 },
55 M032LD2AE { 0x01132D00, 64*1024 },
56 M032LE3AE { 0x01132E00, 128*1024 },
57 M032LG6AE { 0x01132601, 256*1024 },
58 M032LG8AE { 0x01132600, 256*1024 },
59 M032SE3AE { 0x01132E10, 128*1024 },
60 M032SG6AE { 0x01132611, 256*1024 },
61 M032SG8AE { 0x01132610, 256*1024 },
62 M032SIAAE { 0x01132110, 512*1024 },
63 M032KG6AE { 0x01132641, 256*1024 },
64 M032KG8AE { 0x01132640, 256*1024 },
65 M032KIAAE { 0x01132140, 512*1024 },
66 };
67 pdid_map(pdid)
68}
69
70pub mod error {
71 #[derive(Debug)]
72 pub enum Error {
73 HidError(hidapi::HidError),
74 NoResponse,
75 ChecksumError,
76 }
77
78 impl From<hidapi::HidError> for Error {
79 fn from(error: hidapi::HidError) -> Self {
80 Error::HidError(error)
81 }
82 }
83
84 impl std::fmt::Display for Error {
85 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
86 match *self {
87 Error::HidError(ref err) => write!(f, "{}", err),
88 Error::NoResponse => write!(f, "No Response"),
89 Error::ChecksumError => write!(f, "Checksum Error"),
90 }
91 }
92 }
93}
94
95type Result<T> = std::result::Result<T, error::Error>;
96
97#[derive(Debug)]
98pub enum ProgressEvent {
99 Started { total_bytes: u32 },
100 Erased,
101 Programmed { bytes: u32 },
102 Finished,
103 Aborted,
104 StartedErasing,
105 FinishedErasing,
106 AbortedErasing,
107 _Reserved_,
108}
109
110pub struct Progress {
111 handler: Box<dyn Fn(ProgressEvent)>,
112}
113
114impl Progress {
115 pub fn new<F: 'static>(handler: F) -> Self
118 where
119 F: Fn(ProgressEvent),
120 {
121 Self {
122 handler: Box::new(handler),
123 }
124 }
125
126 fn emit(&self, event: ProgressEvent) {
128 (self.handler)(event);
129 }
130
131 pub fn started(&self, total_bytes: u32) {
132 self.emit(ProgressEvent::Started { total_bytes });
133 }
134
135 pub fn erased(&self) {
136 self.emit(ProgressEvent::Erased);
137 }
138
139 pub fn programmed(&self, bytes: u32) {
140 self.emit(ProgressEvent::Programmed { bytes });
141 }
142
143 pub fn finished(&self) {
144 self.emit(ProgressEvent::Finished);
145 }
146
147 pub fn aborted(&self) {
148 self.emit(ProgressEvent::Aborted);
149 }
150}
151
152mod nu_isp_cmd {
153 pub const UPDATE_APROM: u8 = 0xA0;
154 pub const READ_CONFIG: u8 = 0xA2;
155 pub const SYNC_PACKNO: u8 = 0xA4;
156 pub const GET_FWVER: u8 = 0xA6;
157 pub const RUN_APROM: u8 = 0xAB;
158 pub const RUN_LDROM: u8 = 0xAC;
159 pub const CONNECT: u8 = 0xAE;
160 pub const GET_DEVICEID: u8 = 0xB1;
161
162 pub const DATA_PACKET: u8 = 0x00;
163}
164
165pub struct NuIspInfo {
166 pub pdid: u32,
167 pub config: [u32; 2],
168}
169
170pub struct Context<'a> {
171 device: &'a HidDevice,
172 progress: &'a Progress,
173 rpn: Cell<u32>,
174}
175
176impl<'a> Context<'a> {
177 pub fn new(device: &'a hidapi::HidDevice, progress: &'a Progress) -> Self {
178 Context {
179 device,
180 progress,
181 rpn: Cell::new(0),
182 }
183 }
184
185 fn checksum(buf: &[u8]) -> u16 {
186 buf.iter().fold(0_u16, |sum, &h| sum.wrapping_add(h as u16))
187 }
188
189 fn read(&self, buf: &mut [u8], pn: u32) -> Result<()> {
190 let d = self.device;
191 let timeout = 5000;
192 let end_time = std::time::Instant::now() + std::time::Duration::from_millis(timeout);
193 let checksum = Self::checksum(&buf[1..]);
194 loop {
196 let remain = (end_time - std::time::Instant::now()).as_millis() as i32;
197 if remain < 0 {
198 return Err(error::Error::NoResponse);
199 }
200 let len = d.read_timeout(&mut buf[0..64], remain)?;
201 if len < 8 {
202 return Err(error::Error::NoResponse);
203 }
204 let rsum = u16::from_le_bytes(buf[0..2].try_into().unwrap());
205 let rpn = u32::from_le_bytes(buf[4..8].try_into().unwrap());
206 if rpn == pn {
207 if rsum != checksum {
208 return Err(error::Error::ChecksumError);
209 }
210 return Ok(());
211 }
212 }
213 }
214
215 fn nu_isp_connect(&self) -> Result<NuIspInfo> {
216 let d = self.device;
217 self.rpn.set(0);
218
219 let pn = 1 as u32;
221 let buffer = &mut [0_u8; 65];
222 {
223 let buffer = &mut buffer[1..];
224 buffer[0] = nu_isp_cmd::CONNECT;
225 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
226 }
227 d.write(&buffer[0..65])?;
228 self.read(&mut buffer[0..65], pn + 1)?;
229 log::debug!("CONNECT");
230
231 let pn = pn + 2;
233 let sync_pn = 0x01234567 as u32;
234 let buffer = &mut [0_u8; 65];
235 {
236 let buffer = &mut buffer[1..];
237 buffer[0] = nu_isp_cmd::SYNC_PACKNO;
238 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
239 buffer[8..12].copy_from_slice(&sync_pn.to_le_bytes());
240 }
241 d.write(&buffer[0..65])?;
242 self.read(&mut buffer[0..65], sync_pn + 1)?;
243 log::debug!("SYNC");
244
245 let pn = sync_pn + 2;
247 let buffer = &mut [0_u8; 65];
248 {
249 let buffer = &mut buffer[1..];
250 buffer[0] = nu_isp_cmd::GET_FWVER;
251 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
252 }
253 d.write(&buffer[0..65])?;
254 self.read(&mut buffer[0..65], pn + 1)?;
255 let fwver = buffer[8];
256 log::debug!("GET_FWVER");
257 log::info!("FWVER {:#04X}", fwver);
258
259 let pn = pn + 2;
261 let buffer = &mut [0_u8; 65];
262 {
263 let buffer = &mut buffer[1..];
264 buffer[0] = nu_isp_cmd::GET_DEVICEID;
265 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
266 }
267 d.write(&buffer[0..65])?;
268 self.read(&mut buffer[0..65], pn + 1)?;
269 let pdid = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
270 log::debug!("GET_DEVICEID");
271 log::info!("PDID {:08X}", pdid);
272
273 let pn = pn + 2;
275 let buffer = &mut [0_u8; 65];
276 {
277 let buffer = &mut buffer[1..];
278 buffer[0] = nu_isp_cmd::READ_CONFIG;
279 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
280 }
281 d.write(&buffer[0..65])?;
282 self.read(&mut buffer[0..65], pn + 1)?;
283 log::debug!("READ_CONFIG");
284 let config0 = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
285 let config1 = u32::from_le_bytes(buffer[12..16].try_into().unwrap());
286 log::info!("CONFIG {:08X}:{:08X}", config0, config1);
287
288 if config0 == pdid && config1 == 0 {
289 log::warn!("The bootloader is old. You'd better update it.");
290 }
291
292 self.rpn.set(pn + 1);
293 Ok(NuIspInfo {
294 pdid,
295 config: [config0, config1],
296 })
297 }
298
299 pub fn nu_isp_info(&self) -> Result<NuIspInfo> {
300 self.nu_isp_connect()
301 }
302
303 pub fn nu_isp_erase(&self) -> Result<()> {
304 let d = self.device;
305 if self.rpn.get() == 0 {
306 self.nu_isp_connect()?;
307 };
308
309 let rpn = match self.update_aprom(vec![]) {
311 Err(err) => {
312 self.progress.emit(ProgressEvent::AbortedErasing);
313 return Err(err);
314 }
315 Ok(_) => self.rpn.get(),
316 };
317
318 let pn = rpn + 1;
321 let buffer = &mut [0_u8; 65];
322 {
323 let buffer = &mut buffer[1..];
324 buffer[0] = nu_isp_cmd::RUN_LDROM;
325 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
326 }
327 match d.write(&buffer[0..65]) {
328 Ok(_) => (),
329 Err(hidapi::HidError::HidApiError { .. }) => (),
332 Err(e) => Err(e)?
333 }
334 self.rpn.set(0);
338 Ok(())
339 }
340
341 pub fn nu_isp_launch(&self) -> Result<()> {
342 let d = self.device;
343 if self.rpn.get() == 0 {
344 self.nu_isp_connect()?;
345 };
346 let rpn = self.rpn.get();
347
348 {
350 let pn = rpn + 1;
352 let buffer = &mut [0_u8; 65];
353 {
354 let buffer = &mut buffer[1..];
355 buffer[0] = nu_isp_cmd::RUN_APROM;
356 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
357 }
358 match d.write(&buffer[0..65]) {
360 Ok(_) => (),
361 Err(hidapi::HidError::HidApiError { .. }) => (),
364 Err(e) => Err(e)?
365 }
366 }
369
370 self.rpn.set(0);
371 Ok(())
372 }
373
374 pub fn nu_isp_download(&self, binary: Vec<u8>) -> Result<()> {
375 let rpn = self.rpn.get();
378 if rpn == 0 {
379 self.nu_isp_connect()?;
380 };
381
382 let rpn = match self.update_aprom(binary) {
384 Err(err) => {
385 self.progress.aborted();
386 return Err(err);
387 }
388 Ok(_) => self.rpn.get(),
389 };
390
391 self.rpn.set(rpn);
392 self.nu_isp_launch()?;
393 Ok(())
394 }
395
396 fn update_aprom(&self, data: Vec<u8>) -> Result<()> {
397 let d = self.device;
398 let rpn = self.rpn.get();
399 let len = data.len();
400
401 if len == 0 {
403 self.progress.emit(ProgressEvent::StartedErasing);
404 } else {
405 self.progress.started(len as u32);
406 }
407
408 let pn = rpn + 1;
410 let buffer = &mut [0_u8; 65];
411 {
412 let buffer = &mut buffer[1..];
413 buffer[0] = nu_isp_cmd::UPDATE_APROM;
414 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
415 buffer[8..12].copy_from_slice(&(0_u32).to_le_bytes()); buffer[12..16].copy_from_slice(&(len as u32).to_le_bytes());
417 if len == 0 {
418 buffer[16..].copy_from_slice(&[0xFF_u8; 48])
419 } else if len >= 48 {
420 buffer[16..].copy_from_slice(&data[0..48]);
421 } else {
422 buffer[16..][..len].copy_from_slice(&data[0..len]);
423 buffer[16..][len..].copy_from_slice(&[0xFF_u8; 48][..48 - len]);
424 }
425 }
426 d.write(&buffer[0..65])?;
427 self.read(&mut buffer[0..65], pn + 1)?;
428 if len == 0 {
429 self.progress.emit(ProgressEvent::FinishedErasing);
430 self.rpn.set(pn + 1);
431 return Ok(());
432 }
433
434 self.progress.erased();
435
436 let mut pn = pn + 2;
437 let mut idx = 48;
438 while idx < len {
439 self.progress.programmed(idx as u32);
440 let buffer = &mut [0_u8; 65];
441 {
442 let buffer = &mut buffer[1..];
443 buffer[0] = nu_isp_cmd::DATA_PACKET;
444 buffer[4..8].copy_from_slice(&pn.to_le_bytes());
445 let len = std::cmp::min(len - idx, 56);
446 buffer[8..][..len].copy_from_slice(&data[idx..][..len]);
447 }
448 d.write(&buffer[0..65])?;
449 self.read(&mut buffer[0..65], pn + 1)?;
450 pn = pn + 2;
451 idx += 56;
452 }
453 self.progress.finished();
454
455 self.rpn.set(pn - 1);
456 return Ok(());
457 }
458}