Add some additional APIs endpoints for #48 and more

This commit is contained in:
NGnius (Graham) 2022-11-12 23:17:13 -05:00
parent 1610f18278
commit 01c86eeafe
9 changed files with 224 additions and 93 deletions

View file

@ -1,33 +1,38 @@
//use usdpl_back::core::serdes::Primitive; //use usdpl_back::core::serdes::Primitive;
use usdpl_back::AsyncCallable; use usdpl_back::AsyncCallable;
/*pub struct AsyncIsh<T: Send, pub struct AsyncIsh<In: Send + 'static,
TS: (Fn(super::ApiParameterType) -> Result<T, String>) + Send + Sync, Out: Send + 'static,
SG: (Fn(T) -> T) + Send + Sync + 'static, TS: (Fn(super::ApiParameterType) -> Result<In, String>) + Send + Sync,
TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> { Gen: (Fn() -> SG) + Send + Sync,
SG: (Fn(In) -> Out) + Send + Sync + 'static,
TG: (Fn(Out) -> super::ApiParameterType) + Send + Sync> {
pub trans_setter: TS, // assumed to be pretty fast pub trans_setter: TS, // assumed to be pretty fast
pub set_get: SG, // probably has locks (i.e. slow) pub set_get: Gen, // probably has locks (i.e. slow)
pub trans_getter: TG, // assumed to be pretty fast pub trans_getter: TG, // assumed to be pretty fast
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl <T: Send, impl <In: Send + 'static,
TS: (Fn(super::ApiParameterType) -> Result<T, String>) + Send + Sync, Out: Send + 'static,
SG: (Fn(T) -> T) + Send + Sync + 'static, TS: (Fn(super::ApiParameterType) -> Result<In, String>) + Send + Sync,
TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> Gen: (Fn() -> SG) + Send + Sync,
AsyncCallable for AsyncIsh<T, TS, SG, TG> { SG: (Fn(In) -> Out) + Send + Sync + 'static,
TG: (Fn(Out) -> super::ApiParameterType) + Send + Sync>
AsyncCallable for AsyncIsh<In, Out, TS, Gen, SG, TG> {
async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType { async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType {
let t_to_set = match (self.trans_setter)(params) { let t_to_set = match (self.trans_setter)(params) {
Ok(t) => t, Ok(t) => t,
Err(e) => return vec![e.into()] Err(e) => return vec![e.into()]
}; };
let t_got = match tokio::task::spawn_blocking(|| (self.set_get)(t_to_set)).await { let setter = (self.set_get)();
let t_got = match tokio::task::spawn_blocking(move || setter(t_to_set)).await {
Ok(t) => t, Ok(t) => t,
Err(e) => return vec![e.to_string().into()], Err(e) => return vec![e.to_string().into()],
}; };
(self.trans_getter)(t_got) (self.trans_getter)(t_got)
} }
}*/ }
pub struct AsyncIshGetter<T: Send + 'static, pub struct AsyncIshGetter<T: Send + 'static,
Gen: (Fn() -> G) + Send + Sync, Gen: (Fn() -> G) + Send + Sync,

View file

