Temporary checkpoint

This commit is contained in:
Luke D. Jones
2021-08-22 14:01:05 +12:00
parent 8fcd05c2bb
commit 0ed97db4c1
32 changed files with 553 additions and 2341 deletions

85
Cargo.lock generated
View File

@@ -52,7 +52,6 @@ dependencies = [
"rog_anime",
"rog_aura",
"rog_dbus",
"rog_fan_curve 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rog_profiles",
"rog_types",
"tinybmp",
@@ -109,9 +108,9 @@ checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2b_simd"
@@ -214,7 +213,6 @@ dependencies = [
"rog_anime",
"rog_aura",
"rog_dbus",
"rog_fan_curve 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rog_profiles",
"rog_types",
"rusb",
@@ -253,7 +251,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -305,7 +303,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -403,7 +401,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -497,7 +495,7 @@ checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -524,16 +522,6 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "intel-pstate"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dbd48c2f4886e44c137f4acb6ba3cf8df15154a2c996a65ee5e57c54a04c01f"
dependencies = [
"smart-default",
"thiserror",
]
[[package]]
name = "itoa"
version = "0.4.7"
@@ -548,9 +536,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.99"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "libudev-sys"
@@ -620,9 +608,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "miniz_oxide"
@@ -650,7 +638,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
dependencies = [
"bitflags 1.2.1",
"bitflags 1.3.2",
"cc",
"cfg-if 0.1.10",
"libc",
@@ -861,7 +849,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags 1.2.1",
"bitflags 1.3.2",
]
[[package]]
@@ -939,26 +927,10 @@ dependencies = [
"zvariant",
]
[[package]]
name = "rog_fan_curve"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a810b86236d51df31d9098ba1a1876c9f470573f903d13b78f5ee48d4e99ee90"
dependencies = [
"serde",
]
[[package]]
name = "rog_fan_curve"
version = "0.1.10"
source = "git+https://github.com/Yarn/rog_fan_curve.git#d46ead0db7c8c4870dae960c3a43b4ac6ab57c2d"
[[package]]
name = "rog_profiles"
version = "0.1.2"
dependencies = [
"intel-pstate",
"rog_fan_curve 0.1.10 (git+https://github.com/Yarn/rog_fan_curve.git)",
"serde",
"serde_derive",
"zvariant",
@@ -1012,22 +984,22 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "serde"
version = "1.0.127"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.127"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -1049,7 +1021,7 @@ checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -1058,17 +1030,6 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "smart-default"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
]
[[package]]
name = "socket2"
version = "0.4.1"
@@ -1114,9 +1075,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.74"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote 1.0.9",
@@ -1167,7 +1128,7 @@ checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -1375,7 +1336,7 @@ dependencies = [
"proc-macro-crate 0.1.5",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]
[[package]]
@@ -1400,5 +1361,5 @@ dependencies = [
"proc-macro-crate 1.0.0",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.74",
"syn 1.0.75",
]

View File

@@ -1,7 +1,7 @@
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{DbusProxies, Signals};
use rog_profiles::profiles::{FanLevel, Profile};
use rog_profiles::Profile;
use rog_types::gfx_vendors::GfxRequiredUserAction;
use rog_types::gfx_vendors::GfxVendors;
use std::error::Error;
@@ -88,19 +88,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let fan = profile.fan_preset;
let turbo = if profile.turbo { "enabled" } else { "disabled" };
let icon = match fan {
FanLevel::Normal => "asus_notif_yellow",
FanLevel::Boost => "asus_notif_red",
FanLevel::Silent => "asus_notif_green",
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}, turbo {}",
profile.name.to_uppercase(),
turbo
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)

View File

@@ -13,7 +13,6 @@ rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_types = { path = "../rog-types" }
daemon = { path = "../daemon" }
rog_fan_curve = { version = "^0.1", features = ["serde"] }
gumdrop = "^0.8"
yansi-term = "^0.1"

View File

@@ -9,11 +9,10 @@ use profiles_cli::ProfileCommand;
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClient;
use rog_profiles::profiles::Profile;
use rog_types::{
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
supported::{
AnimeSupportedFunctions, FanCpuSupportedFunctions, LedSupportedFunctions,
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
RogBiosSupportedFunctions,
},
};
@@ -152,7 +151,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.platform_profile, &cmd)?,
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
Some(CliCommand::Anime(cmd)) => handle_anime(&dbus, &supported.anime_ctrl, &cmd)?,
Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?,
@@ -384,24 +383,10 @@ fn handle_led_mode(
fn handle_profile(
dbus: &RogDbusClient,
supported: &FanCpuSupportedFunctions,
supported: &PlatformProfileFunctions,
cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if !cmd.next
&& !cmd.create // TODO
&& !cmd.list
&& cmd.profile.is_none()
&& !cmd.active_name
&& !cmd.active_data
&& !cmd.profiles_data
&& cmd.remove.is_none()
&& cmd.curve.is_none() // TODO
&& cmd.fan_preset.is_none() // TODO
&& cmd.turbo.is_none() // TODO
&& cmd.max_percentage.is_none() // TODO
&& cmd.min_percentage.is_none()
// TODO
{
if !cmd.next && !cmd.list && !cmd.active_name && !cmd.active_data && !cmd.profiles_data {
if !cmd.help {
println!("Missing arg or command\n");
}
@@ -411,7 +396,7 @@ fn handle_profile(
.collect();
for line in usage
.iter()
.filter(|line| !line.contains("--curve") || supported.fan_curve_set)
.filter(|line| !line.contains("--curve") || supported.fan_curves)
{
println!("{}", line);
}
@@ -426,78 +411,8 @@ fn handle_profile(
}
if cmd.next {
dbus.proxies().profile().next_fan()?;
dbus.proxies().profile().next_profile()?;
}
if let Some(profile) = &cmd.remove {
dbus.proxies().profile().remove(profile)?
}
if cmd.list {
let profile_names = dbus.proxies().profile().profile_names()?;
println!("Available profiles are {:?}", profile_names);
}
if cmd.active_name {
println!(
"Active profile: {:?}",
dbus.proxies().profile().active_name()?
);
}
if cmd.active_data {
println!("Active profile:");
println!("{:?}", dbus.proxies().profile().active_data()?);
}
if cmd.profiles_data {
println!("Profiles:");
for s in dbus.proxies().profile().all_profile_data()? {
println!("{:?}", s);
}
}
let mut set_profile = false;
let mut profile = Profile::default();
if cmd.create {
set_profile = true;
} else if let Some(ref name) = cmd.profile {
let profiles = dbus.proxies().profile().all_profile_data()?;
for p in profiles {
if p.name == *name {
profile = p;
break;
}
}
if profile.name != *name {
println!("The requested profile doesn't exist, you may need to create it");
std::process::exit(-1);
}
}
if let Some(turbo) = cmd.turbo {
set_profile = true;
profile.turbo = turbo;
}
if let Some(min) = cmd.min_percentage {
set_profile = true;
profile.min_percentage = min;
}
if let Some(max) = cmd.max_percentage {
set_profile = true;
profile.max_percentage = max;
}
if let Some(preset) = cmd.fan_preset {
set_profile = true;
profile.fan_preset = preset;
}
if let Some(ref curve) = cmd.curve {
set_profile = true;
profile.fan_curve = curve.as_config_string();
}
if let Some(ref name) = cmd.profile {
set_profile = true;
profile.name = name.clone();
}
if set_profile {
dbus.proxies().profile().new_or_modify(&profile)?;
}
Ok(())
}

View File

@@ -1,6 +1,4 @@
use gumdrop::Options;
use rog_fan_curve::{Curve, Fan};
use rog_profiles::profiles::FanLevel;
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
@@ -8,10 +6,6 @@ pub struct ProfileCommand {
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "create the profile if it doesn't exist")]
pub create: bool,
#[options(meta = "", help = "remove a profile by name")]
pub remove: Option<String>,
#[options(help = "list available profiles")]
pub list: bool,
#[options(help = "get active profile name")]
@@ -20,34 +14,4 @@ pub struct ProfileCommand {
pub active_data: bool,
#[options(help = "get all profile data")]
pub profiles_data: bool,
// Options for profile
#[options(meta = "", help = "enable or disable cpu turbo")]
pub turbo: Option<bool>,
#[options(meta = "", help = "set min cpu scaling (intel)")]
pub min_percentage: Option<u8>,
#[options(meta = "", help = "set max cpu scaling (intel)")]
pub max_percentage: Option<u8>,
#[options(meta = "", help = "<silent, normal, boost>")]
pub fan_preset: Option<FanLevel>,
#[options(
meta = "",
parse(try_from_str = "parse_fan_curve"),
help = "set fan curve"
)]
pub curve: Option<Curve>,
#[options(free)]
pub profile: Option<String>,
}
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
let curve = Curve::from_config_str(data)?;
if let Err(err) = curve.check_safety(Fan::Cpu) {
return Err(format!("Unsafe curve {:?}", err));
}
if let Err(err) = curve.check_safety(Fan::Gpu) {
return Err(format!("Unsafe curve {:?}", err));
}
Ok(curve)
}

View File

@@ -43,4 +43,3 @@ toml = "^0.5"
# Device control
sysfs-class = "^0.1.2" # used for backlight control and baord ID
rog_fan_curve = { version = "0.1", features = ["serde"] }

View File

@@ -1,8 +1,6 @@
use log::{error, info, warn};
use rog_profiles::profiles::{FanLevel, Profile};
use rog_types::gfx_vendors::GfxVendors;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
@@ -20,61 +18,18 @@ pub struct Config {
pub gfx_tmp_mode: Option<GfxVendors>,
pub gfx_managed: bool,
pub gfx_vfio_enable: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
#[serde(skip)]
pub curr_fan_mode: u8,
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub power_profiles: BTreeMap<String, Profile>,
}
impl Default for Config {
fn default() -> Self {
let mut pwr = BTreeMap::new();
pwr.insert(
"normal".into(),
Profile::new(
"normal".into(),
0,
100,
true,
FanLevel::Normal,
"".to_string(),
),
);
pwr.insert(
"boost".into(),
Profile::new(
"boost".into(),
0,
100,
true,
FanLevel::Boost,
"".to_string(),
),
);
pwr.insert(
"silent".into(),
Profile::new(
"silent".into(),
0,
100,
false,
FanLevel::Silent,
"".to_string(),
),
);
Config {
gfx_mode: GfxVendors::Hybrid,
gfx_tmp_mode: None,
gfx_managed: true,
gfx_vfio_enable: false,
active_profile: "normal".into(),
toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()],
curr_fan_mode: 0,
bat_charge_limit: 100,
power_profiles: pwr,
}
}
}
@@ -125,6 +80,7 @@ impl Config {
fn create_default(file: &mut File) -> Self {
let config = Config::default();
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string_pretty(&config).unwrap();
file.write_all(json.as_bytes())
@@ -146,7 +102,6 @@ impl Config {
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
// copy over serde skipped values
x.gfx_tmp_mode = self.gfx_tmp_mode;
x.curr_fan_mode = self.curr_fan_mode;
*self = x;
}
}

View File

@@ -1,257 +0,0 @@
use crate::VERSION;
use log::{error, info, warn};
use rog_anime::Fade;
use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2};
use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::time::Duration;
pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf";
pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV341 {
pub system: Option<ActionLoader>,
pub boot: Option<ActionLoader>,
pub suspend: Option<ActionLoader>,
pub shutdown: Option<ActionLoader>,
}
impl AnimeConfigV341 {
pub(crate) fn into_current(self) -> AnimeConfig {
AnimeConfig {
system: if let Some(ani) = self.system {
vec![ani]
} else {
vec![]
},
boot: if let Some(ani) = self.boot {
vec![ani]
} else {
vec![]
},
wake: if let Some(ani) = self.suspend {
vec![ani]
} else {
vec![]
},
shutdown: if let Some(ani) = self.shutdown {
vec![ani]
} else {
vec![]
},
brightness: 1.0,
awake_enabled: true,
boot_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV352 {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
impl AnimeConfigV352 {
pub(crate) fn into_current(self) -> AnimeConfig {
AnimeConfig {
system: self.system,
boot: self.boot,
wake: self.wake,
shutdown: self.shutdown,
brightness: 1.0,
awake_enabled: true,
boot_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize, Default)]
pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
impl AnimeConfigCached {
pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in config.system.iter() {
sys.push(ActionData::from_anime_action(ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in config.boot.iter() {
boot.push(ActionData::from_anime_action(ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in config.wake.iter() {
wake.push(ActionData::from_anime_action(ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in config.shutdown.iter() {
shutdown.push(ActionData::from_anime_action(ani)?);
}
self.shutdown = shutdown;
Ok(())
}
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize)]
pub struct AnimeConfig {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub awake_enabled: bool,
pub boot_anim_enabled: bool,
}
impl Default for AnimeConfig {
fn default() -> Self {
AnimeConfig {
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
shutdown: Vec::new(),
brightness: 1.0,
awake_enabled: true,
boot_anim_enabled: true,
}
}
}
impl AnimeConfig {
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load() -> Self {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&ANIME_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
ANIME_CONFIG_PATH
)
}); // okay to cause panic here
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return AnimeConfig::create_default(&mut file);
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV341>(&buf) {
let config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
return config;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV352>(&buf) {
let config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
return config;
}
AnimeConfig::write_backup(buf);
warn!(
"Could not deserialise {}. Backed up as *-old",
ANIME_CONFIG_PATH
);
}
}
AnimeConfig::create_default(&mut file)
}
fn create_default(file: &mut File) -> Self {
// create a default config here
let config = AnimeConfig {
system: vec![],
boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
wake: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
}],
shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
brightness: 1.0,
awake_enabled: true,
boot_anim_enabled: true,
};
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string_pretty(&config).unwrap();
file.write_all(json.as_bytes())
.unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH));
config
}
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(&ANIME_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", ANIME_CONFIG_PATH);
} else {
let x: AnimeConfig = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH));
*self = x;
}
}
}
pub fn write(&self) {
let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
file.write_all(json.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
fn write_backup(buf: String) {
let mut path = ANIME_CONFIG_PATH.to_string();
path.push_str("-old");
let mut file = File::create(&path).expect("Couldn't overwrite config");
file.write_all(buf.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
}

View File

@@ -1,267 +0,0 @@
use crate::laptops::LaptopLedData;
use log::{error, info, warn};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
#[derive(Deserialize, Serialize)]
pub struct AuraConfigV320 {
pub brightness: u32,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>,
}
impl AuraConfigV320 {
pub(crate) fn into_current(self) -> AuraConfig {
AuraConfig {
brightness: <LedBrightness>::from(self.brightness),
current_mode: self.current_mode,
builtins: self.builtins,
multizone: self.multizone,
awake_enabled: true,
sleep_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct AuraConfigV352 {
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>,
}
impl AuraConfigV352 {
pub(crate) fn into_current(self) -> AuraConfig {
AuraConfig {
brightness: self.brightness,
current_mode: self.current_mode,
builtins: self.builtins,
multizone: self.multizone,
awake_enabled: true,
sleep_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct AuraConfig {
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
pub multizone: Option<AuraMultiZone>,
pub awake_enabled: bool,
pub sleep_anim_enabled: bool,
}
impl Default for AuraConfig {
fn default() -> Self {
AuraConfig {
brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(),
multizone: None,
awake_enabled: true,
sleep_anim_enabled: true,
}
}
}
impl AuraConfig {
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load(supported_led_modes: &LaptopLedData) -> Self {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&AURA_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
AURA_CONFIG_PATH
)
}); // okay to cause panic here
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return AuraConfig::create_default(&mut file, supported_led_modes);
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<AuraConfigV320>(&buf) {
let config = data.into_current();
config.write();
info!("Updated AuraConfig version");
return config;
} else if let Ok(data) = serde_json::from_str::<AuraConfigV352>(&buf) {
let config = data.into_current();
config.write();
info!("Updated AuraConfig version");
return config;
}
warn!("Could not deserialise {}", AURA_CONFIG_PATH);
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
}
}
AuraConfig::create_default(&mut file, supported_led_modes)
}
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
// create a default config here
let mut config = AuraConfig::default();
for n in &support_data.standard {
config
.builtins
.insert(*n, AuraEffect::default_with_mode(*n));
}
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string(&config).unwrap();
file.write_all(json.as_bytes())
.unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH));
config
}
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(&AURA_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", AURA_CONFIG_PATH);
} else {
let x: AuraConfig = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
*self = x;
}
}
}
pub fn write(&self) {
let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
file.write_all(json.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
pub fn set_builtin(&mut self, effect: AuraEffect) {
match effect.zone() {
AuraZone::None => {
self.builtins.insert(*effect.mode(), effect);
}
_ => {
if let Some(multi) = self.multizone.as_mut() {
multi.set(effect)
}
}
}
}
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> {
if let Some(multi) = &self.multizone {
if aura_type == AuraModeNum::Static {
return Some(multi.static_());
} else if aura_type == AuraModeNum::Breathe {
return Some(multi.breathe());
}
}
None
}
}
#[derive(Deserialize, Serialize)]
pub struct AuraMultiZone {
static_: [AuraEffect; 4],
breathe: [AuraEffect; 4],
}
impl AuraMultiZone {
pub fn set(&mut self, effect: AuraEffect) {
if effect.mode == AuraModeNum::Static {
match effect.zone {
AuraZone::None => {}
AuraZone::One => self.static_[0] = effect,
AuraZone::Two => self.static_[1] = effect,
AuraZone::Three => self.static_[2] = effect,
AuraZone::Four => self.static_[3] = effect,
}
} else if effect.mode == AuraModeNum::Breathe {
match effect.zone {
AuraZone::None => {}
AuraZone::One => self.breathe[0] = effect,
AuraZone::Two => self.breathe[1] = effect,
AuraZone::Three => self.breathe[2] = effect,
AuraZone::Four => self.breathe[3] = effect,
}
}
}
pub fn static_(&self) -> &[AuraEffect; 4] {
&self.static_
}
pub fn breathe(&self) -> &[AuraEffect; 4] {
&self.breathe
}
}
impl Default for AuraMultiZone {
fn default() -> Self {
Self {
static_: [
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::One,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Two,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Three,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Four,
..Default::default()
},
],
breathe: [
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::One,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Two,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Three,
..Default::default()
},
AuraEffect {
mode: AuraModeNum::Breathe,
zone: AuraZone::Four,
..Default::default()
},
],
}
}
}

View File

