proc: implement basic line discipline
This commit is contained in:
parent
d3e3d3add8
commit
98c4e9152e
@ -1,5 +1,8 @@
|
||||
//! Terminal driver implementation
|
||||
use abi::error::Error;
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
|
||||
use crate::{proc::wait::Wait, sync::IrqSafeSpinlock};
|
||||
|
||||
@ -18,6 +21,7 @@ pub struct CharRing<const N: usize> {
|
||||
wait_read: Wait,
|
||||
wait_write: Wait,
|
||||
inner: IrqSafeSpinlock<CharRingInner<N>>,
|
||||
config: IrqSafeSpinlock<TerminalOptions>,
|
||||
}
|
||||
|
||||
/// Terminal device interface
|
||||
@ -37,26 +41,88 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
|
||||
/// Sends a single byte to the terminal
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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 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();
|
||||
}
|
||||
|
||||
/// Reads and processes data from the terminal
|
||||
fn line_read(&'static self, data: &mut [u8]) -> Result<usize, Error> {
|
||||
let ring = self.ring();
|
||||
let mut config = ring.config.lock();
|
||||
|
||||
if data.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !config.is_canonical() {
|
||||
let byte = ring.getc()?;
|
||||
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
|
||||
@ -109,14 +175,43 @@ impl<const N: usize> CharRing<N> {
|
||||
}),
|
||||
wait_read: Wait::new("char_ring_read"),
|
||||
wait_write: Wait::new("char_ring_write"),
|
||||
config: IrqSafeSpinlock::new(TerminalOptions::const_default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer has data to read
|
||||
pub fn is_readable(&self) -> bool {
|
||||
let inner = self.inner.lock();
|
||||
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
|
||||
pub fn getc(&'static self) -> Result<u8, Error> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user