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_MISC,
23    BTN_TOUCH, EV_CNT, FF_CNT, INPUT_PROP_CNT, INPUT_PROP_DIRECT, KEY_CNT, KEY_DOWN, KEY_LEFT,
24    KEY_POWER, KEY_RIGHT, KEY_SLEEP, KEY_UP, KEY_VOLUMEDOWN, KEY_VOLUMEUP, LED_CNT, MSC_CNT,
25    REL_CNT, REL_WHEEL, SW_CNT, errno, error, uapi,
26};
27use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
28use std::sync::{Arc, Weak};
29use zerocopy::IntoBytes as _; // for `as_bytes()`
30
31uapi::check_arch_independent_layout! {
32    input_id {}
33    input_absinfo {}
34}
35
36type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
37
38pub struct InputFileStatus {
39    /// The number of FIDL events received by this file from Fuchsia input system.
40    ///
41    /// We expect:
42    /// fidl_events_received_count = fidl_events_ignored_count +
43    ///                              fidl_events_unexpected_count +
44    ///                              fidl_events_converted_count
45    /// otherwise starnix ignored events unexpectedly.
46    ///
47    /// fidl_events_unexpected_count should be 0, if not it hints issues from upstream of ui stack.
48    pub fidl_events_received_count: AtomicU64,
49
50    /// The number of FIDL events ignored to this module’s representation of TouchEvent.
51    pub fidl_events_ignored_count: AtomicU64,
52
53    /// The unexpected number of FIDL events reached to this module should be filtered out
54    /// earlier in the UI stack.
55    /// It maybe unexpected format or unexpected order.
56    pub fidl_events_unexpected_count: AtomicU64,
57
58    /// The number of FIDL events converted to this module’s representation of TouchEvent.
59    pub fidl_events_converted_count: AtomicU64,
60
61    /// The number of uapi::input_events generated from TouchEvents.
62    pub uapi_events_generated_count: AtomicU64,
63
64    /// The event time of the last generated uapi::input_event.
65    pub last_generated_uapi_event_timestamp_ns: AtomicI64,
66
67    /// The number of uapi::input_events read from this input file by external process.
68    pub uapi_events_read_count: AtomicU64,
69
70    /// The event time of the last uapi::input_event read by external process.
71    pub last_read_uapi_event_timestamp_ns: AtomicI64,
72
73    /// Number of read calls.
74    pub fd_read_count: AtomicU64,
75
76    /// Number of notify calls.
77    pub fd_notify_count: AtomicU64,
78
79    /// Whether the file was opened without the NONBLOCK flag.
80    pub opened_without_nonblock: AtomicBool,
81
82    /// The timestamp when the file was opened.
83    pub open_timestamp_ns: AtomicI64,
84
85    /// Whether the file was closed (dropped).
86    pub closed: AtomicBool,
87
88    /// The timestamp when the file was closed.
89    pub close_timestamp_ns: AtomicI64,
90
91    /// The weak pointer to the InputFile.
92    pub input_file: LockDepMutex<Weak<InputFile>, InputFileInputFileLock>,
93}
94
95impl InputFileStatus {
96    fn new(node: &fuchsia_inspect::Node) -> Arc<Self> {
97        let status = Arc::new(Self {
98            fidl_events_received_count: AtomicU64::new(0),
99            fidl_events_ignored_count: AtomicU64::new(0),
100            fidl_events_unexpected_count: AtomicU64::new(0),
101            fidl_events_converted_count: AtomicU64::new(0),
102            uapi_events_generated_count: AtomicU64::new(0),
103            last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
104            uapi_events_read_count: AtomicU64::new(0),
105            last_read_uapi_event_timestamp_ns: AtomicI64::new(0),
106            fd_read_count: AtomicU64::new(0),
107            fd_notify_count: AtomicU64::new(0),
108            opened_without_nonblock: AtomicBool::new(false),
109            open_timestamp_ns: AtomicI64::new(0),
110            closed: AtomicBool::new(false),
111            close_timestamp_ns: AtomicI64::new(0),
112            input_file: Default::default(),
113        });
114
115        let cloned_status = status.clone();
116        node.record_lazy_values("status", move || {
117            let cloned_cloned_status = cloned_status.clone();
118            async move {
119                let is_dropped = cloned_cloned_status.input_file.lock().upgrade().is_none();
120                if is_dropped && !cloned_cloned_status.closed.load(Ordering::Relaxed) {
121                    cloned_cloned_status.closed.store(true, Ordering::Relaxed);
122                }
123
124                let inspector = Inspector::default();
125                let root = inspector.root();
126                root.record_uint(
127                    "fd_read_count",
128                    cloned_cloned_status.fd_read_count.load(Ordering::Relaxed),
129                );
130                root.record_uint(
131                    "fd_notify_count",
132                    cloned_cloned_status.fd_notify_count.load(Ordering::Relaxed),
133                );
134                root.record_bool(
135                    "opened_without_nonblock",
136                    cloned_cloned_status.opened_without_nonblock.load(Ordering::Relaxed),
137                );
138                root.record_int(
139                    "open_timestamp_ns",
140                    cloned_cloned_status.open_timestamp_ns.load(Ordering::Relaxed),
141                );
142                root.record_bool("closed", cloned_cloned_status.closed.load(Ordering::Relaxed));
143                root.record_int(
144                    "close_timestamp_ns",
145                    cloned_cloned_status.close_timestamp_ns.load(Ordering::Relaxed),
146                );
147                root.record_uint(
148                    "fidl_events_received_count",
149                    cloned_cloned_status.fidl_events_received_count.load(Ordering::Relaxed),
150                );
151                root.record_uint(
152                    "fidl_events_ignored_count",
153                    cloned_cloned_status.fidl_events_ignored_count.load(Ordering::Relaxed),
154                );
155                root.record_uint(
156                    "fidl_events_unexpected_count",
157                    cloned_cloned_status.fidl_events_unexpected_count.load(Ordering::Relaxed),
158                );
159                root.record_uint(
160                    "fidl_events_converted_count",
161                    cloned_cloned_status.fidl_events_converted_count.load(Ordering::Relaxed),
162                );
163                root.record_uint(
164                    "uapi_events_generated_count",
165                    cloned_cloned_status.uapi_events_generated_count.load(Ordering::Relaxed),
166                );
167                root.record_int(
168                    "last_generated_uapi_event_timestamp_ns",
169                    cloned_cloned_status
170                        .last_generated_uapi_event_timestamp_ns
171                        .load(Ordering::Relaxed),
172                );
173                root.record_uint(
174                    "uapi_events_read_count",
175                    cloned_cloned_status.uapi_events_read_count.load(Ordering::Relaxed),
176                );
177                root.record_int(
178                    "last_read_uapi_event_timestamp_ns",
179                    cloned_cloned_status.last_read_uapi_event_timestamp_ns.load(Ordering::Relaxed),
180                );
181                Ok(inspector)
182            }
183            .boxed()
184        });
185
186        status
187    }
188
189    pub fn count_received_events(&self, count: u64) {
190        self.fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
191    }
192
193    pub fn count_ignored_events(&self, count: u64) {
194        self.fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
195    }
196
197    pub fn count_unexpected_events(&self, count: u64) {
198        self.fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
199    }
200
201    pub fn count_converted_events(&self, count: u64) {
202        self.fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
203    }
204
205    pub fn count_generated_events(&self, count: u64, event_time_ns: i64) {
206        self.uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
207        self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
208    }
209
210    pub fn count_read_events(&self, count: u64, event_time_ns: i64) {
211        self.uapi_events_read_count.fetch_add(count, Ordering::Relaxed);
212        self.last_read_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
213    }
214
215    pub fn count_fd_read_calls(&self) {
216        self.fd_read_count.fetch_add(1, Ordering::Relaxed);
217    }
218
219    pub fn count_fd_notify_calls(&self) {
220        self.fd_notify_count.fetch_add(1, Ordering::Relaxed);
221    }
222
223    pub fn set_opened_without_nonblock(&self) {
224        self.opened_without_nonblock.store(true, Ordering::Relaxed);
225    }
226
227    pub fn set_open_timestamp(&self, timestamp: i64) {
228        self.open_timestamp_ns.store(timestamp, Ordering::Relaxed);
229    }
230
231    pub fn set_closed(&self) {
232        self.closed.store(true, Ordering::Relaxed);
233    }
234
235    pub fn set_closed_timestamp(&self, timestamp: i64) {
236        self.close_timestamp_ns.store(timestamp, Ordering::Relaxed);
237    }
238}
239
240pub struct InputFile {
241    driver_version: u32,
242    input_id: uapi::input_id,
243    supported_event_types: BitSet<{ min_bytes(EV_CNT) }>,
244    supported_keys: BitSet<{ min_bytes(KEY_CNT) }>,
245    supported_position_attributes: BitSet<{ min_bytes(ABS_CNT) }>, // ABSolute position
246    supported_motion_attributes: BitSet<{ min_bytes(REL_CNT) }>,   // RELative motion
247    supported_switches: BitSet<{ min_bytes(SW_CNT) }>,
248    supported_leds: BitSet<{ min_bytes(LED_CNT) }>,
249    supported_haptics: BitSet<{ min_bytes(FF_CNT) }>, // 'F'orce 'F'eedback
250    supported_misc_features: BitSet<{ min_bytes(MSC_CNT) }>,
251    properties: BitSet<{ min_bytes(INPUT_PROP_CNT) }>,
252    mt_slot_axis_info: uapi::input_absinfo,
253    mt_tracking_id_axis_info: uapi::input_absinfo,
254    x_axis_info: uapi::input_absinfo,
255    y_axis_info: uapi::input_absinfo,
256    events: SegQueue<LinuxEventWithTraceId>,
257    waiters: WaitQueue,
258    // InputFile will be initialized with an InputFileStatus that holds Inspect data
259    // `None` for Uinput InputFiles
260    pub inspect_status: Option<Arc<InputFileStatus>>,
261
262    // A descriptive device name. Should contain only alphanumerics and `_`.
263    device_name: String,
264}
265
266pub struct LinuxEventWithTraceId {
267    pub event: uapi::input_event,
268    pub trace_id: Option<fuchsia_trace::Id>,
269}
270
271impl LinuxEventWithTraceId {
272    pub fn new(event: uapi::input_event) -> Self {
273        match event.type_ as u32 {
274            uapi::EV_SYN => {
275                let trace_id = fuchsia_trace::Id::new();
276                fuchsia_trace::duration!("input", "linux_event_create");
277                fuchsia_trace::flow_begin!("input", "linux_event", trace_id);
278                LinuxEventWithTraceId { event: event, trace_id: Some(trace_id) }
279            }
280            // EV_SYN marks the end of a complete input event. Other event types are its properties,
281            // so they don't initiate a trace.
282            _ => LinuxEventWithTraceId { event: event, trace_id: None },
283        }
284    }
285}
286
287/// Returns the minimum number of bytes required to store `n_bits` bits.
288const fn min_bytes(n_bits: u32) -> usize {
289    ((n_bits as usize) + 7) / 8
290}
291
292/// Returns appropriate `INPUT_PROP`-erties for a keyboard device.
293fn keyboard_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
294    let mut attrs = BitSet::new();
295    attrs.set(INPUT_PROP_DIRECT);
296    attrs
297}
298
299/// Returns appropriate `KEY`-board related flags for a touchscreen device.
300fn touch_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
301    let mut attrs = BitSet::new();
302    attrs.set(BTN_TOUCH);
303    attrs.set(BTN_MISC); // Include BTN_MISC as a catchall key event.
304    attrs.set(KEY_SLEEP);
305    attrs.set(KEY_UP);
306    attrs.set(KEY_LEFT);
307    attrs.set(KEY_RIGHT);
308    attrs.set(KEY_DOWN);
309
310    attrs
311}
312
313/// Returns appropriate `ABS`-olute position related flags for a touchscreen device.
314fn touch_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
315    let mut attrs = BitSet::new();
316    attrs.set(ABS_MT_SLOT);
317    attrs.set(ABS_MT_TRACKING_ID);
318    attrs.set(ABS_MT_POSITION_X);
319    attrs.set(ABS_MT_POSITION_Y);
320    attrs
321}
322
323/// Returns appropriate `INPUT_PROP`-erties for a touchscreen device.
324fn touch_properties() -> BitSet<{ min_bytes(INPUT_PROP_CNT) }> {
325    let mut attrs = BitSet::new();
326    attrs.set(INPUT_PROP_DIRECT);
327    attrs
328}
329
330/// Returns appropriate `KEY`-board related flags for a keyboard device.
331fn keyboard_key_attributes() -> BitSet<{ min_bytes(KEY_CNT) }> {
332    let mut attrs = BitSet::new();
333    attrs.set(BTN_MISC);
334    attrs.set(KEY_POWER);
335    attrs.set(KEY_VOLUMEUP);
336    attrs.set(KEY_VOLUMEDOWN);
337    attrs
338}
339
340/// Returns appropriate `ABS`-olute position related flags for a keyboard device.
341fn keyboard_position_attributes() -> BitSet<{ min_bytes(ABS_CNT) }> {
342    BitSet::new()
343}
344
345fn mouse_wheel_attributes() -> BitSet<{ min_bytes(REL_CNT) }> {
346    let mut attrs = BitSet::new();
347    attrs.set(REL_WHEEL);
348    attrs
349}
350
351/// Makes a device name string from a name and device ID details.
352///
353/// For practical reasons the device name should contain alphanumerics and `_`.
354fn get_device_name(name: &str, input_id: &uapi::input_id) -> String {
355    format!("{}_{:04x}_{:04x}_v{}", name, input_id.vendor, input_id.product, input_id.version)
356}
357
358impl InputFile {
359    // Per https://www.linuxjournal.com/article/6429, the driver version is 32-bits wide,
360    // and interpreted as:
361    // * [31-16]: version
362    // * [15-08]: minor
363    // * [07-00]: patch level
364    const DRIVER_VERSION: u32 = 0;
365
366    /// Creates an `InputFile` instance suitable for emulating a touchscreen.
367    ///
368    /// # Parameters
369    /// - `input_id`: device's bustype, vendor id, product id, and version.
370    /// - `width`: width of screen.
371    /// - `height`: height of screen.
372    /// - `inspect_status`: The inspect status for the parent device of "touch_input_file".
373    pub fn new_touch(
374        input_id: uapi::input_id,
375        width: i32,
376        height: i32,
377        node: &fuchsia_inspect::Node,
378    ) -> Self {
379        let device_name = get_device_name("starnix_touch", &input_id);
380        // Fuchsia scales the position reported by the touch sensor to fit view coordinates.
381        // Hence, the range of touch positions is exactly the same as the range of view
382        // coordinates.
383        Self {
384            driver_version: Self::DRIVER_VERSION,
385            input_id,
386            supported_event_types: BitSet::list([uapi::EV_ABS]),
387            supported_keys: touch_key_attributes(),
388            supported_position_attributes: touch_position_attributes(),
389            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
390            supported_switches: BitSet::new(),          // None supported
391            supported_leds: BitSet::new(),              // None supported
392            supported_haptics: BitSet::new(),           // None supported
393            supported_misc_features: BitSet::new(),     // None supported
394            properties: touch_properties(),
395            mt_slot_axis_info: uapi::input_absinfo {
396                minimum: 0,
397                maximum: 10,
398                ..uapi::input_absinfo::default()
399            },
400            mt_tracking_id_axis_info: uapi::input_absinfo {
401                minimum: 0,
402                maximum: i32::MAX,
403                ..uapi::input_absinfo::default()
404            },
405            x_axis_info: uapi::input_absinfo {
406                minimum: 0,
407                maximum: i32::from(width),
408                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
409                // X position.
410                ..uapi::input_absinfo::default()
411            },
412            y_axis_info: uapi::input_absinfo {
413                minimum: 0,
414                maximum: i32::from(height),
415                // TODO(https://fxbug.dev/42075436): `value` field should contain the most recent
416                // Y position.
417                ..uapi::input_absinfo::default()
418            },
419            events: SegQueue::new(),
420            waiters: WaitQueue::default(),
421            inspect_status: Some(InputFileStatus::new(node)),
422            device_name,
423        }
424    }
425
426    /// Creates an `InputFile` instance suitable for emulating a keyboard.
427    ///
428    /// # Parameters
429    /// - `input_id`: device's bustype, vendor id, product id, and version.
430    /// - `inspect_status`: The inspect status for the parent device of "keyboard_input_file".
431    pub fn new_keyboard(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
432        let device_name = get_device_name("starnix_buttons", &input_id);
433        Self {
434            driver_version: Self::DRIVER_VERSION,
435            input_id,
436            supported_event_types: BitSet::list([uapi::EV_KEY]),
437            supported_keys: keyboard_key_attributes(),
438            supported_position_attributes: keyboard_position_attributes(),
439            supported_motion_attributes: BitSet::new(), // None supported, not a mouse.
440            supported_switches: BitSet::new(),          // None supported
441            supported_leds: BitSet::new(),              // None supported
442            supported_haptics: BitSet::new(),           // None supported
443            supported_misc_features: BitSet::new(),     // None supported
444            properties: keyboard_properties(),
445            mt_slot_axis_info: uapi::input_absinfo::default(),
446            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
447            x_axis_info: uapi::input_absinfo::default(),
448            y_axis_info: uapi::input_absinfo::default(),
449            events: SegQueue::new(),
450            waiters: WaitQueue::default(),
451            inspect_status: Some(InputFileStatus::new(node)),
452            device_name,
453        }
454    }
455
456    /// Creates an `InputFile` instance suitable for emulating a mouse wheel.
457    ///
458    /// # Parameters
459    /// - `input_id`: device's bustype, vendor id, product id, and version.
460    /// - `inspect_status`: The inspect status for the parent device of "mouse_input_file".
461    pub fn new_mouse(input_id: uapi::input_id, node: &fuchsia_inspect::Node) -> Self {
462        let device_name = get_device_name("starnix_mouse", &input_id);
463        Self {
464            driver_version: Self::DRIVER_VERSION,
465            input_id,
466            supported_event_types: BitSet::list([uapi::EV_REL]),
467            supported_keys: BitSet::new(), // None supported, scroll only
468            supported_position_attributes: BitSet::new(), // None supported, scroll only
469            supported_motion_attributes: mouse_wheel_attributes(),
470            supported_switches: BitSet::new(), // None supported
471            supported_leds: BitSet::new(),     // None supported
472            supported_haptics: BitSet::new(),  // None supported
473            supported_misc_features: BitSet::new(), // None supported
474            properties: BitSet::new(),         // None supported, scroll only
475            mt_slot_axis_info: uapi::input_absinfo::default(),
476            mt_tracking_id_axis_info: uapi::input_absinfo::default(),
477            x_axis_info: uapi::input_absinfo::default(),
478            y_axis_info: uapi::input_absinfo::default(),
479            events: SegQueue::new(),
480            waiters: WaitQueue::default(),
481            inspect_status: Some(InputFileStatus::new(node)),
482            device_name,
483        }
484    }
485
486    pub fn init_inspect_status(self: &Arc<Self>) {
487        if let Some(inspect) = &self.inspect_status {
488            *inspect.input_file.lock() = Arc::downgrade(self);
489        }
490    }
491
492    pub fn add_events(&self, events: Vec<uapi::input_event>) {
493        if events.is_empty() {
494            return;
495        }
496        if let Some(inspect) = &self.inspect_status {
497            inspect.count_fd_notify_calls();
498        }
499        for event in events {
500            self.events.push(LinuxEventWithTraceId::new(event));
501        }
502        self.waiters.notify_fd_events(FdEvents::POLLIN);
503    }
504
505    pub fn read_events(&self, limit: usize) -> Vec<LinuxEventWithTraceId> {
506        if let Some(inspect) = &self.inspect_status {
507            inspect.count_fd_read_calls();
508        }
509        let mut events = vec![];
510        for _ in 0..limit {
511            if let Some(event) = self.events.pop() {
512                events.push(event);
513            } else {
514                break;
515            }
516        }
517        // We do not notify if the buffer was not enough to read all events.
518        // `query_events` will still return `FdEvents::POLLIN` if there are remaining events,
519        // so the caller can continue reading or poll again.
520        events
521    }
522}
523
524// The bit-mask that removes the variable parts of the EVIOCGNAME ioctl
525// request.
526const EVIOCGNAME_MASK: u32 = 0b11_00_0000_0000_0000_1111_1111_1111_1111;
527
528impl FileOps for InputFile {
529    fileops_impl_nonseekable!();
530    fileops_impl_noop_sync!();
531
532    fn open(&self, file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
533        if let Some(inspect) = &self.inspect_status {
534            inspect.set_open_timestamp(zx::MonotonicInstant::get().into_nanos());
535            if (file.flags() & OpenFlags::NONBLOCK) != OpenFlags::NONBLOCK {
536                inspect.set_opened_without_nonblock();
537            }
538        }
539        Ok(())
540    }
541
542    fn close(
543        self: Box<Self>,
544        _file: &starnix_core::vfs::FileObjectState,
545        _current_task: &CurrentTask,
546    ) {
547        if let Some(inspect) = &self.inspect_status {
548            inspect.set_closed();
549            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
550        }
551    }
552
553    fn ioctl(
554        &self,
555        _file: &FileObject,
556        current_task: &CurrentTask,
557        request: u32,
558        arg: SyscallArg,
559    ) -> Result<SyscallResult, Errno> {
560        let user_addr = UserAddress::from(arg);
561        match request {
562            uapi::EVIOCGVERSION => {
563                current_task.write_object(UserRef::new(user_addr), &self.driver_version)?;
564                Ok(SUCCESS)
565            }
566            uapi::EVIOCGID => {
567                current_task.write_object(UserRef::new(user_addr), &self.input_id)?;
568                Ok(SUCCESS)
569            }
570            uapi::EVIOCGBIT_0 => {
571                current_task
572                    .write_object(UserRef::new(user_addr), &self.supported_event_types.bytes)?;
573                Ok(SUCCESS)
574            }
575            uapi::EVIOCGBIT_EV_KEY => {
576                current_task.write_object(UserRef::new(user_addr), &self.supported_keys.bytes)?;
577                Ok(SUCCESS)
578            }
579            uapi::EVIOCGBIT_EV_ABS => {
580                current_task.write_object(
581                    UserRef::new(user_addr),
582                    &self.supported_position_attributes.bytes,
583                )?;
584                Ok(SUCCESS)
585            }
586            uapi::EVIOCGBIT_EV_REL => {
587                current_task.write_object(
588                    UserRef::new(user_addr),
589                    &self.supported_motion_attributes.bytes,
590                )?;
591                Ok(SUCCESS)
592            }
593            uapi::EVIOCGBIT_EV_SW => {
594                current_task
595                    .write_object(UserRef::new(user_addr), &self.supported_switches.bytes)?;
596                Ok(SUCCESS)
597            }
598            uapi::EVIOCGBIT_EV_LED => {
599                current_task.write_object(UserRef::new(user_addr), &self.supported_leds.bytes)?;
600                Ok(SUCCESS)
601            }
602            uapi::EVIOCGBIT_EV_FF => {
603                current_task
604                    .write_object(UserRef::new(user_addr), &self.supported_haptics.bytes)?;
605                Ok(SUCCESS)
606            }
607            uapi::EVIOCGBIT_EV_MSC => {
608                current_task
609                    .write_object(UserRef::new(user_addr), &self.supported_misc_features.bytes)?;
610                Ok(SUCCESS)
611            }
612            uapi::EVIOCGPROP => {
613                current_task.write_object(UserRef::new(user_addr), &self.properties.bytes)?;
614                Ok(SUCCESS)
615            }
616            uapi::EVIOCGABS_MT_SLOT => {
617                current_task.write_object(UserRef::new(user_addr), &self.mt_slot_axis_info)?;
618                Ok(SUCCESS)
619            }
620            uapi::EVIOCGABS_MT_TRACKING_ID => {
621                current_task
622                    .write_object(UserRef::new(user_addr), &self.mt_tracking_id_axis_info)?;
623                Ok(SUCCESS)
624            }
625            uapi::EVIOCGABS_MT_POSITION_X => {
626                current_task.write_object(UserRef::new(user_addr), &self.x_axis_info)?;
627                Ok(SUCCESS)
628            }
629            uapi::EVIOCGABS_MT_POSITION_Y => {
630                current_task.write_object(UserRef::new(user_addr), &self.y_axis_info)?;
631                Ok(SUCCESS)
632            }
633
634            request_with_params => {
635                // Remove the variable part of the request with params, so
636                // we can identify it.
637                match request_with_params & EVIOCGNAME_MASK {
638                    uapi::EVIOCGNAME_0 => {
639                        // Request to report the device name.
640                        //
641                        // An EVIOCGNAME request comes with the response buffer size encoded in
642                        // bits 29..16 of the request's `u32` code.  This is in contrast to
643                        // most other ioctl request codes in this file, which are fully known
644                        // at compile time, so we need to decode it a bit differently from
645                        // other ioctl codes.
646                        //
647                        // See [here][hh] the macros that do this.
648                        //
649                        // [hh]: https://cs.opensource.google/fuchsia/fuchsia/+/main:third_party/android/platform/bionic/libc/kernel/uapi/linux/input.h;l=82;drc=0f0c18f695543b15b852f68f297744d03d642a26
650                        let device_name = &self.device_name;
651
652                        // The lowest 14 bits of the top 16 bits are the unsigned buffer
653                        // length in bytes.  While we don't use multibyte characters,
654                        // make sure that all sizes below are expressed in terms of
655                        // bytes, not characters.
656                        let buffer_bytes_count =
657                            ((request_with_params >> 16) & ((1 << 14) - 1)) as usize;
658
659                        // Zero out the entire user buffer in case the user reads too much.
660                        // Probably not needed, but I don't think it hurts.
661                        current_task.zero(user_addr, buffer_bytes_count)?;
662                        let device_name_as_bytes = device_name.as_bytes();
663
664                        // Copy all bytes from device name if the buffer is large enough.
665                        // If not, copy one less than the buffer size, to leave space
666                        // for the final NUL.
667                        let to_copy_bytes_count =
668                            std::cmp::min(device_name_as_bytes.len(), buffer_bytes_count - 1);
669                        current_task.write_memory(
670                            user_addr,
671                            &device_name_as_bytes[..to_copy_bytes_count],
672                        )?;
673                        // EVIOCGNAME ioctl returns the number of bytes written.
674                        // Do not forget the trailing NUL.
675                        Ok((to_copy_bytes_count + 1).into())
676                    }
677                    _ => {
678                        track_stub!(
679                            TODO("https://fxbug.dev/322873200"),
680                            "input ioctl",
681                            request_with_params
682                        );
683                        error!(EOPNOTSUPP)
684                    }
685                }
686            }
687        }
688    }
689
690    fn read(
691        &self,
692        _file: &FileObject,
693        current_task: &CurrentTask,
694        offset: usize,
695        data: &mut dyn OutputBuffer,
696    ) -> Result<usize, Errno> {
697        fuchsia_trace::duration!("input", "InputFile::read");
698        debug_assert!(offset == 0);
699        let input_event_size = InputEventPtr::size_of_object_for(current_task);
700
701        // The limit of the buffer is determined by taking the available bytes
702        // and using integer division on the size of uapi::input_event in bytes.
703        let limit = data.available() / input_event_size;
704        let events = self.read_events(limit);
705        if events.is_empty() {
706            // Returns `EAGAIN` for file is opened with or without `O_NONBLOCK`.
707            log_info!("read() returning EAGAIN");
708            return error!(EAGAIN);
709        }
710
711        let last_event_timeval = events.last().expect("events is nonempty").event.time;
712        let last_event_time_ns = duration_from_timeval::<zx::MonotonicTimeline>(last_event_timeval)
713            .unwrap()
714            .into_nanos();
715        self.inspect_status
716            .clone()
717            .map(|status| status.count_read_events(events.len() as u64, last_event_time_ns));
718
719        for event in &events {
720            if let Some(trace_id) = event.trace_id {
721                fuchsia_trace::duration!("input", "linux_event_read");
722                fuchsia_trace::flow_end!("input", "linux_event", trace_id);
723            }
724        }
725
726        if current_task.is_arch32() {
727            let events: Result<Vec<uapi::arch32::input_event>, _> =
728                events.iter().map(|e| uapi::arch32::input_event::try_from(e.event)).collect();
729            let events = events.map_err(|_| errno!(EINVAL))?;
730            data.write_all(events.as_bytes())
731        } else {
732            let events: Vec<uapi::input_event> = events.iter().map(|e| e.event).collect();
733            data.write_all(events.as_bytes())
734        }
735    }
736
737    fn write(
738        &self,
739        _file: &FileObject,
740        _current_task: &CurrentTask,
741        offset: usize,
742        _data: &mut dyn InputBuffer,
743    ) -> Result<usize, Errno> {
744        debug_assert!(offset == 0);
745        track_stub!(TODO("https://fxbug.dev/322874385"), "write() on input device");
746        error!(EOPNOTSUPP)
747    }
748
749    fn wait_async(
750        &self,
751        _file: &FileObject,
752        _current_task: &CurrentTask,
753        waiter: &Waiter,
754        events: FdEvents,
755        handler: EventHandler,
756    ) -> Option<WaitCanceler> {
757        Some(self.waiters.wait_async_fd_events(waiter, events, handler))
758    }
759
760    fn query_events(
761        &self,
762        _file: &FileObject,
763        _current_task: &CurrentTask,
764    ) -> Result<FdEvents, Errno> {
765        Ok(if self.events.is_empty() { FdEvents::empty() } else { FdEvents::POLLIN })
766    }
767}
768
769pub struct ArcInputFile(pub Arc<InputFile>);
770
771impl FileOps for ArcInputFile {
772    fileops_impl_nonseekable!();
773    fileops_impl_noop_sync!();
774
775    fn open(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
776        self.0.as_ref().open(file, current_task)
777    }
778
779    fn close(
780        self: Box<Self>,
781        _file: &starnix_core::vfs::FileObjectState,
782        _current_task: &CurrentTask,
783    ) {
784        let arc_file = *self;
785        if let Some(inspect) = &arc_file.0.inspect_status {
786            inspect.set_closed();
787            inspect.set_closed_timestamp(zx::MonotonicInstant::get().into_nanos());
788        }
789    }
790
791    fn ioctl(
792        &self,
793        file: &FileObject,
794        current_task: &CurrentTask,
795        request: u32,
796        arg: SyscallArg,
797    ) -> Result<SyscallResult, Errno> {
798        self.0.as_ref().ioctl(file, current_task, request, arg)
799    }
800
801    fn read(
802        &self,
803        file: &FileObject,
804        current_task: &CurrentTask,
805        offset: usize,
806        data: &mut dyn OutputBuffer,
807    ) -> Result<usize, Errno> {
808        self.0.as_ref().read(file, current_task, offset, data)
809    }
810
811    fn write(
812        &self,
813        file: &FileObject,
814        current_task: &CurrentTask,
815        offset: usize,
816        data: &mut dyn InputBuffer,
817    ) -> Result<usize, Errno> {
818        self.0.as_ref().write(file, current_task, offset, data)
819    }
820
821    fn wait_async(
822        &self,
823        file: &FileObject,
824        current_task: &CurrentTask,
825        waiter: &Waiter,
826        events: FdEvents,
827        handler: EventHandler,
828    ) -> Option<WaitCanceler> {
829        self.0.as_ref().wait_async(file, current_task, waiter, events, handler)
830    }
831
832    fn query_events(
833        &self,
834        file: &FileObject,
835        current_task: &CurrentTask,
836    ) -> Result<FdEvents, Errno> {
837        self.0.as_ref().query_events(file, current_task)
838    }
839}
840
841pub struct BitSet<const NUM_BYTES: usize> {
842    bytes: [u8; NUM_BYTES],
843}
844
845impl<const NUM_BYTES: usize> BitSet<{ NUM_BYTES }> {
846    pub const fn new() -> Self {
847        Self { bytes: [0; NUM_BYTES] }
848    }
849
850    pub const fn list<const N: usize>(bits: [u32; N]) -> Self {
851        let mut bitset = Self::new();
852        let mut i = 0;
853        while i < bits.len() {
854            bitset.set(bits[i]);
855            i += 1;
856        }
857        bitset
858    }
859
860    pub const fn set(&mut self, bitnum: u32) {
861        let bitnum = bitnum as usize;
862        let byte = bitnum / 8;
863        let bit = bitnum % 8;
864        self.bytes[byte] |= 1 << bit;
865    }
866}
867
868#[cfg(test)]
869mod tests {
870    use super::*;
871    use std::sync::atomic::Ordering;
872
873    #[test]
874    fn test_read_events_no_notify_when_buffer_full() {
875        let inspector = fuchsia_inspect::Inspector::default();
876        let node = inspector.root();
877        let input_file = InputFile::new_touch(
878            uapi::input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
879            100,
880            100,
881            node,
882        );
883
884        // Add some events.
885        let event1 = uapi::input_event {
886            type_: uapi::EV_KEY as u16,
887            code: 1,
888            value: 1,
889            ..Default::default()
890        };
891        let event2 = uapi::input_event {
892            type_: uapi::EV_KEY as u16,
893            code: 2,
894            value: 1,
895            ..Default::default()
896        };
897        input_file.add_events(vec![event1, event2]);
898
899        // Verify that adding events triggered a notification.
900        let notify_count =
901            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
902        assert_eq!(notify_count, 1);
903
904        // Read with a limit of 1.
905        let events = input_file.read_events(1);
906        assert_eq!(events.len(), 1);
907
908        // Verify that no additional notification was sent despite more events remaining.
909        let notify_count =
910            input_file.inspect_status.as_ref().unwrap().fd_notify_count.load(Ordering::Relaxed);
911        assert_eq!(notify_count, 1);
912    }
913}