@@ -1,5 +1,3 @@
use rog_fan_curve::Curve;
use rog_profiles::profiles::Profile;
use rog_types::gfx_vendors::GfxVendors;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
@@ -31,11 +29,7 @@ impl ConfigV317 {
gfx_tmp_mode: None,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
bat_charge_limit: self.bat_charge_limit,
power_profiles: ProfileV317::transform_map(self.power_profiles),
}
}
}
@@ -59,11 +53,7 @@ impl ConfigV324 {
gfx_tmp_mode: None,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
bat_charge_limit: self.bat_charge_limit,
power_profiles: ProfileV317::transform_map(self.power_profiles),
}
}
}
@@ -88,11 +78,7 @@ impl ConfigV341 {
gfx_tmp_mode: None,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
bat_charge_limit: self.bat_charge_limit,
power_profiles: ProfileV317::transform_map(self.power_profiles),
}
}
}
@@ -119,43 +105,32 @@ impl ConfigV352 {
gfx_tmp_mode: None,
gfx_managed: self.gfx_managed,
gfx_vfio_enable: false,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
curr_fan_mode: self.curr_fan_mode,
bat_charge_limit: self.bat_charge_limit,
power_profiles: ProfileV317::transform_map(self.power_profiles),
}
}
}
#[derive(Deserialize, Serialize)]
pub struct ConfigV372 {
pub gfx_mode: GfxVendors,
/// Only for informational purposes.
#[serde(skip)]
pub gfx_tmp_mode: Option<GfxVendors>,
pub gfx_managed: bool,
pub gfx_vfio_enable: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
#[serde(skip)]
pub curr_fan_mode: u8,
pub bat_charge_limit: u8,
pub power_profiles: BTreeMap<String, ProfileV317>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ProfileV317 {
pub min_percentage: u8,
pub max_percentage: u8,
pub turbo: bool,
pub fan_preset: u8,
pub fan_curve: Option<Curve>,
}
impl ProfileV317 {
fn into_current(self, name: String) -> Profile {
Profile {
name,
min_percentage: self.min_percentage,
max_percentage: self.max_percentage,
turbo: self.turbo,
fan_preset: self.fan_preset.into(),
fan_curve: self
.fan_curve
.map_or_else(|| "".to_string(), |c| c.as_config_string()),
}
}
fn transform_map(map: BTreeMap<String, ProfileV317>) -> BTreeMap<String, Profile> {
let mut new_map = BTreeMap::new();
map.iter().for_each(|(k, v)| {
new_map.insert(k.to_string(), v.clone().into_current(k.to_string()));
});
new_map
}
pub fan_curve: Option<()>,
}

View File

@@ -1,456 +0,0 @@
use log::{error, info, warn};
use logind_zbus::ManagerProxy;
use rog_anime::{
usb::{
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
VENDOR_ID,
},
ActionData, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
};
use rog_types::supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle};
use std::{
error::Error,
sync::{Arc, Mutex},
thread::sleep,
};
use std::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use zbus::{dbus_interface, Connection};
use zvariant::ObjectPath;
use crate::{
config_anime::{AnimeConfig, AnimeConfigCached},
error::RogError,
GetSupported,
};
impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
}
}
pub struct CtrlAnime {
handle: DeviceHandle<rusb::GlobalContext>,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl CtrlAnime {
#[inline]
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
// We don't expect this ID to ever change
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
let mut device = device.open()?;
device.reset()?;
device.set_auto_detach_kernel_driver(true).map_err(|err| {
error!("Auto-detach kernel driver failed: {}", err);
err
})?;
device.claim_interface(0).map_err(|err| {
error!("Could not claim device interface: {}", err);
err
})?;
info!("Device has an AniMe Matrix display");
let mut cache = AnimeConfigCached::default();
cache.init_from_config(&config)?;
let ctrl = CtrlAnime {
handle: device,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization();
Ok(ctrl)
}
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
for device in rusb::devices()?.iter() {
let device_desc = device.device_descriptor()?;
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
return Ok(device);
}
}
Err(rusb::Error::NoDevice)
}
/// Start an action thread. This is classed as a singleton and there should be only
/// one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display only) *must*
/// get the mutex lock and set the thread_exit atomic.
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop, including nested
// The only reason for this outer thread is to prevent blocking while waiting for the
// next spawned thread to exit
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
info!("AniMe system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main
let thread_exit;
let thread_running;
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
loop {
if let Ok(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
break;
}
}
loop {
// wait for other threads to set not running so we know they exited
if !thread_running.load(Ordering::SeqCst) {
thread_exit.store(false, Ordering::SeqCst);
info!("AniMe forced a thread to exit");
break;
}
}
'main: loop {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
for action in actions.iter() {
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, thread_exit.clone(), &|frame| {
if let Ok(lock) = inner.try_lock() {
lock.write_data_buffer(frame);
}
})
.unwrap();
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
}
ActionData::Image(image) => {
once = false;
if let Ok(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
}
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Ok(lock) = inner.try_lock() {
let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec());
lock.write_data_buffer(data);
}
// Loop ended, set the atmonics
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
fn write_bytes(&self, message: &[u8]) {
match self.handle.write_control(
0x21, // request_type
0x09, // request
0x35e, // value
0x00, // index
message,
Duration::from_millis(200),
) {
Ok(_) => {}
Err(err) => match err {
rusb::Error::Timeout => {}
_ => error!("Failed to write to led interrupt: {}", err),
},
}
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) {
for led in buffer.get_mut()[7..].iter_mut() {
let mut bright = *led as f32 * self.config.brightness;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::from(buffer);
for row in data.iter() {
self.write_bytes(row);
}
self.write_bytes(&pkt_for_flush());
}
fn do_initialization(&self) {
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]);
self.write_bytes(&pkts[1]);
}
}
pub struct CtrlAnimeTask<'a> {
inner: Arc<Mutex<CtrlAnime>>,
_c: Connection,
manager: ManagerProxy<'a>,
}
impl<'a> CtrlAnimeTask<'a> {
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
let connection = Connection::new_system().unwrap();
let manager = ManagerProxy::new(&connection).unwrap();
let c1 = inner.clone();
// Run this action when the system starts shutting down
manager
.connect_prepare_for_shutdown(move |shutdown| {
if shutdown {
'outer: loop {
if let Ok(lock) = c1.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false);
break 'outer;
}
}
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
let c1 = inner.clone();
// Run this action when the system wakes up from sleep
manager
.connect_prepare_for_sleep(move |sleep| {
if !sleep {
// wait a fraction for things to wake up properly
std::thread::sleep(Duration::from_millis(100));
'outer: loop {
if let Ok(lock) = c1.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true);
break 'outer;
}
}
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
Self {
inner,
_c: connection,
manager,
}
}
}
impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> {
fn do_task(&self) -> Result<(), RogError> {
if let Ok(mut lock) = self.inner.try_lock() {
// Refresh the config and cache incase the user has edited it
let config = AnimeConfig::load();
lock.cache
.init_from_config(&config)
.map_err(|err| {
warn!("CtrlAnimeTask: do_task {}", err);
err
})
.ok();
}
// Check for signals on each task iteration, this will run the callbacks
// if any signal is recieved
self.manager.next_signal()?;
Ok(())
}
}
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
impl crate::Reloadable for CtrlAnimeReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(lock) = self.0.try_lock() {
lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled));
lock.write_bytes(&pkt_for_apply());
lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled));
lock.write_bytes(&pkt_for_apply());
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true);
}
Ok(())
}
}
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusAdd for CtrlAnimeZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
self,
)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err
})
.ok();
}
}
// None of these calls can be guarnateed to succeed unless we loop until okay
// If the try_lock *does* succeed then any other thread trying to lock will not grab it
// until we finish.
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until it is restarted
fn write(&self, input: AnimeDataBuffer) {
'outer: loop {
if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
lock.write_data_buffer(input);
break 'outer;
}
}
}
/// Set the global AniMe brightness
fn set_brightness(&self, bright: f32) {
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
let mut bright = bright;
if bright < 0.0 {
bright = 0.0
} else if bright > 254.0 {
bright = 254.0;
}
lock.config.brightness = bright;
lock.config.write();
break 'outer;
}
}
}
/// Set whether the AniMe is displaying images/data
fn set_on_off(&self, status: bool) {
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
lock.write_bytes(&pkt_for_set_on(status));
lock.config.awake_enabled = status;
lock.config.write();
let states = AnimePowerStates {
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
};
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
break 'outer;
}
}
}
/// Set whether the AniMe will show boot, suspend, or off animations
fn set_boot_on_off(&self, on: bool) {
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
lock.write_bytes(&pkt_for_set_boot(on));
lock.write_bytes(&pkt_for_apply());
lock.config.boot_anim_enabled = on;
lock.config.write();
let states = AnimePowerStates {
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
};
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
break 'outer;
}
}
}
/// The main loop is the base system set action if the user isn't running
/// the user daemon
fn run_main_loop(&self, start: bool) {
if start {
'outer: loop {
if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
break 'outer;
}
}
}
}
/// Get status of if the AniMe LEDs are on
#[dbus_interface(property)]
fn awake_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.awake_enabled;
}
true
}
/// Get the status of if factory system-status animations are enabled
#[dbus_interface(property)]
fn boot_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.boot_anim_enabled;
}
true
}
/// Notify listeners of the status of AniMe LED power and factory system-status animations
#[dbus_interface(signal)]
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>;
}

View File

@@ -264,13 +264,16 @@ impl CtrlGraphics {
let unbinds = devices.iter().map(|dev| dev.unbind());
// Remove NVIDIA graphics devices and their functions
let removes = devices.iter().map(|dev| dev.remove());
unbinds.chain(removes).collect::<Result<_, _>>()
unbinds
.chain(removes)
.collect::<Result<_, _>>()
.map_err(|err| RogError::Command("device unbind error".into(), err))
}
fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), RogError> {
let unbinds = devices.iter().map(|dev| dev.unbind());
unbinds.collect::<Result<_, _>>()
unbinds
.collect::<Result<_, _>>()
.map_err(|err| RogError::Command("device unbind error".into(), err))
}

View File

@@ -4,7 +4,7 @@ pub mod controller;
pub mod system;
pub mod zbus_gfx;
pub mod zbus;
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];

