Fix ryzenadj build error with feature flag, try fix game start callbacks, start LED lib refactor

This commit is contained in:
NGnius (Graham) 2023-12-17 16:13:59 -05:00
parent df71a619d9
commit 310af1b3ae
17 changed files with 337 additions and 146 deletions

9
backend/Cargo.lock generated
View file

@ -820,7 +820,6 @@ name = "limits_core"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
@ -1069,6 +1068,7 @@ dependencies = [
"log",
"regex",
"ron",
"sd_led",
"serde",
"serde_json",
"simplelog",
@ -1216,6 +1216,13 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "sd_led"
version = "0.1.0"
dependencies = [
"log",
]
[[package]]
name = "serde"
version = "1.0.183"

View file

@ -30,6 +30,7 @@ simplelog = "0.12"
limits_core = { version = "3", path = "./limits_core" }
regex = "1"
libryzenadj = { version = "0.12" }
sd_led = { version = "*", path = "./sd_led" }
# ureq's tls feature does not like musl targets
ureq = { version = "2", features = ["json", "gzip", "brotli", "charset"], default-features = false, optional = true }
@ -39,6 +40,7 @@ decky = ["usdpl-back/decky"]
crankshaft = ["usdpl-back/crankshaft"]
encrypt = ["usdpl-back/encrypt"]
online = ["ureq"]
experimental = []
dev_stuff = []
[profile.release]

View file

@ -2,18 +2,11 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "itoa"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]]
name = "limits_core"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
@ -34,12 +27,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
[[package]]
name = "serde"
version = "1.0.166"
@ -60,17 +47,6 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.23"

View file

@ -7,4 +7,3 @@ edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View file

@ -436,7 +436,6 @@ name = "limits_core"
version = "3.0.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]

16
backend/sd_led/Cargo.lock generated Normal file
View file

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "sd_led"
version = "0.1.0"
dependencies = [
"log",
]

10
backend/sd_led/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "sd_led"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# logging
log = "0.4"

193
backend/sd_led/src/lib.rs Normal file
View file

