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, ThermalChargeLevelLock};
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>(&mut self, kernel: &Kernel, device_type: String, ops: T) -> Device {
114 let device_registry = &kernel.device_registry;
115 let device_class = device_registry.objects.virtual_thermal_class();
116
117 let cooling_device =
118 Arc::new(CoolingDevice::<T> { device_id: self.get_next_id(), device_type, ops });
119
120 device_registry.add_numberless_device(
121 cooling_device.get_device_name().as_str().into(),
122 device_class,
123 |device, dir| cooling_device.build_device_dir(device, dir),
124 )
125 }
126}
127
128struct CpuCoolingOps {
129 domain_controller: Arc<fcpu::DomainControllerSynchronousProxy>,
130 domain_id: u64,
131 available_frequencies_hz: Vec<u64>,
132}
133
134impl CoolingOps for CpuCoolingOps {
135 fn get_max_state(&self) -> u32 {
136 (self.available_frequencies_hz.len() - 1) as u32
137 }
138 fn get_state(&self) -> Result<u32, Errno> {
139 let max_frequency_index = self
140 .domain_controller
141 .get_max_frequency(self.domain_id, MonotonicInstant::INFINITE)
142 .map_err(|e| errno!(EIO, anyhow!("Failed to send get_max_frequency call: {:?}", e)))?
143 .map_err(|e| errno!(EIO, anyhow!("Failed response from get_max_frequency: {:?}", e)))?;
144 Ok(max_frequency_index as u32)
145 }
146 fn set_state(&self, state: u32) -> Result<(), Errno> {
147 if state == 0 {
148 self.domain_controller
149 .clear_max_frequency(self.domain_id, MonotonicInstant::INFINITE)
150 .map_err(|e| {
151 errno!(EIO, anyhow!("Failed to send clear_max_frequency call: {:?}", e))
152 })?
153 .map_err(|e| {
154 errno!(EIO, anyhow!("Failed response from clear_max_frequency: {:?}", e))
155 })
156 } else {
157 self.domain_controller
158 .set_max_frequency(self.domain_id, state.into(), MonotonicInstant::INFINITE)
159 .map_err(|e| {
160 errno!(EIO, anyhow!("Failed to send set_max_frequency call: {:?}", e))
161 })?
162 .map_err(|e| {
163 errno!(EIO, anyhow!("Failed response from set_max_frequency: {:?}", e))
164 })
165 }
166 }
167}
168
169fn register_cpu_domains(
170 kernel: &Kernel,
171 registrar: &mut CoolingDeviceRegistrar,
172) -> Result<(), Error> {
173 let domain_controller = Arc::new(
174 fuchsia_component::client::connect_to_protocol_sync::<fcpu::DomainControllerMarker>()
175 .map_err(|error| anyhow!("Failed to connect to DomainController: {:?}", error))?,
176 );
177 let domains = domain_controller
178 .list_domains(MonotonicInstant::INFINITE)
179 .map_err(|e| anyhow!("list_domains failed: {}", e))?;
180
181 for domain in domains {
183 let device_type = domain.name.expect("name not provided");
184 let ops = CpuCoolingOps {
185 domain_controller: domain_controller.clone(),
186 domain_id: domain.id.expect("id not provided"),
187 available_frequencies_hz: domain
188 .available_frequencies_hz
189 .expect("available_frequencies_hz not provided"),
190 };
191 registrar.register(kernel, device_type, ops);
192 }
193 Ok(())
194}
195
196pub fn cooling_device_init(kernel: &Kernel, devices: Vec<String>) -> Result<(), Error> {
203 let mut registrar = CoolingDeviceRegistrar::new();
204 for device_spec in devices.into_iter() {
205 let (device_type, device_param) = device_spec
206 .split_once('=')
207 .map_or_else(|| (device_spec.as_str(), None), |(t, p)| (t, Some(p)));
208 match device_type {
209 "fcc" => {
210 if let Err(e) = register_fcc_device(
212 kernel,
213 &mut registrar,
214 device_param
215 .ok_or_else(|| format_err!("Missing parameter for 'fcc' cooling device"))?,
216 ) {
217 log_error!("Failed to register 'fcc' cooling device: {e:?}");
218 }
219 }
220 "cpu" => {
221 if let Err(e) = register_cpu_domains(kernel, &mut registrar) {
223 log_error!("Failed to register 'cpu' cooling device: {e:?}");
224 }
225 }
226 t => {
227 return Err(format_err!("Unknown cooling device: {t:?}"));
228 }
229 };
230 }
231
232 Ok(())
233}
234
235struct FccCoolingOps {
236 proxy: fbattery::ChargerSynchronousProxy,
237 max_charge_level: u32,
238 charge_level: LockDepMutex<u32, ThermalChargeLevelLock>,
239}
240
241impl FccCoolingOps {
242 fn new(
243 proxy: fbattery::ChargerSynchronousProxy,
244 max_charge_level: u32,
245 charge_level: u32,
246 ) -> Self {
247 Self { proxy, max_charge_level, charge_level: charge_level.into() }
248 }
249}
250
251impl CoolingOps for FccCoolingOps {
252 fn get_max_state(&self) -> u32 {
253 self.max_charge_level
254 }
255
256 fn get_state(&self) -> Result<u32, Errno> {
257 let locked_charge_level = self.charge_level.lock();
258 Ok(*locked_charge_level)
259 }
260
261 fn set_state(&self, state: u32) -> Result<(), Errno> {
262 let mut locked_charge_level = self.charge_level.lock();
263
264 let charge_level = if state > self.max_charge_level {
268 log_warn!(
269 "FCC charge_level of {} exceeds {}; setting to 0",
270 state,
271 self.max_charge_level
272 );
273 0
274 } else {
275 state
276 };
277
278 if charge_level == self.max_charge_level {
281 self.proxy
282 .enable(false, zx::MonotonicInstant::INFINITE)
283 .map_err(|e| errno!(EIO, e))?
284 .map_err(|e| errno!(EIO, e))?;
285 } else if *locked_charge_level == self.max_charge_level {
286 self.proxy
287 .enable(true, zx::MonotonicInstant::INFINITE)
288 .map_err(|e| errno!(EIO, e))?
289 .map_err(|e| errno!(EIO, e))?;
290 }
291
292 *locked_charge_level = charge_level;
293 Ok(())
294 }
295}
296
297fn register_fcc_device(
298 kernel: &Kernel,
299 registrar: &mut CoolingDeviceRegistrar,
300 param: &str,
301) -> Result<(), Error> {
302 let proxy = connect_to_battery_charger().context("Failed to connect to battery Charger")?;
303 let max_charge_level: u32 = param.parse().context("Invalid max_charge_level")?;
304 let ops = FccCoolingOps::new(proxy, max_charge_level, 0);
305
306 registrar.register(kernel, "fcc".to_string(), ops);
307 Ok(())
308}
309
310fn connect_to_battery_charger() -> Result<fbattery::ChargerSynchronousProxy, Error> {
311 let mut dir = std::fs::read_dir(BATTERY_CHARGER_SERVICE_DIRECTORY)
315 .context("Failed to read ChargerService directory")?;
316 let entry = dir
317 .next()
318 .ok_or_else(|| anyhow::format_err!("Missing ChargerService instance"))?
319 .context("Unable to read ChargerService instance")?;
320 let path = entry
321 .path()
322 .join("device")
323 .into_os_string()
324 .into_string()
325 .map_err(|_| anyhow::format_err!("Failed to get device path"))?;
326
327 let (client_end, server_end) = zx::Channel::create();
328 fdio::service_connect(&path, server_end)?;
329 Ok(fbattery::ChargerSynchronousProxy::from_channel(client_end))
330}