Skip to main content

starnix_modules_input/
input_file.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::input_device::InputDeviceInfoHandle;
6use crossbeam::queue::SegQueue;
7use fuchsia_inspect::Inspector;
8use futures::FutureExt;
9use starnix_core::fileops_impl_nonseekable;
10use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
11use starnix_core::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
12use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
13use starnix_core::vfs::{FileObject, FileOps, fileops_impl_noop_sync};
14use starnix_logging::{log_info, track_stub};
15use starnix_sync::{InputFileInputFileLock, LockDepMutex};
16use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
17use starnix_types::time::duration_from_timeval;
18use starnix_uapi::errors::Errno;
19use starnix_uapi::open_flags::OpenFlags;
20use starnix_uapi::user_address::{ArchSpecific, MultiArchUserRef, UserAddress, UserRef};
21use starnix_uapi::vfs::FdEvents;
22use starnix_uapi::{
23    ABS_CNT, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_SLOT, ABS_MT_TRACKING_ID, BTN_EXTRA,
24    BTN_LEFT, BTN_MIDDLE, BTN_MISC, BTN_RIGHT, BTN_SIDE, BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT,
25    INPUT_PROP_DIRECT, INPUT_PROP_POINTER, KEY_CNT, KEY_DOWN, KEY_LEFT, KEY_OK, KEY_POWER,
26    KEY_RIGHT, KEY_SLEEP, KEY_UP, KEY_VOLUMEDOWN, KEY_VOLUMEUP, LED_CNT, MSC_CNT, REL_CNT,
27    REL_HWHEEL, REL_WHEEL, REL_X, REL_Y, SW_CNT, errno, error, uapi,
28};
29use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
30use std::sync::{Arc, Weak};
31use zerocopy::IntoBytes as _; // for `as_bytes()`
32
33uapi::check_arch_independent_layout! {
34    input_id {}
35    input_absinfo {}
36}
37
38type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
39
40pub struct InputFileStatus {
41    /// The number of FIDL events received by this file from Fuchsia input system.
42    ///
43    /// We expect:
44    /// fidl_events_received_count = fidl_events_ignored_count +
45    ///                              fidl_events_unexpected_count +
46    ///                              fidl_events_converted_count
47    /// otherwise starnix ignored events unexpectedly.
48    ///
49    /// fidl_events_unexpected_count should be 0, if not it hints issues from upstream of ui stack.
50    pub fidl_events_received_count: AtomicU64,
51
52    /// The number of FIDL events ignored to this module’s representation of TouchEvent.
53    pub fidl_events_ignored_count: AtomicU64,
54
55    /// The unexpected number of FIDL events reached to this module should be filtered out
56    /// earlier in the UI stack.
57    /// It maybe unexpected format or unexpected order.
58    pub fidl_events_unexpected_count: AtomicU64,
59
60    /// The number of FIDL events converted to this module’s representation of TouchEvent.
61    pub fidl_events_converted_count: AtomicU64,
62
63    /// The number of uapi::input_events generated from TouchEvents.
64    pub uapi_events_generated_count: AtomicU64,
65
66    /// The event time of the last generated uapi::input_event.
67    pub last_generated_uapi_event_timestamp_ns: AtomicI64,
68
69    /// The number of uapi::input_events read from this input file by external process.
70    pub uapi_events_read_count: AtomicU64,
71
72    /// The event time of the last uapi::input_event read by external process.
73    pub last_read_uapi_event_timestamp_ns: AtomicI64,
74
75    /// Number of read calls.
76    pub fd_read_count: AtomicU64,
77
78    /// Number of notify calls.
79    pub fd_notify_count: AtomicU64,
80
81    /// Whether the file was opened without the NONBLOCK flag.
82    pub opened_without_nonblock: AtomicBool,
83
84    /// The timestamp when the file was opened.
85    pub open_timestamp_ns: AtomicI64,
86
87    /// Whether the file was closed (dropped).
88    pub closed: AtomicBool,
89
90    /// The timestamp when the file was closed.
91    pub close_timestamp_ns: AtomicI64,
92
93    /// The weak pointer to the InputFile.
94    pub input_file: LockDepMutex<Weak<InputFile>, InputFileInputFileLock>,
95}
96
97impl InputFileStatus {
98    fn new(node: &fuchsia_inspect::Node) -> Arc<Self> {
99        let status = Arc::new(Self {
100            fidl_events_received_count: AtomicU64::new(0),
101            fidl_events_ignored_count: AtomicU64::new(0),
102            fidl_events_unexpected_count: AtomicU64::new(0),
103            fidl_events_converted_count: AtomicU64::new(0),
104            uapi_events_generated_count: AtomicU64::new(0),
105            last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
106            uapi_events_read_count: AtomicU64::new(0),
107            last_read_uapi_event_timestamp_ns: AtomicI64::new(0),
108            fd_read_count: AtomicU64::new(0),
109            fd_notify_count: AtomicU64::new(0),
110            opened_without_nonblock: AtomicBool::new(false),
111            open_timestamp_ns: AtomicI64::new(0),
112            closed: AtomicBool::new(false),
113            close_timestamp_ns: AtomicI64::new(0),
114            input_file: Default::default(),
115        });
116
117        let cloned_status = status.clone();
118        node.record_lazy_values("status", move || {
119            let cloned_cloned_status = cloned_status.clone();
120            async move {
121                let is_dropped = cloned_cloned_status.input_file.lock().upgrade().is_none();
122                if is_dropped && !cloned_cloned_status.closed.load(Ordering::Relaxed) {
123                    cloned_cloned_status.closed.store(true, Ordering::Relaxed);
124                }
125
126                let inspector = Inspector::default();
127                let root = inspector.root();
128                root.record_uint(
129                    "fd_read_count",
130                    cloned_cloned_status.fd_read_count.load(Ordering::Relaxed),
131                );
132                root.record_uint(
133                    "fd_notify_count",
134                    cloned_cloned_status.fd_notify_count.load(Ordering::Relaxed),
135                );
136                root.record_bool(
137                    "opened_without_nonblock",
138                    cloned_cloned_status.opened_without_nonblock.load(Ordering::Relaxed),
139                );
140                root.record_int(
141                    "open_timestamp_ns",
142                    cloned_cloned_status.open_timestamp_ns.load(Ordering::Relaxed),
143                );
144                root.record_bool("closed", cloned_cloned_status.closed.load(Ordering::Relaxed));
145                root.record_int(
146                    "close_timestamp_ns",
147                    cloned_cloned_status.close_timestamp_ns.load(Ordering::Relaxed),
148                );
149                root.record_uint(
150                    "fidl_events_received_count",
151                    cloned_cloned_status.fidl_events_received_count.load(Ordering::Relaxed),
152                );
153                root.record_uint(
154                    "fidl_events_ignored_count",
155                    cloned_cloned_status.fidl_events_ignored_count.load(Ordering::Relaxed),
156                );
157                root.record_uint(
158                    "fidl_events_unexpected_count",
159                    cloned_cloned_status.fidl_events_unexpected_count.load(Ordering::Relaxed),
160                );
161                root.record_uint(
162                    "fidl_events_converted_count",
163                    cloned_cloned_status.fidl_events_converted_count.load(Ordering::Relaxed),
164                );
165                root.record_uint(
166                    "uapi_events_generated_count",
167                    cloned_cloned_status.uapi_events_generated_count.load(Ordering::Relaxed),
168                );
169                root.record_int(
170                    "last_generated_uapi_event_timestamp_ns",
171                    cloned_cloned_status
172                        .last_generated_uapi_event_timestamp_ns
173                        .load(Ordering::Relaxed),
174                );
175                root.record_uint(
176                    "uapi_events_read_count",
177                    cloned_cloned_status.uapi_events_read_count.load(Ordering::Relaxed),
178                );
179                root.record_int(
180                    "last_read_uapi_event_timestamp_ns",
181                    cloned_cloned_status.last_read_uapi_event_timestamp_ns.load(Ordering::Relaxed),
182                );
183                Ok(inspector)
184            }
185            .boxed()
186        });
187
188        status
189    }
190
191    pub fn count_received_events(&self, count: u64) {
192        self.fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
193    }
194
195    pub fn count_ignored_events(&self, count: u64) {
196        self.fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
197    }
198
199    pub fn count_unexpected_events(&self, count: u64) {
200        self.fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
201    }
202
203    pub fn count_converted_events(&self, count: u64) {
204        self.fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
205    }
206
207    pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
208        self.uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
209        self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
210    }
211
212    pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
213        self.uapi_events_read_count.fetch_add(count, Ordering::Relaxed);
214        self.last_read_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
215    }
216
217    pub fn count_fd_read_calls(&self) {
218        self.fd_read_count.fetch_add(1, Ordering::Relaxed);
219    }
220
221    pub fn count_fd_notify_calls(&self) {
222        self.fd_notify_count.fetch_add(1, Ordering::Relaxed);
223    }
224
225    pub fn set_opened_without_nonblock(&self) {
226        self.opened_without_nonblock.store(true, Ordering::Relaxed);
227    }
228
229    pub fn set_open_timestamp(&self, timestamp: i64) {
230        self.open_timestamp_ns.store(timestamp, Ordering::Relaxed);
231    }
232
233    pub fn set_closed(&self) {
234        self.closed.store(true, Ordering::Relaxed);
235    }
236
237    pub fn set_closed_timestamp(&self, timestamp: i64) {
238        self.close_timestamp_ns.store(timestamp, Ordering::Relaxed);
239    }
240}
241
242pub struct InputFile {
243    driver_version: u32,
244    info: InputDeviceInfoHandle,
245    supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
246    supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
247    supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>, // ABSolute position
248    supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>,   // RELative motion
249    supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
250    supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
251    supported_haptics: BitSet<{ min_bytes(FF_CNT) }>, // 'F'orce 'F'eedback
252    supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
253    properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
254    mt_slot_axis_info: uapi::input_absinfo,
255    mt_tracking_id_axis_info: uapi::input_absinfo,
256    x_axis_info: uapi::input_absinfo,
257    y_axis_info: uapi::input_absinfo,
258    events: SegQueue<LinuxEventWithTraceId>,
259    waiters: WaitQueue,
260    // InputFile will be initialized with an InputFileStatus that holds Inspect data
261    // `None` for Uinput InputFiles
262    pub inspect_status: Option<Arc<InputFileStatus>>,
263}
264
265pub struct LinuxEventWithTraceId {
266    pub event: uapi::input_event,
267    pub trace_id: Option<fuchsia_trace::Id>,
268}
269
270impl LinuxEventWithTraceId {
271    pub fn new(event: uapi::input_event) -> Self {
272        match event.type_ as u32 {
273            uapi::EV_SYN => {
274                let trace_id = fuchsia_trace::Id::new();
275                fuchsia_trace::duration!("input", "linux_event_create");
276                fuchsia_trace::flow_begin!("input", "linux_event", trace_id);
277                LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
278            }
279            // EV_SYN marks the end of a complete input event. Other event types are its properties,
280            // so they don't initiate a trace.
281            _ => LinuxEventWithTraceId { event: event, trace_id: None },
282        }
283    }
284}
285
286/// Returns the minimum number of bytes required to store `n_bits` bits.
287const fn min_bytes(n_bits: u32) -> usize {
288    ((n_bits as usize) + 7) / 8
289}
290
291/// Returns appropriate `INPUT_PROP`-erties for a keyboard device.
292fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
293    let mut attrs = BitSet::new();
294    attrs.set(INPUT_PROP_DIRECT);
295    attrs
296}
297
298/// Returns appropriate `KEY`-board related flags for a touchscreen device.
299fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
300    let mut attrs = BitSet::new();
301    attrs.set(BTN_TOUCH);
302    attrs.set(BTN_MISC); // Include BTN_MISC as a catchall key event.
303    attrs.set(KEY_SLEEP);
304    attrs.set(KEY_UP);
305    attrs.set(KEY_LEFT);
306    attrs.set(KEY_RIGHT);
307    attrs.set(KEY_DOWN);
308
309    attrs
310}
311
312/// Returns appropriate `ABS`-olute position related flags for a touchscreen device.
313fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
314    let mut attrs = BitSet::new();
315    attrs.set(ABS_MT_SLOT);
316    attrs.set(ABS_MT_TRACKING_ID);
317    attrs.set(ABS_MT_POSITION_X);
318    attrs.set(ABS_MT_POSITION_Y);
319    attrs
320}
321
322/// Returns appropriate `INPUT_PROP`-erties for a touchscreen device.
323fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
324    let mut attrs = BitSet::new();
325    attrs.set(INPUT_PROP_DIRECT);
326    attrs
327}
328
329/// Returns appropriate `KEY`-board related flags for a keyboard device.
330fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
331    let mut attrs = BitSet::new();
332    for keycode in starnix_modules_input_event_conversion::keymap::KEY_MAP.all_linux_keycodes() {
333        // `KEY_MAP` also carries a block of test-only keycodes (see b/311425670), 31 of which
334        // fall in the `BTN_*` range. Advertising those would make evdev clients classify this
335        // device as a gamepad in addition to a keyboard, which is the opposite of what this
336        // capability list is for. No real `KEY_*` constant lives in `BTN_MISC..KEY_OK`.
337        if keycode >= BTN_MISC && keycode < KEY_OK {
338            continue;
339        }
340        if (keycode as usize) < KEY_CNT as usize {
341            attrs.set(keycode);
342        }
343    }
344    attrs.set(BTN_MISC);
345    attrs.set(KEY_POWER);
346    attrs.set(KEY_VOLUMEUP);
347    attrs.set(KEY_VOLUMEDOWN);
348    attrs
349}
350
351/// Returns appropriate `ABS`-olute position related flags for a keyboard device.
352fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
353    BitSet::new()
354}
355
356/// Returns appropriate `KEY`-board/button related flags for a mouse device.
357fn mouse_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
358    let mut attrs = BitSet::new();
359    // In Linux UAPI, BTN_LEFT has the numeric value 0x110, which is also aliased to BTN_MOUSE.
360    // Evdev and libevdev treat BTN_MOUSE as the primary indicator that a device is a mouse:
361    // https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/android/platform/external/libevdev/libevdev/libevdev.c;l=1134-1140;drc=7007dd6442654da8be96df23cf632ae0e87d7b30
362    attrs.set(BTN_LEFT);
363    attrs.set(BTN_RIGHT);
364    attrs.set(BTN_MIDDLE);
365    attrs.set(BTN_SIDE);
366    attrs.set(BTN_EXTRA);
367    attrs
368}
369
370/// Returns appropriate `REL`-ative motion related flags for a mouse device.
371fn mouse_motion_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
372    let mut attrs = BitSet::new();
373    attrs.set(REL_X);
374    attrs.set(REL_Y);
375    attrs.set(REL_WHEEL);
376    attrs.set(REL_HWHEEL);
377    attrs
378}
379
380/// Returns appropriate `INPUT_PROP`-erties for a mouse device.
381fn mouse_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
382    let mut attrs = BitSet::new();
383    // INPUT_PROP_POINTER indicates a mouse/pointer device.
384    // INPUT_PROP_DIRECT is for direct touchscreens and must NOT be set for a mouse.
385    attrs.set(INPUT_PROP_POINTER);
386    attrs
387}
388
389impl InputFile {
390    // Per https://www.linuxjournal.com/article/6429, the driver version is 32-bits wide,
391    // and interpreted as:
392    // * [31-16]: version
393    // * [15-08]: minor
394    // * [07-00]: patch level
395    const DRIVER_VERSION: u32 = 0;
396
397    /// Creates an `InputFile` instance suitable for emulating a touchscreen.
398    ///
399    /// # Parameters
400    /// - `info`: input device information.
401    /// - `width`: width of screen.
402    /// - `height`: height of screen.
403    /// - `node`: The inspect node for the parent device of "touch_input_file".
404    pub fn new_touch(
405        info: InputDeviceInfoHandle,
406        width: i32,
407        height: i32,
408        node: &fuchsia_inspect::Node,
409    ) -> Self {
410        // Fuchsia scales the position reported by the touch sensor to fit view coordinates.
411        // Hence, the range of touch positions is exactly the same as the range of view
412        // coordinates.
413        Self {
414            driver_version: Self::DRIVER_VERSION,
415            info,
416            supported_event_types: BitSet::list([uapi::EV_ABS]),
417            supported_keys: touch_key_attributes(),
418            supported_position_attributes: touch_position_attributes(),
419            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
420            supported_switches: BitSet::new(),          // None supported
421            supported_leds: BitSet::new(),              // None supported
422            supported_haptics: BitSet::new(),           // None supported
423            supported_misc_features: BitSet::new(),     // None supported
424            properties: touch_properties(),
425            mt_slot_axis_info: uapi::input_absinfo {
426                minimum: 0,
427                maximum: 10,
428                ..uapi::input_absinfo::default()
429            },
430            mt_tracking_id_axis_info: uapi::input_absinfo {
431                minimum: 0,
432                maximum: i32::MAX,
433                ..uapi::input_absinfo::default()
434            },
435            x_axis_info: uapi::input_absinfo {
436                minimum: 0,
437                maximum: i32::from(width),
438                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
439                // X position.
440                ..uapi::input_absinfo::default()
441            },
442            y_axis_info: uapi::input_absinfo {
443                minimum: 0,
444                maximum: i32::from(height),
445                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
446                // Y position.
447                ..uapi::input_absinfo::default()
448            },
449            events: SegQueue::new(),
450            waiters: WaitQueue::default(),
451            inspect_status: Some(InputFileStatus::new(node)),
452        }
453    }
454
455    /// Creates an `InputFile` instance suitable for emulating a keyboard.
456    ///
457    /// # Parameters
458    /// - `info`: input device information.
459    /// - `node`: The inspect node for the parent device of "keyboard_input_file".
460    pub fn new_keyboard(info: InputDeviceInfoHandle, node: &fuchsia_inspect::Node) -> Self {
461        Self {
462            driver_version: Self::DRIVER_VERSION,
463            info,
464            supported_event_types: BitSet::list([uapi::EV_KEY]),
465            supported_keys: keyboard_key_attributes(),
466            supported_position_attributes: keyboard_position_attributes(),
467            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
468            supported_switches: BitSet::new(),          // None supported
469            supported_leds: BitSet::new(),              // None supported
470            supported_haptics: BitSet::new(),           // None supported
471            supported_misc_features: BitSet::new(),     // None supported
472            properties: keyboard_properties(),
473            mt_slot_axis_info: uapi::input_absinfo::default(),
474            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
475            x_axis_info: uapi::input_absinfo::default(),
476            y_axis_info: uapi::input_absinfo::default(),
477            events: SegQueue::new(),
478            waiters: WaitQueue::default(),
479            inspect_status: Some(InputFileStatus::new(node)),
480        }
481    }
482
483    /// Creates an `InputFile` instance suitable for emulating a mouse.
484    ///
485    /// # Parameters
486    /// - `info`: input device information.
487    /// - `node`: The inspect node for the parent device of "mouse_input_file".
488    pub fn new_mouse(info: InputDeviceInfoHandle, node: &fuchsia_inspect::Node) -> Self {
489        Self {
490            driver_version: Self::DRIVER_VERSION,
491            info,
492            // Mice report relative motion via EV_REL and buttons via EV_KEY.
493            // Absolute motion (EV_ABS) is not supported to avoid misclassification
494            // as a touch digitizer by libinput/Android EventHub.
495            supported_event_types: BitSet::list([uapi::EV_KEY, uapi::EV_REL]),
496            supported_keys: mouse_key_attributes(),
497            supported_position_attributes: BitSet::new(), // Mice report relative motion, not absolute.
498            supported_motion_attributes: mouse_motion_attributes(),
499            supported_switches: BitSet::new(), // None supported
500            supported_leds: BitSet::new(),     // None supported
501            supported_haptics: BitSet::new(),  // None supported
502            supported_misc_features: BitSet::new(), // None supported
503            properties: mouse_properties(),
504            mt_slot_axis_info: uapi::input_absinfo::default(),
505            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
506            x_axis_info: uapi::input_absinfo::default(),
507            y_axis_info: uapi::input_absinfo::default(),
508            events: SegQueue::new(),
509            waiters: WaitQueue::default(),
510            inspect_status: Some(InputFileStatus::new(node)),
511        }
512    }
513
514    pub fn init_inspect_status(self: &Arc<Self>) {
515        if let Some(inspect) = &self.inspect_status {
516            *inspect.input_file.lock() = Arc::downgrade(self);
517        }
518    }
519
520    pub fn add_events(&self, events: Vec<uapi::input_event>) {
521        if events.is_empty() {
522            return;
523        }
524        if let Some(inspect) = &self.inspect_status {
525            inspect.count_fd_notify_calls();
526        }
527        for event in events {
528            self.events.push(LinuxEventWithTraceId::new(event));
529        }
530        self.waiters.notify_fd_events(FdEvents::POLLIN);
531    }
532
533    pub fn read_events(&self, limit: usize) -> Vec<LinuxEventWithTraceId> {
534        if let Some(inspect) = &self.inspect_status {
535            inspect.count_fd_read_calls();
536        }
537        let mut events = vec![];
538        for _ in 0..limit {
539            if let Some(event) = self.events.pop() {
540                events.push(event);
541            } else {
542                break;
543            }
544        }
545        // We do not notify if the buffer was not enough to read all events.
546        // `query_events` will still return `FdEvents::POLLIN` if there are remaining events,
547        // so the caller can continue reading or poll again.
548        events
549    }
550}
551
552// Remove the variable part of the request with params, so we can identify it.
553// Lowest 14 bits of the top 16 bits encode the buffer length in bytes.
554// See https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/android/platform/bionic/libc/kernel/uapi/linux/input.h;l=82;drc=0f0c18f695543b15b852f68f297744d03d642a26
555pub(crate) const EVIOC_VAR_LEN_MASK: u32 = !(uapi::_IOC_SIZEMASK << uapi::_IOC_SIZESHIFT);
556
557pub(crate) const EVIOCGNAME_BASE: u32 = uapi::EVIOCGNAME_0 & EVIOC_VAR_LEN_MASK;
558pub(crate) const EVIOCGPHYS_BASE: u32 = uapi::EVIOCGPHYS_0 & EVIOC_VAR_LEN_MASK;
559pub(crate) const EVIOCGUNIQ_BASE: u32 = uapi::EVIOCGUNIQ_0 & EVIOC_VAR_LEN_MASK;
560pub(crate) const EVIOCGKEY_BASE: u32 = uapi::EVIOCGKEY_0 & EVIOC_VAR_LEN_MASK;
561pub(crate) const EVIOCGLED_BASE: u32 = uapi::EVIOCGLED_0 & EVIOC_VAR_LEN_MASK;
562pub(crate) const EVIOCGSND_BASE: u32 = uapi::EVIOCGSND_0 & EVIOC_VAR_LEN_MASK;
563pub(crate) const EVIOCGSW_BASE: u32 = uapi::EVIOCGSW_0 & EVIOC_VAR_LEN_MASK;
564pub(crate) const EVIOCGPROP_BASE: u32 = uapi::EVIOCGPROP & EVIOC_VAR_LEN_MASK;
565pub(crate) const EVIOCGBIT_0_BASE: u32 = uapi::EVIOCGBIT_0 & EVIOC_VAR_LEN_MASK;
566pub(crate) const EVIOCGBIT_EV_KEY_BASE: u32 = uapi::EVIOCGBIT_EV_KEY & EVIOC_VAR_LEN_MASK;
567pub(crate) const EVIOCGBIT_EV_ABS_BASE: u32 = uapi::EVIOCGBIT_EV_ABS & EVIOC_VAR_LEN_MASK;
568pub(crate) const EVIOCGBIT_EV_REL_BASE: u32 = uapi::EVIOCGBIT_EV_REL & EVIOC_VAR_LEN_MASK;
569pub(crate) const EVIOCGBIT_EV_SW_BASE: u32 = uapi::EVIOCGBIT_EV_SW & EVIOC_VAR_LEN_MASK;
570pub(crate) const EVIOCGBIT_EV_LED_BASE: u32 = uapi::EVIOCGBIT_EV_LED & EVIOC_VAR_LEN_MASK;
571pub(crate) const EVIOCGBIT_EV_FF_BASE: u32 = uapi::EVIOCGBIT_EV_FF & EVIOC_VAR_LEN_MASK;
572pub(crate) const EVIOCGBIT_EV_MSC_BASE: u32 = uapi::EVIOCGBIT_EV_MSC & EVIOC_VAR_LEN_MASK;
573pub(crate) const EVIOCGBIT_EV_SND_BASE: u32 = uapi::EVIOCGBIT_EV_SND & EVIOC_VAR_LEN_MASK;
574
575impl FileOps for InputFile {
576    fileops_impl_nonseekable!();
577    fileops_impl_noop_sync!();
578
579    fn open(&self, file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
580        if let Some(inspect) = &self.inspect_status {
581            inspect.set_open_timestamp(zx::MonotonicInstant::get().into_nanos());
582            if (file.flags() & OpenFlags::NONBLOCK) != OpenFlags::NONBLOCK {
583                inspect.set_opened_without_nonblock();
584            }
585        }
586        Ok(())
587    }
588
589    fn close(
590        self: Box<Self>,
591        _file: &starnix_core::vfs::FileObjectState,
592        _current_task: &CurrentTask,
593    ) {
594        if let Some(inspect) = &self.inspect_status {
595            inspect.set_closed();
596            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
597        }
598    }
599
600    fn ioctl(
601        &self,
602        _file: &FileObject,
603        current_task: &CurrentTask,
604        request: u32,
605        arg: SyscallArg,
606    ) -> Result<SyscallResult, Errno> {
607        let user_addr = UserAddress::from(arg);
608        match request {
609            uapi::EVIOCGVERSION => {
610                current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
611                Ok(SUCCESS)
612            }
613            uapi::EVIOCGID => {
614                let input_id = self.info.lock().input_id;
615                current_task.write_object(UserRef::new(user_addr), &input_id)?;
616                Ok(SUCCESS)
617            }
618            uapi::EVIOCGABS_MT_SLOT => {
619                current_task.write_object(UserRef::new(user_addr), &self.mt_slot_axis_info)?;
620                Ok(SUCCESS)
621            }
622            uapi::EVIOCGABS_MT_TRACKING_ID => {
623                current_task
624                    .write_object(UserRef::new(user_addr), &self.mt_tracking_id_axis_info)?;
625                Ok(SUCCESS)
626            }
627            uapi::EVIOCGABS_MT_POSITION_X => {
628                current_task.write_object(UserRef::new(user_addr), &self.x_axis_info)?;
629                Ok(SUCCESS)
630            }
631            uapi::EVIOCGABS_MT_POSITION_Y => {
632                current_task.write_object(UserRef::new(user_addr), &self.y_axis_info)?;
633                Ok(SUCCESS)
634            }
635            uapi::EVIOCGRAB => {
636                // Xorg and libevdev use EVIOCGRAB to obtain exclusive access to the device.
637                track_stub!(TODO("https://fxbug.dev/322873200"), "EVIOCGRAB");
638                Ok(SUCCESS)
639            }
640
641            request_with_params => {
642                // The lowest 14 bits of the top 16 bits are the unsigned buffer length in bytes.
643                let buffer_bytes_count =
644                    ((request_with_params >> uapi::_IOC_SIZESHIFT) & uapi::_IOC_SIZEMASK) as usize;
645
646                let dir = (request_with_params >> uapi::_IOC_DIRSHIFT) & uapi::_IOC_DIRMASK;
647                let ioc_type = (request_with_params >> uapi::_IOC_TYPESHIFT) & uapi::_IOC_TYPEMASK;
648
649                // Validate that this is an evdev read ioctl ('E').
650                if dir != uapi::_IOC_READ || ioc_type != (b'E' as u32) {
651                    track_stub!(
652                        TODO("https://fxbug.dev/322873200"),
653                        "input ioctl invalid type or dir",
654                        request_with_params
655                    );
656                    return error!(EINVAL);
657                }
658
659                // Helper to copy a bitset slice to user memory.
660                // Note: Linux evdev's bits_to_user returns the number of bytes copied, whereas
661                // existing Starnix ioctl handlers and tests expect 0 (SUCCESS).
662                let write_bits = |bits: &[u8]| -> Result<SyscallResult, Errno> {
663                    if buffer_bytes_count == 0 {
664                        return Ok(SUCCESS);
665                    }
666                    // Zero out the entire user buffer in case the user reads too much.
667                    if current_task.zero(user_addr, buffer_bytes_count)? != buffer_bytes_count {
668                        return error!(EFAULT);
669                    }
670                    let to_copy = std::cmp::min(bits.len(), buffer_bytes_count);
671                    current_task.write_memory(user_addr, &bits[..to_copy])?;
672                    Ok(SUCCESS)
673                };
674
675                // Helper to copy a NUL-terminated string to user memory and return bytes written
676                // including the trailing NUL.
677                let write_string = |bytes: &[u8]| -> Result<SyscallResult, Errno> {
678                    if buffer_bytes_count == 0 {
679                        return Ok(SUCCESS);
680                    }
681                    // Zero out the entire user buffer in case the user reads too much.
682                    if current_task.zero(user_addr, buffer_bytes_count)? != buffer_bytes_count {
683                        return error!(EFAULT);
684                    }
685                    let to_copy = std::cmp::min(bytes.len(), buffer_bytes_count.saturating_sub(1));
686                    current_task.write_memory(user_addr, &bytes[..to_copy])?;
687                    // String queries (EVIOCGNAME, EVIOCGPHYS) return the number of bytes written,
688                    // including the trailing NUL.
689                    Ok((to_copy + 1).into())
690                };
691
692                match request_with_params & EVIOC_VAR_LEN_MASK {
693                    EVIOCGBIT_0_BASE => write_bits(&self.supported_event_types.bytes),
694                    EVIOCGBIT_EV_KEY_BASE => write_bits(&self.supported_keys.bytes),
695                    EVIOCGBIT_EV_ABS_BASE => write_bits(&self.supported_position_attributes.bytes),
696                    EVIOCGBIT_EV_REL_BASE => write_bits(&self.supported_motion_attributes.bytes),
697                    EVIOCGBIT_EV_SW_BASE => write_bits(&self.supported_switches.bytes),
698                    EVIOCGBIT_EV_LED_BASE => write_bits(&self.supported_leds.bytes),
699                    EVIOCGBIT_EV_FF_BASE => write_bits(&self.supported_haptics.bytes),
700                    EVIOCGBIT_EV_MSC_BASE => write_bits(&self.supported_misc_features.bytes),
701                    EVIOCGBIT_EV_SND_BASE => write_bits(&[]),
702                    EVIOCGPROP_BASE => write_bits(&self.properties.bytes),
703                    EVIOCGNAME_BASE => {
704                        let name = self.info.lock().name.clone();
705                        write_string(name.as_bytes())
706                    }
707                    EVIOCGPHYS_BASE => {
708                        let phys = format!("starnix/{}", self.info.lock().name);
709                        write_string(phys.as_bytes())
710                    }
711                    EVIOCGUNIQ_BASE => {
712                        // Starnix synthetic devices do not have a unique identifier (serial/MAC).
713                        // Linux returns -ENOENT when no uniq string is set.
714                        error!(ENOENT)
715                    }
716                    EVIOCGKEY_BASE | EVIOCGLED_BASE | EVIOCGSND_BASE | EVIOCGSW_BASE => {
717                        // Current-state queries: which keys are held down, which LEDs are
718                        // lit, which sounds are playing, and which switches are toggled.
719                        // These are distinct from the EVIOCGBIT capability queries above,
720                        // which report what the device *can* do rather than what it is
721                        // doing right now.
722                        //
723                        // Starnix does not track any of this per-device state, so report
724                        // all-zeros: nothing held, lit, playing, or toggled. That is an
725                        // honest answer for these devices rather than a fabrication, but
726                        // it is still an approximation, hence the stub.
727                        //
728                        // These must not fall through to EINVAL. `xf86-input-evdev` issues
729                        // all four unconditionally during PreInit and treats any failure as
730                        // fatal, so returning an error here prevents X11 from bringing up
731                        // the keyboard, mouse, and touchscreen entirely.
732                        track_stub!(
733                            TODO("https://fxbug.dev/322873200"),
734                            "evdev current-state query",
735                            request_with_params
736                        );
737                        write_bits(&[])
738                    }
739                    _ => {
740                        track_stub!(
741                            TODO("https://fxbug.dev/322873200"),
742                            "input ioctl",
743                            request_with_params
744                        );
745                        error!(EINVAL)
746                    }
747                }
748            }
749        }
750    }
751
752    fn read(
753        &self,
754        _file: &FileObject,
755        current_task: &CurrentTask,
756        offset: usize,
757        data: &mut dyn OutputBuffer,
758    ) -> Result<usize, Errno> {
759        fuchsia_trace::duration!("input", "InputFile::read");
760        debug_assert!(offset == 0);
761        let input_event_size = InputEventPtr::size_of_object_for(current_task);
762
763        // The limit of the buffer is determined by taking the available bytes
764        // and using integer division on the size of uapi::input_event in bytes.
765        let limit = data.available() / input_event_size;
766        let events = self.read_events(limit);
767        if events.is_empty() {
768            // Returns `EAGAIN` for file is opened with or without `O_NONBLOCK`.
769            log_info!("read() returning EAGAIN");
770            return error!(EAGAIN);
771        }
772
773        let last_event_timeval = events.last().expect("events is nonempty").event.time;
774        let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
775            .unwrap()
776            .into_nanos();
777        self.inspect_status
778            .clone()
779            .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
780
781        for event in &events {
782            if let Some(trace_id) = event.trace_id {
783                fuchsia_trace::duration!("input", "linux_event_read");
784                fuchsia_trace::flow_end!("input", "linux_event", trace_id);
785            }
786        }
787
788        if current_task.is_arch32() {
789            let events: Result<Vec<uapi::arch32::input_event>, _> =
790                events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
791            let events = events.map_err(|_| errno!(EINVAL))?;
792            data.write_all(events.as_bytes())
793        } else {
794            let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
795            data.write_all(events.as_bytes())
796        }
797    }
798
799    fn write(
800        &self,
801        _file: &FileObject,
802        _current_task: &CurrentTask,
803        offset: usize,
804        _data: &mut dyn InputBuffer,
805    ) -> Result<usize, Errno> {
806        debug_assert!(offset == 0);
807        track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
808        error!(EOPNOTSUPP)
809    }
810
811    fn wait_async(
812        &self,
813        _file: &FileObject,
814        _current_task: &CurrentTask,
815        waiter: &Waiter,
816        events: FdEvents,
817        handler: EventHandler,
818    ) -> Option<WaitCanceler> {
819        Some(self.waiters.wait_async_fd_events(waiter, events, handler))
820    }
821
822    fn query_events(
823        &self,
824        _file: &FileObject,
825        _current_task: &CurrentTask,
826    ) -> Result<FdEvents, Errno> {
827        Ok(if self.events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
828    }
829}
830
831pub struct ArcInputFile(pub Arc<InputFile>);
832
833impl FileOps for ArcInputFile {
834    fileops_impl_nonseekable!();
835    fileops_impl_noop_sync!();
836
837    fn open(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
838        self.0.as_ref().open(file, current_task)
839    }
840
841    fn close(
842        self: Box<Self>,
843        _file: &starnix_core::vfs::FileObjectState,
844        _current_task: &CurrentTask,
845    ) {
846        let arc_file = *self;
847        if let Some(inspect) = &arc_file.0.inspect_status {
848            inspect.set_closed();
849            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
850        }
851    }
852
853    fn ioctl(
854        &self,
855        file: &FileObject,
856        current_task: &CurrentTask,
857        request: u32,
858        arg: SyscallArg,
859    ) -> Result<SyscallResult, Errno> {
860        self.0.as_ref().ioctl(file, current_task, request, arg)
861    }
862
863    fn read(
864        &self,
865        file: &FileObject,
866        current_task: &CurrentTask,
867        offset: usize,
868        data: &mut dyn OutputBuffer,
869    ) -> Result<usize, Errno> {
870        self.0.as_ref().read(file, current_task, offset, data)
871    }
872
873    fn write(
874        &self,
875        file: &FileObject,
876        current_task: &CurrentTask,
877        offset: usize,
878        data: &mut dyn InputBuffer,
879    ) -> Result<usize, Errno> {
880        self.0.as_ref().write(file, current_task, offset, data)
881    }
882
883    fn wait_async(
884        &self,
885        file: &FileObject,
886        current_task: &CurrentTask,
887        waiter: &Waiter,
888        events: FdEvents,
889        handler: EventHandler,
890    ) -> Option<WaitCanceler> {
891        self.0.as_ref().wait_async(file, current_task, waiter, events, handler)
892    }
893
894    fn query_events(
895        &self,
896        file: &FileObject,
897        current_task: &CurrentTask,
898    ) -> Result<FdEvents, Errno> {
899        self.0.as_ref().query_events(file, current_task)
900    }
901}
902
903pub struct BitSet<const NUM_BYTES: usize> {
904    bytes: [u8; NUM_BYTES],
905}
906
907impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
908    pub const fn new() -> Self {
909        Self { bytes: [0; NUM_BYTES] }
910    }
911
912    pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
913        let mut bitset = Self::new();
914        let mut i = 0;
915        while i < bits.len() {
916            bitset.set(bits[i]);
917            i += 1;
918        }
919        bitset
920    }
921
922    pub const fn set(&mut self, bitnum: u32) {
923        let bitnum = bitnum as usize;
924        let byte = bitnum / 8;
925        let bit = bitnum % 8;
926        self.bytes[byte] |= 1 << bit;
927    }
928
929    #[cfg(test)]
930    pub fn get(&self, bitnum: u32) -> bool {
931        let bitnum = bitnum as usize;
932        let byte = bitnum / 8;
933        let bit = bitnum % 8;
934        (self.bytes[byte] & (1 << bit)) != 0
935    }
936}
937
938#[cfg(test)]
939mod tests {
940    use super::*;
941    use crate::input_device::InputDeviceInfo;
942    use std::sync::atomic::Ordering;
943
944    #[test]
945    fn test_keyboard_input_file_attributes() {
946        let inspector = fuchsia_inspect::Inspector::default();
947        let node = inspector.root();
948        let keyboard_file = InputFile::new_keyboard(
949            InputDeviceInfo::new(
950                uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
951                "keyboard_test".to_string(),
952            ),
953            node,
954        );
955
956        // Event types: EV_KEY
957        assert!(keyboard_file.supported_event_types.get(uapi::EV_KEY));
958        assert!(!keyboard_file.supported_event_types.get(uapi::EV_REL));
959        assert!(!keyboard_file.supported_event_types.get(uapi::EV_ABS));
960
961        // Mapped Linux keys from KEY_MAP
962        assert!(keyboard_file.supported_keys.get(uapi::KEY_A));
963        assert!(keyboard_file.supported_keys.get(uapi::KEY_Z));
964        assert!(keyboard_file.supported_keys.get(uapi::KEY_1));
965        assert!(keyboard_file.supported_keys.get(uapi::KEY_ENTER));
966        assert!(keyboard_file.supported_keys.get(uapi::KEY_SPACE));
967        assert!(keyboard_file.supported_keys.get(uapi::KEY_ESC));
968        assert!(keyboard_file.supported_keys.get(uapi::KEY_TAB));
969        assert!(keyboard_file.supported_keys.get(uapi::KEY_BACKSPACE));
970        assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTSHIFT));
971        assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTCTRL));
972        assert!(keyboard_file.supported_keys.get(uapi::KEY_LEFTALT));
973        assert!(keyboard_file.supported_keys.get(uapi::KEY_POWER));
974        assert!(keyboard_file.supported_keys.get(uapi::KEY_VOLUMEUP));
975        assert!(keyboard_file.supported_keys.get(uapi::KEY_VOLUMEDOWN));
976        assert!(keyboard_file.supported_keys.get(uapi::BTN_MISC));
977
978        // Properties: INPUT_PROP_DIRECT
979        assert!(keyboard_file.properties.get(uapi::INPUT_PROP_DIRECT));
980        assert!(!keyboard_file.properties.get(uapi::INPUT_PROP_POINTER));
981    }
982
983    #[test]
984    fn test_read_events_no_notify_when_buffer_full() {
985        let inspector = fuchsia_inspect::Inspector::default();
986        let node = inspector.root();
987        let input_file = InputFile::new_touch(
988            InputDeviceInfo::new(
989                uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
990                "touch_test".to_string(),
991            ),
992            100,
993            100,
994            node,
995        );
996
997        // Add some events.
998        let event1 = uapi::input_event {
999            type_: uapi::EV_KEY as u16,
1000            code: 1,
1001            value: 1,
1002            ..Default::default()
1003        };
1004        let event2 = uapi::input_event {
1005            type_: uapi::EV_KEY as u16,
1006            code: 2,
1007            value: 1,
1008            ..Default::default()
1009        };
1010        input_file.add_events(vec![event1, event2]);
1011
1012        // Verify that adding events triggered a notification.
1013        let notify_count =
1014            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1015        assert_eq!(notify_count, 1);
1016
1017        // Read with a limit of 1.
1018        let events = input_file.read_events(1);
1019        assert_eq!(events.len(), 1);
1020
1021        // Verify that no additional notification was sent despite more events remaining.
1022        let notify_count =
1023            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
1024        assert_eq!(notify_count, 1);
1025    }
1026
1027    #[test]
1028    fn test_bitset_set_and_get() {
1029        let mut bitset = BitSet::<4>::new();
1030        assert!(!bitset.get(0));
1031        assert!(!bitset.get(15));
1032        assert!(!bitset.get(31));
1033
1034        bitset.set(0);
1035        bitset.set(15);
1036        bitset.set(31);
1037
1038        assert!(bitset.get(0));
1039        assert!(bitset.get(15));
1040        assert!(bitset.get(31));
1041        assert!(!bitset.get(1));
1042        assert!(!bitset.get(14));
1043        assert!(!bitset.get(30));
1044    }
1045
1046    #[test]
1047    fn test_mouse_capabilities() {
1048        let inspector = fuchsia_inspect::Inspector::default();
1049        let node = inspector.root();
1050        let mouse_file = InputFile::new_mouse(
1051            InputDeviceInfo::new(
1052                uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1053                "mouse_test".to_string(),
1054            ),
1055            node,
1056        );
1057
1058        // EV_KEY and EV_REL supported, EV_ABS not supported.
1059        assert!(mouse_file.supported_event_types.get(uapi::EV_KEY));
1060        assert!(mouse_file.supported_event_types.get(uapi::EV_REL));
1061        assert!(!mouse_file.supported_event_types.get(uapi::EV_ABS));
1062
1063        // Buttons supported: BTN_LEFT (BTN_MOUSE), BTN_RIGHT, BTN_MIDDLE, BTN_SIDE, BTN_EXTRA.
1064        assert!(mouse_file.supported_keys.get(BTN_LEFT));
1065        assert!(mouse_file.supported_keys.get(BTN_RIGHT));
1066        assert!(mouse_file.supported_keys.get(BTN_MIDDLE));
1067        assert!(mouse_file.supported_keys.get(BTN_SIDE));
1068        assert!(mouse_file.supported_keys.get(BTN_EXTRA));
1069        assert!(!mouse_file.supported_keys.get(BTN_TOUCH));
1070
1071        // Relative motion: REL_X, REL_Y, REL_WHEEL, REL_HWHEEL.
1072        assert!(mouse_file.supported_motion_attributes.get(REL_X));
1073        assert!(mouse_file.supported_motion_attributes.get(REL_Y));
1074        assert!(mouse_file.supported_motion_attributes.get(REL_WHEEL));
1075        assert!(mouse_file.supported_motion_attributes.get(REL_HWHEEL));
1076
1077        // Properties: INPUT_PROP_POINTER supported, INPUT_PROP_DIRECT not supported.
1078        assert!(mouse_file.properties.get(INPUT_PROP_POINTER));
1079        assert!(!mouse_file.properties.get(INPUT_PROP_DIRECT));
1080    }
1081}