diff --git a/Cargo.toml b/Cargo.toml index e60857c..d3f6030 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ categories = ["os", "no-std", "hardware-support"] axdriver_base = { path = "axdriver_base", version = "0.1" } axdriver_block = { path = "axdriver_block", version = "0.1" } axdriver_display = { path = "axdriver_display", version = "0.1" } +axdriver_input = { path = "axdriver_input", version = "0.1" } axdriver_net = { path = "axdriver_net", version = "0.1" } axdriver_pci = { path = "axdriver_pci", version = "0.1" } axdriver_virtio = { path = "axdriver_virtio", version = "0.1" } diff --git a/axdriver_base/src/lib.rs b/axdriver_base/src/lib.rs index fd14b47..084bc51 100644 --- a/axdriver_base/src/lib.rs +++ b/axdriver_base/src/lib.rs @@ -27,6 +27,8 @@ pub enum DeviceType { Net, /// Graphic display device (e.g., GPU) Display, + /// Input device (e.g., keyboard, mouse). + Input, } /// The error type for device operation failures. diff --git a/axdriver_input/Cargo.toml b/axdriver_input/Cargo.toml new file mode 100644 index 0000000..a94486d --- /dev/null +++ b/axdriver_input/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "axdriver_input" +edition.workspace = true +description = "Common traits and types for input device drivers" +documentation = "https://arceos-org.github.io/axdriver_crates/axdriver_input" +keywords = ["arceos", "driver", "input"] +version.workspace = true +authors = ["mivik "] +license.workspace = true +homepage.workspace = true +repository.workspace = true +categories.workspace = true + +[dependencies] +axdriver_base = { workspace = true } +strum = { version = "0.27.2", default-features = false, features = ["derive"] } diff --git a/axdriver_input/src/lib.rs b/axdriver_input/src/lib.rs new file mode 100644 index 0000000..eb7f0bd --- /dev/null +++ b/axdriver_input/src/lib.rs @@ -0,0 +1,101 @@ +//! Common traits and types for input device drivers. + +#![no_std] + +#[doc(no_inline)] +pub use axdriver_base::{BaseDriverOps, DevError, DevResult, DeviceType}; +use strum::FromRepr; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, FromRepr)] +pub enum EventType { + Synchronization = 0x00, + Key = 0x01, + Relative = 0x02, + Absolute = 0x03, + Misc = 0x04, + Switch = 0x05, + Led = 0x11, + Sound = 0x12, + ForceFeedback = 0x15, +} + +impl EventType { + pub const MAX: u8 = 0x1f; + pub const COUNT: u8 = Self::MAX + 1; + + pub const fn bits_count(&self) -> usize { + match self { + EventType::Synchronization => 0x10, + EventType::Key => 0x300, + EventType::Relative => 0x10, + EventType::Absolute => 0x40, + EventType::Misc => 0x08, + EventType::Switch => 0x12, + EventType::Led => 0x10, + EventType::Sound => 0x08, + EventType::ForceFeedback => 0x80, + } + } +} + +/// An input event, as defined by the Linux input subsystem. +#[repr(C)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct Event { + pub event_type: u16, + pub code: u16, + pub value: u32, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct InputDeviceId { + /// The bustype identifier. + pub bus_type: u16, + /// The vendor identifier. + pub vendor: u16, + /// The product identifier. + pub product: u16, + /// The version identifier. + pub version: u16, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct AbsInfo { + /// The minimum value for the axis. + pub min: u32, + /// The maximum value for the axis. + pub max: u32, + /// The fuzz value used to filter noise from the event stream. + pub fuzz: u32, + /// The size of the dead zone; values less than this will be reported as 0. + pub flat: u32, + /// The resolution for values reported for the axis. + pub res: u32, +} + +/// Operations that require an input device driver to implement. +pub trait InputDriverOps: BaseDriverOps { + /// Returns the device ID of the input device. + fn device_id(&self) -> InputDeviceId; + + /// Returns the physical location of the input device. + fn physical_location(&self) -> &str; + + /// Returns a unique ID of the input device. + fn unique_id(&self) -> &str; + + /// Fetches the bitmap of supported event codes for the specified event + /// type. + /// + /// Returns true if the event type is supported and the bitmap is written to + /// `out`. + fn get_event_bits(&mut self, ty: EventType, out: &mut [u8]) -> DevResult; + + /// Reads an input event from the device. + /// + /// If no events are available, `Err(DevError::Again)` is returned. + fn read_event(&mut self) -> DevResult; +} diff --git a/axdriver_virtio/Cargo.toml b/axdriver_virtio/Cargo.toml index 44fa223..b814405 100644 --- a/axdriver_virtio/Cargo.toml +++ b/axdriver_virtio/Cargo.toml @@ -15,12 +15,14 @@ categories.workspace = true alloc = ["virtio-drivers/alloc"] block = ["axdriver_block"] gpu = ["alloc", "axdriver_display"] +input = ["alloc", "axdriver_input"] net = ["alloc", "axdriver_net"] [dependencies] axdriver_base = { workspace = true } axdriver_block = { workspace = true, optional = true } axdriver_display = { workspace = true, optional = true } +axdriver_input = { workspace = true, optional = true } axdriver_net = { workspace = true, optional = true } log = { workspace = true } virtio-drivers = { version = "0.7.4", default-features = false } diff --git a/axdriver_virtio/src/input.rs b/axdriver_virtio/src/input.rs new file mode 100644 index 0000000..7763a7a --- /dev/null +++ b/axdriver_virtio/src/input.rs @@ -0,0 +1,88 @@ +use alloc::{borrow::ToOwned, string::String}; + +use axdriver_base::{BaseDriverOps, DevError, DevResult, DeviceType}; +use axdriver_input::{Event, EventType, InputDeviceId, InputDriverOps}; +use virtio_drivers::{ + Hal, + device::input::{InputConfigSelect, VirtIOInput as InnerDev}, + transport::Transport, +}; + +use crate::as_dev_err; + +/// The VirtIO Input device driver. +pub struct VirtIoInputDev { + inner: InnerDev, + device_id: InputDeviceId, + name: String, +} + +unsafe impl Send for VirtIoInputDev {} +unsafe impl Sync for VirtIoInputDev {} + +impl VirtIoInputDev { + /// Creates a new driver instance and initializes the device, or returns + /// an error if any step fails. + pub fn try_new(transport: T) -> DevResult { + let mut virtio = InnerDev::new(transport).map_err(as_dev_err)?; + let name = virtio.name().unwrap_or_else(|_| "".to_owned()); + let device_id = virtio.ids().map_err(as_dev_err)?; + let device_id = InputDeviceId { + bus_type: device_id.bustype, + vendor: device_id.vendor, + product: device_id.product, + version: device_id.version, + }; + + Ok(Self { + inner: virtio, + device_id, + name, + }) + } +} + +impl BaseDriverOps for VirtIoInputDev { + fn device_name(&self) -> &str { + &self.name + } + + fn device_type(&self) -> DeviceType { + DeviceType::Input + } +} + +impl InputDriverOps for VirtIoInputDev { + fn device_id(&self) -> InputDeviceId { + self.device_id + } + + fn physical_location(&self) -> &str { + // TODO: unique physical location + "virtio0/input0" + } + + fn unique_id(&self) -> &str { + // TODO: unique ID + "virtio" + } + + fn get_event_bits(&mut self, ty: EventType, out: &mut [u8]) -> DevResult { + let read = self + .inner + .query_config_select(InputConfigSelect::EvBits, ty as u8, out); + Ok(read != 0) + } + + fn read_event(&mut self) -> DevResult { + self.inner.ack_interrupt(); + self.inner + .pop_pending_event() + .map(|e| Event { + event_type: e.event_type, + code: e.code, + value: e.value, + }) + .ok_or(DevError::Again) + } +} diff --git a/axdriver_virtio/src/lib.rs b/axdriver_virtio/src/lib.rs index 0400edd..64aec9e 100644 --- a/axdriver_virtio/src/lib.rs +++ b/axdriver_virtio/src/lib.rs @@ -22,6 +22,11 @@ mod blk; #[cfg(feature = "gpu")] mod gpu; +#[cfg(feature = "input")] +mod input; +#[cfg(feature = "input")] +pub use self::input::VirtIoInputDev; + #[cfg(feature = "net")] mod net; @@ -84,6 +89,7 @@ const fn as_dev_type(t: VirtIoDevType) -> Option { Block => Some(DeviceType::Block), Network => Some(DeviceType::Net), GPU => Some(DeviceType::Display), + Input => Some(DeviceType::Input), _ => None, } }