diff --git a/apps/system/components/DebugConsole/Cargo.toml b/apps/system/components/DebugConsole/Cargo.toml index b10369e..730158b 100644 --- a/apps/system/components/DebugConsole/Cargo.toml +++ b/apps/system/components/DebugConsole/Cargo.toml @@ -7,6 +7,7 @@ members = [ "kata-logger", "kata-shell", "kata-uart-client", + "zmodem", ] [profile.dev] diff --git a/apps/system/components/DebugConsole/kata-io/src/lib.rs b/apps/system/components/DebugConsole/kata-io/src/lib.rs index 5557257..c3519e5 100644 --- a/apps/system/components/DebugConsole/kata-io/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-io/src/lib.rs @@ -1,5 +1,8 @@ #![no_std] +use core::cmp; + +#[derive(Debug)] pub struct Error; pub type Result = core::result::Result; @@ -31,6 +34,8 @@ pub trait Read { pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; + fn flush(&mut self) -> Result<()>; + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { while !buf.is_empty() { match self.write(buf) { @@ -64,3 +69,37 @@ pub enum SeekFrom { pub trait Seek { fn seek(&mut self, pos: SeekFrom) -> Result; } + +impl Read for &[u8] { + fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + buf[..amt].copy_from_slice(a); + *self = b; + Ok(amt) + } +} + +/// Forwarding implementation of Read for &mut +impl<'a, T> Read for &'a mut T +where + T: Read, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + (**self).read(buf) + } +} + +/// Forwarding implementation of Write for &mut +impl<'a, T> Write for &'a mut T +where + T: Write, +{ + fn write(&mut self, buf: &[u8]) -> Result { + (**self).write(buf) + } + + fn flush(&mut self) -> Result<()> { + (**self).flush() + } +} diff --git a/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs b/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs index 03136f0..2b1eda8 100644 --- a/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-uart-client/src/lib.rs @@ -40,7 +40,7 @@ pub extern "C" fn logger_log(level: u8, msg: *const cstr_core::c_char) { pub struct Rx {} impl io::Read for Rx { - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> io::Result { unsafe { rx_mutex_lock(); uart_rx_update(buf.len()); @@ -55,7 +55,7 @@ impl io::Read for Rx { pub struct Tx {} impl io::Write for Tx { - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> io::Result { unsafe { tx_mutex_lock(); let port = core::slice::from_raw_parts_mut(tx_dataport, buf.len()); @@ -65,4 +65,9 @@ impl io::Write for Tx { } Ok(buf.len()) } + + fn flush(&mut self) -> io::Result<()> { + // Do nothing. This implementation has no internal buffering. + Ok(()) + } } diff --git a/apps/system/components/DebugConsole/zmodem/Cargo.toml b/apps/system/components/DebugConsole/zmodem/Cargo.toml index d2261ec..32e7e7c 100644 --- a/apps/system/components/DebugConsole/zmodem/Cargo.toml +++ b/apps/system/components/DebugConsole/zmodem/Cargo.toml @@ -5,13 +5,12 @@ license = "MIT OR Apache-2.0" version = "0.1.0" [dependencies] -clap = "2.21.1" -crc = "1.4.0" -env_logger = "0.4.2" -hex = "0.2.0" -hexdump = "0.1.0" -log = "0.3.7" +crc = { version = "1.4.0", default_features = false } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +kata-io = { path = "../kata-io" } +log = { version = "0.4.14", default-features = false } [dev-dependencies] -lazy_static = "1" +env_logger = "0.9.0" +lazy_static = { version = "1.4.0", features = ["spin"] } rand = "0.3.15" diff --git a/apps/system/components/DebugConsole/zmodem/src/bin/rzm.rs b/apps/system/components/DebugConsole/zmodem/src/bin/rzm.rs deleted file mode 100644 index fa7d6bd..0000000 --- a/apps/system/components/DebugConsole/zmodem/src/bin/rzm.rs +++ /dev/null @@ -1,26 +0,0 @@ -extern crate zmodem; - -extern crate clap; -extern crate env_logger; -extern crate log; - -mod stdinout; - -use clap::{App, Arg}; -use std::fs::File; -use std::path::Path; - -fn main() { - env_logger::init().unwrap(); - - let matches = App::new("Pure Rust implementation of rz utility") - .arg(Arg::with_name("file").required(false).index(1)) - .get_matches(); - - let fileopt = matches.value_of("file").unwrap_or("rz-out"); - let filename = Path::new(fileopt).file_name().unwrap().clone(); - let file = File::create(filename).expect(&format!("Cannot create file {:?}:", filename)); - - let inout = stdinout::CombinedStdInOut::new(); - zmodem::recv::recv(inout, file).unwrap(); -} diff --git a/apps/system/components/DebugConsole/zmodem/src/bin/stdinout/mod.rs b/apps/system/components/DebugConsole/zmodem/src/bin/stdinout/mod.rs deleted file mode 100644 index 9f2a3de..0000000 --- a/apps/system/components/DebugConsole/zmodem/src/bin/stdinout/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::io::*; - -pub struct CombinedStdInOut { - stdin: Stdin, - stdout: Stdout, -} - -impl CombinedStdInOut { - pub fn new() -> CombinedStdInOut { - CombinedStdInOut { - stdin: stdin(), - stdout: stdout(), - } - } -} - -impl Read for CombinedStdInOut { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.stdin.read(buf) - } -} - -impl Write for CombinedStdInOut { - fn write(&mut self, buf: &[u8]) -> Result { - let r = self.stdout.write(buf)?; - self.stdout.flush()?; - Ok(r) - } - - fn flush(&mut self) -> Result<()> { - self.stdout.flush() - } -} diff --git a/apps/system/components/DebugConsole/zmodem/src/bin/szm.rs b/apps/system/components/DebugConsole/zmodem/src/bin/szm.rs deleted file mode 100644 index 3cc7c0c..0000000 --- a/apps/system/components/DebugConsole/zmodem/src/bin/szm.rs +++ /dev/null @@ -1,29 +0,0 @@ -extern crate zmodem; - -extern crate clap; -extern crate env_logger; -extern crate log; - -mod stdinout; - -use clap::{App, Arg}; -use std::fs::File; -use std::path::Path; - -fn main() { - env_logger::init().unwrap(); - - let matches = App::new("Pure Rust implementation of sz utility") - .arg(Arg::with_name("file").required(true).index(1)) - .get_matches(); - - let fileopt = matches.value_of("file").unwrap(); - let mut file = File::open(fileopt).unwrap(); - - let filename = Path::new(fileopt).file_name().unwrap().clone(); - let size = file.metadata().map(|x| x.len() as u32).ok(); - - let inout = stdinout::CombinedStdInOut::new(); - - zmodem::send::send(inout, &mut file, filename.to_str().unwrap(), size).unwrap(); -} diff --git a/apps/system/components/DebugConsole/zmodem/src/frame.rs b/apps/system/components/DebugConsole/zmodem/src/frame.rs index 065516e..f8e6829 100644 --- a/apps/system/components/DebugConsole/zmodem/src/frame.rs +++ b/apps/system/components/DebugConsole/zmodem/src/frame.rs @@ -1,8 +1,11 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + use consts::*; use crc; use hex::*; use proto; -use std::fmt; #[derive(Debug, Eq, PartialEq)] pub struct Frame { @@ -59,7 +62,7 @@ impl Frame { out.append(&mut crc); if self.header == ZHEX { - let hex = out.drain(4..).collect::>().to_hex(); + let hex = out.drain(4..).collect::>().encode_hex::(); out.extend_from_slice(&hex.as_bytes()); } @@ -95,8 +98,8 @@ fn get_crc(header: u8, buf: &[u8]) -> Vec { }; match header { - ZBIN32 => crc::get_crc32(&buf[offset..], None).to_vec(), - _ => crc::get_crc16(&buf[offset..], None).to_vec(), + ZBIN32 => crc::get_crc32(&buf[offset..], None).into(), + _ => crc::get_crc16(&buf[offset..], None).into(), } } @@ -139,6 +142,8 @@ impl fmt::Display for Frame { #[test] fn test_frame() { + use alloc::vec; + assert_eq!( Frame::new(ZBIN, 0).build(), vec![ZPAD, ZLDE, ZBIN, 0, 0, 0, 0, 0, 0, 0] diff --git a/apps/system/components/DebugConsole/zmodem/src/lib.rs b/apps/system/components/DebugConsole/zmodem/src/lib.rs index 90a07f4..7458e85 100644 --- a/apps/system/components/DebugConsole/zmodem/src/lib.rs +++ b/apps/system/components/DebugConsole/zmodem/src/lib.rs @@ -1,15 +1,16 @@ -#[macro_use] -extern crate log; +#![no_std] +extern crate alloc; extern crate crc as crc32; extern crate hex; -extern crate hexdump; +extern crate kata_io; +#[macro_use] +extern crate log; mod consts; mod crc; mod frame; mod proto; -mod rwlog; pub mod recv; pub mod send; diff --git a/apps/system/components/DebugConsole/zmodem/src/proto.rs b/apps/system/components/DebugConsole/zmodem/src/proto.rs index 585a5da..7dc8ed0 100644 --- a/apps/system/components/DebugConsole/zmodem/src/proto.rs +++ b/apps/system/components/DebugConsole/zmodem/src/proto.rs @@ -1,6 +1,8 @@ -use hex::*; -use log::LogLevel::Debug; -use std::io; +use alloc::vec::Vec; +use alloc::{format, vec}; + +use kata_io as io; +use log::Level::Debug; use consts::*; use crc::*; @@ -55,7 +57,7 @@ where read_exact_unescaped(r, &mut v)?; if header == ZHEX { - v = match FromHex::from_hex(&v) { + v = match hex::decode(&v) { Ok(x) => x, _ => { error!("from_hex error"); @@ -104,15 +106,37 @@ where Ok(()) } +/// Read into buf until and including the byte delim +/// +/// This is a one-off implementation of a method of io::BufRead that the +/// original lexxvir/zmodem had used. (So far kata_io is deferring implementing +/// buffering to validate it will become needed in more places.) +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> io::Result +where + R: io::Read, +{ + let mut n = 0usize; + loop { + let b = read_byte(r)?; + buf.push(b); + n += 1; + if b == delim { + break; + } + } + + Ok(n) +} + /// Receives sequence: ZLDE ZCRC* /// Unescapes sequencies such as 'ZLDE ' /// If Ok returns in buf and ZCRC* byte as return value pub fn recv_zlde_frame(header: u8, r: &mut R, buf: &mut Vec) -> io::Result> where - R: io::BufRead, + R: io::Read, { loop { - r.read_until(ZLDE, buf)?; + read_until(r, ZLDE, buf)?; let b = read_byte(r)?; if !is_escaped(b) { @@ -148,7 +172,7 @@ pub fn recv_data( out: &mut OUT, ) -> io::Result where - RW: io::Write + io::BufRead, + RW: io::Write + io::Read, OUT: io::Write, { let mut buf = Vec::new(); @@ -182,7 +206,8 @@ where debug!("CCRCG: CRC next, frame continues nonstop"); } _ => { - panic!(format!("unexpected ZCRC byte: {:02X}", zcrc)); + error!("unexpected ZCRC byte: {:02X}", zcrc); + return Err(io::Error); } } } diff --git a/apps/system/components/DebugConsole/zmodem/src/recv.rs b/apps/system/components/DebugConsole/zmodem/src/recv.rs index 07ed55b..2b69531 100644 --- a/apps/system/components/DebugConsole/zmodem/src/recv.rs +++ b/apps/system/components/DebugConsole/zmodem/src/recv.rs @@ -1,11 +1,11 @@ -use std::io::{Read, Result, Write}; -use std::str::from_utf8; -use std::{thread, time}; +use alloc::vec::Vec; +use core::str::from_utf8; + +use kata_io as io; use consts::*; use frame::*; use proto::*; -use rwlog; #[derive(Debug, PartialEq)] enum State { @@ -53,12 +53,13 @@ impl State { } /// Receives data by Z-Modem protocol -pub fn recv(rw: RW, mut w: W) -> Result +pub fn recv(rw: RW, mut w: W) -> io::Result where - RW: Read + Write, - W: Write, + RW: io::Read + io::Write, + W: io::Write, { - let mut rw_log = rwlog::ReadWriteLog::new(rw); + let mut rw_log = rw; + let mut count = 0; let mut state = State::new(); @@ -121,7 +122,11 @@ where } State::Done => { write_zfin(&mut rw_log)?; - thread::sleep(time::Duration::from_millis(10)); // sleep a bit + // NB: lexxvir/zmodem had a 10ms sleep here that has been removed + // due to no_std. Here we change it to flush() so that a return + // from this function does really indicate all the bytes have + // been written. + rw_log.flush()?; } } } @@ -129,9 +134,9 @@ where Ok(count as usize) } -fn recv_error(w: &mut W, state: &State, count: u32) -> Result<()> +fn recv_error(w: &mut W, state: &State, count: u32) -> io::Result<()> where - W: Write, + W: io::Write, { // TODO: flush input diff --git a/apps/system/components/DebugConsole/zmodem/src/send.rs b/apps/system/components/DebugConsole/zmodem/src/send.rs index 639b9ab..2395d01 100644 --- a/apps/system/components/DebugConsole/zmodem/src/send.rs +++ b/apps/system/components/DebugConsole/zmodem/src/send.rs @@ -1,9 +1,8 @@ -use std::io::{Read, Result, Seek, SeekFrom, Write}; +use kata_io as io; use consts::*; use frame::*; use proto::*; -use rwlog; const SUBPACKET_SIZE: usize = 1024 * 8; const SUBPACKET_PER_ACK: usize = 10; @@ -63,12 +62,12 @@ impl State { } } -pub fn send(rw: RW, r: &mut R, filename: &str, filesize: Option) -> Result<()> +pub fn send(rw: RW, r: &mut R, filename: &str, filesize: Option) -> io::Result<()> where - RW: Read + Write, - R: Read + Seek, + RW: io::Read + io::Write, + R: io::Read + io::Seek, { - let mut rw_log = rwlog::ReadWriteLog::new(rw); + let mut rw_log = rw; let mut data = [0; SUBPACKET_SIZE]; let mut offset: u32; @@ -105,7 +104,7 @@ where } State::SendingData => { offset = frame.get_count(); - r.seek(SeekFrom::Start(offset as u64))?; + r.seek(io::SeekFrom::Start(offset as u64))?; let num = r.read(&mut data)?; diff --git a/apps/system/components/DebugConsole/zmodem/tests/lib.rs b/apps/system/components/DebugConsole/zmodem/tests/lib.rs index e12ee50..cc55497 100644 --- a/apps/system/components/DebugConsole/zmodem/tests/lib.rs +++ b/apps/system/components/DebugConsole/zmodem/tests/lib.rs @@ -12,6 +12,12 @@ use std::result; use std::thread::{sleep, spawn}; use std::time::*; +extern crate kata_io; + +fn forget_err(_e: std::io::Error) -> kata_io::Error { + kata_io::Error {} +} + struct InOut { r: R, w: W, @@ -23,24 +29,66 @@ impl InOut { } } -impl Read for InOut { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.r.read(buf) +impl kata_io::Read for InOut { + fn read(&mut self, buf: &mut [u8]) -> kata_io::Result { + self.r.read(buf).map_err(forget_err) } } -impl Write for InOut { - fn write(&mut self, buf: &[u8]) -> Result { - self.w.write(buf) +impl kata_io::Write for InOut { + fn write(&mut self, buf: &[u8]) -> kata_io::Result { + self.w.write(buf).map_err(forget_err) } - fn flush(&mut self) -> Result<()> { - self.w.flush() + fn flush(&mut self) -> kata_io::Result<()> { + self.w.flush().map_err(forget_err) + } +} + +/// Minimal vector-backed kata_io::Read and kata_io::Write +/// +/// This is a workaround to not being able to implement the kata_io traits for +/// std::io::Cursor in this file, since none of those come from the current +/// crate. +struct WrappedCursor { + c: std::io::Cursor, +} + +impl WrappedCursor { + pub fn into_inner(self) -> T { + self.c.into_inner() + } +} + +impl kata_io::Read for WrappedCursor<&Vec> { + fn read(&mut self, buf: &mut [u8]) -> kata_io::Result { + self.c.read(buf).map_err(forget_err) + } +} + +impl kata_io::Write for WrappedCursor> { + fn write(&mut self, buf: &[u8]) -> kata_io::Result { + self.c.write(buf).map_err(forget_err) + } + + fn flush(&mut self) -> kata_io::Result<()> { + self.c.flush().map_err(forget_err) + } +} + +impl kata_io::Seek for WrappedCursor<&Vec> { + fn seek(&mut self, pos: kata_io::SeekFrom) -> kata_io::Result { + let std_pos = match pos { + kata_io::SeekFrom::Start(n) => std::io::SeekFrom::Start(n), + kata_io::SeekFrom::End(n) => std::io::SeekFrom::End(n), + kata_io::SeekFrom::Current(n) => std::io::SeekFrom::Current(n), + }; + self.c.seek(std_pos).map_err(forget_err) } } lazy_static! { - static ref LOG_INIT: result::Result<(), log::SetLoggerError> = env_logger::init(); + static ref LOG_INIT: result::Result<(), log::SetLoggerError> = Ok(env_logger::init()); static ref RND_VALUES: Vec = { use rand::Rng; let mut rng = rand::thread_rng(); @@ -69,7 +117,9 @@ fn recv_from_sz() { let child_stdout = sz.stdout.unwrap(); let mut inout = InOut::new(child_stdout, child_stdin); - let mut c = Cursor::new(Vec::new()); + let mut c = WrappedCursor { + c: Cursor::new(Vec::new()), + }; zmodem::recv::recv(&mut inout, &mut c).unwrap(); sleep(Duration::from_millis(300)); @@ -97,7 +147,9 @@ fn send_to_rz() { let len = RND_VALUES.len() as u32; let copy = RND_VALUES.clone(); - let mut cur = Cursor::new(©); + let mut cur = WrappedCursor { + c: Cursor::new(©), + }; sleep(Duration::from_millis(300)); @@ -141,12 +193,16 @@ fn lib_send_recv() { let mut inout = InOut::new(inf, outf); let origin = RND_VALUES.clone(); - let mut c = Cursor::new(&origin); + let mut c = WrappedCursor { + c: Cursor::new(&origin), + }; zmodem::send::send(&mut inout, &mut c, "test", None).unwrap(); }); - let mut c = Cursor::new(Vec::new()); + let mut c = WrappedCursor { + c: Cursor::new(Vec::new()), + }; let inf = File::open("test-fifo1").unwrap(); let outf = OpenOptions::new().write(true).open("test-fifo2").unwrap();