Skip to main content

starnix_modules_touch_power_policy/
lib.rs

1// Copyright 2024 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 starnix_core::device::DeviceOps;
6use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
7use starnix_core::task::{CurrentTask, Kernel, LockupDetectorReceiver};
8use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
9use starnix_core::vfs::{
10    CloseFreeSafe, FileObject, FileOps, NamespaceNode, fileops_impl_nonseekable,
11    fileops_impl_noop_sync,
12};
13use starnix_logging::{log_error, log_info};
14use starnix_sync::{LockDepMutex, TouchPowerPolicyEnabledLock};
15use starnix_uapi::device_id::DeviceId;
16use starnix_uapi::error;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::open_flags::OpenFlags;
19use std::sync::Arc;
20use std::sync::mpsc::Sender;
21use zerocopy::IntoBytes;
22
23#[derive(Clone)]
24pub struct TouchPowerPolicyDevice {
25    touch_power_file: Arc<TouchPowerPolicyFile>,
26}
27
28impl TouchPowerPolicyDevice {
29    pub fn new(touch_standby_sender: Sender<bool>) -> Self {
30        TouchPowerPolicyDevice { touch_power_file: TouchPowerPolicyFile::new(touch_standby_sender) }
31    }
32
33    pub fn register(self, kernel: &Kernel) {
34        let registry = &kernel.device_registry;
35        registry
36            .add_dyn_device(kernel, "touch_standby".into(), registry.objects.starnix_class(), self)
37            .expect("can add touch_standby device");
38    }
39
40    pub fn start_relay(
41        &self,
42        kernel: &Kernel,
43        touch_standby_receiver: LockupDetectorReceiver<bool>,
44    ) {
45        let slf = self.clone();
46        let closure = move |_current_task: &CurrentTask| {
47            let mut prev_enabled = true;
48            while let Ok(touch_enabled) = touch_standby_receiver.recv() {
49                if touch_enabled != prev_enabled {
50                    slf.notify_standby_state_changed(touch_enabled);
51                }
52                prev_enabled = touch_enabled;
53            }
54            log_error!("touch_standby relay was terminated unexpectedly.");
55        };
56        let req = SpawnRequestBuilder::new()
57            .with_debug_name("touch-power-policy-relay")
58            .with_sync_closure(closure)
59            .build();
60        kernel.kthreads.spawner().spawn_from_request(req);
61    }
62
63    fn notify_standby_state_changed(&self, touch_enabled: bool) {
64        // TODO(b/341142285): notify input pipeline that touch_standby state has changed
65        log_info!("touch enabled: {:?}", touch_enabled);
66    }
67}
68
69impl DeviceOps for TouchPowerPolicyDevice {
70    fn open(
71        &self,
72        _current_task: &CurrentTask,
73        _devt: DeviceId,
74        _node: &NamespaceNode,
75        _flags: OpenFlags,
76    ) -> Result<Box<dyn FileOps>, Errno> {
77        let touch_policy_file = self.touch_power_file.clone();
78        Ok(Box::new(touch_policy_file))
79    }
80}
81
82pub struct TouchPowerPolicyFile {
83    // When false, Input Pipeline suspends processing of all touch events.
84    touch_enabled: LockDepMutex<bool, TouchPowerPolicyEnabledLock>,
85    // Sender used to send changes to `touch_standby` to the device relay
86    touch_standby_sender: Sender<bool>,
87}
88
89impl TouchPowerPolicyFile {
90    pub fn new(touch_standby_sender: Sender<bool>) -> Arc<Self> {
91        Arc::new(TouchPowerPolicyFile { touch_enabled: true.into(), touch_standby_sender })
92    }
93}
94
95/// `TouchPowerPolicyFile` doesn't implement the `close` method.
96impl CloseFreeSafe for TouchPowerPolicyFile {}
97impl FileOps for TouchPowerPolicyFile {
98    fileops_impl_nonseekable!();
99    fileops_impl_noop_sync!();
100
101    fn read(
102        &self,
103        _file: &FileObject,
104        _current_task: &CurrentTask,
105        offset: usize,
106        data: &mut dyn OutputBuffer,
107    ) -> Result<usize, Errno> {
108        debug_assert!(offset == 0);
109        let touch_enabled = self.touch_enabled.lock().to_owned();
110        data.write_all(touch_enabled.as_bytes())
111    }
112
113    fn write(
114        &self,
115        _file: &FileObject,
116        _current_task: &CurrentTask,
117        _offset: usize,
118        data: &mut dyn InputBuffer,
119    ) -> Result<usize, Errno> {
120        let content = data.read_all()?;
121        let sys_touch_standby = match &*content {
122            b"0" | b"0\n" => false,
123            b"1" | b"1\n" => true,
124            _ => {
125                log_error!("Invalid touch_standby value - must be 0 or 1");
126                return error!(EINVAL);
127            }
128        };
129        *self.touch_enabled.lock() = sys_touch_standby;
130        if let Err(e) = self.touch_standby_sender.send(sys_touch_standby) {
131            log_error!("unable to send recent touch_standby state to device relay: {:?}", e);
132        }
133        Ok(content.len())
134    }
135}