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