cadmus_core/device/power/kobo/
mod.rs1use crate::device::Model;
8use crate::device::power::error::PowerError;
9use crate::device::power::manager::PowerManager;
10use std::fs;
11use std::path::{Path, PathBuf};
12use std::sync::Mutex;
13use std::thread;
14use std::time::Duration;
15
16const STATE_EXTENDED_PATH: &str = "/sys/power/state-extended";
17const STATE_PATH: &str = "/sys/power/state";
18const NEOCMD_PATH: &str = "/sys/devices/virtual/input/input1/neocmd";
19
20pub struct KoboPowerManager {
38 model: Model,
39 initial_cpu_states: Mutex<Vec<PathBuf>>,
40}
41
42impl KoboPowerManager {
43 pub fn new(model: Model) -> Self {
45 KoboPowerManager {
46 model,
47 initial_cpu_states: Mutex::new(Vec::new()),
48 }
49 }
50}
51
52impl PowerManager for KoboPowerManager {
53 fn suspend(&self) -> Result<(), PowerError> {
65 tracing::info!("Suspending device to RAM");
66 tracing::debug!(path = %STATE_EXTENDED_PATH, value = "1", "Deactivating touch screen");
67
68 fs::write(STATE_EXTENDED_PATH, "1").map_err(|e| {
69 tracing::error!(error = %e, path = %STATE_EXTENDED_PATH, "Failed to deactivate touch screen");
70
71 PowerError::Io(e)
72 })?;
73
74 tracing::debug!("Sleeping to prevent write errors");
75 thread::sleep(Duration::from_secs(2));
76 tracing::debug!("Synchronizing file system buffers");
77
78 nix::unistd::sync();
79
80 tracing::debug!(path = %STATE_PATH, value = "mem", "Triggering low power state");
81
82 fs::write(STATE_PATH, "mem").map_err(|e| {
83 tracing::error!(error = %e, path = %STATE_PATH, "Failed to write suspend trigger");
84
85 PowerError::Io(e)
86 })?;
87
88 Ok(())
89 }
90
91 fn resume(&self) -> Result<(), PowerError> {
102 tracing::info!("Resuming device");
103 tracing::debug!(path = %STATE_EXTENDED_PATH, value = "0", "Reactivating touch screen");
104
105 fs::write(STATE_EXTENDED_PATH, "0").map_err(|e| {
106 tracing::error!(error = %e, path = %STATE_EXTENDED_PATH, "Failed to reactivate touch screen");
107
108 PowerError::Io(e)
109 })?;
110
111 match self.model {
112 Model::GloHD | Model::AuraH2O => {
113 tracing::debug!(path = %NEOCMD_PATH, value = "a", "Reinitializing touch controller");
114
115 fs::write(NEOCMD_PATH, "a").map_err(|e| {
116 tracing::warn!(error = %e, path = %NEOCMD_PATH, "Failed to write neocmd");
117
118 PowerError::Io(e)
119 })?;
120 }
121 _ => {}
122 }
123
124 Ok(())
125 }
126
127 fn init_cores(&self) -> Result<(), PowerError> {
128 let cpu_dir = Path::new("/sys/devices/system/cpu");
129 let discovered = super::discover_cores(cpu_dir).map_err(|e| {
130 tracing::warn!(error = %e, "Failed to discover CPU cores");
131 e
132 })?;
133
134 let mut modified_cores = Vec::new();
135 let mut first_error: Option<PowerError> = None;
136
137 for (online_path, initial_state) in discovered {
138 if initial_state == "0" {
139 tracing::info!(path = ?online_path, "Enabling offline CPU core");
140 match fs::write(&online_path, "1") {
141 Ok(()) => modified_cores.push(online_path),
142 Err(e) => {
143 tracing::error!(path = ?online_path, error = %e, "Failed to enable CPU core");
144 if first_error.is_none() {
145 first_error = Some(PowerError::Io(e));
146 }
147 }
148 }
149 }
150 }
151
152 let mut states = self
153 .initial_cpu_states
154 .lock()
155 .map_err(|_| PowerError::LockPoisoned)?;
156 *states = modified_cores;
157
158 match first_error {
159 Some(e) => Err(e),
160 None => Ok(()),
161 }
162 }
163
164 fn restore_cores(&self) -> Result<(), PowerError> {
165 let states = self
166 .initial_cpu_states
167 .lock()
168 .map_err(|_| PowerError::LockPoisoned)?;
169
170 let mut first_error: Option<PowerError> = None;
171
172 for path in states.iter() {
173 tracing::info!(path = ?path, "Disabling CPU core on exit");
174 if let Err(e) = fs::write(path, "0") {
175 tracing::error!(path = ?path, error = %e, "Failed to restore CPU core state");
176 if first_error.is_none() {
177 first_error = Some(PowerError::Io(e));
178 }
179 }
180 }
181
182 match first_error {
183 Some(e) => Err(e),
184 None => Ok(()),
185 }
186 }
187}
188
189pub fn create_power_manager(model: Model) -> Result<Box<dyn PowerManager>, PowerError> {
194 Ok(Box::new(KoboPowerManager::new(model)))
195}