1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use std::io::{Read, Write};
use std::sync::Arc;

use serial2::SerialPort;

fn do_main() -> Result<(), ()> {
	let args: Vec<_> = std::env::args().collect();
	if args.len() != 3 {
		let prog_name = args[0].rsplit_once('/').map(|(_parent, name)| name).unwrap_or(&args[0]);
		eprintln!("Usage: {} PORT BAUD", prog_name);
		return Err(());
	}

	let port_name = &args[1];
	let baud_rate: u32 = args[2]
		.parse()
		.map_err(|_| eprintln!("Error: invalid baud rate: {}", args[2]))?;

	let port = SerialPort::open(port_name, baud_rate)
		.map_err(|e| eprintln!("Error: Failed to open {}: {}", port_name, e))?;
	let port = Arc::new(port);

	// Spawn a thread to read from stdin and write to the serial port.
	std::thread::spawn({
		let port = port.clone();
		let port_name = port_name.to_owned();
		move || {
			if let Err(()) = read_stdin_loop(port, &port_name) {
				std::process::exit(1);
			}
		}
	});

	// Read from serial port and write to stdout in main thread.
	read_serial_loop(port, port_name)?;

	Ok(())
}

fn read_stdin_loop(port: Arc<SerialPort>, port_name: &str) -> Result<(), ()> {
	let stdin = std::io::stdin();
	let mut stdin = stdin.lock();
	let mut buffer = [0; 512];
	loop {
		let read = stdin
			.read(&mut buffer)
			.map_err(|e| eprintln!("Error: Failed to read from stdin: {}", e))?;
		if read == 0 {
			return Ok(());
		} else {
			port.write(&buffer[..read])
				.map_err(|e| eprintln!("Error: Failed to write to {}: {}", port_name, e))?;
		}
	}
}

fn read_serial_loop(port: Arc<SerialPort>, port_name: &str) -> Result<(), ()> {
	let mut buffer = [0; 512];
	loop {
		match port.read(&mut buffer) {
			Ok(0) => return Ok(()),
			Ok(n) => {
				std::io::stdout()
					.write_all(&buffer[..n])
					.map_err(|e| eprintln!("Error: Failed to write to stdout: {}", e))?;
			},
			Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => continue,
			Err(e) => {
				eprintln!("Error: Failed to read from {}: {}", port_name, e);
				return Err(());
			},
		}
	}
}

fn main() {
	if let Err(()) = do_main() {
		std::process::exit(1);
	}
}