Skip to main content

rxprog/
programmer.rs

1use std::ops::RangeInclusive;
2use std::thread;
3use std::time;
4
5use crate::command::{self, Command};
6use crate::target::{OperatingMode, Target};
7use crate::{Error, ErrorKind, Result};
8
9/// Error encountered when attempting to make an initial connection to a device
10#[derive(Debug)]
11pub enum ConnectError {
12    /// The device did not respond
13    NoResponse,
14    /// The device responded with an unknown response
15    BadResponse,
16    /// The device responded with a failure code
17    Failed,
18}
19
20/// A programmer connected to a device, through a serial port
21pub struct Programmer {
22    target: Box<dyn Target>,
23}
24
25impl Programmer {
26    /// Creates a new programmer connected to the provided serial port
27    pub fn new(target: Box<dyn Target>) -> Programmer {
28        Programmer { target }
29    }
30
31    /// Attempts to make an initial connection to the device
32    pub fn connect(mut self) -> Result<ProgrammerConnected> {
33        self.target.reset_into(OperatingMode::Boot);
34
35        self.target.clear_buffers()?;
36
37        for baud_rate in &[9600, 4800, 2400, 1200, 0] {
38            if *baud_rate == 0 {
39                return Err(Error::new(ErrorKind::Connect, "no response from target"));
40            }
41
42            self.target.set_baud_rate(*baud_rate)?;
43
44            let mut attempts = 0;
45            while self.target.bytes_to_read()? < 1 && attempts < 30 {
46                self.target.write(&[0x00])?;
47                thread::sleep(time::Duration::from_millis(10));
48
49                attempts += 1;
50            }
51
52            if self.target.bytes_to_read()? >= 1 {
53                break;
54            }
55        }
56
57        let mut response1 = [0u8; 1];
58        self.target.read_exact(&mut response1)?;
59        let response1 = response1[0];
60
61        if response1 != 0x00 {
62            return Err(Error::new(ErrorKind::Connect, "bad response from target"));
63        }
64
65        self.target.write(&[0x55])?;
66
67        let mut response2 = [0u8; 1];
68        self.target.read_exact(&mut response2)?;
69        let response2 = response2[0];
70
71        match response2 {
72            0xE6 => Ok(ProgrammerConnected {
73                target: self.target,
74            }),
75            0xFF => Err(Error::new(ErrorKind::Connect, "failed to connect")),
76            _ => Err(Error::new(ErrorKind::Connect, "bad response from target")),
77        }
78    }
79}
80
81/// A programmer connected to a device
82pub struct ProgrammerConnected {
83    target: Box<dyn Target>,
84}
85
86impl ProgrammerConnected {
87    /// Retrieve a list of devices supported by the target
88    pub fn supported_devices(&mut self) -> Result<Vec<command::data::SupportedDevice>> {
89        let cmd = command::commands::SupportedDeviceInquiry {};
90        cmd.execute(&mut self.target)
91    }
92
93    /// Selects a device
94    pub fn select_device(
95        mut self,
96        device_code: &String,
97    ) -> Result<ProgrammerConnectedDeviceSelected> {
98        let cmd = command::commands::DeviceSelection {
99            device_code: device_code.clone(),
100        };
101        cmd.execute(&mut self.target)?;
102
103        Ok(ProgrammerConnectedDeviceSelected {
104            target: self.target,
105        })
106    }
107}
108
109/// A programmer connected to a device, with a device selected
110pub struct ProgrammerConnectedDeviceSelected {
111    target: Box<dyn Target>,
112}
113
114impl ProgrammerConnectedDeviceSelected {
115    /// Retrieve a list of supported clock modes
116    pub fn clock_modes(&mut self) -> Result<Vec<u8>> {
117        let cmd = command::commands::ClockModeInquiry {};
118        cmd.execute(&mut self.target)
119    }
120
121    /// Selects a clock mode
122    pub fn select_clock_mode(
123        mut self,
124        clock_mode: u8,
125    ) -> Result<ProgrammerConnectedClockModeSelected> {
126        let cmd = command::commands::ClockModeSelection { mode: clock_mode };
127        cmd.execute(&mut self.target)?;
128
129        Ok(ProgrammerConnectedClockModeSelected {
130            target: self.target,
131        })
132    }
133}
134
135/// A programmer connected to a device, with a clock mode selected
136pub struct ProgrammerConnectedClockModeSelected {
137    target: Box<dyn Target>,
138}
139
140impl ProgrammerConnectedClockModeSelected {
141    /// Retrieve a list of multiplication ratios supported by each clock
142    pub fn multiplication_ratios(
143        &mut self,
144    ) -> Result<Vec<Vec<command::data::MultiplicationRatio>>> {
145        let cmd = command::commands::MultiplicationRatioInquiry {};
146        cmd.execute(&mut self.target)
147    }
148
149    /// Retrive the operating frequency range of each clock
150    pub fn operating_frequencies(&mut self) -> Result<Vec<RangeInclusive<u16>>> {
151        let cmd = command::commands::OperatingFrequencyInquiry {};
152        cmd.execute(&mut self.target)
153    }
154
155    /// Sets a new bit rate for the device connection
156    pub fn set_new_bit_rate(
157        mut self,
158        bit_rate: u16,
159        input_frequency: u16,
160        multiplication_ratios: Vec<command::data::MultiplicationRatio>,
161    ) -> Result<ProgrammerConnectedNewBitRateSelected> {
162        let cmd = command::commands::NewBitRateSelection {
163            bit_rate: bit_rate,
164            input_frequency: input_frequency,
165            multiplication_ratios: multiplication_ratios,
166        };
167        cmd.execute(&mut self.target)?;
168
169        let baud_rate: u32 = (bit_rate as u32) * 100;
170        self.target.set_baud_rate(baud_rate)?;
171
172        let cmd = command::commands::NewBitRateSelectionConfirmation {};
173        cmd.execute(&mut self.target)?;
174
175        Ok(ProgrammerConnectedNewBitRateSelected {
176            target: self.target,
177        })
178    }
179}
180
181/// A programmer connected to a device, after a new bit rate has been selected
182pub struct ProgrammerConnectedNewBitRateSelected {
183    target: Box<dyn Target>,
184}
185
186impl ProgrammerConnectedNewBitRateSelected {
187    /// Retrieves the regions which comprise the user boot area
188    pub fn user_boot_area(&mut self) -> Result<Vec<RangeInclusive<u32>>> {
189        let cmd = command::commands::UserBootAreaInformationInquiry {};
190        cmd.execute(&mut self.target)
191    }
192
193    /// Retrieves the regions which comprise the user area
194    pub fn user_area(&mut self) -> Result<Vec<RangeInclusive<u32>>> {
195        let cmd = command::commands::UserAreaInformationInquiry {};
196        cmd.execute(&mut self.target)
197    }
198
199    /// Retrieves the blocks which can be erased
200    pub fn erasure_block(&mut self) -> Result<Vec<RangeInclusive<u32>>> {
201        let cmd = command::commands::ErasureBlockInformationInquiry {};
202        cmd.execute(&mut self.target)
203    }
204
205    /// Transitions into the programming/erasure wait state
206    pub fn programming_erasure_state_transition(
207        mut self,
208    ) -> Result<ProgrammerConnectedProgrammingErasureState> {
209        let cmd = command::commands::ProgrammingErasureStateTransition {};
210        let response = cmd.execute(&mut self.target)?;
211
212        match response {
213            command::commands::IDCodeProtectionStatus::Disabled => {
214                Ok(ProgrammerConnectedProgrammingErasureState {
215                    target: self.target,
216                })
217            }
218            command::commands::IDCodeProtectionStatus::Enabled => {
219                panic!("Support for ID codes not implemented")
220            }
221        }
222    }
223}
224
225/// A programmer connected to a device, waiting for programming selection commands
226pub struct ProgrammerConnectedProgrammingErasureState {
227    target: Box<dyn Target>,
228}
229
230impl ProgrammerConnectedProgrammingErasureState {
231    /// Selects the user area and data area for programming
232    pub fn program_user_or_data_area(mut self) -> Result<ProgrammerConnectedWaitingForData> {
233        let cmd = command::commands::UserDataAreaProgrammingSelection {};
234        cmd.execute(&mut self.target)?;
235
236        Ok(ProgrammerConnectedWaitingForData {
237            target: self.target,
238        })
239    }
240
241    /// Read `size` bytes of memory starting from `start_address`
242    pub fn read_memory(
243        &mut self,
244        area: command::data::MemoryArea,
245        start_address: u32,
246        size: u32,
247    ) -> Result<Vec<u8>> {
248        let cmd = command::commands::MemoryRead {
249            area,
250            start_address,
251            size,
252        };
253        cmd.execute(&mut self.target)
254    }
255
256    /// Requests the checksum of the user boot area
257    pub fn user_boot_area_checksum(&mut self) -> Result<u32> {
258        let cmd = command::commands::UserBootAreaChecksum {};
259        cmd.execute(&mut self.target)
260    }
261
262    /// Requests the checksum of the user area
263    pub fn user_area_checksum(&mut self) -> Result<u32> {
264        let cmd = command::commands::UserAreaChecksum {};
265        cmd.execute(&mut self.target)
266    }
267}
268
269/// A programmer connected to a device, waiting for data to be programmed into the selected area
270pub struct ProgrammerConnectedWaitingForData {
271    target: Box<dyn Target>,
272}
273
274impl ProgrammerConnectedWaitingForData {
275    /// Writes a block of data to the device
276    pub fn program_block(&mut self, address: u32, data: [u8; 256]) -> Result<()> {
277        let cmd = command::commands::X256ByteProgramming {
278            address: address,
279            data: data,
280        };
281        cmd.execute(&mut self.target)
282    }
283
284    /// Finishes programming
285    pub fn end(mut self) -> Result<ProgrammerConnectedProgrammingErasureState> {
286        let cmd = command::commands::X256ByteProgramming {
287            address: 0xFFFFFFFF,
288            data: [0u8; 256],
289        };
290        cmd.execute(&mut self.target)?;
291
292        Ok(ProgrammerConnectedProgrammingErasureState {
293            target: self.target,
294        })
295    }
296}