View File

@@ -1,56 +0,0 @@
use ::zbus::dbus_interface;
use log::{error, info, warn};
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
use zvariant::ObjectPath;
use crate::ZbusAdd;
use super::controller::CtrlGraphics;
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlGraphics {
fn vendor(&self) -> zbus::fdo::Result<GfxVendors> {
self.get_gfx_mode().map_err(|err| {
error!("GFX: {}", err);
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
})
}
fn power(&self) -> zbus::fdo::Result<GfxPower> {
Self::get_runtime_status().map_err(|err| {
error!("GFX: {}", err);
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
})
}
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
info!("GFX: Switching gfx mode to {}", <&str>::from(vendor));
let msg = self.set_gfx_mode(vendor).map_err(|err| {
error!("GFX: {}", err);
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
})?;
self.notify_gfx(&vendor)
.unwrap_or_else(|err| warn!("GFX: {}", err));
self.notify_action(&msg)
.unwrap_or_else(|err| warn!("GFX: {}", err));
Ok(msg)
}
#[dbus_interface(signal)]
fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {}
#[dbus_interface(signal)]
fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {}
}
impl ZbusAdd for CtrlGraphics {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self)
.map_err(|err| {
warn!("GFX: CtrlGraphics: add_to_server {}", err);
err
})
.ok();
}
}

View File

@@ -1,394 +0,0 @@
// Only these two packets must be 17 bytes
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
use crate::{
config_aura::AuraConfig,
error::RogError,
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
CtrlTask,
};
use log::{info, warn};
use logind_zbus::ManagerProxy;
use rog_aura::{
usb::{
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
LED_AWAKE_ON_SLEEP_ON, LED_SET,
},
AuraEffect, LedBrightness, LED_MSG_LEN,
};
use rog_types::supported::LedSupportedFunctions;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::{fs::OpenOptions, thread::spawn};
use zbus::Connection;
use crate::GetSupported;
impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions;
fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode));
let multizone_led_mode = false;
let per_key_led_mode = false;
let laptop = LaptopLedData::get_data();
let stock_led_modes = laptop.standard;
LedSupportedFunctions {
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
stock_led_modes,
multizone_led_mode,
per_key_led_mode,
}
}
}
pub struct CtrlKbdLed {
pub led_node: Option<String>,
pub bright_node: String,
pub supported_modes: LaptopLedData,
pub flip_effect_write: bool,
pub config: AuraConfig,
}
pub struct CtrlKbdLedTask<'a> {
inner: Arc<Mutex<CtrlKbdLed>>,
_c: Connection,
manager: ManagerProxy<'a>,
}
impl<'a> CtrlKbdLedTask<'a> {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
let connection = Connection::new_system().unwrap();
let manager = ManagerProxy::new(&connection).unwrap();
let c1 = inner.clone();
// Run this action when the system wakes up from sleep
manager
.connect_prepare_for_sleep(move |sleep| {
if !sleep {
let c1 = c1.clone();
spawn(move || {
// wait a fraction for things to wake up properly
//std::thread::sleep(Duration::from_millis(100));
loop {
if let Ok(ref mut lock) = c1.try_lock() {
lock.set_brightness(lock.config.brightness).ok();
break;
}
}
});
}
Ok(())
})
.map_err(|err| {
warn!("CtrlAnimeTask: new() {}", err);
err
})
.ok();
Self {
inner,
_c: connection,
manager,
}
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let mut file = OpenOptions::new()
.read(true)
.open(&lock.bright_node)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
}
_ => RogError::Path((&lock.bright_node).into(), err),
})?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)
.map_err(|err| RogError::Read("buffer".into(), err))?;
if let Some(num) = char::from(buf[0]).to_digit(10) {
if lock.config.brightness != num.into() {
lock.config.read();
lock.config.brightness = num.into();
lock.config.write();
}
return Ok(());
}
Err(RogError::ParseLed)
}
}
impl<'a> CtrlTask for CtrlKbdLedTask<'a> {
fn do_task(&self) -> Result<(), RogError> {
self.manager.next_signal()?;
if let Ok(ref mut lock) = self.inner.try_lock() {
return Self::update_config(lock);
}
Ok(())
}
}
pub struct CtrlKbdLedReloader(pub Arc<Mutex<CtrlKbdLed>>);
impl crate::Reloadable for CtrlKbdLedReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut ctrl) = self.0.try_lock() {
let current = ctrl.config.current_mode;
if let Some(mode) = ctrl.config.builtins.get(&current).cloned() {
ctrl.do_command(mode).ok();
}
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled)
.map_err(|err| warn!("{}", err))
.ok();
}
Ok(())
}
}
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
impl CtrlKbdLedZbus {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
Self(inner)
}
}
impl CtrlKbdLed {
#[inline]
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
// TODO: return error if *all* nodes are None
let mut led_node = None;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
match Self::find_led_node(prod) {
Ok(node) => {
led_node = Some(node);
break;
}
Err(err) => warn!("led_node: {}", err),
}
}
let bright_node = Self::get_kbd_bright_path();
if led_node.is_none() && bright_node.is_none() {
return Err(RogError::MissingFunction(
"All keyboard features missing, you may require a v5.11 series kernel or newer"
.into(),
));
}
if bright_node.is_none() {
return Err(RogError::MissingFunction(
"No brightness control, you may require a v5.11 series kernel or newer".into(),
));
}
let ctrl = CtrlKbdLed {
led_node,
bright_node: bright_node.unwrap(), // If was none then we already returned above
supported_modes,
flip_effect_write: false,
config,
};
Ok(ctrl)
}
fn get_kbd_bright_path() -> Option<String> {
if Path::new(KBD_BRIGHT_PATH).exists() {
return Some(KBD_BRIGHT_PATH.to_string());
}
None
}
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
let mut file = OpenOptions::new()
.read(true)
.open(&self.bright_node)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
}
_ => RogError::Path((&self.bright_node).into(), err),
})?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)
.map_err(|err| RogError::Read("buffer".into(), err))?;
Ok(buf[0])
}
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
let path = Path::new(&self.bright_node);
let mut file =
OpenOptions::new()
.write(true)
.open(&path)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
}
_ => RogError::Path((&self.bright_node).into(), err),
})?;
file.write_all(&[brightness.as_char_code()])
.map_err(|err| RogError::Read("buffer".into(), err))?;
Ok(())
}
/// Set if awake/on LED active, and/or sleep animation active
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
let bytes = if awake && sleep {
LED_AWAKE_ON_SLEEP_ON
} else if awake && !sleep {
LED_AWAKE_ON_SLEEP_OFF
} else if !awake && sleep {
LED_AWAKE_OFF_SLEEP_ON
} else if !awake && !sleep {
LED_AWAKE_OFF_SLEEP_OFF
} else {
LED_AWAKE_ON_SLEEP_ON
};
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
Ok(())
}
fn find_led_node(id_product: &str) -> Result<String, RogError> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
RogError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
RogError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
RogError::Udev("scan_devices failed".into(), err)
})? {
if let Some(parent) = device
.parent_with_subsystem_devtype("usb", "usb_device")
.map_err(|err| {
warn!("{}", err);
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
})?
{
if parent
.attribute_value("idProduct")
.ok_or_else(|| RogError::NotFound("LED idProduct".into()))?
== id_product
{
if let Some(dev_node) = device.devnode() {
info!("Using device at: {:?} for LED control", dev_node);
return Ok(dev_node.to_string_lossy().to_string());
}
}
}
}
Err(RogError::MissingFunction(
"ASUS LED device node not found".into(),
))
}
pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> {
self.set_and_save(mode)
}
/// Should only be used if the bytes you are writing are verified correct
#[inline]
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(led_node) = &self.led_node {
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
// println!("write: {:02x?}", &message);
return file
.write_all(message)
.map_err(|err| RogError::Write("write_bytes".into(), err));
}
}
Err(RogError::NotSupported)
}
/// Write an effect block
#[inline]
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
if self.flip_effect_write {
for row in effect.iter().rev() {
self.write_bytes(row)?;
}
} else {
for row in effect.iter() {
self.write_bytes(row)?;
}
}
self.flip_effect_write = !self.flip_effect_write;
Ok(())
}
/// Used to set a builtin mode and save the settings for it
///
/// This needs to be universal so that settings applied by dbus stick
#[inline]
fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> {
self.config.read();
self.write_mode(&mode)?;
self.config.current_mode = *mode.mode();
self.config.set_builtin(mode);
self.config.write();
Ok(())
}
#[inline]
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
let current = self.config.current_mode;
if let Some(idx) = self
.supported_modes
.standard
.iter()
.position(|v| *v == current)
{
let mut idx = idx;
// goes past end of array
if reverse {
if idx == 0 {
idx = self.supported_modes.standard.len() - 1;
} else {
idx -= 1;
}
} else {
idx += 1;
if idx == self.supported_modes.standard.len() {
idx = 0;
}
}
let next = self.supported_modes.standard[idx];
self.config.read();
if let Some(data) = self.config.builtins.get(&next) {
self.write_mode(data)?;
self.config.current_mode = next;
}
self.config.write();
}
Ok(())
}
#[inline]
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.standard.contains(mode.mode()) {
return Err(RogError::NotSupported);
}
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
Ok(())
}
}

View File

@@ -1,2 +0,0 @@
pub mod controller;
pub mod zbus;

View File

