diff --git a/kernel/src/dev/tty.rs b/kernel/src/dev/tty.rs index 3224a70..36b2a56 100644 --- a/kernel/src/dev/tty.rs +++ b/kernel/src/dev/tty.rs @@ -1,8 +1,9 @@ //! Teletype (TTY) device facilities -use crate::proc::wait::Wait; use crate::dev::serial::SerialDevice; +use crate::proc::wait::Wait; use crate::sync::IrqSafeSpinLock; use error::Errno; +use syscall::termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag}; use vfs::CharDevice; #[derive(Debug)] @@ -17,6 +18,7 @@ struct CharRingInner { pub struct CharRing { wait_read: Wait, wait_write: Wait, + config: IrqSafeSpinLock, inner: IrqSafeSpinLock>, } @@ -24,7 +26,9 @@ pub trait TtyDevice: SerialDevice { fn ring(&self) -> &CharRing; fn line_send(&self, byte: u8) -> Result<(), Errno> { - if byte == b'\n' { + let config = self.ring().config.lock(); + + if byte == b'\n' && config.oflag.contains(TermiosOflag::ONLCR) { self.send(b'\r').ok(); } @@ -32,68 +36,98 @@ pub trait TtyDevice: SerialDevice { } fn recv_byte(&self, mut byte: u8) { - if byte == b'\r' { - // TODO ICRNL conv option + let ring = self.ring(); + let config = ring.config.lock(); + + if byte == b'\r' && config.iflag.contains(TermiosIflag::ICRNL) { byte = b'\n'; } - if byte == 4 { - // TODO handle EOF - self.ring().signal_eof(); - return; + if byte == b'\n' { + if config.lflag.contains(TermiosLflag::ECHO) + || (config.is_canon() && config.lflag.contains(TermiosLflag::ECHONL)) + { + if byte == b'\n' && config.oflag.contains(TermiosOflag::ONLCR) { + self.send(b'\r').ok(); + } + self.send(byte).ok(); + } + } else if config.lflag.contains(TermiosLflag::ECHO) { + let echoe = (byte == config.chars.erase || byte == config.chars.werase) + && config.lflag.contains(TermiosLflag::ECHOE); + let echok = byte == config.chars.kill && config.lflag.contains(TermiosLflag::ECHOE); + + if byte.is_ascii_control() { + if !echoe && !echok { + self.send(b'^').ok(); + self.send(byte + 0x40).ok(); + } + } else { + self.send(byte).ok(); + } } self.ring().putc(byte, false).ok(); - - match byte { - b'\n' => { - // TODO ECHONL option - self.line_send(byte).ok(); - // TODO ICANON - } - 0x17 | 0x7F => (), - 0x0C => { - // TODO ECHO option && ICANON option - self.raw_write(b"^L").ok(); - }, - 0x1B => { - // TODO ECHO option && ICANON option - self.raw_write(b"^[").ok(); - }, - _ => { - // TODO ECHO option - self.line_send(byte).ok(); - } - } } /// Line discipline function fn line_read(&self, data: &mut [u8]) -> Result { let ring = self.ring(); + let mut config = ring.config.lock(); let mut rem = data.len(); let mut off = 0; + if !config.is_canon() { + todo!() + } + while rem != 0 { + drop(config); let byte = match ring.getc() { Ok(ch) => ch, Err(Errno::Interrupt) => { todo!() } - Err(Errno::EndOfFile) => { - break; - } Err(e) => return Err(e), }; + config = ring.config.lock(); - if byte == 0x7F { - if off > 0 { + if byte == config.chars.eof && config.is_canon() { + break; + } + if byte == config.chars.erase && config.is_canon() { + if off > 0 && config.lflag.contains(TermiosLflag::ECHOE) { self.raw_write(b"\x1B[D \x1B[D").ok(); off -= 1; rem += 1; } + + continue; + } + if byte == config.chars.werase && config.is_canon() { + if off > 0 && config.lflag.contains(TermiosLflag::ECHOE) { + let idx = data[..off].iter().rposition(|&ch| ch == b' ').unwrap_or(0); + let len = off; + + for i in idx..len { + self.raw_write(b"\x1B[D \x1B[D").ok(); + off -= 1; + rem += 1; + } + } + + continue; + } + if byte == config.chars.kill && config.is_canon() { + if off > 0 && config.lflag.contains(TermiosLflag::ECHOK) { + while off != 0 { + self.raw_write(b"\x1B[D \x1B[D").ok(); + off -= 1; + rem += 1; + } + } + continue; - } else if byte >= b' ' { - // TODO tty options } data[off] = byte; @@ -156,6 +190,7 @@ impl CharRing { data: [0; N], flags: 0, }), + config: IrqSafeSpinLock::new(Termios::new()), wait_read: Wait::new(), wait_write: Wait::new(), } @@ -195,9 +230,4 @@ impl CharRing { self.wait_read.wakeup_one(); Ok(()) } - - fn signal_eof(&self) { - self.inner.lock().flags |= 1 << 0; - self.wait_read.wakeup_one(); - } } diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 9228f8f..9275409 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -6,6 +6,7 @@ extern crate bitflags; pub mod abi; pub mod stat; +pub mod termios; #[cfg(feature = "user")] pub mod calls; diff --git a/syscall/src/termios.rs b/syscall/src/termios.rs new file mode 100644 index 0000000..19f087c --- /dev/null +++ b/syscall/src/termios.rs @@ -0,0 +1,80 @@ +bitflags! { + pub struct TermiosIflag: u32 { + /// Translate NL to CR on input + const INLCR = 1 << 0; + /// Translate CR to NL on input + const ICRNL = 1 << 1; + } + + pub struct TermiosOflag: u32 { + /// Translate NL to CR-NL on output + const ONLCR = 1 << 0; + } + + pub struct TermiosLflag: u32 { + /// Signal processing (INTR, QUIT, SUSP) + const ISIG = 1 << 0; + /// Canonical mode + const ICANON = 1 << 1; + /// Echo input characters + const ECHO = 1 << 2; + /// If ICANON also set, ERASE erases chars, WERASE erases words + const ECHOE = 1 << 3; + /// If ICANON also set, KILL erases line + const ECHOK = 1 << 4; + /// If ICANON also set, echo NL even if ECHO is not set + const ECHONL = 1 << 5; + } +} + +#[derive(Debug, Clone)] +pub struct TermiosChars { + pub eof: u8, + pub erase: u8, + pub intr: u8, + pub kill: u8, + pub vlnext: u8, + pub werase: u8, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Termios { + pub iflag: TermiosIflag, + pub oflag: TermiosOflag, + pub lflag: TermiosLflag, + pub chars: TermiosChars +} + +impl TermiosChars { + pub const fn new() -> Self { + Self { + eof: 0x04, + erase: 0x7F, + intr: 0x03, + kill: 0x15, + vlnext: 0x16, + werase: 0x17, + } + } +} + +impl Termios { + pub const fn new() -> Self { + Self { + iflag: TermiosIflag::ICRNL, + oflag: TermiosOflag::ONLCR, + // TODO prettify this + lflag: unsafe { + TermiosLflag::from_bits_unchecked( + (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5), + ) + }, + chars: TermiosChars::new() + } + } + + pub const fn is_canon(&self) -> bool { + self.lflag.contains(TermiosLflag::ICANON) + } +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index 87c444b..b327102 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -19,6 +19,10 @@ fn main() -> i32 { loop { print!("> "); let line = readline(libusr::sys::STDIN_FILENO, &mut buf).unwrap(); + if line.is_empty() { + break; + } + let line = line.trim_end_matches('\n'); println!(":: {:?}", line);