CLI args for bios. Cleanup and improve

- dbus method for 'supported modes'
- add dedicated gfx safety
- bring ctrl-gfx back in to main control for better integration
- safely upgrade config files
This commit is contained in:
Luke
2021-01-23 20:40:02 +13:00
parent 703bba9ffd
commit 82900f4645
28 changed files with 993 additions and 215 deletions

View File

@@ -1,16 +1,51 @@
use asus_nb::aura_modes::AuraModes;
use log::{error, warn};
use log::{error, info, warn};
use rog_fan_curve::Curve;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use crate::VERSION;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
/// for parsing old v2.1.2 config
#[derive(Deserialize)]
struct ConfigV212 {
gfx_managed: bool,
active_profile: String,
toggle_profiles: Vec<String>,
// TODO: remove power_profile
power_profile: u8,
bat_charge_limit: u8,
kbd_led_brightness: u8,
kbd_backlight_mode: u8,
kbd_backlight_modes: Vec<AuraModes>,
power_profiles: BTreeMap<String, Profile>,
}
impl ConfigV212 {
fn into_current(self) -> Config {
Config {
gfx_managed: self.gfx_managed,
gfx_nv_mode_is_dedicated: true,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
power_profile: self.power_profile,
bat_charge_limit: self.bat_charge_limit,
kbd_led_brightness: self.kbd_led_brightness,
kbd_backlight_mode: self.kbd_backlight_mode,
kbd_backlight_modes: self.kbd_backlight_modes,
power_profiles: self.power_profiles,
}
}
}
#[derive(Default, Deserialize, Serialize)]
pub struct Config {
pub gfx_managed: bool,
pub gfx_nv_mode_is_dedicated: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
// TODO: remove power_profile
@@ -24,7 +59,7 @@ pub struct Config {
impl Config {
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load(mut self, supported_led_modes: &[u8]) -> Self {
pub fn load(supported_led_modes: &[u8]) -> Self {
let mut file = OpenOptions::new()
.read(true)
.write(true)
@@ -35,23 +70,30 @@ impl Config {
CONFIG_PATH
)); // okay to cause panic here
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
self = Config::create_default(&mut file, &supported_led_modes);
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return Config::create_default(&mut file, &supported_led_modes);
} else {
self = serde_json::from_str(&buf).unwrap_or_else(|_| {
warn!("Could not deserialise {}", CONFIG_PATH);
panic!("Please remove {} then restart asusd", CONFIG_PATH);
});
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} else if let Ok(data) = serde_json::from_str::<ConfigV212>(&buf) {
let config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
return config;
}
warn!("Could not deserialise {}", CONFIG_PATH);
panic!("Please remove {} then restart asusd", CONFIG_PATH);
}
}
self
Config::create_default(&mut file, &supported_led_modes)
}
fn create_default(file: &mut File, supported_led_modes: &[u8]) -> Self {
// create a default config here
let mut config = Config::default();
config.gfx_managed = true;
config.gfx_nv_mode_is_dedicated = true;
config.bat_charge_limit = 100;
config.kbd_backlight_mode = 0;

View File

@@ -21,6 +21,20 @@ use std::error::Error;
use std::time::Duration;
use zbus::dbus_interface;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct AnimeSupportedFunctions(bool);
impl GetSupported for CtrlAnimeDisplay {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(0x0b05, 0x193b).is_ok())
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum AnimatrixCommand {

View File

@@ -1,6 +1,7 @@
use crate::{config::Config, error::RogError};
use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::Write;
@@ -11,8 +12,22 @@ use zbus::dbus_interface;
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
#[derive(Serialize, Deserialize)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
impl GetSupported for CtrlCharge {
type A = ChargeSupportedFunctions;
fn get_supported() -> Self::A {
ChargeSupportedFunctions {
charge_level_set: CtrlCharge::get_battery_path().is_ok(),
}
}
}
pub struct CtrlCharge {
path: &'static str,
config: Arc<Mutex<Config>>,
}
@@ -43,7 +58,7 @@ impl CtrlCharge {
}
#[dbus_interface(signal)]
pub fn notify_charge(&self, limit: u8) -> zbus::Result<()>;
pub fn notify_charge(&self, limit: u8) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for CtrlCharge {
@@ -62,7 +77,6 @@ impl crate::Reloadable for CtrlCharge {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.try_lock() {
config.read();
info!("Reloaded battery charge limit");
self.set(config.bat_charge_limit, &mut config)?;
}
Ok(())
@@ -71,9 +85,8 @@ impl crate::Reloadable for CtrlCharge {
impl CtrlCharge {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let path = CtrlCharge::get_battery_path()?;
info!("Device has battery charge threshold control");
Ok(CtrlCharge { path, config })
CtrlCharge::get_battery_path()?;
Ok(CtrlCharge { config })
}
fn get_battery_path() -> Result<&'static str, RogError> {
@@ -97,10 +110,10 @@ impl CtrlCharge {
let mut file = OpenOptions::new()
.write(true)
.open(self.path)
.map_err(|err| RogError::Path(self.path.into(), err))?;
.open(BAT_CHARGE_PATH)
.map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?;
file.write_all(limit.to_string().as_bytes())
.map_err(|err| RogError::Write(self.path.into(), err))?;
.map_err(|err| RogError::Write(BAT_CHARGE_PATH.into(), err))?;
info!("Battery charge limit: {}", limit);
config.read();

View File

@@ -1,6 +1,10 @@
use crate::config::{Config, Profile};
use crate::{
config::{Config, Profile},
GetSupported,
};
use asus_nb::profile::ProfileEvent;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::{Read, Write};
@@ -19,6 +23,25 @@ pub struct CtrlFanAndCPU {
config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
}
impl GetSupported for CtrlFanAndCPU {
type A = FanCpuSupportedFunctions;
fn get_supported() -> Self::A {
FanCpuSupportedFunctions {
stock_fan_modes: CtrlFanAndCPU::get_fan_path().is_ok(),
min_max_freq: intel_pstate::PState::new().is_ok(),
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
}
}
}
pub struct DbusFanAndCpu {
inner: Arc<Mutex<CtrlFanAndCPU>>,
}
@@ -101,7 +124,7 @@ impl DbusFanAndCpu {
}
#[dbus_interface(signal)]
fn notify_profile(&self, profile: &str) -> zbus::Result<()>;
fn notify_profile(&self, profile: &str) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for DbusFanAndCpu {

View File

@@ -0,0 +1,30 @@
use std::error;
use std::fmt;
#[derive(Debug)]
pub enum GfxError {
ParseVendor,
Path(String, std::io::Error),
Read(String, std::io::Error),
Write(String, std::io::Error),
Module(String, std::io::Error),
Bus(String, std::io::Error),
Command(String, std::io::Error),
}
impl fmt::Display for GfxError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
GfxError::Module(func, error) => write!(f, "Module error: {}: {}", func, error),
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
}
}
}
impl error::Error for GfxError {}

View File

@@ -0,0 +1,384 @@
use ctrl_gfx::error::GfxError;
use ctrl_gfx::*;
use ctrl_rog_bios::CtrlRogBios;
use log::{error, info, warn};
use std::io::Write;
use std::iter::FromIterator;
use std::path::Path;
use std::process::Command;
use std::str::FromStr;
use std::{error::Error, sync::Arc, sync::Mutex};
use sysfs_class::{PciDevice, SysClass};
use system::{GraphicsDevice, Module, PciBus};
use vendors::{GfxCtrlAction, GfxVendors};
use zbus::dbus_interface;
use crate::*;
pub struct CtrlGraphics {
bus: PciBus,
_amd: Vec<GraphicsDevice>,
_intel: Vec<GraphicsDevice>,
nvidia: Vec<GraphicsDevice>,
#[allow(dead_code)]
other: Vec<GraphicsDevice>,
initfs_cmd: Option<Command>,
config: Arc<Mutex<Config>>,
}
trait Dbus {
fn vendor(&self) -> String;
fn power(&self) -> String;
fn set_vendor(&mut self, vendor: String);
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()>;
fn notify_action(&self, action: &str) -> zbus::Result<()>;
}
use std::convert::TryInto;
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl Dbus for CtrlGraphics {
fn vendor(&self) -> String {
Self::get_vendor().unwrap_or_else(|err| format!("Get vendor failed: {}", err))
}
fn power(&self) -> String {
Self::get_runtime_status().unwrap_or_else(|err| format!("Get power status failed: {}", err))
}
fn set_vendor(&mut self, vendor: String) {
if let Ok(tmp) = GfxVendors::from_str(&vendor) {
let action = self.set(tmp).unwrap_or_else(|err| {
warn!("{}", err);
format!("Failed: {}", err.to_string())
});
self.notify_gfx(&vendor)
.unwrap_or_else(|err| warn!("{}", err));
self.notify_action(&action)
.unwrap_or_else(|err| warn!("{}", err));
}
}
#[dbus_interface(signal)]
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()> {}
#[dbus_interface(signal)]
fn notify_action(&self, action: &str) -> zbus::Result<()> {}
}
impl CtrlGraphics {
pub fn new(config: Arc<Mutex<Config>>) -> std::io::Result<CtrlGraphics> {
let bus = PciBus::new()?;
info!("Rescanning PCI bus");
bus.rescan()?;
let devs = PciDevice::all()?;
let functions = |parent: &PciDevice| -> Vec<PciDevice> {
let mut functions = Vec::new();
if let Some(parent_slot) = parent.id().split('.').next() {
for func in devs.iter() {
if let Some(func_slot) = func.id().split('.').next() {
if func_slot == parent_slot {
info!("{}: Function for {}", func.id(), parent.id());
functions.push(func.clone());
}
}
}
}
functions
};
let mut amd = Vec::new();
let mut intel = Vec::new();
let mut nvidia = Vec::new();
let mut other = Vec::new();
for dev in devs.iter() {
let c = dev.class()?;
if 0x03 == (c >> 16) & 0xFF {
match dev.vendor()? {
0x1002 => {
info!("{}: AMD graphics", dev.id());
amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
}
0x10DE => {
info!("{}: NVIDIA graphics", dev.id());
nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
}
0x8086 => {
info!("{}: Intel graphics", dev.id());
intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
}
vendor => {
info!("{}: Other({:X}) graphics", dev.id(), vendor);
other.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
}
}
}
}
let mut initfs_cmd = None;
if Path::new(INITRAMFS_PATH).exists() {
let mut cmd = Command::new("update-initramfs");
cmd.arg("-u");
initfs_cmd = Some(cmd);
info!("Using initramfs update command 'update-initramfs'");
}
// } else if Path::new(DRACUT_PATH).exists() {
// let mut cmd = Command::new("dracut");
// cmd.arg("-f");
// initfs_cmd = Some(cmd);
// info!("Using initramfs update command 'dracut'");
// }
Ok(CtrlGraphics {
bus,
_amd: amd,
_intel: intel,
nvidia,
other,
initfs_cmd,
config,
})
}
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(
&"/org/asuslinux/Gfx".try_into().expect("Some fail here"),
self,
)
.map_err(|err| {
warn!("CtrlGraphics: add_to_server {}", err);
err
})
.ok();
}
pub fn reload(&mut self) -> Result<(), Box<dyn Error>> {
self.auto_power()?;
info!("Reloaded gfx mode: {:?}", CtrlGraphics::get_vendor()?);
Ok(())
}
fn get_prime_discrete() -> Result<String, GfxError> {
let s = std::fs::read_to_string(PRIME_DISCRETE_PATH)
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?
.trim()
.to_owned();
Ok(s)
}
fn set_prime_discrete(mode: &str) -> Result<(), GfxError> {
std::fs::write(PRIME_DISCRETE_PATH, mode)
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?;
Ok(())
}
/// Associated method to get which vendor mode is set
pub fn get_vendor() -> Result<String, GfxError> {
let mode = match Self::get_prime_discrete() {
Ok(m) => m,
Err(_) => "nvidia".to_string(),
};
let modules = Module::all().map_err(|err| GfxError::Read("get_vendor".into(), err))?;
let driver_loaded = if modules
.iter()
.any(|module| module.name == "nouveau" || module.name == "nvidia")
{
true
} else {
false
};
let vendor = if mode == "off" {
if driver_loaded {
info!("dGPU driver loaded for compute mode");
"compute".to_string()
} else {
info!("No dGPU driver loaded");
"integrated".to_string()
}
} else {
info!("Assuming dGPU driver loaded");
if mode == "on-demand" {
"hybrid".to_string()
} else {
"nvidia".to_string()
}
};
Ok(vendor)
}
pub fn is_switching_prime_modes(vendor: &GfxVendors) -> Result<bool, GfxError> {
let prev_mode = GfxVendors::from_str(&Self::get_vendor()?)?;
let x = (prev_mode == GfxVendors::Hybrid || prev_mode == GfxVendors::Nvidia)
&& (*vendor == GfxVendors::Hybrid || *vendor == GfxVendors::Nvidia);
Ok(x)
}
pub fn set_gfx_config(vendor: GfxVendors) -> Result<(), GfxError> {
let mode = if vendor == GfxVendors::Hybrid {
"on-demand\n"
} else if vendor == GfxVendors::Nvidia {
"on\n"
} else {
// Integrated or Compute
"off\n"
};
info!("Setting {} to {}", PRIME_DISCRETE_PATH, mode);
Self::set_prime_discrete(mode)?;
{
info!("Writing {}", MODPROBE_PATH);
let mut file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(MODPROBE_PATH)
.map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;
let text = if vendor == GfxVendors::Hybrid {
MODPROBE_HYBRID
} else if vendor == GfxVendors::Compute {
MODPROBE_COMPUTE
} else if vendor == GfxVendors::Nvidia {
MODPROBE_NVIDIA
} else {
MODPROBE_INTEGRATED
};
file.write_all(text)
.and_then(|_| file.sync_all())
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
}
info!("Writing {}", PRIMARY_GPU_XORG_PATH);
// begin section for non-separated Nvidia xorg modules
// eg, not put in their own directory
let mut file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(PRIMARY_GPU_XORG_PATH)
.map_err(|err| GfxError::Write(PRIMARY_GPU_XORG_PATH.into(), err))?;
let text = if vendor == GfxVendors::Nvidia {
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
} else {
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
};
file.write_all(&text)
.and_then(|_| file.sync_all())
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
let action = if vendor == GfxVendors::Nvidia {
info!("Enabling nvidia-fallback.service");
"enable"
} else {
info!("Disabling nvidia-fallback.service");
"disable"
};
let status = Command::new("systemctl")
.arg(action)
.arg("nvidia-fallback.service")
.status()
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
if !status.success() {
// Error is ignored in case this service is removed
warn!(
"systemctl: {} (ignore warning if service does not exist!)",
status
);
}
Ok(())
}
/// Write out config files if required, enable/disable relevant services, and update the ramdisk
pub fn set(&mut self, vendor: GfxVendors) -> Result<String, GfxError> {
if CtrlRogBios::has_dedicated_gfx_toggle() {
if let Ok(config) = self.config.clone().try_lock() {
// Switch to dedicated if config says to do so
if config.gfx_nv_mode_is_dedicated && vendor == GfxVendors::Nvidia {
CtrlRogBios::set_gfx_mode(true)
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
} else if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
// otherwise if switching to non-Nvidia mode turn off dedicated mode
if ded == 1 && vendor != GfxVendors::Nvidia {
CtrlRogBios::set_gfx_mode(false)
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
}
}
}
}
// Switching from hybrid to/from nvidia shouldn't require a ramdisk update
// or a reboot.
let no_reboot = Self::is_switching_prime_modes(&vendor)?;
Self::set_gfx_config(vendor)?;
let mut required_action = GfxCtrlAction::None;
if !no_reboot {
info!("Updating initramfs");
if let Some(cmd) = self.initfs_cmd.as_mut() {
let status = cmd
.status()
.map_err(|err| GfxError::Write(format!("{:?}", cmd), err))?;
if !status.success() {
error!("Ram disk update failed");
} else {
info!("Successfully updated iniramfs");
}
}
required_action = GfxCtrlAction::Reboot;
} else if no_reboot {
required_action = GfxCtrlAction::RestartX;
}
Ok(required_action.into())
}
pub fn get_runtime_status() -> Result<String, GfxError> {
const PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
let buf = std::fs::read_to_string(PATH).map_err(|err| GfxError::Read(PATH.into(), err))?;
Ok(buf)
}
fn set_power(&self, power: bool) -> Result<(), GfxError> {
if power {
info!("Enabling graphics power");
self.bus
.rescan()
.map_err(|err| GfxError::Bus("bus rescan error".into(), err))?;
} else {
info!("Disabling graphics power");
// Unbind NVIDIA graphics devices and their functions
let unbinds = self.nvidia.iter().map(|dev| dev.unbind());
// Remove NVIDIA graphics devices and their functions
let removes = self.nvidia.iter().map(|dev| dev.remove());
Result::from_iter(unbinds.chain(removes))
.map_err(|err| GfxError::Command("device unbind error".into(), err))?;
}
Ok(())
}
fn auto_power(&self) -> Result<(), GfxError> {
let vendor = CtrlGraphics::get_vendor()?;
self.set_power(vendor != "integrated")
}
}

View File

@@ -0,0 +1,57 @@
pub mod vendors;
pub mod error;
pub mod gfx;
pub mod system;
const PRIME_DISCRETE_PATH: &str = "/etc/prime-discrete";
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
// const DRACUT_PATH: &str = "/usr/bin/dracut";
static MODPROBE_NVIDIA: &[u8] = MODPROBE_HYBRID;
static MODPROBE_HYBRID: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
alias i2c_nvidia_gpu off
options nvidia NVreg_DynamicPowerManagement=0x02
options nvidia-drm modeset=1
"#;
static MODPROBE_COMPUTE: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
alias i2c_nvidia_gpu off
options nvidia NVreg_DynamicPowerManagement=0x02
options nvidia-drm modeset=0
"#;
static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
blacklist nouveau
blacklist nvidia
blacklist nvidia-drm
blacklist nvidia-modeset
alias i2c_nvidia_gpu off
alias nouveau off
alias nvidia off
alias nvidia-drm off
alias nvidia-modeset off
"#;
const PRIMARY_GPU_XORG_PATH: &str = "/etc/X11/xorg.conf.d/90-nvidia-primary.conf";
static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd
Section "OutputClass"
Identifier "nvidia"
MatchDriver "nvidia-drm"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration"
Option "AllowExternalGpus""#;
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
Option "PrimaryGPU" "true""#;
static PRIMARY_GPU_END: &[u8] = br#"
EndSection"#;

View File

@@ -0,0 +1,127 @@
use log::{error, info, warn};
use std::fs::read_to_string;
use std::{fs::write, io, path::PathBuf};
use sysfs_class::{PciDevice, SysClass};
pub struct Module {
pub name: String,
}
impl Module {
fn parse(line: &str) -> io::Result<Module> {
let mut parts = line.split(' ');
let name = parts
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
Ok(Module {
name: name.to_string(),
})
}
pub fn all() -> io::Result<Vec<Module>> {
let mut modules = Vec::new();
let data = read_to_string("/proc/modules")?;
for line in data.lines() {
let module = Module::parse(line)?;
modules.push(module);
}
Ok(modules)
}
}
pub struct PciBus {
path: PathBuf,
}
impl PciBus {
pub fn new() -> io::Result<PciBus> {
let path = PathBuf::from("/sys/bus/pci");
if path.is_dir() {
Ok(PciBus { path })
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"pci directory not found",
))
}
}
pub fn rescan(&self) -> io::Result<()> {
write(self.path.join("rescan"), "1")
}
}
pub struct GraphicsDevice {
id: String,
functions: Vec<PciDevice>,
}
impl GraphicsDevice {
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
GraphicsDevice { id, functions }
}
pub fn exists(&self) -> bool {
self.functions.iter().any(|func| func.path().exists())
}
pub fn unbind(&self) -> Result<(), std::io::Error> {
for func in self.functions.iter() {
if func.path().exists() {
match func.driver() {
Ok(driver) => {
info!("{}: Unbinding {}", driver.id(), func.id());
unsafe {
driver.unbind(&func).map_err(|err| {
error!("gfx unbind: {}", err);
err
})?;
}
}
Err(err) => match err.kind() {
io::ErrorKind::NotFound => (),
_ => {
error!("gfx driver: {:?}, {}", func.path(), err);
return Err(err);
}
},
}
}
}
Ok(())
}
pub fn remove(&self) -> Result<(), std::io::Error> {
for func in self.functions.iter() {
if func.path().exists() {
match func.driver() {
Ok(driver) => {
error!("{}: in use by {}", func.id(), driver.id());
}
Err(why) => match why.kind() {
std::io::ErrorKind::NotFound => {
info!("{}: Removing", func.id());
unsafe {
// ignore errors and carry on
if let Err(err) = func.remove() {
error!("gfx remove: {}", err);
}
}
}
_ => {
error!("Remove device failed");
}
},
}
} else {
warn!("{}: Already removed", func.id());
}
}
info!("Removed all gfx devices");
Ok(())
}
}

