diff --git a/cli/src/main.rs b/cli/src/main.rs index 6de8a85..f27db55 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -7,7 +7,9 @@ enum Opt { ///Gets realtime GPU information Stats, Info, - Start, + StartFanControl, + StopFanControl, + GetFanControl, Stop, } @@ -29,11 +31,15 @@ fn main() { println!("Driver in use: {}", gpu_info.driver); print!("VBIOS Version: {}", gpu_info.vbios_version); }, - Opt::Start => { + Opt::StartFanControl => { println!("{:?}", d.start_fan_control()); }, - Opt::Stop => { + Opt::StopFanControl => { println!("{:?}", d.stop_fan_control()); - } + }, + Opt::GetFanControl => { + println!("{:?}", d.get_fan_control()); + }, + Opt::Stop => d.shutdown(), } } diff --git a/daemon/src/daemon_connection.rs b/daemon/src/daemon_connection.rs index 1d62e04..c312837 100644 --- a/daemon/src/daemon_connection.rs +++ b/daemon/src/daemon_connection.rs @@ -1,5 +1,5 @@ -use crate::{Action, SOCK_PATH, gpu_controller::GpuInfo, gpu_controller::GpuStats, hw_mon::HWMonError}; -use std::io::{Read, Write}; +use crate::{Action, SOCK_PATH, gpu_controller::GpuInfo, gpu_controller::{FanControlInfo, GpuStats}, hw_mon::HWMonError}; +use std::{collections::BTreeMap, io::{Read, Write}}; use std::os::unix::net::UnixStream; #[derive(Debug)] @@ -89,4 +89,25 @@ impl DaemonConnection { Err(_) => Err(DaemonConnectionError::PermissionDenied), } } + + pub fn get_fan_control(&self)-> FanControlInfo { + let mut s = UnixStream::connect(SOCK_PATH).unwrap(); + s.write_all(&bincode::serialize(&Action::GetFanControl).unwrap()).unwrap(); + + let mut buffer = Vec::::new(); + s.read_to_end(&mut buffer).unwrap(); + + bincode::deserialize(&buffer).unwrap() + } + + pub fn set_fan_curve(&self, curve: BTreeMap) { + let mut s = UnixStream::connect(SOCK_PATH).unwrap(); + s.write_all(&bincode::serialize(&Action::SetFanCurve).unwrap()).unwrap(); + s.write_all(&bincode::serialize(&curve).unwrap()).unwrap(); + } + + pub fn shutdown(&self) { + let mut s = UnixStream::connect(SOCK_PATH).unwrap(); + s.write_all(&bincode::serialize(&Action::Shutdown).unwrap()).unwrap(); + } } \ No newline at end of file diff --git a/daemon/src/gpu_controller.rs b/daemon/src/gpu_controller.rs index 0b38951..05ed369 100644 --- a/daemon/src/gpu_controller.rs +++ b/daemon/src/gpu_controller.rs @@ -1,6 +1,6 @@ use crate::hw_mon::{HWMon, HWMonError}; use serde::{Deserialize, Serialize}; -use std::fs; +use std::{collections::BTreeMap, fs}; use std::path::PathBuf; use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice}; @@ -16,6 +16,12 @@ pub struct GpuStats { pub fan_speed: i32, } +#[derive(Serialize, Deserialize, Debug)] +pub struct FanControlInfo { + pub enabled: bool, + pub curve: BTreeMap, +} + #[derive(Clone)] pub struct GpuController { hw_path: PathBuf, @@ -48,9 +54,11 @@ pub struct GpuInfo { impl GpuController { pub fn new(hw_path: PathBuf) -> Self { + let hwmon_path = fs::read_dir(&hw_path.join("hwmon")).unwrap().next().unwrap().unwrap().path(); + let mut controller = GpuController { hw_path: hw_path.clone(), - hw_mon: HWMon::new(&hw_path.join("hwmon/hwmon0")), + hw_mon: HWMon::new(&hwmon_path), gpu_info: Default::default(), }; controller.gpu_info = controller.get_info(); @@ -200,6 +208,19 @@ impl GpuController { self.hw_mon.stop_fan_control() } + pub fn get_fan_control(&self) -> FanControlInfo { + let control = self.hw_mon.get_fan_control(); + + FanControlInfo { + enabled: control.0, + curve: control.1, + } + } + + pub fn set_fan_curve(&self, curve: BTreeMap) { + self.hw_mon.set_fan_curve(curve); + } + fn get_vulkan_info(pci_id: &str) -> VulkanInfo { let instance = Instance::new(None, &InstanceExtensions::none(), None) .expect("failed to create instance"); diff --git a/daemon/src/hw_mon.rs b/daemon/src/hw_mon.rs index c11a6c0..82027bd 100644 --- a/daemon/src/hw_mon.rs +++ b/daemon/src/hw_mon.rs @@ -1,4 +1,4 @@ -use std:: {fs, path::PathBuf, sync::{Arc, atomic::{AtomicBool, Ordering}}, thread, time::Duration}; +use std:: {collections::{BTreeMap}, fs, path::PathBuf, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering}}, thread, time::Duration}; use serde::{Deserialize, Serialize}; @@ -7,21 +7,29 @@ pub enum HWMonError { PermissionDenied, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct HWMon { hwmon_path: PathBuf, fan_max_speed: i32, fan_control: Arc, + fan_curve: Arc>>, } impl HWMon { pub fn new(hwmon_path: &PathBuf) -> HWMon { let fan_max_speed = fs::read_to_string(hwmon_path.join("fan1_max")).unwrap().trim().parse::().unwrap(); + let mut fan_curve: BTreeMap = BTreeMap::new(); + fan_curve.insert(20, 0f64); + fan_curve.insert(40, 0f64); + fan_curve.insert(60, 50f64); + fan_curve.insert(80, 80f64); + fan_curve.insert(100, 100f64); HWMon { hwmon_path: hwmon_path.clone(), fan_max_speed, fan_control: Arc::new(AtomicBool::new(false)), + fan_curve: Arc::new(RwLock::new(fan_curve)), } } @@ -63,7 +71,21 @@ impl HWMon { fs::read_to_string(filename).unwrap().trim().parse::().unwrap() / 1000000 } + pub fn set_fan_curve(&self, curve: BTreeMap) { + println!("trying to set curve"); + let mut current = self.fan_curve.write().unwrap(); + current.clear(); + + for (k, v) in curve.iter() { + current.insert(k.clone(), v.clone()); + } + println!("set curve to {:?}", current); + } + pub fn start_fan_control(&self) -> Result<(), HWMonError> { + if self.fan_control.load(Ordering::SeqCst) { + return Ok(()); + } self.fan_control.store(true, Ordering::SeqCst); match fs::write(self.hwmon_path.join("pwm1_enable"), "1") { @@ -72,8 +94,30 @@ impl HWMon { thread::spawn(move || { while s.fan_control.load(Ordering::SeqCst) { + let curve = s.fan_curve.read().unwrap(); + let temp = s.get_gpu_temp(); - println!("{}", temp); + println!("Current gpu temp: {}", temp); + for (t_low, s_low) in curve.iter() { + match curve.range(t_low..).nth(1) { + Some((t_high, s_high)) => { + if (t_low..t_high).contains(&&temp) { + let speed_ratio = (temp - t_low) as f64 / (t_high - t_low) as f64; //The ratio of which speed to choose within the range of current lower and upper speeds + let speed_percent = s_low + ((s_high - s_low) * speed_ratio); + let pwm = (255f64 * (speed_percent / 100f64)) as i32; + println!("pwm: {}", pwm); + + fs::write(s.hwmon_path.join("pwm1"), pwm.to_string()).expect("Failed to write to pwm1"); + + println!("In the range of {}..{}c {}..{}%, setting speed {}% ratio {}", t_low, t_high, s_low, s_high, speed_percent, speed_ratio); + break; + } + } + None => (), + } + } + drop(curve); //needed to release rwlock so that the curve can be changed + thread::sleep(Duration::from_millis(1000)); } }); @@ -92,6 +136,10 @@ impl HWMon { }, Err(_) => Err(HWMonError::PermissionDenied) } - + } + + pub fn get_fan_control(&self) -> (bool, BTreeMap) { + println!("Fan control: {}", self.fan_control.load(Ordering::SeqCst)); + (self.fan_control.load(Ordering::SeqCst), self.fan_curve.read().unwrap().clone()) } } diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 6c377ee..f96860d 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -2,7 +2,7 @@ pub mod gpu_controller; pub mod hw_mon; pub mod daemon_connection; -use std::{path::PathBuf, io::{Read, Write}}; +use std::{io::{Read, Write}, path::PathBuf, thread}; use serde::{Deserialize, Serialize}; use std::fs; use std::os::unix::net::{UnixListener, UnixStream}; @@ -24,6 +24,9 @@ pub enum Action { GetStats, StartFanControl, StopFanControl, + GetFanControl, + SetFanCurve, + Shutdown, } impl Daemon { @@ -43,13 +46,13 @@ impl Daemon { Daemon { listener, gpu_controller: GpuController::new(PathBuf::from("/sys/class/drm/card0/device"))} } - pub fn listen(mut self) { + pub fn listen(self) { for stream in self.listener.incoming() { match stream { Ok(stream) => { - //let controller = self.gpu_controller.clone(); - //thread::spawn(move || Daemon::handle_connection(controller, stream)); - Daemon::handle_connection(&mut self.gpu_controller, stream); + let mut controller = self.gpu_controller.clone(); + thread::spawn(move || Daemon::handle_connection(&mut controller, stream)); + //Daemon::handle_connection(&mut self.gpu_controller, stream); } Err(err) => { println!("Error: {}", err); @@ -62,16 +65,24 @@ impl Daemon { fn handle_connection(gpu_controller: &mut GpuController, mut stream: UnixStream) { let mut buffer: [u8; 4] = [0; 4]; stream.read(&mut buffer).unwrap(); - println!("finished reading, buffer size {}", buffer.len()); + //println!("finished reading, buffer size {}", buffer.len()); let action: Action = bincode::deserialize(&buffer).expect("Failed to deserialize buffer"); - println!("{:?}", action); + //println!("{:?}", action); let response: Option> = match action { Action::GetStats => Some(bincode::serialize(&gpu_controller.get_stats()).unwrap()), Action::GetInfo => Some(bincode::serialize(&gpu_controller.gpu_info).unwrap()), Action::StartFanControl => Some(bincode::serialize(&gpu_controller.start_fan_control()).unwrap()), Action::StopFanControl => Some(bincode::serialize(&gpu_controller.stop_fan_control()).unwrap()), + Action::GetFanControl => Some(bincode::serialize(&gpu_controller.get_fan_control()).unwrap()), + Action::SetFanCurve => { + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer).unwrap(); + gpu_controller.set_fan_curve(bincode::deserialize(&buffer).expect("Failed to deserialize curve")); + None + }, Action::CheckAlive => Some(vec![1]), + Action::Shutdown => std::process::exit(0), }; if let Some(r) = &response { @@ -80,6 +91,7 @@ impl Daemon { .expect("Failed writing response"); } } + } diff --git a/gui/src/main.rs b/gui/src/main.rs index 84091cc..08b466a 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -4,11 +4,11 @@ extern crate gtk; use daemon::{Daemon, daemon_connection::DaemonConnection}; use gio::prelude::*; -use gtk::{ButtonsType, DialogFlags, Label, LevelBar, MessageType, prelude::*}; +use gtk::{Adjustment, Button, ButtonsType, DialogFlags, Frame, Label, LevelBar, MessageType, Switch, prelude::*}; use gtk::{Builder, MessageDialog, TextBuffer, Window}; -use std::{env::args, thread, time::Duration}; +use std::{collections::BTreeMap, env::args, sync::{Arc, RwLock}, thread, time::Duration}; fn build_ui(application: >k::Application) { let glade_src = include_str!("main_window.glade"); @@ -78,9 +78,23 @@ fn build_ui(application: >k::Application) { let fan_speed_text_buffer: TextBuffer = builder .get_object("fan_speed_text_buffer").unwrap(); + let automatic_fan_control_switch: Switch = builder + .get_object("automatic_fan_control_switch").unwrap(); + + let apply_button: Button = builder + .get_object("apply_button").unwrap(); + + let fan_curve_frame: Frame = builder + .get_object("fan_curve_frame").unwrap(); + + + let mut unpriviliged: bool = false; + let d = match DaemonConnection::new() { Ok(a) => a, Err(_) => { + unpriviliged = true; + let daemon = Daemon::new(); thread::spawn(move || { daemon.listen(); @@ -91,7 +105,7 @@ fn build_ui(application: >k::Application) { DialogFlags::empty(), MessageType::Error, ButtonsType::Ok, - "Running in unpriveleged mode", + "Running in unpriviliged mode", ); diag.run(); diag.hide(); @@ -117,6 +131,12 @@ fn build_ui(application: >k::Application) { vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version); vulkan_features_text_buffer.set_text(&gpu_info.vulkan_info.features); + if unpriviliged { + automatic_fan_control_switch.set_sensitive(false); + fan_curve_frame.set_visible(false); + automatic_fan_control_switch.set_tooltip_text(Some("Unavailable in unprivileged mode")); + } + let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); thread::spawn(move || { @@ -128,6 +148,7 @@ fn build_ui(application: >k::Application) { } }); + rx.attach(None, move |gpu_stats| { vram_usage_level_bar.set_max_value(gpu_stats.mem_total as f64); vram_usage_level_bar.set_value(gpu_stats.mem_used as f64); @@ -146,18 +167,84 @@ fn build_ui(application: >k::Application) { glib::Continue(true) }); - + + let fan_control = d.get_fan_control(); + + if fan_control.enabled { + println!("Automatic fan control disabled!"); + automatic_fan_control_switch.set_active(false); + fan_curve_frame.set_visible(true); + } + else { + println!("Automatic fan control enabled"); + fan_curve_frame.set_visible(false); + } + + let b = apply_button.clone(); + + let switch = automatic_fan_control_switch.clone(); + automatic_fan_control_switch.connect_changed_active(move |_| { + match switch.get_active() { + true => { + fan_curve_frame.set_visible(false); + }, + false => { + fan_curve_frame.set_visible(true); + } + } + + b.set_sensitive(true); + }); + + let curve: Arc>> = Arc::new(RwLock::new(fan_control.curve)); + + for i in 1..6 { + let curve_temperature_adjustment: Adjustment = builder + .get_object(&format!("curve_temperature_adjustment_{}", i)).unwrap(); + + let value = *curve.read().unwrap().get(&(i * 20)).expect("Could not get by index"); + println!("Setting value {} on adjustment {}", value, i); + curve_temperature_adjustment.set_value(value); + + let c = curve.clone(); + let b = apply_button.clone(); + + curve_temperature_adjustment.connect_value_changed(move |adj | { + c.write().unwrap().insert(20 * i, adj.get_value()); + b.set_sensitive(true); + }); + + } + + apply_button.connect_clicked(move |b| { + let curve = curve.read().unwrap().clone(); + println!("setting curve to {:?}", curve); + d.set_fan_curve(curve); + b.set_sensitive(false); + + match automatic_fan_control_switch.get_active() { + true => { + d.stop_fan_control().unwrap(); + }, + false => { + d.start_fan_control().unwrap(); + } + } + }); main_window.set_application(Some(application)); - main_window.show_all(); + main_window.show(); } + fn main() { + println!("Initializing gtk"); let application = gtk::Application::new(Some("com.ilyaz.yagc"), Default::default()) .expect("failed to initialize"); application.connect_activate(|app| { + println!("Activating"); build_ui(app); }); diff --git a/gui/src/main_window.glade b/gui/src/main_window.glade index fb3874d..67b8a40 100644 --- a/gui/src/main_window.glade +++ b/gui/src/main_window.glade @@ -2,6 +2,31 @@ + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + driver @@ -326,7 +351,7 @@ True True - 6 + 7 5 vulkan_device_name_text_buffer True @@ -340,7 +365,7 @@ True True - 6 + 7 5 vulkan_version_text_buffer True @@ -751,7 +776,341 @@ - + + True + False + center + + + True + False + 5 + 5 + 5 + 5 + Automatic fan control + + + + + + False + True + 0 + + + + + True + True + True + + + False + True + 1 + + + + + False + True + 2 + + + + + False + 10 + 10 + 10 + 10 + True + 0.15000000596046448 + out + + + + True + False + + + True + False + PWM % + 90 + + + 0 + 0 + + + + + True + False + Temperature °C + + + 2 + 2 + 5 + + + + + True + False + vertical + + + True + False + True + 100 + 90 + + + False + True + 0 + + + + + True + False + True + 75 + 90 + + + False + True + 1 + + + + + True + False + True + 50 + 90 + + + False + True + 2 + + + + + True + False + True + 25 + 90 + + + False + True + 3 + + + + + True + False + True + 0 + 90 + + + False + True + 4 + + + + + 1 + 0 + + + + + True + False + True + vertical + curve_temperature_adjustment_1 + True + False + 0 + 1 + 0 + False + left + + + 2 + 0 + + + + + True + False + 20 + + + 2 + 1 + + + + + True + False + True + vertical + curve_temperature_adjustment_2 + True + False + 0 + 1 + 0 + False + + + 3 + 0 + + + + + True + False + 40 + + + 3 + 1 + + + + + True + False + True + vertical + curve_temperature_adjustment_3 + True + 1 + 0 + False + + + 4 + 0 + + + + + True + False + 60 + + + 4 + 1 + + + + + True + False + True + True + vertical + curve_temperature_adjustment_4 + True + 1 + 0 + False + + + 5 + 0 + + + + + True + False + 80 + + + 5 + 1 + + + + + True + False + True + vertical + curve_temperature_adjustment_5 + True + 1 + 0 + False + + + 6 + 0 + + + + + True + False + 100 + + + 6 + 1 + + + + + + + + + + + + + + + + + + + True + False + Fan Curve + + + + + False + True + 3 + @@ -769,6 +1128,19 @@ False + + + gtk-apply + True + False + True + True + True + + + False + +