Refactor TTY device management and update the relevant ioctl processing logic
This commit is contained in:
parent
6f6812ff61
commit
77b6d82809
|
|
@ -10,14 +10,11 @@ use aster_cmdline::KCMDLINE;
|
|||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use spin::Once;
|
||||
|
||||
use super::n_tty::tty1_device;
|
||||
use super::vt::active_vt;
|
||||
use crate::{
|
||||
device::{
|
||||
registry::char,
|
||||
tty::{
|
||||
Tty,
|
||||
n_tty::{VtDriver, hvc0_device, serial0_device},
|
||||
},
|
||||
tty::{Tty, TtyFile, hvc::hvc0_device, serial::serial0_device, vt::VtDriver},
|
||||
},
|
||||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
|
|
@ -32,9 +29,8 @@ use crate::{
|
|||
pub struct Tty0Device;
|
||||
|
||||
impl Tty0Device {
|
||||
fn active_vt(&self) -> &Arc<Tty<VtDriver>> {
|
||||
// Currently there is only one virtual terminal `tty1`.
|
||||
tty1_device()
|
||||
fn active_vt(&self) -> Arc<Tty<VtDriver>> {
|
||||
active_vt()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,12 +48,12 @@ impl Device for Tty0Device {
|
|||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
self.active_vt().open()
|
||||
Ok(Box::new(TtyFile(self.active_vt())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for Tty0Device {
|
||||
fn job_control(&self) -> &JobControl {
|
||||
fn job_control(&self) -> Arc<JobControl> {
|
||||
self.active_vt().job_control()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::{
|
|||
device::tty::{Tty, termio::CTermios},
|
||||
fs::inode_handle::FileIo,
|
||||
prelude::*,
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// A TTY driver.
|
||||
|
|
@ -64,4 +65,24 @@ pub trait TtyDriver: Send + Sync + 'static {
|
|||
///
|
||||
/// This method will be called with a spin lock held, so it cannot break atomic mode.
|
||||
fn on_termios_change(&self, old_termios: &CTermios, new_termios: &CTermios);
|
||||
|
||||
/// Driver-specific ioctl handler.
|
||||
///
|
||||
/// This method allows a TTY driver to handle driver-specific
|
||||
/// ioctl commands that are not processed by the generic TTY layer.
|
||||
///
|
||||
/// Semantics:
|
||||
/// - If the driver recognizes and handles the ioctl, it should return
|
||||
/// `Ok(Some(retval))`, where `retval` is the value returned to userspace.
|
||||
/// - If the driver does not recognize the ioctl, it should return
|
||||
/// `Ok(None)` to indicate that the request should be handled by higher
|
||||
/// layers or reported as unsupported.
|
||||
/// - If an error occurs while processing the ioctl, it should return
|
||||
/// `Err(...)`.
|
||||
fn ioctl(&self, _tty: &Tty<Self>, _raw: RawIoctl) -> Result<Option<i32>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, format, sync::Arc};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use ostd::mm::{Infallible, VmReader, VmWriter};
|
||||
use spin::Once;
|
||||
|
||||
use super::{Tty, TtyDriver};
|
||||
use crate::{
|
||||
device::{
|
||||
registry::char,
|
||||
tty::{TtyFile, termio::CTermios},
|
||||
},
|
||||
fs::inode_handle::FileIo,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// The driver for hypervisor console devices.
|
||||
#[derive(Clone)]
|
||||
pub struct HvcDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl TtyDriver for HvcDriver {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/Documentation/admin-guide/devices.txt#L2936>.
|
||||
const DEVICE_MAJOR_ID: u32 = 229;
|
||||
|
||||
fn devtmpfs_path(&self, index: u32) -> Option<String> {
|
||||
Some(format!("hvc{}", index))
|
||||
}
|
||||
|
||||
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(TtyFile(tty)))
|
||||
}
|
||||
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
|
||||
fn can_push(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
Some(&*self.console)
|
||||
}
|
||||
|
||||
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
|
||||
}
|
||||
|
||||
static HVC0: Once<Arc<Tty<HvcDriver>>> = Once::new();
|
||||
|
||||
/// Returns the `hvc0` device.
|
||||
///
|
||||
/// Returns `None` if the device is not found nor initialized.
|
||||
pub fn hvc0_device() -> Option<&'static Arc<Tty<HvcDriver>>> {
|
||||
HVC0.get()
|
||||
}
|
||||
|
||||
pub(super) fn init_in_first_process() -> Result<()> {
|
||||
let devices = aster_console::all_devices();
|
||||
|
||||
// Initialize the `hvc0` device if the virtio console is available.
|
||||
|
||||
let virtio_console = devices
|
||||
.iter()
|
||||
.find(|(name, _)| name.as_str() == aster_virtio::device::console::DEVICE_NAME)
|
||||
.map(|(_, device)| device.clone());
|
||||
|
||||
if let Some(virtio_console) = virtio_console {
|
||||
let driver = HvcDriver {
|
||||
console: virtio_console.clone(),
|
||||
};
|
||||
let hvc0 = Tty::new(0, driver);
|
||||
|
||||
HVC0.call_once(|| hvc0.clone());
|
||||
char::register(hvc0.clone())?;
|
||||
|
||||
virtio_console.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()));
|
||||
let _ = hvc0.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ use super::{
|
|||
CFontOp,
|
||||
termio::{CTermios, CWinSize},
|
||||
};
|
||||
use crate::util::ioctl::{InData, OutData, PassByVal, ioc};
|
||||
use crate::util::ioctl::{InData, OutData, ioc};
|
||||
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/asm-generic/ioctls.h>
|
||||
|
||||
|
|
@ -19,10 +19,4 @@ pub type SetWinSize = ioc!(TIOCSWINSZ, 0x5414, InData<CWinSize>);
|
|||
// TODO: Consider moving this to the `pty` module.
|
||||
pub type GetPtyNumber = ioc!(TIOCGPTN, b'T', 0x30, OutData<u32>);
|
||||
|
||||
pub type SetGraphicsMode = ioc!(KDSETMODE, 0x4B3A, InData<i32, PassByVal>);
|
||||
pub type GetGraphicsMode = ioc!(KDGETMODE, 0x4B3B, OutData<i32>);
|
||||
|
||||
pub type GetKeyboardMode = ioc!(KDGKBMODE, 0x4B44, OutData<i32>);
|
||||
pub type SetKeyboardMode = ioc!(KDSKBMODE, 0x4B45, InData<i32, PassByVal>);
|
||||
|
||||
pub type SetOrGetFont = ioc!(KDFONTOP, 0x4B72, InData<CFontOp>);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_console::{
|
||||
AnyConsoleDevice,
|
||||
font::BitmapFont,
|
||||
mode::{ConsoleMode, KeyboardMode},
|
||||
};
|
||||
use aster_console::{AnyConsoleDevice, font::BitmapFont};
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
use ostd::{mm::VmIo, sync::LocalIrqDisabled};
|
||||
|
||||
use self::{line_discipline::LineDiscipline, termio::CFontOp};
|
||||
|
|
@ -15,7 +12,7 @@ use crate::{
|
|||
fs::{
|
||||
device::{Device, DeviceType},
|
||||
inode_handle::FileIo,
|
||||
utils::StatusFlags,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
|
|
@ -28,18 +25,22 @@ use crate::{
|
|||
mod device;
|
||||
mod driver;
|
||||
mod flags;
|
||||
mod hvc;
|
||||
pub(super) mod ioctl_defs;
|
||||
mod line_discipline;
|
||||
mod n_tty;
|
||||
mod serial;
|
||||
pub(super) mod termio;
|
||||
mod vt;
|
||||
|
||||
pub use device::SystemConsole;
|
||||
pub use driver::TtyDriver;
|
||||
pub(super) use flags::TtyFlags;
|
||||
|
||||
pub(super) fn init_in_first_process() -> Result<()> {
|
||||
n_tty::init_in_first_process()?;
|
||||
hvc::init_in_first_process()?;
|
||||
serial::init_in_first_process()?;
|
||||
device::init_in_first_process()?;
|
||||
vt::init_in_first_process()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ pub struct Tty<D> {
|
|||
index: u32,
|
||||
driver: D,
|
||||
ldisc: SpinLock<LineDiscipline, LocalIrqDisabled>,
|
||||
job_control: JobControl,
|
||||
job_control: Arc<JobControl>,
|
||||
pollee: Pollee,
|
||||
tty_flags: TtyFlags,
|
||||
weak_self: Weak<Self>,
|
||||
|
|
@ -82,7 +83,7 @@ impl<D> Tty<D> {
|
|||
index,
|
||||
driver,
|
||||
ldisc: SpinLock::new(LineDiscipline::new()),
|
||||
job_control: JobControl::new(),
|
||||
job_control: Arc::new(JobControl::new()),
|
||||
pollee: Pollee::new(),
|
||||
tty_flags: TtyFlags::new(),
|
||||
weak_self: weak_ref.clone(),
|
||||
|
|
@ -376,37 +377,22 @@ impl<D: TtyDriver> Tty<D> {
|
|||
|
||||
self.handle_set_font(&font_op)?;
|
||||
}
|
||||
cmd @ SetGraphicsMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = ConsoleMode::try_from(cmd.get())?;
|
||||
if !console.set_mode(mode) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the console mode is not supported");
|
||||
_ => {
|
||||
if (self.weak_self.upgrade().unwrap() as Arc<dyn Terminal>)
|
||||
.job_ioctl(raw_ioctl, false)?
|
||||
.is_none()
|
||||
{
|
||||
match self.driver.ioctl(self, raw_ioctl)? {
|
||||
Some(ret) => return Ok(ret),
|
||||
None => {
|
||||
return Err(crate::prelude::Error::with_message(
|
||||
Errno::ENOTTY,
|
||||
"unhandled ioctl command",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd @ GetGraphicsMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = console.mode().unwrap_or(ConsoleMode::Text);
|
||||
cmd.write(&(mode as i32))?;
|
||||
}
|
||||
cmd @ SetKeyboardMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = KeyboardMode::try_from(cmd.get())?;
|
||||
if !console.set_keyboard_mode(mode) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the keyboard mode is not supported");
|
||||
}
|
||||
}
|
||||
cmd @ GetKeyboardMode => {
|
||||
let console = self.console()?;
|
||||
|
||||
let mode = console.keyboard_mode().unwrap_or(KeyboardMode::Xlate);
|
||||
cmd.write(&(mode as i32))?;
|
||||
}
|
||||
|
||||
_ => (self.weak_self.upgrade().unwrap() as Arc<dyn Terminal>)
|
||||
.job_ioctl(raw_ioctl, false)?,
|
||||
});
|
||||
|
||||
Ok(0)
|
||||
|
|
@ -414,8 +400,8 @@ impl<D: TtyDriver> Tty<D> {
|
|||
}
|
||||
|
||||
impl<D: TtyDriver> Terminal for Tty<D> {
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
fn job_control(&self) -> Arc<JobControl> {
|
||||
self.job_control.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -439,3 +425,43 @@ impl<D: TtyDriver> Device for Tty<D> {
|
|||
D::open(self.weak_self.upgrade().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
struct TtyFile<D>(Arc<Tty<D>>);
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl<D: TtyDriver> Pollable for TtyFile<D> {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents;
|
||||
}
|
||||
|
||||
impl<D: TtyDriver> InodeIo for TtyFile<D> {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
self.0.read(writer, status_flags)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
self.0.write(reader, status_flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl<D: TtyDriver> FileIo for TtyFile<D> {
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
|
||||
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,297 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, format, sync::Arc};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use aster_framebuffer::DummyFramebufferConsole;
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
use ostd::mm::{Infallible, VmReader, VmWriter};
|
||||
use spin::Once;
|
||||
|
||||
use super::{Tty, TtyDriver};
|
||||
use crate::{
|
||||
device::{registry::char, tty::termio::CTermios},
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::ioctl::RawIoctl,
|
||||
};
|
||||
|
||||
/// The driver for VT (virtual terminal) devices.
|
||||
//
|
||||
// TODO: This driver needs to support more features for future VT management.
|
||||
#[derive(Clone)]
|
||||
pub struct VtDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl TtyDriver for VtDriver {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/major.h#L18>.
|
||||
const DEVICE_MAJOR_ID: u32 = 4;
|
||||
|
||||
fn devtmpfs_path(&self, index: u32) -> Option<String> {
|
||||
Some(format!("tty{}", index))
|
||||
}
|
||||
|
||||
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(TtyFile(tty)))
|
||||
}
|
||||
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
|
||||
fn can_push(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
Some(&*self.console)
|
||||
}
|
||||
|
||||
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
|
||||
}
|
||||
|
||||
/// The driver for serial devices.
|
||||
#[derive(Clone)]
|
||||
pub struct SerialDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl SerialDriver {
|
||||
const MINOR_ID_BASE: u32 = 64;
|
||||
}
|
||||
|
||||
impl TtyDriver for SerialDriver {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/major.h#L18>.
|
||||
const DEVICE_MAJOR_ID: u32 = 4;
|
||||
|
||||
fn devtmpfs_path(&self, index: u32) -> Option<String> {
|
||||
Some(format!("ttyS{}", index - Self::MINOR_ID_BASE))
|
||||
}
|
||||
|
||||
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(TtyFile(tty)))
|
||||
}
|
||||
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
|
||||
fn can_push(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
Some(&*self.console)
|
||||
}
|
||||
|
||||
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
|
||||
}
|
||||
|
||||
/// The driver for hypervisor console devices.
|
||||
#[derive(Clone)]
|
||||
pub struct HvcDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl TtyDriver for HvcDriver {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/Documentation/admin-guide/devices.txt#L2936>.
|
||||
const DEVICE_MAJOR_ID: u32 = 229;
|
||||
|
||||
fn devtmpfs_path(&self, index: u32) -> Option<String> {
|
||||
Some(format!("hvc{}", index))
|
||||
}
|
||||
|
||||
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(TtyFile(tty)))
|
||||
}
|
||||
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
|
||||
fn can_push(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
Some(&*self.console)
|
||||
}
|
||||
|
||||
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
|
||||
}
|
||||
|
||||
struct TtyFile<D>(Arc<Tty<D>>);
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl<D: TtyDriver> Pollable for TtyFile<D> {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents;
|
||||
}
|
||||
|
||||
impl<D: TtyDriver> InodeIo for TtyFile<D> {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
self.0.read(writer, status_flags)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
reader: &mut VmReader,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
self.0.write(reader, status_flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.0")]
|
||||
impl<D: TtyDriver> FileIo for TtyFile<D> {
|
||||
fn ioctl(&self, raw_ioctl: RawIoctl) -> Result<i32>;
|
||||
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
static TTY1: Once<Arc<Tty<VtDriver>>> = Once::new();
|
||||
|
||||
static SERIAL0: Once<Arc<Tty<SerialDriver>>> = Once::new();
|
||||
|
||||
static HVC0: Once<Arc<Tty<HvcDriver>>> = Once::new();
|
||||
|
||||
/// Returns the `tty1` device.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the `tty1` device has not been initialized.
|
||||
pub fn tty1_device() -> &'static Arc<Tty<VtDriver>> {
|
||||
TTY1.get().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the `ttyS0` device.
|
||||
///
|
||||
/// Returns `None` if the device is not found nor initialized.
|
||||
pub fn serial0_device() -> Option<&'static Arc<Tty<SerialDriver>>> {
|
||||
SERIAL0.get()
|
||||
}
|
||||
|
||||
/// Returns the `hvc0` device.
|
||||
///
|
||||
/// Returns `None` if the device is not found nor initialized.
|
||||
pub fn hvc0_device() -> Option<&'static Arc<Tty<HvcDriver>>> {
|
||||
HVC0.get()
|
||||
}
|
||||
|
||||
pub(super) fn init_in_first_process() -> Result<()> {
|
||||
let devices = aster_console::all_devices();
|
||||
|
||||
// Initialize the `tty1` device.
|
||||
|
||||
let fb_console = devices
|
||||
.iter()
|
||||
.find(|(name, _)| name.as_str() == aster_framebuffer::CONSOLE_NAME)
|
||||
.map(|(_, device)| device.clone())
|
||||
.unwrap_or_else(|| Arc::new(DummyFramebufferConsole));
|
||||
|
||||
let driver = VtDriver {
|
||||
console: fb_console.clone(),
|
||||
};
|
||||
let tty1 = Tty::new(1, driver);
|
||||
|
||||
TTY1.call_once(|| tty1.clone());
|
||||
char::register(tty1.clone())?;
|
||||
|
||||
fb_console.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()));
|
||||
let _ = tty1.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
|
||||
// Initialize the `ttyS0` device if the serial console is available.
|
||||
|
||||
let serial_console = devices
|
||||
.iter()
|
||||
.find(|(name, _)| name.as_str() == aster_uart::CONSOLE_NAME)
|
||||
.map(|(_, device)| device.clone());
|
||||
|
||||
if let Some(serial_console) = serial_console {
|
||||
let driver = SerialDriver {
|
||||
console: serial_console.clone(),
|
||||
};
|
||||
let serial0 = Tty::new(SerialDriver::MINOR_ID_BASE, driver);
|
||||
|
||||
SERIAL0.call_once(|| serial0.clone());
|
||||
char::register(serial0.clone())?;
|
||||
|
||||
serial_console.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()));
|
||||
let _ = serial0.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
}
|
||||
|
||||
// Initialize the `hvc0` device if the virtio console is available.
|
||||
|
||||
let virtio_console = devices
|
||||
.iter()
|
||||
.find(|(name, _)| name.as_str() == aster_virtio::device::console::DEVICE_NAME)
|
||||
.map(|(_, device)| device.clone());
|
||||
|
||||
if let Some(virtio_console) = virtio_console {
|
||||
let driver = HvcDriver {
|
||||
console: virtio_console.clone(),
|
||||
};
|
||||
let hvc0 = Tty::new(0, driver);
|
||||
|
||||
HVC0.call_once(|| hvc0.clone());
|
||||
char::register(hvc0.clone())?;
|
||||
|
||||
virtio_console.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()));
|
||||
let _ = hvc0.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, format, sync::Arc};
|
||||
|
||||
use aster_console::AnyConsoleDevice;
|
||||
use ostd::mm::{Infallible, VmReader, VmWriter};
|
||||
use spin::Once;
|
||||
|
||||
use super::{Tty, TtyDriver};
|
||||
use crate::{
|
||||
device::{
|
||||
registry::char,
|
||||
tty::{TtyFile, termio::CTermios},
|
||||
},
|
||||
fs::inode_handle::FileIo,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// The driver for serial devices.
|
||||
#[derive(Clone)]
|
||||
pub struct SerialDriver {
|
||||
console: Arc<dyn AnyConsoleDevice>,
|
||||
}
|
||||
|
||||
impl SerialDriver {
|
||||
const MINOR_ID_BASE: u32 = 64;
|
||||
}
|
||||
|
||||
impl TtyDriver for SerialDriver {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/major.h#L18>.
|
||||
const DEVICE_MAJOR_ID: u32 = 4;
|
||||
|
||||
fn devtmpfs_path(&self, index: u32) -> Option<String> {
|
||||
Some(format!("ttyS{}", index - Self::MINOR_ID_BASE))
|
||||
}
|
||||
|
||||
fn open(tty: Arc<Tty<Self>>) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(TtyFile(tty)))
|
||||
}
|
||||
|
||||
fn push_output(&self, chs: &[u8]) -> Result<usize> {
|
||||
self.console.send(chs);
|
||||
Ok(chs.len())
|
||||
}
|
||||
|
||||
fn echo_callback(&self) -> impl FnMut(&[u8]) + '_ {
|
||||
|chs| self.console.send(chs)
|
||||
}
|
||||
|
||||
fn can_push(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn notify_input(&self) {}
|
||||
|
||||
fn console(&self) -> Option<&dyn AnyConsoleDevice> {
|
||||
Some(&*self.console)
|
||||
}
|
||||
|
||||
fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {}
|
||||
}
|
||||
|
||||
static SERIAL0: Once<Arc<Tty<SerialDriver>>> = Once::new();
|
||||
|
||||
/// Returns the `ttyS0` device.
|
||||
///
|
||||
/// Returns `None` if the device is not found nor initialized.
|
||||
pub fn serial0_device() -> Option<&'static Arc<Tty<SerialDriver>>> {
|
||||
SERIAL0.get()
|
||||
}
|
||||
|
||||
pub(super) fn init_in_first_process() -> Result<()> {
|
||||
let devices = aster_console::all_devices();
|
||||
|
||||
// Initialize the `ttyS0` device if the serial console is available.
|
||||
|
||||
let serial_console = devices
|
||||
.iter()
|
||||
.find(|(name, _)| name.as_str() == aster_uart::CONSOLE_NAME)
|
||||
.map(|(_, device)| device.clone());
|
||||
|
||||
if let Some(serial_console) = serial_console {
|
||||
let driver = SerialDriver {
|
||||
console: serial_console.clone(),
|
||||
};
|
||||
let serial0 = Tty::new(SerialDriver::MINOR_ID_BASE, driver);
|
||||
|
||||
SERIAL0.call_once(|| serial0.clone());
|
||||
char::register(serial0.clone())?;
|
||||
|
||||
serial_console.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()));
|
||||
let _ = serial0.push_input(chs.as_slice());
|
||||
},
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Reference in New Issue