Merge "Ports zmodem to no_std and kata_io"

GitOrigin-RevId: 09bc871f7c4fdd64f3b4591a47cf5ba0bffa72c2
This commit is contained in:
Matt Harvey 2021-09-21 19:21:26 +00:00 committed by Sam Leffler
parent c0a44de935
commit 66c03e7858
13 changed files with 191 additions and 144 deletions

View File

@ -7,6 +7,7 @@ members = [
"kata-logger", "kata-logger",
"kata-shell", "kata-shell",
"kata-uart-client", "kata-uart-client",
"zmodem",
] ]
[profile.dev] [profile.dev]

View File

@ -1,5 +1,8 @@
#![no_std] #![no_std]
use core::cmp;
#[derive(Debug)]
pub struct Error; pub struct Error;
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
@ -31,6 +34,8 @@ pub trait Read {
pub trait Write { pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>; fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
while !buf.is_empty() { while !buf.is_empty() {
match self.write(buf) { match self.write(buf) {
@ -64,3 +69,37 @@ pub enum SeekFrom {
pub trait Seek { pub trait Seek {
fn seek(&mut self, pos: SeekFrom) -> Result<u64>; fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
} }
impl Read for &[u8] {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
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<usize> {
(**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<usize> {
(**self).write(buf)
}
fn flush(&mut self) -> Result<()> {
(**self).flush()
}
}

View File

@ -40,7 +40,7 @@ pub extern "C" fn logger_log(level: u8, msg: *const cstr_core::c_char) {
pub struct Rx {} pub struct Rx {}
impl io::Read for Rx { impl io::Read for Rx {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe { unsafe {
rx_mutex_lock(); rx_mutex_lock();
uart_rx_update(buf.len()); uart_rx_update(buf.len());
@ -55,7 +55,7 @@ impl io::Read for Rx {
pub struct Tx {} pub struct Tx {}
impl io::Write for Tx { impl io::Write for Tx {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { unsafe {
tx_mutex_lock(); tx_mutex_lock();
let port = core::slice::from_raw_parts_mut(tx_dataport, buf.len()); let port = core::slice::from_raw_parts_mut(tx_dataport, buf.len());
@ -65,4 +65,9 @@ impl io::Write for Tx {
} }
Ok(buf.len()) Ok(buf.len())
} }
fn flush(&mut self) -> io::Result<()> {
// Do nothing. This implementation has no internal buffering.
Ok(())
}
} }

View File

@ -5,13 +5,12 @@ license = "MIT OR Apache-2.0"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
clap = "2.21.1" crc = { version = "1.4.0", default_features = false }
crc = "1.4.0" hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
env_logger = "0.4.2" kata-io = { path = "../kata-io" }
hex = "0.2.0" log = { version = "0.4.14", default-features = false }
hexdump = "0.1.0"
log = "0.3.7"
[dev-dependencies] [dev-dependencies]
lazy_static = "1" env_logger = "0.9.0"
lazy_static = { version = "1.4.0", features = ["spin"] }
rand = "0.3.15" rand = "0.3.15"

View File

@ -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();
}

View File

@ -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<usize> {
self.stdin.read(buf)
}
}
impl Write for CombinedStdInOut {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let r = self.stdout.write(buf)?;
self.stdout.flush()?;
Ok(r)
}
fn flush(&mut self) -> Result<()> {
self.stdout.flush()
}
}

View File

@ -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();
}

View File

@ -1,8 +1,11 @@
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use consts::*; use consts::*;
use crc; use crc;
use hex::*; use hex::*;
use proto; use proto;
use std::fmt;
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct Frame { pub struct Frame {
@ -59,7 +62,7 @@ impl Frame {
out.append(&mut crc); out.append(&mut crc);
if self.header == ZHEX { if self.header == ZHEX {
let hex = out.drain(4..).collect::<Vec<u8>>().to_hex(); let hex = out.drain(4..).collect::<Vec<u8>>().encode_hex::<String>();
out.extend_from_slice(&hex.as_bytes()); out.extend_from_slice(&hex.as_bytes());
} }
@ -95,8 +98,8 @@ fn get_crc(header: u8, buf: &[u8]) -> Vec<u8> {
}; };
match header { match header {
ZBIN32 => crc::get_crc32(&buf[offset..], None).to_vec(), ZBIN32 => crc::get_crc32(&buf[offset..], None).into(),
_ => crc::get_crc16(&buf[offset..], None).to_vec(), _ => crc::get_crc16(&buf[offset..], None).into(),
} }
} }
@ -139,6 +142,8 @@ impl fmt::Display for Frame {
#[test] #[test]
fn test_frame() { fn test_frame() {
use alloc::vec;
assert_eq!( assert_eq!(
Frame::new(ZBIN, 0).build(), Frame::new(ZBIN, 0).build(),
vec![ZPAD, ZLDE, ZBIN, 0, 0, 0, 0, 0, 0, 0] vec![ZPAD, ZLDE, ZBIN, 0, 0, 0, 0, 0, 0, 0]

View File

@ -1,15 +1,16 @@
#[macro_use] #![no_std]
extern crate log;
extern crate alloc;
extern crate crc as crc32; extern crate crc as crc32;
extern crate hex; extern crate hex;
extern crate hexdump; extern crate kata_io;
#[macro_use]
extern crate log;
mod consts; mod consts;
mod crc; mod crc;
mod frame; mod frame;
mod proto; mod proto;
mod rwlog;
pub mod recv; pub mod recv;
pub mod send; pub mod send;

View File

@ -1,6 +1,8 @@
use hex::*; use alloc::vec::Vec;
use log::LogLevel::Debug; use alloc::{format, vec};
use std::io;
use kata_io as io;
use log::Level::Debug;
use consts::*; use consts::*;
use crc::*; use crc::*;
@ -55,7 +57,7 @@ where
read_exact_unescaped(r, &mut v)?; read_exact_unescaped(r, &mut v)?;
if header == ZHEX { if header == ZHEX {
v = match FromHex::from_hex(&v) { v = match hex::decode(&v) {
Ok(x) => x, Ok(x) => x,
_ => { _ => {
error!("from_hex error"); error!("from_hex error");
@ -104,15 +106,37 @@ where
Ok(()) 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>(r: &mut R, delim: u8, buf: &mut Vec<u8>) -> io::Result<usize>
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: <escaped data> ZLDE ZCRC* <CRC bytes> /// Receives sequence: <escaped data> ZLDE ZCRC* <CRC bytes>
/// Unescapes sequencies such as 'ZLDE <escaped byte>' /// Unescapes sequencies such as 'ZLDE <escaped byte>'
/// If Ok returns <unescaped data> in buf and ZCRC* byte as return value /// If Ok returns <unescaped data> in buf and ZCRC* byte as return value
pub fn recv_zlde_frame<R>(header: u8, r: &mut R, buf: &mut Vec<u8>) -> io::Result<Option<u8>> pub fn recv_zlde_frame<R>(header: u8, r: &mut R, buf: &mut Vec<u8>) -> io::Result<Option<u8>>
where where
R: io::BufRead, R: io::Read,
{ {
loop { loop {
r.read_until(ZLDE, buf)?; read_until(r, ZLDE, buf)?;
let b = read_byte(r)?; let b = read_byte(r)?;
if !is_escaped(b) { if !is_escaped(b) {
@ -148,7 +172,7 @@ pub fn recv_data<RW, OUT>(
out: &mut OUT, out: &mut OUT,
) -> io::Result<bool> ) -> io::Result<bool>
where where
RW: io::Write + io::BufRead, RW: io::Write + io::Read,
OUT: io::Write, OUT: io::Write,
{ {
let mut buf = Vec::new(); let mut buf = Vec::new();
@ -182,7 +206,8 @@ where
debug!("CCRCG: CRC next, frame continues nonstop"); debug!("CCRCG: CRC next, frame continues nonstop");
} }
_ => { _ => {
panic!(format!("unexpected ZCRC byte: {:02X}", zcrc)); error!("unexpected ZCRC byte: {:02X}", zcrc);
return Err(io::Error);
} }
} }
} }

View File

@ -1,11 +1,11 @@
use std::io::{Read, Result, Write}; use alloc::vec::Vec;
use std::str::from_utf8; use core::str::from_utf8;
use std::{thread, time};
use kata_io as io;
use consts::*; use consts::*;
use frame::*; use frame::*;
use proto::*; use proto::*;
use rwlog;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum State { enum State {
@ -53,12 +53,13 @@ impl State {
} }
/// Receives data by Z-Modem protocol /// Receives data by Z-Modem protocol
pub fn recv<RW, W>(rw: RW, mut w: W) -> Result<usize> pub fn recv<RW, W>(rw: RW, mut w: W) -> io::Result<usize>
where where
RW: Read + Write, RW: io::Read + io::Write,
W: Write, W: io::Write,
{ {
let mut rw_log = rwlog::ReadWriteLog::new(rw); let mut rw_log = rw;
let mut count = 0; let mut count = 0;
let mut state = State::new(); let mut state = State::new();
@ -121,7 +122,11 @@ where
} }
State::Done => { State::Done => {
write_zfin(&mut rw_log)?; 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) Ok(count as usize)
} }
fn recv_error<W>(w: &mut W, state: &State, count: u32) -> Result<()> fn recv_error<W>(w: &mut W, state: &State, count: u32) -> io::Result<()>
where where
W: Write, W: io::Write,
{ {
// TODO: flush input // TODO: flush input

View File

@ -1,9 +1,8 @@
use std::io::{Read, Result, Seek, SeekFrom, Write}; use kata_io as io;
use consts::*; use consts::*;
use frame::*; use frame::*;
use proto::*; use proto::*;
use rwlog;
const SUBPACKET_SIZE: usize = 1024 * 8; const SUBPACKET_SIZE: usize = 1024 * 8;
const SUBPACKET_PER_ACK: usize = 10; const SUBPACKET_PER_ACK: usize = 10;
@ -63,12 +62,12 @@ impl State {
} }
} }
pub fn send<RW, R>(rw: RW, r: &mut R, filename: &str, filesize: Option<u32>) -> Result<()> pub fn send<RW, R>(rw: RW, r: &mut R, filename: &str, filesize: Option<u32>) -> io::Result<()>
where where
RW: Read + Write, RW: io::Read + io::Write,
R: Read + Seek, 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 data = [0; SUBPACKET_SIZE];
let mut offset: u32; let mut offset: u32;
@ -105,7 +104,7 @@ where
} }
State::SendingData => { State::SendingData => {
offset = frame.get_count(); 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)?; let num = r.read(&mut data)?;

View File

@ -12,6 +12,12 @@ use std::result;
use std::thread::{sleep, spawn}; use std::thread::{sleep, spawn};
use std::time::*; use std::time::*;
extern crate kata_io;
fn forget_err(_e: std::io::Error) -> kata_io::Error {
kata_io::Error {}
}
struct InOut<R: Read, W: Write> { struct InOut<R: Read, W: Write> {
r: R, r: R,
w: W, w: W,
@ -23,24 +29,66 @@ impl<R: Read, W: Write> InOut<R, W> {
} }
} }
impl<R: Read, W: Write> Read for InOut<R, W> { impl<R: Read, W: Write> kata_io::Read for InOut<R, W> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> kata_io::Result<usize> {
self.r.read(buf) self.r.read(buf).map_err(forget_err)
} }
} }
impl<R: Read, W: Write> Write for InOut<R, W> { impl<R: Read, W: Write> kata_io::Write for InOut<R, W> {
fn write(&mut self, buf: &[u8]) -> Result<usize> { fn write(&mut self, buf: &[u8]) -> kata_io::Result<usize> {
self.w.write(buf) self.w.write(buf).map_err(forget_err)
} }
fn flush(&mut self) -> Result<()> { fn flush(&mut self) -> kata_io::Result<()> {
self.w.flush() 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<T> {
c: std::io::Cursor<T>,
}
impl<T> WrappedCursor<T> {
pub fn into_inner(self) -> T {
self.c.into_inner()
}
}
impl kata_io::Read for WrappedCursor<&Vec<u8>> {
fn read(&mut self, buf: &mut [u8]) -> kata_io::Result<usize> {
self.c.read(buf).map_err(forget_err)
}
}
impl kata_io::Write for WrappedCursor<Vec<u8>> {
fn write(&mut self, buf: &[u8]) -> kata_io::Result<usize> {
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<u8>> {
fn seek(&mut self, pos: kata_io::SeekFrom) -> kata_io::Result<u64> {
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! { 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<u8> = { static ref RND_VALUES: Vec<u8> = {
use rand::Rng; use rand::Rng;
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -69,7 +117,9 @@ fn recv_from_sz() {
let child_stdout = sz.stdout.unwrap(); let child_stdout = sz.stdout.unwrap();
let mut inout = InOut::new(child_stdout, child_stdin); 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(); zmodem::recv::recv(&mut inout, &mut c).unwrap();
sleep(Duration::from_millis(300)); sleep(Duration::from_millis(300));
@ -97,7 +147,9 @@ fn send_to_rz() {
let len = RND_VALUES.len() as u32; let len = RND_VALUES.len() as u32;
let copy = RND_VALUES.clone(); let copy = RND_VALUES.clone();
let mut cur = Cursor::new(&copy); let mut cur = WrappedCursor {
c: Cursor::new(&copy),
};
sleep(Duration::from_millis(300)); sleep(Duration::from_millis(300));
@ -141,12 +193,16 @@ fn lib_send_recv() {
let mut inout = InOut::new(inf, outf); let mut inout = InOut::new(inf, outf);
let origin = RND_VALUES.clone(); 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(); 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 inf = File::open("test-fifo1").unwrap();
let outf = OpenOptions::new().write(true).open("test-fifo2").unwrap(); let outf = OpenOptions::new().write(true).open("test-fifo2").unwrap();