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-shell",
"kata-uart-client",
"zmodem",
]
[profile.dev]

View File

@ -1,5 +1,8 @@
#![no_std]
use core::cmp;
#[derive(Debug)]
pub struct Error;
pub type Result<T> = core::result::Result<T, Error>;
@ -31,6 +34,8 @@ pub trait Read {
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
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<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 {}
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 {
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<usize, io::Error> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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(())
}
}

View File

@ -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"

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 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::<Vec<u8>>().to_hex();
let hex = out.drain(4..).collect::<Vec<u8>>().encode_hex::<String>();
out.extend_from_slice(&hex.as_bytes());
}
@ -95,8 +98,8 @@ fn get_crc(header: u8, buf: &[u8]) -> Vec<u8> {
};
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]

View File

@ -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;

View File

@ -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>(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>
/// Unescapes sequencies such as 'ZLDE <escaped byte>'
/// 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>>
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<RW, OUT>(
out: &mut OUT,
) -> io::Result<bool>
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);
}
}
}

View File

@ -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, W>(rw: RW, mut w: W) -> Result<usize>
pub fn recv<RW, W>(rw: RW, mut w: W) -> io::Result<usize>
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>(w: &mut W, state: &State, count: u32) -> Result<()>
fn recv_error<W>(w: &mut W, state: &State, count: u32) -> io::Result<()>
where
W: Write,
W: io::Write,
{
// 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 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, 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
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)?;

View File

@ -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: Read, W: Write> {
r: R,
w: W,
@ -23,24 +29,66 @@ impl<R: Read, W: Write> InOut<R, W> {
}
}
impl<R: Read, W: Write> Read for InOut<R, W> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.r.read(buf)
impl<R: Read, W: Write> kata_io::Read for InOut<R, W> {
fn read(&mut self, buf: &mut [u8]) -> kata_io::Result<usize> {
self.r.read(buf).map_err(forget_err)
}
}
impl<R: Read, W: Write> Write for InOut<R, W> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.w.write(buf)
impl<R: Read, W: Write> kata_io::Write for InOut<R, W> {
fn write(&mut self, buf: &[u8]) -> kata_io::Result<usize> {
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<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! {
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> = {
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(&copy);
let mut cur = WrappedCursor {
c: Cursor::new(&copy),
};
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();