Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modernize, format, add tracing, adjust log levels #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "MIT"
description = "An implementation of the XMODEM file-transfer protocol."

[dependencies]
log = "^0.3"
log = "^0.4.17"
crc16 = "^0.3"

[dev-dependencies]
Expand Down
221 changes: 133 additions & 88 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
extern crate log;
extern crate crc16;

use std::io::{self, Read, Write};
use std::convert::From;
use std::io::{self, Read, Write};

// TODO: Send CAN byte after too many errors
// TODO: Handle CAN bytes while sending
Expand Down Expand Up @@ -95,11 +95,11 @@ impl Xmodem {
self.errors = 0;

debug!("Starting XMODEM transfer");
try!(self.start_send(dev));
debug!("First byte received. Sending stream.");
try!(self.send_stream(dev, stream));
self.start_send(dev)?;
info!("First byte received. Sending stream.");
self.send_stream(dev, stream)?;
debug!("Sending EOT");
try!(self.finish_send(dev));
self.finish_send(dev)?;

Ok(())
}
Expand All @@ -116,135 +116,153 @@ impl Xmodem {
/// to set the timeout of the device before calling this method. Timeouts on receiving
/// bytes will be counted against `max_errors`, but timeouts on transmitting bytes
/// will be considered a fatal error.
pub fn recv<D: Read + Write, W: Write + ?Sized>(&mut self,
dev: &mut D,
outstream: &mut W,
checksum : Checksum) -> Result<()> {
pub fn recv<D: Read + Write, W: Write + ?Sized>(
&mut self,
dev: &mut D,
outstream: &mut W,
checksum: Checksum,
) -> Result<()> {
self.errors = 0;
self.checksum_mode = checksum;
let ncg = match self.checksum_mode {
Checksum::Standard => NAK,
Checksum::CRC16 => CRC };
Checksum::CRC16 => CRC,
};
debug!("Starting XMODEM receive");
try!(dev.write(&[ncg]));
// FIXME: handle written amount
let _ = dev.write(&[ncg])?;
debug!("NCG sent. Receiving stream.");
let mut packet_num : u8 = 1;
let mut packet_num: u8 = 1;
loop {
match try!(get_byte_timeout(dev)) {
bt @ Some(SOH) | bt @ Some(STX) => { // Handle next packet
match get_byte_timeout(dev)? {
bt @ Some(SOH) | bt @ Some(STX) => {
// Handle next packet
let data_size = match bt {
Some(SOH) => 128,
Some(STX) => 1024,
_ => 0, // Why does the compiler need this?
};
let mut data : Vec<u8> = Vec::new();
let mut data: Vec<u8> = Vec::new();
let packet_size = data_size
+ 2
+ match self.checksum_mode {
Checksum::Standard => 1,
Checksum::CRC16 => 2,
};
data.resize(packet_size,0);
data.resize(packet_size, 0);
match dev.read_exact(&mut data) {
Ok(_) => {
// Check packet number
let pnum = data[0];
let pnum_1c = data[1];
if (packet_num != pnum) || (255-pnum != pnum_1c) {
if (packet_num != pnum) || (255 - pnum != pnum_1c) {
// We've lost data; cancel this transmission
warn!("Cancelling transmission.");
try!(dev.write(&[CAN]));
try!(dev.write(&[CAN]));
return Err(Error::Canceled)
let n = dev.write(&[CAN])?;
debug!("CAN sent, {n} bytes");
let n = dev.write(&[CAN])?;
debug!("CAN sent, {n} bytes");
return Err(Error::Canceled);
}
// Check checksum
let check_ok = match self.checksum_mode {
Checksum::Standard => {
let recv_checksum = data[packet_size-1];
calc_checksum(&data[2..packet_size-1]) == recv_checksum },
let recv_checksum = data[packet_size - 1];
calc_checksum(&data[2..packet_size - 1]) == recv_checksum
}
Checksum::CRC16 => {
let recv_checksum =
((data[packet_size-2] as u16) << 8) +
(data[packet_size-1] as u16);
calc_crc(&data[2..packet_size-2]) == recv_checksum },
let recv_checksum = ((data[packet_size - 2] as u16) << 8)
+ (data[packet_size - 1] as u16);
calc_crc(&data[2..packet_size - 2]) == recv_checksum
}
};
// Response
if check_ok {
packet_num = packet_num.wrapping_add(1);
try!(dev.write(&[ACK]));
try!(outstream.write_all(&data[2..data_size+2]));
let n = dev.write(&[ACK])?;
debug!("ACK sent, {n} bytes");
outstream.write_all(&data[2..data_size + 2])?;
} else {
info!("Packet failed.");
try!(dev.write(&[NAK]));
let n = dev.write(&[NAK])?;
debug!("NAK sent, {n} bytes");
self.errors += 1;
}
},
}
Err(e) => {
error!("Error is {}",e);
error!("Error is {}", e);
// Timeout
self.errors += 1;
warn!("Timeout in packet! Resending NAK.");
try!(dev.write(&[NAK]));
},
let _ = dev.write(&[NAK])?;
}
}
},
Some(EOT) => { // End of file
try!(dev.write(&[ACK]));
break
},
}
Some(EOT) => {
// End of file
let _ = dev.write(&[ACK])?;
break;
}
Some(x) => {
warn!("Unrecognized symbol {:X}!",x);
},
warn!("Unrecognized symbol {:X}!", x);
}
None => {
self.errors += 1;
warn!("Timeout! Resending NAK.");
try!(dev.write(&[NAK]));
},
let _ = dev.write(&[NAK])?;
}
}
if self.errors >= self.max_errors {
error!("Exhausted max retries ({}) while waiting for ACK for EOT", self.max_errors);
error!(
"Exhausted max retries ({}) while waiting for ACK for EOT",
self.max_errors
);
return Err(Error::ExhaustedRetries);
}
}
Ok(())
}

