1use crate::InputEventsRelayHandle;
6use crate::uinput::vfs::{CloseFreeSafe, NamespaceNode};
7
8use bit_vec::BitVec;
9use fidl_fuchsia_ui_test_input::{
10 self as futinput, CoordinateUnit, DisplayDimensions, KeyboardSimulateKeyEventRequest,
11 RegistryRegisterKeyboardAndGetDeviceInfoRequest,
12 RegistryRegisterTouchScreenAndGetDeviceInfoRequest,
13};
14
15use starnix_core::device::kobject::DeviceMetadata;
16use starnix_core::device::{DeviceMode, DeviceOps};
17use starnix_core::fileops_impl_seekless;
18use starnix_core::mm::MemoryAccessorExt;
19use starnix_core::task::{CurrentTask, Kernel};
20use starnix_core::vfs::{self, FileObject, FileOps, fileops_impl_noop_sync};
21use starnix_logging::log_warn;
22use starnix_modules_input_event_conversion::key_linux_to_fuchsia::LinuxKeyboardEventParser;
23use starnix_modules_input_event_conversion::touch_linux_to_fuchsia::LinuxTouchEventParser;
24use starnix_sync::{LockDepMutex, UinputDeviceStateLock};
25use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
26
27use starnix_uapi::errors::Errno;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::user_address::{MultiArchUserRef, UserRef};
30use starnix_uapi::{device_id, errno, error, uapi};
31use std::sync::Arc;
32use std::sync::atomic::{AtomicI32, Ordering};
33
34const UINPUT_VERSION: u32 = 5;
37
38const DEFAULT_TOUCHSCREEN_WIDTH: i32 = 1280;
39const DEFAULT_TOUCHSCREEN_HEIGHT: i32 = 800;
40const DEFAULT_UINPUT_DEVICE_NAME: &str = "starnix_uinput";
41
42type InputEventPtr = MultiArchUserRef<uapi::input_event, uapi::arch32::input_event>;
43
44#[derive(Clone)]
45enum DeviceId {
46 Keyboard,
47 Touchscreen,
48}
49
50pub fn register_uinput_device(
51 kernel: &Kernel,
52 input_relay_handle: Arc<InputEventsRelayHandle>,
53) -> Result<(), Errno> {
54 let registry = &kernel.device_registry;
55 let misc_class = registry.objects.misc_class();
56 let device = UinputDevice::new(input_relay_handle);
57 registry.register_device(
58 kernel,
59 "uinput".into(),
60 DeviceMetadata::new("uinput".into(), device_id::DeviceId::UINPUT, DeviceMode::Char),
61 misc_class,
62 device,
63 )?;
64 Ok(())
65}
66#[derive(Clone)]
67struct UinputDevice {
68 input_relay_handle: Arc<InputEventsRelayHandle>,
69}
70
71impl UinputDevice {
72 pub fn new(input_relay_handle: Arc<InputEventsRelayHandle>) -> Self {
73 Self { input_relay_handle }
74 }
75}
76
77impl DeviceOps for UinputDevice {
78 fn open(
79 &self,
80 _current_task: &CurrentTask,
81 _id: device_id::DeviceId,
82 _node: &NamespaceNode,
83 _flags: OpenFlags,
84 ) -> Result<Box<dyn FileOps>, Errno> {
85 Ok(Box::new(UinputDeviceFile::new(self.input_relay_handle.clone())))
86 }
87}
88
89enum CreatedDevice {
90 None,
91 Keyboard(futinput::KeyboardSynchronousProxy, LinuxKeyboardEventParser),
92 Touchscreen(futinput::TouchScreenSynchronousProxy, Box<LinuxTouchEventParser>),
94}
95
96#[derive(Clone, Copy)]
97struct Range {
98 min: i32,
99 max: i32,
100}
101struct UinputDeviceMutableState {
102 enabled_evbits: BitVec,
103 input_id: Option<uapi::input_id>,
104 name: Option<String>,
105 created_device: CreatedDevice,
106 x_range: Option<Range>,
107 y_range: Option<Range>,
108}
109
110impl UinputDeviceMutableState {
111 fn get_id_and_device_type(&self) -> Option<(uapi::input_id, DeviceId)> {
112 let input_id = match self.input_id {
113 Some(input_id) => input_id,
114 None => return None,
115 };
116 let device_type = match self.enabled_evbits.get(uapi::EV_ABS as usize) {
120 Some(true) => DeviceId::Touchscreen,
121 Some(false) | None => DeviceId::Keyboard,
122 };
123
124 Some((input_id, device_type))
125 }
126
127 fn teardown_device(&mut self) -> bool {
128 if matches!(self.created_device, CreatedDevice::None) {
129 return false;
130 }
131 self.created_device = CreatedDevice::None;
132 destroy_device();
133 true
134 }
135}
136
137struct UinputDeviceFile {
138 inner: LockDepMutex<UinputDeviceMutableState, UinputDeviceStateLock>,
139 input_relay_handle: Arc<InputEventsRelayHandle>,
140}
141
142impl UinputDeviceFile {
143 pub fn new(input_relay_handle: Arc<InputEventsRelayHandle>) -> Self {
144 Self {
145 inner: UinputDeviceMutableState {
146 enabled_evbits: BitVec::from_elem(uapi::EV_CNT as usize, false),
147 input_id: None,
148 name: None,
149 created_device: CreatedDevice::None,
150 x_range: None,
151 y_range: None,
152 }
153 .into(),
154 input_relay_handle,
155 }
156 }
157
158 fn ui_set_evbit(&self, arg: SyscallArg) -> Result<SyscallResult, Errno> {
161 let evbit: u32 = arg.into();
162 match evbit {
163 uapi::EV_KEY | uapi::EV_ABS => {
164 let mut inner = self.inner.lock();
165 if !matches!(inner.created_device, CreatedDevice::None) {
166 return error!(EINVAL);
167 }
168 inner.enabled_evbits.set(evbit as usize, true);
169 Ok(SUCCESS)
170 }
171 _ => {
172 log_warn!("UI_SET_EVBIT with unsupported evbit {}", evbit);
173 error!(EPERM)
174 }
175 }
176 }
177
178 fn ui_abs_setup(
181 &self,
182 current_task: &CurrentTask,
183 abs_setup: UserRef<starnix_uapi::uinput_abs_setup>,
184 ) -> Result<SyscallResult, Errno> {
185 let setup: starnix_uapi::uinput_abs_setup = current_task.read_object(abs_setup)?;
186 if setup.absinfo.minimum >= setup.absinfo.maximum {
187 return error!(EINVAL);
188 }
189 let code: u32 = setup.code.into();
190 match code {
191 uapi::ABS_MT_POSITION_X => {
192 let mut inner = self.inner.lock();
193 if !matches!(inner.created_device, CreatedDevice::None) {
194 return error!(EINVAL);
195 }
196 inner.x_range =
197 Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
198 }
199 uapi::ABS_MT_POSITION_Y => {
200 let mut inner = self.inner.lock();
201 if !matches!(inner.created_device, CreatedDevice::None) {
202 return error!(EINVAL);
203 }
204 inner.y_range =
205 Some(Range { min: setup.absinfo.minimum, max: setup.absinfo.maximum });
206 }
207 _ => {
208 log_warn!("UI_ABS_SETUP ignore event code {}", setup.code);
209 }
210 }
211 Ok(SUCCESS)
212 }
213
214 fn ui_get_version(
218 &self,
219 current_task: &CurrentTask,
220 user_version: UserRef<u32>,
221 ) -> Result<SyscallResult, Errno> {
222 let response: u32 = UINPUT_VERSION;
223 current_task.write_object(user_version, &response)?;
224 Ok(SUCCESS)
225 }
226
227 fn ui_dev_setup(
230 &self,
231 current_task: &CurrentTask,
232 user_uinput_setup: UserRef<uapi::uinput_setup>,
233 ) -> Result<SyscallResult, Errno> {
234 let uinput_setup = current_task.read_object(user_uinput_setup)?;
235 let mut inner = self.inner.lock();
236 if !matches!(inner.created_device, CreatedDevice::None) {
237 return error!(EINVAL);
238 }
239 inner.input_id = Some(uinput_setup.id);
240 let name_bytes: &[u8] = zerocopy::IntoBytes::as_bytes(&uinput_setup.name);
242 let name_len = name_bytes.iter().position(|&c| c == 0).unwrap_or(name_bytes.len());
243 let name = std::str::from_utf8(&name_bytes[..name_len])
244 .ok()
245 .filter(|s| !s.is_empty())
246 .map(|s| s.to_string())
247 .unwrap_or_else(|| DEFAULT_UINPUT_DEVICE_NAME.to_string());
248 inner.name = Some(name);
249 Ok(SUCCESS)
250 }
251
252 fn ui_dev_create(&self, _current_task: &CurrentTask) -> Result<SyscallResult, Errno> {
253 {
254 let inner = self.inner.lock();
255 if !matches!(inner.created_device, CreatedDevice::None) {
256 return error!(EINVAL);
257 }
258 if inner.get_id_and_device_type().is_none() {
259 return error!(EINVAL);
260 }
261 }
262
263 let registry = match fuchsia_component::client::connect_to_protocol_sync::<
265 futinput::RegistryMarker,
266 >() {
267 Ok(proxy) => Some(proxy),
268 Err(_) => {
269 log_warn!("Could not connect to fuchsia.ui.test.input/Registry");
270 None
271 }
272 };
273 self.ui_dev_create_inner(registry)
274 }
275
276 fn ui_dev_create_inner(
279 &self,
280 registry: Option<futinput::RegistrySynchronousProxy>,
282 ) -> Result<SyscallResult, Errno> {
283 let (input_id, device_type, name, x_range, y_range) = {
284 let inner = self.inner.lock();
285 if !matches!(inner.created_device, CreatedDevice::None) {
286 return error!(EINVAL);
287 }
288
289 let (input_id, device_type) = match inner.get_id_and_device_type() {
290 Some((id, dev)) => (id, dev),
291 None => return error!(EINVAL),
292 };
293 let name = inner.name.clone().unwrap_or_else(|| DEFAULT_UINPUT_DEVICE_NAME.to_string());
294 (input_id, device_type, name, inner.x_range, inner.y_range)
295 };
296
297 let proxy = match registry {
298 Some(proxy) => proxy,
299 None => {
300 log_warn!("No Registry available for Uinput.");
301 return error!(EPERM);
302 }
303 };
304
305 let (created_device, device_id) = match device_type {
306 DeviceId::Keyboard => {
307 let (key_client, key_server) =
308 fidl::endpoints::create_sync_proxy::<futinput::KeyboardMarker>();
309
310 let register_res = proxy.register_keyboard_and_get_device_info(
312 RegistryRegisterKeyboardAndGetDeviceInfoRequest {
313 device: Some(key_server),
314 ..Default::default()
315 },
316 zx::MonotonicInstant::INFINITE,
317 );
318
319 match register_res {
320 Ok(resp) => match resp.device_id {
321 Some(device_id) => {
322 let created_device = CreatedDevice::Keyboard(
323 key_client,
324 LinuxKeyboardEventParser::create(),
325 );
326 Ok((created_device, device_id))
327 }
328 None => {
329 log_warn!(
330 "register_keyboard_and_get_device_info response does not include a device_id"
331 );
332 error!(EPERM)
333 }
334 },
335 Err(e) => {
336 log_warn!("Uinput could not register Keyboard device to Registry: {:?}", e);
337 error!(EPERM)
338 }
339 }
340 }
341 DeviceId::Touchscreen => {
342 let (touch_client, touch_server) =
343 fidl::endpoints::create_sync_proxy::<futinput::TouchScreenMarker>();
344
345 let (display_width, display_height) =
346 (*self.input_relay_handle.display_size.lock())
347 .filter(|&(w, h)| w > 0 && h > 0)
348 .unwrap_or((DEFAULT_TOUCHSCREEN_WIDTH, DEFAULT_TOUCHSCREEN_HEIGHT));
349 let (min_x, max_x) = x_range.map(|r| (r.min, r.max)).unwrap_or((0, display_width));
350 let (min_y, max_y) = y_range.map(|r| (r.min, r.max)).unwrap_or((0, display_height));
351 if min_x >= max_x || min_y >= max_y {
352 return error!(EINVAL);
353 }
354
355 let request = RegistryRegisterTouchScreenAndGetDeviceInfoRequest {
356 device: Some(touch_server),
357 coordinate_unit: Some(CoordinateUnit::RegisteredDimensions),
358 display_dimensions: Some(DisplayDimensions {
359 min_x: min_x.into(),
360 max_x: max_x.into(),
361 min_y: min_y.into(),
362 max_y: max_y.into(),
363 }),
364 ..Default::default()
365 };
366
367 let register_res = proxy.register_touch_screen_and_get_device_info(
369 request,
370 zx::MonotonicInstant::INFINITE,
371 );
372
373 match register_res {
374 Ok(resp) => match resp.device_id {
375 Some(device_id) => {
376 let created_device = CreatedDevice::Touchscreen(
377 touch_client,
378 Box::new(LinuxTouchEventParser::create()),
379 );
380 Ok((created_device, device_id))
381 }
382 None => {
383 log_warn!(
384 "register_touch_screen_and_get_device_info response does not include a device_id"
385 );
386 error!(EPERM)
387 }
388 },
389 Err(e) => {
390 log_warn!(
391 "Uinput could not register TouchScreen device to Registry: {:?}",
392 e
393 );
394 error!(EPERM)
395 }
396 }
397 }
398 }?;
399
400 {
401 let mut inner = self.inner.lock();
402 if !matches!(inner.created_device, CreatedDevice::None) {
403 return error!(EINVAL);
404 }
405 inner.created_device = created_device;
406 }
407
408 self.input_relay_handle.update_uinput_device(device_id, input_id, name);
434 new_device();
435
436 Ok(SUCCESS)
437 }
438
439 fn ui_dev_destroy(&self, _current_task: &CurrentTask) -> Result<SyscallResult, Errno> {
440 let mut inner = self.inner.lock();
441 if !inner.teardown_device() {
442 return error!(EPERM);
443 }
444 Ok(SUCCESS)
445 }
446}
447
448static COUNT_OF_UINPUT_DEVICE: AtomicI32 = AtomicI32::new(0);
450
451fn new_device() {
452 let _ = COUNT_OF_UINPUT_DEVICE.fetch_add(1, Ordering::SeqCst);
453}
454
455fn destroy_device() {
456 let _ = COUNT_OF_UINPUT_DEVICE.fetch_sub(1, Ordering::SeqCst);
457}
458
459pub fn uinput_running() -> bool {
460 COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst) > 0
461}
462
463impl CloseFreeSafe for UinputDeviceFile {}
465
466impl Drop for UinputDeviceFile {
467 fn drop(&mut self) {
468 let inner = self.inner.get_mut();
469 inner.teardown_device();
470 }
471}
472
473impl FileOps for UinputDeviceFile {
474 fileops_impl_seekless!();
475 fileops_impl_noop_sync!();
476
477 fn ioctl(
478 &self,
479 _file: &FileObject,
480 current_task: &CurrentTask,
481 request: u32,
482 arg: SyscallArg,
483 ) -> Result<SyscallResult, Errno> {
484 match request {
485 uapi::UI_GET_VERSION => self.ui_get_version(current_task, arg.into()),
486 uapi::UI_SET_EVBIT => self.ui_set_evbit(arg),
487 uapi::UI_ABS_SETUP => self.ui_abs_setup(current_task, arg.into()),
488 uapi::UI_SET_KEYBIT
492 | uapi::UI_SET_ABSBIT
493 | uapi::UI_SET_PHYS
494 | uapi::UI_SET_PROPBIT => Ok(SUCCESS),
495 uapi::UI_DEV_SETUP => self.ui_dev_setup(current_task, arg.into()),
496 uapi::UI_DEV_CREATE => self.ui_dev_create(current_task),
497 uapi::UI_DEV_DESTROY => self.ui_dev_destroy(current_task),
498 _ => {
501 log_warn!("receive unknown ioctl request: {:?}", request);
502 error!(ENOTTY)
503 }
504 }
505 }
506
507 fn write(
508 &self,
509 _file: &vfs::FileObject,
510 current_task: &starnix_core::task::CurrentTask,
511 _offset: usize,
512 data: &mut dyn vfs::buffers::InputBuffer,
513 ) -> Result<usize, Errno> {
514 let content = data.read_all()?;
515 let event =
516 InputEventPtr::read_from_prefix(current_task, &content).map_err(|_| errno!(EINVAL))?;
517 let mut inner = self.inner.lock();
518
519 match &mut inner.created_device {
520 CreatedDevice::Keyboard(proxy, parser) => {
521 let input_report = parser.handle(event);
522 match input_report {
523 Ok(Some(report)) => {
524 if let Some(keyboard_report) = report.keyboard {
525 let res = proxy.simulate_key_event(
526 &KeyboardSimulateKeyEventRequest {
527 pressed_keys: keyboard_report.pressed_keys3,
528 ..Default::default()
529 },
530 zx::MonotonicInstant::INFINITE,
531 );
532 if res.is_err() {
533 return error!(EIO);
534 }
535 }
536 }
537 Ok(None) => (),
538 Err(e) => return Err(e),
539 }
540 }
541 CreatedDevice::Touchscreen(proxy, parser) => {
542 let input_report: Result<Option<futinput::TouchInputReport>, Errno> =
543 parser.handle(event);
544 match input_report {
545 Ok(Some(report)) => {
546 let res =
547 proxy.simulate_touch_event(&report, zx::MonotonicInstant::INFINITE);
548 if res.is_err() {
549 return error!(EIO);
550 }
551 }
552 Ok(None) => (),
553 Err(e) => return Err(e),
554 }
555 }
556 CreatedDevice::None => return error!(EINVAL),
557 }
558
559 Ok(content.len())
560 }
561
562 fn read(
563 &self,
564 _file: &vfs::FileObject,
565 _current_task: &starnix_core::task::CurrentTask,
566 _offset: usize,
567 _data: &mut dyn vfs::buffers::OutputBuffer,
568 ) -> Result<usize, Errno> {
569 log_warn!("uinput FD does not support read().");
570 error!(EINVAL)
571 }
572}
573
574#[cfg(test)]
575mod test {
576 use super::*;
577 use crate::{EventProxyMode, start_input_relays_for_test};
578 use futures::TryStreamExt;
579 use starnix_core::testing::{map_memory, spawn_kernel_and_run};
580 use starnix_core::vfs::FileHandle;
581 use starnix_uapi::user_address::UserAddress;
582 use std::sync::Arc;
583 use test_case::test_case;
584
585 static UINPUT_RUNNING_LOCK: starnix_sync::Mutex<()> = starnix_sync::Mutex::new(());
586
587 fn ensure_uinput_running_lock() -> starnix_sync::MutexGuard<'static, ()> {
588 UINPUT_RUNNING_LOCK.lock()
589 }
590
591 async fn new_kernel_objects(current_task: &CurrentTask) -> (Arc<UinputDeviceFile>, FileHandle) {
592 let (input_relay_handle, _, _, _, _, _, _, _, _, _, _, _) =
593 start_input_relays_for_test(current_task, EventProxyMode::None).await;
594 let dev = Arc::new(UinputDeviceFile::new(input_relay_handle));
595
596 let root_namespace_node = current_task
597 .lookup_path_from_root(".".into())
598 .expect("failed to get namespace node for root");
599
600 let file_object = FileObject::new(
601 current_task,
602 Box::new(dev.clone()),
603 root_namespace_node,
606 OpenFlags::empty(),
607 )
608 .expect("FileObject::new failed");
609 (dev, file_object)
610 }
611
612 #[test_case(uapi::EV_KEY, vec![uapi::EV_KEY as usize] => Ok(SUCCESS))]
613 #[test_case(uapi::EV_ABS, vec![uapi::EV_ABS as usize] => Ok(SUCCESS))]
614 #[test_case(uapi::EV_REL, vec![] => error!(EPERM))]
615 #[::fuchsia::test]
616 async fn ui_set_evbit(bit: u32, expected_evbits: Vec<usize>) -> Result<SyscallResult, Errno> {
617 spawn_kernel_and_run(async move |current_task| {
618 let (dev, file_object) = new_kernel_objects(current_task).await;
619 let r = dev.ioctl(
620 &file_object,
621 current_task,
622 uapi::UI_SET_EVBIT,
623 SyscallArg::from(bit as u64),
624 );
625 for expected_evbit in expected_evbits {
626 assert!(dev.inner.lock().enabled_evbits.get(expected_evbit).unwrap());
627 }
628 r
629 })
630 .await
631 }
632
633 #[::fuchsia::test]
634 async fn ui_set_evbit_call_multi() {
635 spawn_kernel_and_run(async move |current_task| {
636 let (dev, file_object) = new_kernel_objects(current_task).await;
637 let r = dev.ioctl(
638 &file_object,
639 current_task,
640 uapi::UI_SET_EVBIT,
641 SyscallArg::from(uapi::EV_KEY as u64),
642 );
643 assert_eq!(r, Ok(SUCCESS));
644 let r = dev.ioctl(
645 &file_object,
646 current_task,
647 uapi::UI_SET_EVBIT,
648 SyscallArg::from(uapi::EV_ABS as u64),
649 );
650 assert_eq!(r, Ok(SUCCESS));
651 assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_KEY as usize).unwrap());
652 assert!(dev.inner.lock().enabled_evbits.get(uapi::EV_ABS as usize).unwrap());
653 })
654 .await;
655 }
656
657 #[::fuchsia::test]
658 async fn ui_set_keybit() {
659 spawn_kernel_and_run(async move |current_task| {
660 let (dev, file_object) = new_kernel_objects(current_task).await;
661 let r = dev.ioctl(
662 &file_object,
663 current_task,
664 uapi::UI_SET_KEYBIT,
665 SyscallArg::from(uapi::BTN_TOUCH as u64),
666 );
667 assert_eq!(r, Ok(SUCCESS));
668
669 let r = dev.ioctl(
671 &file_object,
672 current_task,
673 uapi::UI_SET_KEYBIT,
674 SyscallArg::from(uapi::KEY_SPACE as u64),
675 );
676 assert_eq!(r, Ok(SUCCESS));
677 })
678 .await;
679 }
680
681 #[::fuchsia::test]
682 async fn ui_set_absbit() {
683 spawn_kernel_and_run(async move |current_task| {
684 let (dev, file_object) = new_kernel_objects(current_task).await;
685 let r = dev.ioctl(
686 &file_object,
687 current_task,
688 uapi::UI_SET_ABSBIT,
689 SyscallArg::from(uapi::ABS_MT_SLOT as u64),
690 );
691 assert_eq!(r, Ok(SUCCESS));
692
693 let r = dev.ioctl(
695 &file_object,
696 current_task,
697 uapi::UI_SET_ABSBIT,
698 SyscallArg::from(uapi::ABS_MT_TOUCH_MAJOR as u64),
699 );
700 assert_eq!(r, Ok(SUCCESS));
701 })
702 .await;
703 }
704
705 #[::fuchsia::test]
706 async fn ui_set_propbit() {
707 spawn_kernel_and_run(async move |current_task| {
708 let (dev, file_object) = new_kernel_objects(current_task).await;
709 let r = dev.ioctl(
710 &file_object,
711 current_task,
712 uapi::UI_SET_PROPBIT,
713 SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
714 );
715 assert_eq!(r, Ok(SUCCESS));
716
717 let r = dev.ioctl(
719 &file_object,
720 current_task,
721 uapi::UI_SET_PROPBIT,
722 SyscallArg::from(uapi::INPUT_PROP_DIRECT as u64),
723 );
724 assert_eq!(r, Ok(SUCCESS));
725 })
726 .await;
727 }
728
729 #[::fuchsia::test]
730 async fn ui_dev_destroy_uncreated() {
731 spawn_kernel_and_run(async move |current_task| {
732 let (dev, file_object) = new_kernel_objects(current_task).await;
733 let _guard = ensure_uinput_running_lock();
734 assert!(!uinput_running());
735 let r =
736 dev.ioctl(&file_object, current_task, uapi::UI_DEV_DESTROY, SyscallArg::from(0u64));
737 assert_eq!(r, error!(EPERM));
738 assert!(!uinput_running());
739 })
740 .await;
741 }
742
743 #[::fuchsia::test]
744 async fn ui_dev_create_updates_existing_device_info() {
745 spawn_kernel_and_run(async move |current_task| {
746 let (dev, _file_object) = new_kernel_objects(current_task).await;
747 let device_id = 42;
748 let initial_input_id = uapi::input_id { bustype: 1, vendor: 2, product: 3, version: 4 };
749 let initial_info =
750 crate::InputDeviceInfo::new(initial_input_id, "initial_name".to_string());
751 dev.input_relay_handle.device_infos.lock().insert(device_id, initial_info.clone());
752
753 let updated_input_id =
754 uapi::input_id { bustype: 10, vendor: 20, product: 30, version: 40 };
755 let updated_name = "updated_uinput_device".to_string();
756
757 dev.input_relay_handle.update_uinput_device(
761 device_id,
762 updated_input_id,
763 updated_name.clone(),
764 );
765
766 let locked_info = initial_info.lock();
768 assert_eq!(locked_info.input_id, updated_input_id);
769 assert_eq!(locked_info.name, updated_name);
770 })
771 .await;
772 }
773
774 #[::fuchsia::test]
775 async fn ui_dev_setup_name_parsing() {
776 spawn_kernel_and_run(async move |current_task| {
777 let (dev, file_object) = new_kernel_objects(current_task).await;
778
779 let mut setup = uapi::uinput_setup::default();
781 setup.id = uapi::input_id { bustype: 3, vendor: 0x1234, product: 0x5678, version: 1 };
782 let name_bytes = b"my_uinput_touch\0";
783 for (i, &b) in name_bytes.iter().enumerate() {
784 setup.name[i] = b as _;
785 }
786
787 let user_setup = map_memory(
788 current_task,
789 UserAddress::default(),
790 std::mem::size_of::<uapi::uinput_setup>() as u64,
791 );
792 current_task.write_object(UserRef::new(user_setup), &setup).expect("write_object");
793
794 let r = dev.ioctl(
795 &file_object,
796 current_task,
797 uapi::UI_DEV_SETUP,
798 SyscallArg::from(user_setup.ptr() as u64),
799 );
800 assert_eq!(r, Ok(SUCCESS));
801
802 {
803 let inner = dev.inner.lock();
804 assert_eq!(inner.input_id, Some(setup.id));
805 assert_eq!(inner.name, Some("my_uinput_touch".to_string()));
806 }
807
808 let mut setup_full = uapi::uinput_setup::default();
810 setup_full.id = setup.id;
811 setup_full.name.fill(b'a' as _);
812 current_task.write_object(UserRef::new(user_setup), &setup_full).expect("write_object");
813 let r = dev.ioctl(
814 &file_object,
815 current_task,
816 uapi::UI_DEV_SETUP,
817 SyscallArg::from(user_setup.ptr() as u64),
818 );
819 assert_eq!(r, Ok(SUCCESS));
820 {
821 let inner = dev.inner.lock();
822 assert_eq!(inner.name, Some("a".repeat(setup_full.name.len())));
823 }
824
825 let mut setup_invalid = uapi::uinput_setup::default();
827 setup_invalid.id = setup.id;
828 setup_invalid.name[0] = 0xff as u8 as _;
829 setup_invalid.name[1] = 0;
830 current_task
831 .write_object(UserRef::new(user_setup), &setup_invalid)
832 .expect("write_object");
833 let r = dev.ioctl(
834 &file_object,
835 current_task,
836 uapi::UI_DEV_SETUP,
837 SyscallArg::from(user_setup.ptr() as u64),
838 );
839 assert_eq!(r, Ok(SUCCESS));
840 {
841 let inner = dev.inner.lock();
842 assert_eq!(inner.name, Some(DEFAULT_UINPUT_DEVICE_NAME.to_string()));
843 }
844
845 let mut setup_empty = uapi::uinput_setup::default();
847 setup_empty.id = setup.id;
848 setup_empty.name[0] = 0;
849 current_task
850 .write_object(UserRef::new(user_setup), &setup_empty)
851 .expect("write_object");
852 let r = dev.ioctl(
853 &file_object,
854 current_task,
855 uapi::UI_DEV_SETUP,
856 SyscallArg::from(user_setup.ptr() as u64),
857 );
858 assert_eq!(r, Ok(SUCCESS));
859 {
860 let inner = dev.inner.lock();
861 assert_eq!(inner.name, Some(DEFAULT_UINPUT_DEVICE_NAME.to_string()));
862 }
863 })
864 .await;
865 }
866
867 fn spawn_mock_registry() -> (
868 futinput::RegistrySynchronousProxy,
869 std::thread::JoinHandle<()>,
870 Arc<std::sync::atomic::AtomicBool>,
871 Arc<starnix_sync::Mutex<Option<DisplayDimensions>>>,
872 ) {
873 let (client, server) = fidl::endpoints::create_endpoints::<futinput::RegistryMarker>();
874 let proxy = client.into_sync_proxy();
875 let received_request = Arc::new(std::sync::atomic::AtomicBool::new(false));
876 let received_flag = received_request.clone();
877 let captured_display_dimensions = Arc::new(starnix_sync::Mutex::new(None));
878 let dimensions_slot = captured_display_dimensions.clone();
879 let handle = std::thread::spawn(move || {
880 let mut executor = fuchsia_async::LocalExecutor::default();
881 executor.run_singlethreaded(async move {
882 let mut stream = server.into_stream();
883 while let Ok(Some(request)) = stream.try_next().await {
884 received_flag.store(true, Ordering::SeqCst);
885 match request {
886 futinput::RegistryRequest::RegisterTouchScreenAndGetDeviceInfo {
887 payload,
888 responder,
889 } => {
890 assert_eq!(
891 payload.coordinate_unit,
892 Some(CoordinateUnit::RegisteredDimensions)
893 );
894 *dimensions_slot.lock() = payload.display_dimensions;
895 let _ = responder.send(
896 futinput::RegistryRegisterTouchScreenAndGetDeviceInfoResponse {
897 device_id: Some(101),
898 ..Default::default()
899 },
900 );
901 }
902 futinput::RegistryRequest::RegisterKeyboardAndGetDeviceInfo {
903 responder,
904 ..
905 } => {
906 let _ = responder.send(
907 futinput::RegistryRegisterKeyboardAndGetDeviceInfoResponse {
908 device_id: Some(102),
909 ..Default::default()
910 },
911 );
912 }
913 _ => {}
914 }
915 }
916 });
917 });
918 (proxy, handle, received_request, captured_display_dimensions)
919 }
920
921 #[::fuchsia::test]
922 async fn ui_dev_create_keyboard_and_destroy() {
923 spawn_kernel_and_run(async move |current_task| {
924 let (dev, file_object) = new_kernel_objects(current_task).await;
925 let _guard = ensure_uinput_running_lock();
926 assert!(!uinput_running());
927 let (proxy, join_handle, received, _) = spawn_mock_registry();
928
929 let input_id = uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 };
930 {
931 let mut inner = dev.inner.lock();
932 inner.input_id = Some(input_id);
933 inner.name = Some("test_keyboard".to_string());
934 inner.enabled_evbits.set(uapi::EV_KEY as usize, true);
935 }
936
937 let r = dev.ui_dev_create_inner(Some(proxy));
938 assert_eq!(r, Ok(SUCCESS));
939 assert!(uinput_running());
940 assert!(received.load(Ordering::SeqCst));
941
942 {
944 let device_infos = dev.input_relay_handle.device_infos.lock();
945 let info = device_infos.get(&102).expect("device info found").lock();
946 assert_eq!(info.name, "test_keyboard");
947 assert_eq!(info.input_id, input_id);
948 }
949
950 let r =
952 dev.ioctl(&file_object, current_task, uapi::UI_DEV_DESTROY, SyscallArg::from(0u64));
953 assert_eq!(r, Ok(SUCCESS));
954 assert!(!uinput_running());
955 assert!(dev.input_relay_handle.device_infos.lock().get(&102).is_some());
957
958 let r =
960 dev.ioctl(&file_object, current_task, uapi::UI_DEV_DESTROY, SyscallArg::from(0u64));
961 assert_eq!(r, error!(EPERM));
962
963 std::mem::drop(dev);
964 std::mem::drop(file_object);
965 let _ = join_handle.join();
966 })
967 .await;
968 }
969
970 #[::fuchsia::test]
971 async fn ui_dev_create_touchscreen_display_size_fallback_and_drop_cleanup() {
972 spawn_kernel_and_run(async move |current_task| {
973 let (input_relay_handle, _, _, _, _, _, _, _, _, _, _, _) =
974 start_input_relays_for_test(current_task, EventProxyMode::None).await;
975 let _guard = ensure_uinput_running_lock();
976 assert!(!uinput_running());
977
978 *input_relay_handle.display_size.lock() = Some((0, 0));
981
982 let dev = Arc::new(UinputDeviceFile::new(input_relay_handle.clone()));
983 let (proxy, join_handle, received, captured_dimensions) = spawn_mock_registry();
984
985 let input_id = uapi::input_id { bustype: 3, vendor: 5, product: 6, version: 7 };
986 {
987 let mut inner = dev.inner.lock();
988 inner.input_id = Some(input_id);
989 inner.name = Some("test_touchscreen".to_string());
990 inner.enabled_evbits.set(uapi::EV_ABS as usize, true);
991 }
993
994 let r = dev.ui_dev_create_inner(Some(proxy));
995 assert_eq!(r, Ok(SUCCESS));
996 assert!(uinput_running());
997 assert!(received.load(Ordering::SeqCst));
998 assert_eq!(
999 *captured_dimensions.lock(),
1000 Some(DisplayDimensions {
1001 min_x: 0,
1002 max_x: DEFAULT_TOUCHSCREEN_WIDTH.into(),
1003 min_y: 0,
1004 max_y: DEFAULT_TOUCHSCREEN_HEIGHT.into(),
1005 })
1006 );
1007
1008 assert!(input_relay_handle.device_infos.lock().get(&101).is_some());
1010
1011 std::mem::drop(dev);
1013 assert!(!uinput_running());
1014 assert!(input_relay_handle.device_infos.lock().get(&101).is_some());
1016
1017 let _ = join_handle.join();
1018 })
1019 .await;
1020 }
1021
1022 #[::fuchsia::test]
1023 async fn ui_dev_create_touchscreen_explicit_ranges() {
1024 spawn_kernel_and_run(async move |current_task| {
1025 let (dev, file_object) = new_kernel_objects(current_task).await;
1026 let _guard = ensure_uinput_running_lock();
1027 assert!(!uinput_running());
1028 let (proxy, join_handle, _, captured_dimensions) = spawn_mock_registry();
1029
1030 let input_id = uapi::input_id { bustype: 3, vendor: 10, product: 20, version: 30 };
1031 {
1032 let mut inner = dev.inner.lock();
1033 inner.input_id = Some(input_id);
1034 inner.name = Some("touch_ranges".to_string());
1035 inner.enabled_evbits.set(uapi::EV_ABS as usize, true);
1036 inner.x_range = Some(Range { min: 100, max: 2000 });
1037 inner.y_range = Some(Range { min: 50, max: 1000 });
1038 }
1039
1040 let r = dev.ui_dev_create_inner(Some(proxy));
1041 assert_eq!(r, Ok(SUCCESS));
1042 assert!(uinput_running());
1043 assert_eq!(
1044 *captured_dimensions.lock(),
1045 Some(DisplayDimensions { min_x: 100, max_x: 2000, min_y: 50, max_y: 1000 })
1046 );
1047 assert!(dev.input_relay_handle.device_infos.lock().get(&101).is_some());
1048
1049 let r = dev.ui_dev_destroy(current_task);
1051 assert_eq!(r, Ok(SUCCESS));
1052 assert!(!uinput_running());
1053 assert!(dev.input_relay_handle.device_infos.lock().get(&101).is_some());
1055
1056 std::mem::drop(dev);
1057 std::mem::drop(file_object);
1058 let _ = join_handle.join();
1059 })
1060 .await;
1061 }
1062
1063 #[::fuchsia::test]
1064 async fn ui_dev_create_already_created_returns_einval() {
1065 spawn_kernel_and_run(async move |current_task| {
1066 let (dev, file_object) = new_kernel_objects(current_task).await;
1067 let _guard = ensure_uinput_running_lock();
1068 assert!(!uinput_running());
1069 let (proxy, join_handle, _, _) = spawn_mock_registry();
1070
1071 let input_id = uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 };
1072 {
1073 let mut inner = dev.inner.lock();
1074 inner.input_id = Some(input_id);
1075 inner.name = Some("test_keyboard".to_string());
1076 inner.enabled_evbits.set(uapi::EV_KEY as usize, true);
1077 }
1078
1079 let r = dev.ui_dev_create_inner(Some(proxy));
1080 assert_eq!(r, Ok(SUCCESS));
1081 assert!(uinput_running());
1082 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 1);
1083
1084 let (proxy2, join_handle2, _, _) = spawn_mock_registry();
1086 let r = dev.ui_dev_create_inner(Some(proxy2));
1087 assert_eq!(r, error!(EINVAL));
1088 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 1);
1089
1090 let r = dev.ui_dev_destroy(current_task);
1092 assert_eq!(r, Ok(SUCCESS));
1093 assert!(!uinput_running());
1094 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 0);
1095
1096 std::mem::drop(dev);
1097 std::mem::drop(file_object);
1098 let _ = join_handle.join();
1099 let _ = join_handle2.join();
1100 })
1101 .await;
1102 }
1103
1104 #[::fuchsia::test]
1105 async fn ui_abs_setup_validates_ranges() {
1106 spawn_kernel_and_run(async move |current_task| {
1107 let (dev, file_object) = new_kernel_objects(current_task).await;
1108 let user_setup = map_memory(
1109 current_task,
1110 UserAddress::default(),
1111 std::mem::size_of::<starnix_uapi::uinput_abs_setup>() as u64,
1112 );
1113
1114 let valid_setup = starnix_uapi::uinput_abs_setup {
1116 code: uapi::ABS_MT_POSITION_X as u16,
1117 absinfo: uapi::input_absinfo { minimum: 0, maximum: 1080, ..Default::default() },
1118 ..Default::default()
1119 };
1120 current_task.write_object(UserRef::new(user_setup), &valid_setup).expect("write");
1121 let r = dev.ioctl(
1122 &file_object,
1123 current_task,
1124 uapi::UI_ABS_SETUP,
1125 SyscallArg::from(user_setup.ptr() as u64),
1126 );
1127 assert_eq!(r, Ok(SUCCESS));
1128
1129 let invalid_setup = starnix_uapi::uinput_abs_setup {
1131 code: uapi::ABS_MT_POSITION_X as u16,
1132 absinfo: uapi::input_absinfo { minimum: 100, maximum: 100, ..Default::default() },
1133 ..Default::default()
1134 };
1135 current_task.write_object(UserRef::new(user_setup), &invalid_setup).expect("write");
1136 let r = dev.ioctl(
1137 &file_object,
1138 current_task,
1139 uapi::UI_ABS_SETUP,
1140 SyscallArg::from(user_setup.ptr() as u64),
1141 );
1142 assert_eq!(r, error!(EINVAL));
1143 })
1144 .await;
1145 }
1146
1147 #[::fuchsia::test]
1148 async fn ui_dev_create_touchscreen_invalid_ranges_returns_einval() {
1149 spawn_kernel_and_run(async move |current_task| {
1150 let (dev, file_object) = new_kernel_objects(current_task).await;
1151 let _guard = ensure_uinput_running_lock();
1152 let (proxy, join_handle, _, _) = spawn_mock_registry();
1153
1154 let input_id = uapi::input_id { bustype: 3, vendor: 10, product: 20, version: 30 };
1155 {
1156 let mut inner = dev.inner.lock();
1157 inner.input_id = Some(input_id);
1158 inner.name = Some("touch_invalid_ranges".to_string());
1159 inner.enabled_evbits.set(uapi::EV_ABS as usize, true);
1160 inner.x_range = Some(Range { min: 2000, max: 100 });
1162 inner.y_range = Some(Range { min: 50, max: 1000 });
1163 }
1164
1165 let r = dev.ui_dev_create_inner(Some(proxy));
1166 assert_eq!(r, error!(EINVAL));
1167 assert!(!uinput_running());
1168
1169 std::mem::drop(dev);
1170 std::mem::drop(file_object);
1171 let _ = join_handle.join();
1172 })
1173 .await;
1174 }
1175
1176 #[::fuchsia::test]
1177 async fn ui_dev_create_failure_preserves_running_count_and_state() {
1178 spawn_kernel_and_run(async move |current_task| {
1179 let (dev, file_object) = new_kernel_objects(current_task).await;
1180 let _guard = ensure_uinput_running_lock();
1181 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 0);
1182 assert!(!uinput_running());
1183
1184 let input_id = uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 };
1185 {
1186 let mut inner = dev.inner.lock();
1187 inner.input_id = Some(input_id);
1188 inner.name = Some("test_keyboard".to_string());
1189 inner.enabled_evbits.set(uapi::EV_KEY as usize, true);
1190 }
1191
1192 let r = dev.ui_dev_create_inner(None);
1194 assert_eq!(r, error!(EPERM));
1195
1196 assert!(matches!(dev.inner.lock().created_device, CreatedDevice::None));
1198 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 0);
1199 assert!(!uinput_running());
1200
1201 std::mem::drop(dev);
1203 std::mem::drop(file_object);
1204 assert_eq!(COUNT_OF_UINPUT_DEVICE.load(Ordering::SeqCst), 0);
1205 assert!(!uinput_running());
1206 })
1207 .await;
1208 }
1209
1210 #[::fuchsia::test]
1211 async fn ui_dev_create_before_setup_returns_einval() {
1212 spawn_kernel_and_run(async move |current_task| {
1213 let (dev, file_object) = new_kernel_objects(current_task).await;
1214 let _guard = ensure_uinput_running_lock();
1215 assert!(!uinput_running());
1216
1217 let r = dev.ui_dev_create_inner(None);
1219 assert_eq!(r, error!(EINVAL));
1220
1221 let r =
1223 dev.ioctl(&file_object, current_task, uapi::UI_DEV_CREATE, SyscallArg::from(0u64));
1224 assert_eq!(r, error!(EINVAL));
1225 })
1226 .await;
1227 }
1228
1229 #[::fuchsia::test]
1230 async fn ui_dev_create_touchscreen_single_axis_range_fallback() {
1231 spawn_kernel_and_run(async move |current_task| {
1232 let (dev, file_object) = new_kernel_objects(current_task).await;
1233 let _guard = ensure_uinput_running_lock();
1234 assert!(!uinput_running());
1235 let (proxy, join_handle, _, captured_dimensions) = spawn_mock_registry();
1236
1237 let input_id = uapi::input_id { bustype: 3, vendor: 10, product: 20, version: 30 };
1238 {
1239 let mut inner = dev.inner.lock();
1240 inner.input_id = Some(input_id);
1241 inner.name = Some("touch_single_axis".to_string());
1242 inner.enabled_evbits.set(uapi::EV_ABS as usize, true);
1243 inner.x_range = Some(Range { min: 100, max: 2000 });
1245 }
1246
1247 let r = dev.ui_dev_create_inner(Some(proxy));
1248 assert_eq!(r, Ok(SUCCESS));
1249 assert!(uinput_running());
1250 assert_eq!(
1251 *captured_dimensions.lock(),
1252 Some(DisplayDimensions { min_x: 100, max_x: 2000, min_y: 0, max_y: 1200 })
1253 );
1254
1255 std::mem::drop(dev);
1256 std::mem::drop(file_object);
1257 let _ = join_handle.join();
1258 })
1259 .await;
1260 }
1261
1262 #[::fuchsia::test]
1263 async fn ui_setup_after_create_returns_einval() {
1264 spawn_kernel_and_run(async move |current_task| {
1265 let (dev, file_object) = new_kernel_objects(current_task).await;
1266 let _guard = ensure_uinput_running_lock();
1267 let (proxy, join_handle, _, _) = spawn_mock_registry();
1268
1269 let input_id = uapi::input_id { bustype: 3, vendor: 10, product: 20, version: 30 };
1270 {
1271 let mut inner = dev.inner.lock();
1272 inner.input_id = Some(input_id);
1273 inner.name = Some("created_dev".to_string());
1274 inner.enabled_evbits.set(uapi::EV_KEY as usize, true);
1275 }
1276
1277 let r = dev.ui_dev_create_inner(Some(proxy));
1278 assert_eq!(r, Ok(SUCCESS));
1279
1280 let r = dev.ioctl(
1282 &file_object,
1283 current_task,
1284 uapi::UI_SET_EVBIT,
1285 SyscallArg::from(uapi::EV_KEY as u64),
1286 );
1287 assert_eq!(r, error!(EINVAL));
1288
1289 let user_setup = map_memory(
1291 current_task,
1292 UserAddress::default(),
1293 std::mem::size_of::<starnix_uapi::uinput_abs_setup>() as u64,
1294 );
1295 let valid_setup = starnix_uapi::uinput_abs_setup {
1296 code: uapi::ABS_MT_POSITION_X as u16,
1297 absinfo: uapi::input_absinfo { minimum: 0, maximum: 100, ..Default::default() },
1298 ..Default::default()
1299 };
1300 current_task.write_object(UserRef::new(user_setup), &valid_setup).expect("write");
1301 let r = dev.ioctl(
1302 &file_object,
1303 current_task,
1304 uapi::UI_ABS_SETUP,
1305 SyscallArg::from(user_setup.ptr() as u64),
1306 );
1307 assert_eq!(r, error!(EINVAL));
1308
1309 let user_dev_setup = map_memory(
1311 current_task,
1312 UserAddress::default(),
1313 std::mem::size_of::<uapi::uinput_setup>() as u64,
1314 );
1315 let dev_setup = uapi::uinput_setup {
1316 id: uapi::input_id { bustype: 3, vendor: 1, product: 2, version: 3 },
1317 ..Default::default()
1318 };
1319 current_task.write_object(UserRef::new(user_dev_setup), &dev_setup).expect("write");
1320 let r = dev.ioctl(
1321 &file_object,
1322 current_task,
1323 uapi::UI_DEV_SETUP,
1324 SyscallArg::from(user_dev_setup.ptr() as u64),
1325 );
1326 assert_eq!(r, error!(EINVAL));
1327
1328 std::mem::drop(dev);
1329 std::mem::drop(file_object);
1330 let _ = join_handle.join();
1331 })
1332 .await;
1333 }
1334}