1use std::io::{Read, Write};
2pub use xymodem_util::*;
3
4const SOH: u8 = 0x01;
9const STX: u8 = 0x02;
10const EOT: u8 = 0x04;
11const ACK: u8 = 0x06;
12const NAK: u8 = 0x15;
13const CAN: u8 = 0x18;
14const CRC: u8 = 0x43;
15
16pub type Result<T> = std::result::Result<T, Error>;
17
18#[derive(Copy, Clone, Debug)]
19pub enum Checksum {
20 Standard,
21 CRC16,
22}
23
24#[derive(Copy, Clone, Debug)]
25pub enum BlockLength {
26 Standard = 128,
27 OneK = 1024,
28}
29
30#[derive(Copy, Clone, Debug)]
32pub struct Xmodem {
33 pub max_errors: u32,
36
37 pub max_initial_errors: u32,
42
43 pub pad_byte: u8,
46
47 pub block_length: BlockLength,
50
51 checksum_mode: Checksum,
53 errors: u32,
54 initial_errors: u32,
55}
56
57impl Xmodem {
58 pub fn new() -> Self {
60 Xmodem {
61 max_errors: 16,
62 max_initial_errors: 16,
63 pad_byte: 0x1a,
64 block_length: BlockLength::Standard,
65 checksum_mode: Checksum::Standard,
66 errors: 0,
67 initial_errors: 0,
68 }
69 }
70
71 pub fn send<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
82 self.errors = 0;
83
84 dbg!("Starting XMODEM transfer");
85 (self.start_send(dev))?;
86 dbg!("First byte received. Sending stream.");
87 (self.send_stream(dev, stream))?;
88 dbg!("Sending EOT");
89 (self.finish_send(dev))?;
90
91 Ok(())
92 }
93
94 pub fn recv<D: Read + Write, W: Write>(
107 &mut self,
108 dev: &mut D,
109 outstream: &mut W,
110 checksum: Checksum,
111 ) -> Result<()> {
112 self.errors = 0;
113 self.checksum_mode = checksum;
114 let mut handled_first_packet = false;
115 dbg!("Starting XMODEM receive");
116
117 let first_char;
118 loop {
119 (dev.write(&[match self.checksum_mode {
120 Checksum::Standard => NAK,
121 Checksum::CRC16 => CRC,
122 }])?);
123
124 match get_byte_timeout(dev)? {
125 bt @ Some(SOH) | bt @ Some(STX) => {
126 first_char = bt.unwrap();
128 break;
129 }
130 _ => {
131 self.initial_errors += 1;
132 if self.initial_errors > self.max_initial_errors {
133 eprint!(
134 "Exhausted max retries ({}) while waiting for SOH or STX",
135 self.max_initial_errors
136 );
137 return Err(Error::ExhaustedRetries);
138 }
139 }
140 }
141 }
142 dbg!("NCG sent. Receiving stream.");
143 let mut packet_num: u8 = 1;
144 loop {
145 match if handled_first_packet {
146 get_byte_timeout(dev)?
147 } else {
148 Some(first_char)
149 } {
150 bt @ Some(SOH) | bt @ Some(STX) => {
151 handled_first_packet = true;
152 let packet_size = match bt {
154 Some(SOH) => 128,
155 Some(STX) => 1024,
156 _ => 0, };
158 let pnum = (get_byte(dev))?; let pnum_1c = (get_byte(dev))?; let cancel_packet = packet_num != pnum || (255 - pnum) != pnum_1c;
162 let mut data: Vec<u8> = Vec::new();
163 data.resize(packet_size, 0);
164 (dev.read_exact(&mut data))?;
165 let success = match self.checksum_mode {
166 Checksum::Standard => {
167 let recv_checksum = (get_byte(dev))?;
168 calc_checksum(&data) == recv_checksum
169 }
170 Checksum::CRC16 => {
171 let recv_checksum =
172 (((get_byte(dev))? as u16) << 8) + (get_byte(dev))? as u16;
173 calc_crc(&data) == recv_checksum
174 }
175 };
176
177 if cancel_packet {
178 (dev.write(&[CAN]))?;
179 (dev.write(&[CAN]))?;
180 return Err(Error::Canceled);
181 }
182 if success {
183 packet_num = packet_num.wrapping_add(1);
184 (dev.write(&[ACK]))?;
185 (outstream.write_all(&data))?;
186 } else {
187 (dev.write(&[NAK]))?;
188 self.errors += 1;
189 }
190 }
191 Some(EOT) => {
192 (dev.write(&[ACK]))?;
194 break;
195 }
196 Some(_) => {
197 warn!("Unrecognized symbol!");
198 }
199 None => {
200 if !handled_first_packet {
201 self.errors = self.max_errors;
202 } else {
203 self.errors += 1;
204 }
205 warn!("Timeout!")
206 }
207 }
208 if self.errors >= self.max_errors {
209 eprint!(
210 "Exhausted max retries ({}) while waiting for ACK for EOT",
211 self.max_errors
212 );
213 return Err(Error::ExhaustedRetries);
214 }
215 }
216 Ok(())
217 }
218 fn start_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
219 let mut cancels = 0u32;
220 loop {
221 match (get_byte_timeout(dev))? {
222 Some(c) => match c {
223 NAK => {
224 dbg!("Standard checksum requested");
225 self.checksum_mode = Checksum::Standard;
226 return Ok(());
227 }
228 CRC => {
229 dbg!("16-bit CRC requested");
230 self.checksum_mode = Checksum::CRC16;
231 return Ok(());
232 }
233 CAN => {
234 warn!("Cancel (CAN) byte received");
235 cancels += 1;
236 }
237 c => warn!("Unknown byte received at start of XMODEM transfer: {}", c),
238 },
239 None => warn!("Timed out waiting for start of XMODEM transfer."),
240 }
241
242 self.errors += 1;
243
244 if cancels >= 2 {
245 eprint!(
246 "Transmission canceled: received two cancel (CAN) bytes \
247 at start of XMODEM transfer"
248 );
249 return Err(Error::Canceled);
250 }
251
252 if self.errors >= self.max_errors {
253 eprint!(
254 "Exhausted max retries ({}) at start of XMODEM transfer.",
255 self.max_errors
256 );
257 if let Err(err) = dev.write_all(&[CAN]) {
258 warn!("Error sending CAN byte: {}", err);
259 }
260 return Err(Error::ExhaustedRetries);
261 }
262 }
263 }
264
265 fn send_stream<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
266 let mut block_num = 0u32;
267 loop {
268 let mut buff = vec![self.pad_byte; self.block_length as usize + 3];
269 let n = (stream.read(&mut buff[3..]))?;
270 if n == 0 {
271 dbg!("Reached EOF");
272 return Ok(());
273 }
274
275 block_num += 1;
276 buff[0] = match self.block_length {
277 BlockLength::Standard => SOH,
278 BlockLength::OneK => STX,
279 };
280 buff[1] = (block_num & 0xFF) as u8;
281 buff[2] = 0xFF - buff[1];
282
283 match self.checksum_mode {
284 Checksum::Standard => {
285 let checksum = calc_checksum(&buff[3..]);
286 buff.push(checksum);
287 }
288 Checksum::CRC16 => {
289 let crc = calc_crc(&buff[3..]);
290 buff.push(((crc >> 8) & 0xFF) as u8);
291 buff.push((crc & 0xFF) as u8);
292 }
293 }
294
295 dbg!("Sending block {}", block_num);
296 (dev.write_all(&buff))?;
297
298 match (get_byte_timeout(dev))? {
299 Some(c) => {
300 if c == ACK {
301 dbg!("Received ACK for block {}", block_num);
302 continue;
303 } else {
304 warn!("Expected ACK, got {}", c);
305 }
306 }
308 None => warn!("Timeout waiting for ACK for block {}", block_num),
309 }
310
311 self.errors += 1;
312
313 if self.errors >= self.max_errors {
314 eprint!(
315 "Exhausted max retries ({}) while sending block {} in XMODEM transfer",
316 self.max_errors, block_num
317 );
318 return Err(Error::ExhaustedRetries);
319 }
320 }
321 }
322
323 fn finish_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
324 loop {
325 (dev.write_all(&[EOT]))?;
326
327 match (get_byte_timeout(dev))? {
328 Some(c) => {
329 if c == ACK {
330 info!("XMODEM transmission successful");
331 return Ok(());
332 } else {
333 warn!("Expected ACK, got {}", c);
334 }
335 }
336 None => warn!("Timeout waiting for ACK for EOT"),
337 }
338
339 self.errors += 1;
340
341 if self.errors >= self.max_errors {
342 eprint!(
343 "Exhausted max retries ({}) while waiting for ACK for EOT",
344 self.max_errors
345 );
346 return Err(Error::ExhaustedRetries);
347 }
348 }
349 }
350}