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