mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-09-16 06:58:29 +00:00
Forks lexxvir/zmodem
This is commit acdc761522679de2c52e0a7fa2640d48f7bd0ab5 on GitHub. The project is not actively maintained and requires substantial change to port to no_std and kata_io traits, making copying more appealing than tracking upstream. This change adds the unmodified files as a local diffbase. Change-Id: I8846f4842d75d01f07b2857998819115a9c449ba GitOrigin-RevId: 5cb637810ddfa5a35d73d680c16c36855f1b70ef
This commit is contained in:
4
apps/system/components/DebugConsole/zmodem/.gitignore
vendored
Normal file
4
apps/system/components/DebugConsole/zmodem/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
*.sw[po]
|
17
apps/system/components/DebugConsole/zmodem/Cargo.toml
Normal file
17
apps/system/components/DebugConsole/zmodem/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["aarbuzov"]
|
||||
name = "zmodem"
|
||||
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"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1"
|
||||
rand = "0.3.15"
|
28
apps/system/components/DebugConsole/zmodem/src/bin/rzm.rs
Normal file
28
apps/system/components/DebugConsole/zmodem/src/bin/rzm.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
extern crate zmodem;
|
||||
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
extern crate clap;
|
||||
|
||||
mod stdinout;
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use clap::{Arg, App};
|
||||
|
||||
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();
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
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()
|
||||
}
|
||||
}
|
31
apps/system/components/DebugConsole/zmodem/src/bin/szm.rs
Normal file
31
apps/system/components/DebugConsole/zmodem/src/bin/szm.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
extern crate zmodem;
|
||||
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
extern crate clap;
|
||||
|
||||
mod stdinout;
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use clap::{Arg, App};
|
||||
|
||||
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();
|
||||
}
|
39
apps/system/components/DebugConsole/zmodem/src/consts.rs
Normal file
39
apps/system/components/DebugConsole/zmodem/src/consts.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
pub const ZPAD: u8 = b'*';
|
||||
pub const ZLDE: u8 = 0x18;
|
||||
pub const ZLDEE: u8 = 0x58;
|
||||
pub const ZBIN: u8 = b'A'; // 0x41
|
||||
pub const ZHEX: u8 = b'B'; // 0x42
|
||||
pub const ZBIN32: u8 = b'C'; // 0x43
|
||||
|
||||
pub const ESC_FF: u8 = b'm';
|
||||
pub const ESC_7F: u8 = b'l';
|
||||
|
||||
/// Frame types
|
||||
pub const ZRQINIT: u8 = 0; /* Request receive init */
|
||||
pub const ZRINIT: u8 = 1; /* Receive init */
|
||||
pub const ZSINIT: u8 = 2; /* Send init sequence (optional) */
|
||||
pub const ZACK: u8 = 3; /* ACK to above */
|
||||
pub const ZFILE: u8 = 4; /* File name from sender */
|
||||
pub const ZSKIP: u8 = 5; /* To sender: skip this file */
|
||||
pub const ZNAK: u8 = 6; /* Last packet was garbled */
|
||||
pub const ZABORT: u8 = 7; /* Abort batch transfers */
|
||||
pub const ZFIN: u8 = 8; /* Finish session */
|
||||
pub const ZRPOS: u8 = 9; /* Resume data trans at this position */
|
||||
pub const ZDATA: u8 = 10; /* Data packet(s) follow */
|
||||
pub const ZEOF: u8 = 11; /* End of file */
|
||||
pub const ZFERR: u8 = 12; /* Fatal Read or Write error Detected */
|
||||
pub const ZCRC: u8 = 13; /* Request for file CRC and response */
|
||||
pub const ZCHALLENGE: u8 = 14; /* Receiver's Challenge */
|
||||
pub const ZCOMPL: u8 = 15; /* Request is complete */
|
||||
pub const ZCAN: u8 = 16; /* Other end canned session with CAN*5 */
|
||||
pub const ZFREECNT: u8 = 17; /* Request for free bytes on filesystem */
|
||||
pub const ZCOMMAND: u8 = 18; /* Command from sending program */
|
||||
pub const ZSTDERR: u8 = 19; /* Output to standard error, data follows */
|
||||
|
||||
/* ZDLE sequences */
|
||||
pub const ZCRCE: u8 = b'h'; /* CRC next, frame ends, header packet follows */
|
||||
pub const ZCRCG: u8 = b'i'; /* CRC next, frame continues nonstop */
|
||||
pub const ZCRCQ: u8 = b'j'; /* CRC next, frame continues, ZACK expected */
|
||||
pub const ZCRCW: u8 = b'k'; /* CRC next, ZACK expected, end of frame */
|
||||
|
||||
pub const XON: u8 = 0x11;
|
88
apps/system/components/DebugConsole/zmodem/src/crc.rs
Normal file
88
apps/system/components/DebugConsole/zmodem/src/crc.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use crc32::{crc32};
|
||||
use crc32::crc32::IEEE_TABLE;
|
||||
use crc32::crc32::update;
|
||||
|
||||
/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
|
||||
static CRCTAB: [u16; 256] = [
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||
];
|
||||
|
||||
/*
|
||||
* updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
|
||||
* NOTE: First srgument must be in range 0 to 255.
|
||||
* Second argument is referenced twice.
|
||||
*
|
||||
* Programmers may incorporate any or all code into their programs,
|
||||
* giving proper credit within the source. Publication of the
|
||||
* source routines is permitted so long as proper credit is given
|
||||
* to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
|
||||
* Omen Technology.
|
||||
*/
|
||||
|
||||
fn updcrc(cp: u8, crc: u16) -> u16 {
|
||||
let idx = ((crc >> 8) & 255) as usize ;
|
||||
CRCTAB[idx] ^ (crc << 8) ^ (cp as u16)
|
||||
}
|
||||
|
||||
pub fn get_crc16(buf: &[u8], zcrc: Option<u8>) -> [u8; 2] {
|
||||
let mut crc = 0;
|
||||
for x in buf {
|
||||
crc = updcrc(*x, crc);
|
||||
}
|
||||
|
||||
if let Some(x) = zcrc {
|
||||
crc = updcrc(x, crc);
|
||||
}
|
||||
|
||||
crc = updcrc(0, updcrc(0, crc));
|
||||
|
||||
[
|
||||
(crc >> 8) as u8,
|
||||
(crc & 0xff) as u8,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn get_crc32(buf: &[u8], zcrc: Option<u8>) -> [u8; 4] {
|
||||
let mut crc = crc32::checksum_ieee(buf);
|
||||
if let Some(x) = zcrc {
|
||||
crc = update(crc, &IEEE_TABLE, &[x]);
|
||||
}
|
||||
|
||||
[
|
||||
(crc & 0xff) as u8,
|
||||
(crc >> 8) as u8,
|
||||
(crc >> 16) as u8,
|
||||
(crc >> 24) as u8,
|
||||
]
|
||||
}
|
174
apps/system/components/DebugConsole/zmodem/src/frame.rs
Normal file
174
apps/system/components/DebugConsole/zmodem/src/frame.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
use std::fmt;
|
||||
use consts::*;
|
||||
use crc;
|
||||
use hex::*;
|
||||
use proto;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Frame {
|
||||
header: u8,
|
||||
ftype: u8,
|
||||
flags: [u8; 4],
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new(header: u8, ftype: u8) -> Frame {
|
||||
Frame {
|
||||
header,
|
||||
ftype,
|
||||
flags: [0; 4],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flags<'b>(&'b mut self, flags: &[u8; 4]) -> &'b mut Frame {
|
||||
self.flags = *flags;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn count<'b>(&'b mut self, count: u32) -> &'b mut Frame {
|
||||
self.flags = [
|
||||
(count >> 0) as u8,
|
||||
(count >> 8) as u8,
|
||||
(count >> 16) as u8,
|
||||
(count >> 24) as u8,
|
||||
];
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_count(&self) -> u32 {
|
||||
(self.flags[3] as u32) << 24
|
||||
| (self.flags[2] as u32) << 16
|
||||
| (self.flags[1] as u32) << 8
|
||||
| (self.flags[0] as u32)
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
out.push(ZPAD);
|
||||
if self.header == ZHEX {
|
||||
out.push(ZPAD);
|
||||
}
|
||||
|
||||
out.push(ZLDE);
|
||||
out.push(self.header);
|
||||
out.push(self.ftype);
|
||||
out.extend_from_slice(&self.flags);
|
||||
|
||||
let mut crc = get_crc(self.header, &out);
|
||||
out.append(&mut crc);
|
||||
|
||||
if self.header == ZHEX {
|
||||
let hex = out.drain(4..).collect::<Vec<u8>>().to_hex();
|
||||
out.extend_from_slice(&hex.as_bytes());
|
||||
}
|
||||
|
||||
let tmp = out.drain(3..).collect::<Vec<_>>();
|
||||
let mut tmp2 = Vec::new();
|
||||
proto::escape_buf(&tmp, &mut tmp2);
|
||||
out.extend_from_slice(&tmp2);
|
||||
|
||||
if self.header == ZHEX {
|
||||
out.extend_from_slice(b"\r\n");
|
||||
|
||||
if self.ftype != ZACK && self.ftype != ZFIN {
|
||||
out.push(XON);
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub fn get_frame_type(&self) -> u8 {
|
||||
self.ftype
|
||||
}
|
||||
|
||||
pub fn get_header(&self) -> u8 {
|
||||
self.header
|
||||
}
|
||||
}
|
||||
|
||||
fn get_crc(header: u8, buf: &[u8]) -> Vec<u8> {
|
||||
let offset = match header {
|
||||
ZHEX => 4,
|
||||
_ => 3,
|
||||
};
|
||||
|
||||
match header {
|
||||
ZBIN32 => crc::get_crc32(&buf[offset..], None).to_vec(),
|
||||
_ => crc::get_crc16(&buf[offset..], None).to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Frame {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let hdr = match self.header {
|
||||
ZHEX => "ZHEX",
|
||||
ZBIN => "ZBIN",
|
||||
ZBIN32 => "ZBIN32",
|
||||
_ => "???",
|
||||
};
|
||||
|
||||
let ft = match self.ftype {
|
||||
ZRQINIT => "ZRQINIT",
|
||||
ZRINIT => "ZRINIT",
|
||||
ZSINIT => "ZSINIT",
|
||||
ZACK => "ZACK",
|
||||
ZFILE => "ZFILE",
|
||||
ZSKIP => "ZSKIP",
|
||||
ZNAK => "ZNAK",
|
||||
ZABORT => "ZABORT",
|
||||
ZFIN => "ZFIN",
|
||||
ZRPOS => "ZRPOS",
|
||||
ZDATA => "ZDATA",
|
||||
ZEOF => "ZEOF",
|
||||
ZFERR => "ZFERR",
|
||||
ZCRC => "ZCRC",
|
||||
ZCHALLENGE => "ZCHALLENGE",
|
||||
ZCOMPL => "ZCOMPL",
|
||||
ZCAN => "ZCAN",
|
||||
ZFREECNT => "ZFREECNT",
|
||||
ZCOMMAND => "ZCOMMAND",
|
||||
ZSTDERR => "ZSTDERR",
|
||||
_ => "???",
|
||||
};
|
||||
|
||||
write!(f, "{}({})", hdr, ft)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frame() {
|
||||
assert_eq!(
|
||||
Frame::new(ZBIN, 0).build(),
|
||||
vec![ZPAD, ZLDE, ZBIN, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
assert_eq!(
|
||||
Frame::new(ZBIN32, 0).build(),
|
||||
vec![ZPAD, ZLDE, ZBIN32, 0, 0, 0, 0, 0, 29, 247, 34, 198]);
|
||||
|
||||
assert_eq!(
|
||||
Frame::new(ZBIN, 0)
|
||||
.flags(&[1; 4])
|
||||
.build(),
|
||||
vec![ZPAD, ZLDE, ZBIN, 0, 1, 1, 1, 1, 98, 148]);
|
||||
|
||||
assert_eq!(
|
||||
Frame::new(ZBIN, 0)
|
||||
.flags(&[1; 4])
|
||||
.build(),
|
||||
vec![ZPAD, ZLDE, ZBIN, 0, 1, 1, 1, 1, 98, 148]);
|
||||
|
||||
assert_eq!(
|
||||
Frame::new(ZHEX, 0)
|
||||
.flags(&[1; 4])
|
||||
.build(),
|
||||
vec![ZPAD, ZPAD, ZLDE, ZHEX,
|
||||
b'0', b'0',
|
||||
b'0', b'1',
|
||||
b'0', b'1',
|
||||
b'0', b'1',
|
||||
b'0', b'1',
|
||||
54, 50, 57, 52,
|
||||
b'\r', b'\n', XON]);
|
||||
}
|
15
apps/system/components/DebugConsole/zmodem/src/lib.rs
Normal file
15
apps/system/components/DebugConsole/zmodem/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate crc as crc32;
|
||||
extern crate hex;
|
||||
extern crate hexdump;
|
||||
|
||||
mod consts;
|
||||
mod frame;
|
||||
mod crc;
|
||||
mod proto;
|
||||
mod rwlog;
|
||||
|
||||
pub mod recv;
|
||||
pub mod send;
|
430
apps/system/components/DebugConsole/zmodem/src/proto.rs
Normal file
430
apps/system/components/DebugConsole/zmodem/src/proto.rs
Normal file
@@ -0,0 +1,430 @@
|
||||
use std::io;
|
||||
use hex::*;
|
||||
use log::LogLevel::{Debug};
|
||||
|
||||
use consts::*;
|
||||
use frame::*;
|
||||
use crc::*;
|
||||
|
||||
/// Looking for sequence: ZPAD [ZPAD] ZLDE
|
||||
/// Returns true if found otherwise false
|
||||
pub fn find_zpad<R>(r: &mut R) -> io::Result<bool>
|
||||
where R: io::Read {
|
||||
|
||||
// looking for first ZPAD
|
||||
if read_byte(r)? != ZPAD {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// get next byte
|
||||
let mut b = read_byte(r)?;
|
||||
|
||||
// skip second ZPAD
|
||||
if b == ZPAD {
|
||||
b = read_byte(r)?;
|
||||
}
|
||||
|
||||
// expect ZLDE
|
||||
if b != ZLDE {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn parse_header<'a, R>(mut r: R) -> io::Result<Option<Frame>>
|
||||
where R: io::Read {
|
||||
|
||||
let header = read_byte(&mut r)?;
|
||||
|
||||
match header {
|
||||
ZBIN32 | ZBIN | ZHEX => (),
|
||||
_ => {
|
||||
error!("unexpected header byte!");
|
||||
return Ok(None)
|
||||
},
|
||||
};
|
||||
|
||||
let len = 1 + 4; // frame type + flags
|
||||
let len = if header == ZBIN32 { 4 } else { 2 } + len;
|
||||
let len = if header == ZHEX { len * 2 } else { len };
|
||||
let mut v = vec![0; len];
|
||||
|
||||
read_exact_unescaped(r, &mut v)?;
|
||||
|
||||
if header == ZHEX {
|
||||
v = match FromHex::from_hex(&v) {
|
||||
Ok(x) => x,
|
||||
_ => {
|
||||
error!("from_hex error");
|
||||
return Ok(None);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let crc1 = v[5..].to_vec();
|
||||
let crc2 = match header {
|
||||
ZBIN32 => get_crc32(&v[..5], None).to_vec(),
|
||||
_ => get_crc16(&v[..5], None).to_vec(),
|
||||
};
|
||||
|
||||
if crc1 != crc2 {
|
||||
error!("crc mismatch: {:?} != {:?}", crc1, crc2);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut frame = Frame::new(header, v[0]);
|
||||
frame.flags(&[v[1], v[2], v[3], v[4]]);
|
||||
|
||||
if log_enabled!(Debug) {
|
||||
debug!("Got frame: {}", frame);
|
||||
match frame.get_frame_type() {
|
||||
ZACK | ZRPOS => debug!(" offset = {}", frame.get_count()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(frame))
|
||||
}
|
||||
|
||||
/// Read out up to len bytes and remove escaped ones
|
||||
fn read_exact_unescaped<R>(mut r: R, buf: &mut [u8]) -> io::Result<()>
|
||||
where R: io::Read {
|
||||
|
||||
for x in buf {
|
||||
*x = match read_byte(&mut r)? {
|
||||
ZLDE => unescape(read_byte(&mut r)?),
|
||||
y => y,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
||||
loop {
|
||||
r.read_until(ZLDE, buf)?;
|
||||
let b = read_byte(r)?;
|
||||
|
||||
if !is_escaped(b) {
|
||||
*buf.last_mut().unwrap() = b; // replace ZLDE by ZCRC* byte
|
||||
break;
|
||||
}
|
||||
|
||||
*buf.last_mut().unwrap() = unescape(b);
|
||||
}
|
||||
|
||||
let crc_len = if header == ZBIN32 { 4 } else { 2 };
|
||||
let mut crc1 = vec![0; crc_len];
|
||||
|
||||
read_exact_unescaped(r, &mut crc1)?;
|
||||
|
||||
let crc2 = match header {
|
||||
ZBIN32 => get_crc32(buf, None).to_vec(),
|
||||
_ => get_crc16(buf, None).to_vec(),
|
||||
};
|
||||
|
||||
if crc1 != crc2 {
|
||||
error!("crc mismatch: {:?} != {:?}", crc1, crc2);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(buf.pop()) // pop ZCRC* byte
|
||||
}
|
||||
|
||||
pub fn recv_data<RW, OUT>(header: u8, count: &mut u32, rw: &mut RW, out: &mut OUT) -> io::Result<bool>
|
||||
where RW: io::Write + io::BufRead,
|
||||
OUT: io::Write {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
buf.clear();
|
||||
|
||||
let zcrc = match recv_zlde_frame(header, rw, &mut buf)? {
|
||||
Some(x) => x,
|
||||
None => return Ok(false),
|
||||
};
|
||||
|
||||
out.write_all(&buf)?;
|
||||
*count += buf.len() as u32;
|
||||
|
||||
match zcrc {
|
||||
ZCRCW => {
|
||||
debug!("ZCRCW: CRC next, ZACK expected, end of frame");
|
||||
write_zack(rw, *count)?;
|
||||
return Ok(true);
|
||||
},
|
||||
ZCRCE => {
|
||||
debug!("ZCRCE: CRC next, frame ends, header packet follows");
|
||||
return Ok(true);
|
||||
},
|
||||
ZCRCQ => {
|
||||
debug!("ZCRCQ: CRC next, frame continues, ZACK expected");
|
||||
write_zack(rw, *count)?
|
||||
},
|
||||
ZCRCG => {
|
||||
debug!("CCRCG: CRC next, frame continues nonstop");
|
||||
},
|
||||
_ => {
|
||||
panic!(format!("unexpected ZCRC byte: {:02X}", zcrc));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts escaped byte to unescaped one
|
||||
fn unescape(escaped_byte: u8) -> u8 {
|
||||
match escaped_byte {
|
||||
ESC_FF => 0xFF,
|
||||
ESC_7F => 0x7F,
|
||||
x => if x & 0x60 != 0 { x ^ 0x40 } else { x },
|
||||
}
|
||||
}
|
||||
|
||||
fn is_escaped(byte: u8) -> bool {
|
||||
match byte {
|
||||
ZCRCE | ZCRCG | ZCRCQ | ZCRCW => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads out one byte
|
||||
fn read_byte<R>(r: &mut R) -> io::Result<u8>
|
||||
where R: io::Read {
|
||||
let mut b = [0; 1];
|
||||
r.read_exact(&mut b).map(|_| b[0])
|
||||
}
|
||||
|
||||
/// Writes ZRINIT frame
|
||||
pub fn write_zrinit<W>(w: &mut W) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZRINIT");
|
||||
w.write_all(&Frame::new(ZHEX, ZRINIT).flags(&[0, 0, 0, 0x23]).build())
|
||||
}
|
||||
|
||||
/// Writes ZRQINIT frame
|
||||
pub fn write_zrqinit<W>(w: &mut W) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZRQINIT");
|
||||
w.write_all(&Frame::new(ZHEX, ZRQINIT).build())
|
||||
}
|
||||
|
||||
/// Writes ZFILE frame
|
||||
pub fn write_zfile<W>(w: &mut W, filename: &str, filesize: Option<u32>) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZFILE");
|
||||
w.write_all(&Frame::new(ZBIN32, ZFILE).build())?;
|
||||
|
||||
let mut zfile_data = format!("{}\0", filename);
|
||||
if let Some(size) = filesize {
|
||||
zfile_data += &format!(" {}", size);
|
||||
}
|
||||
zfile_data += &format!("\0");
|
||||
|
||||
debug!("ZFILE supplied data: {}", zfile_data);
|
||||
write_zlde_data(w, ZCRCW, zfile_data.as_bytes())
|
||||
}
|
||||
|
||||
/// Writes ZACK frame
|
||||
pub fn write_zack<W>(w: &mut W, count: u32) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZACK bytes={}", count);
|
||||
w.write_all(&Frame::new(ZHEX, ZACK).count(count).build())
|
||||
}
|
||||
|
||||
/// Writes ZFIN frame
|
||||
pub fn write_zfin<W>(w: &mut W) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZFIN");
|
||||
w.write_all(&Frame::new(ZHEX, ZFIN).build())
|
||||
}
|
||||
|
||||
/// Writes ZNAK frame
|
||||
pub fn write_znak<W>(w: &mut W) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZNAK");
|
||||
w.write_all(&Frame::new(ZHEX, ZNAK).build())
|
||||
}
|
||||
|
||||
/// Writes ZRPOS frame
|
||||
pub fn write_zrpos<W>(w: &mut W, count: u32) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZRPOS bytes={}", count);
|
||||
w.write_all(&Frame::new(ZHEX, ZRPOS).count(count).build())
|
||||
}
|
||||
|
||||
/// Writes ZDATA frame
|
||||
pub fn write_zdata<W>(w: &mut W, offset: u32) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZDATA offset={}", offset);
|
||||
w.write_all(&Frame::new(ZBIN32, ZDATA).count(offset).build())
|
||||
}
|
||||
|
||||
/// Writes ZEOF frame
|
||||
pub fn write_zeof<W>(w: &mut W, offset: u32) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
debug!("write ZEOF offset={}", offset);
|
||||
w.write_all(&Frame::new(ZBIN32, ZEOF).count(offset).build())
|
||||
}
|
||||
|
||||
pub fn write_zlde_data<W>(w: &mut W, zcrc_byte: u8, data: &[u8]) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
if log_enabled!(Debug) {
|
||||
debug!(" ZCRC{} subpacket, size = {}",
|
||||
match zcrc_byte {
|
||||
ZCRCE => "E",
|
||||
ZCRCG => "G",
|
||||
ZCRCQ => "Q",
|
||||
ZCRCW => "W",
|
||||
_ => "?",
|
||||
},
|
||||
data.len());
|
||||
}
|
||||
|
||||
let crc = get_crc32(data, Some(zcrc_byte));
|
||||
|
||||
write_escape(w, data)?;
|
||||
w.write(&[ZLDE, zcrc_byte])?;
|
||||
write_escape(w, &crc)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_escape<W>(w: &mut W, data: &[u8]) -> io::Result<()>
|
||||
where W: io::Write {
|
||||
|
||||
//let mut w = io::BufWriter::new(w);
|
||||
|
||||
let mut esc_data = Vec::with_capacity(data.len() + data.len()/10);
|
||||
escape_buf(data, &mut esc_data);
|
||||
w.write_all(&esc_data)
|
||||
}
|
||||
|
||||
/// Writes "Over & Out"
|
||||
pub fn write_over_and_out<W>(w: &mut W) -> io::Result<()>
|
||||
where W: io::Write
|
||||
{
|
||||
w.write_all("OO".as_bytes())
|
||||
}
|
||||
|
||||
|
||||
pub fn escape_buf(src: &[u8], dst: &mut Vec<u8>) {
|
||||
for x in src {
|
||||
match *x {
|
||||
0xFF => dst.extend_from_slice(&[ZLDE, ESC_FF]),
|
||||
0x7F => dst.extend_from_slice(&[ZLDE, ESC_7F]),
|
||||
0x10 | 0x90 | 0x11 | 0x91 | 0x13 | 0x93
|
||||
=> dst.extend_from_slice(&[ZLDE, x ^ 0x40]),
|
||||
ZLDE => dst.extend_from_slice(&[ZLDE, ZLDEE]),
|
||||
x => dst.push(x),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use consts::*;
|
||||
use frame::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_find_zpad() {
|
||||
let v = vec![ZPAD, ZLDE];
|
||||
assert!(find_zpad(&mut v.as_slice()).unwrap());
|
||||
|
||||
let v = vec![ZPAD, ZPAD, ZLDE];
|
||||
assert!(find_zpad(&mut v.as_slice()).unwrap());
|
||||
|
||||
let v = vec![ZLDE];
|
||||
assert!(!find_zpad(&mut v.as_slice()).unwrap());
|
||||
|
||||
let v = vec![];
|
||||
assert!(find_zpad(&mut v.as_slice()).is_err());
|
||||
|
||||
let v = vec![0; 100];
|
||||
assert!(!find_zpad(&mut v.as_slice()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_exact_unescaped() {
|
||||
let i = [0; 32];
|
||||
let mut o = [0; 32];
|
||||
read_exact_unescaped(&i[..], &mut o).unwrap();
|
||||
assert_eq!(i, o);
|
||||
|
||||
let i = [ZLDE, b'm', ZLDE, b'l', ZLDE, 0x6f];
|
||||
let mut o = [0; 3];
|
||||
read_exact_unescaped(&i[..], &mut o).unwrap();
|
||||
assert_eq!(o, [0xff, 0x7f, 0x2f]);
|
||||
|
||||
let i = [ZLDE, b'm', 0, 2, ZLDE, b'l'];
|
||||
let mut o = [0; 4];
|
||||
read_exact_unescaped(&i[..], &mut o).unwrap();
|
||||
assert_eq!(o, [0xff, 0, 2, 0x7f]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_header() {
|
||||
let i = [ZHEX, b'0', b'1', b'0', b'1', b'0', b'2', b'0', b'3', b'0', b'4', b'a', b'7', b'5', b'2'];
|
||||
assert_eq!(
|
||||
&mut parse_header(&i[..]).unwrap().unwrap(),
|
||||
Frame::new(ZHEX, 1).flags(&[0x1, 0x2, 0x3, 0x4]));
|
||||
|
||||
let frame = 1;
|
||||
let i = [ZBIN, frame, 0xa, 0xb, 0xc, 0xd, 0xa6, 0xcb];
|
||||
assert_eq!(
|
||||
&mut parse_header(&i[..]).unwrap().unwrap(),
|
||||
Frame::new(ZBIN, frame).flags(&[0xa, 0xb, 0xc, 0xd]));
|
||||
|
||||
let frame = 1;
|
||||
let i = [ZBIN32, frame, 0xa, 0xb, 0xc, 0xd, 0x99, 0xe2, 0xae, 0x4a];
|
||||
assert_eq!(
|
||||
&mut parse_header(&i[..]).unwrap().unwrap(),
|
||||
Frame::new(ZBIN32, frame).flags(&[0xa, 0xb, 0xc, 0xd]));
|
||||
|
||||
let frame = 1;
|
||||
let i = [ZBIN, frame, 0xa, ZLDE, b'l', 0xd, ZLDE, b'm', 0x5e, 0x6f];
|
||||
assert_eq!(
|
||||
&mut parse_header(&i[..]).unwrap().unwrap(),
|
||||
Frame::new(ZBIN, frame).flags(&[0xa, 0x7f, 0xd, 0xff]));
|
||||
|
||||
let frame = 1;
|
||||
let i = [0xaa, frame, 0xa, 0xb, 0xc, 0xd, 0xf, 0xf];
|
||||
assert_eq!(parse_header(&i[..]).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recv_zlde_frame() {
|
||||
let i = vec![ZLDE, ZCRCE, 237, 174];
|
||||
let mut v = vec![];
|
||||
assert_eq!(recv_zlde_frame(ZBIN, &mut i.as_slice(), &mut v).unwrap(), Some(ZCRCE));
|
||||
assert_eq!(&v[..], []);
|
||||
|
||||
let i = vec![ZLDE, 0x00, ZLDE, ZCRCW, 221, 205];
|
||||
let mut v = vec![];
|
||||
assert_eq!(recv_zlde_frame(ZBIN, &mut i.as_slice(), &mut v).unwrap(), Some(ZCRCW));
|
||||
assert_eq!(&v[..], [0x00]);
|
||||
|
||||
let i = vec![0, 1, 2, 3, 4, ZLDE, 0x60, ZLDE, 0x60, ZLDE, ZCRCQ, 85, 114, 241, 70];
|
||||
let mut v = vec![];
|
||||
assert_eq!(recv_zlde_frame(ZBIN32, &mut i.as_slice(), &mut v).unwrap(), Some(ZCRCQ));
|
||||
assert_eq!(&v[..], [0, 1, 2, 3, 4, 0x20, 0x20]);
|
||||
}
|
||||
}
|
135
apps/system/components/DebugConsole/zmodem/src/recv.rs
Normal file
135
apps/system/components/DebugConsole/zmodem/src/recv.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use std::{thread, time};
|
||||
use std::io::{Read, Write, Result};
|
||||
use std::str::from_utf8;
|
||||
|
||||
use consts::*;
|
||||
use proto::*;
|
||||
use rwlog;
|
||||
use frame::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum State {
|
||||
/// Sending ZRINIT
|
||||
SendingZRINIT,
|
||||
|
||||
/// Processing ZFILE supplementary data
|
||||
ProcessingZFILE,
|
||||
|
||||
/// Receiving file's content
|
||||
ReceivingData,
|
||||
|
||||
/// Checking length of received data
|
||||
CheckingData,
|
||||
|
||||
/// All works done, exiting
|
||||
Done,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new() -> State {
|
||||
State::SendingZRINIT
|
||||
}
|
||||
|
||||
fn next(self, frame: &Frame) -> State {
|
||||
match (self, frame.get_frame_type()) {
|
||||
(State::SendingZRINIT, ZFILE) => State::ProcessingZFILE,
|
||||
(State::SendingZRINIT, _) => State::SendingZRINIT,
|
||||
|
||||
(State::ProcessingZFILE, ZDATA) => State::ReceivingData,
|
||||
(State::ProcessingZFILE, _) => State::ProcessingZFILE,
|
||||
|
||||
(State::ReceivingData, ZDATA) => State::ReceivingData,
|
||||
(State::ReceivingData, ZEOF) => State::CheckingData,
|
||||
|
||||
(State::CheckingData, ZDATA) => State::ReceivingData,
|
||||
(State::CheckingData, ZFIN) => State::Done,
|
||||
|
||||
(s, _) => {
|
||||
error!("Unexpected (state, frame) combination: {:#?} {}", s, frame);
|
||||
s // don't change current state
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives data by Z-Modem protocol
|
||||
pub fn recv<RW, W>(rw: RW, mut w: W) -> Result<usize>
|
||||
where RW: Read + Write,
|
||||
W: Write
|
||||
{
|
||||
let mut rw_log = rwlog::ReadWriteLog::new(rw);
|
||||
let mut count = 0;
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
write_zrinit(&mut rw_log)?;
|
||||
|
||||
while state != State::Done {
|
||||
if !find_zpad(&mut rw_log)? {
|
||||
continue;
|
||||
}
|
||||
|
||||
let frame = match parse_header(&mut rw_log)? {
|
||||
Some(x) => x,
|
||||
None => { recv_error(&mut rw_log, &state, count)?; continue },
|
||||
};
|
||||
|
||||
state = state.next(&frame);
|
||||
debug!("State: {:?}", state);
|
||||
|
||||
// do things according new state
|
||||
match state {
|
||||
State::SendingZRINIT => {
|
||||
write_zrinit(&mut rw_log)?;
|
||||
},
|
||||
State::ProcessingZFILE => {
|
||||
let mut buf = Vec::new();
|
||||
|
||||
if recv_zlde_frame(frame.get_header(), &mut rw_log, &mut buf)?.is_none() {
|
||||
write_znak(&mut rw_log)?;
|
||||
}
|
||||
else {
|
||||
write_zrpos(&mut rw_log, count)?;
|
||||
|
||||
// TODO: process supplied data
|
||||
if let Ok(s) = from_utf8(&buf) {
|
||||
debug!(target: "proto", "ZFILE supplied data: {}", s);
|
||||
}
|
||||
}
|
||||
},
|
||||
State::ReceivingData => {
|
||||
if frame.get_count() != count ||
|
||||
!recv_data(frame.get_header(), &mut count, &mut rw_log, &mut w)? {
|
||||
write_zrpos(&mut rw_log, count)?;
|
||||
}
|
||||
},
|
||||
State::CheckingData => {
|
||||
if frame.get_count() != count {
|
||||
error!("ZEOF offset mismatch: frame({}) != recv({})", frame.get_count(), count);
|
||||
// receiver ignores the ZEOF because a new zdata is coming
|
||||
}
|
||||
else {
|
||||
write_zrinit(&mut rw_log)?;
|
||||
}
|
||||
},
|
||||
State::Done => {
|
||||
write_zfin(&mut rw_log)?;
|
||||
thread::sleep(time::Duration::from_millis(10)); // sleep a bit
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count as usize)
|
||||
}
|
||||
|
||||
fn recv_error<W>(w: &mut W, state: &State, count: u32) -> Result<()>
|
||||
where W: Write
|
||||
{
|
||||
// TODO: flush input
|
||||
|
||||
match *state {
|
||||
State::ReceivingData => write_zrpos(w, count),
|
||||
_ => write_znak(w),
|
||||
}
|
||||
}
|
||||
|
67
apps/system/components/DebugConsole/zmodem/src/rwlog.rs
Normal file
67
apps/system/components/DebugConsole/zmodem/src/rwlog.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::io::*;
|
||||
use log::LogLevel::*;
|
||||
use hexdump::*;
|
||||
|
||||
pub struct ReadWriteLog<RW> {
|
||||
inner: BufReader<RW>,
|
||||
}
|
||||
|
||||
impl<RW: Read + Write> ReadWriteLog<RW> {
|
||||
pub fn new(rw: RW) -> ReadWriteLog<RW> {
|
||||
ReadWriteLog {
|
||||
inner: BufReader::new(rw),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Read for ReadWriteLog<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let r = self.inner.read(buf)?;
|
||||
|
||||
if log_enabled!(Debug) {
|
||||
debug!("In:");
|
||||
for x in hexdump_iter(&buf[..r]) {
|
||||
debug!("{}", x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> BufRead for ReadWriteLog<R> {
|
||||
fn fill_buf(&mut self) -> Result<&[u8]> {
|
||||
let r = self.inner.fill_buf()?;
|
||||
|
||||
if log_enabled!(Debug) {
|
||||
debug!("In:");
|
||||
for x in hexdump_iter(r) {
|
||||
debug!("{}", x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<RW: Write + Read> Write for ReadWriteLog<RW> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if log_enabled!(Debug) {
|
||||
debug!("Out:");
|
||||
for x in hexdump_iter(buf) {
|
||||
debug!("{}", x);
|
||||
}
|
||||
}
|
||||
|
||||
self.inner.get_mut().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.inner.get_mut().flush()
|
||||
}
|
||||
}
|
||||
|
146
apps/system/components/DebugConsole/zmodem/src/send.rs
Normal file
146
apps/system/components/DebugConsole/zmodem/src/send.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use std::io::{Read, Write, Result, Seek, SeekFrom};
|
||||
|
||||
use consts::*;
|
||||
use proto::*;
|
||||
use rwlog;
|
||||
use frame::*;
|
||||
|
||||
const SUBPACKET_SIZE: usize = 1024 * 8;
|
||||
const SUBPACKET_PER_ACK: usize = 10;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum State {
|
||||
/// Waiting ZRINIT invite (do nothing)
|
||||
WaitingInit,
|
||||
|
||||
/// Sending ZRQINIT
|
||||
SendingZRQINIT,
|
||||
|
||||
/// Sending ZFILE frame
|
||||
SendingZFILE,
|
||||
|
||||
/// Do nothing, just waiting for ZPOS
|
||||
WaitingZPOS,
|
||||
|
||||
/// Sending ZDATA & subpackets
|
||||
SendingData,
|
||||
|
||||
/// Sending ZFIN
|
||||
SendingZFIN,
|
||||
|
||||
/// All works done, exiting
|
||||
Done,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new() -> State {
|
||||
State::WaitingInit
|
||||
}
|
||||
|
||||
fn next(self, frame: &Frame) -> State {
|
||||
match (self, frame.get_frame_type()) {
|
||||
(State::WaitingInit, ZRINIT) => State::SendingZFILE,
|
||||
(State::WaitingInit, _) => State::SendingZRQINIT,
|
||||
|
||||
(State::SendingZRQINIT, ZRINIT) => State::SendingZFILE,
|
||||
|
||||
(State::SendingZFILE, ZRPOS) => State::SendingData,
|
||||
(State::SendingZFILE, ZRINIT) => State::WaitingZPOS,
|
||||
|
||||
(State::WaitingZPOS, ZRPOS) => State::SendingData,
|
||||
|
||||
(State::SendingData, ZACK) => State::SendingData,
|
||||
(State::SendingData, ZRPOS) => State::SendingData,
|
||||
(State::SendingData, ZRINIT) => State::SendingZFIN,
|
||||
|
||||
(State::SendingZFIN, ZFIN) => State::Done,
|
||||
|
||||
(s, _) => {
|
||||
error!("Unexpected (state, frame) combination: {:#?} {}", s, frame);
|
||||
s // don't change current state
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send<RW, R>(rw: RW, r: &mut R, filename: &str, filesize: Option<u32>) -> Result<()>
|
||||
where RW: Read + Write,
|
||||
R: Read + Seek
|
||||
{
|
||||
let mut rw_log = rwlog::ReadWriteLog::new(rw);
|
||||
|
||||
let mut data = [0; SUBPACKET_SIZE];
|
||||
let mut offset: u32;
|
||||
|
||||
write_zrqinit(&mut rw_log)?;
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
while state != State::Done {
|
||||
rw_log.flush()?;
|
||||
|
||||
if !find_zpad(&mut rw_log)? {
|
||||
continue;
|
||||
}
|
||||
|
||||
let frame = match parse_header(&mut rw_log)? {
|
||||
Some(x) => x,
|
||||
None => { write_znak(&mut rw_log)?; continue },
|
||||
};
|
||||
|
||||
state = state.next(&frame);
|
||||
debug!("State: {:?}", state);
|
||||
|
||||
// do things according new state
|
||||
match state {
|
||||
State::SendingZRQINIT => {
|
||||
write_zrqinit(&mut rw_log)?;
|
||||
},
|
||||
State::SendingZFILE => {
|
||||
write_zfile(&mut rw_log, filename, filesize)?;
|
||||
},
|
||||
State::SendingData => {
|
||||
offset = frame.get_count();
|
||||
r.seek(SeekFrom::Start(offset as u64))?;
|
||||
|
||||
let num = r.read(&mut data)?;
|
||||
|
||||
if num == 0 {
|
||||
write_zeof(&mut rw_log, offset)?;
|
||||
}
|
||||
else {
|
||||
// ZBIN32|ZDATA
|
||||
// ZCRCG - best perf
|
||||
// ZCRCQ - mid perf
|
||||
// ZCRCW - worst perf
|
||||
// ZCRCE - send at end
|
||||
write_zdata(&mut rw_log, offset)?;
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
i += 1;
|
||||
|
||||
write_zlde_data(&mut rw_log, ZCRCG, &data[..num])?;
|
||||
offset += num as u32;
|
||||
|
||||
let num = r.read(&mut data)?;
|
||||
if num < data.len() || i >= SUBPACKET_PER_ACK {
|
||||
write_zlde_data(&mut rw_log, ZCRCW, &data[..num])?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::SendingZFIN => {
|
||||
write_zfin(&mut rw_log)?;
|
||||
},
|
||||
State::Done => {
|
||||
write_over_and_out(&mut rw_log)?;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
160
apps/system/components/DebugConsole/zmodem/tests/lib.rs
Normal file
160
apps/system/components/DebugConsole/zmodem/tests/lib.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
extern crate zmodem;
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate rand;
|
||||
|
||||
use std::process::*;
|
||||
use std::fs::{File, remove_file, OpenOptions};
|
||||
use std::io::*;
|
||||
use std::time::*;
|
||||
use std::thread::{sleep, spawn};
|
||||
use std::result;
|
||||
|
||||
struct InOut<R: Read, W: Write> {
|
||||
r: R,
|
||||
w: W,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> InOut<R, W> {
|
||||
pub fn new(r: R, w: W) -> InOut<R, W> {
|
||||
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> Write for InOut<R, W> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.w.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.w.flush()
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref LOG_INIT: result::Result<(), log::SetLoggerError> = env_logger::init();
|
||||
static ref RND_VALUES: Vec<u8> = {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut buf = vec![0; 1024 * 1024 * 11];
|
||||
rng.fill_bytes(&mut buf);
|
||||
buf
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn recv_from_sz() {
|
||||
let _ = LOG_INIT.is_ok();
|
||||
|
||||
let mut f = File::create("recv_from_sz").unwrap();
|
||||
f.write_all(&RND_VALUES).unwrap();
|
||||
|
||||
let sz = Command::new("sz")
|
||||
.arg("recv_from_sz")
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("sz failed to run");
|
||||
|
||||
let child_stdin = sz.stdin.unwrap();
|
||||
let child_stdout = sz.stdout.unwrap();
|
||||
let mut inout = InOut::new(child_stdout, child_stdin);
|
||||
|
||||
let mut c = Cursor::new(Vec::new());
|
||||
zmodem::recv::recv(&mut inout, &mut c).unwrap();
|
||||
|
||||
sleep(Duration::from_millis(300));
|
||||
remove_file("recv_from_sz").unwrap();
|
||||
|
||||
assert_eq!(RND_VALUES.clone(), c.into_inner());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn send_to_rz() {
|
||||
let _ = LOG_INIT.is_ok();
|
||||
|
||||
let _ = remove_file("send_to_rz");
|
||||
|
||||
let sz = Command::new("rz")
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("rz failed to run");
|
||||
|
||||
let child_stdin = sz.stdin.unwrap();
|
||||
let child_stdout = sz.stdout.unwrap();
|
||||
let mut inout = InOut::new(child_stdout, child_stdin);
|
||||
|
||||
let len = RND_VALUES.len() as u32;
|
||||
let copy = RND_VALUES.clone();
|
||||
let mut cur = Cursor::new(©);
|
||||
|
||||
sleep(Duration::from_millis(300));
|
||||
|
||||
zmodem::send::send(&mut inout, &mut cur, "send_to_rz", Some(len)).unwrap();
|
||||
|
||||
sleep(Duration::from_millis(300));
|
||||
|
||||
let mut f = File::open("send_to_rz").expect("open 'send_to_rz'");
|
||||
let mut received = Vec::new();
|
||||
f.read_to_end(&mut received).unwrap();
|
||||
remove_file("send_to_rz").unwrap();
|
||||
|
||||
assert!(copy == received);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn lib_send_recv() {
|
||||
let _ = LOG_INIT;
|
||||
|
||||
let _ = remove_file("test-fifo1");
|
||||
let _ = remove_file("test-fifo2");
|
||||
|
||||
let _ = Command::new("mkfifo")
|
||||
.arg("test-fifo1")
|
||||
.spawn()
|
||||
.expect("mkfifo failed to run")
|
||||
.wait();
|
||||
|
||||
let _ = Command::new("mkfifo")
|
||||
.arg("test-fifo2")
|
||||
.spawn()
|
||||
.expect("mkfifo failed to run")
|
||||
.wait();
|
||||
|
||||
sleep(Duration::from_millis(300));
|
||||
|
||||
spawn(move || {
|
||||
let outf = OpenOptions::new().write(true).open("test-fifo1").unwrap();
|
||||
let inf = File::open("test-fifo2").unwrap();
|
||||
let mut inout = InOut::new(inf, outf);
|
||||
|
||||
let origin = RND_VALUES.clone();
|
||||
let mut c = Cursor::new(&origin);
|
||||
|
||||
zmodem::send::send(&mut inout, &mut c, "test", None).unwrap();
|
||||
});
|
||||
|
||||
let mut c = Cursor::new(Vec::new());
|
||||
|
||||
let inf = File::open("test-fifo1").unwrap();
|
||||
let outf = OpenOptions::new().write(true).open("test-fifo2").unwrap();
|
||||
let mut inout = InOut::new(inf, outf);
|
||||
|
||||
zmodem::recv::recv(&mut inout, &mut c).unwrap();
|
||||
|
||||
let _ = remove_file("test-fifo1");
|
||||
let _ = remove_file("test-fifo2");
|
||||
|
||||
assert_eq!(RND_VALUES.clone(), c.into_inner());
|
||||
}
|
Reference in New Issue
Block a user