txmodems/variants/api/
xmodem.rs1use alloc::{boxed::Box, vec, vec::Vec};
2use core::convert::From;
3
4use crate::common::{
5 calc_checksum, calc_crc, get_byte, get_byte_timeout, ModemError,
6 ModemResult, ModemTrait, XModemTrait,
7};
8use core2::io::{Read, Write};
9
10use crate::variants::xmodem::{
11 common::{BlockLengthKind, ChecksumKind},
12 Consts,
13};
14
15#[derive(Default, Debug, Copy, Clone)]
21pub struct XModem {
22 pub max_errors: u32,
25
26 pub pad_byte: u8,
29
30 pub block_length: BlockLengthKind,
33
34 checksum_mode: ChecksumKind,
36 errors: u32,
37}
38
39impl ModemTrait for XModem {
40 fn new() -> Self
41 where
42 Self: Sized,
43 {
44 Self {
45 max_errors: 16,
46 pad_byte: 0x1a,
47 block_length: BlockLengthKind::Standard,
48 checksum_mode: ChecksumKind::Standard,
49 errors: 0,
50 }
51 }
52}
53
54impl XModemTrait for XModem {
55 fn send<D, R>(&mut self, dev: &mut D, inp: &mut R) -> ModemResult<()>
56 where
57 D: Read + Write,
58 R: Read,
59 {
60 self.errors = 0;
61
62 self.init_send(dev)?;
63
64 self.send_stream(dev, inp)?;
65
66 self.finish_send(dev)?;
67
68 Ok(())
69 }
70
71 fn receive<D, W>(
72 &mut self,
73 dev: &mut D,
74 out: &mut W,
75 checksum: ChecksumKind,
76 ) -> ModemResult<()>
77 where
78 D: Read + Write,
79 W: Write,
80 {
81 self.errors = 0;
82 self.checksum_mode = checksum;
83
84 dev.write_all(&[match self.checksum_mode {
85 ChecksumKind::Standard => Consts::NAK.into(),
86 ChecksumKind::Crc16 => Consts::CRC.into(),
87 }])?;
88
89 let mut packet_num: u8 = 1;
90 loop {
91 match get_byte_timeout(dev)?.map(Consts::from) {
92 bt @ Some(Consts::SOH | Consts::STX) => {
93 let packet_size = match bt {
95 Some(Consts::SOH) => 128,
96 Some(Consts::STX) => 1024,
97 _ => 0, };
99 let pnum = get_byte(dev)?; let pnum_1c = get_byte(dev)?; let cancel_packet =
103 packet_num != pnum || (255 - pnum) != pnum_1c;
104 let mut data: Vec<u8> = Vec::new();
105 data.resize(packet_size, 0);
106 dev.read_exact(&mut data)?;
107 let success = match self.checksum_mode {
108 ChecksumKind::Standard => {
109 let recv_checksum = get_byte(dev)?;
110 calc_checksum(&data) == recv_checksum
111 }
112 ChecksumKind::Crc16 => {
113 let recv_checksum = (u16::from(get_byte(dev)?)
114 << 8)
115 + u16::from(get_byte(dev)?);
116 calc_crc(&data) == recv_checksum
117 }
118 };
119
120 if cancel_packet {
121 dev.write_all(&[Consts::CAN.into()])?;
122 dev.write_all(&[Consts::CAN.into()])?;
123 return Err(ModemError::Canceled);
124 }
125 if success {
126 packet_num = packet_num.wrapping_add(1);
127 dev.write_all(&[Consts::ACK.into()])?;
128 out.write_all(&data)?;
129 } else {
130 dev.write_all(&[Consts::NAK.into()])?;
131 self.errors += 1;
132 }
133 }
134 #[allow(non_snake_case)]
135 Some(_EOT) => {
136 dev.write_all(&[Consts::ACK.into()])?;
138 break;
139 }
140 None => {
141 self.errors += 1;
142 }
143 }
144 if self.errors >= self.max_errors {
145 dev.write_all(&[Consts::CAN.into()])?;
146 return Err(ModemError::ExhaustedRetries {
147 errors: Box::from(self.errors),
148 });
149 }
150 }
151 Ok(())
152 }
153
154 fn init_send<D>(&mut self, dev: &mut D) -> ModemResult<()>
155 where
156 D: Read + Write,
157 {
158 let mut cancels = 0u32;
159 loop {
160 if let Some(c) = get_byte_timeout(dev)?.map(Consts::from) {
161 match c {
162 Consts::NAK => {
163 self.checksum_mode = ChecksumKind::Standard;
164 return Ok(());
165 }
166 Consts::CRC => {
167 self.checksum_mode = ChecksumKind::Crc16;
168 return Ok(());
169 }
170 Consts::CAN => {
171 cancels += 1;
172 }
173 _c => (),
174 }
175 }
176
177 self.errors += 1;
178
179 if cancels >= 2 {
180 return Err(ModemError::Canceled);
181 }
182
183 if self.errors >= self.max_errors {
184 return Err(ModemError::ExhaustedRetries {
186 errors: Box::from(self.errors),
187 });
188 }
189 }
190 }
191
192 fn finish_send<D>(&mut self, dev: &mut D) -> ModemResult<()>
193 where
194 D: Read + Write,
195 {
196 loop {
197 dev.write_all(&[Consts::EOT.into()])?;
198
199 if let Some(c) = get_byte_timeout(dev)? {
200 #[allow(clippy::redundant_else)]
202 if c == Consts::ACK.into() {
203 return Ok(());
204 }
205 };
206
207 self.errors += 1;
208
209 if self.errors >= self.max_errors {
210 return Err(ModemError::ExhaustedRetries {
211 errors: Box::from(self.errors),
212 });
213 }
214 }
215 }
216
217 fn send_stream<D, R>(&mut self, dev: &mut D, inp: &mut R) -> ModemResult<()>
218 where
219 D: Read + Write,
220 R: Read,
221 {
222 let mut block_num = 0u32;
223 loop {
224 let mut buff = vec![self.pad_byte; self.block_length as usize + 3];
225 let n = inp.read(&mut buff[3..])?;
226 if n == 0 {
227 return Ok(());
228 }
229
230 block_num += 1;
231 buff[0] = match self.block_length {
232 BlockLengthKind::Standard => Consts::SOH.into(),
233 BlockLengthKind::OneK => Consts::STX.into(),
234 };
235 buff[1] = (&block_num & 0xFF) as u8;
236 buff[2] = 0xFF - &buff[1];
237
238 match self.checksum_mode {
239 ChecksumKind::Standard => {
240 let checksum = calc_checksum(&buff[3..]);
241 buff.push(checksum);
242 }
243 ChecksumKind::Crc16 => {
244 let crc = calc_crc(&buff[3..]);
245 buff.push(((crc >> 8) & 0xFF) as u8);
246 buff.push((&crc & 0xFF) as u8);
247 }
248 }
249
250 dev.write_all(&buff)?;
251
252 if let Some(c) = get_byte_timeout(dev)? {
253 if c == Consts::ACK.into() {
254 continue;
255 }
256 }
258
259 self.errors += 1;
260
261 if self.errors >= self.max_errors {
262 return Err(ModemError::ExhaustedRetries {
263 errors: Box::from(self.errors),
264 });
265 }
266 }
267 }
268}