diff --git a/Cargo.lock b/Cargo.lock index 9a52f21..ba9af37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,12 +65,29 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "numtoa" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" + [[package]] name = "pbr" version = "1.1.1" @@ -112,6 +129,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" + [[package]] name = "serial2" version = "0.2.32" @@ -134,6 +166,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termion" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb" +dependencies = [ + "libc", + "libredox", + "numtoa", + "redox_termios", +] + [[package]] name = "tulflash" version = "0.1.0" @@ -141,6 +185,7 @@ dependencies = [ "argp", "pbr", "serial2", + "termion", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1c5e732..17ca17a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" argp = "0.4.0" pbr = "1.1.1" serial2 = "0.2.32" +termion = "4.0.5" diff --git a/src/args.rs b/src/args.rs index 8db4424..17dc2e3 100644 --- a/src/args.rs +++ b/src/args.rs @@ -22,7 +22,8 @@ pub struct Args { pub enum ArgCommand { Read(ArgRead), Write(ArgWrite), - Erase(ArgErase) + Erase(ArgErase), + Monitor(ArgMonitor) } #[derive(FromArgs)] @@ -80,6 +81,11 @@ pub struct ArgWrite { #[argp(subcommand, name = "erase")] pub struct ArgErase {} +#[derive(FromArgs)] +#[argp(description = "Opens the serial monitor.")] +#[argp(subcommand, name = "monitor")] +pub struct ArgMonitor {} + pub fn parse() -> Args { let args: Args = argp::parse_args_or_exit(argp::DEFAULT); args diff --git a/src/main.rs b/src/main.rs index c288df6..d62d9e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,9 @@ use args::*; mod ihex; use ihex::{IntelHexWriter, IntelHexReader, DataRecord}; +mod monitor; +use monitor::Monitor; + mod target; use target::{PhysicalTarget, Target}; @@ -201,6 +204,13 @@ fn erase_chip(_args: ArgErase, target: &mut PhysicalTarget) -> Result<(), Error> Ok(()) } +fn monitor_chip(_args: ArgMonitor, target: &mut PhysicalTarget) -> Result<(), Error> { + let _ = Monitor::new().run(target.port())?; + target.resync_target().unwrap(); + target.read_info(0x00, 0x00).unwrap(); + Ok(()) +} + fn main() { let args = args::parse(); @@ -211,7 +221,8 @@ fn main() { let res = match args.command { ArgCommand::Read(args) => read_chip(args, &mut target), ArgCommand::Write(args) => write_chip(args, &mut target), - ArgCommand::Erase(args) => erase_chip(args, &mut target) + ArgCommand::Erase(args) => erase_chip(args, &mut target), + ArgCommand::Monitor(args) => monitor_chip(args, &mut target) }; if let Err(err) = res { diff --git a/src/monitor.rs b/src/monitor.rs new file mode 100644 index 0000000..3699774 --- /dev/null +++ b/src/monitor.rs @@ -0,0 +1,76 @@ +use std::io::Error; +use std::time::Duration; + +use serial2::SerialPort; + +use termion::raw::IntoRawMode; +use termion::event::Key; +use termion::input::TermRead; + +pub struct Monitor { + hooks: Vec<(Key, &'static str)> +} + +impl Monitor { + pub fn new() -> Self { + let mut hooks = Vec::new(); + + hooks.push((Key::Ctrl('c'), "Exit monitor")); + hooks.push((Key::Ctrl('r'), "Reboot target")); + + Self { hooks } + } + + pub fn add_hook(&mut self, key: Key, desc: &'static str) { + self.hooks.push((key, desc)); + } + + pub fn run(&mut self, port: &mut SerialPort) -> Result { + port.set_dtr(true)?; + port.set_rts(true)?; + std::thread::sleep(Duration::from_millis(100)); + port.set_dtr(false)?; + port.set_rts(false)?; + + println!("Monitor controls:"); + + for (key, desc) in &self.hooks { + let key = match key { + Key::Char(c) => format!("{}", c.to_uppercase()), + Key::Ctrl(c) => format!("Ctrl+{}", c.to_uppercase()), + key => format!("{:?}", key) + }; + + println!(" {} - {}", key, desc); + } + + println!(); + + let mut _stdout = std::io::stdout().into_raw_mode().unwrap(); + + let mut stdin = termion::async_stdin().keys(); + + 'main: loop { + if let Some(Ok(key)) = stdin.next() { + if let Key::Ctrl('r') = key { + port.set_dtr(true)?; + port.set_rts(true)?; + std::thread::sleep(Duration::from_millis(10)); + port.set_dtr(false)?; + port.set_rts(false)?; + continue + } + + for (hook_key, _) in &self.hooks { + if hook_key == &key { + break 'main Ok(key) + } + } + println!("{:?}\r", key); + } else { + std::thread::sleep(Duration::from_millis(100)); + } + } + } +} + diff --git a/src/target.rs b/src/target.rs index 4289ef8..8263d44 100644 --- a/src/target.rs +++ b/src/target.rs @@ -22,24 +22,10 @@ impl PhysicalTarget { port.set_read_timeout(Duration::from_millis(100))?; - port.set_dtr(false)?; - std::thread::sleep(Duration::from_millis(100)); - - for i in (0..5).rev() { - port.write(b"U")?; - - let mut buf = [0u8; 1]; - port.read(&mut buf)?; - - if buf[0] == b'U' { break } - - if i == 0 { - Err(Error::other(format!("Invalid sync response: 0x{:02X}", buf[0])))? - } - } - let mut new = Self { port }; + new.resync_target()?; + let manufacturer = new.read_info(0x00, 0x00).unwrap(); let family = new.read_info(0x00, 0x01).unwrap(); let name = new.read_info(0x00, 0x02).unwrap(); @@ -50,6 +36,29 @@ impl PhysicalTarget { Ok(new) } + pub fn resync_target(&mut self) -> Result<(), Error> { + self.port.set_dtr(true)?; + self.port.set_rts(true)?; + std::thread::sleep(Duration::from_millis(10)); + self.port.set_dtr(false)?; + std::thread::sleep(Duration::from_millis(100)); + + for i in (0..5).rev() { + self.port.write(b"U")?; + + let mut buf = [0u8; 1]; + self.port.read(&mut buf)?; + + if buf[0] == b'U' { break } + + if i == 0 { + Err(Error::other(format!("Invalid sync response: 0x{:02X}", buf[0])))? + } + } + + Ok(()) + } + pub fn read_info(&mut self, category: u8, idx: u8) -> Result { self.send_command(0x05, 0, &[category, idx])?; @@ -126,6 +135,10 @@ impl PhysicalTarget { Ok(()) } + + pub fn port(&mut self) -> &mut SerialPort { + &mut self.port + } } impl Target for PhysicalTarget { @@ -228,7 +241,11 @@ impl Target for PhysicalTarget { impl Drop for PhysicalTarget { fn drop(&mut self) { - let _ = self.port.set_dtr(true); + self.port.set_dtr(true).unwrap(); + self.port.set_rts(true).unwrap(); + std::thread::sleep(Duration::from_millis(100)); + self.port.set_dtr(false).unwrap(); + self.port.set_rts(false).unwrap(); } }