virtio/devices/
virtio_input.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! Support for the VirtIO Input Device
6//!
7//! <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-3850008>
8//!
9//! This implementation assumes the input device is a keyboard that follows the
10//! [event code](https://www.kernel.org/doc/Documentation/input/event-codes.txt)
11//! format that Linux uses.
12
13use kernel::utilities::cells::OptionalCell;
14
15use crate::devices::{VirtIODeviceDriver, VirtIODeviceType};
16use crate::queues::split_queue::{SplitVirtqueue, SplitVirtqueueClient, VirtqueueBuffer};
17
18/// Event: separate events.
19const EV_SYN: u16 = 0;
20/// Event: state change of keyboard.
21const EV_KEY: u16 = 1;
22
23/// VirtIO for input devices (e.g., keyboards).
24pub struct VirtIOInput<'a> {
25    /// Queue of events from the device (e.g., keyboard).
26    eventq: &'a SplitVirtqueue<'static, 'static, 3>,
27    /// Queue of status updates from this driver (e.g., turn on LED).
28    statusq: &'a SplitVirtqueue<'static, 'static, 1>,
29    /// Buffer to hold status updates.
30    status_buffer: OptionalCell<&'static mut [u8]>,
31    /// Store keys sent across multiple events.
32    keys: [OptionalCell<(u16, bool)>; 2],
33    /// Keyboard callback client.
34    client: OptionalCell<&'a dyn kernel::hil::keyboard::KeyboardClient>,
35}
36
37impl<'a> VirtIOInput<'a> {
38    pub fn new(
39        eventq: &'a SplitVirtqueue<'static, 'static, 3>,
40        statusq: &'a SplitVirtqueue<'static, 'static, 1>,
41        status_buffer: &'static mut [u8],
42    ) -> Self {
43        eventq.enable_used_callbacks();
44
45        Self {
46            eventq,
47            statusq,
48            status_buffer: OptionalCell::new(status_buffer),
49            keys: [const { OptionalCell::empty() }; 2],
50            client: OptionalCell::empty(),
51        }
52    }
53
54    pub fn provide_buffers(
55        &self,
56        event_buffer1: &'static mut [u8],
57        event_buffer2: &'static mut [u8],
58        event_buffer3: &'static mut [u8],
59    ) {
60        // Provide the device three buffers to hold up to two keys and a sync
61        // event.
62        let event_buffer_len = event_buffer1.len();
63        let mut buffer_chain = [Some(VirtqueueBuffer {
64            buf: event_buffer1,
65            len: event_buffer_len,
66            device_writeable: true,
67        })];
68        self.eventq.provide_buffer_chain(&mut buffer_chain).unwrap();
69
70        let event_buffer_len = event_buffer2.len();
71        let mut buffer_chain = [Some(VirtqueueBuffer {
72            buf: event_buffer2,
73            len: event_buffer_len,
74            device_writeable: true,
75        })];
76        self.eventq.provide_buffer_chain(&mut buffer_chain).unwrap();
77
78        let event_buffer_len = event_buffer3.len();
79        let mut buffer_chain = [Some(VirtqueueBuffer {
80            buf: event_buffer3,
81            len: event_buffer_len,
82            device_writeable: true,
83        })];
84        self.eventq.provide_buffer_chain(&mut buffer_chain).unwrap();
85    }
86}
87
88impl SplitVirtqueueClient<'static> for VirtIOInput<'_> {
89    fn buffer_chain_ready(
90        &self,
91        queue_number: u32,
92        buffer_chain: &mut [Option<VirtqueueBuffer<'static>>],
93        _bytes_used: usize,
94    ) {
95        fn parse_event(buf: &[u8]) -> Result<(u16, u16, u32), ()> {
96            let event_type = u16::from_le_bytes(buf.get(0..2).ok_or(())?.try_into().or(Err(()))?);
97            let event_code = u16::from_le_bytes(buf.get(2..4).ok_or(())?.try_into().or(Err(()))?);
98            let event_value = u32::from_le_bytes(buf.get(4..8).ok_or(())?.try_into().or(Err(()))?);
99            Ok((event_type, event_code, event_value))
100        }
101
102        if queue_number == self.eventq.queue_number().unwrap() {
103            // Received an input device event
104
105            // Process the incoming key. If this is the SYN_REPORT then our key
106            // press is finished and we can call the client.
107            let end = if let Some(event_buffer) = &buffer_chain[0] {
108                if let Ok((event_type, event_code, event_value)) = parse_event(event_buffer.buf) {
109                    if event_type == EV_KEY {
110                        // This is a key down press. Save in the next available
111                        // slot.
112                        if self.keys[0].is_none() {
113                            self.keys[0].set((event_code, event_value == 1));
114                        } else {
115                            if self.keys[1].is_none() {
116                                self.keys[1].set((event_code, event_value == 1));
117                            }
118                        }
119                    }
120
121                    // If this is a SYN_REPORT return true
122                    event_type == EV_SYN && event_code == 0 && event_value == 0
123                } else {
124                    false
125                }
126            } else {
127                false
128            };
129
130            self.eventq.provide_buffer_chain(buffer_chain).unwrap();
131
132            if end {
133                // Signal to client that we got key presses.
134                let mut key_presses: [(u16, bool); 2] = [(0, false); 2];
135                let mut length = 0;
136                self.keys[0].take().map(|key| {
137                    key_presses[length] = key;
138                    length += 1;
139                });
140                self.keys[1].take().map(|key| {
141                    key_presses[length] = key;
142                    length += 1;
143                });
144                self.client.map(|client| {
145                    client.keys_pressed(&key_presses[0..length], Ok(()));
146                });
147            }
148        } else if queue_number == self.statusq.queue_number().unwrap() {
149            // Sent a status update
150
151            let status_buffer = buffer_chain[0].take().expect("No status buffer").buf;
152
153            self.status_buffer.replace(status_buffer);
154        }
155    }
156}
157
158impl VirtIODeviceDriver for VirtIOInput<'_> {
159    fn negotiate_features(&self, _offered_features: u64) -> Option<u64> {
160        // We don't support any special features and do not care about
161        // what the device offers.
162        Some(0)
163    }
164
165    fn device_type(&self) -> VirtIODeviceType {
166        VirtIODeviceType::InputDevice
167    }
168}
169
170impl<'a> kernel::hil::keyboard::Keyboard<'a> for VirtIOInput<'a> {
171    fn set_client(&self, client: &'a dyn kernel::hil::keyboard::KeyboardClient) {
172        self.client.set(client);
173    }
174}