From 176ab0a639062c871d7a9ee2021b519e4bdea5fb Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Sun, 7 Mar 2021 22:13:29 +1300 Subject: [PATCH] Rebootless graphics switching This changes out how the current graphics switching works, enabling asusd to stop/start the display-manager to enable/disable PCI devices and add/remove drivers as required. All existing graphics modes and commands still work as normal. G-Sync enable is now only through the bios setting, and on reboot will set all relevant settings to Nvidia mode. --- CHANGELOG.md | 12 + Cargo.lock | 4 +- asusctl/Cargo.toml | 2 +- asusctl/src/main.rs | 78 ++----- daemon/Cargo.toml | 2 +- daemon/src/config.rs | 75 +------ daemon/src/config_old.rs | 100 +++++++++ daemon/src/ctrl_fan_cpu.rs | 2 +- daemon/src/ctrl_gfx/error.rs | 4 + daemon/src/ctrl_gfx/gfx.rs | 412 ++++++++++++++++------------------ daemon/src/ctrl_gfx/mod.rs | 33 +-- daemon/src/ctrl_gfx/system.rs | 27 +++ daemon/src/ctrl_rog_bios.rs | 126 +++++++++-- daemon/src/daemon.rs | 6 +- daemon/src/error.rs | 2 + daemon/src/lib.rs | 1 + data/asusd.service | 3 +- rog-types/src/gfx_vendors.rs | 21 +- 18 files changed, 501 insertions(+), 409 deletions(-) create mode 100644 daemon/src/config_old.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a40f4a12..c808e083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- GU502LU led-modes +### Changed +- Graphics switching is now rebootless, the daemon will now restart the + display-manager to switch modes instead. Caveats are: + + There is no confirmation from the daemon, the program issuing the command + must confirm the request. + + systemd only +- Laptops with dedicated Nvidia mode: + + You still must reboot for the bios to switch modes + + On boot if dedicated mode is active then asusd will update the required configs + to put display-manager in nvidia mode # [3.0.0] - 2021-02-22 ### Added diff --git a/Cargo.lock b/Cargo.lock index fb5effba..f9bc074b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ dependencies = [ [[package]] name = "asusctl" -version = "3.0.0" +version = "3.1.0" dependencies = [ "gumdrop", "rog_dbus", @@ -187,7 +187,7 @@ dependencies = [ [[package]] name = "daemon" -version = "3.0.1" +version = "3.1.0" dependencies = [ "env_logger", "intel-pstate", diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index ecd032f2..b04e007e 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asusctl" -version = "3.0.0" +version = "3.1.0" authors = ["Luke D Jones "] edition = "2018" diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 803bb9b6..97195c61 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -6,7 +6,7 @@ use rog_types::{ gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}, }; -use std::{env::args, process::Command}; +use std::env::args; use yansi_term::Colour::Green; use yansi_term::Colour::Red; @@ -241,6 +241,7 @@ fn main() -> Result<(), Box> { false }; println!("Bios dedicated GPU on: {}", res); + println!("You must reboot your system to activate dedicated Nvidia mode"); } } None => { @@ -286,46 +287,18 @@ fn do_gfx( dbus_client: &AuraDbusClient, ) -> Result<(), Box> { if let Some(mode) = command.mode { - println!("Updating settings, please wait..."); + println!("Changing graphics modes..."); println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`"); - dbus_client - .proxies() - .gfx() - .gfx_write_mode(<&str>::from(&mode).into())?; - let res = dbus_client.gfx_wait_changed()?; - match res.as_str() { - "reboot" => { - println!( - "{}", - Green.paint("\nGraphics vendor mode changed successfully\n"), - ); - do_gfx_action( - command.force, - Command::new("systemctl").arg("reboot").arg("-i"), - "Reboot Linux PC", - "Please reboot when ready", - )?; - } - "restartx" => { - println!( - "{}", - Green.paint("\nGraphics vendor mode changed successfully\n") - ); - do_gfx_action( - command.force, - Command::new("systemctl") - .arg("restart") - .arg("display-manager.service"), - "Restart display-manager server", - "Please restart display-manager when ready", - )?; - std::process::exit(1) - } - _ => { - println!("{}", Red.paint(&format!("\n{}\n", res.as_str())),); - std::process::exit(-1); - } + if do_gfx_action( + command.force, + "This will restart your display-manager. Please save all work!", + "Setting graphics mode...", + ) { + dbus_client.proxies().gfx().gfx_write_mode(mode.into())?; + let res = dbus_client.gfx_wait_changed()?; + println!("{}", res); + std::process::exit(1) } std::process::exit(-1) } @@ -344,34 +317,17 @@ fn do_gfx( Ok(()) } -fn do_gfx_action( - no_confirm: bool, - command: &mut Command, - ask_msg: &str, - cancel_msg: &str, -) -> Result<(), Box> { - println!("{}? y/n", ask_msg); +fn do_gfx_action(no_confirm: bool, ask_msg: &str, ok_msg: &str) -> bool { + println!("{}", Red.paint(&format!("{} Continue?", ask_msg))); let mut buf = String::new(); - if no_confirm { - let status = command.status()?; - - if !status.success() { - println!("systemctl: returned with {}", status); - } - } std::io::stdin().read_line(&mut buf).expect("Input failed"); let input = buf.chars().next().unwrap() as char; if input == 'Y' || input == 'y' || no_confirm { - let status = command.status()?; - - if !status.success() { - println!("systemctl: returned with {}", status); - } - } else { - println!("{}", Red.paint(&format!("{}", cancel_msg))); + println!("{}", Green.paint(&format!("{}", ok_msg))); + return true; } - Ok(()) + false } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 620fb01e..a75c1ba9 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daemon" -version = "3.0.1" +version = "3.1.0" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 0e455a89..3c90b615 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,84 +1,22 @@ use log::{error, info, warn}; use rog_fan_curve::Curve; -use rog_types::aura_modes::AuraModes; +use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors}; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; +use crate::config_old::*; 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, - bat_charge_limit: u8, - active_profile: String, - toggle_profiles: Vec, - power_profiles: BTreeMap, - power_profile: u8, - kbd_led_brightness: u8, - kbd_backlight_mode: u8, - kbd_backlight_modes: Vec, -} - -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, - curr_fan_mode: 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, - } - } -} - -/// for parsing old v2.2.2 config -#[derive(Deserialize)] -struct ConfigV222 { - gfx_managed: bool, - bat_charge_limit: u8, - active_profile: String, - toggle_profiles: Vec, - power_profiles: BTreeMap, - power_profile: u8, - kbd_led_brightness: u8, - kbd_backlight_mode: u8, - kbd_backlight_modes: Vec, -} - -impl ConfigV222 { - 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, - curr_fan_mode: 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(Deserialize, Serialize)] pub struct Config { + pub gfx_mode: GfxVendors, pub gfx_managed: bool, - pub gfx_nv_mode_is_dedicated: bool, pub active_profile: String, pub toggle_profiles: Vec, - // TODO: remove power_profile #[serde(skip)] pub curr_fan_mode: u8, pub bat_charge_limit: u8, @@ -96,8 +34,8 @@ impl Default for Config { pwr.insert("silent".into(), Profile::new(0, 100, true, 2, None)); Config { + gfx_mode: GfxVendors::Hybrid, gfx_managed: true, - gfx_nv_mode_is_dedicated: true, active_profile: "normal".into(), toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()], curr_fan_mode: 0, @@ -129,6 +67,11 @@ impl Config { } else { if let Ok(data) = serde_json::from_str(&buf) { return data; + } else if let Ok(data) = serde_json::from_str::(&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::(&buf) { let config = data.into_current(); config.write(); diff --git a/daemon/src/config_old.rs b/daemon/src/config_old.rs new file mode 100644 index 00000000..4ac38cfa --- /dev/null +++ b/daemon/src/config_old.rs @@ -0,0 +1,100 @@ +use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors}; +use serde_derive::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +use crate::config::{Config, Profile}; + +/// for parsing old v2.1.2 config +#[derive(Deserialize)] +pub(crate) struct ConfigV212 { + gfx_managed: bool, + bat_charge_limit: u8, + active_profile: String, + toggle_profiles: Vec, + power_profiles: BTreeMap, + power_profile: u8, + kbd_led_brightness: u8, + kbd_backlight_mode: u8, + kbd_backlight_modes: Vec, +} + +impl ConfigV212 { + pub(crate) fn into_current(self) -> Config { + Config { + gfx_mode: GfxVendors::Hybrid, + gfx_managed: self.gfx_managed, + active_profile: self.active_profile, + toggle_profiles: self.toggle_profiles, + curr_fan_mode: 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, + } + } +} + +/// for parsing old v2.2.2 config +#[derive(Deserialize)] +pub(crate) struct ConfigV222 { + gfx_managed: bool, + bat_charge_limit: u8, + active_profile: String, + toggle_profiles: Vec, + power_profiles: BTreeMap, + power_profile: u8, + kbd_led_brightness: u8, + kbd_backlight_mode: u8, + kbd_backlight_modes: Vec, +} + +impl ConfigV222 { + pub(crate) fn into_current(self) -> Config { + Config { + gfx_mode: GfxVendors::Hybrid, + gfx_managed: self.gfx_managed, + active_profile: self.active_profile, + toggle_profiles: self.toggle_profiles, + curr_fan_mode: 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(Deserialize, Serialize)] +pub(crate) struct ConfigV301 { + pub gfx_managed: bool, + pub gfx_nv_mode_is_dedicated: bool, + pub active_profile: String, + pub toggle_profiles: Vec, + // TODO: remove power_profile + #[serde(skip)] + pub curr_fan_mode: u8, + pub bat_charge_limit: u8, + pub kbd_led_brightness: u8, + pub kbd_backlight_mode: u8, + pub kbd_backlight_modes: Vec, + pub power_profiles: BTreeMap, +} + +impl ConfigV301 { + pub(crate) fn into_current(self) -> Config { + Config { + gfx_mode: GfxVendors::Hybrid, + gfx_managed: self.gfx_managed, + active_profile: self.active_profile, + toggle_profiles: self.toggle_profiles, + curr_fan_mode: self.curr_fan_mode, + 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, + } + } +} diff --git a/daemon/src/ctrl_fan_cpu.rs b/daemon/src/ctrl_fan_cpu.rs index a4842674..a8a221a1 100644 --- a/daemon/src/ctrl_fan_cpu.rs +++ b/daemon/src/ctrl_fan_cpu.rs @@ -8,7 +8,7 @@ use rog_types::profile::{FanLevel, ProfileEvent}; use serde_derive::{Deserialize, Serialize}; use std::convert::TryInto; use std::fs::OpenOptions; -use std::io::{Read, Write}; +use std::io::Write; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; diff --git a/daemon/src/ctrl_gfx/error.rs b/daemon/src/ctrl_gfx/error.rs index d01036d0..824a2575 100644 --- a/daemon/src/ctrl_gfx/error.rs +++ b/daemon/src/ctrl_gfx/error.rs @@ -12,6 +12,8 @@ pub enum GfxError { Module(String, std::io::Error), Bus(String, std::io::Error), Command(String, std::io::Error), + Modprobe(String), + DisplayManager(String), } impl fmt::Display for GfxError { @@ -25,6 +27,8 @@ impl fmt::Display for GfxError { 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), + GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), + GfxError::DisplayManager(detail) => write!(f, "Display manager: {}", detail), } } } diff --git a/daemon/src/ctrl_gfx/gfx.rs b/daemon/src/ctrl_gfx/gfx.rs index 4c01ef27..978085e1 100644 --- a/daemon/src/ctrl_gfx/gfx.rs +++ b/daemon/src/ctrl_gfx/gfx.rs @@ -1,16 +1,14 @@ use ctrl_gfx::error::GfxError; use ctrl_gfx::*; -use ctrl_rog_bios::CtrlRogBios; use log::{error, info, warn}; -use rog_types::gfx_vendors::{GfxCtrlAction, GfxVendors}; +use rog_types::gfx_vendors::GfxVendors; use std::io::Write; use std::iter::FromIterator; -use std::path::Path; use std::process::Command; use std::str::FromStr; use std::{sync::Arc, sync::Mutex}; use sysfs_class::{PciDevice, SysClass}; -use system::{GraphicsDevice, Module, PciBus}; +use system::{GraphicsDevice, PciBus}; use zbus::dbus_interface; use crate::*; @@ -22,7 +20,6 @@ pub struct CtrlGraphics { nvidia: Vec, #[allow(dead_code)] other: Vec, - initfs_cmd: Option, config: Arc>, } @@ -39,7 +36,9 @@ 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)) + self.get_gfx_mode() + .map(|gfx| gfx.into()) + .unwrap_or_else(|err| format!("Get vendor failed: {}", err)) } fn power(&self) -> String { @@ -48,13 +47,13 @@ impl Dbus for CtrlGraphics { fn set_vendor(&mut self, vendor: String) { if let Ok(tmp) = GfxVendors::from_str(&vendor) { - let action = self.set(tmp).unwrap_or_else(|err| { + let msg = self.set_gfx_config(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) + self.notify_action(&msg) .unwrap_or_else(|err| warn!("{}", err)); } } @@ -86,7 +85,7 @@ impl ZbusAdd for CtrlGraphics { impl Reloadable for CtrlGraphics { fn reload(&mut self) -> Result<(), RogError> { self.auto_power()?; - info!("Reloaded gfx mode: {:?}", CtrlGraphics::get_vendor()?); + info!("Reloaded gfx mode: {:?}", self.get_gfx_mode()?); Ok(()) } } @@ -143,161 +142,42 @@ impl CtrlGraphics { } } - 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, }) } - fn get_prime_discrete() -> Result { - 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<(), RogError> { - std::fs::write(PRIME_DISCRETE_PATH, mode) - .map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?; + fn save_gfx_mode(&self, vendor: GfxVendors) -> Result<(), RogError> { + if let Ok(mut config) = self.config.lock() { + config.gfx_mode = vendor.clone(); + config.write(); + return Ok(()); + } + // TODO: Error here Ok(()) } /// Associated method to get which vendor mode is set - pub fn get_vendor() -> Result { - 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 = modules - .iter() - .any(|module| module.name == "nouveau" || module.name == "nvidia"); - - 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 get_gfx_mode(&self) -> Result { + if let Ok(config) = self.config.lock() { + return Ok(config.gfx_mode.clone()); + } + // TODO: Error here + Ok(GfxVendors::Hybrid) } - fn is_switching_prime_modes(&self, vendor: &GfxVendors) -> Result { - let prev_mode = GfxVendors::from_str(&Self::get_vendor()?)?; - if prev_mode == GfxVendors::Integrated - && (*vendor == GfxVendors::Hybrid || *vendor == GfxVendors::Nvidia) - { - return Ok(true); - } - if (prev_mode == GfxVendors::Hybrid || prev_mode == GfxVendors::Nvidia) - && *vendor == GfxVendors::Integrated - { - return Ok(true); - } - if let Ok(config) = self.config.clone().try_lock() { - if CtrlRogBios::has_dedicated_gfx_toggle() && config.gfx_nv_mode_is_dedicated { - if prev_mode == GfxVendors::Hybrid && *vendor == GfxVendors::Nvidia { - return Ok(true); - } - if *vendor == GfxVendors::Hybrid && prev_mode == GfxVendors::Nvidia { - return Ok(true); - } - } - } - Ok(false) + fn get_runtime_status() -> Result { + 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) } - pub fn set_gfx_config(vendor: GfxVendors) -> Result<(), RogError> { - 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))?; - + fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> { let action = if vendor == GfxVendors::Nvidia { info!("Enabling nvidia-fallback.service"); "enable" @@ -319,92 +199,180 @@ impl CtrlGraphics { status ); } + Ok(()) } - /// Write out config files if required, enable/disable relevant services, and update the ramdisk - fn set(&mut self, vendor: GfxVendors) -> Result { - // Switching from hybrid to/from nvidia shouldn't require a ramdisk update - // or a reboot. - let reboot = self.is_switching_prime_modes(&vendor)?; - - 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)); - } - } - } - } - - Self::set_gfx_config(vendor.clone())?; - - let mut required_action = GfxCtrlAction::None; - if reboot { - info!("Updating initramfs"); - if let Some(cmd) = self.initfs_cmd.as_mut() { - // If switching to Nvidia dedicated we need these modules included - if Path::new(DRACUT_PATH).exists() && vendor == GfxVendors::Nvidia { - cmd.arg("--add-drivers"); - cmd.arg("nvidia nvidia-drm nvidia-modeset nvidia-uvm"); - info!("System uses dracut, forcing nvidia modules to be included in init"); - } - - let status = cmd - .status() - .map_err(|err| GfxError::Write(format!("{:?}", cmd), err))?; - if !status.success() { - error!("Ram disk update failed"); - return Ok("Ram disk update failed".into()); - } else { - info!("Successfully updated iniramfs"); - } - } - required_action = GfxCtrlAction::Reboot; - } else if !reboot { - required_action = GfxCtrlAction::RestartX; - } - - Ok(required_action.into()) - } - - fn get_runtime_status() -> Result { - 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<(), RogError> { - if power { - info!("Enabling graphics power"); - self.bus - .rescan() - .map_err(|err| GfxError::Bus("bus rescan error".into(), err))?; + fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> { + let text = if vendor == GfxVendors::Nvidia { + [PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat() } else { - info!("Disabling graphics power"); + [PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat() + }; - // Unbind NVIDIA graphics devices and their functions - let unbinds = self.nvidia.iter().map(|dev| dev.unbind()); + info!("Writing {}", PRIMARY_GPU_XORG_PATH); + 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))?; - // Remove NVIDIA graphics devices and their functions - let removes = self.nvidia.iter().map(|dev| dev.remove()); + file.write_all(&text) + .and_then(|_| file.sync_all()) + .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; + Ok(()) + } - Result::from_iter(unbinds.chain(removes)) - .map_err(|err| GfxError::Command("device unbind error".into(), err))?; - } + fn write_modprobe_conf() -> Result<(), RogError> { + 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))?; + + file.write_all(MODPROBE_BASE) + .and_then(|_| file.sync_all()) + .map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?; Ok(()) } - fn auto_power(&self) -> Result<(), RogError> { - let vendor = CtrlGraphics::get_vendor()?; - self.set_power(vendor != "integrated") + fn unbind_remove_nvidia(&self) -> Result<(), RogError> { + // 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 do_driver_action(driver: &str, action: &str) -> Result<(), RogError> { + let mut cmd = Command::new(action); + cmd.arg(driver); + + let status = cmd + .status() + .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; + if !status.success() { + let msg = format!("{} {} failed: {:?}", action, driver, status); + error!("{}", msg); + return Err(GfxError::Modprobe(msg).into()); + } + Ok(()) + } + + fn do_display_manager_action(action: &str) -> Result<(), RogError> { + let service = "display-manager.service"; + let mut cmd = Command::new("systemctl"); + cmd.arg(action); + cmd.arg(service); + + let status = cmd + .status() + .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; + if !status.success() { + let msg = format!("systemctl {} {} failed: {:?}", action, service, status); + error!("{}", msg); + return Err(GfxError::DisplayManager(msg).into()); + } + Ok(()) + } + + fn wait_display_manager_inactive() -> Result<(), RogError> { + let service = "display-manager.service"; + let mut cmd = Command::new("systemctl"); + cmd.arg("is-active"); + cmd.arg(service); + + let mut count = 0; + + while count <= 4 { + let output = cmd + .output() + .map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?; + if output.stdout.starts_with("inactive".as_bytes()) { + return Ok(()); + } + std::thread::sleep(std::time::Duration::from_millis(500)); + count += 1; + } + return Err( + GfxError::DisplayManager("display-manager did not completely stop".into()).into(), + ); + } + + pub fn do_vendor_tasks(&mut self, vendor: GfxVendors) -> Result<(), RogError> { + Self::write_xorg_conf(vendor)?; + Self::write_modprobe_conf()?; // TODO: Not required here, should put in startup? + + // Rescan before doing remove or add drivers + self.bus + .rescan() + .map_err(|err| GfxError::Bus("bus rescan error".into(), err))?; + + let drivers = vec!["nvidia_drm", "nvidia_modeset", "nvidia"]; // i2c_nvidia_gpu? + + match vendor { + GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => { + for driver in drivers { + Self::do_driver_action(driver, "modprobe")?; + } + } + // TODO: compute mode, needs different setup + // GfxVendors::Compute => {} + GfxVendors::Integrated => { + for driver in drivers { + Self::do_driver_action(driver, "rmmod")?; + } + self.unbind_remove_nvidia()?; + } + } + + self.save_gfx_mode(vendor)?; + Ok(()) + } + + /// For manually calling (not on boot/startup) + /// + /// Will stop and start display manager without warning + pub fn set_gfx_config(&mut self, vendor: GfxVendors) -> Result { + Self::do_display_manager_action("stop")?; + Self::wait_display_manager_inactive()?; + self.do_vendor_tasks(vendor)?; + Self::do_display_manager_action("start")?; + // TODO: undo if failed? Save last mode, catch errors... + let v: &str = vendor.into(); + Ok(format!("Graphics mode changed to {} successfully", v)) + } + + // 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)); + // } + // } + // } + // } + + fn auto_power(&mut self) -> Result<(), RogError> { + let vendor = self.get_gfx_mode()?; + self.do_vendor_tasks(vendor)?; + Self::toggle_fallback_service(vendor)?; + Ok(()) } } diff --git a/daemon/src/ctrl_gfx/mod.rs b/daemon/src/ctrl_gfx/mod.rs index 00b64538..ef4541b7 100644 --- a/daemon/src/ctrl_gfx/mod.rs +++ b/daemon/src/ctrl_gfx/mod.rs @@ -4,40 +4,17 @@ 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 +static MODPROBE_BASE: &[u8] = br#"# Automatically generated by asusd +#blacklist i2c_nvidia_gpu +#alias i2c_nvidia_gpu off +blacklist nouveau +alias nouveau 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 diff --git a/daemon/src/ctrl_gfx/system.rs b/daemon/src/ctrl_gfx/system.rs index a97f9fbc..f70d6eb6 100644 --- a/daemon/src/ctrl_gfx/system.rs +++ b/daemon/src/ctrl_gfx/system.rs @@ -50,6 +50,7 @@ impl PciBus { } } + /// Will rescan the device tree, which adds all removed devices back pub fn rescan(&self) -> io::Result<()> { write(self.path.join("rescan"), "1") } @@ -95,6 +96,32 @@ impl GraphicsDevice { Ok(()) } + pub fn rebind(&self) -> Result<(), std::io::Error> { + for func in self.functions.iter() { + if func.path().exists() { + match func.driver() { + Ok(driver) => { + info!("{}: Binding {}", driver.id(), func.id()); + unsafe { + driver.bind(&func).map_err(|err| { + error!("gfx bind: {}", 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() { diff --git a/daemon/src/ctrl_rog_bios.rs b/daemon/src/ctrl_rog_bios.rs index f498f0a5..40b5823f 100644 --- a/daemon/src/ctrl_rog_bios.rs +++ b/daemon/src/ctrl_rog_bios.rs @@ -1,16 +1,18 @@ -use crate::{config::Config, ctrl_gfx::gfx::CtrlGraphics, error::RogError, GetSupported}; -//use crate::dbus::DbusEvents; -use log::{info, warn}; -use rog_types::gfx_vendors::GfxVendors; +use crate::{config::Config, error::RogError, GetSupported}; +use log::{error, 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::process::Command; use std::sync::Arc; use std::sync::Mutex; +use std::{convert::TryInto, io::BufRead}; use zbus::dbus_interface; +const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs"; +const DRACUT_PATH: &str = "/usr/bin/dracut"; + static ASUS_SWITCH_GRAPHIC_MODE: &str = "/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e"; static ASUS_POST_LOGO_SOUND: &str = @@ -40,7 +42,7 @@ impl GetSupported for CtrlRogBios { #[dbus_interface(name = "org.asuslinux.Daemon")] impl CtrlRogBios { pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) { - Self::set_gfx_mode(dedicated) + self.set_gfx_mode(dedicated) .map_err(|err| { warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err); err @@ -134,12 +136,12 @@ impl CtrlRogBios { } } - Ok(CtrlRogBios { _config: config }) + Ok(CtrlRogBios { + _config: config, + }) } fn set_path_mutable(path: &str) -> Result<(), RogError> { - use std::process::Command; - let output = Command::new("/usr/bin/chattr") .arg("-i") .arg(path) @@ -179,7 +181,7 @@ impl CtrlRogBios { Ok(data[idx] as i8) } - pub(super) fn set_gfx_mode(dedicated: bool) -> Result<(), RogError> { + pub(super) fn set_gfx_mode(&self, dedicated: bool) -> Result<(), RogError> { let path = ASUS_SWITCH_GRAPHIC_MODE; let mut file = OpenOptions::new() .read(true) @@ -201,15 +203,17 @@ impl CtrlRogBios { 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)); - } - } - } + self.update_initramfs(dedicated)?; + + // 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(()) } @@ -254,4 +258,88 @@ impl CtrlRogBios { Ok(()) } + + // required for g-sync mode + fn update_initramfs(&self, dedicated: bool) -> Result<(), RogError> { + 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'"); + } + + if let Some(mut cmd) = initfs_cmd { + info!("Updating initramfs"); + + // If switching to Nvidia dedicated we need these modules included + if Path::new(DRACUT_PATH).exists() && dedicated { + cmd.arg("--add-drivers"); + cmd.arg("nvidia nvidia-drm nvidia-modeset nvidia-uvm"); + info!("System uses dracut, forcing nvidia modules to be included in init"); + } else if Path::new(INITRAMFS_PATH).exists() { + let modules = vec![ + "nvidia\n", + "nvidia-drm\n", + "nvidia-modeset\n", + "nvidia-uvm\n", + ]; + + let module_include = Path::new("/etc/initramfs-tools/modules"); + + if dedicated { + let mut file = std::fs::OpenOptions::new() + .append(true) + .open(module_include) + .map_err(|err| { + RogError::Write(module_include.to_string_lossy().to_string(), err) + })?; + // add nvidia modules to module_include + file.write_all(modules.concat().as_bytes()).unwrap(); + } else { + let file = std::fs::OpenOptions::new() + .read(true) + .open(module_include) + .map_err(|err| { + RogError::Write(module_include.to_string_lossy().to_string(), err) + })?; + + let mut buf = Vec::new(); + // remove modules + for line in std::io::BufReader::new(file).lines() { + if let Ok(l) = line { + if !modules.contains(&l.as_ref()) { + buf.append(&mut l.as_bytes().to_vec()); + } + } + } + + let file = std::fs::OpenOptions::new() + .write(true) + .open(module_include) + .map_err(|err| { + RogError::Write(module_include.to_string_lossy().to_string(), err) + })?; + std::io::BufWriter::new(file).write_all(&buf).unwrap(); + } + } + + let status = cmd + .status() + .map_err(|err| RogError::Write(format!("{:?}", cmd), err))?; + if !status.success() { + error!("Ram disk update failed"); + return Err(RogError::Initramfs("Ram disk update failed".into()).into()); + } else { + info!("Successfully updated initramfs"); + } + } + Ok(()) + } } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 3cc823c6..ec90520d 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -109,11 +109,11 @@ fn start_daemon() -> Result<(), Box> { // 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" { + if let Ok(vendor) = ctrl.get_gfx_mode() { + if ded == 1 && vendor != GfxVendors::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"); - CtrlGraphics::set_gfx_config(GfxVendors::Nvidia)?; + ctrl.do_vendor_tasks(GfxVendors::Nvidia)?; } else if ded == 0 { info!("Dedicated GFX toggle is off"); } diff --git a/daemon/src/error.rs b/daemon/src/error.rs index 670ca2e9..25953a3c 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -25,6 +25,7 @@ pub enum RogError { MissingLedBrightNode(String, std::io::Error), ReloadFail(String), GfxSwitching(GfxError), + Initramfs(String), } impl fmt::Display for RogError { @@ -48,6 +49,7 @@ impl fmt::Display for RogError { 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), RogError::GfxSwitching(deets) => write!(f, "Graphics switching error: {}", deets), + RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), } } } diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 529a32f6..aa59cd58 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,6 +1,7 @@ #![deny(unused_must_use)] /// Configuration loading, saving pub mod config; +pub(crate) mod config_old; /// Control of AniMe matrix display pub mod ctrl_anime; /// Control of battery charge level diff --git a/data/asusd.service b/data/asusd.service index a68c19d5..1acde0d4 100644 --- a/data/asusd.service +++ b/data/asusd.service @@ -9,4 +9,5 @@ Restart=on-failure Restart=always RestartSec=1 Type=dbus -BusName=org.asuslinux.Daemon \ No newline at end of file +BusName=org.asuslinux.Daemon +Before=display-manager.service \ No newline at end of file diff --git a/rog-types/src/gfx_vendors.rs b/rog-types/src/gfx_vendors.rs index 05a43732..c901845b 100644 --- a/rog-types/src/gfx_vendors.rs +++ b/rog-types/src/gfx_vendors.rs @@ -1,4 +1,6 @@ -#[derive(Debug, PartialEq, Clone)] +use serde_derive::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)] pub enum GfxVendors { Nvidia, Integrated, @@ -28,9 +30,9 @@ impl FromStr for GfxVendors { } } -impl From<&GfxVendors> for &str { - fn from(mode: &GfxVendors) -> Self { - match mode { +impl Into<&str> for GfxVendors { + fn into(self) -> &'static str { + match self { GfxVendors::Nvidia => "nvidia", GfxVendors::Hybrid => "hybrid", GfxVendors::Compute => "compute", @@ -39,6 +41,17 @@ impl From<&GfxVendors> for &str { } } +impl Into for GfxVendors { + fn into(self) -> String { + match self { + GfxVendors::Nvidia => "nvidia".to_string(), + GfxVendors::Hybrid => "hybrid".to_string(), + GfxVendors::Compute => "compute".to_string(), + GfxVendors::Integrated => "integrated".to_string(), + } + } +} + #[derive(Debug)] pub enum GfxCtrlAction { Reboot,