Add DIMM Temperature to LHM. (#1743)
* Add DIMM Temperature to LHM. * Remove RAMSPDToolkit as submodule and use nuget package instead. Upgrade System.Management assembly (required for nuget package). Implement retry mechanism for RAM detection due to possible issue at system boot. * Change to static field. * Driver now back in LHM. Ring0 replaced with OLS implementation. * Update package version. * Undo most of changes. Implement IDriver interface. * Change package to new one. * Review changes. Explicitly set UseWMI (current default is also false). * PR comments: Now having one DimmMemory instance per RAM stick. Separated memory sensors into two new classes. * PR feedback --------- Co-authored-by: PhyxionNL <7643972+PhyxionNL@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using LibreHardwareMonitor.Hardware.Memory.Sensors;
|
||||
using RAMSPDToolkit.SPD;
|
||||
using RAMSPDToolkit.SPD.Enums;
|
||||
using RAMSPDToolkit.SPD.Interfaces;
|
||||
using RAMSPDToolkit.SPD.Interop.Shared;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal sealed class DimmMemory : Hardware
|
||||
{
|
||||
private readonly SpdThermalSensor _thermalSensor;
|
||||
|
||||
public DimmMemory(SPDAccessor accessor, string name, Identifier identifier, ISettings settings)
|
||||
: base(name, identifier, settings)
|
||||
{
|
||||
//Check which kind of RAM we have
|
||||
switch (accessor.MemoryType())
|
||||
{
|
||||
case SPDMemoryType.SPD_DDR4_SDRAM:
|
||||
case SPDMemoryType.SPD_DDR4E_SDRAM:
|
||||
case SPDMemoryType.SPD_LPDDR4_SDRAM:
|
||||
case SPDMemoryType.SPD_LPDDR4X_SDRAM:
|
||||
_thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}",
|
||||
accessor.Index,
|
||||
SensorType.Temperature,
|
||||
this,
|
||||
settings,
|
||||
accessor as IThermalSensor);
|
||||
|
||||
break;
|
||||
case SPDMemoryType.SPD_DDR5_SDRAM:
|
||||
case SPDMemoryType.SPD_LPDDR5_SDRAM:
|
||||
//Check if we are on correct page or if write protection is not enabled
|
||||
if (accessor.PageData.HasFlag(PageData.ThermalData) || !accessor.HasSPDWriteProtection)
|
||||
{
|
||||
_thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}",
|
||||
accessor.Index,
|
||||
SensorType.Temperature,
|
||||
this,
|
||||
settings,
|
||||
accessor as IThermalSensor);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Add thermal sensor
|
||||
if (_thermalSensor != null)
|
||||
ActivateSensor(_thermalSensor);
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Memory;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
_thermalSensor?.UpdateSensor();
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal sealed class GenericLinuxMemory : Hardware
|
||||
{
|
||||
private readonly Sensor _physicalMemoryAvailable;
|
||||
private readonly Sensor _physicalMemoryLoad;
|
||||
private readonly Sensor _physicalMemoryUsed;
|
||||
private readonly Sensor _virtualMemoryAvailable;
|
||||
private readonly Sensor _virtualMemoryLoad;
|
||||
private readonly Sensor _virtualMemoryUsed;
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Memory;
|
||||
|
||||
public GenericLinuxMemory(string name, ISettings settings) : base(name, new Identifier("ram"), settings)
|
||||
{
|
||||
_physicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings);
|
||||
ActivateSensor(_physicalMemoryUsed);
|
||||
|
||||
_physicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings);
|
||||
ActivateSensor(_physicalMemoryAvailable);
|
||||
|
||||
_physicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings);
|
||||
ActivateSensor(_physicalMemoryLoad);
|
||||
|
||||
_virtualMemoryUsed = new Sensor("Virtual Memory Used", 2, SensorType.Data, this, settings);
|
||||
ActivateSensor(_virtualMemoryUsed);
|
||||
|
||||
_virtualMemoryAvailable = new Sensor("Virtual Memory Available", 3, SensorType.Data, this, settings);
|
||||
ActivateSensor(_virtualMemoryAvailable);
|
||||
|
||||
_virtualMemoryLoad = new Sensor("Virtual Memory", 1, SensorType.Load, this, settings);
|
||||
ActivateSensor(_virtualMemoryLoad);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] memoryInfo = File.ReadAllLines("/proc/meminfo");
|
||||
|
||||
{
|
||||
float totalMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemTotal:"))) / 1024.0f / 1024.0f;
|
||||
float freeMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemFree:"))) / 1024.0f / 1024.0f;
|
||||
float cachedMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("Cached:"))) / 1024.0f / 1024.0f;
|
||||
|
||||
float usedMemory_GB = totalMemory_GB - freeMemory_GB - cachedMemory_GB;
|
||||
|
||||
_physicalMemoryUsed.Value = usedMemory_GB;
|
||||
_physicalMemoryAvailable.Value = totalMemory_GB;
|
||||
_physicalMemoryLoad.Value = 100.0f * (usedMemory_GB / totalMemory_GB);
|
||||
}
|
||||
{
|
||||
float totalSwapMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapTotal"))) / 1024.0f / 1024.0f;
|
||||
float freeSwapMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapFree"))) / 1024.0f / 1024.0f;
|
||||
float usedSwapMemory_GB = totalSwapMemory_GB - freeSwapMemory_GB;
|
||||
|
||||
_virtualMemoryUsed.Value = usedSwapMemory_GB;
|
||||
_virtualMemoryAvailable.Value = totalSwapMemory_GB;
|
||||
_virtualMemoryLoad.Value = 100.0f * (usedSwapMemory_GB / totalSwapMemory_GB);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_physicalMemoryUsed.Value = null;
|
||||
_physicalMemoryAvailable.Value = null;
|
||||
_physicalMemoryLoad.Value = null;
|
||||
|
||||
_virtualMemoryUsed.Value = null;
|
||||
_virtualMemoryAvailable.Value = null;
|
||||
_virtualMemoryLoad.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static long GetMemInfoValue(string line)
|
||||
{
|
||||
// Example: "MemTotal: 32849676 kB"
|
||||
|
||||
string valueWithUnit = line.Split(':').Skip(1).First().Trim();
|
||||
string valueAsString = valueWithUnit.Split(' ').First();
|
||||
|
||||
return long.Parse(valueAsString);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal sealed class GenericWindowsMemory : Hardware
|
||||
{
|
||||
private readonly Sensor _physicalMemoryAvailable;
|
||||
private readonly Sensor _physicalMemoryLoad;
|
||||
private readonly Sensor _physicalMemoryUsed;
|
||||
private readonly Sensor _virtualMemoryAvailable;
|
||||
private readonly Sensor _virtualMemoryLoad;
|
||||
private readonly Sensor _virtualMemoryUsed;
|
||||
|
||||
public GenericWindowsMemory(string name, ISettings settings) : base(name, new Identifier("ram"), settings)
|
||||
{
|
||||
_physicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings);
|
||||
ActivateSensor(_physicalMemoryUsed);
|
||||
|
||||
_physicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings);
|
||||
ActivateSensor(_physicalMemoryAvailable);
|
||||
|
||||
_physicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings);
|
||||
ActivateSensor(_physicalMemoryLoad);
|
||||
|
||||
_virtualMemoryUsed = new Sensor("Virtual Memory Used", 2, SensorType.Data, this, settings);
|
||||
ActivateSensor(_virtualMemoryUsed);
|
||||
|
||||
_virtualMemoryAvailable = new Sensor("Virtual Memory Available", 3, SensorType.Data, this, settings);
|
||||
ActivateSensor(_virtualMemoryAvailable);
|
||||
|
||||
_virtualMemoryLoad = new Sensor("Virtual Memory", 1, SensorType.Load, this, settings);
|
||||
ActivateSensor(_virtualMemoryLoad);
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Memory; }
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf<Kernel32.MEMORYSTATUSEX>() };
|
||||
|
||||
if (!Kernel32.GlobalMemoryStatusEx(ref status))
|
||||
return;
|
||||
|
||||
_physicalMemoryUsed.Value = (float)(status.ullTotalPhys - status.ullAvailPhys) / (1024 * 1024 * 1024);
|
||||
_physicalMemoryAvailable.Value = (float)status.ullAvailPhys / (1024 * 1024 * 1024);
|
||||
_physicalMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPhys) / status.ullTotalPhys);
|
||||
|
||||
_virtualMemoryUsed.Value = (float)(status.ullTotalPageFile - status.ullAvailPageFile) / (1024 * 1024 * 1024);
|
||||
_virtualMemoryAvailable.Value = (float)status.ullAvailPageFile / (1024 * 1024 * 1024);
|
||||
_virtualMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPageFile) / status.ullTotalPageFile);
|
||||
}
|
||||
}
|
||||
@@ -5,28 +5,137 @@
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Timers;
|
||||
using RAMSPDToolkit.I2CSMBus;
|
||||
using RAMSPDToolkit.SPD;
|
||||
using RAMSPDToolkit.SPD.Enums;
|
||||
using RAMSPDToolkit.SPD.Interfaces;
|
||||
using RAMSPDToolkit.SPD.Interop.Shared;
|
||||
using RAMSPDToolkit.Windows.Driver;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal class MemoryGroup : IGroup
|
||||
{
|
||||
private readonly Hardware[] _hardware;
|
||||
//Retry 12x
|
||||
private const int RetryCount = 12;
|
||||
|
||||
//Retry every 2.5 seconds
|
||||
private const double RetryTime = 2500;
|
||||
private static readonly object _lock = new();
|
||||
|
||||
private readonly List<Hardware> _hardware = [];
|
||||
private int _elapsedCounter;
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
static MemoryGroup()
|
||||
{
|
||||
if (Ring0.IsOpen)
|
||||
{
|
||||
//Assign implementation of IDriver
|
||||
DriverManager.Driver = new RAMSPDToolkitDriver(Ring0.KernelDriver);
|
||||
SMBusManager.UseWMI = false;
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryGroup(ISettings settings)
|
||||
{
|
||||
_hardware = new Hardware[] { Software.OperatingSystem.IsUnix ? new GenericLinuxMemory("Generic Memory", settings) : new GenericWindowsMemory("Generic Memory", settings) };
|
||||
_hardware.Add(new VirtualMemory(settings));
|
||||
_hardware.Add(new TotalMemory(settings));
|
||||
|
||||
//No RAM detected
|
||||
if (!DetectThermalSensors(out List<SPDAccessor> accessors))
|
||||
{
|
||||
//Retry a couple of times
|
||||
//SMBus might not be detected right after boot
|
||||
_timer = new Timer(RetryTime);
|
||||
|
||||
_timer.Elapsed += (_, _) =>
|
||||
{
|
||||
if (_elapsedCounter++ >= RetryCount || DetectThermalSensors(out accessors))
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer = null;
|
||||
|
||||
if (accessors != null)
|
||||
AddDimms(accessors, settings);
|
||||
}
|
||||
};
|
||||
|
||||
_timer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddDimms(accessors, settings);
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (Hardware ram in _hardware)
|
||||
ram.Close();
|
||||
}
|
||||
|
||||
private static bool DetectThermalSensors(out List<SPDAccessor> accessors)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var list = new List<SPDAccessor>();
|
||||
|
||||
bool ramDetected = false;
|
||||
|
||||
SMBusManager.DetectSMBuses();
|
||||
|
||||
//Go through detected SMBuses
|
||||
foreach (var smbus in SMBusManager.RegisteredSMBuses)
|
||||
{
|
||||
//Go through possible RAM slots
|
||||
for (byte i = SPDConstants.SPD_BEGIN; i <= SPDConstants.SPD_END; ++i)
|
||||
{
|
||||
//Detect type of RAM, if available
|
||||
var detector = new SPDDetector(smbus, i);
|
||||
|
||||
//RAM available and detected
|
||||
if (detector.Accessor != null)
|
||||
{
|
||||
//We are only interested in modules with thermal sensor
|
||||
if (detector.Accessor is IThermalSensor { HasThermalSensor: true })
|
||||
list.Add(detector.Accessor);
|
||||
|
||||
ramDetected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accessors = list.Count > 0 ? list : [];
|
||||
return ramDetected;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDimms(List<SPDAccessor> accessors, ISettings settings)
|
||||
{
|
||||
foreach (var ram in accessors)
|
||||
{
|
||||
//Default value
|
||||
string name = $"DIMM #{ram.Index}";
|
||||
|
||||
//Check if we can switch to the correct page
|
||||
if (ram.ChangePage(PageData.ModulePartNumber))
|
||||
{
|
||||
name = $"{ram.GetModuleManufacturerString()} - {ram.ModulePartNumber()} (#{ram.Index})";
|
||||
}
|
||||
|
||||
var memory = new DimmMemory(ram, name, new Identifier("ram"), settings);
|
||||
|
||||
_hardware.Add(memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal static class MemoryLinux
|
||||
{
|
||||
public static void Update(TotalMemory memory)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] memoryInfo = File.ReadAllLines("/proc/meminfo");
|
||||
|
||||
{
|
||||
float totalMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemTotal:"))) / 1024.0f / 1024.0f;
|
||||
float freeMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemFree:"))) / 1024.0f / 1024.0f;
|
||||
float cachedMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("Cached:"))) / 1024.0f / 1024.0f;
|
||||
|
||||
float usedMemoryGb = totalMemoryGb - freeMemoryGb - cachedMemoryGb;
|
||||
|
||||
memory.PhysicalMemoryUsed.Value = usedMemoryGb;
|
||||
memory.PhysicalMemoryAvailable.Value = totalMemoryGb;
|
||||
memory.PhysicalMemoryLoad.Value = 100.0f * (usedMemoryGb / totalMemoryGb);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
memory.PhysicalMemoryUsed.Value = null;
|
||||
memory.PhysicalMemoryAvailable.Value = null;
|
||||
memory.PhysicalMemoryLoad.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Update(VirtualMemory memory)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] memoryInfo = File.ReadAllLines("/proc/meminfo");
|
||||
|
||||
{
|
||||
float totalSwapMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapTotal"))) / 1024.0f / 1024.0f;
|
||||
float freeSwapMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapFree"))) / 1024.0f / 1024.0f;
|
||||
float usedSwapMemoryGb = totalSwapMemoryGb - freeSwapMemoryGb;
|
||||
|
||||
memory.VirtualMemoryUsed.Value = usedSwapMemoryGb;
|
||||
memory.VirtualMemoryAvailable.Value = totalSwapMemoryGb;
|
||||
memory.VirtualMemoryLoad.Value = 100.0f * (usedSwapMemoryGb / totalSwapMemoryGb);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
memory.VirtualMemoryUsed.Value = null;
|
||||
memory.VirtualMemoryAvailable.Value = null;
|
||||
memory.VirtualMemoryLoad.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static long GetMemInfoValue(string line)
|
||||
{
|
||||
// Example: "MemTotal: 32849676 kB"
|
||||
|
||||
string valueWithUnit = line.Split(':').Skip(1).First().Trim();
|
||||
string valueAsString = valueWithUnit.Split(' ').First();
|
||||
|
||||
return long.Parse(valueAsString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal static class MemoryWindows
|
||||
{
|
||||
public static void Update(TotalMemory memory)
|
||||
{
|
||||
Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf<Kernel32.MEMORYSTATUSEX>() };
|
||||
|
||||
if (!Kernel32.GlobalMemoryStatusEx(ref status))
|
||||
return;
|
||||
|
||||
memory.PhysicalMemoryUsed.Value = (float)(status.ullTotalPhys - status.ullAvailPhys) / (1024 * 1024 * 1024);
|
||||
memory.PhysicalMemoryAvailable.Value = (float)status.ullAvailPhys / (1024 * 1024 * 1024);
|
||||
memory.PhysicalMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPhys) / status.ullTotalPhys);
|
||||
}
|
||||
|
||||
public static void Update(VirtualMemory memory)
|
||||
{
|
||||
Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf<Kernel32.MEMORYSTATUSEX>() };
|
||||
|
||||
if (!Kernel32.GlobalMemoryStatusEx(ref status))
|
||||
return;
|
||||
|
||||
memory.VirtualMemoryUsed.Value = (float)(status.ullTotalPageFile - status.ullAvailPageFile) / (1024 * 1024 * 1024);
|
||||
memory.VirtualMemoryAvailable.Value = (float)status.ullAvailPageFile / (1024 * 1024 * 1024);
|
||||
memory.VirtualMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPageFile) / status.ullTotalPageFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using RAMSPDToolkit.SPD.Interfaces;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory.Sensors;
|
||||
|
||||
internal class SpdThermalSensor(string name, int index, SensorType sensorType, Hardware hardware, ISettings settings, IThermalSensor thermalSensor)
|
||||
: Sensor(name, index, sensorType, hardware, settings)
|
||||
{
|
||||
public bool UpdateSensor()
|
||||
{
|
||||
if (!thermalSensor.UpdateTemperature())
|
||||
return false;
|
||||
|
||||
Value = thermalSensor.Temperature;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal sealed class TotalMemory : Hardware
|
||||
{
|
||||
public TotalMemory(ISettings settings)
|
||||
: base("Total Memory", new Identifier("ram"), settings)
|
||||
{
|
||||
PhysicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings);
|
||||
ActivateSensor(PhysicalMemoryUsed);
|
||||
|
||||
PhysicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings);
|
||||
ActivateSensor(PhysicalMemoryAvailable);
|
||||
|
||||
PhysicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings);
|
||||
ActivateSensor(PhysicalMemoryLoad);
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Memory;
|
||||
|
||||
internal Sensor PhysicalMemoryAvailable { get; }
|
||||
|
||||
internal Sensor PhysicalMemoryLoad { get; }
|
||||
|
||||
internal Sensor PhysicalMemoryUsed { get; }
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (Software.OperatingSystem.IsUnix)
|
||||
{
|
||||
MemoryLinux.Update(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryWindows.Update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Memory;
|
||||
|
||||
internal sealed class VirtualMemory : Hardware
|
||||
{
|
||||
public VirtualMemory(ISettings settings)
|
||||
: base("Virtual Memory", new Identifier("ram"), settings)
|
||||
{
|
||||
VirtualMemoryUsed = new Sensor("Memory Used", 2, SensorType.Data, this, settings);
|
||||
ActivateSensor(VirtualMemoryUsed);
|
||||
|
||||
VirtualMemoryAvailable = new Sensor("Memory Available", 3, SensorType.Data, this, settings);
|
||||
ActivateSensor(VirtualMemoryAvailable);
|
||||
|
||||
VirtualMemoryLoad = new Sensor("Memory", 1, SensorType.Load, this, settings);
|
||||
ActivateSensor(VirtualMemoryLoad);
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Memory;
|
||||
|
||||
internal Sensor VirtualMemoryAvailable { get; }
|
||||
|
||||
internal Sensor VirtualMemoryLoad { get; }
|
||||
|
||||
internal Sensor VirtualMemoryUsed { get; }
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (Software.OperatingSystem.IsUnix)
|
||||
{
|
||||
MemoryLinux.Update(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryWindows.Update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using RAMSPDToolkit.Windows.Driver;
|
||||
|
||||
using IOCC = LibreHardwareMonitor.Interop.Ring0;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IDriver"/> interface for RAMSPDToolkit.
|
||||
/// </summary>
|
||||
internal class RAMSPDToolkitDriver : IDriver
|
||||
{
|
||||
private KernelDriver _kernelDriver;
|
||||
|
||||
private const byte PCI_MAX_NUMBER_OF_BUS = 255;
|
||||
private const byte PCI_NUMBER_OF_DEVICE = 32;
|
||||
private const byte PCI_NUMBER_OF_FUNCTION = 8;
|
||||
|
||||
public bool IsOpen => _kernelDriver != null;
|
||||
|
||||
public RAMSPDToolkitDriver(KernelDriver kernelDriver)
|
||||
{
|
||||
_kernelDriver = kernelDriver;
|
||||
}
|
||||
|
||||
//Driver is being managed internally by other classes, so nothing to do in Load + Unload
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public byte ReadIoPortByte(ushort port)
|
||||
{
|
||||
byte value = 0;
|
||||
|
||||
ReadIoPortByteEx(port, ref value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public ushort ReadIoPortWord(ushort port)
|
||||
{
|
||||
ushort value = 0;
|
||||
|
||||
ReadIoPortWordEx(port, ref value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public uint ReadIoPortDword(ushort port)
|
||||
{
|
||||
uint value = 0;
|
||||
|
||||
ReadIoPortDwordEx(port, ref value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool ReadIoPortByteEx(ushort port, ref byte value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_BYTE, port, ref value);
|
||||
}
|
||||
|
||||
public bool ReadIoPortWordEx(ushort port, ref ushort value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_WORD, port, ref value);
|
||||
}
|
||||
|
||||
public bool ReadIoPortDwordEx(ushort port, ref uint value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_DWORD, port, ref value);
|
||||
}
|
||||
|
||||
public void WriteIoPortByte(ushort port, byte value)
|
||||
{
|
||||
WriteIoPortByteEx(port, value);
|
||||
}
|
||||
|
||||
public void WriteIoPortWord(ushort port, ushort value)
|
||||
{
|
||||
WriteIoPortWordEx(port, value);
|
||||
}
|
||||
|
||||
public void WriteIoPortDword(ushort port, uint value)
|
||||
{
|
||||
WriteIoPortDwordEx(port, value);
|
||||
}
|
||||
|
||||
public bool WriteIoPortByteEx(ushort port, byte value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
var input = new WriteIoPortInputByte { PortNumber = port, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_BYTE, input);
|
||||
}
|
||||
|
||||
public bool WriteIoPortWordEx(ushort port, ushort value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
var input = new WriteIoPortInputWord { PortNumber = port, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_WORD, input);
|
||||
}
|
||||
|
||||
public bool WriteIoPortDwordEx(ushort port, uint value)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return false;
|
||||
|
||||
var input = new WriteIoPortInputDword { PortNumber = port, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_DWORD, input);
|
||||
}
|
||||
|
||||
public uint FindPciDeviceById(ushort vendorId, ushort deviceId, byte index)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return uint.MaxValue;
|
||||
|
||||
if (vendorId == ushort.MaxValue)
|
||||
{
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
|
||||
for (byte bus = 0; bus < PCI_MAX_NUMBER_OF_BUS; ++bus)
|
||||
{
|
||||
for (byte dev = 0; dev < PCI_NUMBER_OF_DEVICE; ++dev)
|
||||
{
|
||||
bool multiFuncFlag = false;
|
||||
|
||||
for (byte func = 0; func < PCI_NUMBER_OF_FUNCTION; ++func)
|
||||
{
|
||||
if (!multiFuncFlag && func > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint pciAddress = Ring0.GetPciAddress(bus, dev, func);
|
||||
|
||||
uint id = 0;
|
||||
|
||||
if (PciConfigRead(pciAddress, 0, ref id))
|
||||
{
|
||||
if (func == 0) //Is Multi Function Device
|
||||
{
|
||||
byte type = 0;
|
||||
|
||||
if (PciConfigRead(pciAddress, 0x0E, ref type))
|
||||
{
|
||||
if ((type & 0x80) != 0)
|
||||
{
|
||||
multiFuncFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (id == (vendorId | ((uint)deviceId << 16)))
|
||||
{
|
||||
if (count == index)
|
||||
{
|
||||
return pciAddress;
|
||||
}
|
||||
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public uint FindPciDeviceByClass(byte baseClass, byte subClass, byte programIf, byte index)
|
||||
{
|
||||
if (!IsOpen)
|
||||
return uint.MaxValue;
|
||||
|
||||
uint count = 0;
|
||||
|
||||
for (byte bus = 0; bus < PCI_MAX_NUMBER_OF_BUS; ++bus)
|
||||
{
|
||||
for (byte dev = 0; dev < PCI_NUMBER_OF_DEVICE; ++dev)
|
||||
{
|
||||
bool multiFuncFlag = false;
|
||||
|
||||
for (byte func = 0; func < PCI_NUMBER_OF_FUNCTION; ++func)
|
||||
{
|
||||
if (!multiFuncFlag && func > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint pciAddress = Ring0.GetPciAddress(bus, dev, func);
|
||||
|
||||
var conf = new Conf();
|
||||
|
||||
if (PciConfigRead(pciAddress, 0, ref conf))
|
||||
{
|
||||
if (func == 0) //Is Multi Function Device
|
||||
{
|
||||
byte type = 0;
|
||||
|
||||
if (PciConfigRead(pciAddress, 0x0E, ref type))
|
||||
{
|
||||
if ((type & 0x80) != 0)
|
||||
{
|
||||
multiFuncFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var temp = (uint)baseClass << 24 | (uint)subClass << 16 | (uint)programIf << 8;
|
||||
|
||||
if ((conf.C & 0xFFFFFF00) == temp)
|
||||
{
|
||||
if (count == index)
|
||||
{
|
||||
return pciAddress;
|
||||
}
|
||||
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public byte ReadPciConfigByte(uint pciAddress, byte regAddress)
|
||||
{
|
||||
byte value = 0;
|
||||
|
||||
if (ReadPciConfigByteEx(pciAddress, regAddress, ref value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return byte.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadPciConfigWord(uint pciAddress, byte regAddress)
|
||||
{
|
||||
ushort value = 0;
|
||||
|
||||
if (ReadPciConfigWordEx(pciAddress, regAddress, ref value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ushort.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public uint ReadPciConfigDword(uint pciAddress, byte regAddress)
|
||||
{
|
||||
uint value = 0;
|
||||
|
||||
if (ReadPciConfigDwordEx(pciAddress, regAddress, ref value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return uint.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReadPciConfigByteEx(uint pciAddress, uint regAddress, ref byte value)
|
||||
{
|
||||
return PciConfigRead(pciAddress, regAddress, ref value);
|
||||
}
|
||||
|
||||
public bool ReadPciConfigWordEx(uint pciAddress, uint regAddress, ref ushort value)
|
||||
{
|
||||
return PciConfigRead(pciAddress, regAddress, ref value);
|
||||
}
|
||||
|
||||
public bool ReadPciConfigDwordEx(uint pciAddress, uint regAddress, ref uint value)
|
||||
{
|
||||
return PciConfigRead(pciAddress, regAddress, ref value);
|
||||
}
|
||||
|
||||
public void WritePciConfigByte(uint pciAddress, byte regAddress, byte value)
|
||||
{
|
||||
WritePciConfigByteEx(pciAddress, regAddress, value);
|
||||
}
|
||||
|
||||
public void WritePciConfigWord(uint pciAddress, byte regAddress, ushort value)
|
||||
{
|
||||
WritePciConfigWordEx(pciAddress, regAddress, value);
|
||||
}
|
||||
|
||||
public void WritePciConfigDword(uint pciAddress, byte regAddress, uint value)
|
||||
{
|
||||
WritePciConfigDwordEx(pciAddress, regAddress, value);
|
||||
}
|
||||
|
||||
public bool WritePciConfigByteEx(uint pciAddress, uint regAddress, byte value)
|
||||
{
|
||||
var input = new WritePciConfigInputByte { PciAddress = pciAddress, RegAddress = regAddress, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input);
|
||||
}
|
||||
|
||||
public bool WritePciConfigWordEx(uint pciAddress, uint regAddress, ushort value)
|
||||
{
|
||||
var input = new WritePciConfigInputWord { PciAddress = pciAddress, RegAddress = regAddress, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input);
|
||||
}
|
||||
|
||||
public bool WritePciConfigDwordEx(uint pciAddress, uint regAddress, uint value)
|
||||
{
|
||||
var input = new WritePciConfigInputDword { PciAddress = pciAddress, RegAddress = regAddress, Value = value };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input);
|
||||
}
|
||||
|
||||
private bool PciConfigRead<TValue>(uint pciAddress, uint regAddress, ref TValue value)
|
||||
where TValue : struct
|
||||
{
|
||||
var input = new ReadPciConfigInput { PciAddress = pciAddress, RegAddress = regAddress };
|
||||
return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_PCI_CONFIG, input, ref value);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WriteIoPortInputByte
|
||||
{
|
||||
public uint PortNumber;
|
||||
public byte Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WriteIoPortInputWord
|
||||
{
|
||||
public uint PortNumber;
|
||||
public ushort Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WriteIoPortInputDword
|
||||
{
|
||||
public uint PortNumber;
|
||||
public uint Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct ReadPciConfigInput
|
||||
{
|
||||
public uint PciAddress;
|
||||
public uint RegAddress;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WritePciConfigInputByte
|
||||
{
|
||||
public uint PciAddress;
|
||||
public uint RegAddress;
|
||||
public byte Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WritePciConfigInputWord
|
||||
{
|
||||
public uint PciAddress;
|
||||
public uint RegAddress;
|
||||
public ushort Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WritePciConfigInputDword
|
||||
{
|
||||
public uint PciAddress;
|
||||
public uint RegAddress;
|
||||
public uint Value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct Conf
|
||||
{
|
||||
public uint A;
|
||||
public uint B;
|
||||
public uint C;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ internal static class Ring0
|
||||
|
||||
public static bool IsOpen => _driver != null;
|
||||
|
||||
internal static KernelDriver KernelDriver => _driver;
|
||||
|
||||
public static void Open()
|
||||
{
|
||||
// no implementation for unix systems
|
||||
|
||||
@@ -20,9 +20,16 @@ internal static class Ring0
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_GET_REFCOUNT = new(OLS_TYPE, 0x801, Kernel32.IOControlCode.Access.Any);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_MSR = new(OLS_TYPE, 0x821, Kernel32.IOControlCode.Access.Any);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_MSR = new(OLS_TYPE, 0x822, Kernel32.IOControlCode.Access.Any);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_BYTE = new(OLS_TYPE, 0x833, Kernel32.IOControlCode.Access.Read);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_BYTE = new(OLS_TYPE, 0x836, Kernel32.IOControlCode.Access.Write);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_PCI_CONFIG = new(OLS_TYPE, 0x851, Kernel32.IOControlCode.Access.Read);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_PCI_CONFIG = new(OLS_TYPE, 0x852, Kernel32.IOControlCode.Access.Write);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_MEMORY = new(OLS_TYPE, 0x841, Kernel32.IOControlCode.Access.Read);
|
||||
}
|
||||
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_BYTE = new(OLS_TYPE, 0x833, Kernel32.IOControlCode.Access.Read);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_WORD = new(OLS_TYPE, 0x834, Kernel32.IOControlCode.Access.Read);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_DWORD = new(OLS_TYPE, 0x835, Kernel32.IOControlCode.Access.Read);
|
||||
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_BYTE = new(OLS_TYPE, 0x836, Kernel32.IOControlCode.Access.Write);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_WORD = new(OLS_TYPE, 0x837, Kernel32.IOControlCode.Access.Write);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_DWORD = new(OLS_TYPE, 0x838, Kernel32.IOControlCode.Access.Write);
|
||||
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_PCI_CONFIG = new(OLS_TYPE, 0x851, Kernel32.IOControlCode.Access.Read);
|
||||
public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_PCI_CONFIG = new(OLS_TYPE, 0x852, Kernel32.IOControlCode.Access.Write);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HidSharp" Version="2.1.0" />
|
||||
<PackageReference Include="RAMSPDToolkit-NDD" Version="1.1.4" />
|
||||
<PackageReference Include="System.Management" Version="9.0.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user