⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/
input_event.rs

1//! Unified input event subsystem.
2//!
3//! Collects keyboard and mouse input into a single event stream using
4//! a Linux-compatible event structure. User-space reads events via
5//! `sys_input_read()`.
6
7use core::sync::atomic::{AtomicUsize, Ordering};
8
9/// Input event types (Linux evdev compatible).
10pub const EV_KEY: u16 = 0x01;
11pub const EV_REL: u16 = 0x02;
12#[allow(dead_code)] // Future: touchscreen, tablet
13pub const EV_ABS: u16 = 0x03;
14
15/// Relative axis codes.
16pub const REL_X: u16 = 0x00;
17pub const REL_Y: u16 = 0x01;
18
19/// Mouse button codes (Linux BTN_* values).
20pub const BTN_LEFT: u16 = 0x110;
21pub const BTN_RIGHT: u16 = 0x111;
22pub const BTN_MIDDLE: u16 = 0x112;
23
24/// Input event structure (ABI-stable for user space).
25#[repr(C)]
26#[derive(Debug, Clone, Copy)]
27pub struct InputEvent {
28    pub timestamp: u64,
29    pub event_type: u16,
30    pub code: u16,
31    pub value: i32,
32}
33
34impl InputEvent {
35    pub const fn key(code: u16, pressed: bool) -> Self {
36        Self {
37            timestamp: 0, // Filled on push
38            event_type: EV_KEY,
39            code,
40            value: if pressed { 1 } else { 0 },
41        }
42    }
43
44    pub const fn rel(code: u16, value: i32) -> Self {
45        Self {
46            timestamp: 0,
47            event_type: EV_REL,
48            code,
49            value,
50        }
51    }
52}
53
54/// Ring buffer for input events.
55const EVENT_BUFFER_SIZE: usize = 256;
56
57struct EventBuffer {
58    buf: [InputEvent; EVENT_BUFFER_SIZE],
59    head: AtomicUsize,
60    tail: AtomicUsize,
61}
62
63impl EventBuffer {
64    const fn new() -> Self {
65        Self {
66            buf: [InputEvent {
67                timestamp: 0,
68                event_type: 0,
69                code: 0,
70                value: 0,
71            }; EVENT_BUFFER_SIZE],
72            head: AtomicUsize::new(0),
73            tail: AtomicUsize::new(0),
74        }
75    }
76
77    fn push(&mut self, mut event: InputEvent) {
78        event.timestamp = crate::arch::timer::read_hw_timestamp();
79        let head = self.head.load(Ordering::Relaxed);
80        let next = (head + 1) & (EVENT_BUFFER_SIZE - 1);
81        let tail = self.tail.load(Ordering::Acquire);
82        if next == tail {
83            return; // Buffer full, drop event
84        }
85        self.buf[head] = event;
86        self.head.store(next, Ordering::Release);
87    }
88
89    fn pop(&self) -> Option<InputEvent> {
90        let tail = self.tail.load(Ordering::Relaxed);
91        let head = self.head.load(Ordering::Acquire);
92        if tail == head {
93            return None;
94        }
95        let event = self.buf[tail];
96        self.tail
97            .store((tail + 1) & (EVENT_BUFFER_SIZE - 1), Ordering::Release);
98        Some(event)
99    }
100}
101
102// SAFETY: EventBuffer uses atomic head/tail. Single producer (poll_all)
103// and single consumer (read_event).
104unsafe impl Send for EventBuffer {}
105unsafe impl Sync for EventBuffer {}
106
107static EVENT_BUFFER: spin::Mutex<EventBuffer> = spin::Mutex::new(EventBuffer::new());
108
109/// Push an input event into the global event queue.
110///
111/// Called by keyboard and mouse drivers.
112pub fn push_event(event: InputEvent) {
113    EVENT_BUFFER.lock().push(event);
114}
115
116/// Read the next input event from the queue.
117pub fn read_event() -> Option<InputEvent> {
118    EVENT_BUFFER.lock().pop()
119}
120
121/// Poll all input sources and convert to input events.
122///
123/// Called periodically (e.g., from APIC timer or shell loop).
124pub fn poll_all() {
125    // Poll keyboard and mouse hardware, then drain decoded buffers
126    #[cfg(target_arch = "x86_64")]
127    {
128        // Poll PS/2 controller directly for both keyboard and mouse data.
129        // The APIC takes over interrupt routing from the PIC, so IRQ1
130        // (keyboard) and IRQ12 (mouse) may never fire. We must poll the
131        // controller status port (0x64) to capture input from the QEMU
132        // graphical window.
133        //
134        // Status register bits:
135        //   bit 0 = output buffer full (data available in port 0x60)
136        //   bit 5 = data is from auxiliary (mouse) port
137        //
138        // We loop to drain all pending PS/2 bytes in a single poll_all()
139        // call, dispatching keyboard bytes to handle_scancode() and mouse
140        // bytes to poll_mouse_byte().
141        for _ in 0..64 {
142            // SAFETY: Reading PS/2 status register (port 0x64).
143            let status = unsafe { crate::arch::x86_64::inb(0x64) };
144            if (status & 0x01) == 0 {
145                break; // No data available
146            }
147            if (status & 0x20) != 0 {
148                // Bit 5 set: data is from auxiliary (mouse) port
149                // SAFETY: Reading PS/2 data port (port 0x60).
150                let byte = unsafe { crate::arch::x86_64::inb(0x60) };
151                crate::drivers::mouse::poll_mouse_byte(byte);
152            } else {
153                // Bit 5 clear: data is from keyboard port
154                // SAFETY: Reading PS/2 data port (port 0x60).
155                let scancode = unsafe { crate::arch::x86_64::inb(0x60) };
156                crate::drivers::keyboard::handle_scancode(scancode);
157            }
158        }
159    }
160
161    // Drain decoded keyboard buffer into unified event stream
162    #[cfg(target_arch = "x86_64")]
163    {
164        while let Some(key_byte) = crate::drivers::keyboard::read_key() {
165            push_event(InputEvent::key(key_byte as u16, true));
166        }
167    }
168
169    // Drain decoded mouse events into unified event stream
170    #[cfg(target_arch = "x86_64")]
171    {
172        while let Some(mouse_event) = crate::drivers::mouse::read_event() {
173            // Relative movement events
174            if mouse_event.dx != 0 {
175                push_event(InputEvent::rel(REL_X, mouse_event.dx as i32));
176            }
177            if mouse_event.dy != 0 {
178                push_event(InputEvent::rel(REL_Y, mouse_event.dy as i32));
179            }
180            // Button events
181            static PREV_BUTTONS: core::sync::atomic::AtomicU8 =
182                core::sync::atomic::AtomicU8::new(0);
183            let prev = PREV_BUTTONS.load(Ordering::Relaxed);
184            let changed = mouse_event.buttons ^ prev;
185            if (changed & super::mouse::BUTTON_LEFT) != 0 {
186                push_event(InputEvent::key(
187                    BTN_LEFT,
188                    (mouse_event.buttons & super::mouse::BUTTON_LEFT) != 0,
189                ));
190            }
191            if (changed & super::mouse::BUTTON_RIGHT) != 0 {
192                push_event(InputEvent::key(
193                    BTN_RIGHT,
194                    (mouse_event.buttons & super::mouse::BUTTON_RIGHT) != 0,
195                ));
196            }
197            if (changed & super::mouse::BUTTON_MIDDLE) != 0 {
198                push_event(InputEvent::key(
199                    BTN_MIDDLE,
200                    (mouse_event.buttons & super::mouse::BUTTON_MIDDLE) != 0,
201                ));
202            }
203            PREV_BUTTONS.store(mouse_event.buttons, Ordering::Relaxed);
204        }
205    }
206}