View File

@@ -0,0 +1,86 @@
#[derive(Debug, PartialEq, Clone)]
pub enum GfxVendors {
Nvidia,
Integrated,
Compute,
Hybrid,
}
use std::str::FromStr;
use super::error::GfxError;
impl FromStr for GfxVendors {
type Err = GfxError;
fn from_str(s: &str) -> Result<Self, GfxError> {
match s.to_lowercase().as_str() {
"nvidia" => Ok(GfxVendors::Nvidia),
"hybrid" => Ok(GfxVendors::Hybrid),
"compute" => Ok(GfxVendors::Compute),
"integrated" => Ok(GfxVendors::Integrated),
"nvidia\n" => Ok(GfxVendors::Nvidia),
"hybrid\n" => Ok(GfxVendors::Hybrid),
"compute\n" => Ok(GfxVendors::Compute),
"integrated\n" => Ok(GfxVendors::Integrated),
_ => Err(GfxError::ParseVendor),
}
}
}
impl From<&GfxVendors> for &str {
fn from(mode: &GfxVendors) -> Self {
match mode {
GfxVendors::Nvidia => "nvidia",
GfxVendors::Hybrid => "hybrid",
GfxVendors::Compute => "compute",
GfxVendors::Integrated => "integrated",
}
}
}
#[derive(Debug)]
pub enum GfxCtrlAction {
Reboot,
RestartX,
None,
}
impl FromStr for GfxCtrlAction {
type Err = GfxError;
fn from_str(s: &str) -> Result<Self, GfxError> {
match s.to_lowercase().as_str() {
"reboot" => Ok(GfxCtrlAction::Reboot),
"restartx" => Ok(GfxCtrlAction::RestartX),
"none" => Ok(GfxCtrlAction::None),
_ => Err(GfxError::ParseVendor),
}
}
}
impl From<&GfxCtrlAction> for &str {
fn from(mode: &GfxCtrlAction) -> Self {
match mode {
GfxCtrlAction::Reboot => "reboot",
GfxCtrlAction::RestartX => "restartx",
GfxCtrlAction::None => "none",
}
}
}
impl From<&GfxCtrlAction> for String {
fn from(mode: &GfxCtrlAction) -> Self {
match mode {
GfxCtrlAction::Reboot => "reboot".into(),
GfxCtrlAction::RestartX => "restartx".into(),
GfxCtrlAction::None => "none".into(),
}
}
}
impl From<GfxCtrlAction> for String {
fn from(mode: GfxCtrlAction) -> Self {
(&mode).into()
}
}

