1use anyhow::{Context, Error, anyhow, format_err};
6use fidl::endpoints::SynchronousProxy;
7use fidl_fuchsia_power_battery as fbattery;
8use fidl_fuchsia_power_cpu as fcpu;
9use starnix_core::device::kobject::Device;
10use starnix_core::fs::sysfs::build_device_directory;
11use starnix_core::task::{CurrentTask, Kernel};
12use starnix_core::vfs::FsNodeOps;
13use starnix_core::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
14use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
15use starnix_logging::{log_error, log_warn};
16use starnix_sync::{LockDepMutex, Locked, ThermalChargeLevelLock, Unlocked};
17use starnix_uapi::errors::{Errno, errno};
18use starnix_uapi::file_mode::mode;
19use std::borrow::Cow;
20use std::sync::Arc;
21use zx::MonotonicInstant;
22
23const BATTERY_CHARGER_SERVICE_DIRECTORY: &str = "/svc/fuchsia.power.battery.ChargerService";
24
25trait CoolingOps: Send + Sync + 'static {
26 fn get_max_state(&self) -> u32;
27 fn get_state(&self) -> Result<u32, Errno>;
28 fn set_state(&self, state: u32) -> Result<(), Errno>;
29}
30
31impl<T: CoolingOps> CoolingOps for Arc<T> {
32 fn get_max_state(&self) -> u32 {
33 self.as_ref().get_max_state()
34 }
35
36 fn get_state(&self) -> Result<u32, Errno> {
37 self.as_ref().get_state()
38 }
39
40 fn set_state(&self, state: u32) -> Result<(), Errno> {
41 self.as_ref().set_state(state)
42 }
43}
44
45struct CoolingDevice<T: CoolingOps> {
46 device_id: u32,
47 device_type: String,
48 ops: T,
49}
50
51impl<T: CoolingOps> CoolingDevice<T> {
52 fn get_device_name(&self) -> String {
53 format!("cooling_device{}", self.device_id)
54 }
55
56 fn build_device_dir(self: Arc<Self>, device: &Device, dir: &SimpleDirectoryMutator) {
57 build_device_directory(device, dir);
58 dir.entry(
59 "max_state",
60 BytesFile::new_node(format!("{}\n", self.ops.get_max_state()).into_bytes()),
61 mode!(IFREG, 0o444),
62 );
63 dir.entry(
64 "type",
65 BytesFile::new_node(format!("{}\n", self.device_type).into_bytes()),
66 mode!(IFREG, 0o444),
67 );
68 dir.entry("cur_state", CurStateFile::new_node(self), mode!(IFREG, 0o644));
69 }
70}
71
72struct CurStateFile<T: CoolingOps> {
74 cooling_device: Arc<CoolingDevice<T>>,
75}
76
77impl<T: CoolingOps> CurStateFile<T> {
78 fn new_node(cooling_device: Arc<CoolingDevice<T>>) -> impl FsNodeOps {
79 BytesFile::new_node(Self { cooling_device })
80 }
81}
82
83impl<T: CoolingOps> BytesFileOps for CurStateFile<T> {
84 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
85 let state = self.cooling_device.ops.get_state()?;
86 Ok(format!("{}\n", state).into_bytes().into())
87 }
88
89 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
90 let input = str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
91 let input_num = input.trim().parse().map_err(|_| errno!(EINVAL))?;
92 self.cooling_device.ops.set_state(input_num)
93 }
94}
95
96struct CoolingDeviceRegistrar {
98 next_id: u32,
99}
100
101impl CoolingDeviceRegistrar {
102 fn new() -> Self {
103 Self { next_id: 0 }
104 }
105
106 fn get_next_id(&mut self) -> u32 {
107 let id = self.next_id;
108 self.next_id += 1;
109 id
110 }
111
112 fn register<T: CoolingOps>(
114 &mut self,
115 locked: &mut Locked<Unlocked>,
116 kernel: &Kernel,
117 device_type: String,
118 ops: T,
119 ) -> Device {
120 let device_registry = &kernel.device_registry;
121 let device_class = device_registry.objects.virtual_thermal_class();
122
123 let cooling_device =
124 Arc::new(CoolingDevice::<T> { device_id: self.get_next_id(), device_type, ops });
125
126 device_registry.add_numberless_device(
127 locked,
128 cooling_device.get_device_name().as_str().into(),
129 device_class,
130 |device, dir| cooling_device.build_device_dir(device, dir),
131 )
132 }
133}
134
135struct CpuCoolingOps {
136 domain_controller: Arc<fcpu::DomainControllerSynchronousProxy>,
137 domain_id: u64,
138 available_frequencies_hz: Vec<u64>,
139}
140
141impl CoolingOps for CpuCoolingOps {
142 fn get_max_state(&self) -> u32 {
143 (self.available_frequencies_hz.len() - 1) as u32
144 }
145 fn get_state(&self) -> Result<u32, Errno> {
146 let max_frequency_index = self
147 .domain_controller
148 .get_max_frequency(self.domain_id, MonotonicInstant::INFINITE)
149 .map_err(|e| errno!(EIO, anyhow!("Failed to send get_max_frequency call: {:?}", e)))?
150 .map_err(|e| errno!(EIO, anyhow!("Failed response from get_max_frequency: {:?}", e)))?;
151 Ok(max_frequency_index as u32)
152 }
153 fn set_state(&self, state: u32) -> Result<(), Errno> {
154 if state == 0 {
155 self.domain_controller
156 .clear_max_frequency(self.domain_id, MonotonicInstant::INFINITE)
157 .map_err(|e| {
158 errno!(EIO, anyhow!("Failed to send clear_max_frequency call: {:?}", e))
159 })?
160 .map_err(|e| {
161 errno!(EIO, anyhow!("Failed response from clear_max_frequency: {:?}", e))
162 })
163 } else {
164 self.domain_controller
165 .set_max_frequency(self.domain_id, state.into(), MonotonicInstant::INFINITE)
166 .map_err(|e| {
167 errno!(EIO, anyhow!("Failed to send set_max_frequency call: {:?}", e))
168 })?
169 .map_err(|e| {
170 errno!(EIO, anyhow!("Failed response from set_max_frequency: {:?}", e))
171 })
172 }
173 }
174}
175
176fn register_cpu_domains(
177 locked: &mut Locked<Unlocked>,
178 kernel: &Kernel,
179 registrar: &mut CoolingDeviceRegistrar,
180) -> Result<(), Error> {
181 let domain_controller = Arc::new(
182 fuchsia_component::client::connect_to_protocol_sync::<fcpu::DomainControllerMarker>()
183 .map_err(|error| anyhow!("Failed to connect to DomainController: {:?}", error))?,
184 );
185 let domains = domain_controller
186 .list_domains(MonotonicInstant::INFINITE)
187 .map_err(|e| anyhow!("list_domains failed: {}", e))?;
188
189 for domain in domains {
191 let device_type = domain.name.expect("name not provided");
192 let ops = CpuCoolingOps {
193 domain_controller: domain_controller.clone(),
194 domain_id: domain.id.expect("id not provided"),
195 available_frequencies_hz: domain
196 .available_frequencies_hz
197 .expect("available_frequencies_hz not provided"),
198 };
199 registrar.register(locked, kernel, device_type, ops);
200 }
201 Ok(())
202}
203
204pub fn cooling_device_init(
211 locked: &mut Locked<Unlocked>,
212 kernel: &Kernel,
213 devices: Vec<String>,
214) -> Result<(), Error> {
215 let mut registrar = CoolingDeviceRegistrar::new();
216 for device_spec in devices.into_iter() {
217 let (device_type, device_param) = device_spec
218 .split_once('=')
219 .map_or_else(|| (device_spec.as_str(), None), |(t, p)| (t, Some(p)));
220 match device_type {
221 "fcc" => {
222 if let Err(e) = register_fcc_device(
224 locked,
225 kernel,
226 &mut registrar,
227 device_param
228 .ok_or_else(|| format_err!("Missing parameter for 'fcc' cooling device"))?,
229 ) {
230 log_error!("Failed to register 'fcc' cooling device: {e:?}");
231 }
232 }
233 "cpu" => register_cpu_domains(locked, kernel, &mut registrar)?,
234 t => {
235 return Err(format_err!("Unknown cooling device: {t:?}"));
236 }
237 };
238 }
239
240 Ok(())
241}
242
243struct FccCoolingOps {
244 proxy: fbattery::ChargerSynchronousProxy,
245 max_charge_level: u32,
246 charge_level: LockDepMutex<u32, ThermalChargeLevelLock>,
247}
248
249impl FccCoolingOps {
250 fn new(
251 proxy: fbattery::ChargerSynchronousProxy,
252 max_charge_level: u32,
253 charge_level: u32,
254 ) -> Self {
255 Self { proxy, max_charge_level, charge_level: charge_level.into() }
256 }
257}
258
259impl CoolingOps for FccCoolingOps {
260 fn get_max_state(&self) -> u32 {
261 self.max_charge_level
262 }
263
264 fn get_state(&self) -> Result<u32, Errno> {
265 let locked_charge_level = self.charge_level.lock();
266 Ok(*locked_charge_level)
267 }
268
269 fn set_state(&self, state: u32) -> Result<(), Errno> {
270 let mut locked_charge_level = self.charge_level.lock();
271
272 let charge_level = if state > self.max_charge_level {
276 log_warn!(
277 "FCC charge_level of {} exceeds {}; setting to 0",
278 state,
279 self.max_charge_level
280 );
281 0
282 } else {
283 state
284 };
285
286 if charge_level == self.max_charge_level {
289 self.proxy
290 .enable(false, zx::MonotonicInstant::INFINITE)
291 .map_err(|e| errno!(EIO, e))?
292 .map_err(|e| errno!(EIO, e))?;
293 } else if *locked_charge_level == self.max_charge_level {
294 self.proxy
295 .enable(true, zx::MonotonicInstant::INFINITE)
296 .map_err(|e| errno!(EIO, e))?
297 .map_err(|e| errno!(EIO, e))?;
298 }
299
300 *locked_charge_level = charge_level;
301 Ok(())
302 }
303}
304
305fn register_fcc_device(
306 locked: &mut Locked<Unlocked>,
307 kernel: &Kernel,
308 registrar: &mut CoolingDeviceRegistrar,
309 param: &str,
310) -> Result<(), Error> {
311 let proxy = connect_to_battery_charger().context("Failed to connect to battery Charger")?;
312 let max_charge_level: u32 = param.parse().context("Invalid max_charge_level")?;
313 let ops = FccCoolingOps::new(proxy, max_charge_level, 0);
314
315 registrar.register(locked, kernel, "fcc".to_string(), ops);
316 Ok(())
317}
318
319fn connect_to_battery_charger() -> Result<fbattery::ChargerSynchronousProxy, Error> {
320 let mut dir = std::fs::read_dir(BATTERY_CHARGER_SERVICE_DIRECTORY)
324 .context("Failed to read ChargerService directory")?;
325 let entry = dir
326 .next()
327 .ok_or_else(|| anyhow::format_err!("Missing ChargerService instance"))?
328 .context("Unable to read ChargerService instance")?;
329 let path = entry
330 .path()
331 .join("device")
332 .into_os_string()
333 .into_string()
334 .map_err(|_| anyhow::format_err!("Failed to get device path"))?;
335
336 let (client_end, server_end) = zx::Channel::create();
337 fdio::service_connect(&path, server_end)?;
338 Ok(fbattery::ChargerSynchronousProxy::from_channel(client_end))
339}