Merge branch 'endrift/steamosctl-cleanup' into 'master'

steamosctl cleanup

See merge request holo/steamos-manager!24
This commit is contained in:
Vicki Pfau 2024-05-15 01:05:17 +00:00
commit 80c598ce68
6 changed files with 243 additions and 151 deletions

View file

@ -9,9 +9,11 @@ use anyhow::Result;
use clap::{Parser, Subcommand};
use itertools::Itertools;
use std::ops::Deref;
use std::str::FromStr;
use steamos_manager::cec::HdmiCecState;
use steamos_manager::hardware::FanControlState;
use steamos_manager::power::GPUPerformanceLevel;
use steamos_manager::proxy::ManagerProxy;
use steamos_manager::wifi::WifiBackend;
use steamos_manager::wifi::{WifiBackend, WifiDebugMode, WifiPowerManagement};
use zbus::fdo::PropertiesProxy;
use zbus::names::InterfaceName;
use zbus::{zvariant, Connection};
@ -19,98 +21,122 @@ use zbus::{zvariant, Connection};
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Args {
/// Optionally get all properties
#[arg(short, long)]
all_properties: bool,
#[command(subcommand)]
command: Option<Commands>,
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
GetAlsCalibrationGain {},
GetHardwareCurrentlySupported {},
/// Get all properties
GetAllProperties,
/// Get luminance sensor calibration gain
GetAlsCalibrationGain,
/// Get if the hardware is currently supported
GetHardwareCurrentlySupported,
/// Set the fan control state
SetFanControlState {
// Set the fan control state.
// 0 - BIOS, 1 - OS
#[arg(short, long)]
value: u32,
/// Valid options are bios, os
state: FanControlState,
},
GetFanControlState {},
/// Get the fan control state
GetFanControlState,
/// Set the GPU performance level
SetGPUPerformanceLevel {
// Set the gpu performance level
// 0 = Auto, 1 = Low, 2 = High, 3 = Manual, 4 = Profile Peak
#[arg(short, long)]
value: u32,
/// Valid levels are auto, low, high, manual, peak_performance
level: GPUPerformanceLevel,
},
GetGPUPerformanceLevel {},
/// Get the GPU performance level
GetGPUPerformanceLevel,
/// Set the GPU clock value manually. Only works when performance level is set to Manual
SetManualGPUClock {
// Set the GPU clock value manually
// Controls the GPU clock frequency in MHz when GPUPerformanceLevel is set to Manual
#[arg(short, long)]
value: u32,
/// GPU clock frequency in MHz
freq: u32,
},
GetManualGPUClock {},
GetManualGPUClockMax {},
GetManualGPUClockMin {},
/// Get the GPU clock frequency, in MHz. Only works when performance level is set to Manual
GetManualGPUClock,
/// Get the maximum allowed GPU clock frequency for the Manual performance level
GetManualGPUClockMax,
/// Get the minimum allowed GPU clock frequency for the Manual performance level
GetManualGPUClockMin,
/// Set the TDP limit
SetTDPLimit {
// Set the TDP limit
#[arg(short, long)]
value: u32,
/// TDP limit, in W
limit: u32,
},
GetTDPLimit {},
GetTDPLimitMax {},
GetTDPLimitMin {},
/// Get the TDP limit
GetTDPLimit,
GetVersion {},
/// Get the maximum allowed TDP limit
GetTDPLimitMax,
/// Get the minimum allowed TDP limit
GetTDPLimitMin,
/// Get the current API version
GetVersion,
/// Set the wifi backend if possible
SetWifiBackend {
// Set the wifi backend to given string if possible
// Supported values are iwd|wpa_supplicant
#[arg(short, long)]
backend: String,
/// Supported backends are iwd, wpa_supplicant
backend: WifiBackend,
},
GetWifiBackend {},
/// Get the wifi backend
GetWifiBackend,
/// Set wifi debug mode
SetWifiDebugMode {
// Set wifi debug mode to given value
// 1 for on, 0 for off currently
#[arg(short, long)]
mode: u32,
/// Valid modes are on, off
mode: WifiDebugMode,
/// The size of the debug buffer, in bytes
#[arg(default_value_t = 20000)]
buffer: u32,
},
GetWifiDebugMode {},
/// Get wifi debug mode
GetWifiDebugMode,
/// Set the wifi power management state
SetWifiPowerManagementState {
// Set the wifi power management state
// 0 - disabled, 1 - enabled
#[arg(short, long)]
value: u32,
/// Valid modes are enabled, disabled
state: WifiPowerManagement,
},
GetWifiPowerManagementState {},
/// Get the wifi power management state
GetWifiPowerManagementState,
GetHdmiCecState {},
/// Get the state of HDMI-CEC support
GetHdmiCecState,
/// Set the state of HDMI-CEC support
SetHdmiCecState {
// Set the state of HDMI-CEC support
// 0 - disabled, 1 - only controls, 2 - TV waking
#[arg(short, long)]
value: u32,
/// Valid modes are disabled, control-only, control-and-wake
state: HdmiCecState,
},
UpdateBios {},
UpdateDock {},
TrimDevices {},
FactoryReset {},
/// Update the BIOS, if possible
UpdateBios,
/// Update the dock, if possible
UpdateDock,
/// Trim applicable drives
TrimDevices,
/// Factory reset the device
FactoryReset,
}
#[tokio::main]
@ -124,7 +150,9 @@ async fn main() -> Result<()> {
let conn = Connection::session().await?;
let proxy = ManagerProxy::builder(&conn).build().await?;
if args.all_properties {
// Then process arguments
match &args.command {
Commands::GetAllProperties => {
let properties_proxy = PropertiesProxy::new(
&conn,
"com.steampowered.SteamOSManager1",
@ -141,112 +169,120 @@ async fn main() -> Result<()> {
println!("{key}: {val}");
}
}
// Then process arguments
match &args.command {
Some(Commands::GetAlsCalibrationGain {}) => {
Commands::GetAlsCalibrationGain => {
let gain = proxy.als_calibration_gain().await?;
println!("ALS calibration gain: {gain}");
}
Some(Commands::GetHardwareCurrentlySupported {}) => {
Commands::GetHardwareCurrentlySupported => {
let supported = proxy.hardware_currently_supported().await?;
println!("Hardware currently supported: {supported}");
}
Some(Commands::GetVersion {}) => {
Commands::GetVersion => {
let version = proxy.version().await?;
println!("Version: {version}");
}
Some(Commands::SetFanControlState { value }) => {
proxy.set_fan_control_state(*value).await?;
Commands::SetFanControlState { state } => {
proxy.set_fan_control_state(*state as u32).await?;
}
Some(Commands::SetGPUPerformanceLevel { value }) => {
proxy.set_gpu_performance_level(*value).await?;
Commands::GetFanControlState => {
let state = proxy.fan_control_state().await?;
match FanControlState::try_from(state) {
Ok(s) => println!("Fan control state: {}", s.to_string()),
Err(_) => println!("Got unknown value {state} from backend"),
}
Some(Commands::GetGPUPerformanceLevel {}) => {
}
Commands::SetGPUPerformanceLevel { level } => {
proxy.set_gpu_performance_level(*level as u32).await?;
}
Commands::GetGPUPerformanceLevel => {
let level = proxy.gpu_performance_level().await?;
println!("GPU performance level: {level}");
match GPUPerformanceLevel::try_from(level) {
Ok(l) => println!("GPU performance level: {}", l.to_string()),
Err(_) => println!("Got unknown value {level} from backend"),
}
Some(Commands::SetManualGPUClock { value }) => {
proxy.set_manual_gpu_clock(*value).await?;
}
Some(Commands::GetManualGPUClock {}) => {
Commands::SetManualGPUClock { freq } => {
proxy.set_manual_gpu_clock(*freq).await?;
}
Commands::GetManualGPUClock => {
let clock = proxy.manual_gpu_clock().await?;
println!("Manual GPU Clock: {clock}");
}
Some(Commands::GetManualGPUClockMax {}) => {
Commands::GetManualGPUClockMax => {
let value = proxy.manual_gpu_clock_max().await?;
println!("Manual GPU Clock Max: {value}");
}
Some(Commands::GetManualGPUClockMin {}) => {
Commands::GetManualGPUClockMin => {
let value = proxy.manual_gpu_clock_min().await?;
println!("Manual GPU Clock Min: {value}");
}
Some(Commands::SetTDPLimit { value }) => {
proxy.set_tdp_limit(*value).await?;
Commands::SetTDPLimit { limit } => {
proxy.set_tdp_limit(*limit).await?;
}
Some(Commands::GetTDPLimit {}) => {
Commands::GetTDPLimit => {
let limit = proxy.tdp_limit().await?;
println!("TDP limit: {limit}");
}
Some(Commands::GetFanControlState {}) => {
let state = proxy.fan_control_state().await?;
println!("Fan control state: {state}");
}
Some(Commands::GetTDPLimitMax {}) => {
Commands::GetTDPLimitMax => {
let value = proxy.tdp_limit_max().await?;
println!("TDP limit max: {value}");
}
Some(Commands::GetTDPLimitMin {}) => {
Commands::GetTDPLimitMin => {
let value = proxy.tdp_limit_min().await?;
println!("TDP limit min: {value}");
}
Some(Commands::SetWifiBackend { backend }) => match WifiBackend::from_str(backend) {
Ok(b) => {
proxy.set_wifi_backend(b as u32).await?;
}
Err(_) => {
println!("Unknown wifi backend {backend}");
}
Commands::SetWifiBackend { backend } => {
proxy.set_wifi_backend(*backend as u32).await?;
},
Some(Commands::GetWifiBackend {}) => {
Commands::GetWifiBackend => {
let backend = proxy.wifi_backend().await?;
let backend_string = WifiBackend::try_from(backend).unwrap().to_string();
println!("Wifi backend: {backend_string}");
match WifiBackend::try_from(backend) {
Ok(be) => println!("Wifi backend: {}", be.to_string()),
Err(_) => println!("Got unknown value {backend} from backend"),
}
Some(Commands::SetWifiDebugMode { mode }) => {
proxy.set_wifi_debug_mode(*mode, 20000).await?;
}
Some(Commands::GetWifiDebugMode {}) => {
Commands::SetWifiDebugMode { mode, buffer } => {
proxy.set_wifi_debug_mode(*mode as u32, *buffer).await?;
}
Commands::GetWifiDebugMode => {
let mode = proxy.wifi_debug_mode_state().await?;
println!("Wifi debug mode: {mode}");
match WifiDebugMode::try_from(mode) {
Ok(m) => println!("Wifi debug mode: {}", m.to_string()),
Err(_) => println!("Got unknown value {mode} from backend"),
}
Some(Commands::SetWifiPowerManagementState { value }) => {
proxy.set_wifi_power_management_state(*value).await?;
}
Some(Commands::GetWifiPowerManagementState {}) => {
Commands::SetWifiPowerManagementState { state } => {
proxy.set_wifi_power_management_state(*state as u32).await?;
}
Commands::GetWifiPowerManagementState => {
let state = proxy.wifi_power_management_state().await?;
println!("Wifi power management state: {state}");
match WifiPowerManagement::try_from(state) {
Ok(s) => println!("Wifi power management state: {}", s.to_string()),
Err(_) => println!("Got unknown value {state} from backend"),
}
Some(Commands::SetHdmiCecState { value }) => {
proxy.set_hdmi_cec_state(*value).await?;
}
Some(Commands::GetHdmiCecState {}) => {
Commands::SetHdmiCecState { state } => {
proxy.set_hdmi_cec_state(*state as u32).await?;
}
Commands::GetHdmiCecState => {
let state = proxy.hdmi_cec_state().await?;
println!("HDMI-CEC state: {state}");
match HdmiCecState::try_from(state) {
Ok(s) => println!("HDMI-CEC state: {}", s.to_human_readable()),
Err(_) => println!("Got unknown value {state} from backend"),
}
Some(Commands::UpdateBios {}) => {
}
Commands::UpdateBios => {
let _ = proxy.update_bios().await?;
}
Some(Commands::UpdateDock {}) => {
Commands::UpdateDock => {
let _ = proxy.update_dock().await?;
}
Some(Commands::FactoryReset {}) => {
Commands::FactoryReset => {
let _ = proxy.prepare_factory_reset().await?;
}
Some(Commands::TrimDevices {}) => {
Commands::TrimDevices => {
let _ = proxy.trim_devices().await?;
}
None => {}
}
Ok(())

View file

@ -6,8 +6,9 @@
* SPDX-License-Identifier: MIT
*/
use anyhow::Result;
use anyhow::{bail, Error, Result};
use std::fmt;
use std::str::FromStr;
use zbus::Connection;
use crate::systemd::{daemon_reload, EnableState, SystemdUnit};
@ -31,6 +32,18 @@ impl TryFrom<u32> for HdmiCecState {
}
}
impl FromStr for HdmiCecState {
type Err = Error;
fn from_str(input: &str) -> Result<HdmiCecState, Self::Err> {
Ok(match input {
"disable" | "disabled" | "off" => HdmiCecState::Disabled,
"control-only" | "ControlOnly" => HdmiCecState::ControlOnly,
"control-wake" | "control-and-wake" | "ControlAndWake" => HdmiCecState::ControlAndWake,
v => bail!("No enum match for value {v}"),
})
}
}
impl fmt::Display for HdmiCecState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -41,7 +54,17 @@ impl fmt::Display for HdmiCecState {
}
}
pub struct HdmiCecControl<'dbus> {
impl HdmiCecState {
pub fn to_human_readable(&self) -> &'static str {
match self {
HdmiCecState::Disabled => "disabled",
HdmiCecState::ControlOnly => "control-only",
HdmiCecState::ControlAndWake => "control-and-wake",
}
}
}
pub(crate) struct HdmiCecControl<'dbus> {
plasma_rc_unit: SystemdUnit<'dbus>,
wakehook_unit: SystemdUnit<'dbus>,
connection: Connection,

View file

@ -5,7 +5,7 @@
* SPDX-License-Identifier: MIT
*/
use anyhow::{Error, Result};
use anyhow::{bail, Error, Result};
use std::fmt;
use std::str::FromStr;
use tokio::fs;
@ -19,7 +19,7 @@ const BOARD_VENDOR_PATH: &str = "/sys/class/dmi/id/board_vendor";
const BOARD_NAME_PATH: &str = "/sys/class/dmi/id/board_name";
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum HardwareVariant {
pub(crate) enum HardwareVariant {
Unknown,
Jupiter,
Galileo,
@ -27,7 +27,7 @@ pub enum HardwareVariant {
#[derive(PartialEq, Debug, Copy, Clone)]
#[repr(u32)]
pub enum HardwareCurrentlySupported {
pub(crate) enum HardwareCurrentlySupported {
Unsupported = 0,
Supported = 1,
}
@ -85,6 +85,17 @@ impl TryFrom<u32> for FanControlState {
}
}
impl FromStr for FanControlState {
type Err = Error;
fn from_str(input: &str) -> Result<FanControlState, Self::Err> {
Ok(match input.to_lowercase().as_str() {
"bios" => FanControlState::Bios,
"os" => FanControlState::Os,
v => bail!("No enum match for value {v}"),
})
}
}
impl fmt::Display for FanControlState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -94,7 +105,7 @@ impl fmt::Display for FanControlState {
}
}
pub async fn variant() -> Result<HardwareVariant> {
pub(crate) async fn variant() -> Result<HardwareVariant> {
let board_vendor = fs::read_to_string(path(BOARD_VENDOR_PATH)).await?;
if board_vendor.trim_end() != "Valve" {
return Ok(HardwareVariant::Unknown);
@ -104,7 +115,7 @@ pub async fn variant() -> Result<HardwareVariant> {
HardwareVariant::from_str(board_name.trim_end())
}
pub async fn check_support() -> Result<HardwareCurrentlySupported> {
pub(crate) async fn check_support() -> Result<HardwareCurrentlySupported> {
// Run jupiter-check-support note this script does exit 1 for "Support: No" case
// so no need to parse output, etc.
let res = script_exit_code("/usr/bin/jupiter-check-support", &[] as &[String; 0]).await?;
@ -115,7 +126,7 @@ pub async fn check_support() -> Result<HardwareCurrentlySupported> {
})
}
pub struct FanControl {
pub(crate) struct FanControl {
connection: Connection,
}

View file

@ -14,17 +14,17 @@ use tokio::signal::unix::{signal, SignalKind};
use tokio_util::sync::CancellationToken;
use tracing::{info, warn};
mod cec;
mod ds_inhibit;
mod error;
mod hardware;
mod manager;
mod power;
mod process;
mod sls;
mod systemd;
pub mod cec;
pub mod daemon;
pub mod hardware;
pub mod power;
pub mod proxy;
pub mod wifi;

View file

@ -5,7 +5,7 @@
* SPDX-License-Identifier: MIT
*/
use anyhow::{anyhow, bail, ensure, Error, Result};
use anyhow::{bail, ensure, Error, Result};
use std::path::PathBuf;
use std::str::FromStr;
use tokio::fs::{self, File};
@ -52,14 +52,14 @@ impl TryFrom<u32> for GPUPerformanceLevel {
impl FromStr for GPUPerformanceLevel {
type Err = Error;
fn from_str(input: &str) -> Result<GPUPerformanceLevel, Self::Err> {
match input {
"auto" => Ok(GPUPerformanceLevel::Auto),
"low" => Ok(GPUPerformanceLevel::Low),
"high" => Ok(GPUPerformanceLevel::High),
"manual" => Ok(GPUPerformanceLevel::Manual),
"peak_performance" => Ok(GPUPerformanceLevel::ProfilePeak),
v => Err(anyhow!("No enum match for value {v}")),
}
Ok(match input {
"auto" => GPUPerformanceLevel::Auto,
"low" => GPUPerformanceLevel::Low,
"high" => GPUPerformanceLevel::High,
"manual" => GPUPerformanceLevel::Manual,
"peak_performance" => GPUPerformanceLevel::ProfilePeak,
v => bail!("No enum match for value {v}"),
})
}
}
@ -75,7 +75,7 @@ impl ToString for GPUPerformanceLevel {
}
}
pub async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
pub(crate) async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
let base = find_hwmon().await?;
let level = fs::read_to_string(base.join(GPU_PERFORMANCE_LEVEL_SUFFIX))
.await
@ -84,7 +84,7 @@ pub async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
GPUPerformanceLevel::from_str(level.trim())
}
pub async fn set_gpu_performance_level(level: GPUPerformanceLevel) -> Result<()> {
pub(crate) async fn set_gpu_performance_level(level: GPUPerformanceLevel) -> Result<()> {
let level: String = level.to_string();
let base = find_hwmon().await?;
write_synced(base.join(GPU_PERFORMANCE_LEVEL_SUFFIX), level.as_bytes())
@ -92,7 +92,7 @@ pub async fn set_gpu_performance_level(level: GPUPerformanceLevel) -> Result<()>
.inspect_err(|message| error!("Error writing to sysfs file: {message}"))
}
pub async fn set_gpu_clocks(clocks: u32) -> Result<()> {
pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> {
// Set GPU clocks to given value valid between 200 - 1600
// Only used when GPU Performance Level is manual, but write whenever called.
ensure!((200..=1600).contains(&clocks), "Invalid clocks");
@ -125,7 +125,7 @@ pub async fn set_gpu_clocks(clocks: u32) -> Result<()> {
Ok(())
}
pub async fn get_gpu_clocks() -> Result<u32> {
pub(crate) async fn get_gpu_clocks() -> Result<u32> {
let base = find_hwmon().await?;
let clocks_file = File::open(base.join(GPU_CLOCKS_SUFFIX)).await?;
let mut reader = BufReader::new(clocks_file);
@ -170,14 +170,14 @@ async fn find_hwmon() -> Result<PathBuf> {
}
}
pub async fn get_tdp_limit() -> Result<u32> {
pub(crate) async fn get_tdp_limit() -> Result<u32> {
let base = find_hwmon().await?;
let power1cap = fs::read_to_string(base.join(TDP_LIMIT1)).await?;
let power1cap: u32 = power1cap.trim_end().parse()?;
Ok(power1cap / 1000000)
}
pub async fn set_tdp_limit(limit: u32) -> Result<()> {
pub(crate) async fn set_tdp_limit(limit: u32) -> Result<()> {
// Set TDP limit given if within range (3-15)
// Returns false on error or out of range
ensure!((3..=15).contains(&limit), "Invalid limit");
@ -201,7 +201,7 @@ pub async fn set_tdp_limit(limit: u32) -> Result<()> {
}
#[cfg(test)]
pub mod test {
pub(crate) mod test {
use super::*;
use crate::testing;
use anyhow::anyhow;

View file

@ -64,6 +64,17 @@ impl TryFrom<u32> for WifiDebugMode {
}
}
impl FromStr for WifiDebugMode {
type Err = Error;
fn from_str(input: &str) -> Result<WifiDebugMode, Self::Err> {
Ok(match input {
"enable" | "enabled" | "on" | "1" => WifiDebugMode::On,
"disable" | "disabled" | "off" | "0" => WifiDebugMode::Off,
v => bail!("No enum match for value {v}"),
})
}
}
impl fmt::Display for WifiDebugMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -84,6 +95,17 @@ impl TryFrom<u32> for WifiPowerManagement {
}
}
impl FromStr for WifiPowerManagement {
type Err = Error;
fn from_str(input: &str) -> Result<WifiPowerManagement, Self::Err> {
Ok(match input {
"enable" | "enabled" | "on" | "1" => WifiPowerManagement::Enabled,
"disable" | "disabled" | "off" | "0" => WifiPowerManagement::Disabled,
v => bail!("No enum match for value {v}"),
})
}
}
impl fmt::Display for WifiPowerManagement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {