1use 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
33const 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 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 let device_type = match self.enabled_evbits.clone().get(uapi::EV_ABS as usize) {
147 Some(true) => {
148 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 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 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 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 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 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 fn ui_dev_create_inner(
277 &self,
278 current_task: &CurrentTask,
279 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 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 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 }
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
453static 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
468impl 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 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 _ => {
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 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 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 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 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}