Skip to content

Commit

Permalink
Merge pull request #236 from quartiq/feature/serial-cleanup
Browse files Browse the repository at this point in the history
Upgrading serial support for Putty and TeraTerm, Add backspace support
  • Loading branch information
ryan-summers authored Oct 12, 2022
2 parents 664d1fb + 20b62de commit 5be1fa5
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
* 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`
Expand Down
86 changes: 61 additions & 25 deletions src/serial_terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -305,32 +327,42 @@ 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 \x08");
}
},
Err(_) => {
continue;
}

// If we're echoing a CR, send a linefeed as well.
if value == b'\r' {
self.write(b"\n");
}

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();
None
return 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,
Expand Down Expand Up @@ -378,7 +410,11 @@ impl SerialTerminal {
fn parse_buffer(&self) -> Result<Option<Request>, &'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")?;
Expand Down

0 comments on commit 5be1fa5

Please sign in to comment.