View File

@@ -4,8 +4,17 @@ static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
use crate::{config::Config, error::RogError, laptops::HELP_ADDRESS};
use asus_nb::{aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, LED_MSG_LEN};
use crate::{
config::Config,
error::RogError,
laptops::{match_laptop, HELP_ADDRESS},
};
use asus_nb::{
aura_brightness_bytes,
aura_modes::{AuraModes, PER_KEY},
fancy::KeyColourArray,
LED_MSG_LEN,
};
use log::{info, warn};
use std::fs::OpenOptions;
use std::io::{Read, Write};
@@ -14,6 +23,44 @@ use std::sync::Mutex;
use std::{convert::TryInto, path::Path};
use zbus::dbus_interface;
use crate::GetSupported;
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct LedSupportedFunctions {
pub brightness_set: bool,
pub stock_led_modes: Option<Vec<u8>>,
pub per_key_led_mode: bool,
}
impl GetSupported for CtrlKbdBacklight {
type A = LedSupportedFunctions;
fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode));
let mut stock_led_modes = None;
let mut per_key_led_mode = false;
if let Some(laptop) = match_laptop() {
let modes = laptop.supported_modes().to_vec();
if modes.contains(&PER_KEY) {
per_key_led_mode = true;
let modes = modes
.iter()
.filter(|x| **x != PER_KEY)
.map(|x| *x)
.collect();
stock_led_modes = Some(modes);
}
}
LedSupportedFunctions {
brightness_set: CtrlKbdBacklight::get_kbd_bright_path().is_ok(),
stock_led_modes,
per_key_led_mode,
}
}
}
pub struct CtrlKbdBacklight {
led_node: Option<String>,
#[allow(dead_code)]

View File

@@ -0,0 +1,261 @@
use crate::{
config::Config,
ctrl_gfx::{gfx::CtrlGraphics, vendors::GfxVendors},
error::RogError,
GetSupported,
};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
static ASUS_SWITCH_GRAPHIC_MODE: &str =
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
static ASUS_POST_LOGO_SOUND: &str =
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
pub struct CtrlRogBios {
config: Arc<Mutex<Config>>,
}
#[derive(Serialize, Deserialize)]
pub struct RogBiosSupportedFunctions {
pub post_sound_toggle: bool,
pub dedicated_gfx_toggle: bool,
}
impl GetSupported for CtrlRogBios {
type A = RogBiosSupportedFunctions;
fn get_supported() -> Self::A {
RogBiosSupportedFunctions {
post_sound_toggle: CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND).is_ok(),
dedicated_gfx_toggle: CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok(),
}
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlRogBios {
pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) {
Self::set_gfx_mode(dedicated)
.map_err(|err| {
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
err
})
.ok();
self.notify_dedicated_graphic_mode(dedicated)
.map_err(|err| {
warn!("CtrlRogBios: notify_asus_switch_graphic_mode {}", err);
err
})
.ok();
}
pub fn dedicated_graphic_mode(&self) -> i8 {
Self::get_gfx_mode()
.map_err(|err| {
warn!("CtrlRogBios: get_gfx_mode {}", err);
err
})
.unwrap_or(-1)
}
#[dbus_interface(signal)]
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
// // // // // // // // // //
pub fn set_post_boot_sound(&mut self, on: bool) {
Self::set_boot_sound(on)
.map_err(|err| {
warn!("CtrlRogBios: set_post_boot_sound {}", err);
err
})
.ok();
self.notify_post_boot_sound(on)
.map_err(|err| {
warn!("CtrlRogBios: notify_post_boot_sound {}", err);
err
})
.ok();
}
pub fn post_boot_sound(&self) -> i8 {
Self::get_boot_sound()
.map_err(|err| {
warn!("CtrlRogBios: get_boot_sound {}", err);
err
})
.unwrap_or(-1)
}
#[dbus_interface(signal)]
pub fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for CtrlRogBios {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/RogBios".try_into().unwrap(), self)
.map_err(|err| {
warn!("CtrlRogBios: add_to_server {}", err);
err
})
.ok();
}
}
impl crate::Reloadable for CtrlRogBios {
fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}
impl CtrlRogBios {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
match CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE) {
Ok(_) => {
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
}
Err(err) => {
info!("ROG Switchable Graphics (bios) not detected: {}", err);
}
}
match CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND) {
Ok(_) => {
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
}
Err(err) => {
info!("ROG boot sound toggle (bios) not detected: {}", err);
}
}
Ok(CtrlRogBios { config })
}
fn set_path_mutable(path: &str) -> Result<(), RogError> {
use std::process::Command;
let output = Command::new("/usr/bin/chattr")
.arg("-i")
.arg(path)
.output()
.map_err(|err| RogError::Path(path.into(), err))?;
info!("Set {} writeable: status: {}", path, output.status);
Ok(())
}
fn check_path_exists(path: &str) -> Result<(), RogError> {
if Path::new(path).exists() {
Ok(())
} else {
Err(RogError::MissingFunction(path.into()))
}
}
pub fn has_dedicated_gfx_toggle() -> bool {
if CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok() {
return true;
}
false
}
pub fn get_gfx_mode() -> Result<i8, RogError> {
let path = ASUS_SWITCH_GRAPHIC_MODE;
let mut file = OpenOptions::new()
.read(true)
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
file.read_to_end(&mut data)
.map_err(|err| RogError::Read(path.into(), err))?;
let idx = data.len() - 1;
Ok(data[idx] as i8)
}
pub(super) fn set_gfx_mode(dedicated: bool) -> Result<(), RogError> {
let path = ASUS_SWITCH_GRAPHIC_MODE;
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let idx = data.len() - 1;
if dedicated {
data[idx] = 1;
info!("Set system-level graphics mode: Dedicated Nvidia");
} else {
data[idx] = 0;
info!("Set system-level graphics mode: Optimus");
}
file.write_all(&data)
.map_err(|err| RogError::Path(path.into(), err))?;
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
if let Ok(vendor) = CtrlGraphics::get_vendor() {
if ded == 1 && vendor != "nvidia" {
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
CtrlGraphics::set_gfx_config(GfxVendors::Nvidia)
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
}
}
}
Ok(())
}
pub fn get_boot_sound() -> Result<i8, RogError> {
let path = ASUS_POST_LOGO_SOUND;
let mut file = OpenOptions::new()
.read(true)
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
file.read_to_end(&mut data)
.map_err(|err| RogError::Read(path.into(), err))?;
let idx = data.len() - 1;
Ok(data[idx] as i8)
}
pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> {
let path = ASUS_POST_LOGO_SOUND;
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new();
file.read_to_end(&mut data)
.map_err(|err| RogError::Read(path.into(), err))?;
let idx = data.len() - 1;
if on {
data[idx] = 1;
info!("Set boot POST sound on");
} else {
data[idx] = 0;
info!("Set boot POST sound off");
}
file.write_all(&data)
.map_err(|err| RogError::Path(path.into(), err))?;
Ok(())
}
}