@@ -1,165 +0,0 @@
use log::{error, warn};
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use super::controller::CtrlKbdLedZbus;
impl crate::ZbusAdd for CtrlKbdLedZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
.map_err(|err| {
error!("DbusKbdLed: add_to_server {}", err);
})
.ok();
}
}
/// The main interface for changing, reading, or notfying signals
///
/// LED commands are split between Brightness, Modes, Per-Key
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlKbdLedZbus {
/// Set the keyboard brightness level (0-3)
fn set_brightness(&mut self, brightness: LedBrightness) {
if let Ok(ctrl) = self.0.try_lock() {
ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err))
.ok();
}
}
/// Set the keyboard LED to enabled while the device is awake
fn set_awake_enabled(&mut self, enabled: bool) {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled)
.map_err(|err| warn!("{}", err))
.ok();
ctrl.config.awake_enabled = enabled;
ctrl.config.write();
let states = LedPowerStates {
enabled: ctrl.config.awake_enabled,
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
};
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
}
}
/// Set the keyboard LED suspend animation to enabled while the device is suspended
fn set_sleep_enabled(&mut self, enabled: bool) {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled)
.map_err(|err| warn!("{}", err))
.ok();
ctrl.config.sleep_anim_enabled = enabled;
ctrl.config.write();
let states = LedPowerStates {
enabled: ctrl.config.awake_enabled,
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
};
self.notify_power_states(&states)
.unwrap_or_else(|err| warn!("{}", err));
}
}
fn set_led_mode(&mut self, effect: AuraEffect) {
if let Ok(mut ctrl) = self.0.try_lock() {
match ctrl.do_command(effect) {
Ok(_) => {
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone())
.unwrap_or_else(|err| warn!("{}", err));
}
}
Err(err) => {
warn!("{}", err);
}
}
}
}
fn next_led_mode(&self) {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.toggle_mode(false)
.unwrap_or_else(|err| warn!("{}", err));
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone())
.unwrap_or_else(|err| warn!("{}", err));
}
}
}
fn prev_led_mode(&self) {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.toggle_mode(true)
.unwrap_or_else(|err| warn!("{}", err));
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
self.notify_led(mode.clone())
.unwrap_or_else(|err| warn!("{}", err));
}
}
}
#[dbus_interface(property)]
fn awake_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.awake_enabled;
}
true
}
#[dbus_interface(property)]
fn sleep_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.sleep_anim_enabled;
}
true
}
/// Return the current mode data
#[dbus_interface(property)]
fn led_mode(&self) -> String {
if let Ok(ctrl) = self.0.try_lock() {
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
if let Ok(json) = serde_json::to_string(&mode) {
return json;
}
}
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not deserialise".to_string()
}
/// Return a list of available modes
#[dbus_interface(property)]
fn led_modes(&self) -> String {
if let Ok(ctrl) = self.0.try_lock() {
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
return json;
}
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not serialise".to_string()
}
/// Return the current LED brightness
#[dbus_interface(property)]
fn led_brightness(&self) -> i8 {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
}
warn!("SetKeyBacklight could not serialise");
-1
}
#[dbus_interface(signal)]
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
#[dbus_interface(signal)]
fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>;
}

View File

