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