redbear-power: consume real /scheme/coretemp and /scheme/sys/cpu sources

This commit is contained in:
2026-07-08 01:14:26 +03:00
parent 838e17f4fa
commit eeb0292572
2 changed files with 49 additions and 5 deletions
@@ -2,12 +2,16 @@
//!
//! Probes the host kernel once at startup and selects the correct
//! path for each data source (MSR, ACPI PSS, /proc/stat, cpufreq
//! sysfs, hwmon temperatures). On Redox OS the `/scheme/sys/...`
//! sysfs, hwmon temperatures). On Red Bear OS the `/scheme/sys/...`
//! paths are tried first; on Linux the equivalent `/dev/cpu/*/msr`,
//! `/proc/stat`, and `/sys/devices/system/cpu/cpu*/cpufreq/` paths
//! are used as fallbacks so the TUI shows live data on a developer
//! host too.
//!
//! Red Bear OS data sources are now real (not stubs): the kernel
//! serves hardware MSRs through `/scheme/sys/msr/` and per-CPU
//! scheduler statistics through `/scheme/sys/cpu/{n}/stat`.
//!
//! Each probe emits exactly one `eprintln!` line at startup naming
//! the data source and the failure mode. This matches cpu-x's
//! `MSG_VERBOSE` pattern (core.cpp:380-410) and lets the user know
@@ -11,16 +11,19 @@
//! - `curr*_input` — current in milli-Amps
//! - `*_label` — human-readable label for the corresponding `_input`
//!
//! On Redox, the `hwmon` scheme is not yet implemented, so when the sysfs
//! path is absent the module falls back to reading the CPU package
//! temperature via the `IA32_THERM_STATUS` MSR exposed through
//! `/scheme/sys/msr/`.
//! On Red Bear OS the thermal surface is provided by daemons:
//! - `/scheme/thermal/zones/` — thermal zones from `thermald` (ACPI + CPU die)
//! - `/scheme/coretemp/` — per-CPU die temperatures from `coretempd`
//! - `/scheme/sys/msr/` — direct MSR fallback for CPU package temperature
//!
//! The Redox sources are preferred over the Linux sysfs path when present.
use std::fs;
use std::path::{Path, PathBuf};
const SYS_HWMON: &str = "/sys/class/hwmon";
const REDOX_THERMAL: &str = "/scheme/thermal";
const REDOX_CORETEMP: &str = "/scheme/coretemp";
#[derive(Default, Clone, Debug)]
pub struct SensorReading {
@@ -175,6 +178,37 @@ fn read_redox_thermal_zones() -> Vec<HwmonChip> {
chips
}
/// Enumerate Redox `/scheme/coretemp/` directories and return one
/// `HwmonChip`-like entry per CPU with a temperature reading.
fn read_redox_coretemp() -> Vec<HwmonChip> {
let root = Path::new(REDOX_CORETEMP);
let Ok(entries) = fs::read_dir(root) else {
return Vec::new();
};
let mut chips = Vec::new();
for entry in entries.flatten() {
let name = match entry.file_name().into_string() {
Ok(n) => n,
Err(_) => continue,
};
let temp_path = root.join(&name).join("temperature");
let Some(temp_raw) = read_sysfs_i64(&temp_path) else {
continue;
};
chips.push(HwmonChip {
name: "coretemp".to_string(),
path: root.join(&name),
readings: vec![SensorReading {
kind: SensorKind::Temp,
label: Some(format!("CPU {}", name)),
raw_value: temp_raw * 1000,
display_value: format_sensor(SensorKind::Temp, temp_raw * 1000),
}],
});
}
chips
}
/// Read all `*_input` files in the chip directory, grouped by prefix.
fn read_chip_readings(chip_dir: &Path) -> Vec<SensorReading> {
let entries = match fs::read_dir(chip_dir) {
@@ -250,6 +284,12 @@ impl SensorInfo {
return Self { chips };
}
}
if Path::new(REDOX_CORETEMP).exists() {
let chips = read_redox_coretemp();
if !chips.is_empty() {
return Self { chips };
}
}
let Ok(dirs) = fs::read_dir(SYS_HWMON) else {
if let Some(temp_c) = read_first_cpu_temp_msr() {
return Self {