@ -3,14 +3,14 @@ use std::sync::{Arc, Mutex};
use usdpl_back::core::serdes::Primitive; use usdpl_back::core::serdes::Primitive;
use usdpl_back::AsyncCallable; use usdpl_back::AsyncCallable;
use crate::settings::{Cpu, SettingError, SettingVariant, MinMax}; use crate::settings::{Cpus, SettingError, SettingVariant, MinMax};
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; //use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
use super::handler::{ApiMessage, CpuMessage}; use super::handler::{ApiMessage, CpuMessage};
/// Available CPUs web method /// Available CPUs web method
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType { pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
super::utility::map_result( super::utility::map_result(
Cpu::cpu_count() Cpus::cpu_count()
.map(|x| x as u64) .map(|x| x as u64)
.ok_or_else( .ok_or_else(
|| SettingError { || SettingError {
@ -90,6 +90,38 @@ pub fn set_cpus_online(
} }
}*/ }*/
pub fn set_smt(
sender: Sender<ApiMessage>,
) -> impl AsyncCallable {
let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety
let getter = move || {
let sender2 = sender.clone();
move |smt: bool| {
let (tx, rx) = mpsc::channel();
let callback = move |values: Vec<bool>| tx.send(values).expect("set_smt callback send failed");
sender2.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::SetSmt(smt, Box::new(callback)))).expect("set_smt send failed");
rx.recv().expect("set_smt callback recv failed")
}
};
super::async_utils::AsyncIsh {
trans_setter: |params| {
if let Some(&Primitive::Bool(smt_value)) = params.get(0) {
Ok(smt_value)
} else {
Err("set_smt missing/invalid parameter 0".to_owned())
}
},
set_get: getter,
trans_getter: |result| {
let mut output = Vec::with_capacity(result.len());
for &status in result.as_slice() {
output.push(status.into());
}
output
}
}
}
pub fn get_cpus_online( pub fn get_cpus_online(
sender: Sender<ApiMessage>, sender: Sender<ApiMessage>,
) -> impl AsyncCallable { ) -> impl AsyncCallable {

View file

@ -1,6 +1,6 @@
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
use crate::settings::{Settings, Cpu, Gpu, Battery, General, OnSet, OnResume, MinMax}; use crate::settings::{Settings, Cpus, Gpu, Battery, General, OnSet, OnResume, MinMax};
use crate::persist::SettingsJson; use crate::persist::SettingsJson;
use crate::utility::unwrap_maybe_fatal; use crate::utility::unwrap_maybe_fatal;
@ -35,6 +35,7 @@ impl BatteryMessage {
pub enum CpuMessage { pub enum CpuMessage {
SetCpuOnline(usize, bool), SetCpuOnline(usize, bool),
SetCpusOnline(Vec<bool>), SetCpusOnline(Vec<bool>),
SetSmt(bool, Callback<Vec<bool>>),
GetCpusOnline(Callback<Vec<bool>>), GetCpusOnline(Callback<Vec<bool>>),
SetClockLimits(usize, Option<MinMax<u64>>), SetClockLimits(usize, Option<MinMax<u64>>),
GetClockLimits(usize, Callback<Option<MinMax<u64>>>), GetClockLimits(usize, Callback<Option<MinMax<u64>>>),
@ -44,32 +45,40 @@ pub enum CpuMessage {
} }
impl CpuMessage { impl CpuMessage {
fn process(self, settings: &mut Vec<Cpu>) { fn process(self, settings: &mut Cpus) {
match self { match self {
Self::SetCpuOnline(index, status) => {settings.get_mut(index).map(|c| c.online = status);}, Self::SetCpuOnline(index, status) => {settings.cpus.get_mut(index).map(|c| c.online = status);},
Self::SetCpusOnline(cpus) => { Self::SetCpusOnline(cpus) => {
for i in 0..cpus.len() { for i in 0..cpus.len() {
settings.get_mut(i).map(|c| c.online = cpus[i]); settings.cpus.get_mut(i).map(|c| c.online = cpus[i]);
} }
}, },
Self::SetSmt(status, cb) => {
let mut result = Vec::with_capacity(settings.cpus.len());
for i in 0..settings.cpus.len() {
settings.cpus[i].online = settings.cpus[i].online && (status || i % 2 == 0);
result.push(settings.cpus[i].online);
}
cb(result);
}
Self::GetCpusOnline(cb) => { Self::GetCpusOnline(cb) => {
let mut result = Vec::with_capacity(settings.len()); let mut result = Vec::with_capacity(settings.cpus.len());
for cpu in settings { for cpu in &settings.cpus {
result.push(cpu.online); result.push(cpu.online);
} }
cb(result); cb(result);
}, },
Self::SetClockLimits(index, clocks) => {settings.get_mut(index).map(|c| c.clock_limits = clocks);}, Self::SetClockLimits(index, clocks) => {settings.cpus.get_mut(index).map(|c| c.clock_limits = clocks);},
Self::GetClockLimits(index, cb) => {settings.get(index).map(|c| cb(c.clock_limits.clone()));}, Self::GetClockLimits(index, cb) => {settings.cpus.get(index).map(|c| cb(c.clock_limits.clone()));},
Self::SetCpuGovernor(index, gov) => {settings.get_mut(index).map(|c| c.governor = gov);}, Self::SetCpuGovernor(index, gov) => {settings.cpus.get_mut(index).map(|c| c.governor = gov);},
Self::SetCpusGovernor(govs) => { Self::SetCpusGovernor(govs) => {
for i in 0..govs.len() { for i in 0..govs.len() {
settings.get_mut(i).map(|c| c.governor = govs[i].clone()); settings.cpus.get_mut(i).map(|c| c.governor = govs[i].clone());
} }
}, },
Self::GetCpusGovernor(cb) => { Self::GetCpusGovernor(cb) => {
let mut result = Vec::with_capacity(settings.len()); let mut result = Vec::with_capacity(settings.cpus.len());
for cpu in settings { for cpu in &settings.cpus {
result.push(cpu.governor.clone()); result.push(cpu.governor.clone());
} }
cb(result); cb(result);

View file

@ -94,6 +94,10 @@ fn main() -> Result<(), ()> {
"CPU_get_onlines", "CPU_get_onlines",
api::cpu::get_cpus_online(api_sender.clone()) api::cpu::get_cpus_online(api_sender.clone())
) )
.register_async(
"CPU_set_smt",
api::cpu::set_smt(api_sender.clone())
)
.register( .register(
"CPU_set_clock_limits", "CPU_set_clock_limits",
api::cpu::set_clock_limits(api_sender.clone()) api::cpu::set_clock_limits(api_sender.clone())

View file

@ -4,6 +4,131 @@ use super::MinMax;
use super::{OnResume, OnSet, SettingError, SettingsRange}; use super::{OnResume, OnSet, SettingError, SettingsRange};
use crate::persist::CpuJson; use crate::persist::CpuJson;
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
#[derive(Debug, Clone)]
pub struct Cpus {
pub cpus: Vec<Cpu>,
pub smt: bool,
pub smt_capable: bool,
}
impl OnSet for Cpus {
fn on_set(&mut self) -> Result<(), SettingError> {
if self.smt_capable {
// toggle SMT
if self.smt {
usdpl_back::api::files::write_single(CPU_SMT_PATH, "on").map_err(|e| {
SettingError {
msg: format!(
"Failed to write `on` to `{}`: {}",
CPU_SMT_PATH, e
),
setting: super::SettingVariant::Cpu,
}
})?;
} else {
usdpl_back::api::files::write_single(CPU_SMT_PATH, "off").map_err(|e| {
SettingError {
msg: format!(
"Failed to write `off` to `{}`: {}",
CPU_SMT_PATH, e
),
setting: super::SettingVariant::Cpu,
}
})?;
}
}
for (i, cpu) in self.cpus.as_mut_slice().iter_mut().enumerate() {
cpu.state.do_set_online = self.smt || i % 2 == 0;
cpu.on_set()?;
}
Ok(())
}
}
impl OnResume for Cpus {
fn on_resume(&self) -> Result<(), SettingError> {
for cpu in &self.cpus {
cpu.on_resume()?;
}
Ok(())
}
}
impl Cpus {
pub fn cpu_count() -> Option<usize> {
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
if let Some(dash_index) = data.find('-') {
let data = data.split_off(dash_index + 1);
if let Ok(max_cpu) = data.parse::<usize>() {
return Some(max_cpu + 1);
}
}
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
None
}
fn system_smt_capabilities() -> (bool, bool) {
match usdpl_back::api::files::read_single::<_, String, _>(CPU_SMT_PATH) {
Ok(val) => (val.trim().to_lowercase() == "on", true),
Err(_) => (false, false)
}
}
pub fn system_default() -> Self {
if let Some(max_cpu) = Self::cpu_count() {
let mut sys_cpus = Vec::with_capacity(max_cpu);
for i in 0..max_cpu {
sys_cpus.push(Cpu::from_sys(i));
}
let (smt_status, can_smt) = Self::system_smt_capabilities();
Self {
cpus: sys_cpus,
smt: smt_status,
smt_capable: can_smt,
}
} else {
Self {
cpus: vec![],
smt: false,
smt_capable: false,
}
}
}
#[inline]
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self {
let (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(other.len());
let max_cpus = Self::cpu_count();
for (i, cpu) in other.drain(..).enumerate() {
// prevent having more CPUs than available
if let Some(max_cpus) = max_cpus {
if i == max_cpus {
break;
}
}
result.push(Cpu::from_json(cpu, version, i));
}
if let Some(max_cpus) = max_cpus {
if result.len() != max_cpus {
let mut sys_cpus = Cpus::system_default();
for i in result.len()..sys_cpus.cpus.len() {
result.push(sys_cpus.cpus.remove(i));
}
}
}
Self {
cpus: result,
smt: true,
smt_capable: can_smt,
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Cpu { pub struct Cpu {
pub online: bool, pub online: bool,
@ -16,8 +141,6 @@ pub struct Cpu {
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
impl Cpu { impl Cpu {
#[inline] #[inline]
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self { pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
@ -41,7 +164,7 @@ impl Cpu {
fn set_all(&mut self) -> Result<(), SettingError> { fn set_all(&mut self) -> Result<(), SettingError> {
// set cpu online/offline // set cpu online/offline
if self.index != 0 { // cpu0 cannot be disabled if self.index != 0 && self.state.do_set_online { // cpu0 cannot be disabled
let online_path = cpu_online_path(self.index); let online_path = cpu_online_path(self.index);
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| { usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
SettingError { SettingError {
@ -162,31 +285,6 @@ impl Cpu {
state: crate::state::Cpu::default(), state: crate::state::Cpu::default(),
} }
} }
pub fn cpu_count() -> Option<usize> {
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
if let Some(dash_index) = data.find('-') {
let data = data.split_off(dash_index + 1);
if let Ok(max_cpu) = data.parse::<usize>() {
return Some(max_cpu + 1);
}
}
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
None
}
pub fn system_default() -> Vec<Self> {
if let Some(max_cpu) = Self::cpu_count() {
let mut cpus = Vec::with_capacity(max_cpu);
for i in 0..max_cpu {
cpus.push(Self::from_sys(i));
}
cpus
} else {
Vec::with_capacity(0)
}
}
} }
impl Into<CpuJson> for Cpu { impl Into<CpuJson> for Cpu {

View file

@ -2,9 +2,9 @@ use std::convert::Into;
use std::path::PathBuf; use std::path::PathBuf;
//use std::sync::{Arc, Mutex}; //use std::sync::{Arc, Mutex};
use super::{Battery, Cpu, Gpu}; use super::{Battery, Cpus, Gpu};
use super::{OnResume, OnSet, SettingError}; use super::{OnResume, OnSet, SettingError};
use crate::persist::{CpuJson, SettingsJson}; use crate::persist::SettingsJson;
//use crate::utility::unwrap_lock; //use crate::utility::unwrap_lock;
const LATEST_VERSION: u64 = 0; const LATEST_VERSION: u64 = 0;
@ -44,7 +44,7 @@ impl OnSet for General {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Settings { pub struct Settings {
pub general: General, pub general: General,
pub cpus: Vec<Cpu>, pub cpus: Cpus,
pub gpu: Gpu, pub gpu: Gpu,
pub battery: Battery, pub battery: Battery,
} }
@ -52,9 +52,7 @@ pub struct Settings {
impl OnSet for Settings { impl OnSet for Settings {
fn on_set(&mut self) -> Result<(), SettingError> { fn on_set(&mut self) -> Result<(), SettingError> {
self.battery.on_set()?; self.battery.on_set()?;
for cpu in self.cpus.iter_mut() { self.cpus.on_set()?;
cpu.on_set()?;
}
self.gpu.on_set()?; self.gpu.on_set()?;
self.general.on_set()?; self.general.on_set()?;
Ok(()) Ok(())
@ -71,7 +69,7 @@ impl Settings {
path: json_path, path: json_path,
name: other.name, name: other.name,
}, },
cpus: Self::convert_cpus(other.cpus, other.version), cpus: Cpus::from_json(other.cpus, other.version),
gpu: Gpu::from_json(other.gpu, other.version), gpu: Gpu::from_json(other.gpu, other.version),
battery: Battery::from_json(other.battery, other.version), battery: Battery::from_json(other.battery, other.version),
}, },
@ -81,36 +79,13 @@ impl Settings {
path: json_path, path: json_path,
name: other.name, name: other.name,
}, },
cpus: Self::convert_cpus(other.cpus, other.version), cpus: Cpus::from_json(other.cpus, other.version),
gpu: Gpu::from_json(other.gpu, other.version), gpu: Gpu::from_json(other.gpu, other.version),
battery: Battery::from_json(other.battery, other.version), battery: Battery::from_json(other.battery, other.version),
}, },
} }
} }
fn convert_cpus(mut cpus: Vec<CpuJson>, version: u64) -> Vec<Cpu> {
let mut result = Vec::with_capacity(cpus.len());
let max_cpus = Cpu::cpu_count();
for (i, cpu) in cpus.drain(..).enumerate() {
// prevent having more CPUs than available
if let Some(max_cpus) = max_cpus {
if i == max_cpus {
break;
}
}
result.push(Cpu::from_json(cpu, version, i));
}
if let Some(max_cpus) = max_cpus {
if result.len() != max_cpus {
let mut sys_cpus = Cpu::system_default();
for i in result.len()..sys_cpus.len() {
result.push(sys_cpus.remove(i));
}
}
}
result
}
pub fn system_default(json_path: PathBuf) -> Self { pub fn system_default(json_path: PathBuf) -> Self {
Self { Self {
general: General { general: General {
@ -118,14 +93,14 @@ impl Settings {
path: json_path, path: json_path,
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
}, },
cpus: Cpu::system_default(), cpus: Cpus::system_default(),
gpu: Gpu::system_default(), gpu: Gpu::system_default(),
battery: Battery::system_default(), battery: Battery::system_default(),
} }
} }
pub fn load_system_default(&mut self) { pub fn load_system_default(&mut self) {
self.cpus = Cpu::system_default(); self.cpus = Cpus::system_default();
self.gpu = Gpu::system_default(); self.gpu = Gpu::system_default();
self.battery = Battery::system_default(); self.battery = Battery::system_default();
} }
@ -143,7 +118,7 @@ impl Settings {
self.general.persistent = false; self.general.persistent = false;
self.general.name = name; self.general.name = name;
} else { } else {
self.cpus = Self::convert_cpus(settings_json.cpus, settings_json.version); self.cpus = Cpus::from_json(settings_json.cpus, settings_json.version);
self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version); self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version);
self.battery = Battery::from_json(settings_json.battery, settings_json.version); self.battery = Battery::from_json(settings_json.battery, settings_json.version);
self.general.persistent = true; self.general.persistent = true;
@ -166,9 +141,7 @@ impl OnResume for Settings {
log::debug!("Applying settings for on_resume"); log::debug!("Applying settings for on_resume");
self.battery.on_resume()?; self.battery.on_resume()?;
log::debug!("Resumed battery"); log::debug!("Resumed battery");
for cpu in self.cpus.iter() { self.cpus.on_resume()?;
cpu.on_resume()?;
}
log::debug!("Resumed CPUs"); log::debug!("Resumed CPUs");
self.gpu.on_resume()?; self.gpu.on_resume()?;
log::debug!("Resumed GPU"); log::debug!("Resumed GPU");
@ -184,7 +157,7 @@ impl Into<SettingsJson> for Settings {
version: LATEST_VERSION, version: LATEST_VERSION,
name: self.general.name.clone(), name: self.general.name.clone(),
persistent: self.general.persistent, persistent: self.general.persistent,
cpus: self.cpus cpus: self.cpus.cpus
.clone() .clone()
.drain(..) .drain(..)
.map(|cpu| cpu.into()) .map(|cpu| cpu.into())

View file

@ -7,7 +7,7 @@ mod min_max;
mod traits; mod traits;
pub use battery::Battery; pub use battery::Battery;
pub use cpu::Cpu; pub use cpu::{Cpu, Cpus};
pub use general::{SettingVariant, Settings, General}; pub use general::{SettingVariant, Settings, General};
pub use gpu::Gpu; pub use gpu::Gpu;
pub use min_max::MinMax; pub use min_max::MinMax;

View file

@ -2,6 +2,7 @@
pub struct Cpu { pub struct Cpu {
pub clock_limits_set: bool, pub clock_limits_set: bool,
pub is_resuming: bool, pub is_resuming: bool,
pub do_set_online: bool,
} }
impl std::default::Default for Cpu { impl std::default::Default for Cpu {
@ -9,6 +10,7 @@ impl std::default::Default for Cpu {
Self { Self {
clock_limits_set: false, clock_limits_set: false,
is_resuming: false, is_resuming: false,
do_set_online: true,
} }
} }
} }

View file

@ -62,6 +62,10 @@ export async function unsetBatteryChargeRate(): Promise<any[]> {
// CPU // CPU
export async function setCpuSmt(status: boolean): Promise<number> {
return (await call_backend("CPU_set_smt", [status]))[0];
}
export async function getCpuCount(): Promise<number> { export async function getCpuCount(): Promise<number> {
return (await call_backend("CPU_count", []))[0]; return (await call_backend("CPU_count", []))[0];
} }
@ -150,6 +154,10 @@ export async function loadGeneralDefaultSettings(): Promise<boolean> {
return (await call_backend("GENERAL_load_default_settings", []))[0]; return (await call_backend("GENERAL_load_default_settings", []))[0];
} }
export async function loadGeneralSystemSettings(): Promise<boolean> {
return (await call_backend("GENERAL_load_system_settings", []))[0];
}
export async function getGeneralSettingsName(): Promise<boolean> { export async function getGeneralSettingsName(): Promise<boolean> {
return (await call_backend("GENERAL_get_name", []))[0]; return (await call_backend("GENERAL_get_name", []))[0];
} }