95bbc56f97
- P48: Fix AmlSerdeValue::Package pattern (struct variant, not tuple) - P54: Add missing thermal.rs module referenced by P44 ThermalState with zone discovery via ACPI _TMP evaluation
142 lines
3.7 KiB
Diff
142 lines
3.7 KiB
Diff
diff --git a/drivers/acpid/src/thermal.rs b/drivers/acpid/src/thermal.rs
|
|
new file mode 100644
|
|
index 00000000..4614e481
|
|
--- /dev/null
|
|
+++ b/drivers/acpid/src/thermal.rs
|
|
@@ -0,0 +1,135 @@
|
|
+use acpi::aml::namespace::AmlName;
|
|
+use std::str::FromStr;
|
|
+use std::sync::RwLock;
|
|
+
|
|
+use crate::acpi::{AcpiContext, AmlEvalError};
|
|
+use amlserde::AmlSerdeValue;
|
|
+
|
|
+#[derive(Clone, Debug)]
|
|
+pub struct ThermalZone {
|
|
+ pub name: String,
|
|
+ pub temperature_raw: Option<u64>,
|
|
+}
|
|
+
|
|
+impl ThermalZone {
|
|
+ fn from_zone_eval(ctx: &AcpiContext, zone_name: &str) -> Result<Self, ThermalError> {
|
|
+ let aml_prefix = format!("\\_TZ_.{zone_name}");
|
|
+
|
|
+ let mut temp_raw = None;
|
|
+
|
|
+ if let Ok(tmp_name) = AmlName::from_str(&format!("{aml_prefix}._TMP")) {
|
|
+ match ctx.aml_eval(tmp_name, Vec::new()) {
|
|
+ Ok(value) => {
|
|
+ if let AmlSerdeValue::Integer(t) = value {
|
|
+ temp_raw = Some(t as u64);
|
|
+ }
|
|
+ }
|
|
+ Err(e) => {
|
|
+ log::debug!("Thermal zone {zone_name}: _TMP eval failed: {e:?}");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ Ok(Self {
|
|
+ name: zone_name.to_owned(),
|
|
+ temperature_raw: temp_raw,
|
|
+ })
|
|
+ }
|
|
+
|
|
+ pub fn temperature_celsius(&self) -> Option<f64> {
|
|
+ self.temperature_raw.map(|t| (t as f64 - 273.15) / 10.0)
|
|
+ }
|
|
+
|
|
+ pub fn to_text(&self) -> String {
|
|
+ let mut s = String::new();
|
|
+ s.push_str(&format!("name={}\n", self.name));
|
|
+ if let Some(c) = self.temperature_celsius() {
|
|
+ s.push_str(&format!("temperature={:.1}°C\n", c));
|
|
+ } else {
|
|
+ s.push_str("temperature=na\n");
|
|
+ }
|
|
+ s
|
|
+ }
|
|
+}
|
|
+
|
|
+#[derive(Debug)]
|
|
+pub enum ThermalError {
|
|
+ EvalError(AmlEvalError),
|
|
+ NotFound,
|
|
+}
|
|
+
|
|
+impl From<AmlEvalError> for ThermalError {
|
|
+ fn from(value: AmlEvalError) -> Self {
|
|
+ ThermalError::EvalError(value)
|
|
+ }
|
|
+}
|
|
+
|
|
+pub fn discover_thermal_zones(ctx: &AcpiContext) -> Vec<ThermalZone> {
|
|
+ let mut zones = Vec::new();
|
|
+
|
|
+ let zone_names = match ctx.thermal_zone_names() {
|
|
+ Ok(names) => names,
|
|
+ Err(e) => {
|
|
+ log::debug!("Thermal zone discovery failed: {e:?}");
|
|
+ return zones;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ for zone_name in zone_names {
|
|
+ match ThermalZone::from_zone_eval(ctx, &zone_name) {
|
|
+ Ok(zone) => {
|
|
+ if zone.temperature_raw.is_some() {
|
|
+ log::info!(
|
|
+ "Thermal zone discovered: {} = {:.1}°C",
|
|
+ zone.name,
|
|
+ zone.temperature_celsius().unwrap_or(0.0)
|
|
+ );
|
|
+ }
|
|
+ zones.push(zone);
|
|
+ }
|
|
+ Err(e) => {
|
|
+ log::warn!("Thermal zone {zone_name}: discovery failed: {e:?}");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ zones
|
|
+}
|
|
+
|
|
+pub struct ThermalState {
|
|
+ zones: RwLock<Vec<ThermalZone>>,
|
|
+}
|
|
+
|
|
+impl ThermalState {
|
|
+ pub fn new() -> Self {
|
|
+ Self {
|
|
+ zones: RwLock::new(Vec::new()),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn refresh(&self, ctx: &AcpiContext) {
|
|
+ let discovered = discover_thermal_zones(ctx);
|
|
+ if let Ok(mut zones) = self.zones.write() {
|
|
+ *zones = discovered;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn zones(&self) -> Vec<ThermalZone> {
|
|
+ self.zones.read().map(|g| g.clone()).unwrap_or_default()
|
|
+ }
|
|
+
|
|
+ pub fn zone_by_name(&self, name: &str) -> Option<ThermalZone> {
|
|
+ self.zones
|
|
+ .read()
|
|
+ .ok()?
|
|
+ .iter()
|
|
+ .find(|z| z.name == name)
|
|
+ .cloned()
|
|
+ }
|
|
+}
|
|
+
|
|
+impl Default for ThermalState {
|
|
+ fn default() -> Self {
|
|
+ Self::new()
|
|
+ }
|
|
+}
|