View File

@@ -1,10 +1,14 @@
use ctrl_gfx::ctrl_gfx::CtrlGraphics;
use daemon::config::Config;
use daemon::ctrl_anime::CtrlAnimeDisplay;
use daemon::ctrl_charge::CtrlCharge;
use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu};
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
use daemon::laptops::match_laptop;
use daemon::{
config::Config, laptops::print_board_info, supported::SupportedFunctions, GetSupported,
};
use daemon::{
ctrl_anime::CtrlAnimeDisplay,
ctrl_gfx::{gfx::CtrlGraphics, vendors::GfxVendors},
};
use asus_nb::DBUS_NAME;
use daemon::{CtrlTask, Reloadable, ZbusAdd};
@@ -15,6 +19,7 @@ use std::io::Write;
use std::sync::Arc;
use std::sync::Mutex;
use daemon::ctrl_rog_bios::CtrlRogBios;
use std::convert::Into;
use std::convert::TryInto;
use zbus::fdo;
@@ -40,14 +45,16 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
// - to maintain constant times of 1ms, per-key colours should use
// the effect endpoint so that the complete colour block is written
// as fast as 1ms per row of the matrix inside it. (10ms total time)
//
// DBUS processing takes 6ms if not tokiod
fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
print_board_info();
println!("{}", serde_json::to_string_pretty(&supported).unwrap());
let laptop = match_laptop();
let config = if let Some(laptop) = laptop.as_ref() {
Config::default().load(laptop.supported_modes())
Config::load(laptop.supported_modes())
} else {
Config::default().load(&[])
Config::load(&[])
};
let connection = Connection::new_system()?;
@@ -55,9 +62,24 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
let mut object_server = zbus::ObjectServer::new(&connection);
supported.add_to_server(&mut object_server);
let enable_gfx_switching = config.gfx_managed;
let config = Arc::new(Mutex::new(config));
match CtrlRogBios::new(config.clone()) {
Ok(mut ctrl) => {
// Do a reload of any settings
ctrl.reload()
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
// Then register to dbus server
ctrl.add_to_server(&mut object_server);
}
Err(err) => {
error!("rog_bios_control: {}", err);
}
}
match CtrlCharge::new(config.clone()) {
Ok(mut ctrl) => {
// Do a reload of any settings
@@ -81,10 +103,25 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
}
if enable_gfx_switching {
match CtrlGraphics::new() {
match CtrlGraphics::new(config.clone()) {
Ok(mut ctrl) => {
ctrl.reload()
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
// Need to check if a laptop has the dedicated gfx switch
if CtrlRogBios::has_dedicated_gfx_toggle() {
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
if let Ok(vendor) = CtrlGraphics::get_vendor() {
if ded == 1 && vendor != "nvidia" {
error!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
error!("You must reboot to enable Nvidia driver");
ctrl.set(GfxVendors::Nvidia)?;
} else if ded == 0 {
info!("Dedicated GFX toggle is off");
}
}
}
} else {
ctrl.reload()
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
}
ctrl.add_to_server(&mut object_server);
}
Err(err) => {

View File

@@ -20,6 +20,7 @@ pub enum RogError {
DoTask(String),
MissingFunction(String),
MissingLedBrightNode(String, std::io::Error),
ReloadFail(String),
}
impl fmt::Display for RogError {
@@ -41,6 +42,7 @@ impl fmt::Display for RogError {
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),
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
}
}
}

View File

@@ -27,13 +27,12 @@ impl LaptopBase {
}
pub fn match_laptop() -> Option<LaptopBase> {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();
for device in rusb::devices().expect("Failed here").iter() {
let device_desc = device.device_descriptor().expect("Failed there");
if device_desc.vendor_id() == 0x0b05 {
match device_desc.product_id() {
0x1866 => {
let laptop = select_1866_device("1866".to_owned());
print_modes(&laptop.supported_modes);
return Some(laptop);
}
0x1869 => return Some(select_1866_device("1869".to_owned())),
@@ -61,10 +60,6 @@ fn select_1866_device(prod: String) -> LaptopBase {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
let prod_name = dmi.product_name().expect("Could not get product_name");
info!("Product name: {}", prod_name.trim());
info!("Board name: {}", board_name.trim());
let mut laptop = LaptopBase {
usb_product: prod,
@@ -81,7 +76,18 @@ fn select_1866_device(prod: String) -> LaptopBase {
laptop
}
fn print_modes(supported_modes: &[u8]) {
pub fn print_board_info() {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_name = dmi.product_name().expect("Could not get product_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
info!("Product name: {}", prod_name.trim());
info!("Product family: {}", prod_family.trim());
info!("Board name: {}", board_name.trim());
}
pub fn print_modes(supported_modes: &[u8]) {
if !supported_modes.is_empty() {
info!("Supported Keyboard LED modes are:");
for mode in supported_modes {

View File

@@ -8,11 +8,17 @@ pub mod ctrl_charge;
///
pub mod ctrl_fan_cpu;
///
pub mod ctrl_leds;
pub mod ctrl_gfx;
///
pub mod ctrl_leds;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_rog_bios;
/// Laptop matching to determine capabilities
pub mod laptops;
/// Fetch all supported functions for the laptop
pub mod supported;
mod error;
use crate::error::RogError;
@@ -38,3 +44,9 @@ pub trait CtrlTaskComplex {
fn do_task(&mut self, config: &mut Config, event: Self::A);
}
pub trait GetSupported {
type A;
fn get_supported() -> Self::A;
}

View File

@@ -4,8 +4,7 @@ use asus_nb::{
core_dbus::AuraDbusClient,
profile::{ProfileCommand, ProfileEvent},
};
use ctrl_gfx::vendors::GfxVendors;
use daemon::ctrl_fan_cpu::FanLevel;
use daemon::{ctrl_fan_cpu::FanLevel, ctrl_gfx::vendors::GfxVendors};
use gumdrop::{Opt, Options};
use log::LevelFilter;
use std::{env::args, io::Write, process::Command};
@@ -18,11 +17,13 @@ struct CLIStart {
help: bool,
#[options(help = "show program version number")]
version: bool,
#[options(meta = "VAL", help = "<off, low, med, high>")]
#[options(help = "show supported functions of this laptop")]
show_supported: bool,
#[options(meta = "", help = "<off, low, med, high>")]
kbd_bright: Option<LedBrightness>,
#[options(meta = "PWR", help = "<silent, normal, boost>")]
#[options(meta = "", help = "<silent, normal, boost>")]
pwr_profile: Option<FanLevel>,
#[options(meta = "CHRG", help = "<20-100>")]
#[options(meta = "", help = "<20-100>")]
chg_limit: Option<u8>,
#[options(command)]
command: Option<CliCommand>,
@@ -38,6 +39,8 @@ enum CliCommand {
Graphics(GraphicsCommand),
#[options(name = "anime", help = "Manage AniMe Matrix")]
AniMe(AniMeCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
#[derive(Options)]
@@ -56,7 +59,10 @@ struct LedModeCommand {
struct GraphicsCommand {
#[options(help = "print help message")]
help: bool,
#[options(help = "Set graphics mode: <nvidia, hybrid, compute, integrated>")]
#[options(
meta = "",
help = "Set graphics mode: <nvidia, hybrid, compute, integrated>"
)]
mode: Option<GfxVendors>,
#[options(help = "Get the current mode")]
get: bool,
@@ -70,14 +76,31 @@ struct GraphicsCommand {
struct AniMeCommand {
#[options(help = "print help message")]
help: bool,
#[options(help = "turn on/off the panel (accept/reject write requests)")]
#[options(
meta = "",
help = "turn on/off the panel (accept/reject write requests)"
)]
turn: Option<AniMeStatusValue>,
#[options(help = "turn on/off the panel at boot (with Asus effect)")]
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
boot: Option<AniMeStatusValue>,
#[options(command)]
command: Option<AniMeActions>,
}
#[derive(Options, Debug)]
struct BiosCommand {
#[options(help = "print help message")]
help: bool,
#[options(meta = "", no_long, help = "toggle bios POST sound")]
post_sound_set: Option<bool>,
#[options(no_long, help = "read bios POST sound")]
post_sound_get: bool,
#[options(meta = "", no_long, help = "toggle GPU to/from dedicated mode")]
dedicated_gfx_set: Option<bool>,
#[options(no_long, help = "get GPU mode")]
dedicated_gfx_get: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
@@ -117,78 +140,157 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Version: {}", daemon::VERSION);
}
let writer = AuraDbusClient::new()?;
let anime_writer = AniMeDbusWriter::new()?;
let dbus_client = AuraDbusClient::new()?;
let anime_dbus_client = AniMeDbusWriter::new()?;
match parsed.command {
Some(CliCommand::LedMode(mode)) => {
if (mode.command.is_none() && !mode.prev_mode && !mode.next_mode) || mode.help {
println!("Missing arg or command\n\n{}", mode.self_usage());
if let Some(lst) = mode.self_command_list() {
println!("\n{}", lst);
}
}
if mode.next_mode && mode.prev_mode {
println!("Please specify either next or previous")
}
if mode.next_mode {
writer.next_keyboard_led_mode()?;
dbus_client.next_keyboard_led_mode()?;
} else if mode.prev_mode {
writer.prev_keyboard_led_mode()?;
dbus_client.prev_keyboard_led_mode()?;
} else if let Some(command) = mode.command {
writer.write_builtin_mode(&command.into())?
dbus_client.write_builtin_mode(&command.into())?
}
}
Some(CliCommand::Profile(command)) => {
if command.next {
writer.next_fan_profile()?;
Some(CliCommand::Profile(cmd)) => {
if (!cmd.next
&& !cmd.create
&& cmd.curve.is_none()
&& cmd.max_percentage.is_none()
&& cmd.min_percentage.is_none()
&& cmd.preset.is_none()
&& cmd.profile.is_none()
&& cmd.turbo.is_none())
|| cmd.help
{
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst);
}
}
if cmd.next {
dbus_client.next_fan_profile()?;
} else {
writer.write_profile_command(&ProfileEvent::Cli(command))?
dbus_client.write_profile_command(&ProfileEvent::Cli(cmd))?
}
}
Some(CliCommand::Graphics(command)) => do_gfx(command, &writer)?,
Some(CliCommand::AniMe(anime)) => {
if let Some(anime_turn) = anime.turn {
anime_writer.turn_on_off(anime_turn.into())?
Some(CliCommand::Graphics(cmd)) => do_gfx(cmd, &dbus_client)?,
Some(CliCommand::AniMe(cmd)) => {
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst);
}
}
if let Some(anime_boot) = anime.boot {
anime_writer.turn_boot_on_off(anime_boot.into())?
if let Some(anime_turn) = cmd.turn {
anime_dbus_client.turn_on_off(anime_turn.into())?
}
if let Some(action) = anime.command {
if let Some(anime_boot) = cmd.boot {
anime_dbus_client.turn_boot_on_off(anime_boot.into())?
}
if let Some(action) = cmd.command {
match action {
AniMeActions::Leds(anime_leds) => {
let led_brightness = anime_leds.led_brightness();
anime_writer.set_leds_brightness(led_brightness)?;
anime_dbus_client.set_leds_brightness(led_brightness)?;
}
}
}
}
None => (),
Some(CliCommand::Bios(cmd)) => {
if (cmd.dedicated_gfx_set.is_none()
&& !cmd.dedicated_gfx_get
&& cmd.post_sound_set.is_none()
&& !cmd.post_sound_get)
|| cmd.help
{
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst);
}
}
if let Some(opt) = cmd.post_sound_set {
dbus_client.set_bios_post_sound(opt)?;
}
if cmd.post_sound_get {
let res = if dbus_client.get_bios_post_sound()? == 1 {
true
} else {
false
};
println!("Bios POST sound on: {}", res);
}
if let Some(opt) = cmd.dedicated_gfx_set {
dbus_client.set_bios_dedicated_gfx(opt)?;
}
if cmd.dedicated_gfx_get {
let res = if dbus_client.get_bios_dedicated_gfx()? == 1 {
true
} else {
false
};
println!("Bios dedicated GPU on: {}", res);
}
}
None => {
if (!parsed.show_supported
&& parsed.kbd_bright.is_none()
&& parsed.pwr_profile.is_none()
&& parsed.chg_limit.is_none())
|| parsed.help
{
println!("{}", CLIStart::usage());
println!();
println!("{}", CLIStart::command_list().unwrap());
}
}
}
if let Some(brightness) = parsed.kbd_bright {
match brightness.level() {
None => {
let level = writer.get_led_brightness()?;
let level = dbus_client.get_led_brightness()?;
println!("Current keyboard led brightness: {}", level.to_string());
}
Some(level) => writer.write_brightness(level)?,
Some(level) => dbus_client.write_brightness(level)?,
}
}
if parsed.show_supported {
let dat = dbus_client.get_supported_functions()?;
println!("Supported laptop functions:\n{}", dat.to_string());
}
if let Some(fan_level) = parsed.pwr_profile {
writer.write_fan_mode(fan_level.into())?;
dbus_client.write_fan_mode(fan_level.into())?;
}
if let Some(chg_limit) = parsed.chg_limit {
writer.write_charge_limit(chg_limit)?;
dbus_client.write_charge_limit(chg_limit)?;
}
Ok(())
}
fn do_gfx(
command: GraphicsCommand,
writer: &AuraDbusClient,
dbus_client: &AuraDbusClient,
) -> Result<(), Box<dyn std::error::Error>> {
if let Some(mode) = command.mode {
println!("Updating settings, please wait...");
println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`");
writer.write_gfx_mode(mode)?;
let res = writer.wait_gfx_changed()?;
dbus_client.write_gfx_mode(<&str>::from(&mode).into())?;
let res = dbus_client.wait_gfx_changed()?;
match res.as_str() {
"reboot" => {
println!(
@@ -222,11 +324,11 @@ fn do_gfx(
std::process::exit(-1)
}
if command.get {
let res = writer.get_gfx_mode()?;
let res = dbus_client.get_gfx_mode()?;
println!("Current graphics mode: {}", res);
}
if command.pow {
let res = writer.get_gfx_pwr()?;
let res = dbus_client.get_gfx_pwr()?;
if res.contains("active") {
println!("Current power status: {}", Red.paint(&format!("{}", res)));
} else {

View File

@@ -0,0 +1,56 @@
use std::convert::TryInto;
use log::warn;
use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
use crate::{
ctrl_anime::{AnimeSupportedFunctions, CtrlAnimeDisplay},
ctrl_charge::{ChargeSupportedFunctions, CtrlCharge},
ctrl_fan_cpu::{CtrlFanAndCPU, FanCpuSupportedFunctions},
ctrl_leds::{CtrlKbdBacklight, LedSupportedFunctions},
ctrl_rog_bios::{CtrlRogBios, RogBiosSupportedFunctions},
GetSupported,
};
#[derive(Serialize, Deserialize)]
pub struct SupportedFunctions {
anime_ctrl: AnimeSupportedFunctions,
charge_ctrl: ChargeSupportedFunctions,
fan_cpu_ctrl: FanCpuSupportedFunctions,
keyboard_led: LedSupportedFunctions,
rog_bios_ctrl: RogBiosSupportedFunctions,
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl SupportedFunctions {
fn supported_functions(&self) -> String {
serde_json::to_string_pretty(self).unwrap()
}
}
impl crate::ZbusAdd for SupportedFunctions {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Supported".try_into().unwrap(), self)
.map_err(|err| {
warn!("SupportedFunctions: add_to_server {}", err);
err
})
.ok();
}
}
impl GetSupported for SupportedFunctions {
type A = SupportedFunctions;
fn get_supported() -> Self::A {
SupportedFunctions {
keyboard_led: CtrlKbdBacklight::get_supported(),
anime_ctrl: CtrlAnimeDisplay::get_supported(),
charge_ctrl: CtrlCharge::get_supported(),
fan_cpu_ctrl: CtrlFanAndCPU::get_supported(),
rog_bios_ctrl: CtrlRogBios::get_supported(),
}
}
}