Add evdev module
This commit is contained in:
parent
2034055f90
commit
869e04f6bb
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#![expect(dead_code)]
|
||||
|
||||
use alloc::format;
|
||||
use core::ops::Range;
|
||||
|
||||
use device_id::{DeviceId, MajorId};
|
||||
|
|
@ -173,9 +174,14 @@ impl<'a> DevtmpfsName<'a> {
|
|||
|
||||
pub(super) fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
|
||||
for device in collect_all() {
|
||||
let name = device.devtmpfs_name().dev_name().to_string();
|
||||
let devtmpfs_name = device.devtmpfs_name();
|
||||
let path = if let Some(class_name) = devtmpfs_name.class_name() {
|
||||
format!("{}/{}", class_name, devtmpfs_name.dev_name())
|
||||
} else {
|
||||
devtmpfs_name.dev_name().to_string()
|
||||
};
|
||||
let device = Arc::new(CharFile::new(device));
|
||||
add_node(device, &name, fs_resolver)?;
|
||||
add_node(device, &path, fs_resolver)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,282 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::sync::Weak;
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use aster_input::{
|
||||
event_type_codes::{EventTypes, SynEvent},
|
||||
input_dev::InputEvent,
|
||||
};
|
||||
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
|
||||
use ostd::{
|
||||
mm::{VmReader, VmWriter},
|
||||
sync::Mutex,
|
||||
Pod,
|
||||
};
|
||||
|
||||
use super::EvdevDevice;
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, IoctlCmd, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
syscall::ClockId,
|
||||
util::ring_buffer::{RbConsumer, RbProducer, RingBuffer},
|
||||
};
|
||||
|
||||
pub(super) const EVDEV_BUFFER_SIZE: usize = 64;
|
||||
|
||||
impl From<ClockId> for i32 {
|
||||
fn from(clock_id: ClockId) -> Self {
|
||||
clock_id as i32
|
||||
}
|
||||
}
|
||||
|
||||
define_atomic_version_of_integer_like_type!(ClockId, try_from = true, {
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AtomicClockId(core::sync::atomic::AtomicI32);
|
||||
});
|
||||
|
||||
// Compatible with Linux's event format.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
pub struct EvdevEvent {
|
||||
pub sec: u64,
|
||||
pub usec: u64,
|
||||
pub type_: u16,
|
||||
pub code: u16,
|
||||
pub value: i32,
|
||||
}
|
||||
|
||||
impl EvdevEvent {
|
||||
pub fn from_event_and_time(event: &InputEvent, time: Duration) -> Self {
|
||||
let (type_, code, value) = event.to_raw();
|
||||
Self {
|
||||
sec: time.as_secs(),
|
||||
usec: time.subsec_micros() as u64,
|
||||
type_,
|
||||
code,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An opened file from an evdev device (`EvdevDevice`).
|
||||
pub struct EvdevFile {
|
||||
/// Consumer for reading events.
|
||||
consumer: Mutex<RbConsumer<EvdevEvent>>,
|
||||
/// Clock ID for this opened evdev file.
|
||||
clock_id: AtomicClockId,
|
||||
/// Number of events available.
|
||||
event_count: AtomicUsize,
|
||||
/// Number of complete event packets available (ended with SYN_REPORT).
|
||||
packet_count: AtomicUsize,
|
||||
/// Pollee for event notification.
|
||||
pollee: Pollee,
|
||||
/// Weak reference to the evdev device that owns this evdev file.
|
||||
evdev: Weak<EvdevDevice>,
|
||||
}
|
||||
|
||||
impl EvdevFile {
|
||||
pub(super) fn new(
|
||||
buffer_size: usize,
|
||||
evdev: Weak<EvdevDevice>,
|
||||
) -> (Self, RbProducer<EvdevEvent>) {
|
||||
let (producer, consumer) = RingBuffer::new(buffer_size).split();
|
||||
|
||||
let evdev_file = Self {
|
||||
consumer: Mutex::new(consumer),
|
||||
// Default to be CLOCK_MONOTONIC
|
||||
clock_id: AtomicClockId::new(ClockId::CLOCK_MONOTONIC),
|
||||
event_count: AtomicUsize::new(0),
|
||||
packet_count: AtomicUsize::new(0),
|
||||
pollee: Pollee::new(),
|
||||
evdev,
|
||||
};
|
||||
(evdev_file, producer)
|
||||
}
|
||||
|
||||
/// Returns the clock ID for this opened evdev file.
|
||||
pub(super) fn clock_id(&self) -> ClockId {
|
||||
self.clock_id.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Checks if the EvdevEvent is a `SYN_REPORT` event.
|
||||
fn is_syn_report_event(&self, event: &EvdevEvent) -> bool {
|
||||
event.type_ == EventTypes::SYN.as_index() && event.code == SynEvent::Report as u16
|
||||
}
|
||||
|
||||
/// Checks if the EvdevEvent is a `SYN_DROPPED` event.
|
||||
fn is_syn_dropped_event(&self, event: &EvdevEvent) -> bool {
|
||||
event.type_ == EventTypes::SYN.as_index() && event.code == SynEvent::Dropped as u16
|
||||
}
|
||||
|
||||
/// Checks if buffer has complete event packets.
|
||||
pub fn has_complete_packets(&self) -> bool {
|
||||
self.packet_count.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
|
||||
/// Increments event count.
|
||||
pub fn increment_event_count(&self) {
|
||||
self.event_count.fetch_add(1, Ordering::Relaxed);
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
|
||||
/// Decrements event count.
|
||||
pub fn decrement_event_count(&self) {
|
||||
self.event_count.fetch_sub(1, Ordering::Relaxed);
|
||||
if self.event_count.load(Ordering::Relaxed) == 0 {
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// Increments packet count.
|
||||
pub fn increment_packet_count(&self) {
|
||||
self.packet_count.fetch_add(1, Ordering::Relaxed);
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
|
||||
/// Decrements packet count.
|
||||
pub fn decrement_packet_count(&self) {
|
||||
self.packet_count.fetch_sub(1, Ordering::Relaxed);
|
||||
if self.packet_count.load(Ordering::Relaxed) == 0 {
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes events and writes them to the writer.
|
||||
/// Returns the total number of bytes written, or EAGAIN if no events available.
|
||||
fn process_events(&self, max_events: usize, writer: &mut VmWriter) -> Result<usize> {
|
||||
const EVENT_SIZE: usize = core::mem::size_of::<EvdevEvent>();
|
||||
|
||||
let mut consumer = self.consumer.lock();
|
||||
let mut event_count = 0;
|
||||
|
||||
for _ in 0..max_events {
|
||||
let Some(event) = consumer.pop() else {
|
||||
break;
|
||||
};
|
||||
|
||||
// Check if this is a SYN_REPORT or SYN_DROPPED event.
|
||||
let is_syn_report = self.is_syn_report_event(&event);
|
||||
let is_syn_dropped = self.is_syn_dropped_event(&event);
|
||||
|
||||
// Write event directly to writer.
|
||||
writer.write_val(&event)?;
|
||||
event_count += 1;
|
||||
|
||||
self.decrement_event_count();
|
||||
|
||||
if is_syn_report || is_syn_dropped {
|
||||
self.decrement_packet_count();
|
||||
}
|
||||
}
|
||||
|
||||
if event_count == 0 {
|
||||
return Err(Error::with_message(Errno::EAGAIN, "No events available"));
|
||||
}
|
||||
|
||||
Ok(event_count * EVENT_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for EvdevFile {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.pollee.poll_with(mask, poller, || {
|
||||
let has_complete_packets = self.has_complete_packets();
|
||||
|
||||
let mut events = IoEvents::empty();
|
||||
if has_complete_packets && mask.contains(IoEvents::IN) {
|
||||
events |= IoEvents::IN;
|
||||
}
|
||||
|
||||
events
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for EvdevFile {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let requested_bytes = writer.avail();
|
||||
let max_events = requested_bytes / core::mem::size_of::<EvdevEvent>();
|
||||
|
||||
if max_events == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let is_nonblocking = status_flags.contains(StatusFlags::O_NONBLOCK);
|
||||
match self.process_events(max_events, writer) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) if e.error() == Errno::EAGAIN => {
|
||||
if is_nonblocking {
|
||||
Err(e)
|
||||
} else {
|
||||
self.wait_events(IoEvents::IN, None, || {
|
||||
self.process_events(max_events, writer)
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
_reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
// TODO: support write operation on evdev devices.
|
||||
Err(Error::with_message(
|
||||
Errno::ENOSYS,
|
||||
"WRITE operation not supported on evdev devices",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for EvdevFile {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
// TODO: support ioctl operation on evdev devices.
|
||||
Err(Error::with_message(
|
||||
Errno::EINVAL,
|
||||
"IOCTL operation not supported on evdev devices",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EvdevFile {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("EvdevFile")
|
||||
.field("event_count", &self.event_count.load(Ordering::Relaxed))
|
||||
.field("clock_id", &self.clock_id())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EvdevFile {
|
||||
fn drop(&mut self) {
|
||||
if let Some(evdev) = self.evdev.upgrade() {
|
||||
evdev.detach_closed_files();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Event device (evdev) support.
|
||||
//!
|
||||
//! Character device with major number 13. The minor numbers are dynamically allocated.
|
||||
//! Devices appear as `/dev/input/eventX` where X is the minor number.
|
||||
//!
|
||||
//! Reference: <https://elixir.bootlin.com/linux/v6.17/source/include/uapi/linux/major.h>
|
||||
|
||||
mod file;
|
||||
|
||||
use alloc::{
|
||||
format,
|
||||
string::String,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use aster_input::{
|
||||
event_type_codes::{EventTypes, SynEvent},
|
||||
input_dev::{InputDevice, InputEvent},
|
||||
input_handler::{ConnectError, InputHandler, InputHandlerClass},
|
||||
};
|
||||
use aster_time::read_monotonic_time;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use file::{EvdevEvent, EvdevFile, EVDEV_BUFFER_SIZE};
|
||||
use ostd::sync::SpinLock;
|
||||
use spin::Once;
|
||||
|
||||
use super::char::{acquire_major, register, unregister, CharDevice, MajorIdOwner};
|
||||
use crate::{
|
||||
device::char::DevtmpfsName,
|
||||
fs::inode_handle::FileIo,
|
||||
prelude::*,
|
||||
syscall::ClockId,
|
||||
time::clocks::{
|
||||
BootTimeClock, MonotonicClock, MonotonicCoarseClock, MonotonicRawClock, RealTimeClock,
|
||||
RealTimeCoarseClock,
|
||||
},
|
||||
util::ring_buffer::RbProducer,
|
||||
};
|
||||
|
||||
/// Major device number for evdev devices.
|
||||
const EVDEV_MAJOR_ID: u16 = 13;
|
||||
|
||||
/// Global minor number allocator for evdev devices.
|
||||
static EVDEV_MINOR_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// Global registry of evdev devices.
|
||||
static EVDEV_DEVICES: SpinLock<BTreeMap<MinorId, Arc<EvdevDevice>>> =
|
||||
SpinLock::new(BTreeMap::new());
|
||||
|
||||
pub struct EvdevDevice {
|
||||
/// Input device associated with this evdev.
|
||||
device: Arc<dyn InputDevice>,
|
||||
/// List of opened evdev files with their producers.
|
||||
///
|
||||
/// # Deadlock Prevention
|
||||
///
|
||||
/// This lock is acquired in both the task and interrupt contexts.
|
||||
/// We must make sure that this lock is taken with the local IRQs disabled.
|
||||
/// Otherwise, we would be vulnerable to deadlock.
|
||||
opened_files: SpinLock<Vec<(Weak<EvdevFile>, RbProducer<EvdevEvent>)>>,
|
||||
/// Device node name (e.g., "event0").
|
||||
node_name: String,
|
||||
/// Device ID.
|
||||
id: DeviceId,
|
||||
}
|
||||
|
||||
impl Debug for EvdevDevice {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let opened_count = self
|
||||
.opened_files
|
||||
.lock()
|
||||
.iter()
|
||||
.filter(|(file, _)| file.strong_count() > 0)
|
||||
.count();
|
||||
f.debug_struct("EvdevDevice")
|
||||
.field("minor", &self.id.minor())
|
||||
.field("device_name", &self.device.name())
|
||||
.field("opened_files", &opened_count)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EvdevDevice {
|
||||
pub(self) fn new(minor: u32, device: Arc<dyn InputDevice>) -> Self {
|
||||
let node_name = format!("event{}", minor);
|
||||
let major = EVDEV_MAJOR.get().unwrap().get();
|
||||
let minor_id = MinorId::new(minor);
|
||||
|
||||
Self {
|
||||
device,
|
||||
opened_files: SpinLock::new(Vec::new()),
|
||||
node_name,
|
||||
id: DeviceId::new(major, minor_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if this evdev device is associated with the given input device.
|
||||
pub(self) fn matches_input_device(&self, input_device: &Arc<dyn InputDevice>) -> bool {
|
||||
Arc::ptr_eq(&self.device, input_device)
|
||||
}
|
||||
|
||||
/// Adds an opened evdev file to this evdev device.
|
||||
fn attach_file(&self, file: Weak<EvdevFile>, producer: RbProducer<EvdevEvent>) {
|
||||
let mut opened_files = self.opened_files.disable_irq().lock();
|
||||
opened_files.push((file, producer));
|
||||
}
|
||||
|
||||
/// Removes closed evdev files from this evdev device.
|
||||
pub(self) fn detach_closed_files(&self) {
|
||||
let mut opened_files = self.opened_files.disable_irq().lock();
|
||||
opened_files.retain(|(file, _)| file.strong_count() > 0);
|
||||
}
|
||||
|
||||
/// Distributes events to all opened evdev files.
|
||||
pub(self) fn pass_events(&self, events: &[InputEvent]) {
|
||||
let mut opened_files = self.opened_files.lock();
|
||||
|
||||
// Send events to all opened evdev files using their producers.
|
||||
for (file_weak, producer) in opened_files.iter_mut() {
|
||||
let Some(file) = file_weak.upgrade() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for event in events {
|
||||
// Get time according to the opened evdev file's clock type.
|
||||
let time = self.get_time_for_file(&file);
|
||||
let timed_event = EvdevEvent::from_event_and_time(event, time);
|
||||
|
||||
// Try to push event to the buffer.
|
||||
if let Some(()) = producer.push(timed_event) {
|
||||
file.increment_event_count();
|
||||
|
||||
// Check if this is a SYN_REPORT event
|
||||
if self.is_syn_report_event(event) {
|
||||
file.increment_packet_count();
|
||||
}
|
||||
} else {
|
||||
// TODO: In Linux's implementation, when the buffer is full, evdev will pop the two
|
||||
// oldest events to ensure that both the SYN_DROPPED event and the current event can
|
||||
// be pushed into the buffer. However, since we are using `RingBuffer`, evdev cannot
|
||||
// pop events. Considering the convenience of lock-free programming with `RingBuffer`
|
||||
// and the rarity of this error condition, we choose to retain the use of `RingBuffer`
|
||||
// and instead attempt to push both the SYN_DROPPED event and the current event.
|
||||
let dropped_event = EvdevEvent::from_event_and_time(
|
||||
&InputEvent::from_sync_event(SynEvent::Dropped),
|
||||
time,
|
||||
);
|
||||
|
||||
// Try to push SYN_DROPPED event.
|
||||
if let Some(()) = producer.push(dropped_event) {
|
||||
file.increment_event_count();
|
||||
file.increment_packet_count();
|
||||
|
||||
// Try to push the original event.
|
||||
if let Some(()) = producer.push(timed_event) {
|
||||
file.increment_event_count();
|
||||
|
||||
// Check if this is a SYN_REPORT event.
|
||||
if self.is_syn_report_event(event) {
|
||||
file.increment_packet_count();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Failed to push SYN_DROPPED event, emit a warning.
|
||||
log::warn!(
|
||||
"Failed to push SYN_DROPPED event to evdev file buffer (buffer full)"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the event is a SYN_REPORT event (marks end of packet).
|
||||
fn is_syn_report_event(&self, event: &InputEvent) -> bool {
|
||||
let (type_, code, _) = event.to_raw();
|
||||
type_ == EventTypes::SYN.as_index() && code == SynEvent::Report as u16
|
||||
}
|
||||
|
||||
/// Gets time according to the opened evdev file's clock ID.
|
||||
fn get_time_for_file(&self, file: &EvdevFile) -> Duration {
|
||||
let clock_id = file.clock_id();
|
||||
|
||||
match clock_id {
|
||||
ClockId::CLOCK_REALTIME => RealTimeClock::get().read_time(),
|
||||
ClockId::CLOCK_MONOTONIC => MonotonicClock::get().read_time(),
|
||||
ClockId::CLOCK_MONOTONIC_RAW => MonotonicRawClock::get().read_time(),
|
||||
ClockId::CLOCK_REALTIME_COARSE => RealTimeCoarseClock::get().read_time(),
|
||||
ClockId::CLOCK_MONOTONIC_COARSE => MonotonicCoarseClock::get().read_time(),
|
||||
ClockId::CLOCK_BOOTTIME => BootTimeClock::get().read_time(),
|
||||
// For process/thread clocks, fallback to monotonic time.
|
||||
ClockId::CLOCK_PROCESS_CPUTIME_ID | ClockId::CLOCK_THREAD_CPUTIME_ID => {
|
||||
read_monotonic_time()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new opened evdev file for this evdev device.
|
||||
pub(self) fn create_file(self: &Arc<Self>, buffer_size: usize) -> Result<Arc<EvdevFile>> {
|
||||
// Create the evdev file and get the producer.
|
||||
let (file, producer) = EvdevFile::new(buffer_size, Arc::downgrade(self));
|
||||
let file = Arc::new(file);
|
||||
let file_weak = Arc::downgrade(&file);
|
||||
|
||||
// Attach the opened evdev file to this device.
|
||||
self.attach_file(file_weak, producer);
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for EvdevDevice {
|
||||
fn devtmpfs_name(&self) -> DevtmpfsName<'_> {
|
||||
DevtmpfsName::new(&self.node_name, Some("input"))
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Arc<dyn FileIo>> {
|
||||
// Get the Arc<EvdevDevice> from the registry.
|
||||
let devices = EVDEV_DEVICES.lock();
|
||||
let Some(evdev) = devices.get(&self.id.minor()) else {
|
||||
return Err(Error::with_message(
|
||||
Errno::ENODEV,
|
||||
"evdev device not found in registry",
|
||||
));
|
||||
};
|
||||
|
||||
// Create a new opened evdev file for this evdev device.
|
||||
let file = evdev.create_file(EVDEV_BUFFER_SIZE)?;
|
||||
Ok(file as Arc<dyn FileIo>)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evdev handler class that creates device nodes for input devices.
|
||||
#[derive(Debug)]
|
||||
struct EvdevHandlerClass;
|
||||
|
||||
impl InputHandlerClass for EvdevHandlerClass {
|
||||
fn name(&self) -> &str {
|
||||
"evdev"
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
dev: Arc<dyn InputDevice>,
|
||||
) -> core::result::Result<Arc<dyn InputHandler>, ConnectError> {
|
||||
// Allocate a new minor number.
|
||||
let minor = EVDEV_MINOR_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let minor_id = MinorId::new(minor);
|
||||
|
||||
// Create evdev device.
|
||||
let evdev = Arc::new(EvdevDevice::new(minor, dev.clone()));
|
||||
|
||||
// Register the device with the char device subsystem.
|
||||
register(evdev.clone()).map_err(|_| ConnectError::InternalError)?;
|
||||
|
||||
// Add to our registry for lookup during disconnect.
|
||||
EVDEV_DEVICES.lock().insert(minor_id, evdev.clone());
|
||||
|
||||
// Create handler instance for this device.
|
||||
let handler = EvdevHandler::new(evdev);
|
||||
Ok(Arc::new(handler))
|
||||
}
|
||||
|
||||
fn disconnect(&self, dev: &Arc<dyn InputDevice>) {
|
||||
let mut devices = EVDEV_DEVICES.lock();
|
||||
let device_name = dev.name();
|
||||
|
||||
// Find the device by checking if it matches the input device.
|
||||
let mut found_minor = None;
|
||||
for (minor, evdev) in devices.iter() {
|
||||
if evdev.matches_input_device(dev) {
|
||||
found_minor = Some(*minor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(minor) = found_minor {
|
||||
let evdev = devices.remove(&minor).unwrap();
|
||||
let device_id = evdev.id();
|
||||
|
||||
// Unregister from the char device subsystem.
|
||||
if let Err(e) = unregister(device_id) {
|
||||
log::warn!(
|
||||
"Failed to unregister evdev device '{}' (minor: {}): {:?}",
|
||||
device_name,
|
||||
minor.get(),
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Implement device node deletion when the functionality is available.
|
||||
log::info!(
|
||||
"Disconnected evdev device '{}' (minor: {}), device node /dev/input/event{} still exists",
|
||||
device_name,
|
||||
minor.get(),
|
||||
minor.get()
|
||||
);
|
||||
} else {
|
||||
log::warn!(
|
||||
"Attempted to disconnect device '{}' but it did not connect to evdev",
|
||||
device_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evdev handler instance for a specific device.
|
||||
#[derive(Debug)]
|
||||
pub struct EvdevHandler {
|
||||
evdev: Arc<EvdevDevice>,
|
||||
}
|
||||
|
||||
impl EvdevHandler {
|
||||
fn new(evdev: Arc<EvdevDevice>) -> Self {
|
||||
Self { evdev }
|
||||
}
|
||||
}
|
||||
|
||||
impl InputHandler for EvdevHandler {
|
||||
fn handle_events(&self, events: &[InputEvent]) {
|
||||
self.evdev.pass_events(events);
|
||||
}
|
||||
}
|
||||
|
||||
static EVDEV_MAJOR: Once<MajorIdOwner> = Once::new();
|
||||
|
||||
pub(super) fn init_in_first_kthread() {
|
||||
EVDEV_MAJOR.call_once(|| acquire_major(MajorId::new(EVDEV_MAJOR_ID)).unwrap());
|
||||
|
||||
static REGISTERED_EVDDEV_CLASS: spin::Once<
|
||||
aster_input::input_handler::RegisteredInputHandlerClass,
|
||||
> = spin::Once::new();
|
||||
|
||||
let handler_class = Arc::new(EvdevHandlerClass);
|
||||
let handle = aster_input::register_handler_class(handler_class);
|
||||
REGISTERED_EVDDEV_CLASS.call_once(|| handle);
|
||||
|
||||
log::info!("Evdev device support initialized");
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
mod char;
|
||||
mod disk;
|
||||
mod evdev;
|
||||
mod mem;
|
||||
pub mod misc;
|
||||
mod pty;
|
||||
|
|
@ -28,6 +29,7 @@ pub fn init_in_first_kthread() {
|
|||
disk::init_in_first_kthread();
|
||||
mem::init_in_first_kthread();
|
||||
misc::init_in_first_kthread();
|
||||
evdev::init_in_first_kthread();
|
||||
}
|
||||
|
||||
/// Init the device node in fs, must be called after mounting rootfs.
|
||||
|
|
|
|||
Loading…
Reference in New Issue