proc: implement basic line discipline

This commit is contained in:
Mark Poliakov 2023-07-20 16:19:23 +03:00
parent d3e3d3add8
commit 98c4e9152e

View File

@ -1,5 +1,8 @@
//! Terminal driver implementation //! Terminal driver implementation
use abi::error::Error; use abi::{
error::Error,
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
};
use crate::{proc::wait::Wait, sync::IrqSafeSpinlock}; use crate::{proc::wait::Wait, sync::IrqSafeSpinlock};
@ -18,6 +21,7 @@ pub struct CharRing<const N: usize> {
wait_read: Wait, wait_read: Wait,
wait_write: Wait, wait_write: Wait,
inner: IrqSafeSpinlock<CharRingInner<N>>, inner: IrqSafeSpinlock<CharRingInner<N>>,
config: IrqSafeSpinlock<TerminalOptions>,
} }
/// Terminal device interface /// Terminal device interface
@ -37,26 +41,88 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
/// Sends a single byte to the terminal /// Sends a single byte to the terminal
fn line_send(&self, byte: u8) -> Result<(), Error> { fn line_send(&self, byte: u8) -> Result<(), Error> {
let config = self.ring().config.lock();
if byte == b'\n' && config.output.contains(TerminalOutputOptions::NL_TO_CRNL) {
self.send(b'\r').ok();
}
self.send(byte) self.send(byte)
} }
/// Receives a single byte from the terminal /// Receives a single byte from the terminal
fn recv_byte(&self, byte: u8) { fn recv_byte(&self, mut byte: u8) {
let ring = self.ring(); let ring = self.ring();
let config = ring.config.lock();
if byte == b'\r' && config.input.contains(TerminalInputOptions::CR_TO_NL) {
byte = b'\n';
}
if byte == b'\n' {
let echo = config.line.contains(TerminalLineOptions::ECHO)
|| config
.line
.contains(TerminalLineOptions::CANONICAL | TerminalLineOptions::ECHO_NL);
if config.output.contains(TerminalOutputOptions::NL_TO_CRNL) {
self.send(b'\r').ok();
}
self.send(byte).ok();
} else if config.line.contains(TerminalLineOptions::ECHO) {
// TODO proper control
if byte.is_ascii_control() {
self.send(b'^').ok();
self.send(byte + 0x40).ok();
} else {
self.send(byte).ok();
}
}
// TODO handle signals
ring.putc(byte, false).ok(); ring.putc(byte, false).ok();
} }
/// Reads and processes data from the terminal /// Reads and processes data from the terminal
fn line_read(&'static self, data: &mut [u8]) -> Result<usize, Error> { fn line_read(&'static self, data: &mut [u8]) -> Result<usize, Error> {
let ring = self.ring(); let ring = self.ring();
let mut config = ring.config.lock();
if data.is_empty() { if data.is_empty() {
return Ok(0); return Ok(0);
} }
let byte = ring.getc()?; if !config.is_canonical() {
data[0] = byte; let byte = ring.getc()?;
Ok(1) data[0] = byte;
Ok(1)
} else {
let mut rem = data.len();
let mut off = 0;
// Run until either end of buffer or return condition is reached
while rem != 0 {
drop(config);
let byte = ring.getc()?;
config = ring.config.lock();
if byte == config.chars.eof && config.is_canonical() {
break;
}
// TODO handle special characters
data[off] = byte;
off += 1;
rem -= 1;
if byte == b'\n' || byte == b'\r' {
break;
}
}
Ok(off)
}
} }
/// Processes and writes the data to the terminal /// Processes and writes the data to the terminal
@ -109,13 +175,42 @@ impl<const N: usize> CharRing<N> {
}), }),
wait_read: Wait::new("char_ring_read"), wait_read: Wait::new("char_ring_read"),
wait_write: Wait::new("char_ring_write"), wait_write: Wait::new("char_ring_write"),
config: IrqSafeSpinlock::new(TerminalOptions::const_default()),
} }
} }
/// Returns `true` if the buffer has data to read /// Returns `true` if the buffer has data to read
pub fn is_readable(&self) -> bool { pub fn is_readable(&self) -> bool {
let inner = self.inner.lock(); let inner = self.inner.lock();
inner.is_readable() || inner.flags != 0 let config = self.config.lock();
if config.is_canonical() {
let mut rd = inner.rd;
let mut count = 0usize;
loop {
let readable = if rd <= inner.wr {
(inner.wr - rd) > 0
} else {
(inner.wr + (N - rd)) > 0
};
if !readable {
break;
}
let byte = inner.data[rd];
if byte == b'\n' {
count += 1;
}
rd = (rd + 1) % N;
}
count != 0 || inner.flags != 0
} else {
inner.is_readable() || inner.flags != 0
}
} }
/// Reads a single character from the buffer, blocking until available /// Reads a single character from the buffer, blocking until available