commit
2220c3084f
1
build.sh
1
build.sh
@ -3,3 +3,4 @@
|
||||
cargo build --release
|
||||
strip target/x86_64-unknown-linux-musl/release/amdfand
|
||||
upx --best --lzma target/x86_64-unknown-linux-musl/release/amdfand
|
||||
|
||||
|
@ -130,15 +130,11 @@ impl LogLevel {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Config {
|
||||
log_level: LogLevel,
|
||||
cards: Vec<Card>,
|
||||
speed_matrix: Vec<MatrixPoint>,
|
||||
temp_input: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn cards(&self) -> &[Card] {
|
||||
&self.cards
|
||||
}
|
||||
|
||||
pub fn speed_for_temp(&self, temp: f64) -> f64 {
|
||||
let idx = match self.speed_matrix.iter().rposition(|p| p.temp <= temp) {
|
||||
Some(idx) => idx,
|
||||
@ -162,6 +158,10 @@ impl Config {
|
||||
self.log_level
|
||||
}
|
||||
|
||||
pub fn temp_input(&self) -> Option<&str> {
|
||||
self.temp_input.as_deref()
|
||||
}
|
||||
|
||||
fn min_speed(&self) -> f64 {
|
||||
self.speed_matrix.first().map(|p| p.speed).unwrap_or(0f64)
|
||||
}
|
||||
@ -175,7 +175,6 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
log_level: LogLevel::Error,
|
||||
cards: vec![Card(0)],
|
||||
speed_matrix: vec![
|
||||
MatrixPoint {
|
||||
temp: 4f64,
|
||||
@ -210,6 +209,7 @@ impl Default for Config {
|
||||
speed: 100f64,
|
||||
},
|
||||
],
|
||||
temp_input: Some(String::from("temp1_input")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,23 +6,20 @@ use crate::FanMode;
|
||||
|
||||
/// Change card fan mode to either automatic or manual
|
||||
pub fn run(switcher: Switcher, mode: FanMode, config: Config) -> std::io::Result<()> {
|
||||
let mut controllers = crate::utils::controllers(&config, true)?;
|
||||
let mut hw_mons = crate::utils::hw_mons(&config, true)?;
|
||||
|
||||
let cards = match switcher.card {
|
||||
Some(card_id) => match controllers
|
||||
.iter()
|
||||
.position(|hw_mon| *hw_mon.card == card_id)
|
||||
{
|
||||
Some(card) => vec![controllers.remove(card)],
|
||||
Some(card_id) => match hw_mons.iter().position(|hw_mon| **hw_mon.card() == card_id) {
|
||||
Some(card) => vec![hw_mons.remove(card)],
|
||||
None => {
|
||||
eprintln!("Card does not exists. Available cards: ");
|
||||
for hw_mon in controllers {
|
||||
eprintln!(" * {}", *hw_mon.card);
|
||||
for hw_mon in hw_mons {
|
||||
eprintln!(" * {}", *hw_mon.card());
|
||||
}
|
||||
return Err(not_found());
|
||||
}
|
||||
},
|
||||
None => controllers,
|
||||
None => hw_mons,
|
||||
};
|
||||
|
||||
for hw_mon in cards {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::utils::controllers;
|
||||
use crate::utils::hw_mons;
|
||||
use crate::AmdFanError;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -45,11 +45,11 @@ pub fn run(monitor: Monitor, config: Config) -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
pub fn verbose(config: Config) -> std::io::Result<()> {
|
||||
let mut controllers = controllers(&config, true)?;
|
||||
let mut controllers = hw_mons(&config, true)?;
|
||||
loop {
|
||||
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
|
||||
for hw_mon in controllers.iter_mut() {
|
||||
println!("Card {:3}", hw_mon.card.to_string().replace("card", ""));
|
||||
println!("Card {:3}", hw_mon.card().to_string().replace("card", ""));
|
||||
println!(" MIN | MAX | PWM | %");
|
||||
let min = hw_mon.pwm_min();
|
||||
let max = hw_mon.pwm_max();
|
||||
@ -87,13 +87,13 @@ pub fn verbose(config: Config) -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
pub fn short(config: Config) -> std::io::Result<()> {
|
||||
let mut controllers = controllers(&config, true)?;
|
||||
let mut controllers = hw_mons(&config, true)?;
|
||||
loop {
|
||||
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
|
||||
for hw_mon in controllers.iter_mut() {
|
||||
println!(
|
||||
"Card {:3} | Temp | MIN | MAX | PWM | %",
|
||||
hw_mon.card.to_string().replace("card", "")
|
||||
hw_mon.card().to_string().replace("card", "")
|
||||
);
|
||||
let min = hw_mon.pwm_min();
|
||||
let max = hw_mon.pwm_max();
|
||||
|
@ -5,7 +5,7 @@ use crate::io_err::not_found;
|
||||
|
||||
/// Start service which will change fan speed according to config and GPU temperature
|
||||
pub fn run(config: Config) -> std::io::Result<()> {
|
||||
let mut controllers = crate::utils::controllers(&config, true)?;
|
||||
let mut controllers = crate::utils::hw_mons(&config, true)?;
|
||||
if controllers.is_empty() {
|
||||
return Err(not_found());
|
||||
}
|
||||
@ -13,8 +13,8 @@ pub fn run(config: Config) -> std::io::Result<()> {
|
||||
loop {
|
||||
for hw_mon in controllers.iter_mut() {
|
||||
let gpu_temp = hw_mon.max_gpu_temp().unwrap_or_default();
|
||||
log::debug!("Current {} temperature: {}", hw_mon.card, gpu_temp);
|
||||
let last = *cache.entry(*hw_mon.card).or_insert(1_000f64);
|
||||
log::debug!("Current {} temperature: {}", hw_mon.card(), gpu_temp);
|
||||
let last = *cache.entry(**hw_mon.card()).or_insert(1_000f64);
|
||||
|
||||
if (last - gpu_temp).abs() < 0.001f64 {
|
||||
log::debug!("Temperature didn't change");
|
||||
@ -26,7 +26,7 @@ pub fn run(config: Config) -> std::io::Result<()> {
|
||||
if let Err(e) = hw_mon.set_speed(speed) {
|
||||
log::error!("Failed to change speed to {}. {:?}", speed, e);
|
||||
}
|
||||
cache.insert(*hw_mon.card, gpu_temp);
|
||||
cache.insert(**hw_mon.card(), gpu_temp);
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_secs(4));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::config::Card;
|
||||
use crate::config::{Card, Config};
|
||||
use crate::io_err::{invalid_input, not_found};
|
||||
use crate::utils::linear_map;
|
||||
use crate::{AmdFanError, HwMon, HW_MON_DIR, ROOT_DIR};
|
||||
use crate::{AmdFanError, HW_MON_DIR, ROOT_DIR};
|
||||
|
||||
/// pulse width modulation fan control minimum level (0)
|
||||
const PULSE_WIDTH_MODULATION_MIN: &str = "pwm1_min";
|
||||
@ -17,12 +17,28 @@ const PULSE_WIDTH_MODULATION_MODE: &str = "pwm1_enable";
|
||||
|
||||
// static PULSE_WIDTH_MODULATION_DISABLED: &str = "0";
|
||||
const PULSE_WIDTH_MODULATION_AUTO: &str = "2";
|
||||
const PULSE_WIDTH_MODULATION_MANUAL: &str = "1";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HwMon {
|
||||
/// HW MOD cord (ex. card0)
|
||||
card: Card,
|
||||
/// MW MOD name (ex. hwmod0)
|
||||
name: String,
|
||||
/// Minimal modulation (between 0-255)
|
||||
pwm_min: Option<u32>,
|
||||
/// Maximal modulation (between 0-255)
|
||||
pwm_max: Option<u32>,
|
||||
/// List of available temperature inputs for current HW MOD
|
||||
temp_inputs: Vec<String>,
|
||||
/// Preferred temperature input
|
||||
temp_input: Option<String>,
|
||||
}
|
||||
|
||||
impl HwMon {
|
||||
pub fn new(card: &Card, name: &str) -> Self {
|
||||
pub fn new(card: &Card, name: &str, config: &Config) -> Self {
|
||||
Self {
|
||||
card: *card,
|
||||
temp_input: config.temp_input().map(String::from),
|
||||
name: String::from(name),
|
||||
pwm_min: None,
|
||||
pwm_max: None,
|
||||
@ -31,6 +47,12 @@ impl HwMon {
|
||||
}
|
||||
|
||||
pub fn max_gpu_temp(&self) -> std::io::Result<f64> {
|
||||
if let Some(input) = self.temp_input.as_deref() {
|
||||
return self
|
||||
.read_gpu_temp(input)
|
||||
.map(|temp| temp as f64 / 1000f64)
|
||||
.map_err(|_| invalid_input());
|
||||
}
|
||||
let mut results = Vec::with_capacity(self.temp_inputs.len());
|
||||
for name in self.temp_inputs.iter() {
|
||||
results.push(self.read_gpu_temp(name).unwrap_or(0));
|
||||
@ -63,6 +85,11 @@ impl HwMon {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn card(&self) -> &Card {
|
||||
&self.card
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn name(&self) -> std::io::Result<String> {
|
||||
self.read("name")
|
||||
@ -89,12 +116,6 @@ impl HwMon {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_fan_manual(&self) -> bool {
|
||||
self.read(PULSE_WIDTH_MODULATION_MODE)
|
||||
.map(|s| s.as_str() == PULSE_WIDTH_MODULATION_MANUAL)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn is_fan_automatic(&self) -> bool {
|
||||
self.read(PULSE_WIDTH_MODULATION_MODE)
|
||||
.map(|s| s.as_str() == PULSE_WIDTH_MODULATION_AUTO)
|
||||
@ -190,7 +211,7 @@ fn hw_mon_dir_path(card: &Card, name: &str) -> std::path::PathBuf {
|
||||
hw_mon_dirs_path(card).join(name)
|
||||
}
|
||||
|
||||
pub(crate) fn open_hw_mon(card: Card) -> std::io::Result<HwMon> {
|
||||
pub(crate) fn open_hw_mon(card: Card, config: &Config) -> std::io::Result<HwMon> {
|
||||
let read_path = hw_mon_dirs_path(&card);
|
||||
let entries = std::fs::read_dir(read_path)?;
|
||||
let name = entries
|
||||
@ -206,5 +227,5 @@ pub(crate) fn open_hw_mon(card: Card) -> std::io::Result<HwMon> {
|
||||
.take(1)
|
||||
.last()
|
||||
.ok_or_else(not_found)?;
|
||||
Ok(HwMon::new(&card, &name))
|
||||
Ok(HwMon::new(&card, &name, config))
|
||||
}
|
||||
|
17
src/main.rs
17
src/main.rs
@ -3,7 +3,7 @@ use std::io::ErrorKind;
|
||||
|
||||
use gumdrop::Options;
|
||||
|
||||
use crate::config::{load_config, Card};
|
||||
use crate::config::load_config;
|
||||
|
||||
mod config;
|
||||
mod fan;
|
||||
@ -47,15 +47,6 @@ impl std::fmt::Display for AmdFanError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HwMon {
|
||||
card: Card,
|
||||
name: String,
|
||||
pwm_min: Option<u32>,
|
||||
pwm_max: Option<u32>,
|
||||
temp_inputs: Vec<String>,
|
||||
}
|
||||
|
||||
pub enum FanMode {
|
||||
Manual,
|
||||
Automatic,
|
||||
@ -86,7 +77,7 @@ fn main() -> std::io::Result<()> {
|
||||
let opts: Opts = Opts::parse_args_default_or_exit();
|
||||
|
||||
if opts.version {
|
||||
println!("{}", env!("CARGO_PKG_VERSION"));
|
||||
println!("amdfand {}", env!("CARGO_PKG_VERSION"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@ -102,12 +93,12 @@ fn main() -> std::io::Result<()> {
|
||||
}
|
||||
Some(fan::FanCommand::Available(_)) => {
|
||||
println!("Available cards");
|
||||
utils::controllers(&config, false)?
|
||||
utils::hw_mons(&config, false)?
|
||||
.into_iter()
|
||||
.for_each(|hw_mon| {
|
||||
println!(
|
||||
" * {:6>} - {}",
|
||||
hw_mon.card,
|
||||
hw_mon.card(),
|
||||
hw_mon.name().unwrap_or_default()
|
||||
);
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::config::{Card, Config};
|
||||
use crate::{hw_mon, HwMon, ROOT_DIR};
|
||||
use crate::hw_mon::HwMon;
|
||||
use crate::{hw_mon, ROOT_DIR};
|
||||
|
||||
/// linear mapping from the xrange to the yrange
|
||||
pub fn linear_map(x: f64, x1: f64, x2: f64, y1: f64, y2: f64) -> f64 {
|
||||
@ -19,11 +20,10 @@ pub fn read_cards() -> std::io::Result<Vec<Card>> {
|
||||
|
||||
/// Wrap cards in HW Mon manipulator and
|
||||
/// filter cards so only amd and listed in config cards are accessible
|
||||
pub fn controllers(config: &Config, filter: bool) -> std::io::Result<Vec<HwMon>> {
|
||||
pub fn hw_mons(config: &Config, filter: bool) -> std::io::Result<Vec<HwMon>> {
|
||||
Ok(read_cards()?
|
||||
.into_iter()
|
||||
.filter(|card| !filter || config.cards().iter().any(|name| **name == **card))
|
||||
.filter_map(|card| hw_mon::open_hw_mon(card).ok())
|
||||
.filter_map(|card| hw_mon::open_hw_mon(card, config).ok())
|
||||
.filter(|hw_mon| !filter || { hw_mon.is_amd() })
|
||||
.filter(|hw_mon| !filter || hw_mon.name_is_amd())
|
||||
.collect())
|
||||
|
Loading…
Reference in New Issue
Block a user