From 9c062b1350abd5205c61555ceaeeeacccad4fd9e Mon Sep 17 00:00:00 2001 From: Cautreoxit Date: Fri, 22 Aug 2025 16:08:23 +0800 Subject: [PATCH] Add input subsystem Co-authored-by: TinaZhangZW --- Cargo.lock | 4 +- kernel/comps/input/Cargo.toml | 4 +- kernel/comps/input/src/event_type_codes.rs | 360 ++++++++++++++++ kernel/comps/input/src/input_core.rs | 197 +++++++++ kernel/comps/input/src/input_dev.rs | 463 +++++++++++++++++++++ kernel/comps/input/src/input_handler.rs | 73 ++++ kernel/comps/input/src/key.rs | 128 ------ kernel/comps/input/src/lib.rs | 125 ++++-- kernel/src/driver/mod.rs | 4 +- 9 files changed, 1190 insertions(+), 168 deletions(-) create mode 100644 kernel/comps/input/src/event_type_codes.rs create mode 100644 kernel/comps/input/src/input_core.rs create mode 100644 kernel/comps/input/src/input_dev.rs create mode 100644 kernel/comps/input/src/input_handler.rs delete mode 100644 kernel/comps/input/src/key.rs diff --git a/Cargo.lock b/Cargo.lock index 5440501df..1b3ac7a1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,8 +131,10 @@ dependencies = [ name = "aster-input" version = "0.1.0" dependencies = [ + "bitflags 1.3.2", + "bitvec", "component", - "int-to-c-enum", + "log", "ostd", "spin", ] diff --git a/kernel/comps/input/Cargo.toml b/kernel/comps/input/Cargo.toml index a220c8e8a..7da21886b 100644 --- a/kernel/comps/input/Cargo.toml +++ b/kernel/comps/input/Cargo.toml @@ -7,9 +7,11 @@ edition = "2021" [dependencies] spin = "0.9.4" +log = "0.4" ostd = { path = "../../../ostd" } component = { path = "../../libs/comp-sys/component" } -int-to-c-enum = { path = "../../libs/int-to-c-enum" } +bitvec = { version = "1.0", default-features = false, features = ["alloc"] } +bitflags = "1.3" [lints] workspace = true diff --git a/kernel/comps/input/src/event_type_codes.rs b/kernel/comps/input/src/event_type_codes.rs new file mode 100644 index 000000000..441362cbf --- /dev/null +++ b/kernel/comps/input/src/event_type_codes.rs @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Event types and codes. +//! +//! This file is based on the USB HUT 1.12 specification +//! (see ) and follows the +//! Linux input event codes standard for maximum compatibility. +//! +//! The USB HUT standard ensures consistent event type and value +//! definitions across different input devices and platforms. + +use bitflags::bitflags; +use bitvec::prelude::*; + +bitflags! { + /// Input event types. + pub struct EventTypes: u32 { + /// Synchronization events. + const SYN = 1 << 0x00; + /// Key press/release events. + const KEY = 1 << 0x01; + /// Relative movement events. (mouse, trackball, etc.) + const REL = 1 << 0x02; + /// Absolute position events. (touchpad, tablet, etc.) + const ABS = 1 << 0x03; + /// Miscellaneous events. + const MSC = 1 << 0x04; + /// Switch events. + const SW = 1 << 0x05; + /// LED events. + const LED = 1 << 0x11; + /// Sound events. + const SND = 1 << 0x12; + /// Repeat events. + const REP = 1 << 0x14; + /// Force feedback events. + const FF = 1 << 0x15; + /// Power management events. + const PWR = 1 << 0x16; + /// Force feedback status events. + const FF_STATUS = 1 << 0x17; + } +} + +impl Default for EventTypes { + fn default() -> Self { + Self::new() + } +} + +impl EventTypes { + /// Creates a new empty set of event type flags. + pub const fn new() -> Self { + Self::empty() + } + + /// Gets the event index as a `u16` value for this event type. + /// + /// # Panics + /// + /// This method will panic if there are multiple event bits set in `self`. + pub const fn as_index(&self) -> u16 { + // Check if exactly one bit is set + let bits = self.bits(); + assert!( + bits != 0 && (bits & (bits - 1)) == 0, + "`EventTypes::as_index` expects exactly one event bit to be set" + ); + + match *self { + Self::SYN => 0x00, + Self::KEY => 0x01, + Self::REL => 0x02, + Self::ABS => 0x03, + Self::MSC => 0x04, + Self::SW => 0x05, + Self::LED => 0x11, + Self::SND => 0x12, + Self::REP => 0x14, + Self::FF => 0x15, + Self::PWR => 0x16, + Self::FF_STATUS => 0x17, + _ => unreachable!(), + } + } +} + +/// Synchronization events. +#[repr(u16)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SynEvent { + Report = 0x00, + Config = 0x01, + MtReport = 0x02, + Dropped = 0x03, +} + +/// Relative axes. +#[repr(u16)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RelCode { + X = 0x00, + Y = 0x01, + Z = 0x02, + Rx = 0x03, + Ry = 0x04, + Rz = 0x05, + HWheel = 0x06, + Dial = 0x07, + Wheel = 0x08, + Misc = 0x09, + Reserved = 0x0a, + WheelHiRes = 0x0b, + HWheelHiRes = 0x0c, +} +/// The maximum value for relative axes. +const REL_MAX: usize = 0x0f; +/// The number of relative axes. +const REL_COUNT: usize = REL_MAX + 1; + +/// A set of [`RelCode`] represented as a bitmap. +#[derive(Debug, Clone)] +pub struct RelCodeSet(BitVec); + +impl Default for RelCodeSet { + fn default() -> Self { + Self::new() + } +} + +impl RelCodeSet { + /// Creates an empty set. + pub fn new() -> Self { + Self(BitVec::repeat(false, REL_COUNT)) + } + + /// Sets a relative code in the set. + pub fn set(&mut self, rel_code: RelCode) { + let index = rel_code as usize; + self.0.set(index, true); + } + + /// Clears a relative code from the set. + pub fn clear(&mut self, rel_code: RelCode) { + let index = rel_code as usize; + self.0.set(index, false); + } + + /// Checks if the set contains a relative code. + pub fn contain(&self, rel_code: RelCode) -> bool { + let index = rel_code as usize; + self.0.get(index).map(|b| *b).unwrap() + } +} + +/// A set of [`KeyCode`] represented as a bitmap. +#[derive(Debug, Clone)] +pub struct KeyCodeSet(BitVec); + +impl Default for KeyCodeSet { + fn default() -> Self { + Self::new() + } +} + +impl KeyCodeSet { + /// Creates an empty set. + pub fn new() -> Self { + Self(BitVec::repeat(false, KEY_COUNT)) + } + + /// Sets a key code in the set. + pub fn set(&mut self, key_code: KeyCode) { + let index = key_code as usize; + self.0.set(index, true); + } + + /// Clears a key code from the set. + pub fn clear(&mut self, key_code: KeyCode) { + let index = key_code as usize; + self.0.set(index, false); + } + + /// Checks if the set contains a key code. + pub fn contain(&self, key_code: KeyCode) -> bool { + let index = key_code as usize; + self.0.get(index).map(|b| *b).unwrap() + } + + /// Checks if the set contains any key codes in the `range`. + /// + /// # Panics + /// + /// This method will panic if the `range` contains invalid key codes. + pub fn contain_any(&self, range: core::ops::Range) -> bool { + assert!(range.is_empty() || range.end <= KEY_COUNT); + range.into_iter().any(|i| *self.0.get(i).unwrap()) + } +} + +/// Common keyboard and mouse keys. +// TODO: Add more uncommon key codes. +#[repr(u16)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KeyCode { + // Reserved key. + Reserved = 0, + + // Function keys. + Esc = 1, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + F11 = 87, + F12 = 88, + + // Number row. + Num1 = 2, + Num2 = 3, + Num3 = 4, + Num4 = 5, + Num5 = 6, + Num6 = 7, + Num7 = 8, + Num8 = 9, + Num9 = 10, + Num0 = 11, + Minus = 12, + Equal = 13, + Backspace = 14, + + // First row (QWERTY). + Tab = 15, + Q = 16, + W = 17, + E = 18, + R = 19, + T = 20, + Y = 21, + U = 22, + I = 23, + O = 24, + P = 25, + LeftBrace = 26, // [ + RightBrace = 27, // ] + Backslash = 43, // \ + + // Second row (ASDF). + CapsLock = 58, + A = 30, + S = 31, + D = 32, + F = 33, + G = 34, + H = 35, + J = 36, + K = 37, + L = 38, + Semicolon = 39, // ; + Apostrophe = 40, // ' + Enter = 28, + + // Third row (ZXCV). + LeftShift = 42, + Z = 44, + X = 45, + C = 46, + V = 47, + B = 48, + N = 49, + M = 50, + Comma = 51, + Dot = 52, + Slash = 53, // / + RightShift = 54, + + // Bottom row. + LeftCtrl = 29, + LeftAlt = 56, + Space = 57, + RightAlt = 100, + RightCtrl = 97, + + // Special keys. + Grave = 41, // ` + LeftMeta = 125, // Windows/Cmd key + RightMeta = 126, + Menu = 139, // Context menu key + + // Arrow keys. + Up = 103, + Down = 108, + Left = 105, + Right = 106, + + // Navigation cluster. + Home = 102, + End = 107, + PageUp = 104, + PageDown = 109, + Insert = 110, + Delete = 111, + + // Common modifier states. + NumLock = 69, + ScrollLock = 70, + + // Numpad. + Kp0 = 82, + Kp1 = 79, + Kp2 = 80, + Kp3 = 81, + Kp4 = 75, + Kp5 = 76, + Kp6 = 77, + Kp7 = 71, + Kp8 = 72, + Kp9 = 73, + KpDot = 83, + KpPlus = 78, + KpMinus = 74, + KpAsterisk = 55, // * + KpSlash = 98, // / + KpEnter = 96, + + // Common media keys. + Mute = 113, + VolumeDown = 114, + VolumeUp = 115, + + // Starting code of button events. + BtnMisc = 0x100, + + // Mouse buttons. + BtnLeft = 0x110, + BtnRight = 0x111, + BtnMiddle = 0x112, + BtnSide = 0x113, // Mouse side button + BtnExtra = 0x114, // Mouse extra button + BtnForward = 0x115, // Mouse forward button + BtnBack = 0x116, // Mouse back button +} + +/// The maximum value for key codes. +const KEY_MAX: usize = 0x120; +/// The number of key codes. +const KEY_COUNT: usize = KEY_MAX + 1; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KeyStatus { + Released, + Pressed, +} diff --git a/kernel/comps/input/src/input_core.rs b/kernel/comps/input/src/input_core.rs new file mode 100644 index 000000000..7e314e2f9 --- /dev/null +++ b/kernel/comps/input/src/input_core.rs @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::{sync::Arc, vec::Vec}; +use core::fmt::Debug; + +use ostd::sync::{RwLock, WriteIrqDisabled}; + +use crate::{ + input_dev::RegisteredInputDevice, + input_handler::{BoundInputHandler, InputHandlerClass}, + InputDevice, +}; + +/// Registry entry for each registered device. +/// +/// This serves as the connection point between devices and their handlers. +#[derive(Debug)] +struct InputDeviceRegistry { + /// The input device. + device: Arc, + /// Handlers connected to this device. + handlers: Arc, WriteIrqDisabled>>, +} + +/// The core component of the input subsystem. +/// +/// `InputCore` manages all registered input devices and handler classes. +/// It coordinates the connection between devices and handlers, and routes +/// input events from devices to their associated handlers. +#[derive(Debug)] +pub(crate) struct InputCore { + /// All registered devices with their associated handlers. + devices: Vec, + /// All registered handler classes that can connect to devices. + handler_classes: Vec>, +} + +impl InputCore { + /// Creates a new input core. + pub(crate) fn new() -> Self { + Self { + devices: Vec::new(), + handler_classes: Vec::new(), + } + } + + /// Registers a new handler class. + pub(crate) fn register_handler_class(&mut self, handler_class: Arc) { + // Connect to all existing devices + for device_registry in self.devices.iter() { + match handler_class.connect(device_registry.device.clone()) { + Ok(handler) => { + device_registry.handlers.write().push(BoundInputHandler { + handler, + handler_class: handler_class.clone(), + }); + log::info!( + "Input: successfully connected handler class {} to device {}", + handler_class.name(), + device_registry.device.name() + ); + } + Err(e) => { + log::info!( + "Input: failed to connect handler class {} to device {}: {:?}", + handler_class.name(), + device_registry.device.name(), + e + ); + } + } + } + + log::info!("Input: registered handler class {}", handler_class.name()); + self.handler_classes.push(handler_class); + } + + /// Unregisters a handler class. + pub(crate) fn unregister_handler_class( + &mut self, + handler_class: &Arc, + ) -> Option> { + // Find the handler class and remove it. + let pos = self + .handler_classes + .iter() + .position(|h| Arc::ptr_eq(h, handler_class))?; + let handler_class = self.handler_classes.swap_remove(pos); + + for device_registry in self.devices.iter() { + let mut handlers = device_registry.handlers.write(); + let Some(pos) = handlers + .iter() + .position(|h| Arc::ptr_eq(&h.handler_class, &handler_class)) + else { + continue; + }; + let handler = handlers.swap_remove(pos); + drop(handlers); + drop(handler); + + handler_class.disconnect(&device_registry.device); + } + + log::info!("Input: unregistered handler class {}", handler_class.name()); + Some(handler_class) + } + + /// Registers a new input device. + pub(crate) fn register_device( + &mut self, + device: Arc, + ) -> RegisteredInputDevice { + // Connect all existing handler classes. + let mut connected_handlers = Vec::new(); + for handler_class in self.handler_classes.iter() { + match handler_class.connect(device.clone()) { + Ok(handler) => { + connected_handlers.push(BoundInputHandler { + handler, + handler_class: handler_class.clone(), + }); + log::info!( + "Input: successfully connected handler class {} to device {}", + handler_class.name(), + device.name() + ); + } + Err(e) => { + log::info!( + "Input: failed to connect handler class {} to device {}: {:?}", + handler_class.name(), + device.name(), + e + ); + } + } + } + let handlers = Arc::new(RwLock::new(connected_handlers)); + + // Add the device registry. + let new_registry = InputDeviceRegistry { + device: device.clone(), + handlers: handlers.clone(), + }; + self.devices.push(new_registry); + + log::info!("Input: registered device {}", device.name()); + RegisteredInputDevice::new(device, handlers) + } + + /// Unregisters an input device. + pub(crate) fn unregister_device( + &mut self, + device: &Arc, + ) -> Option> { + // Find the device and remove it. + let pos = self + .devices + .iter() + .position(|registry| Arc::ptr_eq(®istry.device, device))?; + let device_registry = self.devices.swap_remove(pos); + + // Take all handlers connected to this device and clear the list. + let mut handlers = device_registry.handlers.write(); + let bound_handlers = core::mem::take(&mut *handlers); + drop(handlers); + + // Disconnect handler classes that were connected. + for bound_handler in bound_handlers.into_iter() { + bound_handler + .handler_class + .disconnect(&device_registry.device); + } + + log::info!("Input: unregistered device {}", device.name()); + Some(device_registry.device) + } + + /// Counts the number of registered devices. + pub(crate) fn count_devices(&self) -> usize { + self.devices.len() + } + + /// Counts the number of registered handler classes. + pub(crate) fn count_handler_class(&self) -> usize { + self.handler_classes.len() + } + + /// Gets all registered devices. + pub(crate) fn all_devices(&self) -> Vec> { + self.devices + .iter() + .map(|registry| registry.device.clone()) + .collect() + } +} diff --git a/kernel/comps/input/src/input_dev.rs b/kernel/comps/input/src/input_dev.rs new file mode 100644 index 000000000..a2b90ce35 --- /dev/null +++ b/kernel/comps/input/src/input_dev.rs @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::{sync::Arc, vec::Vec}; +use core::{any::Any, fmt::Debug}; + +use ostd::{ + sync::{RwLock, WriteIrqDisabled}, + Pod, +}; + +use crate::{ + event_type_codes::{EventTypes, KeyCode, KeyCodeSet, KeyStatus, RelCode, RelCodeSet, SynEvent}, + input_handler::BoundInputHandler, + unregister_device, +}; + +/// Input event. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InputEvent { + /// Synchronization events (EV_SYN) + Sync(SynEvent), + /// Key press/release events (EV_KEY) + Key(KeyCode, KeyStatus), + /// Relative movement events (EV_REL) + Relative(RelCode, i32), + // TODO: Add EV_ABS, EV_MSC, EV_SW, EV_LED, EV_SND, ... as needed +} + +impl InputEvent { + /// Creates a synchronization event. + pub fn from_sync_event(sync_type: SynEvent) -> Self { + Self::Sync(sync_type) + } + + /// Creates a key event. + pub fn from_key_and_status(key: KeyCode, status: KeyStatus) -> Self { + Self::Key(key, status) + } + + /// Creates a relative movement event. + pub fn from_relative_move(axis: RelCode, value: i32) -> Self { + Self::Relative(axis, value) + } + + /// Converts enum to raw Linux input event triplet (type, code, value). + pub fn to_raw(&self) -> (u16, u16, i32) { + match self { + InputEvent::Sync(sync_type) => ( + EventTypes::SYN.as_index(), + *sync_type as u16, + 0, // Sync events always have value = 0 + ), + InputEvent::Key(key, status) => { + (EventTypes::KEY.as_index(), *key as u16, *status as i32) + } + InputEvent::Relative(axis, value) => (EventTypes::REL.as_index(), *axis as u16, *value), + } + } + + /// Gets the event type. + pub fn event_type(&self) -> EventTypes { + match self { + InputEvent::Sync(_) => EventTypes::SYN, + InputEvent::Key(_, _) => EventTypes::KEY, + InputEvent::Relative(_, _) => EventTypes::REL, + } + } +} + +/// Input device identifier. +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +pub struct InputId { + /// Bus type identifier. + bustype: u16, + /// Vendor ID of the device manufacturer. + vendor: u16, + /// Product ID of the specific device model. + product: u16, + /// Version number of the device. + version: u16, +} + +impl InputId { + /// Creates a new `InputId` with the specified values. + pub fn new(bustype: u16, vendor: u16, product: u16, version: u16) -> Self { + Self { + bustype, + vendor, + product, + version, + } + } + + /// Gets the bus type. + pub fn bustype(&self) -> u16 { + self.bustype + } + + /// Gets the vendor ID. + pub fn vendor(&self) -> u16 { + self.vendor + } + + /// Gets the product ID. + pub fn product(&self) -> u16 { + self.product + } + + /// Gets the version number. + pub fn version(&self) -> u16 { + self.version + } + + // Common bus types. + pub const BUS_PCI: u16 = 0x01; + pub const BUS_ISAPNP: u16 = 0x02; + pub const BUS_USB: u16 = 0x03; + pub const BUS_HIL: u16 = 0x04; + pub const BUS_BLUETOOTH: u16 = 0x05; + pub const BUS_VIRTUAL: u16 = 0x06; + pub const BUS_ISA: u16 = 0x10; + pub const BUS_I8042: u16 = 0x11; + pub const BUS_XTKBD: u16 = 0x12; + pub const BUS_RS232: u16 = 0x13; + pub const BUS_GAMEPORT: u16 = 0x14; + pub const BUS_PARPORT: u16 = 0x15; + pub const BUS_AMIGA: u16 = 0x16; + pub const BUS_ADB: u16 = 0x17; + pub const BUS_I2C: u16 = 0x18; + pub const BUS_HOST: u16 = 0x19; + pub const BUS_GSC: u16 = 0x1A; + pub const BUS_ATARI: u16 = 0x1B; + pub const BUS_SPI: u16 = 0x1C; + pub const BUS_RMI: u16 = 0x1D; + pub const BUS_CEC: u16 = 0x1E; + pub const BUS_INTEL_ISHTP: u16 = 0x1F; +} + +/// Input device capability bitmaps. +#[derive(Debug, Clone)] +pub struct InputCapability { + /// Supported event types (`EV_KEY`, `EV_REL`, etc.) + supported_event_types: EventTypes, + /// Supported key/button codes. + supported_keys: KeyCodeSet, + /// Supported relative axis codes. + supported_relative_axes: RelCodeSet, + // TODO: Add supported_absolute_axes, supported_misc, etc. +} + +impl Default for InputCapability { + fn default() -> Self { + Self::new() + } +} + +impl InputCapability { + /// Creates a new empty input capability. + pub fn new() -> Self { + Self { + supported_event_types: EventTypes::new(), + supported_keys: KeyCodeSet::new(), + supported_relative_axes: RelCodeSet::new(), + } + } + + /// Sets event type capability. + pub fn set_supported_event_type(&mut self, event_type: EventTypes) { + self.supported_event_types |= event_type; + } + + /// Checks if an event type is supported. + pub fn support_event_type(&self, event_type: EventTypes) -> bool { + self.supported_event_types.contains(event_type) + } + + /// Removes support for an event type. + pub fn clear_supported_event_type(&mut self, event_type: EventTypes) { + self.supported_event_types &= !event_type; + } + + /// Sets key capability. + pub fn set_supported_key(&mut self, key_code: KeyCode) { + self.supported_keys.set(key_code); + self.set_supported_event_type(EventTypes::KEY); + } + + /// Checks if a key code is supported. + pub fn support_key(&self, key_code: KeyCode) -> bool { + self.supported_keys.contain(key_code) + } + + /// Detects whether the device is keyboard-like. + /// + /// We follow the rules defined by Linux: a device is a keyboard if + /// - it supports `EV_KEY`, and + /// - it has any non-button key set (i.e., below `BTN_MISC`). + pub fn look_like_keyboard(&self) -> bool { + if !self.support_event_type(EventTypes::KEY) { + return false; + } + + self.supported_keys + .contain_any(0..KeyCode::BtnMisc as usize) + } + + /// Removes support for a key code. + pub fn clear_supported_key(&mut self, key_code: KeyCode) { + self.supported_keys.clear(key_code); + } + + /// Sets relative axis capability. + pub fn set_supported_relative_axis(&mut self, rel_code: RelCode) { + self.supported_relative_axes.set(rel_code); + self.set_supported_event_type(EventTypes::REL); + } + + /// Checks if a relative code is supported. + pub fn support_relative_axis(&self, rel_code: RelCode) -> bool { + self.supported_relative_axes.contain(rel_code) + } + + /// Removes support for a relative code. + pub fn clear_supported_relative_axis(&mut self, rel_code: RelCode) { + self.supported_relative_axes.clear(rel_code); + } +} + +pub trait InputDevice: Send + Sync + Any + Debug { + /// Device name. + fn name(&self) -> &str; + + /// Physical location of the device in the system topology. + /// + /// This string describes the physical path through which the device is connected + /// to the system. It helps identify where the device is physically located and + /// how it's connected (e.g., USB port, ISA bus, etc.). + /// + /// # Examples + /// - `"isa0060/serio0/input0"` - i8042 keyboard connected via ISA bus + /// - `"usb-0000:00:1d.0-1/input0"` - USB device connected to specific USB port + fn phys(&self) -> &str; + + /// Unique identifier for the device instance. + /// + /// This string provides a unique identifier for the specific device instance, + /// typically derived from device-specific information like serial numbers, + /// MAC addresses, or other hardware-level unique identifiers. + /// + /// # Examples + /// - `"00:1B:DC:0F:AC:27"` - MAC address for Bluetooth devices + /// - `"S/N: 12345678"` - Device serial number + /// - `""` - Empty string for devices without unique identifiers + fn uniq(&self) -> &str; + + /// Device ID. + fn id(&self) -> InputId; + + /// Device capabilities. + fn capability(&self) -> &InputCapability; +} + +/// Registered input device that can submit events to handlers. +#[derive(Debug)] +pub struct RegisteredInputDevice { + /// Original device. + device: Arc, + /// Reference to bound handlers for direct event dispatch. + handlers: Arc, WriteIrqDisabled>>, +} + +impl RegisteredInputDevice { + pub(crate) fn new( + device: Arc, + handlers: Arc, WriteIrqDisabled>>, + ) -> Self { + Self { device, handlers } + } + + /// Submits multiple events in batch. + pub fn submit_events(&self, events: &[InputEvent]) { + debug_assert!( + events.iter().all(|e| self.is_event_supported(e)), + "Device '{}' submitted unsupported event", + self.device.name() + ); + + let handlers = self.handlers.read(); + if handlers.is_empty() { + log::debug!( + "Input: dropped events from device {} because it has no handlers", + self.device.name() + ); + return; + } + + for bound_handler in handlers.iter() { + bound_handler.handler.handle_events(events); + } + } + + /// Gets the underlying device reference. + pub fn device(&self) -> &Arc { + &self.device + } + + /// Counts the number of connected handlers. + pub fn count_handlers(&self) -> usize { + self.handlers.read().len() + } + + /// Checks if the device supports a specific event based on its capabilities. + fn is_event_supported(&self, event: &InputEvent) -> bool { + let capability = self.device.capability(); + + match event { + InputEvent::Sync(_) => capability.support_event_type(EventTypes::SYN), + InputEvent::Key(key_event, _) => { + capability.support_event_type(EventTypes::KEY) && capability.support_key(*key_event) + } + InputEvent::Relative(rel_event, _) => { + capability.support_event_type(EventTypes::REL) + && capability.support_relative_axis(*rel_event) + } + } + } +} + +impl InputCapability { + /// Adds all standard keyboard keys to the capability. + pub fn add_standard_keyboard_keys(&mut self) { + let standard_keys = [ + // Function keys + KeyCode::Esc, + KeyCode::F1, + KeyCode::F2, + KeyCode::F3, + KeyCode::F4, + KeyCode::F5, + KeyCode::F6, + KeyCode::F7, + KeyCode::F8, + KeyCode::F9, + KeyCode::F10, + KeyCode::F11, + KeyCode::F12, + // Number row + KeyCode::Num1, + KeyCode::Num2, + KeyCode::Num3, + KeyCode::Num4, + KeyCode::Num5, + KeyCode::Num6, + KeyCode::Num7, + KeyCode::Num8, + KeyCode::Num9, + KeyCode::Num0, + KeyCode::Minus, + KeyCode::Equal, + KeyCode::Backspace, + // First row (QWERTY) + KeyCode::Tab, + KeyCode::Q, + KeyCode::W, + KeyCode::E, + KeyCode::R, + KeyCode::T, + KeyCode::Y, + KeyCode::U, + KeyCode::I, + KeyCode::O, + KeyCode::P, + KeyCode::LeftBrace, + KeyCode::RightBrace, + KeyCode::Backslash, + // Second row (ASDF) + KeyCode::CapsLock, + KeyCode::A, + KeyCode::S, + KeyCode::D, + KeyCode::F, + KeyCode::G, + KeyCode::H, + KeyCode::J, + KeyCode::K, + KeyCode::L, + KeyCode::Semicolon, + KeyCode::Apostrophe, + KeyCode::Enter, + // Third row (ZXCV) + KeyCode::LeftShift, + KeyCode::Z, + KeyCode::X, + KeyCode::C, + KeyCode::V, + KeyCode::B, + KeyCode::N, + KeyCode::M, + KeyCode::Comma, + KeyCode::Dot, + KeyCode::Slash, + KeyCode::RightShift, + // Bottom row + KeyCode::LeftCtrl, + KeyCode::LeftAlt, + KeyCode::Space, + KeyCode::RightAlt, + KeyCode::RightCtrl, + // Special keys + KeyCode::Grave, + KeyCode::LeftMeta, + KeyCode::RightMeta, + KeyCode::Menu, + // Arrow keys + KeyCode::Up, + KeyCode::Down, + KeyCode::Left, + KeyCode::Right, + // Navigation cluster + KeyCode::Home, + KeyCode::End, + KeyCode::PageUp, + KeyCode::PageDown, + KeyCode::Insert, + KeyCode::Delete, + // Lock keys + KeyCode::NumLock, + KeyCode::ScrollLock, + // Numpad + KeyCode::Kp0, + KeyCode::Kp1, + KeyCode::Kp2, + KeyCode::Kp3, + KeyCode::Kp4, + KeyCode::Kp5, + KeyCode::Kp6, + KeyCode::Kp7, + KeyCode::Kp8, + KeyCode::Kp9, + KeyCode::KpDot, + KeyCode::KpPlus, + KeyCode::KpMinus, + KeyCode::KpAsterisk, + KeyCode::KpSlash, + KeyCode::KpEnter, + // Common media keys + KeyCode::Mute, + KeyCode::VolumeDown, + KeyCode::VolumeUp, + ]; + + for key in standard_keys { + self.set_supported_key(key); + } + } +} + +impl Drop for RegisteredInputDevice { + fn drop(&mut self) { + unregister_device(&self.device).unwrap(); + } +} diff --git a/kernel/comps/input/src/input_handler.rs b/kernel/comps/input/src/input_handler.rs new file mode 100644 index 000000000..3b80a0e7b --- /dev/null +++ b/kernel/comps/input/src/input_handler.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::Arc; +use core::{any::Any, fmt::Debug}; + +use crate::{input_dev::InputEvent, unregister_handler_class, InputDevice}; + +/// Errors that can occur when connecting to an input device. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ConnectError { + /// Device is not compatible with this handler class. + IncompatibleDevice, + /// Failed to create device node. + DeviceNodeCreationFailed, + /// Device is already connected. + AlreadyConnected, + /// Other internal error. + InternalError, +} + +/// A trait that represents an input handler class. +/// +/// Once registered to the input core (via [`register_handler_class`]), the +/// input handler class will try to connect to each input device (via +/// [`connect`]). If it succeeds, an [`InputHandler`] will be created to handle +/// the input events from that device. +/// +/// [`register_handler_class`]: crate::register_handler_class +/// [`connect`]: Self::connect +/// [`InputHandler`]: crate::InputHandler +pub trait InputHandlerClass: Send + Sync + Any + Debug { + /// Returns the class name of the handler class. + fn name(&self) -> &str; + + /// Tries to connect to the input device. + /// + /// On success, this method will return `Ok()` with a new input handler. + /// Otherwise, it will return `Err(ConnectError)`. + fn connect(&self, dev: Arc) -> Result, ConnectError>; + + /// Disconnects from a device. + fn disconnect(&self, dev: &Arc); +} + +/// A trait that represents an individual input handler instance for a specific device. +/// +/// Each handler instance is created by an [`InputHandlerClass`] when it successfully +/// connects to an input device (via [`InputHandlerClass::connect`]). The handler +/// is responsible for processing input events from the associated device. +/// +/// [`InputHandlerClass`]: crate::InputHandlerClass +/// [`InputHandlerClass::connect`]: crate::InputHandlerClass::connect +pub trait InputHandler: Send + Sync + Debug { + /// Handles multiple input events from the device. + fn handle_events(&self, events: &[InputEvent]); +} + +/// An input handler bound with the class that created it. +#[derive(Debug, Clone)] +pub(crate) struct BoundInputHandler { + pub(crate) handler: Arc, + pub(crate) handler_class: Arc, +} + +/// Registered input handler class that can create handlers. +#[derive(Debug)] +pub struct RegisteredInputHandlerClass(pub(crate) Arc); + +impl Drop for RegisteredInputHandlerClass { + fn drop(&mut self) { + unregister_handler_class(&self.0).unwrap(); + } +} diff --git a/kernel/comps/input/src/key.rs b/kernel/comps/input/src/key.rs deleted file mode 100644 index 9f394c105..000000000 --- a/kernel/comps/input/src/key.rs +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -use int_to_c_enum::TryFromInt; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, TryFromInt)] -#[repr(u16)] -pub enum Key { - Reserved = 0, - ESC = 1, - One = 2, - Two = 3, - Three = 4, - Four = 5, - Five = 6, - Six = 7, - Seven = 8, - Eight = 9, - Nine = 10, - Zero = 11, - Minus = 12, - Equal = 13, - BackSpace = 14, - Tab = 15, - Q = 16, - W = 17, - E = 18, - R = 19, - T = 20, - Y = 21, - U = 22, - I = 23, - O = 24, - P = 25, - /// Symbol: [ - LeftBrace = 26, - /// Symbol: ] - RightBrace = 27, - Enter = 28, - LeftCtrl = 29, - A = 30, - S = 31, - D = 32, - F = 33, - G = 34, - H = 35, - J = 36, - K = 37, - L = 38, - /// Symbol: ; - SemiColon = 39, - /// Symbol: ' - Apostrophe = 40, - /// Symbol: ` - Grave = 41, - LeftShift = 42, - /// Symbol: \ - BackSlash = 43, - Z = 44, - X = 45, - C = 46, - V = 47, - B = 48, - N = 49, - M = 50, - Comma = 51, - Dot = 52, - // Symbol: / - Slash = 53, - RightShift = 54, - /// Keypad asterisk, Symbol: * - KpAsterisk = 55, - LeftAlt = 56, - Space = 57, - Capslock = 58, - F1 = 59, - F2 = 60, - F3 = 61, - F4 = 62, - F5 = 63, - F6 = 64, - F7 = 65, - F8 = 66, - F9 = 67, - F10 = 68, - NumLock = 69, - ScrollLock = 70, - Kp7 = 71, - Kp8 = 72, - Kp9 = 73, - KpMinus = 74, - Kp4 = 75, - Kp5 = 76, - Kp6 = 77, - KpPlus = 78, - Kp1 = 79, - Kp2 = 80, - Kp3 = 81, - Kp0 = 82, - KpDot = 83, - - F11 = 87, - F12 = 88, - - KpEnter = 96, - RightCtrl = 97, - KpSlash = 98, - - RightAlt = 100, - LineFeed = 101, - Home = 102, - Up = 103, - PageUp = 104, - Left = 105, - Right = 106, - End = 107, - Down = 108, - PageDown = 109, - Insert = 110, - Delete = 111, - - LeftMeta = 125, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum KeyStatus { - Pressed, - Released, -} diff --git a/kernel/comps/input/src/lib.rs b/kernel/comps/input/src/lib.rs index 5d1704574..ae9af3713 100644 --- a/kernel/comps/input/src/lib.rs +++ b/kernel/comps/input/src/lib.rs @@ -1,76 +1,129 @@ // SPDX-License-Identifier: MPL-2.0 //! The input devices of Asterinas. +//! +//! This crate provides a comprehensive input subsystem for handling various input devices, +//! including keyboards, mice, etc. It implements an event-driven architecture similar to +//! the Linux input subsystem. +//! +//! # Architecture +//! +//! ```text +//! Input Device → Input Core → Input Handler +//! ↓ ↓ ↓ +//! Hardware Event Router Event Consumer +//! (e.g., evdev) +//! ``` +//! +//! # Example Usage +//! +//! ``` +//! // Register an input device +//! let device = Arc::new(MyInputDevice::new()); +//! let registered_device = input::register_device(device); +//! +//! // Register an input handler +//! let handler = Arc::new(MyInputHandler::new()); +//! input::register_handler(handler); +//! +//! // Submit a key event from device +//! let key_event = InputEvent::from_key_and_status(linux_key, key_status); +//! registered_device.submit_events(&[key_event]); +//! ``` +//! #![no_std] #![deny(unsafe_code)] -#![feature(fn_traits)] extern crate alloc; -pub mod key; +pub mod event_type_codes; +mod input_core; +pub mod input_dev; +pub mod input_handler; -use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec}; -use core::{any::Any, fmt::Debug}; +use alloc::{sync::Arc, vec::Vec}; use component::{init_component, ComponentInitError}; -use key::{Key, KeyStatus}; use ostd::sync::SpinLock; use spin::Once; -#[derive(Debug, Clone, Copy)] -pub enum InputEvent { - KeyBoard(Key, KeyStatus), -} +use self::input_core::InputCore; +use crate::{ + input_dev::{InputDevice, RegisteredInputDevice}, + input_handler::{InputHandlerClass, RegisteredInputHandlerClass}, +}; -pub trait InputDevice: Send + Sync + Any + Debug { - fn register_callbacks(&self, function: &'static (dyn Fn(InputEvent) + Send + Sync)); -} - -pub fn register_device(name: String, device: Arc) { - COMPONENT - .get() - .unwrap() - .input_device_table +/// Registers a handler class. +pub fn register_handler_class( + handler_class: Arc, +) -> RegisteredInputHandlerClass { + let component = COMPONENT.get().unwrap(); + component + .input_core .lock() - .insert(name, device); + .register_handler_class(handler_class.clone()); + RegisteredInputHandlerClass(handler_class) } -pub fn get_device(str: &str) -> Option> { - COMPONENT - .get() - .unwrap() - .input_device_table +/// Unregisters a handler class. +pub(crate) fn unregister_handler_class( + handler_class: &Arc, +) -> Option> { + let component = COMPONENT.get().unwrap(); + component + .input_core .lock() - .get(str) - .cloned() + .unregister_handler_class(handler_class) } -pub fn all_devices() -> Vec<(String, Arc)> { - let input_devs = COMPONENT.get().unwrap().input_device_table.lock(); - input_devs - .iter() - .map(|(name, device)| (name.clone(), device.clone())) - .collect() +/// Registers an input device. +pub fn register_device(device: Arc) -> RegisteredInputDevice { + let component = COMPONENT.get().unwrap(); + component.input_core.lock().register_device(device) +} + +/// Unregisters an input device. +pub(crate) fn unregister_device(device: &Arc) -> Option> { + let component = COMPONENT.get().unwrap(); + component.input_core.lock().unregister_device(device) +} + +/// Counts the number of registered devices. +pub fn count_devices() -> usize { + let component = COMPONENT.get().unwrap(); + component.input_core.lock().count_devices() +} + +/// Counts the number of registered handler classes. +pub fn count_handler_class() -> usize { + let component = COMPONENT.get().unwrap(); + component.input_core.lock().count_handler_class() +} + +/// Gets all registered devices. +pub fn all_devices() -> Vec> { + let component = COMPONENT.get().unwrap(); + component.input_core.lock().all_devices() } static COMPONENT: Once = Once::new(); #[init_component] fn component_init() -> Result<(), ComponentInitError> { - let a = Component::init()?; - COMPONENT.call_once(|| a); + let component = Component::init()?; + COMPONENT.call_once(|| component); Ok(()) } #[derive(Debug)] struct Component { - input_device_table: SpinLock>>, + input_core: SpinLock, } impl Component { pub fn init() -> Result { Ok(Self { - input_device_table: SpinLock::new(BTreeMap::new()), + input_core: SpinLock::new(InputCore::new()), }) } } diff --git a/kernel/src/driver/mod.rs b/kernel/src/driver/mod.rs index 6c381867f..094eaba8c 100644 --- a/kernel/src/driver/mod.rs +++ b/kernel/src/driver/mod.rs @@ -7,8 +7,8 @@ use log::info; pub fn init() { // print all the input device to make sure input crate will compile - for (name, _) in aster_input::all_devices() { - info!("Found Input device, name:{}", name); + for device in aster_input::all_devices() { + info!("Found Input device, name:{}", device.name()); } if let Some(console) = FRAMEBUFFER_CONSOLE.get() {