Skip to main content

nu_isp/
lib.rs

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
19/// Lookup chip product name from its PDID.
20pub fn get_partinfo(pdid: u32) -> Option<PartInfo> {
21    let pdid_map = pdid_map! {
22        // PARTNAME { pdid, flash_size }
23        /* NUC123 */
24        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        /* NUC126 */
37        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        /* NUC029 */
45        NUC029LGE { 0x00295C50, 256*1024 },
46        NUC029SGE { 0x00295C51, 256*1024 },
47        NUC029KGE { 0x00295C52, 256*1024 },
48        /* M032 */
49        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    /// Create a new `Progress` structure with a given `handler` to be called on events.
116    // pub fn new(handler: impl Fn(ProgressEvent) + 'static) -> Self {
117    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    /// Emit a flashing progress event.
127    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        // This loop is needed to support older crappy bootloaders
195        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        // CONNECT
220        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        // SYNC_PACKNO
232        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        // GET_FWVER
246        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        // GET_DEVICEID
260        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        // READ_CONFIG
274        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        // This will only erase APROM.
310        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        // Reset and reboot the bootloader
319        // RUN_LDROM
320        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            // Here, we are forced to ignore any error which is caused by
330            // target immediately stopping responding upon our request.
331            Err(hidapi::HidError::HidApiError { .. }) => (),
332            Err(e) => Err(e)?
333        }
334        // d.read(&mut buffer[0..64])?;
335        // let _rpn = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
336
337        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        // Reset and boot from application
349        {
350            // RUN_APROM
351            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            // d.write(&buffer[0..65])?;
359            match d.write(&buffer[0..65]) {
360                Ok(_) => (),
361                // Here, we are forced to ignore any error which is caused by
362                // target immediately stopping responding upon our request.
363                Err(hidapi::HidError::HidApiError { .. }) => (),
364                Err(e) => Err(e)?
365            }
366            // d.read(&mut buffer[0..64])?;
367            // let _rpn = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
368        }
369
370        self.rpn.set(0);
371        Ok(())
372    }
373
374    pub fn nu_isp_download(&self, binary: Vec<u8>) -> Result<()> {
375        // TODO check flash size
376
377        let rpn = self.rpn.get();
378        if rpn == 0 {
379            self.nu_isp_connect()?;
380        };
381
382        // UPDATE_APROM
383        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 length == 0 we are going to only erase
402        if len == 0 {
403            self.progress.emit(ProgressEvent::StartedErasing);
404        } else {
405            self.progress.started(len as u32);
406        }
407
408        // UPDATE_APROM
409        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()); // start address, this is unused
416            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}