@ -0,0 +1,193 @@
//! Rough Rust port of some BatCtrl functionality
//! Original: /usr/share/jupiter_controller_fw_updater/RA_bootloader_updater/linux_host_tools/BatCtrl
//! I do not have access to the source code, so this is my own interpretation of what it does.
//!
//! But also Quanta is based in a place with some questionable copyright practices, so...
pub mod raw_io;
use std::io::Error;
pub fn set_led(red_unused: bool, green_aka_white: bool, blue_unused: bool) -> Result<usize, Error> {
let payload: u8 = 0x80
| (red_unused as u8 & 1)
| ((green_aka_white as u8 & 1) << 1)
| ((blue_unused as u8 & 1) << 2);
//log::info!("Payload: {:b}", payload);
raw_io::write2(Setting::LEDStatus as _, payload)
}
pub fn set(setting: Setting, mode: u8) -> Result<usize, Error> {
raw_io::write2(setting as u8, mode)
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Setting {
CycleCount = 0x32,
ControlBoard = 0x6C,
Charge = 0xA6,
ChargeMode = 0x76,
LEDStatus = 199,
LEDBreathing = 0x63,
FanSpeed = 0x2c, // lower 3 bits seem to not do everything, every other bit increases speed -- 5 total steps, 0xf4 seems to do something similar too
// 0x40 write 0x08 makes LED red + green turn on
// 0x58 write 0x80 shuts off battery power (bms?)
// 0x63 makes blue (0x02) or white (0x01) LED breathing effect
// 0x7a write 0x01, 0x02, or 0x03 turns off display
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum ControlBoard {
Enable = 0xAA,
Disable = 0xAB,
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum ChargeMode {
Normal = 0,
Discharge = 0x42,
Idle = 0x45,
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Charge {
Enable = 0,
Disable = 4,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(dead_code)]
fn led_all_experiment_test() -> Result<(), Error> {
let original = raw_io::write_read(Setting::LEDStatus as _)?;
let sleep_dur = std::time::Duration::from_millis(1000);
for b in 0..0x7F {
let actual = 0x80 | b;
raw_io::write2(Setting::LEDStatus as _, actual)?;
println!("Wrote {actual:#b} to LED byte");
std::thread::sleep(sleep_dur);
}
raw_io::write2(Setting::LEDStatus as _, original)?;
Ok(())
}
#[test]
#[allow(dead_code)]
fn led_singles_experiment_test() -> Result<(), Error> {
let original = raw_io::write_read(Setting::LEDStatus as _)?;
let sleep_dur = std::time::Duration::from_millis(1000);
let mut value = 1;
for _ in 0..std::mem::size_of::<u8>()*8 {
let actual = 0x80 | value;
raw_io::write2(Setting::LEDStatus as _, actual)?;
println!("Wrote {actual:#b} to LED byte");
value = value << 1;
std::thread::sleep(sleep_dur);
}
raw_io::write2(Setting::LEDStatus as _, original)?;
Ok(())
}
#[test]
#[allow(dead_code)]
fn led_specify_experiment_test() -> Result<(), Error> {
let mut buffer = String::new();
println!("LED number(s) to display?");
std::io::stdin().read_line(&mut buffer)?;
let mut resultant = 0;
let original = raw_io::write_read(Setting::LEDStatus as _)?;
for word in buffer.split(' ') {
let trimmed_word = word.trim();
if !trimmed_word.is_empty() {
let value: u8 = trimmed_word.parse().expect("Invalid u8 number");
let actual = 0x80 | value;
raw_io::wait_ready_for_write()?;
raw_io::write2(Setting::LEDStatus as _, actual)?;
println!("Wrote {actual:#b} to LED byte");
resultant |= actual;
}
}
println!("Effectively wrote {resultant:#b} to LED byte");
println!("Press enter to return to normal");
std::io::stdin().read_line(&mut buffer)?;
raw_io::write2(Setting::LEDStatus as _, original)?;
Ok(())
}
#[test]
#[allow(dead_code)]
fn breath_specify_experiment_test() -> Result<(), Error> {
let mut buffer = String::new();
println!("LED number(s) to display?");
std::io::stdin().read_line(&mut buffer)?;
for word in buffer.split(' ') {
let trimmed_word = word.trim();
if !trimmed_word.is_empty() {
let value: u8 = trimmed_word.parse().expect("Invalid u8 number");
let actual = 0x20 | value;
raw_io::wait_ready_for_write()?;
raw_io::write2(0x63, actual)?;
println!("Wrote {actual:#b} to LED breathing byte");
}
}
println!("Press enter to return to normal");
std::io::stdin().read_line(&mut buffer)?;
raw_io::write2(0x63, 0)?;
Ok(())
}
#[test]
#[allow(dead_code)]
fn unmapped_ports_experiment_test() -> Result<(), Error> {
let sleep_dur = std::time::Duration::from_millis(10000);
let value = 0xaa;
for addr in 0x63..0x64 {
//raw_io::wait_ready_for_read()?;
//let read = raw_io::write_read(addr)?;
raw_io::wait_ready_for_write()?;
raw_io::write2(addr, value)?;
println!("wrote {value:#b} for {addr:#x} port");
std::thread::sleep(sleep_dur);
}
//raw_io::write2(Setting::LEDStatus as _, 0)?;
Ok(())
}
#[test]
#[allow(dead_code)]
fn write_specify_experiment_test() -> Result<(), Error> {
let mut buffer = String::new();
println!("Register?");
std::io::stdin().read_line(&mut buffer)?;
let register: u8 = buffer.trim().parse().expect("Invalid u8 number");
buffer.clear();
println!("Value(s)?");
std::io::stdin().read_line(&mut buffer)?;
for word in buffer.split(' ') {
let trimmed_word = word.trim();
if !trimmed_word.is_empty() {
let value: u8 = trimmed_word.parse().expect("Invalid u8 number");
raw_io::wait_ready_for_write()?;
raw_io::write2(register, value)?;
println!("Wrote {value:#09b} to {register:#02x} register");
}
}
println!("Press enter to clear register");
std::io::stdin().read_line(&mut buffer)?;
raw_io::write2(register, 0)?;
Ok(())
}
}

View file

@ -0,0 +1,51 @@
use std::fs::OpenOptions;
use std::io::{Error, Read, Seek, SeekFrom, Write};
#[inline]
pub fn write2(p0: u8, p1: u8) -> Result<usize, Error> {
write_to(0x6c, 0x81)?;
wait_ready_for_write()?;
let count0 = write_to(0x68, p0)?;
wait_ready_for_write()?;
let count1 = write_to(0x68, p1)?;
Ok(count0 + count1)
}
#[inline]
pub fn write_read(p0: u8) -> Result<u8, Error> {
write_to(0x6c, 0x81)?;
wait_ready_for_write()?;
write_to(0x68, p0)?;
wait_ready_for_read()?;
read_from(0x68)
}
pub fn write_to(location: u64, value: u8) -> Result<usize, Error> {
let mut file = OpenOptions::new().write(true).open("/dev/port")?;
file.seek(SeekFrom::Start(location))?;
file.write(&[value])
}
pub fn read_from(location: u64) -> Result<u8, Error> {
let mut file = OpenOptions::new().read(true).open("/dev/port")?;
file.seek(SeekFrom::Start(location))?;
let mut buffer = [0];
file.read(&mut buffer)?;
Ok(buffer[0])
}
pub fn wait_ready_for_write() -> Result<(), Error> {
let mut count = 0;
while count < 0x1ffff && (read_from(0x6c)? & 2) != 0 {
count += 1;
}
Ok(())
}
pub fn wait_ready_for_read() -> Result<(), Error> {
let mut count = 0;
while count < 0x1ffff && (read_from(0x6c)? & 1) == 0 {
count += 1;
}
Ok(())
}

View file

@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize};
pub struct GpuJson {
pub fast_ppt: Option<u64>,
pub slow_ppt: Option<u64>,
pub tdp: Option<u64>,
pub tdp_boost: Option<u64>,
pub clock_limits: Option<MinMaxJson<u64>>,
pub slow_memory: bool,
#[serde(skip_serializing_if = "Option::is_none")]
@ -19,6 +21,8 @@ impl Default for GpuJson {
Self {
fast_ppt: None,
slow_ppt: None,
tdp: None,
tdp_boost: None,
clock_limits: None,
slow_memory: false,
root: None,

View file

@ -14,6 +14,8 @@ pub struct Gpu {
pub slow_memory: bool,
pub fast_ppt: Option<u64>,
pub slow_ppt: Option<u64>,
pub tdp: Option<u64>,
pub tdp_boost: Option<u64>,
pub clock_limits: Option<MinMax<u64>>,
pub limits: GenericGpuLimit,
sysfs: BasicEntityPath,
@ -70,6 +72,16 @@ impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
} else {
None
},
tdp: if limits.tdp.is_some() {
persistent.tdp
} else {
None
},
tdp_boost: if limits.tdp_boost.is_some() {
persistent.tdp_boost
} else {
None
},
clock_limits: clock_lims,
limits,
sysfs: Self::find_card_sysfs(persistent.root)
@ -81,6 +93,8 @@ impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
slow_memory: false,
fast_ppt: None,
slow_ppt: None,
tdp: None,
tdp_boost: None,
clock_limits: None,
limits,
sysfs: Self::find_card_sysfs(None::<&'static str>),
@ -94,6 +108,8 @@ impl Into<GpuJson> for Gpu {
GpuJson {
fast_ppt: self.fast_ppt,
slow_ppt: self.slow_ppt,
tdp: self.tdp,
tdp_boost: self.tdp_boost,
clock_limits: self.clock_limits.map(|x| x.into()),
slow_memory: false,
root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned()))

View file

@ -17,6 +17,7 @@ fn msg_or_err<D: std::fmt::Display, E: std::fmt::Display>(output: &mut String, m
fn log_capabilities(ryzenadj: &RyzenAdj) {
log::info!("RyzenAdj v{}.{}.{}", libryzenadj::libryzenadj_sys::RYZENADJ_REVISION_VER, libryzenadj::libryzenadj_sys::RYZENADJ_MAJOR_VER, libryzenadj::libryzenadj_sys::RYZENADJ_MINIOR_VER);
#[cfg(feature = "experimental")]
if let Some(x) = ryzenadj.get_init_table_err() {
log::warn!("RyzenAdj table init error: {}", x);
}

View file

@ -6,7 +6,7 @@ use sysfuss::capability::attributes;
use limits_core::json_v2::GenericBatteryLimit;
use super::util::ChargeMode;
use sd_led::ChargeMode;
use crate::api::RangeLimit;
use crate::persist::{BatteryEventJson, BatteryJson};
use crate::settings::{TBattery, ProviderBuilder};
@ -131,7 +131,7 @@ impl EventInstruction {
fn set_charge_mode(&self) -> Result<(), SettingError> {
if let Some(charge_mode) = self.charge_mode {
super::util::set(super::util::Setting::ChargeMode, charge_mode as _)
sd_led::set(sd_led::Setting::ChargeMode, charge_mode as _)
.map_err(|e| SettingError {
msg: format!("Failed to set charge mode: {}", e),
setting: crate::settings::SettingVariant::Battery,
@ -329,7 +329,7 @@ impl Battery {
fn set_charge_mode(&mut self) -> Result<(), SettingError> {
if let Some(charge_mode) = self.charge_mode {
self.state.charge_mode_set = true;
super::util::set(super::util::Setting::ChargeMode, charge_mode as _)
sd_led::set(sd_led::Setting::ChargeMode, charge_mode as _)
.map_err(|e| SettingError {
msg: format!("Failed to set charge mode: {}", e),
setting: crate::settings::SettingVariant::Battery,
@ -337,7 +337,7 @@ impl Battery {
.map(|_| ())
} else if self.state.charge_mode_set {
self.state.charge_mode_set = false;
super::util::set(super::util::Setting::ChargeMode, ChargeMode::Normal as _)
sd_led::set(sd_led::Setting::ChargeMode, ChargeMode::Normal as _)
.map_err(|e| SettingError {
msg: format!("Failed to set charge mode: {}", e),
setting: crate::settings::SettingVariant::Battery,

View file

@ -303,6 +303,8 @@ impl Into<GpuJson> for Gpu {
GpuJson {
fast_ppt: self.fast_ppt,
slow_ppt: self.slow_ppt,
tdp: None,
tdp_boost: None,
clock_limits: self.clock_limits.map(|x| x.into()),
slow_memory: self.slow_memory,
root: self.sysfs_card.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|r| r.to_owned()))

View file

@ -1,13 +1,5 @@
//! Rough Rust port of some BatCtrl functionality
//! Original: /usr/share/jupiter_controller_fw_updater/RA_bootloader_updater/linux_host_tools/BatCtrl
//! I do not have access to the source code, so this is my own interpretation of what it does.
//!
//! But also Quanta is based in a place with some questionable copyright practices, so...
#![allow(dead_code)]
use std::fs::OpenOptions;
use std::io::{Error, Read, Seek, SeekFrom, Write};
pub const JUPITER_HWMON_NAME: &'static str = "jupiter";
pub const STEAMDECK_HWMON_NAME: &'static str = "steamdeck_hwmon";
pub const GPU_HWMON_NAME: &'static str = "amdgpu";
@ -25,63 +17,6 @@ pub fn card_also_has(card: &dyn sysfuss::SysEntity, extensions: &'static [&'stat
.all(|ext| card.as_ref().join(ext).exists())
}
#[inline]
fn write2(p0: u8, p1: u8) -> Result<usize, Error> {
write_to(0x6c, 0x81)?;
wait_ready_for_write()?;
let count0 = write_to(0x68, p0)?;
wait_ready_for_write()?;
let count1 = write_to(0x68, p1)?;
Ok(count0 + count1)
}
fn write_read(p0: u8) -> Result<u8, Error> {
write_to(0x6c, 0x81)?;
wait_ready_for_write()?;
write_to(0x68, p0)?;
wait_ready_for_read()?;
read_from(0x68)
}
fn write_to(location: u64, value: u8) -> Result<usize, Error> {
let mut file = OpenOptions::new().write(true).open("/dev/port")?;
file.seek(SeekFrom::Start(location))?;
file.write(&[value])
}
fn read_from(location: u64) -> Result<u8, Error> {
let mut file = OpenOptions::new().read(true).open("/dev/port")?;
file.seek(SeekFrom::Start(location))?;
let mut buffer = [0];
file.read(&mut buffer)?;
Ok(buffer[0])
}
fn wait_ready_for_write() -> Result<(), Error> {
let mut count = 0;
while count < 0x1ffff && (read_from(0x6c)? & 2) != 0 {
count += 1;
}
Ok(())
}
fn wait_ready_for_read() -> Result<(), Error> {
let mut count = 0;
while count < 0x1ffff && (read_from(0x6c)? & 1) == 0 {
count += 1;
}
Ok(())
}
pub fn set_led(red_unused: bool, green_aka_white: bool, blue_unused: bool) -> Result<usize, Error> {
let payload: u8 = 0x80
| (red_unused as u8 & 1)
| ((green_aka_white as u8 & 1) << 1)
| ((blue_unused as u8 & 1) << 2);
//log::info!("Payload: {:b}", payload);
write2(Setting::LEDStatus as _, payload)
}
const THINGS: &[u8] = &[
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1,
@ -92,55 +27,20 @@ const THINGS: &[u8] = &[
const TIME_UNIT: std::time::Duration = std::time::Duration::from_millis(200);
pub fn flash_led() {
let old_led_state = write_read(Setting::LEDStatus as _)
let led_status = sd_led::Setting::LEDStatus;
let old_led_state = sd_led::raw_io::write_read(led_status as _)
.map_err(|e| log::error!("Failed to read LED status: {}", e));
for &code in THINGS {
let on = code != 0;
if let Err(e) = set_led(on, on, false) {
if let Err(e) = sd_led::set_led(on, on, false) {
log::error!("Thing err: {}", e);
}
std::thread::sleep(TIME_UNIT);
}
if let Ok(old_led_state) = old_led_state {
log::debug!("Restoring LED state to {:#02b}", old_led_state);
write2(Setting::LEDStatus as _, old_led_state)
sd_led::raw_io::write2(led_status as _, old_led_state)
.map_err(|e| log::error!("Failed to restore LED status: {}", e))
.unwrap();
}
}
pub fn set(setting: Setting, mode: u8) -> Result<usize, Error> {
write2(setting as u8, mode)
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Setting {
CycleCount = 0x32,
ControlBoard = 0x6C,
Charge = 0xA6,
ChargeMode = 0x76,
LEDStatus = 199,
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum ControlBoard {
Enable = 0xAA,
Disable = 0xAB,
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum ChargeMode {
Normal = 0,
Discharge = 0x42,
Idle = 0x45,
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Charge {
Enable = 0,
Disable = 4,
}

View file

@ -34,6 +34,8 @@ impl Into<GpuJson> for Gpu {
GpuJson {
fast_ppt: None,
slow_ppt: None,
tdp: None,
tdp_boost: None,
clock_limits: None,
slow_memory: false,
root: None,

View file

@ -163,12 +163,20 @@ const reload = function() {
backend.resolve(backend.getMessages(null), (messages: backend.Message[]) => { set_value(MESSAGE_LIST, messages) });
};
// init USDPL WASM and connection to back-end
(async function(){
await backend.initBackend();
usdplReady = true;
reload(); // technically this is only a load
const clearHooks = function() {
clearInterval(periodicHook!);
periodicHook = null;
lifetimeHook?.unregister();
startHook?.unregister();
endHook?.unregister();
backend.log(backend.LogLevel.Debug, "Unregistered PowerTools callbacks, so long and thanks for all the fish.");
};
const registerCallbacks = function(autoclear: boolean) {
if (autoclear) {
clearHooks();
}
// register Steam callbacks
//@ts-ignore
lifetimeHook = SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update) => {
@ -203,6 +211,15 @@ const reload = function() {
});
backend.log(backend.LogLevel.Debug, "Registered PowerTools callbacks, hello!");
};
// init USDPL WASM and connection to back-end
(async function(){
await backend.initBackend();
usdplReady = true;
reload(); // technically this is only a load
registerCallbacks(true);
})();
const periodicals = function() {
@ -334,19 +351,15 @@ export default definePlugin((serverApi: ServerAPI) => {
if (now.getDate() == 1 && now.getMonth() == 3) {
ico = <span><GiDynamite /><GiTimeTrap /><GiTimeBomb /></span>;
}
registerCallbacks(false);
return {
title: <div className={staticClasses.Title}>PowerTools</div>,
content: <Content serverAPI={serverApi} />,
icon: ico,
onDismount() {
backend.log(backend.LogLevel.Debug, "PowerTools shutting down");
clearInterval(periodicHook!);
periodicHook = null;
lifetimeHook?.unregister();
startHook?.unregister();
endHook?.unregister();
clearHooks();
//serverApi.routerHook.removeRoute("/decky-plugin-test");
backend.log(backend.LogLevel.Debug, "Unregistered PowerTools callbacks, so long and thanks for all the fish.");
},
};
});