// SPDX-License-Identifier: MPL-2.0 //! The console device of Asterinas. #![no_std] #![deny(unsafe_code)] extern crate alloc; pub mod font; pub mod mode; use alloc::{collections::BTreeMap, fmt::Debug, string::String, sync::Arc, vec::Vec}; use core::any::Any; use component::{ComponentInitError, init_component}; use ostd::{ mm::{Infallible, VmReader}, sync::{LocalIrqDisabled, SpinLock, SpinLockGuard}, }; use spin::Once; pub type ConsoleCallback = dyn Fn(VmReader) + Send + Sync; /// An error returned by [`AnyConsoleDevice::set_font`]. #[derive(Debug, Clone, Copy)] pub enum ConsoleSetFontError { InappropriateDevice, InvalidFont, } pub trait AnyConsoleDevice: Send + Sync + Any + Debug { /// Sends data to the console device. fn send(&self, buf: &[u8]); /// Registers a callback that will be invoked when the console device receives data. /// /// The callback may be called in the interrupt context. Therefore, it should _never_ sleep. fn register_callback(&self, callback: &'static ConsoleCallback); /// Sets the font of the console device. fn set_font(&self, _font: font::BitmapFont) -> Result<(), ConsoleSetFontError> { Err(ConsoleSetFontError::InappropriateDevice) } // TODO: Add support for getting the font of the console device. /// Sets the console mode (text or graphics, see [`mode::ConsoleMode`]). /// /// Returns true if the mode was changed, false if the mode is not supported. #[must_use] fn set_mode(&self, _mode: mode::ConsoleMode) -> bool { false } /// Gets the current console mode. /// /// Returns the current console mode, or `None` if mode switching is not supported. fn mode(&self) -> Option { None } /// Sets the keyboard mode (see [`mode::KeyboardMode`]). /// /// Returns true if the mode was changed, false if the mode is not supported. #[must_use] fn set_keyboard_mode(&self, _mode: mode::KeyboardMode) -> bool { false } /// Gets the current keyboard mode. /// /// Returns the current keyboard mode, or `None` if mode switching is not supported. fn keyboard_mode(&self) -> Option { None } } pub fn register_device(name: String, device: Arc) { COMPONENT .get() .unwrap() .console_device_table .lock() .insert(name, device); } pub fn all_devices() -> Vec<(String, Arc)> { let console_devices = COMPONENT.get().unwrap().console_device_table.lock(); console_devices .iter() .map(|(name, device)| (name.clone(), device.clone())) .collect() } pub fn all_devices_lock<'a>() -> SpinLockGuard<'a, BTreeMap>, LocalIrqDisabled> { COMPONENT.get().unwrap().console_device_table.lock() } static COMPONENT: Once = Once::new(); #[init_component] fn component_init() -> Result<(), ComponentInitError> { let component = Component::init()?; COMPONENT.call_once(|| component); Ok(()) } #[derive(Debug)] struct Component { console_device_table: SpinLock>, LocalIrqDisabled>, } impl Component { pub fn init() -> Result { Ok(Self { console_device_table: SpinLock::new(BTreeMap::new()), }) } }