From 6a91e6cd0b645269e66d12b11fbc588f62ae406a Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 10 Oct 2022 13:01:02 +0200 Subject: [PATCH 1/3] Upgrading serial support --- CHANGELOG.md | 2 + src/serial_terminal.rs | 93 +++++++++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4737efe..731964e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Settings are backwards compatible with previous Booster releases. * Fan speed is now stored in EEPROM and configurable via the serial interface. * Support for different ethernet daughterboards using the ENC424J00 has been added. +* Serial port now supports TeraTerm + Putty +* Serial port now supports backspacing ### Changed * Removed custom `mutex` implementation in favor of leveraging `shared-bus` diff --git a/src/serial_terminal.rs b/src/serial_terminal.rs index bb899b31..7a4d2199 100644 --- a/src/serial_terminal.rs +++ b/src/serial_terminal.rs @@ -277,13 +277,35 @@ impl SerialTerminal { // will likely be cleared up as data is processed. match self .output_buffer_producer - .grant_exact(data.len()) + .grant_exact(data.len() + data.iter().filter(|&x| *x == b'\n').count()) .or_else(|_| self.output_buffer_producer.grant_max_remaining(data.len())) { Ok(mut grant) => { - let len = grant.buf().len(); - grant.buf().copy_from_slice(&data[..len]); - grant.commit(len); + let buf = grant.buf(); + let mut buf_iter = buf.iter_mut(); + + // Copy the data into the outbound buffer. For each Newline encounteded, append a + // carriage return to support formating in raw terminals such as Putty. + for val in data.iter() { + if let Some(pos) = buf_iter.next() { + *pos = *val; + } + + if *val == b'\n' { + if let Some(pos) = buf_iter.next() { + *pos = b'\r'; + } + } + } + + let length = { + // Count returns the remaining number of positions, which is nominally zero. + // The length of the amount of data we pushed into the iterator is thus the + // original buffer length minus the remaining items. + let remainder = buf_iter.count(); + buf.len() - remainder + }; + grant.commit(length); } Err(_) => return, }; @@ -305,29 +327,48 @@ impl SerialTerminal { let mut buffer = [0u8; 64]; match self.usb_serial.read(&mut buffer) { Ok(count) => { - // Echo the data back. - self.write(&buffer[..count]); - - match self.input_buffer.extend_from_slice(&buffer[..count]) { - Ok(_) => match self.parse_buffer() { - // If there's nothing requested yet, keep waiting for more data. - Ok(None) => None, - - // If we got a request, clear the buffer and process it. - Ok(Some(result)) => Some(result), - - // Otherwise, if there was an error, clear the buffer and print it. - Err(msg) => { - self.write(msg.as_bytes()); - self.print_help(); - self.reset(); - None + for value in &buffer[..count] { + // Interpret the DEL and BS characters as a request to delete the most recently + // provided value. This supports Putty and TeraTerm defaults. + if *value == b'\x08' || *value == b'\x7F' { + // If there was previously data in the buffer, go back one, print an empty + // space, and then go back again to clear out the character on the user's + // screen. + if self.input_buffer.pop().is_some() { + // Note: we use this sequence because not all terminal emulators + // support the DEL character. + self.write(&[b'\x08', b' ', b'\x08']); } - }, - Err(_) => { + continue; + } + + // If we're echoing a CR, send a linefeed as well. + if *value == b'\r' { + self.write(&[b'\n', b'\r']); + } else { + self.write(&[*value]); + } + + if self.input_buffer.push(*value).is_err() { // Make a best effort to inform the user of the overflow. self.write("[!] Buffer overflow\n".as_bytes()); self.reset(); + return None; + } + } + + match self.parse_buffer() { + // If there's nothing requested yet, keep waiting for more data. + Ok(None) => None, + + // If we got a request, clear the buffer and process it. + Ok(Some(result)) => Some(result), + + // Otherwise, if there was an error, clear the buffer and print it. + Err(msg) => { + self.write(msg.as_bytes()); + self.print_help(); + self.reset(); None } } @@ -378,7 +419,11 @@ impl SerialTerminal { fn parse_buffer(&self) -> Result, &'static str> { // If there's a line available in the buffer, parse it as a command. let mut lex = { - if let Some(pos) = self.input_buffer.iter().position(|&c| c == b'\n') { + if let Some(pos) = self + .input_buffer + .iter() + .position(|&c| c == b'\n' || c == b'\r') + { // Attempt to convert the slice to a string. let line = core::str::from_utf8(&self.input_buffer[..pos]).map_err(|_| "Invalid bytes")?; From 0c622c01dc51efce707a62f4d770e405c8e0c7e7 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 10 Oct 2022 13:09:46 +0200 Subject: [PATCH 2/3] Simplifying result handling --- src/serial_terminal.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/serial_terminal.rs b/src/serial_terminal.rs index 7a4d2199..41d83401 100644 --- a/src/serial_terminal.rs +++ b/src/serial_terminal.rs @@ -357,21 +357,12 @@ impl SerialTerminal { } } - match self.parse_buffer() { - // If there's nothing requested yet, keep waiting for more data. - Ok(None) => None, - - // If we got a request, clear the buffer and process it. - Ok(Some(result)) => Some(result), - - // Otherwise, if there was an error, clear the buffer and print it. - Err(msg) => { - self.write(msg.as_bytes()); - self.print_help(); - self.reset(); - None - } - } + self.parse_buffer().unwrap_or_else(|msg| { + self.write(msg.as_bytes()); + self.print_help(); + self.reset(); + None + }) } // If there's no data available, don't process anything. Err(UsbError::WouldBlock) => None, From 20b62deb4129ec7515dcf89bcc6ae97172bfde31 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 12 Oct 2022 13:19:54 +0200 Subject: [PATCH 3/3] Addressing PR review --- src/serial_terminal.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serial_terminal.rs b/src/serial_terminal.rs index 41d83401..484bbc42 100644 --- a/src/serial_terminal.rs +++ b/src/serial_terminal.rs @@ -327,29 +327,29 @@ impl SerialTerminal { let mut buffer = [0u8; 64]; match self.usb_serial.read(&mut buffer) { Ok(count) => { - for value in &buffer[..count] { + for &value in &buffer[..count] { // Interpret the DEL and BS characters as a request to delete the most recently // provided value. This supports Putty and TeraTerm defaults. - if *value == b'\x08' || *value == b'\x7F' { + if value == b'\x08' || value == b'\x7F' { // If there was previously data in the buffer, go back one, print an empty // space, and then go back again to clear out the character on the user's // screen. if self.input_buffer.pop().is_some() { // Note: we use this sequence because not all terminal emulators // support the DEL character. - self.write(&[b'\x08', b' ', b'\x08']); + self.write(b"\x08 \x08"); } continue; } // If we're echoing a CR, send a linefeed as well. - if *value == b'\r' { - self.write(&[b'\n', b'\r']); - } else { - self.write(&[*value]); + if value == b'\r' { + self.write(b"\n"); } - if self.input_buffer.push(*value).is_err() { + self.write(&[value]); + + if self.input_buffer.push(value).is_err() { // Make a best effort to inform the user of the overflow. self.write("[!] Buffer overflow\n".as_bytes()); self.reset();