@@ -1,116 +1,114 @@
use crate::error::RogError;
use crate::{config::Config, GetSupported};
use crate::GetSupported;
use log::{info, warn};
use rog_profiles::profiles::Profile;
use rog_types::supported::FanCpuSupportedFunctions;
use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurves, Profile};
use rog_types::supported::PlatformProfileFunctions;
use std::sync::Arc;
use std::sync::Mutex;
pub struct CtrlFanAndCpu {
pub config: Arc<Mutex<Config>>,
use super::config::ProfileConfig;
pub struct CtrlPlatformTask {
config: Arc<Mutex<ProfileConfig>>,
}
impl GetSupported for CtrlFanAndCpu {
type A = FanCpuSupportedFunctions;
impl CtrlPlatformTask {
pub fn new(config: Arc<Mutex<ProfileConfig>>) -> Self {
Self { config }
}
}
impl crate::CtrlTask for CtrlPlatformTask {
fn do_task(&self) -> Result<(), RogError> {
if let Ok(mut lock) = self.config.try_lock() {
// Refresh the config in-case the user has edited it
if let Some(curves) = &mut lock.fan_curves {
curves.update_from_platform();
}
}
Ok(())
}
}
pub struct CtrlPlatformProfile {
pub config: Arc<Mutex<ProfileConfig>>,
}
impl GetSupported for CtrlPlatformProfile {
type A = PlatformProfileFunctions;
fn get_supported() -> Self::A {
FanCpuSupportedFunctions {
stock_fan_modes: Profile::get_fan_path().is_ok(),
min_max_freq: Profile::get_intel_supported(),
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
if !Profile::is_platform_profile_supported() {
warn!(r#"
platform_profile kernel interface not found, your laptop does not support this, or the iterface is missing.
To enable profile support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/8/18/1022
"#);
}
if !FanCurves::is_fan_curves_supported() {
info!(r#"
fan curves kernel interface not found, your laptop does not support this, or the iterface is missing.
To enable fan-curve support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/8/20/232
Please note that as of 24/08/2021 this is not final.
"#);
}
PlatformProfileFunctions {
platform_profile: Profile::is_platform_profile_supported(),
fan_curves: FanCurves::is_fan_curves_supported(),
}
}
}
impl crate::Reloadable for CtrlFanAndCpu {
/// Fetcht he active profile and use that to set all related components up
impl crate::Reloadable for CtrlPlatformProfile {
/// Fetch the active profile and use that to set all related components up
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut cfg) = self.config.clone().try_lock() {
let active = cfg.active_profile.clone();
if let Some(existing) = cfg.power_profiles.get_mut(&active) {
existing.set_system_all()?;
if let Ok(cfg) = self.config.clone().try_lock() {
if let Some(curves) = &cfg.fan_curves {
curves.update_platform();
}
}
Ok(())
}
}
impl CtrlFanAndCpu {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
Profile::get_fan_path()?;
info!("Device has fan control available");
Ok(CtrlFanAndCpu { config })
impl CtrlPlatformProfile {
pub fn new(config: Arc<Mutex<ProfileConfig>>) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() {
info!("Device has profile control available");
return Ok(CtrlPlatformProfile { config });
}
Err(ProfileError::NotSupported.into())
}
/// Toggle to next profile in list
pub(super) fn do_next_profile(&mut self) -> Result<(), RogError> {
pub fn save_config(&self) {
if let Ok(lock) = self.config.lock() {
lock.write();
}
}
/// Toggle to next profile in list. This will first read the config, switch, then write out
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() {
// Read first just incase the user has modified the config before calling this
config.read();
let mut toggle_index = config
.toggle_profiles
.binary_search(&config.active_profile)
.unwrap_or(0)
+ 1;
if toggle_index >= config.toggle_profiles.len() {
toggle_index = 0;
match config.active {
Profile::Balanced => {
Profile::set_profile(Profile::Performance);
config.active = Profile::Performance;
}
Profile::Performance => {
Profile::set_profile(Profile::Quiet);
config.active = Profile::Quiet;
}
Profile::Quiet => {
Profile::set_profile(Profile::Balanced);
config.active = Profile::Balanced;
}
}
let profile = config.toggle_profiles[toggle_index].clone();
if let Some(existing) = config.power_profiles.get(&profile) {
existing.set_system_all()?;
config.active_profile = existing.name.clone();
config.write();
info!("Profile was changed to: {}", &profile);
} else {
warn!(
"toggle_profile {} does not exist in power_profiles",
&profile
);
return Err(RogError::MissingProfile(profile.to_string()));
}
}
Ok(())
}
pub(super) fn set_active(&mut self, profile: &str) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() {
// Read first just incase the user has modified the config before calling this
config.read();
if let Some(existing) = config.power_profiles.get(profile) {
existing.set_system_all()?;
config.active_profile = existing.name.clone();
config.write();
info!("Profile was changed to: {}", profile);
} else {
warn!(
"toggle_profile {} does not exist in power_profiles",
profile
);
return Err(RogError::MissingProfile(profile.to_string()));
}
}
Ok(())
}
/// Create a new profile if the requested name doesn't exist, or modify existing
pub(super) fn new_or_modify(&mut self, profile: &Profile) -> Result<(), RogError> {
if let Ok(mut config) = self.config.clone().try_lock() {
config.read();
if let Some(existing) = config.power_profiles.get_mut(&profile.name) {
*existing = profile.clone();
existing.set_system_all()?;
} else {
config
.power_profiles
.insert(profile.name.clone(), profile.clone());
profile.set_system_all()?;
}
config.active_profile = profile.name.clone();
config.write();
}
Ok(())

View File

@@ -1,3 +1,3 @@
pub mod zbus;
pub mod config;
pub mod controller;
pub mod zbus;

View File

@@ -1,58 +1,54 @@
use log::warn;
use rog_profiles::profiles::Profile;
use rog_profiles::FanCurve;
use rog_profiles::Profile;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::{dbus_interface, fdo::Error};
use zvariant::ObjectPath;
use super::controller::CtrlFanAndCpu;
use super::controller::CtrlPlatformProfile;
pub struct FanAndCpuZbus {
inner: Arc<Mutex<CtrlFanAndCpu>>,
static UNSUPPORTED_MSG: &str =
"Fan curves are not supported on this laptop or you require a patched kernel";
pub struct ProfileZbus {
inner: Arc<Mutex<CtrlPlatformProfile>>,
}
impl FanAndCpuZbus {
pub fn new(inner: Arc<Mutex<CtrlFanAndCpu>>) -> Self {
impl ProfileZbus {
pub fn new(inner: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { inner }
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl FanAndCpuZbus {
/// Create new profile and make active
fn set_profile(&self, profile: String) {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.set_active(&profile)
.unwrap_or_else(|err| warn!("{}", err));
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(profiles) = Profile::get_profile_names() {
return Ok(profiles);
}
self.do_notification();
Err(Error::Failed(
"Failed to get all profile details".to_string(),
))
}
/// New or modify profile details and make active, will create if it does not exist
fn new_or_modify(&self, profile: Profile) {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.new_or_modify(&profile)
.unwrap_or_else(|err| warn!("{}", err));
}
self.do_notification();
}
/// Fetch the active profile name
/// Toggle to next platform_profile. Names provided by `Profiles`
fn next_profile(&mut self) {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.do_next_profile()
ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err));
}
self.do_notification();
}
/// Fetch the active profile name
fn active_name(&mut self) -> zbus::fdo::Result<String> {
fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
return Ok(cfg.active_profile.clone());
return Ok(cfg.active);
}
}
Err(Error::Failed(
@@ -60,94 +56,96 @@ impl FanAndCpuZbus {
))
}
// TODO: Profile can't implement Type because of Curve
/// Fetch the active profile details
fn active_data(&mut self) -> zbus::fdo::Result<Profile> {
/// Set this platform_profile name as active
fn set_active_profile(&self, profile: Profile) {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
// Read first just incase the user has modified the config before calling this
cfg.read();
Profile::set_profile(profile);
cfg.active = profile;
}
ctrl.save_config();
}
self.do_notification();
}
/// Get a list of profiles that have fan-curves enabled.
fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
return Ok(profile.clone());
if let Some(curves) = &cfg.fan_curves {
return Ok(curves.get_enabled_curve_names().to_vec());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
}
Err(Error::Failed(
"Failed to get active profile details".to_string(),
"Failed to get enabled fan curve names".to_string(),
))
}
/// Fetch all profile data
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
/// Get the fan-curve data for the currently active Profile
fn active_fan_curve_data(&mut self) -> zbus::fdo::Result<FanCurve> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
return Ok(cfg.power_profiles.values().cloned().collect());
if let Some(curves) = &cfg.fan_curves {
return Ok((*curves.get_active_fan_curves()).clone());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
}
Err(Error::Failed(
"Failed to get all profile details".to_string(),
))
Err(Error::Failed("Failed to get fan curve data".to_string()))
}
fn profile_names(&self) -> zbus::fdo::Result<Vec<String>> {
/// Get fan-curve data for each Profile as an array of objects
fn fan_curves(&self) -> zbus::fdo::Result<Vec<FanCurve>> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
let profile_names = cfg.power_profiles.keys().cloned().collect::<Vec<String>>();
return Ok(profile_names);
if let Some(curves) = &cfg.fan_curves {
return Ok(curves.get_all_fan_curves());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
}
Err(Error::Failed("Failed to get all profile names".to_string()))
Err(Error::Failed("Failed to get all fan curves".to_string()))
}
fn remove(&self, profile: &str) -> zbus::fdo::Result<()> {
/// Set this fan-curve data
fn set_fan_curve(&self, curve: FanCurve) -> zbus::fdo::Result<()> {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if !cfg.power_profiles.contains_key(profile) {
return Err(Error::Failed("Invalid profile specified".to_string()));
if let Some(curves) = &mut cfg.fan_curves {
curves.set_fan_curve(curve);
}
if cfg.power_profiles.keys().len() == 1 {
return Err(Error::Failed("Cannot delete the last profile".to_string()));
}
if cfg.active_profile == *profile {
return Err(Error::Failed(
"Cannot delete the active profile".to_string(),
));
}
cfg.power_profiles.remove(profile);
cfg.write();
return Ok(());
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
ctrl.save_config();
}
Err(Error::Failed("Failed to lock configuration".to_string()))
Err(Error::Failed("Failed to set fan curves".to_string()))
}
#[dbus_interface(signal)]
fn notify_profile(&self, profile: &Profile) -> zbus::Result<()> {}
}
impl FanAndCpuZbus {
impl ProfileZbus {
fn do_notification(&self) {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() {
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
self.notify_profile(profile)
.unwrap_or_else(|err| warn!("{}", err));
}
self.notify_profile(&cfg.active)
.unwrap_or_else(|err| warn!("{}", err));
}
}
}
}
impl crate::ZbusAdd for FanAndCpuZbus {
impl crate::ZbusAdd for ProfileZbus {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(

View File

@@ -5,20 +5,20 @@ use zvariant::ObjectPath;
use zvariant_derive::Type;
use crate::{
ctrl_anime::CtrlAnime, ctrl_charge::CtrlCharge, ctrl_leds::controller::CtrlKbdLed,
ctrl_profiles::controller::CtrlFanAndCpu, ctrl_rog_bios::CtrlRogBios, GetSupported,
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge,
ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported,
};
use rog_types::supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, FanCpuSupportedFunctions,
LedSupportedFunctions, RogBiosSupportedFunctions,
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
PlatformProfileFunctions, RogBiosSupportedFunctions,
};
#[derive(Serialize, Deserialize, Type)]
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
pub fan_cpu_ctrl: FanCpuSupportedFunctions,
pub platform_profile: PlatformProfileFunctions,
pub keyboard_led: LedSupportedFunctions,
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
@@ -53,7 +53,7 @@ impl GetSupported for SupportedFunctions {
anime_ctrl: CtrlAnime::get_supported(),
keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlCharge::get_supported(),
fan_cpu_ctrl: CtrlFanAndCpu::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlRogBios::get_supported(),
}
}

View File

@@ -1,33 +1,38 @@
use daemon::ctrl_leds::controller::{
use daemon::ctrl_anime::config::AnimeConfig;
use daemon::ctrl_anime::zbus::CtrlAnimeZbus;
use daemon::ctrl_aura::config::AuraConfig;
use daemon::ctrl_aura::controller::{
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
};
use daemon::ctrl_charge::CtrlCharge;
use daemon::ctrl_profiles::config::ProfileConfig;
use daemon::ctrl_profiles::controller::CtrlPlatformTask;
use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
};
use daemon::{config_anime::AnimeConfig, config_aura::AuraConfig, ctrl_charge::CtrlCharge};
use daemon::{ctrl_anime::*, ctrl_gfx::controller::CtrlGraphics};
use daemon::{
ctrl_profiles::{controller::CtrlFanAndCpu, zbus::FanAndCpuZbus},
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
laptops::LaptopLedData,
};
use ::zbus::{fdo, Connection, ObjectServer};
use daemon::{CtrlTask, Reloadable, ZbusAdd};
use log::LevelFilter;
use log::{error, info, warn};
use rog_dbus::DBUS_NAME;
use rog_types::gfx_vendors::GfxVendors;
use std::env;
use std::error::Error;
use std::io::Write;
use std::sync::Arc;
use std::sync::Mutex;
use std::env;
use daemon::ctrl_rog_bios::CtrlRogBios;
use std::convert::Into;
use zbus::fdo;
use zbus::Connection;
use zvariant::ObjectPath;
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
@@ -43,7 +48,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
if !is_service {
println!("asusd schould be only run from the right systemd service");
println!("do not run in your terminal, if you need an logs please use journalctl -b -u asusd");
println!(
"do not run in your terminal, if you need an logs please use journalctl -b -u asusd"
);
println!("asusd will now exit");
return Ok(());
}
@@ -71,7 +78,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
let connection = Connection::new_system()?;
fdo::DBusProxy::new(&connection)?
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut object_server = zbus::ObjectServer::new(&connection);
let mut object_server = ObjectServer::new(&connection);
let config = Config::load();
let enable_gfx_switching = config.gfx_managed;
@@ -105,12 +112,16 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
match CtrlFanAndCpu::new(config.clone()) {
let profile_config = Arc::new(Mutex::new(ProfileConfig::load(PROFILE_CONFIG_PATH.into())));
match CtrlPlatformProfile::new(profile_config.clone()) {
Ok(mut ctrl) => {
ctrl.reload()
.unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl));
FanAndCpuZbus::new(tmp).add_to_server(&mut object_server);
ProfileZbus::new(tmp).add_to_server(&mut object_server);
tasks.push(Box::new(CtrlPlatformTask::new(profile_config)));
}
Err(err) => {
error!("Profile control: {}", err);

View File

@@ -1,4 +1,3 @@
use rog_fan_curve::CurveError;
use rog_profiles::error::ProfileError;
use rog_types::error::GraphicsError;
use std::convert::From;
@@ -17,7 +16,6 @@ pub enum RogError {
Write(String, std::io::Error),
NotSupported,
NotFound(String),
FanCurve(CurveError),
DoTask(String),
MissingFunction(String),
MissingLedBrightNode(String, std::io::Error),
@@ -44,7 +42,6 @@ impl fmt::Display for RogError {
RogError::Write(path, error) => write!(f, "Write {}: {}", path, error),
RogError::NotSupported => write!(f, "Not supported"),
RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
RogError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
@@ -62,12 +59,6 @@ impl fmt::Display for RogError {
impl std::error::Error for RogError {}
impl From<CurveError> for RogError {
fn from(err: CurveError) -> Self {
RogError::FanCurve(err)
}
}
impl From<GraphicsError> for RogError {
fn from(err: GraphicsError) -> Self {
match err {

View File

@@ -1,17 +1,15 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
pub mod config_anime;
pub mod config_aura;
pub(crate) mod config_old;
/// Control of AniMe matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control of battery charge level
pub mod ctrl_charge;
/// GPU switching and power
pub mod ctrl_gfx;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_leds;
/// Control CPU min/max freq and turbo, fan mode, fan curves
///
/// Intel machines can control:

View File

@@ -16,4 +16,4 @@ rog_profiles = { path = "../rog-profiles" }
rog_types = { path = "../rog-types" }
zbus = "^1.9"
zbus_macros = "^1.9"
zvariant = "^2.5"
zvariant = "^2.8"

View File

@@ -12,7 +12,7 @@ pub mod zbus_supported;
use rog_anime::AnimePowerStates;
use rog_aura::{AuraEffect, LedPowerStates};
use rog_profiles::profiles::Profile;
use rog_profiles::Profile;
use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
use std::sync::mpsc::{channel, Receiver};
use zbus::{Connection, Result, SignalReceiver};

View File

@@ -21,7 +21,7 @@
use std::sync::mpsc::Sender;
use rog_profiles::profiles::Profile;
use rog_profiles::{FanCurve, Profile};
use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy(
@@ -29,26 +29,29 @@ use zbus::{dbus_proxy, Connection, Result};
default_path = "/org/asuslinux/Profile"
)]
trait Daemon {
/// Profiles method
fn profiles(&self) -> zbus::Result<Vec<Profile>>;
/// NextProfile method
fn next_profile(&self) -> zbus::Result<()>;
/// Profile, get the active profile
fn active_name(&self) -> zbus::Result<String>;
fn active_profile(&self) -> zbus::Result<Profile>;
/// Set the specific profile as active
fn set_active_profile(&self, profile: Profile) -> zbus::Result<()>;
/// Get enabled fan curves
fn enabled_fan_profiles(&self) -> zbus::Result<Vec<Profile>>;
/// Get the active `Profile` data
fn active_data(&self) -> zbus::Result<Profile>;
fn active_fan_data(&self) -> zbus::Result<FanCurve>;
/// Profiles method
fn profiles(&self) -> zbus::Result<Vec<Profile>>;
/// Get all fan curve data
fn fan_curves(&self) -> zbus::Result<Vec<FanCurve>>;
/// ProfileNames method
fn profile_names(&self) -> zbus::Result<Vec<String>>;
/// Remove method
fn remove(&self, profile: &str) -> zbus::Result<()>;
/// SetProfile method
fn new_or_modify(&self, profile: &Profile) -> zbus::Result<()>;
/// Set a fan curve. If a field is empty then the exisiting saved curve is used
fn set_fan_curve(&self, curve: FanCurve) -> zbus::Result<()>;
/// NotifyProfile signal
#[dbus_proxy(signal)]
@@ -69,40 +72,15 @@ impl<'a> ProfileProxy<'a> {
}
#[inline]
pub fn active_name(&self) -> Result<String> {
self.0.active_name()
}
#[inline]
pub fn active_data(&self) -> Result<Profile> {
self.0.active_data()
}
#[inline]
pub fn all_profile_data(&self) -> Result<Vec<Profile>> {
pub fn profiles(&self) -> Result<Vec<Profile>> {
self.0.profiles()
}
#[inline]
pub fn next_fan(&self) -> Result<()> {
pub fn next_profile(&self) -> Result<()> {
self.0.next_profile()
}
#[inline]
pub fn profile_names(&self) -> Result<Vec<String>> {
self.0.profile_names()
}
#[inline]
pub fn remove(&self, profile: &str) -> Result<()> {
self.0.remove(profile)
}
#[inline]
pub fn new_or_modify(&self, profile: &Profile) -> Result<()> {
self.0.new_or_modify(profile)
}
#[inline]
pub fn connect_notify_profile(&self, send: Sender<Profile>) -> zbus::fdo::Result<()> {
self.0.connect_notify_profile(move |data| {

View File

@@ -9,10 +9,8 @@ default = ["dbus"]
dbus = ["zvariant", "zvariant_derive"]
[dependencies]
rog_fan_curve = { git = "https://github.com/Yarn/rog_fan_curve.git" }
serde = "^1.0"
serde_derive = "^1.0"
intel-pstate = "^0.2"
zvariant = { version = "^2.6", optional = true }
zvariant_derive = { version = "^2.6", optional = true }

View File

@@ -1,18 +1,12 @@
use std::fmt;
use intel_pstate::PStateError;
use rog_fan_curve::CurveError;
#[derive(Debug)]
pub enum ProfileError {
ParseFanLevel,
Path(String, std::io::Error),
Read(String, std::io::Error),
Write(String, std::io::Error),
NotSupported,
NotFound(String),
IntelPstate(PStateError),
FanCurve(CurveError),
Io(std::io::Error),
//Zbus(zbus::Error),
}
@@ -21,14 +15,11 @@ impl fmt::Display for ProfileError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ProfileError::ParseFanLevel => write!(f, "Parse profile error"),
ProfileError::Path(path, error) => write!(f, "Path {}: {}", path, error),
ProfileError::Read(path, error) => write!(f, "Read {}: {}", path, error),
ProfileError::Write(path, error) => write!(f, "Write {}: {}", path, error),
ProfileError::NotSupported => write!(f, "Not supported"),
ProfileError::NotFound(deets) => write!(f, "Not found: {}", deets),
ProfileError::IntelPstate(err) => write!(f, "Intel pstate error: {}", err),
ProfileError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
ProfileError::Io(detail) => write!(f, "std::io error: {}", detail),
//Error::Zbus(detail) => write!(f, "Zbus error: {}", detail),
}
@@ -36,15 +27,3 @@ impl fmt::Display for ProfileError {
}
impl std::error::Error for ProfileError {}
impl From<PStateError> for ProfileError {
fn from(err: PStateError) -> Self {
ProfileError::IntelPstate(err)
}
}
impl From<CurveError> for ProfileError {
fn from(err: CurveError) -> Self {
ProfileError::FanCurve(err)
}
}

View File

@@ -1,8 +1,284 @@
pub mod error;
pub mod profiles;
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
use std::{
fs::OpenOptions,
io::{Read, Write},
path::{Path, PathBuf},
};
use error::ProfileError;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile";
pub static PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices";
pub static FAN_CURVE_BASE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/";
pub static FAN_CURVE_ACTIVE_FILE: &str = "enabled_fan_curve_profiles";
pub static FAN_CURVE_FILENAME_PART: &str = "_fan_curve_";
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum Profile {
Balanced,
Performance,
Quiet,
}
impl Profile {
pub fn is_platform_profile_supported() -> bool {
Path::new(PLATFORM_PROFILES).exists()
}
pub fn get_active_profile() -> Result<Profile, ProfileError> {
let mut file = OpenOptions::new()
.read(true)
.open(&PLATFORM_PROFILE)
.unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILE));
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
Ok(buf.as_str().into())
}
pub fn get_profile_names() -> Result<Vec<Profile>, ProfileError> {
let mut file = OpenOptions::new()
.read(true)
.open(&PLATFORM_PROFILES)
.unwrap_or_else(|_| panic!("{} not found", &PLATFORM_PROFILES));
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
Ok(buf.rsplit(' ').map(|p| p.into()).collect())
}
pub fn set_profile(profile: Profile) {
let mut file = OpenOptions::new()
.write(true)
.open(PLATFORM_PROFILE)
.unwrap_or_else(|_| panic!("{} not found", PLATFORM_PROFILE));
file.write_all(<&str>::from(profile).as_bytes()).unwrap();
}
}
impl Default for Profile {
fn default() -> Self {
Self::Balanced
}
}
impl From<Profile> for &str {
fn from(profile: Profile) -> &'static str {
match profile {
Profile::Balanced => "balanced",
Profile::Performance => "performance",
Profile::Quiet => "quiet",
}
}
}
impl From<&str> for Profile {
fn from(profile: &str) -> Profile {
match profile.to_ascii_lowercase().trim() {
"balanced" => Profile::Balanced,
"performance" => Profile::Performance,
"quiet" => Profile::Quiet,
_ => Profile::Balanced,
}
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum FanCurvePU {
CPU,
GPU,
}
impl From<FanCurvePU> for &str {
fn from(pu: FanCurvePU) -> &'static str {
match pu {
FanCurvePU::CPU => "cpu",
FanCurvePU::GPU => "gpu",
}
}
}
impl Default for FanCurvePU {
fn default() -> Self {
Self::CPU
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct FanCurve {
pub profile: Profile,
pub cpu: String,
pub gpu: String,
}
/// Main purpose of `FanCurves` is to enable retoring state on system boot
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug)]
pub struct FanCurves {
active_curves: Vec<Profile>,
balanced: FanCurve,
performance: FanCurve,
quiet: FanCurve,
}
impl Default for FanCurves {
fn default() -> Self {
let mut curves = Self {
active_curves: Default::default(),
balanced: Default::default(),
performance: Default::default(),
quiet: Default::default(),
};
curves.balanced.profile = Profile::Balanced;
curves.performance.profile = Profile::Performance;
curves.quiet.profile = Profile::Quiet;
curves
}
}
impl FanCurves {
pub fn is_fan_curves_supported() -> bool {
let mut path = PathBuf::new();
path.push(FAN_CURVE_BASE_PATH);
path.push(FAN_CURVE_ACTIVE_FILE);
path.exists()
}
pub fn update_from_platform(&mut self) {
self.balanced.cpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::CPU);
self.balanced.gpu = Self::get_fan_curve_from_file(Profile::Balanced, FanCurvePU::GPU);
self.performance.cpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::CPU);
self.performance.gpu = Self::get_fan_curve_from_file(Profile::Performance, FanCurvePU::GPU);
self.quiet.cpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::CPU);
self.quiet.gpu = Self::get_fan_curve_from_file(Profile::Quiet, FanCurvePU::GPU);
}
pub fn update_platform(&self) {
Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::CPU, &self.balanced.cpu);
Self::set_fan_curve_for_platform(Profile::Balanced, FanCurvePU::GPU, &self.balanced.gpu);
Self::set_fan_curve_for_platform(
Profile::Performance,
FanCurvePU::CPU,
&self.performance.cpu,
);
Self::set_fan_curve_for_platform(
Profile::Performance,
FanCurvePU::GPU,
&self.performance.gpu,
);
Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::CPU, &self.quiet.cpu);
Self::set_fan_curve_for_platform(Profile::Quiet, FanCurvePU::GPU, &self.quiet.gpu);
}
pub fn get_enabled_curve_names(&self) -> &[Profile] {
&self.active_curves
}
pub fn get_all_fan_curves(&self) -> Vec<FanCurve> {
vec![
self.balanced.clone(),
self.performance.clone(),
self.quiet.clone(),
]
}
pub fn get_active_fan_curves(&self) -> &FanCurve {
match Profile::get_active_profile().unwrap() {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
}
}
pub fn get_fan_curves_for(&self, name: Profile) -> &FanCurve {
match name {
Profile::Balanced => &self.balanced,
Profile::Performance => &self.performance,
Profile::Quiet => &self.quiet,
}
}
fn get_fan_curve_from_file(name: Profile, pu: FanCurvePU) -> String {
let mut file: String = FAN_CURVE_BASE_PATH.into();
file.push_str(pu.into());
file.push_str(FAN_CURVE_FILENAME_PART);
file.push_str(name.into());
let mut file = OpenOptions::new()
.read(true)
.open(&file)
.unwrap_or_else(|_| panic!("{} not found", &file));
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
buf.trim().to_string()
}
pub fn get_fan_curve_for(&self, name: &Profile, pu: &FanCurvePU) -> &str {
match name {
Profile::Balanced => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
Profile::Performance => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
Profile::Quiet => match pu {
FanCurvePU::CPU => &self.balanced.cpu,
FanCurvePU::GPU => &self.balanced.gpu,
},
}
}
fn set_fan_curve_for_platform(name: Profile, pu: FanCurvePU, curve: &str) {
let mut file: String = FAN_CURVE_BASE_PATH.into();
file.push_str(pu.into());
file.push_str(FAN_CURVE_FILENAME_PART);
file.push_str(name.into());
let mut file = OpenOptions::new()
.write(true)
.open(&file)
.unwrap_or_else(|_| panic!("{} not found", &file));
file.write_all(curve.as_bytes()).unwrap();
}
pub fn set_fan_curve(&mut self, curve: FanCurve) {
// First, set the profiles.
Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::CPU, &curve.cpu);
match curve.profile {
Profile::Balanced => self.balanced.cpu = curve.cpu,
Profile::Performance => self.performance.cpu = curve.cpu,
Profile::Quiet => self.quiet.cpu = curve.cpu,
};
Self::set_fan_curve_for_platform(curve.profile, FanCurvePU::GPU, &curve.gpu);
match curve.profile {
Profile::Balanced => self.balanced.gpu = curve.gpu,
Profile::Performance => self.performance.gpu = curve.gpu,
Profile::Quiet => self.quiet.cpu = curve.gpu,
};
// Any curve that was blank will have been reset, so repopulate the settings
// Note: successfully set curves will just be re-read in.
self.update_from_platform();
}
}

View File

@@ -1,185 +0,0 @@
use rog_fan_curve::{Curve, Fan};
use serde_derive::{Deserialize, Serialize};
use std::io::Write;
use std::{fs::OpenOptions, path::Path, str::FromStr};
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
use crate::{error::ProfileError, AMD_BOOST_PATH, FAN_TYPE_1_PATH, FAN_TYPE_2_PATH};
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Profile {
pub name: String,
pub min_percentage: u8,
pub max_percentage: u8,
pub turbo: bool,
pub fan_preset: FanLevel,
pub fan_curve: String,
}
impl Default for Profile {
fn default() -> Self {
Profile {
name: "new".into(),
min_percentage: 0,
max_percentage: 100,
turbo: false,
fan_preset: FanLevel::Normal,
fan_curve: "".to_string(),
}
}
}
impl Profile {
pub fn new(
name: String,
min_percentage: u8,
max_percentage: u8,
turbo: bool,
fan_preset: FanLevel,
fan_curve: String,
) -> Self {
Profile {
name,
min_percentage,
max_percentage,
turbo,
fan_preset,
fan_curve,
}
}
pub fn get_intel_supported() -> bool {
intel_pstate::PState::new().is_ok()
}
pub fn get_fan_path() -> Result<&'static str, ProfileError> {
if Path::new(FAN_TYPE_1_PATH).exists() {
Ok(FAN_TYPE_1_PATH)
} else if Path::new(FAN_TYPE_2_PATH).exists() {
Ok(FAN_TYPE_2_PATH)
} else {
Err(ProfileError::NotSupported)
}
}
pub fn set_system_pstate(&self) -> Result<(), ProfileError> {
// Set CPU pstate
if let Ok(pstate) = intel_pstate::PState::new() {
pstate.set_min_perf_pct(self.min_percentage)?;
pstate.set_max_perf_pct(self.max_percentage)?;
pstate.set_no_turbo(!self.turbo)?;
} else {
// must be AMD CPU
let mut file = OpenOptions::new()
.write(true)
.open(AMD_BOOST_PATH)
.map_err(|err| ProfileError::Path(AMD_BOOST_PATH.into(), err))?;
let boost = if self.turbo { "1" } else { "0" }; // opposite of Intel
file.write_all(boost.as_bytes())
.map_err(|err| ProfileError::Write(AMD_BOOST_PATH.into(), err))?;
}
Ok(())
}
pub fn set_system_fan_mode(&self) -> Result<(), ProfileError> {
let path = Profile::get_fan_path()?;
let mut fan_ctrl = OpenOptions::new()
.write(true)
.open(path)
.map_err(|err| ProfileError::Path(path.into(), err))?;
fan_ctrl
.write_all(format!("{}\n", <u8>::from(self.fan_preset)).as_bytes())
.map_err(|err| ProfileError::Write(path.into(), err))?;
Ok(())
}
pub fn set_system_fan_curve(&self) -> Result<(), ProfileError> {
if !self.fan_curve.is_empty() {
if let Ok(curve) = Profile::parse_fan_curve(&self.fan_curve) {
use rog_fan_curve::Board;
if let Some(board) = Board::from_board_name() {
curve.apply(board, Fan::Cpu)?;
curve.apply(board, Fan::Gpu)?;
}
}
}
Ok(())
}
pub fn set_system_all(&self) -> Result<(), ProfileError> {
self.set_system_pstate()?;
if self.fan_curve.is_empty() {
self.set_system_fan_mode()?;
} else {
self.set_system_fan_curve()?;
}
Ok(())
}
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
let curve = Curve::from_config_str(data)?;
if let Err(err) = curve.check_safety(Fan::Cpu) {
return Err(format!("Unsafe curve {:?}", err));
}
if let Err(err) = curve.check_safety(Fan::Gpu) {
return Err(format!("Unsafe curve {:?}", err));
}
Ok(curve)
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum FanLevel {
Normal,
Boost,
Silent,
}
impl FromStr for FanLevel {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"normal" => Ok(FanLevel::Normal),
"boost" => Ok(FanLevel::Boost),
"silent" => Ok(FanLevel::Silent),
_ => Err("Invalid fan level"),
}
}
}
impl From<u8> for FanLevel {
fn from(n: u8) -> Self {
match n {
0 => FanLevel::Normal,
1 => FanLevel::Boost,
2 => FanLevel::Silent,
_ => FanLevel::Normal,
}
}
}
impl From<FanLevel> for u8 {
fn from(n: FanLevel) -> Self {
match n {
FanLevel::Normal => 0,
FanLevel::Boost => 1,
FanLevel::Silent => 2,
}
}
}
impl From<&FanLevel> for u8 {
fn from(n: &FanLevel) -> Self {
match n {
FanLevel::Normal => 0,
FanLevel::Boost => 1,
FanLevel::Silent => 2,
}
}
}

View File

@@ -7,7 +7,7 @@ use zvariant_derive::Type;
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
pub fan_cpu_ctrl: FanCpuSupportedFunctions,
pub platform_profile: PlatformProfileFunctions,
pub keyboard_led: LedSupportedFunctions,
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
@@ -21,10 +21,9 @@ pub struct ChargeSupportedFunctions {
}
#[derive(Serialize, Deserialize, Type, Debug)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
pub struct PlatformProfileFunctions {
pub platform_profile: bool,
pub fan_curves: bool,
}
#[derive(Serialize, Deserialize, Type, Debug)]
@@ -45,7 +44,7 @@ impl fmt::Display for SupportedFunctions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", self.anime_ctrl)?;
writeln!(f, "{}", self.charge_ctrl)?;
writeln!(f, "{}", self.fan_cpu_ctrl)?;
writeln!(f, "{}", self.platform_profile)?;
writeln!(f, "{}", self.keyboard_led)?;
writeln!(f, "{}", self.rog_bios_ctrl)
}
@@ -67,12 +66,11 @@ impl fmt::Display for ChargeSupportedFunctions {
)
}
}
impl fmt::Display for FanCpuSupportedFunctions {
impl fmt::Display for PlatformProfileFunctions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Fan:")?;
writeln!(f, "\tStock fan modes: {}", self.stock_fan_modes)?;
writeln!(f, "\tMin/max frequency: {}", self.min_max_freq)?;
writeln!(f, "\tFan curve control: {}", self.fan_curve_set)
writeln!(f, "Platform profiles:")?;
writeln!(f, "\tplatform: {}", self.platform_profile)?;
writeln!(f, "\tfan curves: {}", self.fan_curves)
}
}
impl fmt::Display for LedSupportedFunctions {