You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.2 KiB
Rust
185 lines
5.2 KiB
Rust
use std::fs::File;
|
|
|
|
use crate::{error::IoctlError, config::open_device_file, read, write};
|
|
|
|
pub const MAX_FAN_SPEED: u8 = 0xff;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum Fan {
|
|
Fan1,
|
|
Fan2,
|
|
Fan3,
|
|
}
|
|
|
|
impl Fan {
|
|
pub fn try_from_u8(num: u8) -> Option<Self> {
|
|
match num {
|
|
0 => Some(Self::Fan1),
|
|
1 => Some(Self::Fan2),
|
|
2 => Some(Self::Fan3),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub enum PerformanceProfile {
|
|
Quiet,
|
|
Powersave,
|
|
Entertainment,
|
|
Performance,
|
|
}
|
|
|
|
impl Default for PerformanceProfile {
|
|
fn default() -> Self {
|
|
Self::Performance
|
|
}
|
|
}
|
|
|
|
impl PerformanceProfile {
|
|
fn as_clevo_arg(&self) -> u32 {
|
|
match self {
|
|
PerformanceProfile::Quiet => 0x00,
|
|
PerformanceProfile::Powersave => 0x01,
|
|
PerformanceProfile::Entertainment => 0x02,
|
|
PerformanceProfile::Performance => 0x03,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct IoInterface {
|
|
file: File,
|
|
}
|
|
|
|
impl IoInterface {
|
|
pub fn new() -> Result<Self, std::io::Error> {
|
|
let file = open_device_file()?;
|
|
|
|
if read::hwcheck_cl(&file).is_ok() {
|
|
Ok(Self { file })
|
|
} else {
|
|
todo!("Uniwill isn't supported yet");
|
|
}
|
|
}
|
|
|
|
pub fn set_fan_speed_percent(
|
|
&mut self,
|
|
fan: Fan,
|
|
fan_speed_percent: u8,
|
|
) -> Result<(), IoctlError> {
|
|
let mut fan_speed_raw: [u8; 3] = [0; 3];
|
|
|
|
let fan_speed_percent = fan_speed_percent.clamp(0, 100);
|
|
|
|
for (i, fan_speed) in fan_speed_raw.iter_mut().enumerate() {
|
|
let selected_fan = Fan::try_from_u8(i as u8).unwrap();
|
|
if selected_fan == fan {
|
|
*fan_speed = (fan_speed_percent as f64 * 0xFF as f64 / 100.0).round() as u8;
|
|
} else {
|
|
*fan_speed = self.read_fanspeed_raw(fan)?;
|
|
}
|
|
}
|
|
|
|
let mut argument: u32 = fan_speed_raw[0] as u32;
|
|
argument |= (fan_speed_raw[1] as u32) << 0x08;
|
|
argument |= (fan_speed_raw[2] as u32) << 0x10;
|
|
write::cl_fanspeed(&self.file, argument)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get_fan_speed_percent(&self, fan: Fan) -> Result<u8, IoctlError> {
|
|
let fan_speed_raw = self.read_fanspeed_raw(fan)?;
|
|
Ok(((fan_speed_raw as f64 / MAX_FAN_SPEED as f64) * 100.0).round() as u8)
|
|
}
|
|
|
|
fn read_fanspeed_raw(&self, fan: Fan) -> Result<u8, IoctlError> {
|
|
self.read_faninfo_raw(fan)
|
|
.map(|value| (value & 0xFF).try_into().unwrap())
|
|
}
|
|
|
|
fn read_faninfo_raw(&self, fan: Fan) -> Result<u32, IoctlError> {
|
|
match fan {
|
|
Fan::Fan1 => read::cl_faninfo1(&self.file),
|
|
Fan::Fan2 => read::cl_faninfo2(&self.file),
|
|
Fan::Fan3 => read::cl_faninfo3(&self.file),
|
|
}
|
|
}
|
|
|
|
pub fn set_fans_auto(&self) -> Result<(), IoctlError> {
|
|
write::cl_fanauto(&self.file, 0xF)
|
|
}
|
|
|
|
// I'm not sure if that works though...
|
|
pub fn set_fans_manual(&self) -> Result<(), IoctlError> {
|
|
write::cl_fanauto(&self.file, 0x0)
|
|
}
|
|
|
|
pub fn set_web_cam_enabled(&self, status: bool) -> Result<(), IoctlError> {
|
|
write::cl_webcam_sw(&self.file, if status { 1 } else { 0 })
|
|
}
|
|
|
|
pub fn get_web_cam_enabled(&self) -> Result<bool, IoctlError> {
|
|
read::cl_webcam_sw(&self.file).map(|val| val != 0)
|
|
}
|
|
|
|
pub fn set_performance_profile(&self, profile: PerformanceProfile) -> Result<(), IoctlError> {
|
|
write::cl_perf_profile(&self.file, profile.as_clevo_arg())
|
|
}
|
|
|
|
pub fn get_fan_temperature(&self, fan: Fan) -> Result<u8, IoctlError> {
|
|
let fan_info_raw = self.read_faninfo_raw(fan)?;
|
|
|
|
// Explicitly use temp2 since more consistently implemented
|
|
//int fanTemp1 = (int8_t) ((fanInfo >> 0x08) & 0xff);
|
|
let fan_temp_2: u8 = ((fan_info_raw >> 0x10) & 0xff).try_into().unwrap();
|
|
|
|
// If a fan is not available a low value is read out
|
|
if fan_temp_2 <= 1 {
|
|
Err(IoctlError::DevNotAvailable)
|
|
} else {
|
|
Ok(fan_temp_2)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn interface() {
|
|
sudo::escalate_if_needed().unwrap();
|
|
|
|
let mut io = IoInterface::new().unwrap();
|
|
|
|
// Check webcam
|
|
io.set_web_cam_enabled(false).unwrap();
|
|
assert_eq!(io.get_web_cam_enabled().unwrap(), false);
|
|
|
|
io.set_web_cam_enabled(true).unwrap();
|
|
assert_eq!(io.get_web_cam_enabled().unwrap(), true);
|
|
|
|
// Set performance profile
|
|
io.set_performance_profile(PerformanceProfile::Quiet)
|
|
.unwrap();
|
|
|
|
// Get temperatures
|
|
assert!(20 < io.get_fan_temperature(Fan::Fan1).unwrap());
|
|
assert_eq!(
|
|
io.get_fan_temperature(Fan::Fan2).unwrap_err(),
|
|
IoctlError::DevNotAvailable
|
|
);
|
|
|
|
// Check fans
|
|
io.set_fan_speed_percent(Fan::Fan1, 100).unwrap();
|
|
assert_eq!(io.get_fan_speed_percent(Fan::Fan1).unwrap(), 100);
|
|
|
|
io.set_fan_speed_percent(Fan::Fan1, 50).unwrap();
|
|
assert_eq!(io.get_fan_speed_percent(Fan::Fan1).unwrap(), 50);
|
|
|
|
io.set_fan_speed_percent(Fan::Fan1, 10).unwrap();
|
|
assert_eq!(io.get_fan_speed_percent(Fan::Fan1).unwrap(), 10);
|
|
}
|
|
}
|