Skip to main content

starnix_modules_input/
uinput.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::uinput::vfs::{CloseFreeSafe, NamespaceNode};
6use crate::{InputEventsRelayHandle, InputFile, OpenedFiles};
7use bit_vec::BitVec;
8use fidl_fuchsia_ui_test_input::{
9    self as futinput, CoordinateUnit, DisplayDimensions, KeyboardSimulateKeyEventRequest,
10    RegistryRegisterKeyboardAndGetDeviceInfoRequest,
11    RegistryRegisterTouchScreenAndGetDeviceInfoRequest,
12};
13use fuchsia_inspect;
14use starnix_core::device::kobject::{Device, DeviceMetadata};
15use starnix_core::device::{DeviceMode, DeviceOps};
16use starnix_core::fileops_impl_seekless;
17use starnix_core::mm::MemoryAccessorExt;
18use starnix_core::task::{CurrentTask, Kernel};
19use starnix_core::vfs::{self, FileObject, FileOps, FsString, fileops_impl_noop_sync};
20use starnix_logging::log_warn;
21use starnix_modules_input_event_conversion::key_linux_to_fuchsia::LinuxKeyboardEventParser;
22use starnix_modules_input_event_conversion::touch_linux_to_fuchsia::LinuxTouchEventParser;
23use starnix_sync::{LockDepMutex, UinputDeviceStateLock};
24use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
25use starnix_uapi::device_id::INPUT_MAJOR;
26use starnix_uapi::errors::Errno;
27use starnix_uapi::open_flags::OpenFlags;
28use starnix_uapi::user_address::{MultiArchUserRef, UserRef};
29use starnix_uapi::{device_id, errno, error, uapi};
30use std::sync::Arc;
31use std::sync::atomic::{AtomicI32, Ordering};
32
33// Return the current uinput API version 5, it also told caller this uinput
34// supports UI_DEV_SETUP.
35const UINPUT_VERSION: u32 = 5;
36
37type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
38
39#[derive(Clone)]
40enum DeviceId {
41    Keyboard,
42    Touchscreen(i32, i32),
43}
44
45pub fn register_uinput_device(
46    kernel: &Kernel,
47    input_event_relay_handle: Arc<InputEventsRelayHandle>,
48) -> Result<(), Errno> {
49    let registry = &kernel.device_registry;
50    let misc_class = registry.objects.misc_class();
51    let inspect_node = Arc::new(kernel.inspect_node.create_child("uinput"));
52    let device = UinputDevice::new(input_event_relay_handle, inspect_node);
53    registry.register_device(
54        kernel,
55        "uinput".into(),
56        DeviceMetadata::new("uinput".into(), device_id::DeviceId::UINPUT, DeviceMode::Char),
57        misc_class,
58        device,
59    )?;
60    Ok(())
61}
62
63fn add_and_register_input_device(
64    system_task: &CurrentTask,
65    dev_ops: impl DeviceOps,
66    device_id: u32,
67) -> Result<Device, Errno> {
68    let kernel = system_task.kernel();
69    let registry = &kernel.device_registry;
70
71    let input_class = registry.objects.input_class();
72
73    registry.register_device(
74        system_task.kernel(),
75        FsString::from(format!("event{}", device_id)).as_ref(),
76        DeviceMetadata::new(
77            format!("input/event{}", device_id).into(),
78            starnix_uapi::device_id::DeviceId::new(INPUT_MAJOR, device_id),
79            DeviceMode::Char,
80        ),
81        input_class,
82        dev_ops,
83    )
84}
85
86#[derive(Clone)]
87struct UinputDevice {
88    input_event_relay: Arc<InputEventsRelayHandle>,
89    inspect_node: Arc<fuchsia_inspect::Node>,
90}
91
92impl UinputDevice {
93    pub fn new(
94        input_event_relay: Arc<InputEventsRelayHandle>,
95        inspect_node: Arc<fuchsia_inspect::Node>,
96    ) -> Self {
97        Self { input_event_relay, inspect_node }
98    }
99}
100
101impl DeviceOps for UinputDevice {
102    fn open(
103        &self,
104        _current_task: &CurrentTask,
105        _id: device_id::DeviceId,
106        _node: &NamespaceNode,
107        _flags: OpenFlags,
108    ) -> Result<Box<dyn FileOps>, Errno> {
109        Ok(Box::new(UinputDeviceFile::new(
110            self.input_event_relay.clone(),
111            self.inspect_node.clone(),
112        )))
113    }
114}
115
116enum CreatedDevice {
117    None,
118    Keyboard(futinput::KeyboardSynchronousProxy, LinuxKeyboardEventParser),
119    // LinuxTouchEventParser need to boxed to avoid warning: large-enum-variant.
120    Touchscreen(futinput::TouchScreenSynchronousProxy, Box<LinuxTouchEventParser>),
121}
122
123struct Range {
124    min: i32,
125    max: i32,
126}
127struct UinputDeviceMutableState {
128    enabled_evbits: BitVec,
129    input_id: Option<uapi::input_id>,
130    created_device: CreatedDevice,
131    k_device: Option<Device>,
132    device_id: Option<u32>,
133    x_range: Option<Range>,
134    y_range: Option<Range>,
135}
136
137impl UinputDeviceMutableState {
138    fn get_id_and_device_type(&self) -> Option<(uapi::input_id, DeviceId)> {
139        let input_id = match self.input_id {
140            Some(input_id) => input_id,
141            None => return None,
142        };
143        // Currently only support Keyboard and Touchscreen, if evbits contains
144        // EV_ABS, consider it is Touchscreen. This need to be revisit when we
145        // want to support more device types.
146        let device_type = match self.enabled_evbits.clone().get(uapi::EV_ABS as usize) {
147            Some(true) => {
148                // TODO(b/304595635): Check if screen size is required.
149                let mut touchscreen_width = 1000;
150                let mut touchscreen_height = 1000;
151
152                if self.x_range.is_some() && self.y_range.is_some() {
153                    let x_range = self.x_range.as_ref().unwrap();
154                    let y_range = self.y_range.as_ref().unwrap();
155                    touchscreen_width = x_range.max - x_range.min;
156                    touchscreen_height = y_range.max - y_range.min;
157                }
158                DeviceId::Touchscreen(touchscreen_width, touchscreen_height)
159            }
160            Some(false) | None => DeviceId::Keyboard,
161        };
162
163        Some((input_id, device_type))
164    }
165}
166
167struct UinputDeviceFile {
168    input_event_relay: Arc<InputEventsRelayHandle>,
169    inner: LockDepMutex<UinputDeviceMutableState, UinputDeviceStateLock>,
170    inspect_node: Arc<fuchsia_inspect::Node>,
171}
172
173impl UinputDeviceFile {
174    pub fn new(
175        input_event_relay: Arc<InputEventsRelayHandle>,
176        inspect_node: Arc<fuchsia_inspect::Node>,
177    ) -> Self {
178        Self {
179            input_event_relay,
180            inner: UinputDeviceMutableState {
181                enabled_evbits: BitVec::from_elem(uapi::EV_CNT as usize, false),
182                input_id: None,
183                created_device: CreatedDevice::None,
184                k_device: None,
185                device_id: None,
186                x_range: None,
187                y_range: None,
188            }
189            .into(),
190            inspect_node,
191        }
192    }
193
194    /// UI_SET_EVBIT caller pass a u32 as the event type "EV_*" to set this
195    /// uinput device may handle events with the given event type.
196    fn ui_set_evbit(&self, arg: SyscallArg) -> Result<SyscallResult, Errno> {
197        let evbit: u32 = arg.into();
198        match evbit {
199            uapi::EV_KEY | uapi::EV_ABS => {
200                self.inner.lock().enabled_evbits.set(evbit as usize, true);
201                Ok(SUCCESS)
202            }
203            _ => {
204                log_warn!("UI_SET_EVBIT with unsupported evbit {}", evbit);
205                error!(EPERM)
206            }
207        }
208    }
209
210    /// UI_ABS_SETUP caller pass in event codes and min and max value for
211    /// the event code.
212    fn ui_abs_setup(
213        &self,
214        current_task: &CurrentTask,
215        abs_setup: UserRef<starnix_uapi::uinput_abs_setup>,
216    ) -> Result<SyscallResult, Errno> {
217        let setup: starnix_uapi::uinput_abs_setup = current_task.read_object(abs_setup)?;
218        let code: u32 = setup.code.into();
219        match code {
220            uapi::ABS_MT_POSITION_X => {
221                self.inner.lock().x_range =
222                    Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
223            }
224            uapi::ABS_MT_POSITION_Y => {
225                self.inner.lock().y_range =
226                    Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
227            }
228            _ => {
229                log_warn!("UI_ABS_SETUP ignore event code {}", setup.code);
230            }
231        }
232        Ok(SUCCESS)
233    }
234
235    /// UI_GET_VERSION caller pass a address for u32 to `arg` to receive the
236    /// uinput version. ioctl returns SUCCESS(0) for success calls, and
237    /// EFAULT(14) for given address is null.
238    fn ui_get_version(
239        &self,
240        current_task: &CurrentTask,
241        user_version: UserRef<u32>,
242    ) -> Result<SyscallResult, Errno> {
243        let response: u32 = UINPUT_VERSION;
244        current_task.write_object(user_version, &response)?;
245        Ok(SUCCESS)
246    }
247
248    /// UI_DEV_SETUP set the name of device and input_id (bustype, vendor id,
249    /// product id, version) to the uinput device.
250    fn ui_dev_setup(
251        &self,
252        current_task: &CurrentTask,
253        user_uinput_setup: UserRef<uapi::uinput_setup>,
254    ) -> Result<SyscallResult, Errno> {
255        let uinput_setup = current_task.read_object(user_uinput_setup)?;
256        self.inner.lock().input_id = Some(uinput_setup.id);
257        Ok(SUCCESS)
258    }
259
260    fn ui_dev_create(&self, current_task: &CurrentTask) -> Result<SyscallResult, Errno> {
261        // Only eng and userdebug builds include the `fuchsia.ui.test.input` service.
262        let registry = match fuchsia_component::client::connect_to_protocol_sync::<
263            futinput::RegistryMarker,
264        >() {
265            Ok(proxy) => Some(proxy),
266            Err(_) => {
267                log_warn!("Could not connect to fuchsia.ui.test.input/Registry");
268                None
269            }
270        };
271        self.ui_dev_create_inner(current_task, registry)
272    }
273
274    /// UI_DEV_CREATE calls create the uinput device with given information
275    /// from previous ioctl() calls.
276    fn ui_dev_create_inner(
277        &self,
278        current_task: &CurrentTask,
279        // Takes `registry` arg so we can manually inject a mock registry in unit tests.
280        registry: Option<futinput::RegistrySynchronousProxy>,
281    ) -> Result<SyscallResult, Errno> {
282        match registry {
283            Some(proxy) => {
284                let mut inner = self.inner.lock();
285                let (input_id, device_type) = match inner.get_id_and_device_type() {
286                    Some((id, dev)) => (id, dev),
287                    None => return error!(EINVAL),
288                };
289
290                let open_files: OpenedFiles = Default::default();
291
292                let (registered_device_id, inspect_status) = match device_type {
293                    DeviceId::Keyboard => {
294                        let (key_client, key_server) =
295                            fidl::endpoints::create_sync_proxy::<futinput::KeyboardMarker>();
296                        inner.created_device =
297                            CreatedDevice::Keyboard(key_client, LinuxKeyboardEventParser::create());
298
299                        // Register a keyboard
300                        let register_res = proxy.register_keyboard_and_get_device_info(
301                            RegistryRegisterKeyboardAndGetDeviceInfoRequest {
302                                device: Some(key_server),
303                                ..Default::default()
304                            },
305                            zx::MonotonicInstant::INFINITE,
306                        );
307
308                        match register_res {
309                            Ok(resp) => match resp.device_id {
310                                Some(device_id) => {
311                                    inner.device_id = Some(device_id);
312                                    let node = self
313                                        .inspect_node
314                                        .create_child(format!("uinput_device_{}", device_id));
315                                    let inspect_status = crate::InputDeviceStatus::new(node);
316                                    self.input_event_relay.add_keyboard_device(
317                                        device_id,
318                                        open_files.clone(),
319                                        Some(inspect_status.clone()),
320                                    );
321                                    (device_id, inspect_status)
322                                }
323                                None => {
324                                    log_warn!(
325                                        "register_keyboard_and_get_device_info response does not include a device_id"
326                                    );
327                                    return error!(EPERM);
328                                }
329                            },
330                            Err(e) => {
331                                log_warn!(
332                                    "Uinput could not register Keyboard device to Registry: {:?}",
333                                    e
334                                );
335                                return error!(EPERM);
336                            }
337                        }
338                    }
339                    DeviceId::Touchscreen(_width, _height) => {
340                        let (touch_client, touch_server) =
341                            fidl::endpoints::create_sync_proxy::<futinput::TouchScreenMarker>();
342                        inner.created_device = CreatedDevice::Touchscreen(
343                            touch_client,
344                            Box::new(LinuxTouchEventParser::create()),
345                        );
346
347                        let mut request = RegistryRegisterTouchScreenAndGetDeviceInfoRequest {
348                            device: Some(touch_server),
349                            coordinate_unit: Some(CoordinateUnit::PhysicalPixels),
350                            ..Default::default()
351                        };
352
353                        if inner.x_range.is_some() && inner.y_range.is_some() {
354                            request.coordinate_unit = Some(CoordinateUnit::RegisteredDimensions);
355                            let x_range = inner.x_range.as_ref().unwrap();
356                            let y_range = inner.y_range.as_ref().unwrap();
357                            request.display_dimensions = Some(DisplayDimensions {
358                                min_x: x_range.min.into(),
359                                max_x: x_range.max.into(),
360                                min_y: y_range.min.into(),
361                                max_y: y_range.max.into(),
362                            });
363                        }
364
365                        // Register a touchscreen
366                        let register_res = proxy.register_touch_screen_and_get_device_info(
367                            request,
368                            zx::Instant::INFINITE,
369                        );
370
371                        match register_res {
372                            Ok(resp) => match resp.device_id {
373                                Some(device_id) => {
374                                    inner.device_id = Some(device_id);
375                                    let node = self
376                                        .inspect_node
377                                        .create_child(format!("uinput_device_{}", device_id));
378                                    let inspect_status = crate::InputDeviceStatus::new(node);
379                                    self.input_event_relay.add_touch_device(
380                                        device_id,
381                                        open_files.clone(),
382                                        Some(inspect_status.clone()),
383                                    );
384                                    (device_id, inspect_status)
385                                }
386                                None => {
387                                    log_warn!(
388                                        "register_touch_screen_and_get_device_info response does not include a device_id"
389                                    );
390                                    return error!(EPERM);
391                                }
392                            },
393                            Err(e) => {
394                                log_warn!(
395                                    "Uinput could not register Keyboard device to Registry: {:?}",
396                                    e
397                                );
398                                return error!(EPERM);
399                            }
400                        }
401                    }
402                };
403
404                let device = add_and_register_input_device(
405                    current_task,
406                    VirtualDevice { input_id, devt: device_type, open_files, inspect_status },
407                    registered_device_id,
408                )?;
409                inner.k_device = Some(device);
410
411                new_device();
412
413                Ok(SUCCESS)
414            }
415            None => {
416                log_warn!("No Registry available for Uinput.");
417                error!(EPERM)
418            }
419        }
420    }
421
422    fn ui_dev_destroy(&self, current_task: &CurrentTask) -> Result<SyscallResult, Errno> {
423        let mut inner = self.inner.lock();
424        match inner.device_id {
425            Some(device_id) => {
426                self.input_event_relay.remove_device(device_id);
427            }
428            None => {
429                // This is possible if caller does not call create device but calls destroy.
430                // No cleanup is needed for input event relay in this case.
431            }
432        }
433
434        match inner.k_device.clone() {
435            Some(device) => {
436                let kernel = current_task.kernel();
437                kernel.device_registry.remove_device(current_task, device);
438            }
439            None => {
440                log_warn!("UI_DEV_DESTROY kHandle not found");
441                return error!(EPERM);
442            }
443        }
444        inner.k_device = None;
445        inner.created_device = CreatedDevice::None;
446
447        destroy_device();
448
449        Ok(SUCCESS)
450    }
451}
452
453// TODO(b/312467059): Remove once ESC -> Power workaround can be remove.
454static COUNT_OF_UINPUT_DEVICE: AtomicI32 = AtomicI32::new(0);
455
456fn new_device() {
457    let _ = COUNT_OF_UINPUT_DEVICE.fetch_add(1, Ordering::SeqCst);
458}
459
460fn destroy_device() {
461    let _ = COUNT_OF_UINPUT_DEVICE.fetch_sub(1, Ordering::SeqCst);
462}
463
464pub fn uinput_running() -> bool {
465    COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst) > 0
466}
467
468/// `UinputDeviceFile` doesn't implement the `close` method.
469impl CloseFreeSafe for UinputDeviceFile {}
470impl FileOps for UinputDeviceFile {
471    fileops_impl_seekless!();
472    fileops_impl_noop_sync!();
473
474    fn ioctl(
475        &self,
476        _file: &FileObject,
477        current_task: &CurrentTask,
478        request: u32,
479        arg: SyscallArg,
480    ) -> Result<SyscallResult, Errno> {
481        match request {
482            uapi::UI_GET_VERSION => self.ui_get_version(current_task, arg.into()),
483            uapi::UI_SET_EVBIT => self.ui_set_evbit(arg),
484            uapi::UI_ABS_SETUP => self.ui_abs_setup(current_task, arg.into()),
485            // `fuchsia.ui.test.input.Registry` does not use some uinput ioctl
486            // request, just ignore the request and return SUCCESS, even args
487            // is invalid.
488            uapi::UI_SET_KEYBIT
489            | uapi::UI_SET_ABSBIT
490            | uapi::UI_SET_PHYS
491            | uapi::UI_SET_PROPBIT => Ok(SUCCESS),
492            uapi::UI_DEV_SETUP => self.ui_dev_setup(current_task, arg.into()),
493            uapi::UI_DEV_CREATE => self.ui_dev_create(current_task),
494            uapi::UI_DEV_DESTROY => self.ui_dev_destroy(current_task),
495            // default_ioctl() handles file system related requests and reject
496            // others.
497            _ => {
498                log_warn!("receive unknown ioctl request: {:?}", request);
499                error!(ENOTTY)
500            }
501        }
502    }
503
504    fn write(
505        &self,
506        _file: &vfs::FileObject,
507        current_task: &starnix_core::task::CurrentTask,
508        _offset: usize,
509        data: &mut dyn vfs::buffers::InputBuffer,
510    ) -> Result<usize, Errno> {
511        let content = data.read_all()?;
512        let event =
513            InputEventPtr::read_from_prefix(current_task, &content).map_err(|_| errno!(EINVAL))?;
514        let mut inner = self.inner.lock();
515
516        match &mut inner.created_device {
517            CreatedDevice::Keyboard(proxy, parser) => {
518                let input_report = parser.handle(event);
519                match input_report {
520                    Ok(Some(report)) => {
521                        if let Some(keyboard_report) = report.keyboard {
522                            let res = proxy.simulate_key_event(
523                                &KeyboardSimulateKeyEventRequest {
524                                    pressed_keys: keyboard_report.pressed_keys3,
525                                    ..Default::default()
526                                },
527                                zx::MonotonicInstant::INFINITE,
528                            );
529                            if res.is_err() {
530                                return error!(EIO);
531                            }
532                        }
533                    }
534                    Ok(None) => (),
535                    Err(e) => return Err(e),
536                }
537            }
538            CreatedDevice::Touchscreen(proxy, parser) => {
539                let input_report: Result<Option<futinput::TouchInputReport>, Errno> =
540                    parser.handle(event);
541                match input_report {
542                    Ok(Some(report)) => {
543                        let res =
544                            proxy.simulate_touch_event(&report, zx::MonotonicInstant::INFINITE);
545                        if res.is_err() {
546                            return error!(EIO);
547                        }
548                    }
549                    Ok(None) => (),
550                    Err(e) => return Err(e),
551                }
552            }
553            CreatedDevice::None => return error!(EINVAL),
554        }
555
556        Ok(content.len())
557    }
558
559    fn read(
560        &self,
561        _file: &vfs::FileObject,
562        _current_task: &starnix_core::task::CurrentTask,
563        _offset: usize,
564        _data: &mut dyn vfs::buffers::OutputBuffer,
565    ) -> Result<usize, Errno> {
566        log_warn!("uinput FD does not support read().");
567        error!(EINVAL)
568    }
569}
570
571#[derive(Clone)]
572pub struct VirtualDevice {
573    input_id: uapi::input_id,
574    devt: DeviceId,
575    open_files: OpenedFiles,
576    inspect_status: Arc<crate::InputDeviceStatus>,
577}
578
579impl DeviceOps for VirtualDevice {
580    fn open(
581        &self,
582        _current_task: &CurrentTask,
583        _id: device_id::DeviceId,
584        _node: &NamespaceNode,
585        _flags: OpenFlags,
586    ) -> Result<Box<dyn FileOps>, Errno> {
587        let mut file_nodes = self.inspect_status.file_nodes.lock();
588        let child_node =
589            self.inspect_status.node.create_child(format!("file_{}", file_nodes.len()));
590        let input_file = match &self.devt {
591            DeviceId::Keyboard => Arc::new(InputFile::new_keyboard(self.input_id, &child_node)),
592            DeviceId::Touchscreen(width, height) => Arc::new(InputFile::new_touch(
593                self.input_id,
594                width.clone(),
595                height.clone(),
596                &child_node,
597            )),
598        };
599
600        file_nodes.push(child_node);
601        input_file.init_inspect_status();
602        self.open_files.lock().push(Arc::downgrade(&input_file));
603
604        Ok(Box::new(crate::input_file::ArcInputFile(input_file)))
605    }
606}
607
608#[cfg(test)]
609mod test {
610    use super::*;
611    use crate::{EventProxyMode, start_input_relays_for_test};
612    use starnix_core::testing::spawn_kernel_and_run;
613    use starnix_core::vfs::FileHandle;
614    use std::sync::Arc;
615    use test_case::test_case;
616
617    async fn new_kernel_objects(current_task: &CurrentTask) -> (Arc<UinputDeviceFile>, FileHandle) {
618        let (input_relay_handle, _, _, _, _, _, _, _, _, _, _, _) =
619            start_input_relays_for_test(current_task, EventProxyMode::None).await;
620        let inspector = fuchsia_inspect::Inspector::default();
621        let dev = Arc::new(UinputDeviceFile::new(
622            input_relay_handle,
623            Arc::new(inspector.root().create_child("uinput")),
624        ));
625
626        let root_namespace_node = current_task
627            .lookup_path_from_root(".".into())
628            .expect("failed to get namespace node for root");
629
630        let file_object = FileObject::new(
631            current_task,
632            Box::new(dev.clone()),
633            // The input node doesn't really live at the root of the filesystem.
634            // But the test doesn't need to be 100% representative of production.
635            root_namespace_node,
636            OpenFlags::empty(),
637        )
638        .expect("FileObject::new failed");
639        (dev, file_object)
640    }
641
642    #[test_case(uapi::EV_KEY, vec![uapi::EV_KEY as usize] => Ok(SUCCESS))]
643    #[test_case(uapi::EV_ABS, vec![uapi::EV_ABS as usize] => Ok(SUCCESS))]
644    #[test_case(uapi::EV_REL, vec![] => error!(EPERM))]
645    #[::fuchsia::test]
646    async fn ui_set_evbit(bit: u32, expected_evbits: Vec<usize>) -> Result<SyscallResult, Errno> {
647        spawn_kernel_and_run(async move |current_task| {
648            let (dev, file_object) = new_kernel_objects(current_task).await;
649            let r = dev.ioctl(
650                &file_object,
651                current_task,
652                uapi::UI_SET_EVBIT,
653                SyscallArg::from(bit as u64),
654            );
655            for expected_evbit in expected_evbits {
656                assert!(dev.inner.lock().enabled_evbits.get(expected_evbit).unwrap());
657            }
658            r
659        })
660        .await
661    }
662
663    #[::fuchsia::test]
664    async fn ui_set_evbit_call_multi() {
665        spawn_kernel_and_run(async move |current_task| {
666            let (dev, file_object) = new_kernel_objects(current_task).await;
667            let r = dev.ioctl(
668                &file_object,
669                current_task,
670                uapi::UI_SET_EVBIT,
671                SyscallArg::from(uapi::EV_KEY as u64),
672            );
673            assert_eq!(r, Ok(SUCCESS));
674            let r = dev.ioctl(
675                &file_object,
676                current_task,
677                uapi::UI_SET_EVBIT,
678                SyscallArg::from(uapi::EV_ABS as u64),
679            );
680            assert_eq!(r, Ok(SUCCESS));
681            assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_KEY as usize).unwrap());
682            assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_ABS as usize).unwrap());
683        })
684        .await;
685    }
686
687    #[::fuchsia::test]
688    async fn ui_set_keybit() {
689        spawn_kernel_and_run(async move |current_task| {
690            let (dev, file_object) = new_kernel_objects(current_task).await;
691            let r = dev.ioctl(
692                &file_object,
693                current_task,
694                uapi::UI_SET_KEYBIT,
695                SyscallArg::from(uapi::BTN_TOUCH as u64),
696            );
697            assert_eq!(r, Ok(SUCCESS));
698
699            // also test call multi times.
700            let r = dev.ioctl(
701                &file_object,
702                current_task,
703                uapi::UI_SET_KEYBIT,
704                SyscallArg::from(uapi::KEY_SPACE as u64),
705            );
706            assert_eq!(r, Ok(SUCCESS));
707        })
708        .await;
709    }
710
711    #[::fuchsia::test]
712    async fn ui_set_absbit() {
713        spawn_kernel_and_run(async move |current_task| {
714            let (dev, file_object) = new_kernel_objects(current_task).await;
715            let r = dev.ioctl(
716                &file_object,
717                current_task,
718                uapi::UI_SET_ABSBIT,
719                SyscallArg::from(uapi::ABS_MT_SLOT as u64),
720            );
721            assert_eq!(r, Ok(SUCCESS));
722
723            // also test call multi times.
724            let r = dev.ioctl(
725                &file_object,
726                current_task,
727                uapi::UI_SET_ABSBIT,
728                SyscallArg::from(uapi::ABS_MT_TOUCH_MAJOR as u64),
729            );
730            assert_eq!(r, Ok(SUCCESS));
731        })
732        .await;
733    }
734
735    #[::fuchsia::test]
736    async fn ui_set_propbit() {
737        spawn_kernel_and_run(async move |current_task| {
738            let (dev, file_object) = new_kernel_objects(current_task).await;
739            let r = dev.ioctl(
740                &file_object,
741                current_task,
742                uapi::UI_SET_PROPBIT,
743                SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
744            );
745            assert_eq!(r, Ok(SUCCESS));
746
747            // also test call multi times.
748            let r = dev.ioctl(
749                &file_object,
750                current_task,
751                uapi::UI_SET_PROPBIT,
752                SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
753            );
754            assert_eq!(r, Ok(SUCCESS));
755        })
756        .await;
757    }
758}