use super::TemperatureResult;
use libloading::{Library, Symbol};
use once_cell::sync::OnceCell;
use std::ffi::c_int;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
fn read_lock<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
lock.read().unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn write_lock<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
lock.write()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
const AMD_DLL_PATHS: &[&str] = &[
"C:\\Program Files\\AMD\\RyzenMaster\\bin\\AMDRyzenMasterMonitoringDLL.dll",
"C:\\Program Files (x86)\\AMD\\RyzenMaster\\bin\\AMDRyzenMasterMonitoringDLL.dll",
"C:\\Program Files\\AMD\\CNext\\CNext\\AMDRyzenMasterMonitoringDLL.dll",
];
#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
pub struct RmQuickStats {
pub d_temperature: f64,
pub d_peak_core_speed: f64,
pub d_avg_core_speed: f64,
pub d_fabric_clock: f64,
pub d_mem_clock: f64,
pub d_cpu_power: f64,
pub d_soc_power: f64,
}
const _: () = assert!(
std::mem::size_of::<RmQuickStats>() == 56,
"RmQuickStats size mismatch - expected 56 bytes (7 × f64)"
);
type PlatformInitFn = unsafe extern "C" fn() -> c_int;
type ShortQueryFn = unsafe extern "C" fn(*mut RmQuickStats) -> c_int;
type PlatformUninitFn = unsafe extern "C" fn() -> c_int;
struct AmdLibraryState {
library: Library,
initialized: bool,
}
pub struct AmdRyzenSource {
library_state: RwLock<Option<AmdLibraryState>>,
load_attempted: OnceCell<bool>,
}
impl Default for AmdRyzenSource {
fn default() -> Self {
Self::new()
}
}
impl AmdRyzenSource {
pub fn new() -> Self {
Self {
library_state: RwLock::new(None),
load_attempted: OnceCell::new(),
}
}
fn try_load_library(&self) -> bool {
*self.load_attempted.get_or_init(|| self.do_load_library())
}
fn do_load_library(&self) -> bool {
for path in AMD_DLL_PATHS {
match unsafe { Library::new(*path) } {
Ok(lib) => {
let init_result: Result<Symbol<PlatformInitFn>, _> =
unsafe { lib.get(b"PlatformInit\0") };
if let Ok(init_fn) = init_result {
let result = unsafe { init_fn() };
if result == 0 {
*write_lock(&self.library_state) = Some(AmdLibraryState {
library: lib,
initialized: true,
});
return true;
}
}
}
Err(_) => continue,
}
}
false
}
pub fn get_temperature(&self) -> TemperatureResult {
if !self.try_load_library() {
return TemperatureResult::NotFound;
}
let state_guard = read_lock(&self.library_state);
let state = match state_guard.as_ref() {
Some(s) if s.initialized => s,
_ => return TemperatureResult::NotFound,
};
let query_fn: Result<Symbol<ShortQueryFn>, _> =
unsafe { state.library.get(b"ShortQuery\0") };
match query_fn {
Ok(query) => {
let mut stats = RmQuickStats::default();
let result = unsafe { query(&mut stats) };
if result == 0 {
let temp = stats.d_temperature;
if temp > 0.0 && temp < 150.0 {
return TemperatureResult::Success(temp.round() as u32);
}
TemperatureResult::NoValidReading
} else {
TemperatureResult::Error
}
}
Err(_) => TemperatureResult::Error,
}
}
}
impl Drop for AmdRyzenSource {
fn drop(&mut self) {
if let Some(state) = write_lock(&self.library_state).take()
&& state.initialized
{
let uninit_fn: Result<Symbol<PlatformUninitFn>, _> =
unsafe { state.library.get(b"PlatformUninit\0") };
if let Ok(uninit) = uninit_fn {
unsafe {
uninit();
}
}
}
}
}