300 lines
8.4 KiB
Rust
Raw Normal View History

2023-07-18 18:03:45 +03:00
//! Terminal driver implementation
2023-12-22 11:24:47 +02:00
use core::{
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};
2023-07-20 16:19:23 +03:00
use abi::{
error::Error,
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
process::{ProcessGroupId, Signal},
2023-07-20 16:19:23 +03:00
};
use device_api::serial::SerialDevice;
2023-12-22 11:24:47 +02:00
use futures_util::Future;
use libk::task::process::ProcessImpl;
2024-02-05 12:35:09 +02:00
use libk_util::{ring::RingBuffer, sync::IrqSafeSpinlock, waker::QueueWaker};
2023-07-18 18:03:45 +03:00
2024-03-12 14:46:54 +02:00
use crate::{proc::io::ProcessIoImpl, task::process::ProcessManagerImpl};
2023-07-18 18:03:45 +03:00
struct TerminalRing {
buffer: IrqSafeSpinlock<RingBuffer<u8>>,
2023-12-22 11:24:47 +02:00
eof: AtomicBool,
notify: QueueWaker,
}
impl TerminalRing {
const CAPACITY: usize = 128;
const fn new() -> Self {
2023-12-22 11:24:47 +02:00
Self {
buffer: IrqSafeSpinlock::new(RingBuffer::with_capacity(TerminalRing::CAPACITY)),
2023-12-22 11:24:47 +02:00
eof: AtomicBool::new(false),
notify: QueueWaker::new(),
}
}
fn write(&self, ch: u8, signal: bool) {
2023-12-22 11:24:47 +02:00
self.buffer.lock().write(ch);
if signal {
self.notify.wake_one();
}
}
fn signal(&self) {
2023-12-22 11:24:47 +02:00
self.notify.wake_all();
}
2023-12-26 22:24:38 +02:00
fn read_blocking(&self) -> impl Future<Output = Option<u8>> + '_ {
2023-12-22 11:24:47 +02:00
struct F<'f> {
ring: &'f TerminalRing,
}
impl<'f> Future for F<'f> {
type Output = Option<u8>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.ring.notify.register(cx.waker());
if self
.ring
.eof
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
self.ring.notify.remove(cx.waker());
return Poll::Ready(None);
}
let mut lock = self.ring.buffer.lock();
if lock.is_readable() {
self.ring.notify.remove(cx.waker());
2023-12-28 10:37:35 +02:00
Poll::Ready(Some(unsafe { lock.read_single_unchecked() }))
2023-12-22 11:24:47 +02:00
} else {
Poll::Pending
}
}
}
F { ring: self }
}
}
2023-09-05 16:17:48 +03:00
struct TtyContextInner {
config: TerminalOptions,
process_group: Option<ProcessGroupId>,
2023-07-18 18:03:45 +03:00
}
2023-12-07 12:50:54 +02:00
/// Represents the context of a terminal device
2023-09-05 16:17:48 +03:00
pub struct TtyContext {
2023-12-22 11:24:47 +02:00
ring: TerminalRing, // AsyncRing<u8, 128>,
2023-09-05 16:17:48 +03:00
inner: IrqSafeSpinlock<TtyContextInner>,
2023-07-18 18:03:45 +03:00
}
2024-01-30 01:32:44 +02:00
// TODO merge this code with PTY
2023-07-18 18:03:45 +03:00
/// Terminal device interface
2023-09-05 16:17:48 +03:00
pub trait TtyDevice: SerialDevice {
2023-07-18 18:03:45 +03:00
/// Returns the ring buffer associated with the device
2023-09-05 16:17:48 +03:00
fn context(&self) -> &TtyContext;
2023-07-18 18:03:45 +03:00
2023-07-27 16:24:52 +03:00
/// Sets the process group to which signals from this terminal should be delivered
fn set_signal_group(&self, id: ProcessGroupId) {
2023-09-05 16:17:48 +03:00
self.context().inner.lock().process_group.replace(id);
2023-07-27 16:24:52 +03:00
}
2023-07-18 18:03:45 +03:00
/// Sends a single byte to the terminal
fn line_send(&self, byte: u8) -> Result<(), Error> {
2023-09-05 16:17:48 +03:00
let cx = self.context();
let inner = cx.inner.lock();
if byte == b'\n'
&& inner
.config
.output
.contains(TerminalOutputOptions::NL_TO_CRNL)
{
2023-07-20 16:19:23 +03:00
self.send(b'\r').ok();
}
2023-09-05 16:17:48 +03:00
drop(inner);
2023-07-18 18:03:45 +03:00
self.send(byte)
}
/// Receives a single byte from the terminal
2023-07-20 16:19:23 +03:00
fn recv_byte(&self, mut byte: u8) {
2023-09-05 16:17:48 +03:00
let cx = self.context();
let inner = cx.inner.lock();
2023-07-20 16:19:23 +03:00
2023-09-05 16:17:48 +03:00
if byte == b'\r' && inner.config.input.contains(TerminalInputOptions::CR_TO_NL) {
2023-07-20 16:19:23 +03:00
byte = b'\n';
}
if byte == b'\n' {
2023-07-20 18:51:56 +03:00
// TODO implement proper echo here
2023-09-05 16:17:48 +03:00
let _echo = inner.config.line.contains(TerminalLineOptions::ECHO)
|| inner
.config
2023-07-20 16:19:23 +03:00
.line
.contains(TerminalLineOptions::CANONICAL | TerminalLineOptions::ECHO_NL);
2023-07-20 18:51:56 +03:00
2023-09-05 16:17:48 +03:00
if inner
.config
.output
.contains(TerminalOutputOptions::NL_TO_CRNL)
{
2023-07-20 16:19:23 +03:00
self.send(b'\r').ok();
}
self.send(byte).ok();
2023-09-05 16:17:48 +03:00
} else if inner.config.line.contains(TerminalLineOptions::ECHO) {
2023-07-20 16:19:23 +03:00
if byte.is_ascii_control() {
2023-09-05 16:17:48 +03:00
if byte != inner.config.chars.erase && byte != inner.config.chars.werase {
2023-08-06 17:06:34 +03:00
self.send(b'^').ok();
self.send(byte + 0x40).ok();
}
2023-07-20 16:19:23 +03:00
} else {
self.send(byte).ok();
}
}
// byte == config.chars.interrupt
2023-09-05 16:17:48 +03:00
if byte == inner.config.chars.interrupt
&& inner.config.line.contains(TerminalLineOptions::SIGNAL)
{
let pgrp = inner.process_group;
2023-12-22 11:24:47 +02:00
cx.signal();
2023-07-27 16:24:52 +03:00
if let Some(pgrp) = pgrp {
2023-09-05 16:17:48 +03:00
drop(inner);
ProcessImpl::<ProcessManagerImpl, ProcessIoImpl>::signal_group(
pgrp,
Signal::Interrupted,
);
2023-07-27 16:24:52 +03:00
return;
} else {
debugln!("Terminal has no process group attached");
}
}
2023-07-20 16:19:23 +03:00
2023-12-22 11:24:47 +02:00
let canonical = inner.config.line.contains(TerminalLineOptions::CANONICAL);
2023-09-05 16:17:48 +03:00
drop(inner);
2023-12-22 11:24:47 +02:00
cx.putc(byte, !canonical || byte == b'\n' || byte == b'\r');
2023-07-18 18:03:45 +03:00
}
/// Reads and processes data from the terminal
2023-09-05 16:17:48 +03:00
async fn line_read(&self, data: &mut [u8]) -> Result<usize, Error> {
let cx = self.context();
let mut inner = cx.inner.lock();
2023-07-18 18:03:45 +03:00
if data.is_empty() {
return Ok(0);
}
2023-09-05 16:17:48 +03:00
if !inner.config.is_canonical() {
drop(inner);
2023-12-22 11:24:47 +02:00
let Some(byte) = cx.getc().await else {
return Ok(0);
};
2023-07-20 16:19:23 +03:00
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 {
2023-09-05 16:17:48 +03:00
drop(inner);
2023-12-22 11:24:47 +02:00
let Some(byte) = cx.getc().await else {
break;
};
2023-09-05 16:17:48 +03:00
inner = cx.inner.lock();
2023-07-20 16:19:23 +03:00
2023-09-05 16:17:48 +03:00
if inner.config.is_canonical() {
if byte == inner.config.chars.eof {
2023-08-06 17:06:34 +03:00
break;
2023-09-05 16:17:48 +03:00
} else if byte == inner.config.chars.erase {
2023-08-06 17:06:34 +03:00
// Erase
if off != 0 {
self.raw_write(b"\x1b[D \x1b[D")?;
2023-08-06 17:06:34 +03:00
off -= 1;
rem += 1;
}
continue;
2023-09-05 16:17:48 +03:00
} else if byte == inner.config.chars.werase {
2023-08-06 17:06:34 +03:00
todo!()
}
2023-07-20 16:19:23 +03:00
}
data[off] = byte;
off += 1;
rem -= 1;
if byte == b'\n' || byte == b'\r' {
break;
}
}
Ok(off)
}
2023-07-18 18:03:45 +03:00
}
/// Processes and writes the data to the terminal
fn line_write(&self, data: &[u8]) -> Result<usize, Error> {
for &byte in data {
self.line_send(byte)?;
}
Ok(data.len())
}
/// Writes raw data to the terminal bypassing the processing functions
2023-08-06 17:06:34 +03:00
fn raw_write(&self, data: &[u8]) -> Result<usize, Error> {
for &byte in data {
self.send(byte)?;
}
Ok(data.len())
2023-07-18 18:03:45 +03:00
}
}
2023-09-05 16:17:48 +03:00
impl TtyContext {
2023-12-07 12:50:54 +02:00
/// Constructs a new [TtyContext]
2023-09-05 16:17:48 +03:00
pub fn new() -> Self {
2023-07-18 18:03:45 +03:00
Self {
2023-12-22 11:24:47 +02:00
ring: TerminalRing::new(), // AsyncRing::new(0),
2023-09-05 16:17:48 +03:00
inner: IrqSafeSpinlock::new(TtyContextInner {
config: TerminalOptions::const_default(),
2023-07-27 16:24:52 +03:00
process_group: None,
2023-07-18 18:03:45 +03:00
}),
}
}
/// Signals an event on the terminal
2023-12-22 11:24:47 +02:00
pub fn signal(&self) {
self.ring.signal()
}
2023-12-07 12:50:54 +02:00
/// Writes a single character to the terminal
2023-12-22 11:24:47 +02:00
pub fn putc(&self, ch: u8, signal: bool) {
self.ring.write(ch, signal);
2023-07-18 18:03:45 +03:00
}
2023-12-07 12:50:54 +02:00
/// Performs a blocking read of a single character from the terminal
2023-12-22 11:24:47 +02:00
pub async fn getc(&self) -> Option<u8> {
self.ring.read_blocking().await
2023-07-18 18:03:45 +03:00
}
2023-12-07 12:50:54 +02:00
/// Changes the configuration of the terminal
pub fn set_config(&self, config: &TerminalOptions) -> Result<(), Error> {
2023-12-05 14:55:12 +02:00
self.inner.lock().config = *config;
Ok(())
}
2023-12-07 12:50:54 +02:00
/// Returns the configuration of the terminal
pub fn config(&self) -> TerminalOptions {
self.inner.lock().config
}
2023-07-18 18:03:45 +03:00
}