diff --git a/kernel/src/device/tty/device.rs b/kernel/src/device/tty/device.rs index 164b17c22..dfcaaedb1 100644 --- a/kernel/src/device/tty/device.rs +++ b/kernel/src/device/tty/device.rs @@ -16,7 +16,7 @@ use crate::{ registry::char, tty::{ Tty, - n_tty::{VtDriver, hvc0_device}, + n_tty::{VtDriver, hvc0_device, serial0_device}, }, }, fs::{ @@ -114,6 +114,7 @@ impl SystemConsole { let device = match console_name { "tty0" => Some(Arc::new(Tty0Device) as _), + "ttyS0" => serial0_device().cloned().map(|device| device as _), "hvc0" => hvc0_device().cloned().map(|device| device as _), _ => None, }; diff --git a/kernel/src/device/tty/n_tty.rs b/kernel/src/device/tty/n_tty.rs index 7ade4a336..c68fc7e12 100644 --- a/kernel/src/device/tty/n_tty.rs +++ b/kernel/src/device/tty/n_tty.rs @@ -63,6 +63,50 @@ impl TtyDriver for VtDriver { fn on_termios_change(&self, _old_termios: &CTermios, _new_termios: &CTermios) {} } +/// The driver for serial devices. +#[derive(Clone)] +pub struct SerialDriver { + console: Arc, +} + +impl SerialDriver { + const MINOR_ID_BASE: u32 = 64; +} + +impl TtyDriver for SerialDriver { + // Reference: . + const DEVICE_MAJOR_ID: u32 = 4; + + fn devtmpfs_path(&self, index: u32) -> Option { + Some(format!("ttyS{}", index - Self::MINOR_ID_BASE)) + } + + fn open(tty: Arc>) -> Result> { + Ok(Box::new(TtyFile(tty))) + } + + fn push_output(&self, chs: &[u8]) -> Result { + 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 { @@ -145,6 +189,8 @@ impl FileIo for TtyFile { static TTY1: Once>> = Once::new(); +static SERIAL0: Once>> = Once::new(); + static HVC0: Once>> = Once::new(); /// Returns the `tty1` device. @@ -156,6 +202,13 @@ pub fn tty1_device() -> &'static Arc> { TTY1.get().unwrap() } +/// Returns the `ttyS0` device. +/// +/// Returns `None` if the device is not found nor initialized. +pub fn serial0_device() -> Option<&'static Arc>> { + SERIAL0.get() +} + /// Returns the `hvc0` device. /// /// Returns `None` if the device is not found nor initialized. @@ -190,6 +243,31 @@ pub(super) fn init_in_first_process() -> Result<()> { }, ))); + // 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| { + 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