diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 3be74da..d10af58 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -8,4 +8,4 @@ description = "Power toolbox for Linux devices" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sysfuss = { version = "0.3", path = "../../../sysfs-nav" } +sysfuss = { version = "0.4", path = "../../../sysfs-nav" } diff --git a/crates/core/src/primitives/boolean_number.rs b/crates/core/src/primitives/boolean_number.rs new file mode 100644 index 0000000..b84465c --- /dev/null +++ b/crates/core/src/primitives/boolean_number.rs @@ -0,0 +1,37 @@ +/// Boolean value represented as the number 1 or 0 +#[derive(PartialEq, Eq, Debug)] +pub struct BoolNum(pub bool); + +#[derive(Copy, Clone, Debug)] +pub struct ParseBoolNumError; + +impl core::fmt::Display for ParseBoolNumError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "provided string was not `1` or `0`") + } +} + +impl std::error::Error for ParseBoolNumError {} + +impl core::str::FromStr for BoolNum { + type Err = ParseBoolNumError; + fn from_str(s: &str) -> Result { + match s.chars().next() { + Some('0') => Ok(Self(false)), + Some('1') => Ok(Self(true)), + Some(_) => Err(ParseBoolNumError), + None => Err(ParseBoolNumError), + } + } +} + +impl core::fmt::Display for BoolNum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let c = match self.0 { + true => '1', + false => '0', + }; + write!(f, "{}", c) + } +} + diff --git a/crates/core/src/primitives/mod.rs b/crates/core/src/primitives/mod.rs index 39f69cf..6ed6da5 100644 --- a/crates/core/src/primitives/mod.rs +++ b/crates/core/src/primitives/mod.rs @@ -1,7 +1,13 @@ //! Primitive types for power control values +mod boolean_number; +pub use boolean_number::BoolNum; + mod range; -pub use range::{Range, RangeList, RangeListParseErr, RangeListItem}; +pub use range::{Range, RangeList, RangeListParseErr, RangeListItem, RangeListIter}; + +mod space_separated_list; +pub use space_separated_list::SpacedList; mod value; pub use value::Value; diff --git a/crates/core/src/primitives/range.rs b/crates/core/src/primitives/range.rs index e31d582..f7e943a 100644 --- a/crates/core/src/primitives/range.rs +++ b/crates/core/src/primitives/range.rs @@ -37,6 +37,70 @@ impl From for RangeListParseErr { } } +impl RangeList { + /// Convert range list into iterable and specificy assumption about range inclusiveness + /// (true -> ranges will be from min up to and including max; false -> from min up to max) + pub fn into_iter(self, inclusive: bool) -> RangeListIter { + RangeListIter { + range_list: self, + index: 0, + inclusive, + inner: None, + } + } +} + +/// Iterator implementation for RangeList +pub struct RangeListIter { + range_list: RangeList, + index: usize, + inclusive: bool, + inner: Option>>, +} + +impl core::iter::IntoIterator for RangeList { + type Item = usize; + type IntoIter = RangeListIter; + + fn into_iter(self) -> Self::IntoIter { + RangeListIter { + range_list: self, + index: 0, + inclusive: true, + inner: None, + } + } +} + +impl core::iter::Iterator for RangeListIter { + type Item = usize; + + fn next(&mut self) -> Option { + if let Some(mut inner) = self.inner.take() { + if let Some(next) = inner.next() { + self.inner = Some(inner); + return Some(next); + } + } + if let Some(next_element) = self.range_list.items.get(self.index) { + self.index += 1; + match next_element { + RangeListItem::Single(num) => Some(*num), + RangeListItem::Range(range) => { + if self.inclusive { + self.inner = Some(Box::new(range.min..=range.max)) + } else { + self.inner = Some(Box::new(range.min..range.max)) + } + self.next() + } + } + } else { + None + } + } +} + impl core::fmt::Display for RangeListParseErr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -126,4 +190,18 @@ mod test { let parsed: Result, _> = "1,2-".parse(); assert!(parsed.is_err_and(|e| matches!(e, RangeListParseErr::UnexpectedEnd))) } + + #[test] + fn iter_range() { + let range_list = RangeList { + items: vec![ + RangeListItem::Single(2), + RangeListItem::Range(Range { min: 4, max: 7 }), + RangeListItem::Single(3), + RangeListItem::Range(Range { min: 20, max: 24 }), + ], + }; + let actual: Vec = range_list.into_iter(true).collect(); + assert_eq!(actual, vec![2, 4, 5, 6, 7, 3, 20, 21, 22, 23, 24]) + } } diff --git a/crates/core/src/primitives/space_separated_list.rs b/crates/core/src/primitives/space_separated_list.rs new file mode 100644 index 0000000..956eeba --- /dev/null +++ b/crates/core/src/primitives/space_separated_list.rs @@ -0,0 +1,35 @@ +use std::str::FromStr; + +/// A list of items separated by a list +pub struct SpacedList(pub Vec); + +impl FromStr for SpacedList { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + let mut results = Vec::new(); + for chars in s.split(' ') { + if !chars.is_empty() { + results.push(T::from_str(chars)?); + } + } + Ok(Self(results)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parse_list_of_str() { + let strings: SpacedList = "abcd wxyz".parse().expect("fail"); + assert_eq!(strings.0, vec!["abcd", "wxyz"]); + } + + #[test] + fn parse_list_of_num() { + let strings: SpacedList = "1 2 3 4 5 6 7 8 9 10 11".parse().expect("fail"); + assert_eq!(strings.0, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + } +} diff --git a/crates/procbox/Cargo.toml b/crates/procbox/Cargo.toml index 45aec04..5bb0cba 100644 --- a/crates/procbox/Cargo.toml +++ b/crates/procbox/Cargo.toml @@ -7,4 +7,4 @@ description = "Power toolbox for processors" [dependencies] powerbox = { version = "0.1", path = "../core" } -sysfuss = { version = "0.3", path = "../../../sysfs-nav" } +sysfuss = { version = "0.4", path = "../../../sysfs-nav" } diff --git a/crates/procbox/src/cpu/basic.rs b/crates/procbox/src/cpu/basic_general.rs similarity index 86% rename from crates/procbox/src/cpu/basic.rs rename to crates/procbox/src/cpu/basic_general.rs index 7fe973e..a1dd886 100644 --- a/crates/procbox/src/cpu/basic.rs +++ b/crates/procbox/src/cpu/basic_general.rs @@ -4,7 +4,7 @@ use sysfuss::{BasicEntityPath, SysEntityAttributesExt}; use super::{CpuPower, CpuPowerOp}; -const DEFAULT_CPU_ROOT: &str = "/sys/devices/system/cpu"; +pub(super) const DEFAULT_CPU_ROOT: &str = "/sys/devices/system/cpu"; const CPUS_PRESENT: &str = "present"; const CPUS_POSSIBLE: &str = "possible"; const CPUS_KERNEL_MAX: &str = "kernel_max"; @@ -20,7 +20,7 @@ pub struct BasicCpus { impl BasicCpus { fn is_online(&self) -> bool { - self.sysfs.attribute::(CPUS_PRESENT).is_ok_and(|s| s.trim_end() != "") + self.sysfs.attribute::(CPUS_PRESENT).is_ok_and(|s| s.trim_end() != "") } fn is_exists(&self) -> bool { @@ -204,7 +204,7 @@ impl Power for BasicCpus { fn act(&self, _: GetSMT) -> Result { if self.sysfs.exists(&CPUS_SMT_ACTIVE) { - match self.sysfs.attribute::(CPUS_SMT_ACTIVE) { + match self.sysfs.attribute::(CPUS_SMT_ACTIVE) { Ok(s) => Ok(s.trim_end() == "1"), Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), Err(sysfuss::EitherErr2::Second(_)) => panic!("Infallible error"), @@ -298,7 +298,7 @@ impl Power> for BasicCpus { fn act(&self, _: GetPresent) -> Result, powerbox::PowerError> { if self.sysfs.exists(&CPUS_PRESENT) { - self.sysfs.attribute::, _>(CPUS_PRESENT).map_err(|e| match e { + self.sysfs.attribute::>(CPUS_PRESENT).map_err(|e| match e { sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e), sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown, }) @@ -353,7 +353,7 @@ impl Power> for BasicCpus { fn act(&self, _: GetPossible) -> Result, powerbox::PowerError> { if self.sysfs.exists(&CPUS_POSSIBLE) { - self.sysfs.attribute::, _>(CPUS_POSSIBLE).map_err(|e| match e { + self.sysfs.attribute::>(CPUS_POSSIBLE).map_err(|e| match e { sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e), sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown, }) @@ -410,7 +410,7 @@ impl Power for BasicCpus { fn act(&self, _: GetKernelMax) -> Result { if self.sysfs.exists(&CPUS_KERNEL_MAX) { - self.sysfs.attribute::(CPUS_KERNEL_MAX).map_err(|e| match e { + self.sysfs.attribute::(CPUS_KERNEL_MAX).map_err(|e| match e { sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e), sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown, }) @@ -464,7 +464,7 @@ impl Power> for BasicCpus { fn act(&self, _: GetCpusOnline) -> Result, powerbox::PowerError> { if self.sysfs.exists(&CPUS_ONLINE) { - self.sysfs.attribute::, _>(CPUS_ONLINE).map_err(|e| match e { + self.sysfs.attribute::>(CPUS_ONLINE).map_err(|e| match e { sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e), sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown, }) @@ -519,7 +519,7 @@ impl Power> for BasicCpus { fn act(&self, _: GetCpusOffline) -> Result, powerbox::PowerError> { if self.sysfs.exists(&CPUS_OFFLINE) { - self.sysfs.attribute::, _>(CPUS_OFFLINE).map_err(|e| match e { + self.sysfs.attribute::>(CPUS_OFFLINE).map_err(|e| match e { sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e), sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown, }) @@ -549,85 +549,68 @@ mod test_cpus_offline { } } -/// General single CPU functionality -pub struct BasicCpu { - +/// Get CPU by index +pub struct GetCpu(usize); +impl PowerOp for GetCpu { + fn is_eq_op(&self, _: &Self) -> bool { + true + } } -pub enum BasicCpuOp { - Online(Online), - CpuFreq(CpuFreq), - //CpuIdle(), - //ACPI(), +impl CpuPowerOp for GetCpu {} + +impl Power for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if let Ok(possible_cpus) = self.act(GetPossible) { + Box::new(possible_cpus.into_iter(true) + .map(|index| GetCpu(index))) + } else { + Box::new(core::iter::empty()) + } + } + + fn act(&self, i: GetCpu) -> Result { + Ok(super::BasicCpu::with_root(self.sysfs.as_ref().join(format!("cpu{}", i.0)))) + } } -pub struct SetCpuOnline(pub bool); -pub struct GetCpuOnline; -pub enum Online { - Get(GetCpuOnline), - Set(SetCpuOnline), +/// Get all CPUs present in the system +pub struct GetCpus; +impl PowerOp for GetCpus { + fn is_eq_op(&self, _: &Self) -> bool { + true + } } -pub struct GetAffectedCpus; // range or number?? -pub struct GetRelatedCpus; // range or number?? -pub enum CpuFreq { - AffectedCpus(GetAffectedCpus), - RelatedCpus(GetRelatedCpus), - CpuInfo(CpuInfo), - Energy(EnergyPerf), - Scaling(), -} +impl CpuPowerOp for GetCpus {} -pub struct GetCpuInfoMinFreq; // number -pub struct GetCpuInfoMaxFreq; // number -pub enum CpuInfo { - MinFreq(GetCpuInfoMinFreq), - MaxFreq(GetCpuInfoMaxFreq), -} +impl Power> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } -pub struct GetEnergyPerfAvailablePreferences; // list of string -pub enum EnergyPerf { - AvailablePreferences(GetEnergyPerfAvailablePreferences), - Preference(EnergyPerfPreference), -} + fn is_available(&self) -> bool { + self.is_exists() + } -pub struct GetEnergyPerfPreference; // string -pub struct SetEnergyPerfPreference(String); -pub enum EnergyPerfPreference { - Get(GetEnergyPerfPreference), - Set(SetEnergyPerfPreference) -} + fn supported_operations(&self) -> Box> { + Box::new(core::iter::once(GetCpus)) + } -pub struct GetScalingAvailableGovernors; // list of string -pub struct GetScalingCurFreq; // number -pub struct GetScalingDriver; // string -pub enum Scaling { - AvailableGovernors(GetScalingAvailableGovernors), - CurFreq(GetScalingCurFreq), - Driver(GetScalingDriver), - Governor(ScalingGovernor), - MaxFreq(ScalingMaxFreq), - MinFreq(ScalingMinFreq), - //Setspeed(), -} - -pub struct GetScalingGovernor; // string -pub struct SetScalingGovernor(String); -pub enum ScalingGovernor { - Get(GetScalingGovernor), - Set(SetScalingGovernor), -} - -pub struct GetScalingMaxFreq; // number -pub struct SetScalingMaxFreq(usize); -pub enum ScalingMaxFreq { - Get(GetScalingMaxFreq), - Set(SetScalingMaxFreq), -} - -pub struct GetScalingMinFreq; // number -pub struct SetScalingMinFreq(usize); -pub enum ScalingMinFreq { - Get(GetScalingMinFreq), - Set(SetScalingMinFreq), + fn act(&self, _: GetCpus) -> Result, powerbox::PowerError> { + let possible_cpus: Vec<_> = Power::::supported_operations(self).collect(); + let mut results = Vec::with_capacity(possible_cpus.len()); + for cpu in possible_cpus { + results.push(Power::::act(self, cpu)?); + } + Ok(results) + } } diff --git a/crates/procbox/src/cpu/basic_single.rs b/crates/procbox/src/cpu/basic_single.rs new file mode 100644 index 0000000..e51ad09 --- /dev/null +++ b/crates/procbox/src/cpu/basic_single.rs @@ -0,0 +1,1190 @@ +use powerbox::{Power, PowerOp}; +use powerbox::primitives::{Value, RangeList, SpacedList}; +use sysfuss::{BasicEntityPath, SysEntityAttributesExt}; + +use super::{CpuPower, CpuPowerOp}; + +const CPU_ONLINE: &str = "online"; + +const CPUFREQ_AFFECTED_CPUS: &str = "cpufreq/affected_cpus"; +const CPUFREQ_RELATED_CPUS: &str = "cpufreq/related_cpus"; +const CPUFREQ_CPUINFO_MIN: &str = "cpufreq/cpuinfo_min_freq"; +const CPUFREQ_CPUINFO_MAX: &str = "cpufreq/cpuinfo_max_freq"; +const CPUFREQ_ENERGY_PERF_AVAILABLE_PREFS: &str = "cpufreq/energy_performance_available_preferences"; +const CPUFREQ_ENERGY_PERF_PREF: &str = "cpufreq/energy_performance_preference"; +const CPUFREQ_SCALING_AVAILABLE_GOVS: &str = "cpufreq/scaling_available_governors"; +const CPUFREQ_SCALING_GOV: &str = "cpufreq/scaling_governor"; +const CPUFREQ_SCALING_CUR_FREQ: &str = "cpufreq/scaling_cur_freq"; +const CPUFREQ_SCALING_MAX_FREQ: &str = "cpufreq/scaling_max_freq"; +const CPUFREQ_SCALING_MIN_FREQ: &str = "cpufreq/scaling_min_freq"; +const CPUFREQ_SCALING_DRIVER: &str = "cpufreq/scaling_driver"; +//const CPUFREQ_SCALING_SETSPEED: &str = "cpufreq/scaling_setspeed"; + +/// General single CPU functionality +pub struct BasicCpu { + sysfs: BasicEntityPath, +} + +impl BasicCpu { + fn is_online(&self) -> bool { + self.sysfs.attribute::(CPU_ONLINE).is_ok_and(|num| num == 1) + } + + fn is_exists(&self) -> bool { + self.sysfs.as_ref().exists() + } + + pub fn new(cpu: usize) -> Self { + Self { + sysfs: BasicEntityPath::new(std::path::PathBuf::from(super::basic_general::DEFAULT_CPU_ROOT).join(format!("cpu{}", cpu))), + } + } + + pub fn with_root(p: impl AsRef) -> Self { + Self { + sysfs: BasicEntityPath::new(p), + } + } +} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + todo!() + } + + fn act(&self, _op: BasicCpuOp) -> Result { + todo!() + } +} + +impl CpuPower for BasicCpu {} + +pub enum BasicCpuOp { + Online(Online), + CpuFreq(CpuFreq), + //CpuIdle(), + //ACPI(), +} + +impl PowerOp for BasicCpuOp { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Online(online) => if let Self::Online(other) = other { online.is_eq_op(other) } else { false }, + Self::CpuFreq(cpufreq) => if let Self::CpuFreq(other) = other { cpufreq.is_eq_op(other) } else { false }, + } + } +} + +impl CpuPowerOp for BasicCpuOp {} + +pub struct SetCpuOnline(pub bool); + +impl PowerOp for SetCpuOnline { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for SetCpuOnline {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPU_ONLINE) { Box::new(core::iter::once(SetCpuOnline(true))) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: SetCpuOnline) -> Result<(), powerbox::PowerError> { + if self.sysfs.exists(&CPU_ONLINE) { + self.sysfs.set(CPU_ONLINE, powerbox::primitives::BoolNum(op.0)).map_err(powerbox::PowerError::Io) + } else { + Err(powerbox::PowerError::Unknown) + } + } +} + +impl core::convert::Into for SetCpuOnline { + fn into(self) -> Online { + Online::Set(self) + } +} + +pub struct GetCpuOnline; + +impl PowerOp for GetCpuOnline { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetCpuOnline {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPU_ONLINE) { Box::new(core::iter::once(GetCpuOnline)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetCpuOnline) -> Result { + match self.sysfs.attribute::(CPU_ONLINE) { + Ok(val) => Ok(val.0), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetCpuOnline { + fn into(self) -> Online { + Online::Get(self) + } +} + +pub enum Online { + Get(GetCpuOnline), + Set(SetCpuOnline), +} + +impl PowerOp for Online { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Get(_) => matches!(other, Self::Get(_)), + Self::Set(_) => matches!(other, Self::Set(_)), + } + } +} + +impl CpuPowerOp for Online {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new(Power::::supported_operations(self).map(core::convert::Into::::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into))) + } + + fn act(&self, op: Online) -> Result { + match op { + Online::Get(get) => Power::::act(self, get).map(core::convert::Into::into), + Online::Set(set) => Power::::act(self, set).map(|_| Value::Unknown), + } + } +} + +impl core::convert::Into for Online { + fn into(self) -> BasicCpuOp { + BasicCpuOp::Online(self) + } +} + +pub struct GetAffectedCpus; // range or number?? + +impl PowerOp for GetAffectedCpus { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetAffectedCpus {} + +impl Power> for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_AFFECTED_CPUS) { Box::new(core::iter::once(GetAffectedCpus)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetAffectedCpus) -> Result, powerbox::PowerError> { + match self.sysfs.attribute::>(CPUFREQ_AFFECTED_CPUS) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetAffectedCpus { + fn into(self) -> CpuFreq { + CpuFreq::AffectedCpus(self) + } +} + +pub struct GetRelatedCpus; // range or number?? + +impl PowerOp for GetRelatedCpus { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetRelatedCpus {} + +impl Power> for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_RELATED_CPUS) { Box::new(core::iter::once(GetRelatedCpus)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetRelatedCpus) -> Result, powerbox::PowerError> { + match self.sysfs.attribute::>(CPUFREQ_RELATED_CPUS) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetRelatedCpus { + fn into(self) -> CpuFreq { + CpuFreq::RelatedCpus(self) + } +} + +pub enum CpuFreq { + AffectedCpus(GetAffectedCpus), + RelatedCpus(GetRelatedCpus), + CpuInfo(CpuInfo), + Energy(EnergyPerf), + Scaling(Scaling), +} + +impl PowerOp for CpuFreq { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::AffectedCpus(_) => matches!(other, Self::AffectedCpus(_)), + Self::RelatedCpus(_) => matches!(other, Self::RelatedCpus(_)), + Self::CpuInfo(cpu_info) => if let Self::CpuInfo(other) = other { cpu_info.is_eq_op(other) } else { false }, + Self::Energy(en) => if let Self::Energy(other) = other { en.is_eq_op(other) } else { false }, + Self::Scaling(scaling) => if let Self::Scaling(other) = other { scaling.is_eq_op(other) } else { false }, + } + } +} + +impl CpuPowerOp for CpuFreq {} + +impl Power>> for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + ) + } + + fn act(&self, op: CpuFreq) -> Result>, powerbox::PowerError> { + match op { + CpuFreq::AffectedCpus(affected) => Power::::act(self, affected).map(Value::Custom), + CpuFreq::RelatedCpus(related) => Power::::act(self, related).map(Value::Custom), + //CpuFreq::CpuInfo(cpu_info) => if let Self::CpuInfo(other) = other { cpu_info.is_eq_op(other) } else { false }, + //CpuFreq::Energy(en) => if let Self::Energy(other) = other { en.is_eq_op(other) } else { false }, + //CpuFreq::Scaling(scaling) => if let Self::Scaling(other) = other { scaling.is_eq_op(other) } else { false }, + _ => todo!(), + } + } +} + +pub struct GetCpuInfoMinFreq; // number + +impl PowerOp for GetCpuInfoMinFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetCpuInfoMinFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_CPUINFO_MIN) { Box::new(core::iter::once(GetCpuInfoMinFreq)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetCpuInfoMinFreq) -> Result { + match self.sysfs.attribute::(CPUFREQ_CPUINFO_MIN) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetCpuInfoMinFreq { + fn into(self) -> CpuInfo { + CpuInfo::MinFreq(self) + } +} + +pub struct GetCpuInfoMaxFreq; // number + +impl PowerOp for GetCpuInfoMaxFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetCpuInfoMaxFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_CPUINFO_MAX) { Box::new(core::iter::once(GetCpuInfoMaxFreq)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetCpuInfoMaxFreq) -> Result { + match self.sysfs.attribute::(CPUFREQ_CPUINFO_MAX) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetCpuInfoMaxFreq { + fn into(self) -> CpuInfo { + CpuInfo::MaxFreq(self) + } +} + +pub enum CpuInfo { + MinFreq(GetCpuInfoMinFreq), + MaxFreq(GetCpuInfoMaxFreq), +} + +impl PowerOp for CpuInfo { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::MinFreq(_) => matches!(other, Self::MinFreq(_)), + Self::MaxFreq(_) => matches!(other, Self::MaxFreq(_)), + } + } +} + +impl CpuPowerOp for CpuInfo {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: CpuInfo) -> Result { + match op { + CpuInfo::MinFreq(min) => self.act(min), + CpuInfo::MaxFreq(max) => self.act(max), + } + } +} + +impl core::convert::Into for CpuInfo { + fn into(self) -> CpuFreq { + CpuFreq::CpuInfo(self) + } +} + +pub struct GetEnergyPerfAvailablePreferences; // list of string + +impl PowerOp for GetEnergyPerfAvailablePreferences { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetEnergyPerfAvailablePreferences {} + +impl Power> for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_ENERGY_PERF_AVAILABLE_PREFS) { Box::new(core::iter::once(GetEnergyPerfAvailablePreferences)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetEnergyPerfAvailablePreferences) -> Result, powerbox::PowerError> { + match self.sysfs.attribute::>(CPUFREQ_ENERGY_PERF_AVAILABLE_PREFS) { + Ok(val) => Ok(val.0), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetEnergyPerfAvailablePreferences { + fn into(self) -> EnergyPerf { + EnergyPerf::AvailablePreferences(self) + } +} + +pub enum EnergyPerf { + AvailablePreferences(GetEnergyPerfAvailablePreferences), + Preference(EnergyPerfPreference), +} + +impl PowerOp for EnergyPerf { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::AvailablePreferences(_) => matches!(other, Self::AvailablePreferences(_)), + Self::Preference(pref) => if let Self::Preference(other) = other { pref.is_eq_op(other) } else { false }, + } + } +} + +impl CpuPowerOp for EnergyPerf {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: EnergyPerf) -> Result { + match op { + EnergyPerf::AvailablePreferences(avail) => self.act(avail).map(|x| Value::Custom(Box::new(x) as _)), + EnergyPerf::Preference(pref) => self.act(pref).map(Value::into_any), + } + } +} + +impl core::convert::Into for EnergyPerf { + fn into(self) -> CpuFreq { + CpuFreq::Energy(self) + } +} + +pub struct GetEnergyPerfPreference; // string + +impl PowerOp for GetEnergyPerfPreference { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetEnergyPerfPreference {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_ENERGY_PERF_PREF) { Box::new(core::iter::once(GetEnergyPerfPreference)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetEnergyPerfPreference) -> Result { + match self.sysfs.attribute::(CPUFREQ_ENERGY_PERF_PREF) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetEnergyPerfPreference { + fn into(self) -> EnergyPerfPreference { + EnergyPerfPreference::Get(self) + } +} + +pub struct SetEnergyPerfPreference(String); + +impl PowerOp for SetEnergyPerfPreference { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for SetEnergyPerfPreference {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_ENERGY_PERF_PREF) { Box::new(core::iter::once(SetEnergyPerfPreference("".to_string()))) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: SetEnergyPerfPreference) -> Result<(), powerbox::PowerError> { + self.sysfs.set(CPUFREQ_ENERGY_PERF_PREF, op.0).map_err(powerbox::PowerError::Io) + } +} + +impl core::convert::Into for SetEnergyPerfPreference { + fn into(self) -> EnergyPerfPreference { + EnergyPerfPreference::Set(self) + } +} + +pub enum EnergyPerfPreference { + Get(GetEnergyPerfPreference), + Set(SetEnergyPerfPreference) +} + +impl PowerOp for EnergyPerfPreference { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Get(_) => matches!(other, Self::Get(_)), + Self::Set(_) => matches!(other, Self::Set(_)), + } + } +} + +impl CpuPowerOp for EnergyPerfPreference {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: EnergyPerfPreference) -> Result { + match op { + EnergyPerfPreference::Get(get) => self.act(get).map(Value::String), + EnergyPerfPreference::Set(set) => self.act(set).map(|_| Value::Unknown), + } + } +} + +impl core::convert::Into for EnergyPerfPreference { + fn into(self) -> EnergyPerf { + EnergyPerf::Preference(self) + } +} + +pub struct GetScalingAvailableGovernors; // list of string + +impl PowerOp for GetScalingAvailableGovernors { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingAvailableGovernors {} + +impl Power> for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_AVAILABLE_GOVS) { Box::new(core::iter::once(GetScalingAvailableGovernors)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingAvailableGovernors) -> Result, powerbox::PowerError> { + match self.sysfs.attribute::>(CPUFREQ_SCALING_AVAILABLE_GOVS) { + Ok(val) => Ok(val.0), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingAvailableGovernors { + fn into(self) -> Scaling { + Scaling::AvailableGovernors(self) + } +} + +pub struct GetScalingCurFreq; // number + +impl PowerOp for GetScalingCurFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingCurFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_CUR_FREQ) { Box::new(core::iter::once(GetScalingCurFreq)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingCurFreq) -> Result { + match self.sysfs.attribute::(CPUFREQ_SCALING_CUR_FREQ) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingCurFreq { + fn into(self) -> Scaling { + Scaling::CurFreq(self) + } +} + +pub struct GetScalingDriver; // string + +impl PowerOp for GetScalingDriver { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingDriver {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_DRIVER) { Box::new(core::iter::once(GetScalingDriver)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingDriver) -> Result { + match self.sysfs.attribute::(CPUFREQ_SCALING_DRIVER) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingDriver { + fn into(self) -> Scaling { + Scaling::Driver(self) + } +} + +pub enum Scaling { + AvailableGovernors(GetScalingAvailableGovernors), + CurFreq(GetScalingCurFreq), + Driver(GetScalingDriver), + Governor(ScalingGovernor), + MaxFreq(ScalingMaxFreq), + MinFreq(ScalingMinFreq), + //Setspeed(), +} + +impl PowerOp for Scaling { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::AvailableGovernors(_) => matches!(other, Self::AvailableGovernors(_)), + Self::CurFreq(_) => matches!(other, Self::CurFreq(_)), + Self::Driver(_) => matches!(other, Self::Driver(_)), + Self::Governor(gov) => if let Self::Governor(other) = other { gov.is_eq_op(other) } else { false }, + Self::MaxFreq(max) => if let Self::MaxFreq(other) = other { max.is_eq_op(other) } else { false }, + Self::MinFreq(min) => if let Self::MinFreq(other) = other { min.is_eq_op(other) } else { false }, + } + } +} + +impl CpuPowerOp for Scaling {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: Scaling) -> Result { + match op { + Scaling::AvailableGovernors(avail) => self.act(avail).map(|x| Value::Custom(Box::new(x) as _)), + Scaling::CurFreq(get_freq) => self.act(get_freq).map(|x| Value::Int(x as _)), + Scaling::Driver(driver) => self.act(driver).map(Value::String), + Scaling::Governor(gov) => self.act(gov).map(Value::into_any), + Scaling::MaxFreq(max_freq) => self.act(max_freq).map(Value::into_any), + Scaling::MinFreq(min_freq) => self.act(min_freq).map(Value::into_any), + } + } +} + +impl core::convert::Into for Scaling { + fn into(self) -> CpuFreq { + CpuFreq::Scaling(self) + } +} + +pub struct GetScalingGovernor; // string + +impl PowerOp for GetScalingGovernor { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingGovernor {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_GOV) { Box::new(core::iter::once(GetScalingGovernor)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingGovernor) -> Result { + match self.sysfs.attribute::(CPUFREQ_SCALING_GOV) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingGovernor { + fn into(self) -> ScalingGovernor { + ScalingGovernor::Get(self) + } +} + +pub struct SetScalingGovernor(String); + +impl PowerOp for SetScalingGovernor { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for SetScalingGovernor {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_GOV) { Box::new(core::iter::once(SetScalingGovernor("".to_owned()))) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: SetScalingGovernor) -> Result<(), powerbox::PowerError> { + self.sysfs.set(CPUFREQ_SCALING_GOV, op.0).map_err(powerbox::PowerError::Io) + } +} + +impl core::convert::Into for SetScalingGovernor { + fn into(self) -> ScalingGovernor { + ScalingGovernor::Set(self) + } +} + +pub enum ScalingGovernor { + Get(GetScalingGovernor), + Set(SetScalingGovernor), +} + +impl PowerOp for ScalingGovernor { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Get(_) => matches!(other, Self::Get(_)), + Self::Set(_) => matches!(other, Self::Set(_)), + } + } +} + +impl CpuPowerOp for ScalingGovernor {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: ScalingGovernor) -> Result { + match op { + ScalingGovernor::Get(get) => self.act(get).map(Value::String), + ScalingGovernor::Set(set) => self.act(set).map(|_| Value::Unknown) + } + } +} + +impl core::convert::Into for ScalingGovernor { + fn into(self) -> Scaling { + Scaling::Governor(self) + } +} + +pub struct GetScalingMaxFreq; // number + +impl PowerOp for GetScalingMaxFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingMaxFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_MAX_FREQ) { Box::new(core::iter::once(GetScalingMaxFreq)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingMaxFreq) -> Result { + match self.sysfs.attribute::(CPUFREQ_SCALING_MAX_FREQ) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingMaxFreq { + fn into(self) -> ScalingMaxFreq { + ScalingMaxFreq::Get(self) + } +} + +pub struct SetScalingMaxFreq(usize); + +impl PowerOp for SetScalingMaxFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for SetScalingMaxFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_MAX_FREQ) { Box::new(core::iter::once(SetScalingMaxFreq(0))) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: SetScalingMaxFreq) -> Result<(), powerbox::PowerError> { + self.sysfs.set(CPUFREQ_SCALING_MAX_FREQ, op.0).map_err(powerbox::PowerError::Io) + } +} + +impl core::convert::Into for SetScalingMaxFreq { + fn into(self) -> ScalingMaxFreq { + ScalingMaxFreq::Set(self) + } +} + +pub enum ScalingMaxFreq { + Get(GetScalingMaxFreq), + Set(SetScalingMaxFreq), +} + +impl PowerOp for ScalingMaxFreq { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Get(_) => matches!(other, Self::Get(_)), + Self::Set(_) => matches!(other, Self::Set(_)), + } + } +} + +impl CpuPowerOp for ScalingMaxFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: ScalingMaxFreq) -> Result { + match op { + ScalingMaxFreq::Get(get) => self.act(get).map(|x| Value::UInt(x as _)), + ScalingMaxFreq::Set(set) => self.act(set).map(|_| Value::Unknown) + } + } +} + +impl core::convert::Into for ScalingMaxFreq { + fn into(self) -> Scaling { + Scaling::MaxFreq(self) + } +} + +pub struct GetScalingMinFreq; // number + +impl PowerOp for GetScalingMinFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetScalingMinFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_MIN_FREQ) { Box::new(core::iter::once(GetScalingMinFreq)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetScalingMinFreq) -> Result { + match self.sysfs.attribute::(CPUFREQ_SCALING_MIN_FREQ) { + Ok(val) => Ok(val), + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } +} + +impl core::convert::Into for GetScalingMinFreq { + fn into(self) -> ScalingMinFreq { + ScalingMinFreq::Get(self) + } +} + +pub struct SetScalingMinFreq(usize); + +impl PowerOp for SetScalingMinFreq { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for SetScalingMinFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUFREQ_SCALING_MIN_FREQ) { Box::new(core::iter::once(SetScalingMinFreq(0))) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: SetScalingMinFreq) -> Result<(), powerbox::PowerError> { + self.sysfs.set(CPUFREQ_SCALING_MIN_FREQ, op.0).map_err(powerbox::PowerError::Io) + } +} + +impl core::convert::Into for SetScalingMinFreq { + fn into(self) -> ScalingMinFreq { + ScalingMinFreq::Set(self) + } +} + +pub enum ScalingMinFreq { + Get(GetScalingMinFreq), + Set(SetScalingMinFreq), +} + +impl PowerOp for ScalingMinFreq { + fn is_eq_op(&self, other: &Self) -> bool { + match self { + Self::Get(_) => matches!(other, Self::Get(_)), + Self::Set(_) => matches!(other, Self::Set(_)), + } + } +} + +impl CpuPowerOp for ScalingMinFreq {} + +impl Power for BasicCpu { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::into)) + ) + } + + fn act(&self, op: ScalingMinFreq) -> Result { + match op { + ScalingMinFreq::Get(get) => self.act(get).map(|x| Value::UInt(x as _)), + ScalingMinFreq::Set(set) => self.act(set).map(|_| Value::Unknown) + } + } +} + +impl core::convert::Into for ScalingMinFreq { + fn into(self) -> Scaling { + Scaling::MinFreq(self) + } +} diff --git a/crates/procbox/src/cpu/mod.rs b/crates/procbox/src/cpu/mod.rs index d4d5fe1..aad8f0a 100644 --- a/crates/procbox/src/cpu/mod.rs +++ b/crates/procbox/src/cpu/mod.rs @@ -3,5 +3,9 @@ mod cpu_trait; pub use cpu_trait::{CpuPower, CpuPowerOp}; #[allow(missing_docs)] -pub mod basic; -pub use basic::{BasicCpus, BasicCpu}; +pub mod basic_general; +pub use basic_general::BasicCpus; + +#[allow(missing_docs)] +pub mod basic_single; +pub use basic_single::BasicCpu;