| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use alloc::vec::Vec; |
| use alloc::{format, vec}; |
| |
| use cantrip_io as io; |
| use log::Level::Debug; |
| |
| use consts::*; |
| use crc::*; |
| use frame::*; |
| |
| /// 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<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 hex::decode(&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<CI, CO, DO>( |
| header: u8, |
| count: &mut u32, |
| channel_in: &mut CI, |
| channel_out: &mut CO, |
| data_out: &mut DO, |
| ) -> io::Result<bool> |
| where |
| CI: io::BufRead, |
| CO: io::Write, |
| DO: io::Write, |
| { |
| let mut buf = Vec::new(); |
| |
| loop { |
| buf.clear(); |
| |
| let zcrc = match recv_zlde_frame(header, channel_in, &mut buf)? { |
| Some(x) => x, |
| None => return Ok(false), |
| }; |
| |
| data_out.write_all(&buf)?; |
| *count += buf.len() as u32; |
| |
| match zcrc { |
| ZCRCW => { |
| debug!("ZCRCW: CRC next, ZACK expected, end of frame"); |
| write_zack(channel_out, *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(channel_out, *count)? |
| } |
| ZCRCG => { |
| debug!("CCRCG: CRC next, frame continues nonstop"); |
| } |
| _ => { |
| error!("unexpected ZCRC byte: {:02X}", zcrc); |
| return Err(io::Error); |
| } |
| } |
| } |
| } |
| |
| /// 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 { !matches!(byte, ZCRCE | ZCRCG | ZCRCQ | ZCRCW) } |
| |
| /// 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 += "\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 super::*; |
| use consts::*; |
| use frame::*; |
| |
| #[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]); |
| } |
| } |