fn start_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
let mut cancels = 0u32;
loop {
match try!(get_byte_timeout(dev)) {
Some(c) => {
match c {
NAK => {
debug!("Standard checksum requested");
self.checksum_mode = Checksum::Standard;
return Ok(());
}
CRC => {
debug!("16-bit CRC requested");
self.checksum_mode = Checksum::CRC16;
return Ok(());
}
CAN => {
warn!("Cancel (CAN) byte received");
cancels += 1;
},
c => warn!("Unknown byte received at start of XMODEM transfer: {}", c),
debug!("Send...");
match get_byte_timeout(dev)? {
Some(c) => match c {
NAK => {
debug!("Standard checksum requested");
self.checksum_mode = Checksum::Standard;
return Ok(());
}
CRC => {
debug!("16-bit CRC requested");
self.checksum_mode = Checksum::CRC16;
return Ok(());
}
CAN => {
warn!("Cancel (CAN) byte received");
cancels += 1;
}
b => warn!("Unknown byte received at start of XMODEM transfer: {b:x}",),
},
None => warn!("Timed out waiting for start of XMODEM transfer."),
}

self.errors += 1;

if cancels >= 2 {
error!("Transmission canceled: received two cancel (CAN) bytes \
at start of XMODEM transfer");
error!(
"Transmission canceled: received two cancel (CAN) bytes \
at start of XMODEM transfer"
);
return Err(Error::Canceled);
}

if self.errors >= self.max_errors {
error!("Exhausted max retries ({}) at start of XMODEM transfer.", self.max_errors);
error!(
"Exhausted max retries ({}) at start of XMODEM transfer.",
self.max_errors
);
if let Err(err) = dev.write_all(&[CAN]) {
warn!("Error sending CAN byte: {}", err);
}
Expand All @@ -257,7 +275,7 @@ impl Xmodem {
let mut block_num = 0u32;
loop {
let mut buff = vec![self.pad_byte; self.block_length as usize + 3];
let n = try!(stream.read(&mut buff[3..]));
let n = stream.read(&mut buff[3..])?;
if n == 0 {
debug!("Reached EOF");
return Ok(());
Expand All @@ -275,51 +293,69 @@ impl Xmodem {
Checksum::Standard => {
let checksum = calc_checksum(&buff[3..]);
buff.push(checksum);
},
}
Checksum::CRC16 => {
let crc = calc_crc(&buff[3..]);
buff.push(((crc >> 8) & 0xFF) as u8);
buff.push((crc & 0xFF) as u8);
}
}

debug!("Sending block {}", block_num);
try!(dev.write_all(&buff));
info!("Sending block {block_num}");
dev.write_all(&buff)?;

match try!(get_byte_timeout(dev)) {
match get_byte_timeout(dev)? {
Some(c) => {
if c == ACK {
debug!("Received ACK for block {}", block_num);
continue
} else {
warn!("Expected ACK, got {}", c);
match c {
ACK => {
debug!("Received ACK for block {block_num}");
continue;
}
NAK => {
error!("Received NAK for block {block_num} :(");
}
CAN => {
// TODO handle CAN bytes
warn!("Received CAN for block {block_num}, currently unhandled");
}
b'C' => {
warn!("Expected ACK, got C...");
}
b => {
warn!("Expected ACK, got 0x{b:x}");
}
}
// TODO handle CAN bytes
},
}
None => warn!("Timeout waiting for ACK for block {}", block_num),
}

self.errors += 1;

if self.errors >= self.max_errors {
error!("Exhausted max retries ({}) while sending block {} in XMODEM transfer",
self.max_errors, block_num);
error!(
"Exhausted max retries ({}) while sending block {} in XMODEM transfer",
self.max_errors, block_num
);
return Err(Error::ExhaustedRetries);
}
}
}

fn finish_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
loop {
try!(dev.write_all(&[EOT]));
dev.write_all(&[EOT])?;

match try!(get_byte_timeout(dev)) {
Some(c) => {
if c == ACK {
match get_byte_timeout(dev)? {
Some(c) => match c {
ACK => {
info!("XMODEM transmission successful");
return Ok(());
} else {
warn!("Expected ACK, got {}", c);
}
NAK => {
error!("Received NAK, transfer error :(");
}
b => {
warn!("Expected ACK, got {b:x}, transfer error");
}
},
None => warn!("Timeout waiting for ACK for EOT"),
Expand All @@ -328,7 +364,10 @@ impl Xmodem {
self.errors += 1;

if self.errors >= self.max_errors {
error!("Exhausted max retries ({}) while waiting for ACK for EOT", self.max_errors);
error!(
"Exhausted max retries ({}) while waiting for ACK for EOT",
self.max_errors
);
return Err(Error::ExhaustedRetries);
}
}
Expand All @@ -345,15 +384,21 @@ fn calc_crc(data: &[u8]) -> u16 {

fn get_byte<R: Read>(reader: &mut R) -> std::io::Result<u8> {
let mut buff = [0];
try!(reader.read_exact(&mut buff));
trace!("Read a byte...");
reader.read_exact(&mut buff)?;
Ok(buff[0])
}

/// Turns timeout errors into `Ok(None)`
fn get_byte_timeout<R: Read>(reader: &mut R) -> std::io::Result<Option<u8>> {
trace!("Get a byte...");
match get_byte(reader) {
Ok(c) => Ok(Some(c)),
Ok(c) => {
trace!("Got a byte.");
Ok(Some(c))
}
Err(err) => {
trace!("Got no byte.");
if err.kind() == io::ErrorKind::TimedOut {
Ok(None)
} else {
Expand Down