-
-
Save mkeeter/509ad699cfac0112944f4b968ce04187 to your computer and use it in GitHub Desktop.
Minimal XMODEM sender
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{Read, Write}; | |
use anyhow::{anyhow, bail, Result}; | |
use libftdi1_sys as ffi; | |
const ACK: u8 = 0x06; | |
const NAK: u8 = 0x15; | |
const SUB: u8 = 0x1A; | |
const EOT: u8 = 0x04; | |
fn main() -> Result<()> { | |
let mut device = ftdi::find_by_vid_pid(0x0403, 0x6011) | |
.interface(ftdi::Interface::A) | |
.open()?; | |
device.usb_reset()?; | |
device.configure( | |
ftdi::Bits::Eight, | |
ftdi::StopBits::One, | |
ftdi::Parity::None, | |
)?; | |
device.usb_purge_buffers()?; | |
device.set_write_chunksize(64); | |
device.set_read_chunksize(64); | |
device.set_baud_rate(3_000_000)?; | |
device.set_latency_timer(2)?; | |
device.set_flow_control(ftdi::FlowControl::RtsCts)?; | |
device.set_bitmode(0xFF, ftdi::BitMode::Reset)?; | |
let result = unsafe { | |
// Not sure if this is needed! | |
(*device.libftdi_context()).usb_read_timeout = 1000; | |
(*device.libftdi_context()).usb_write_timeout = 1000; | |
ffi::ftdi_setdtr_rts(device.libftdi_context(), 1, 1) | |
}; | |
if result != 0 { | |
bail!("Could not set DTR / RTS"); | |
} | |
let filename = std::env::args() | |
.nth(1) | |
.ok_or_else(|| anyhow!("Missing filename"))?; | |
let data = std::fs::read(filename)?; | |
loop { | |
let mut c = vec![0u8]; | |
if device.read_exact(&mut c).is_ok() { | |
match c[0] as char { | |
'C' => break, | |
c => bail!("Invalid start char '{}'", c), | |
} | |
} | |
} | |
for (i, chunk) in data.chunks(1024).enumerate() { | |
if i % 100 == 0 { | |
print!(" Sending {}/{}\r", i, (data.len() + 1023) / 1024); | |
} | |
let packet_num = (i + 1) as u8; | |
let mut packet = vec![0x02, packet_num, 0xFF - packet_num]; | |
let mut crc = 0u16; | |
for b in chunk.iter().chain(std::iter::repeat(&SUB)).take(1024) { | |
packet.push(*b); | |
crc = update_crc(*b, crc); | |
} | |
crc = update_crc(0, update_crc(0, crc)); | |
packet.push((crc >> 8) as u8); // CRC | |
packet.push(crc as u8); | |
device.write_all(&packet)?; | |
let mut ack = [0u8]; | |
let mut okay = false; | |
for _ in 0..1000 { | |
let _ = device.read_exact(&mut ack); | |
match ack[0] { | |
ACK => { | |
okay = true; | |
break; | |
} | |
NAK => bail!("Got NAK"), | |
_ => continue, //bail!("Got unknown ack {}", i), | |
} | |
} | |
if !okay { | |
bail!("Missed NAK"); | |
} | |
} | |
device.write_all(&[EOT])?; | |
println!("\nDONE"); | |
Ok(()) | |
} | |
/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ | |
const 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 update_crc(cp: u8, crc: u16) -> u16 { | |
CRCTAB[((crc >> 8) & 255) as usize] ^ (crc << 8) ^ (cp as u16) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment