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::AsyncCallable;
/*pub struct AsyncIsh<T: Send,
TS: (Fn(super::ApiParameterType) -> Result<T, String>) + Send + Sync,
SG: (Fn(T) -> T) + Send + Sync + 'static,
TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> {
pub struct AsyncIsh<In: Send + 'static,
Out: Send + 'static,
TS: (Fn(super::ApiParameterType) -> Result<In, String>) + 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 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
}
#[async_trait::async_trait]
impl <T: Send,
TS: (Fn(super::ApiParameterType) -> Result<T, String>) + Send + Sync,
SG: (Fn(T) -> T) + Send + Sync + 'static,
TG: (Fn(T) -> super::ApiParameterType) + Send + Sync>
AsyncCallable for AsyncIsh<T, TS, SG, TG> {
impl <In: Send + 'static,
Out: Send + 'static,
TS: (Fn(super::ApiParameterType) -> Result<In, String>) + Send + Sync,
Gen: (Fn() -> SG) + Send + Sync,
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 {
let t_to_set = match (self.trans_setter)(params) {
Ok(t) => t,
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,
Err(e) => return vec![e.to_string().into()],
};
(self.trans_getter)(t_got)
}
}*/
}
pub struct AsyncIshGetter<T: Send + 'static,
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::AsyncCallable;
use crate::settings::{Cpu, SettingError, SettingVariant, MinMax};
use crate::settings::{Cpus, SettingError, SettingVariant, MinMax};
//use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
use super::handler::{ApiMessage, CpuMessage};
/// Available CPUs web method
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
super::utility::map_result(
Cpu::cpu_count()
Cpus::cpu_count()
.map(|x| x as u64)
.ok_or_else(
|| 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(
sender: Sender<ApiMessage>,
) -> impl AsyncCallable {

View file

@ -1,6 +1,6 @@
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::utility::unwrap_maybe_fatal;
@ -35,6 +35,7 @@ impl BatteryMessage {
pub enum CpuMessage {
SetCpuOnline(usize, bool),
SetCpusOnline(Vec<bool>),
SetSmt(bool, Callback<Vec<bool>>),
GetCpusOnline(Callback<Vec<bool>>),
SetClockLimits(usize, Option<MinMax<u64>>),
GetClockLimits(usize, Callback<Option<MinMax<u64>>>),
@ -44,32 +45,40 @@ pub enum CpuMessage {
}
impl CpuMessage {
fn process(self, settings: &mut Vec<Cpu>) {
fn process(self, settings: &mut Cpus) {
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) => {
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) => {
let mut result = Vec::with_capacity(settings.len());
for cpu in settings {
let mut result = Vec::with_capacity(settings.cpus.len());
for cpu in &settings.cpus {
result.push(cpu.online);
}
cb(result);
},
Self::SetClockLimits(index, clocks) => {settings.get_mut(index).map(|c| c.clock_limits = clocks);},
Self::GetClockLimits(index, cb) => {settings.get(index).map(|c| cb(c.clock_limits.clone()));},
Self::SetCpuGovernor(index, gov) => {settings.get_mut(index).map(|c| c.governor = gov);},
Self::SetClockLimits(index, clocks) => {settings.cpus.get_mut(index).map(|c| c.clock_limits = clocks);},
Self::GetClockLimits(index, cb) => {settings.cpus.get(index).map(|c| cb(c.clock_limits.clone()));},
Self::SetCpuGovernor(index, gov) => {settings.cpus.get_mut(index).map(|c| c.governor = gov);},
Self::SetCpusGovernor(govs) => {
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) => {
let mut result = Vec::with_capacity(settings.len());
for cpu in settings {
let mut result = Vec::with_capacity(settings.cpus.len());
for cpu in &settings.cpus {
result.push(cpu.governor.clone());
}
cb(result);

View file

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

View file

@ -4,6 +4,131 @@ use super::MinMax;
use super::{OnResume, OnSet, SettingError, SettingsRange};
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)]
pub struct Cpu {
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_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 {
#[inline]
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
@ -41,7 +164,7 @@ impl Cpu {
fn set_all(&mut self) -> Result<(), SettingError> {
// 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);
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
SettingError {
@ -162,31 +285,6 @@ impl Cpu {
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 {

View file

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

View file

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

View file

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

View file

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