mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Redefine the TTY driver interface
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
5a9a63e1a7
commit
67065835ef
@ -24,7 +24,7 @@ pub struct FramebufferConsole {
|
||||
state: SpinLock<ConsoleState, LocalIrqDisabled>,
|
||||
}
|
||||
|
||||
pub static CONSOLE_NAME: &str = "Framebuffer-Console";
|
||||
pub const CONSOLE_NAME: &str = "Framebuffer-Console";
|
||||
|
||||
pub static FRAMEBUFFER_CONSOLE: Once<Arc<FramebufferConsole>> = Once::new();
|
||||
|
||||
|
@ -12,7 +12,7 @@ use ostd::Pod;
|
||||
|
||||
use crate::transport::{ConfigManager, VirtioTransport};
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Block";
|
||||
pub const DEVICE_NAME: &str = "Virtio-Block";
|
||||
|
||||
bitflags! {
|
||||
/// features for virtio block device
|
||||
|
@ -3,4 +3,4 @@
|
||||
pub mod config;
|
||||
pub mod device;
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Console";
|
||||
pub const DEVICE_NAME: &str = "Virtio-Console";
|
||||
|
@ -32,7 +32,7 @@ use ostd::{io::IoMem, Pod};
|
||||
|
||||
use crate::transport::VirtioTransport;
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Input";
|
||||
pub const DEVICE_NAME: &str = "Virtio-Input";
|
||||
|
||||
/// Select value used for [`device::InputDevice::query_config_select()`].
|
||||
#[repr(u8)]
|
||||
|
@ -4,4 +4,4 @@ pub mod config;
|
||||
pub mod device;
|
||||
pub mod header;
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Net";
|
||||
pub const DEVICE_NAME: &str = "Virtio-Net";
|
||||
|
@ -14,7 +14,8 @@ pub mod device;
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
|
||||
pub static DEVICE_NAME: &str = "Virtio-Vsock";
|
||||
pub const DEVICE_NAME: &str = "Virtio-Vsock";
|
||||
|
||||
pub trait VsockDeviceIrqHandler = Fn() + Send + Sync + 'static;
|
||||
|
||||
pub fn register_device(name: String, device: Arc<SpinLock<SocketDevice>>) {
|
||||
|
@ -11,11 +11,12 @@ mod zero;
|
||||
#[cfg(all(target_arch = "x86_64", feature = "cvm_guest"))]
|
||||
mod tdxguest;
|
||||
|
||||
use alloc::format;
|
||||
|
||||
pub use pty::{new_pty_pair, PtyMaster, PtySlave};
|
||||
pub use random::Random;
|
||||
pub use urandom::Urandom;
|
||||
|
||||
use self::tty::get_n_tty;
|
||||
use crate::{
|
||||
fs::device::{add_node, Device, DeviceId, DeviceType},
|
||||
prelude::*,
|
||||
@ -25,23 +26,37 @@ use crate::{
|
||||
pub fn init() -> Result<()> {
|
||||
let null = Arc::new(null::Null);
|
||||
add_node(null, "null")?;
|
||||
|
||||
let zero = Arc::new(zero::Zero);
|
||||
add_node(zero, "zero")?;
|
||||
|
||||
tty::init();
|
||||
let console = get_n_tty().clone();
|
||||
add_node(console, "console")?;
|
||||
|
||||
let tty = Arc::new(tty::TtyDevice);
|
||||
add_node(tty, "tty")?;
|
||||
|
||||
let console = tty::system_console().clone();
|
||||
add_node(console, "console")?;
|
||||
|
||||
for (index, tty) in tty::iter_n_tty().enumerate() {
|
||||
add_node(tty.clone(), &format!("tty{}", index))?;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ostd::if_tdx_enabled!({
|
||||
add_node(Arc::new(tdxguest::TdxGuest), "tdx_guest")?;
|
||||
});
|
||||
|
||||
let random = Arc::new(random::Random);
|
||||
add_node(random, "random")?;
|
||||
|
||||
let urandom = Arc::new(urandom::Urandom);
|
||||
add_node(urandom, "urandom")?;
|
||||
|
||||
pty::init()?;
|
||||
|
||||
shm::init()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
98
kernel/src/device/pty/driver.rs
Normal file
98
kernel/src/device/pty/driver.rs
Normal file
@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::sync::SpinLock;
|
||||
|
||||
use crate::{
|
||||
device::tty::{Tty, TtyDriver},
|
||||
events::IoEvents,
|
||||
prelude::{return_errno_with_message, Errno, Result},
|
||||
process::signal::Pollee,
|
||||
util::ring_buffer::RingBuffer,
|
||||
};
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
/// A pseudoterminal driver.
|
||||
///
|
||||
/// This is contained in the PTY slave, but it maintains the output buffer and the pollee of the
|
||||
/// master. The pollee of the slave is part of the [`Tty`] structure (see the definition of
|
||||
/// [`PtySlave`]).
|
||||
pub struct PtyDriver {
|
||||
output: SpinLock<RingBuffer<u8>>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
/// A pseudoterminal slave.
|
||||
pub type PtySlave = Tty<PtyDriver>;
|
||||
|
||||
impl PtyDriver {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
output: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)),
|
||||
pollee: Pollee::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut output = self.output.lock();
|
||||
if output.is_empty() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the buffer is empty");
|
||||
}
|
||||
|
||||
let read_len = output.len().min(buf.len());
|
||||
output.pop_slice(&mut buf[..read_len]).unwrap();
|
||||
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
pub(super) fn pollee(&self) -> &Pollee {
|
||||
&self.pollee
|
||||
}
|
||||
|
||||
pub(super) fn buffer_len(&self) -> usize {
|
||||
self.output.lock().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDriver for PtyDriver {
|
||||
fn push_output(&self, chs: &[u8]) {
|
||||
let mut output = self.output.lock();
|
||||
|
||||
for ch in chs {
|
||||
// TODO: This is termios-specific behavior and should be part of the TTY implementation
|
||||
// instead of the TTY driver implementation. See the ONLCR flag for more details.
|
||||
if *ch == b'\n' {
|
||||
output.push_overwrite(b'\r');
|
||||
output.push_overwrite(b'\n');
|
||||
continue;
|
||||
}
|
||||
output.push_overwrite(*ch);
|
||||
}
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
|
||||
fn drain_output(&self) {
|
||||
self.output.lock().clear();
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
let mut output = self.output.lock();
|
||||
let mut has_notified = false;
|
||||
|
||||
move |chs| {
|
||||
for ch in chs {
|
||||
output.push_overwrite(*ch);
|
||||
}
|
||||
|
||||
if !has_notified {
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
has_notified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
155
kernel/src/device/pty/master.rs
Normal file
155
kernel/src/device/pty/master.rs
Normal file
@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use ostd::task::Task;
|
||||
|
||||
use super::{driver::PtyDriver, PtySlave};
|
||||
use crate::{
|
||||
current_userspace,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
devpts::DevPts,
|
||||
file_table::FdFlags,
|
||||
fs_resolver::FsPath,
|
||||
inode_handle::FileIo,
|
||||
utils::{AccessMode, Inode, InodeMode, IoctlCmd},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
posix_thread::{AsPosixThread, AsThreadLocal},
|
||||
signal::{PollHandle, Pollable},
|
||||
Terminal,
|
||||
},
|
||||
};
|
||||
|
||||
const IO_CAPACITY: usize = 4096;
|
||||
|
||||
/// A pseudoterminal master.
|
||||
///
|
||||
/// A pseudoterminal contains two buffers:
|
||||
/// * The input buffer is written by the master and read by the slave, which is maintained in the
|
||||
/// line discipline (part of [`PtySlave`], which is a [`Tty`]).
|
||||
/// * The output buffer is written by the slave and read by the master, which is maintained in the
|
||||
/// driver (i.e., [`PtyDriver`]).
|
||||
///
|
||||
/// [`Tty`]: crate::device::tty::Tty
|
||||
pub struct PtyMaster {
|
||||
ptmx: Arc<dyn Inode>,
|
||||
slave: Arc<PtySlave>,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub(super) fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> {
|
||||
let slave = PtySlave::new(index, PtyDriver::new());
|
||||
|
||||
Arc::new(Self { ptmx, slave })
|
||||
}
|
||||
|
||||
pub(super) fn slave(&self) -> &Arc<PtySlave> {
|
||||
&self.slave
|
||||
}
|
||||
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
if self.slave().driver().buffer_len() > 0 {
|
||||
IoEvents::IN | IoEvents::OUT
|
||||
} else {
|
||||
IoEvents::OUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for PtyMaster {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.slave
|
||||
.driver()
|
||||
.pollee()
|
||||
.poll_with(mask, poller, || self.check_io_events())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for PtyMaster {
|
||||
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
let mut buf = vec![0u8; writer.avail().min(IO_CAPACITY)];
|
||||
let read_len = self.wait_events(IoEvents::IN, None, || {
|
||||
self.slave.driver().try_read(&mut buf)
|
||||
})?;
|
||||
self.slave.driver().pollee().invalidate();
|
||||
|
||||
// TODO: Confirm what we should do if `write_fallible` fails in the middle.
|
||||
writer.write_fallible(&mut buf[..read_len].into())?;
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn write(&self, reader: &mut VmReader) -> Result<usize> {
|
||||
let mut buf = vec![0u8; reader.remain().min(IO_CAPACITY)];
|
||||
let write_len = reader.read_fallible(&mut buf.as_mut_slice().into())?;
|
||||
|
||||
self.slave.push_input(&buf[..write_len]);
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TCSETSW
|
||||
| IoctlCmd::TCSETSF
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ
|
||||
| IoctlCmd::TIOCGPTN => return self.slave.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: Lock or unlock the PTY.
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current_task = Task::current().unwrap();
|
||||
let posix_thread = current_task.as_posix_thread().unwrap();
|
||||
let thread_local = current_task.as_thread_local().unwrap();
|
||||
|
||||
// TODO: Deal with `open()` flags.
|
||||
let slave = {
|
||||
let slave_name = {
|
||||
let devpts_path = super::DEV_PTS.get().unwrap().abs_path();
|
||||
format!("{}/{}", devpts_path, self.slave.index())
|
||||
};
|
||||
|
||||
let fs_path = FsPath::try_from(slave_name.as_str())?;
|
||||
|
||||
let inode_handle = {
|
||||
let fs = posix_thread.fs().resolver().read();
|
||||
let flags = AccessMode::O_RDWR as u32;
|
||||
let mode = (InodeMode::S_IRUSR | InodeMode::S_IWUSR).bits();
|
||||
fs.open(&fs_path, flags, mode)?
|
||||
};
|
||||
Arc::new(inode_handle)
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let file_table = thread_local.borrow_file_table();
|
||||
let mut file_table_locked = file_table.unwrap().write();
|
||||
// TODO: Deal with the `O_CLOEXEC` flag.
|
||||
file_table_locked.insert(slave, FdFlags::empty())
|
||||
};
|
||||
return Ok(fd);
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let len = self.slave.driver().buffer_len() as i32;
|
||||
current_userspace!().write_val(arg, &len)?;
|
||||
}
|
||||
_ => (self.slave.clone() as Arc<dyn Terminal>).job_ioctl(cmd, arg, true)?,
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtyMaster {
|
||||
fn drop(&mut self) {
|
||||
let fs = self.ptmx.fs();
|
||||
let devpts = fs.downcast_ref::<DevPts>().unwrap();
|
||||
|
||||
let index = self.slave.index();
|
||||
devpts.remove_slave(index);
|
||||
}
|
||||
}
|
@ -10,10 +10,11 @@ use crate::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
#[expect(clippy::module_inception)]
|
||||
mod pty;
|
||||
mod driver;
|
||||
mod master;
|
||||
|
||||
pub use pty::{PtyMaster, PtySlave};
|
||||
pub use driver::PtySlave;
|
||||
pub use master::PtyMaster;
|
||||
use spin::Once;
|
||||
|
||||
static DEV_PTS: Once<Dentry> = Once::new();
|
||||
|
@ -1,347 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use ostd::task::Task;
|
||||
|
||||
use crate::{
|
||||
current_userspace,
|
||||
device::tty::line_discipline::LineDiscipline,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceId, DeviceType},
|
||||
devpts::DevPts,
|
||||
file_table::FdFlags,
|
||||
fs_resolver::FsPath,
|
||||
inode_handle::FileIo,
|
||||
utils::{AccessMode, Inode, InodeMode, IoctlCmd},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
broadcast_signal_async,
|
||||
posix_thread::{AsPosixThread, AsThreadLocal},
|
||||
signal::{PollHandle, Pollable, Pollee},
|
||||
JobControl, Terminal,
|
||||
},
|
||||
util::ring_buffer::RingBuffer,
|
||||
};
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
const IO_CAPACITY: usize = 4096;
|
||||
|
||||
/// Pseudo terminal master.
|
||||
/// Internally, it has two buffers.
|
||||
/// One is inside ldisc, which is written by master and read by slave,
|
||||
/// the other is a ring buffer, which is written by slave and read by master.
|
||||
pub struct PtyMaster {
|
||||
ptmx: Arc<dyn Inode>,
|
||||
index: u32,
|
||||
slave: Arc<PtySlave>,
|
||||
input: SpinLock<RingBuffer<u8>>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> {
|
||||
Arc::new_cyclic(move |master| {
|
||||
let slave = Arc::new_cyclic(move |weak_self| PtySlave {
|
||||
ldisc: SpinLock::new(LineDiscipline::new()),
|
||||
job_control: JobControl::new(),
|
||||
pollee: Pollee::new(),
|
||||
master: master.clone(),
|
||||
weak_self: weak_self.clone(),
|
||||
});
|
||||
|
||||
PtyMaster {
|
||||
ptmx,
|
||||
index,
|
||||
slave,
|
||||
input: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)),
|
||||
pollee: Pollee::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn ptmx(&self) -> &Arc<dyn Inode> {
|
||||
&self.ptmx
|
||||
}
|
||||
|
||||
pub fn slave(&self) -> &Arc<PtySlave> {
|
||||
&self.slave
|
||||
}
|
||||
|
||||
fn slave_push(&self, chs: &[u8]) {
|
||||
let mut input = self.input.lock();
|
||||
|
||||
for ch in chs {
|
||||
// TODO: This is termios-specific behavior and should be part of the TTY implementation
|
||||
// instead of the TTY driver implementation. See the ONLCR flag for more details.
|
||||
if *ch == b'\n' {
|
||||
input.push_overwrite(b'\r');
|
||||
input.push_overwrite(b'\n');
|
||||
continue;
|
||||
}
|
||||
input.push_overwrite(*ch);
|
||||
}
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
|
||||
fn slave_echo(&self) -> impl FnMut(&str) + '_ {
|
||||
let mut input = self.input.lock();
|
||||
let mut has_notified = false;
|
||||
|
||||
move |content| {
|
||||
for byte in content.as_bytes() {
|
||||
input.push_overwrite(*byte);
|
||||
}
|
||||
|
||||
if !has_notified {
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
has_notified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut input = self.input.lock();
|
||||
if input.is_empty() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the buffer is empty");
|
||||
}
|
||||
|
||||
let read_len = input.len().min(buf.len());
|
||||
input.pop_slice(&mut buf[..read_len]).unwrap();
|
||||
self.pollee.invalidate();
|
||||
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
let input = self.input.lock();
|
||||
|
||||
if !input.is_empty() {
|
||||
IoEvents::IN | IoEvents::OUT
|
||||
} else {
|
||||
IoEvents::OUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for PtyMaster {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.pollee
|
||||
.poll_with(mask, poller, || self.check_io_events())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for PtyMaster {
|
||||
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
let mut buf = vec![0u8; writer.avail().min(IO_CAPACITY)];
|
||||
let read_len = self.wait_events(IoEvents::IN, None, || self.try_read(&mut buf))?;
|
||||
|
||||
writer.write_fallible(&mut buf[..read_len].into())?;
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn write(&self, reader: &mut VmReader) -> Result<usize> {
|
||||
let mut buf = vec![0u8; reader.remain().min(IO_CAPACITY)];
|
||||
let write_len = reader.read_fallible(&mut buf.as_mut_slice().into())?;
|
||||
|
||||
self.slave.master_push(&buf[..write_len]);
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TIOCGPTN
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ => return self.slave.ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: lock/unlock pty
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current_task = Task::current().unwrap();
|
||||
let posix_thread = current_task.as_posix_thread().unwrap();
|
||||
let thread_local = current_task.as_thread_local().unwrap();
|
||||
|
||||
// TODO: deal with open options
|
||||
let slave = {
|
||||
let slave_name = {
|
||||
let devpts_path = super::DEV_PTS.get().unwrap().abs_path();
|
||||
format!("{}/{}", devpts_path, self.index())
|
||||
};
|
||||
|
||||
let fs_path = FsPath::try_from(slave_name.as_str())?;
|
||||
|
||||
let inode_handle = {
|
||||
let fs = posix_thread.fs().resolver().read();
|
||||
let flags = AccessMode::O_RDWR as u32;
|
||||
let mode = (InodeMode::S_IRUSR | InodeMode::S_IWUSR).bits();
|
||||
fs.open(&fs_path, flags, mode)?
|
||||
};
|
||||
Arc::new(inode_handle)
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let file_table = thread_local.borrow_file_table();
|
||||
let mut file_table_locked = file_table.unwrap().write();
|
||||
// TODO: deal with the O_CLOEXEC flag
|
||||
file_table_locked.insert(slave, FdFlags::empty())
|
||||
};
|
||||
return Ok(fd);
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let len = self.input.lock().len() as i32;
|
||||
current_userspace!().write_val(arg, &len)?;
|
||||
}
|
||||
_ => (self.slave.clone() as Arc<dyn Terminal>).job_ioctl(cmd, arg, true)?,
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtyMaster {
|
||||
fn drop(&mut self) {
|
||||
let fs = self.ptmx.fs();
|
||||
let devpts = fs.downcast_ref::<DevPts>().unwrap();
|
||||
|
||||
let index = self.index;
|
||||
devpts.remove_slave(index);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PtySlave {
|
||||
ldisc: SpinLock<LineDiscipline>,
|
||||
job_control: JobControl,
|
||||
pollee: Pollee,
|
||||
master: Weak<PtyMaster>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl PtySlave {
|
||||
pub fn index(&self) -> u32 {
|
||||
self.master().index()
|
||||
}
|
||||
|
||||
fn master(&self) -> Arc<PtyMaster> {
|
||||
self.master.upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn master_push(&self, chs: &[u8]) {
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
|
||||
let master = self.master();
|
||||
let mut echo = master.slave_echo();
|
||||
|
||||
for ch in chs {
|
||||
ldisc.push_char(
|
||||
*ch,
|
||||
|signum| {
|
||||
if let Some(foreground) = self.job_control.foreground() {
|
||||
broadcast_signal_async(Arc::downgrade(&foreground), signum);
|
||||
}
|
||||
},
|
||||
&mut echo,
|
||||
);
|
||||
}
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
if self.ldisc.lock().buffer_len() != 0 {
|
||||
IoEvents::IN | IoEvents::OUT
|
||||
} else {
|
||||
IoEvents::OUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtySlave {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> crate::fs::device::DeviceId {
|
||||
DeviceId::new(88, self.index())
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for PtySlave {
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for PtySlave {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.pollee
|
||||
.poll_with(mask, poller, || self.check_io_events())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for PtySlave {
|
||||
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
self.job_control.wait_until_in_foreground()?;
|
||||
|
||||
// TODO: Add support for non-blocking mode and timeout
|
||||
let mut buf = vec![0u8; writer.avail().min(IO_CAPACITY)];
|
||||
let read_len =
|
||||
self.wait_events(IoEvents::IN, None, || self.ldisc.lock().try_read(&mut buf))?;
|
||||
self.pollee.invalidate();
|
||||
|
||||
writer.write_fallible(&mut buf[..read_len].into())?;
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn write(&self, reader: &mut VmReader) -> Result<usize> {
|
||||
let mut buf = vec![0u8; reader.remain().min(IO_CAPACITY)];
|
||||
let write_len = reader.read_fallible(&mut buf.as_mut_slice().into())?;
|
||||
|
||||
self.master().slave_push(&buf[..write_len]);
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
let termios = *self.ldisc.lock().termios();
|
||||
current_userspace!().write_val(arg, &termios)?;
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
self.ldisc.lock().set_termios(termios);
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
let idx = self.index();
|
||||
current_userspace!().write_val(arg, &idx)?;
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
let winsize = self.ldisc.lock().window_size();
|
||||
current_userspace!().write_val(arg, &winsize)?;
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = current_userspace!().read_val(arg)?;
|
||||
self.ldisc.lock().set_window_size(winsize);
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let buffer_len = self.ldisc.lock().buffer_len() as i32;
|
||||
current_userspace!().write_val(arg, &buffer_len)?;
|
||||
}
|
||||
_ => (self.weak_self.upgrade().unwrap() as Arc<dyn Terminal>)
|
||||
.job_ioctl(cmd, arg, false)?,
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
@ -1,89 +1,23 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::mm::{Infallible, VmReader};
|
||||
use spin::Once;
|
||||
/// A TTY driver.
|
||||
///
|
||||
/// A driver exposes some device-specific behavior to [`Tty`]. For example, a device provides
|
||||
/// methods to write to the output buffer (see [`Self::push_output`]), where the output buffer can
|
||||
/// be the monitor if the underlying device is framebuffer, or just a ring buffer if the underlying
|
||||
/// device is pseduoterminal).
|
||||
///
|
||||
/// [`Tty`]: super::Tty
|
||||
pub trait TtyDriver: Send + Sync + 'static {
|
||||
/// Pushes characters into the output buffer.
|
||||
fn push_output(&self, chs: &[u8]);
|
||||
|
||||
use crate::{
|
||||
device::tty::{get_n_tty, Tty},
|
||||
prelude::*,
|
||||
};
|
||||
/// Drains the output buffer.
|
||||
fn drain_output(&self);
|
||||
|
||||
pub static TTY_DRIVER: Once<Arc<TtyDriver>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
for (_, device) in aster_console::all_devices() {
|
||||
device.register_callback(&console_input_callback)
|
||||
}
|
||||
let tty_driver = Arc::new(TtyDriver::new());
|
||||
// FIXME: install n_tty into tty_driver?
|
||||
let n_tty = get_n_tty();
|
||||
tty_driver.install(n_tty.clone());
|
||||
TTY_DRIVER.call_once(|| tty_driver);
|
||||
}
|
||||
|
||||
pub struct TtyDriver {
|
||||
ttys: SpinLock<Vec<Arc<Tty>>>,
|
||||
}
|
||||
|
||||
impl TtyDriver {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ttys: SpinLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the tty device in driver's internal table.
|
||||
pub fn lookup(&self, index: usize) -> Result<Arc<Tty>> {
|
||||
let ttys = self.ttys.disable_irq().lock();
|
||||
// Return the tty device corresponding to idx
|
||||
if index >= ttys.len() {
|
||||
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
|
||||
}
|
||||
let tty = ttys[index].clone();
|
||||
drop(ttys);
|
||||
Ok(tty)
|
||||
}
|
||||
|
||||
/// Install a new tty into the driver's internal tables.
|
||||
pub fn install(self: &Arc<Self>, tty: Arc<Tty>) {
|
||||
tty.set_driver(Arc::downgrade(self));
|
||||
self.ttys.disable_irq().lock().push(tty);
|
||||
}
|
||||
|
||||
/// remove a new tty into the driver's internal tables.
|
||||
pub fn remove(&self, index: usize) -> Result<()> {
|
||||
let mut ttys = self.ttys.disable_irq().lock();
|
||||
if index >= ttys.len() {
|
||||
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
|
||||
}
|
||||
let removed_tty = ttys.remove(index);
|
||||
removed_tty.set_driver(Weak::new());
|
||||
drop(ttys);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_char(&self, ch: u8) {
|
||||
// FIXME: should the char send to all ttys?
|
||||
for tty in &*self.ttys.disable_irq().lock() {
|
||||
tty.push_char(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TtyDriver {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn console_input_callback(mut reader: VmReader<Infallible>) {
|
||||
let tty_driver = get_tty_driver();
|
||||
while reader.remain() > 0 {
|
||||
let ch = reader.read_val().unwrap();
|
||||
tty_driver.push_char(ch);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tty_driver() -> &'static TtyDriver {
|
||||
TTY_DRIVER.get().unwrap()
|
||||
/// Returns a callback function that echoes input characters to the output buffer.
|
||||
///
|
||||
/// Note that the implementation may choose to hold a lock during the life of the callback.
|
||||
/// During this time, calls to other methods such as [`Self::push_output`] may cause deadlocks.
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_;
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use super::termio::{KernelTermios, WinSize, CC_C_CHAR};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
@ -61,10 +59,6 @@ impl CurrentLine {
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.buffer.is_full()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.buffer.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl LineDiscipline {
|
||||
@ -79,7 +73,7 @@ impl LineDiscipline {
|
||||
}
|
||||
|
||||
/// Pushes a character to the line discipline.
|
||||
pub fn push_char<F1: FnMut(SigNum), F2: FnMut(&str)>(
|
||||
pub fn push_char<F1: FnMut(SigNum), F2: FnMut(&[u8])>(
|
||||
&mut self,
|
||||
ch: u8,
|
||||
mut signal_callback: F1,
|
||||
@ -135,19 +129,17 @@ impl LineDiscipline {
|
||||
}
|
||||
|
||||
// TODO: respect output flags
|
||||
fn output_char<F: FnMut(&str)>(&self, ch: u8, mut echo_callback: F) {
|
||||
fn output_char<F: FnMut(&[u8])>(&self, ch: u8, mut echo_callback: F) {
|
||||
match ch {
|
||||
b'\n' => echo_callback("\n"),
|
||||
b'\r' => echo_callback("\r\n"),
|
||||
b'\n' => echo_callback(b"\n"),
|
||||
b'\r' => echo_callback(b"\r\n"),
|
||||
ch if ch == *self.termios.get_special_char(CC_C_CHAR::VERASE) => {
|
||||
// Write a space to overwrite the current character
|
||||
let backspace: &str = core::str::from_utf8(b"\x08 \x08").unwrap();
|
||||
echo_callback(backspace);
|
||||
echo_callback(b"\x08 \x08");
|
||||
}
|
||||
ch if is_printable_char(ch) => print!("{}", char::from(ch)),
|
||||
ch if is_printable_char(ch) => echo_callback(&[ch]),
|
||||
ch if is_ctrl_char(ch) && self.termios.contains_echo_ctl() => {
|
||||
let ctrl_char = format!("^{}", ctrl_char_to_printable(ch));
|
||||
echo_callback(&ctrl_char);
|
||||
echo_callback(&[b'^', ctrl_char_to_printable(ch)]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -267,7 +259,7 @@ fn char_to_signal(ch: u8, termios: &KernelTermios) -> Option<SigNum> {
|
||||
}
|
||||
}
|
||||
|
||||
fn ctrl_char_to_printable(ch: u8) -> char {
|
||||
fn ctrl_char_to_printable(ch: u8) -> u8 {
|
||||
debug_assert!(is_ctrl_char(ch));
|
||||
char::from_u32((ch + b'A' - 1) as u32).unwrap()
|
||||
ch + b'A' - 1
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::{early_print, sync::LocalIrqDisabled};
|
||||
use spin::Once;
|
||||
use ostd::sync::LocalIrqDisabled;
|
||||
|
||||
use self::{driver::TtyDriver, line_discipline::LineDiscipline};
|
||||
use self::line_discipline::LineDiscipline;
|
||||
use crate::{
|
||||
current_userspace,
|
||||
events::IoEvents,
|
||||
@ -21,65 +20,65 @@ use crate::{
|
||||
};
|
||||
|
||||
mod device;
|
||||
pub mod driver;
|
||||
pub mod line_discipline;
|
||||
pub mod termio;
|
||||
mod driver;
|
||||
mod line_discipline;
|
||||
mod n_tty;
|
||||
mod termio;
|
||||
|
||||
pub use device::TtyDevice;
|
||||
|
||||
static N_TTY: Once<Arc<Tty>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
let name = CString::new("console").unwrap();
|
||||
let tty = Tty::new(name);
|
||||
N_TTY.call_once(|| tty);
|
||||
driver::init();
|
||||
}
|
||||
pub use driver::TtyDriver;
|
||||
pub(super) use n_tty::init;
|
||||
pub use n_tty::{iter_n_tty, system_console};
|
||||
|
||||
const IO_CAPACITY: usize = 4096;
|
||||
|
||||
pub struct Tty {
|
||||
/// tty_name
|
||||
#[expect(unused)]
|
||||
name: CString,
|
||||
/// line discipline
|
||||
/// A teletyper (TTY).
|
||||
///
|
||||
/// This abstracts the general functionality of a TTY in a way that
|
||||
/// - Any input device driver can use [`Tty::push_input`] to push input characters, and users can
|
||||
/// [`Tty::read`] from the TTY;
|
||||
/// - Users can also [`Tty::write`] output characters to the TTY and the output device driver will
|
||||
/// receive the characters from [`TtyDriver::push_output`] where the generic parameter `D` is
|
||||
/// the [`TtyDriver`].
|
||||
///
|
||||
/// ```text
|
||||
/// +------------+ +-------------+
|
||||
/// |input device| |output device|
|
||||
/// | driver | | driver |
|
||||
/// +-----+------+ +------^------+
|
||||
/// | |
|
||||
/// | +-------+ |
|
||||
/// +-----> TTY +-----+
|
||||
/// +-------+
|
||||
/// Tty::push_input D::push_output
|
||||
/// ```
|
||||
pub struct Tty<D> {
|
||||
index: u32,
|
||||
driver: D,
|
||||
ldisc: SpinLock<LineDiscipline, LocalIrqDisabled>,
|
||||
job_control: JobControl,
|
||||
pollee: Pollee,
|
||||
/// driver
|
||||
driver: SpinLock<Weak<TtyDriver>>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl Tty {
|
||||
pub fn new(name: CString) -> Arc<Self> {
|
||||
impl<D> Tty<D> {
|
||||
pub fn new(index: u32, driver: D) -> Arc<Self> {
|
||||
Arc::new_cyclic(move |weak_ref| Tty {
|
||||
name,
|
||||
index,
|
||||
driver,
|
||||
ldisc: SpinLock::new(LineDiscipline::new()),
|
||||
job_control: JobControl::new(),
|
||||
pollee: Pollee::new(),
|
||||
driver: SpinLock::new(Weak::new()),
|
||||
weak_self: weak_ref.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_driver(&self, driver: Weak<TtyDriver>) {
|
||||
*self.driver.disable_irq().lock() = driver;
|
||||
pub fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn push_char(&self, ch: u8) {
|
||||
// FIXME: Use `early_print` to avoid calling virtio-console.
|
||||
// This is only a workaround
|
||||
self.ldisc.lock().push_char(
|
||||
ch,
|
||||
|signum| {
|
||||
if let Some(foreground) = self.job_control.foreground() {
|
||||
broadcast_signal_async(Arc::downgrade(&foreground), signum);
|
||||
}
|
||||
},
|
||||
|content| early_print!("{}", content),
|
||||
);
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
pub fn driver(&self) -> &D {
|
||||
&self.driver
|
||||
}
|
||||
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
@ -91,14 +90,34 @@ impl Tty {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for Tty {
|
||||
impl<D: TtyDriver> Tty<D> {
|
||||
pub fn push_input(&self, chs: &[u8]) {
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
let mut echo = self.driver.echo_callback();
|
||||
|
||||
for ch in chs {
|
||||
ldisc.push_char(
|
||||
*ch,
|
||||
|signum| {
|
||||
if let Some(foreground) = self.job_control.foreground() {
|
||||
broadcast_signal_async(Arc::downgrade(&foreground), signum);
|
||||
}
|
||||
},
|
||||
&mut echo,
|
||||
);
|
||||
}
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Pollable for Tty<D> {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.pollee
|
||||
.poll_with(mask, poller, || self.check_io_events())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Tty {
|
||||
impl<D: TtyDriver> FileIo for Tty<D> {
|
||||
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
self.job_control.wait_until_in_foreground()?;
|
||||
|
||||
@ -117,11 +136,7 @@ impl FileIo for Tty {
|
||||
let mut buf = vec![0u8; reader.remain().min(IO_CAPACITY)];
|
||||
let write_len = reader.read_fallible(&mut buf.as_mut_slice().into())?;
|
||||
|
||||
if let Ok(content) = alloc::str::from_utf8(&buf[..write_len]) {
|
||||
print!("{content}");
|
||||
} else {
|
||||
println!("Not utf-8 content: {:?}", buf);
|
||||
}
|
||||
self.driver.push_output(&buf[..write_len]);
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
@ -140,8 +155,9 @@ impl FileIo for Tty {
|
||||
IoctlCmd::TCSETSW => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
|
||||
self.ldisc.lock().set_termios(termios);
|
||||
// TODO: Drain the output buffer
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
ldisc.set_termios(termios);
|
||||
self.driver.drain_output();
|
||||
}
|
||||
IoctlCmd::TCSETSF => {
|
||||
let termios = current_userspace!().read_val(arg)?;
|
||||
@ -149,7 +165,7 @@ impl FileIo for Tty {
|
||||
let mut ldisc = self.ldisc.lock();
|
||||
ldisc.set_termios(termios);
|
||||
ldisc.drain_input();
|
||||
// TODO: Drain the output buffer
|
||||
self.driver.drain_output();
|
||||
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
@ -163,6 +179,16 @@ impl FileIo for Tty {
|
||||
|
||||
self.ldisc.lock().set_window_size(winsize);
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
let idx = self.index;
|
||||
|
||||
current_userspace!().write_val(arg, &idx)?;
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let buffer_len = self.ldisc.lock().buffer_len() as u32;
|
||||
|
||||
current_userspace!().write_val(arg, &buffer_len)?;
|
||||
}
|
||||
_ => (self.weak_self.upgrade().unwrap() as Arc<dyn Terminal>)
|
||||
.job_ioctl(cmd, arg, false)?,
|
||||
}
|
||||
@ -171,23 +197,18 @@ impl FileIo for Tty {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for Tty {
|
||||
impl<D: TtyDriver> Terminal for Tty<D> {
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Tty {
|
||||
impl<D: TtyDriver> Device for Tty<D> {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as /dev/console in linux.
|
||||
DeviceId::new(88, 0)
|
||||
DeviceId::new(88, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_n_tty() -> &'static Arc<Tty> {
|
||||
N_TTY.get().unwrap()
|
||||
}
|
||||
|
77
kernel/src/device/tty/n_tty.rs
Normal file
77
kernel/src/device/tty/n_tty.rs
Normal file
@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use ostd::mm::{Infallible, VmReader, VmWriter};
|
||||
use spin::Once;
|
||||
|
||||
use super::{Tty, TtyDriver};
|
||||
|
||||
pub struct ConsoleDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl TtyDriver for ConsoleDriver {
|
||||
fn push_output(&self, chs: &[u8]) {
|
||||
self.console.send(chs);
|
||||
}
|
||||
|
||||
fn drain_output(&self) {}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
}
|
||||
|
||||
static N_TTY: Once<Box<[Arc<Tty<ConsoleDriver>>]>> = Once::new();
|
||||
|
||||
pub(in crate::device) fn init() {
|
||||
let devices = {
|
||||
let mut devices = aster_console::all_devices();
|
||||
// Sort by priorities to ensure that the TTY for the virtio-console device comes first. Is
|
||||
// there a better way than hardcoding this?
|
||||
devices.sort_by_key(|(name, _)| match name.as_str() {
|
||||
aster_virtio::device::console::DEVICE_NAME => 0,
|
||||
aster_framebuffer::CONSOLE_NAME => 1,
|
||||
_ => 2,
|
||||
});
|
||||
devices
|
||||
};
|
||||
|
||||
let ttys = devices
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (_, device))| create_n_tty(index as _, device))
|
||||
.collect();
|
||||
N_TTY.call_once(|| ttys);
|
||||
}
|
||||
|
||||
fn create_n_tty(index: u32, device: Arc<dyn AnyConsoleDevice>) -> Arc<Tty<ConsoleDriver>> {
|
||||
let driver = ConsoleDriver {
|
||||
console: device.clone(),
|
||||
};
|
||||
|
||||
let tty = Tty::new(index, driver);
|
||||
let tty_cloned = tty.clone();
|
||||
|
||||
device.register_callback(Box::leak(Box::new(
|
||||
move |mut reader: VmReader<Infallible>| {
|
||||
let mut chs = vec![0u8; reader.remain()];
|
||||
reader.read(&mut VmWriter::from(chs.as_mut_slice()));
|
||||
tty.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
|
||||
tty_cloned
|
||||
}
|
||||
|
||||
/// Returns the system console, i.e., `/dev/console`.
|
||||
pub fn system_console() -> &'static Arc<Tty<ConsoleDriver>> {
|
||||
&N_TTY.get().unwrap()[0]
|
||||
}
|
||||
|
||||
/// Iterates all TTY devices, i.e., `/dev/tty1`, `/dev/tty2`, e.t.c.
|
||||
pub fn iter_n_tty() -> impl Iterator<Item = &'static Arc<Tty<ConsoleDriver>>> {
|
||||
N_TTY.get().unwrap().iter()
|
||||
}
|
@ -37,7 +37,7 @@ pub fn spawn_init_process(
|
||||
set_session_and_group(&process);
|
||||
|
||||
// FIXME: This should be done by the userspace init process.
|
||||
(crate::device::tty::get_n_tty().clone() as Arc<dyn Terminal>).set_control(&process)?;
|
||||
(crate::device::tty::system_console().clone() as Arc<dyn Terminal>).set_control(&process)?;
|
||||
|
||||
process.run();
|
||||
|
||||
|
@ -731,15 +731,14 @@ pub fn enqueue_signal_async(process: Weak<Process>, signum: SigNum) {
|
||||
/// This is the asynchronous version of [`ProcessGroup::broadcast_signal`]. By asynchronous, this
|
||||
/// method submits a work item and returns, so this method doesn't sleep and can be used in atomic
|
||||
/// mode.
|
||||
pub fn broadcast_signal_async(process_group: Weak<ProcessGroup>, signal: SigNum) {
|
||||
pub fn broadcast_signal_async(process_group: Weak<ProcessGroup>, signum: SigNum) {
|
||||
use super::signal::signals::kernel::KernelSignal;
|
||||
use crate::thread::work_queue;
|
||||
|
||||
let signal = KernelSignal::new(signal);
|
||||
work_queue::submit_work_func(
|
||||
move || {
|
||||
if let Some(process_group) = process_group.upgrade() {
|
||||
process_group.broadcast_signal(signal);
|
||||
process_group.broadcast_signal(KernelSignal::new(signum));
|
||||
}
|
||||
},
|
||||
work_queue::WorkPriority::High,
|
||||
|
Reference in New Issue
Block a user