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