hardware: Let fan control be configured and disabled

This commit is contained in:
Vicki Pfau 2024-08-14 19:44:29 -07:00
parent 92235d0f47
commit 7dc0d0969d
4 changed files with 94 additions and 16 deletions

View file

@ -16,3 +16,6 @@ script = "/usr/lib/hwsupport/format-device.sh"
label_flag = "--label" label_flag = "--label"
device_flag = "--device" device_flag = "--device"
no_validate_flag = "--skip-validation" no_validate_flag = "--skip-validation"
[fan_control]
systemd = "jupiter-fan-control.service"

View file

@ -5,14 +5,15 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
use anyhow::{anyhow, bail, Error, Result}; use anyhow::{anyhow, bail, ensure, Error, Result};
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use tokio::fs; use tokio::fs;
use zbus::Connection; use zbus::Connection;
use crate::path; use crate::path;
use crate::process::script_exit_code; use crate::platform::{platform_config, ServiceConfig};
use crate::process::{run_script, script_exit_code};
use crate::systemd::SystemdUnit; use crate::systemd::SystemdUnit;
const BOARD_VENDOR_PATH: &str = "/sys/class/dmi/id/board_vendor"; const BOARD_VENDOR_PATH: &str = "/sys/class/dmi/id/board_vendor";
@ -148,22 +149,57 @@ impl FanControl {
} }
pub async fn get_state(&self) -> Result<FanControlState> { pub async fn get_state(&self) -> Result<FanControlState> {
let jupiter_fan_control = let config = platform_config().await?;
SystemdUnit::new(self.connection.clone(), "jupiter-fan-control.service").await?; match config
let active = jupiter_fan_control.active().await?; .as_ref()
Ok(match active { .and_then(|config| config.fan_control.as_ref())
true => FanControlState::Os, {
false => FanControlState::Bios, Some(ServiceConfig::Systemd(service)) => {
}) let jupiter_fan_control =
SystemdUnit::new(self.connection.clone(), service).await?;
let active = jupiter_fan_control.active().await?;
Ok(match active {
true => FanControlState::Os,
false => FanControlState::Bios,
})
}
Some(ServiceConfig::Script {
start: _,
stop: _,
status,
}) => {
let res = script_exit_code(&status.script, &status.script_args).await?;
ensure!(res >= 0, "Script exited abnormally");
FanControlState::try_from(res as u32)
}
None => bail!("Fan control not configured"),
}
} }
pub async fn set_state(&self, state: FanControlState) -> Result<()> { pub async fn set_state(&self, state: FanControlState) -> Result<()> {
// Run what steamos-polkit-helpers/jupiter-fan-control does // Run what steamos-polkit-helpers/jupiter-fan-control does
let jupiter_fan_control = let config = platform_config().await?;
SystemdUnit::new(self.connection.clone(), "jupiter-fan-control.service").await?; match config
match state { .as_ref()
FanControlState::Os => jupiter_fan_control.start().await, .and_then(|config| config.fan_control.as_ref())
FanControlState::Bios => jupiter_fan_control.stop().await, {
Some(ServiceConfig::Systemd(service)) => {
let jupiter_fan_control =
SystemdUnit::new(self.connection.clone(), service).await?;
match state {
FanControlState::Os => jupiter_fan_control.start().await,
FanControlState::Bios => jupiter_fan_control.stop().await,
}
}
Some(ServiceConfig::Script {
start,
stop,
status: _,
}) => match state {
FanControlState::Os => run_script(&start.script, &start.script_args).await,
FanControlState::Bios => run_script(&stop.script, &stop.script_args).await,
},
None => bail!("Fan control not configured"),
} }
} }
} }
@ -172,6 +208,7 @@ impl FanControl {
pub mod test { pub mod test {
use super::*; use super::*;
use crate::error::to_zbus_fdo_error; use crate::error::to_zbus_fdo_error;
use crate::platform::{PlatformConfig, ServiceConfig};
use crate::{enum_roundtrip, testing}; use crate::{enum_roundtrip, testing};
use std::time::Duration; use std::time::Duration;
use tokio::fs::{create_dir_all, write}; use tokio::fs::{create_dir_all, write};
@ -322,6 +359,16 @@ pub mod test {
sleep(Duration::from_millis(10)).await; sleep(Duration::from_millis(10)).await;
h.test.platform_config.replace(Some(PlatformConfig {
factory_reset: None,
update_bios: None,
update_dock: None,
storage: None,
fan_control: Some(ServiceConfig::Systemd(String::from(
"jupiter-fan-control.service",
))),
}));
let fan_control = FanControl::new(connection); let fan_control = FanControl::new(connection);
assert_eq!( assert_eq!(
fan_control.get_state().await.unwrap(), fan_control.get_state().await.unwrap(),

View file

@ -582,7 +582,12 @@ pub(crate) async fn create_interfaces(
object_server.at(MANAGER_PATH, factory_reset).await?; object_server.at(MANAGER_PATH, factory_reset).await?;
} }
object_server.at(MANAGER_PATH, fan_control).await?; if config
.as_ref()
.is_some_and(|config| config.fan_control.is_some())
{
object_server.at(MANAGER_PATH, fan_control).await?;
}
if !get_available_gpu_performance_levels() if !get_available_gpu_performance_levels()
.await .await
@ -645,7 +650,7 @@ mod test {
use crate::daemon::user::UserContext; use crate::daemon::user::UserContext;
use crate::hardware::test::fake_model; use crate::hardware::test::fake_model;
use crate::hardware::HardwareVariant; use crate::hardware::HardwareVariant;
use crate::platform::{PlatformConfig, ScriptConfig, StorageConfig}; use crate::platform::{PlatformConfig, ScriptConfig, ServiceConfig, StorageConfig};
use crate::{power, testing}; use crate::{power, testing};
use std::time::Duration; use std::time::Duration;
@ -664,6 +669,9 @@ mod test {
update_bios: Some(ScriptConfig::default()), update_bios: Some(ScriptConfig::default()),
update_dock: Some(ScriptConfig::default()), update_dock: Some(ScriptConfig::default()),
storage: Some(StorageConfig::default()), storage: Some(StorageConfig::default()),
fan_control: Some(ServiceConfig::Systemd(String::from(
"jupiter-fan-control.service",
))),
}) })
} }
@ -767,6 +775,13 @@ mod test {
.unwrap()); .unwrap());
} }
#[tokio::test]
async fn interface_missing_fan_control1() {
let test = start(None).await.expect("start");
assert!(test_interface_missing::<FanControl1>(&test.connection).await);
}
#[tokio::test] #[tokio::test]
async fn interface_matches_gpu_performance_level1() { async fn interface_matches_gpu_performance_level1() {
let test = start(all_config()).await.expect("start"); let test = start(all_config()).await.expect("start");

View file

@ -25,6 +25,7 @@ pub(crate) struct PlatformConfig {
pub update_bios: Option<ScriptConfig>, pub update_bios: Option<ScriptConfig>,
pub update_dock: Option<ScriptConfig>, pub update_dock: Option<ScriptConfig>,
pub storage: Option<StorageConfig>, pub storage: Option<StorageConfig>,
pub fan_control: Option<ServiceConfig>,
} }
#[derive(Clone, Default, Deserialize, Debug)] #[derive(Clone, Default, Deserialize, Debug)]
@ -34,6 +35,18 @@ pub(crate) struct ScriptConfig {
pub script_args: Vec<String>, pub script_args: Vec<String>,
} }
#[derive(Clone, Deserialize, Debug)]
pub(crate) enum ServiceConfig {
#[serde(rename = "systemd")]
Systemd(String),
#[serde(rename = "script")]
Script {
start: ScriptConfig,
stop: ScriptConfig,
status: ScriptConfig,
},
}
#[derive(Clone, Default, Deserialize, Debug)] #[derive(Clone, Default, Deserialize, Debug)]
pub(crate) struct StorageConfig { pub(crate) struct StorageConfig {
pub trim_devices: ScriptConfig, pub trim_devices: ScriptConfig,