Use file-scoped namespaces (#842)

This commit is contained in:
PhyxionNL
2022-10-09 13:22:57 +02:00
committed by GitHub
parent 3ac329f247
commit 58cef111a1
175 changed files with 34026 additions and 34204 deletions
@@ -13,281 +13,280 @@ using System.Reflection;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public abstract class AtaStorage : AbstractStorage
{
public abstract class AtaStorage : AbstractStorage
// array of all hard drive types, matching type is searched in this order
private static readonly Type[] _hddTypes = { typeof(SsdPlextor), typeof(SsdIntel), typeof(SsdSandforce), typeof(SsdIndilinx), typeof(SsdSamsung), typeof(SsdMicron), typeof(GenericHardDisk) };
private IDictionary<SmartAttribute, Sensor> _sensors;
/// <summary>
/// Gets the SMART data.
/// </summary>
public ISmart Smart { get; }
/// <summary>
/// Gets the SMART attributes.
/// </summary>
public IReadOnlyList<SmartAttribute> SmartAttributes { get; }
internal AtaStorage(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, string id, int index, IReadOnlyList<SmartAttribute> smartAttributes, ISettings settings)
: base(storageInfo, name, firmwareRevision, id, index, settings)
{
// array of all hard drive types, matching type is searched in this order
private static readonly Type[] _hddTypes = { typeof(SsdPlextor), typeof(SsdIntel), typeof(SsdSandforce), typeof(SsdIndilinx), typeof(SsdSamsung), typeof(SsdMicron), typeof(GenericHardDisk) };
private IDictionary<SmartAttribute, Sensor> _sensors;
/// <summary>
/// Gets the SMART data.
/// </summary>
public ISmart Smart { get; }
/// <summary>
/// Gets the SMART attributes.
/// </summary>
public IReadOnlyList<SmartAttribute> SmartAttributes { get; }
internal AtaStorage(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, string id, int index, IReadOnlyList<SmartAttribute> smartAttributes, ISettings settings)
: base(storageInfo, name, firmwareRevision, id, index, settings)
{
Smart = smart;
if (smart.IsValid)
smart.EnableSmart();
Smart = smart;
if (smart.IsValid)
smart.EnableSmart();
SmartAttributes = smartAttributes;
CreateSensors();
}
SmartAttributes = smartAttributes;
CreateSensors();
}
internal static AbstractStorage CreateInstance(StorageInfo storageInfo, ISettings settings)
internal static AbstractStorage CreateInstance(StorageInfo storageInfo, ISettings settings)
{
ISmart smart = new WindowsSmart(storageInfo.Index);
string name = null;
string firmwareRevision = null;
Kernel32.SMART_ATTRIBUTE[] smartAttributes = { };
if (smart.IsValid)
{
ISmart smart = new WindowsSmart(storageInfo.Index);
string name = null;
string firmwareRevision = null;
Kernel32.SMART_ATTRIBUTE[] smartAttributes = { };
bool nameValid = smart.ReadNameAndFirmwareRevision(out name, out firmwareRevision);
bool smartEnabled = smart.EnableSmart();
if (smart.IsValid)
{
bool nameValid = smart.ReadNameAndFirmwareRevision(out name, out firmwareRevision);
bool smartEnabled = smart.EnableSmart();
if (smartEnabled)
smartAttributes = smart.ReadSmartData();
if (smartEnabled)
smartAttributes = smart.ReadSmartData();
if (!nameValid)
{
name = null;
firmwareRevision = null;
}
}
else
if (!nameValid)
{
string[] logicalDrives = WindowsStorage.GetLogicalDrives(storageInfo.Index);
if (logicalDrives == null || logicalDrives.Length == 0)
{
smart.Close();
return null;
}
bool hasNonZeroSizeDrive = false;
foreach (string logicalDrive in logicalDrives)
{
try
{
var driveInfo = new DriveInfo(logicalDrive);
if (driveInfo.TotalSize > 0)
{
hasNonZeroSizeDrive = true;
break;
}
}
catch (ArgumentException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
}
if (!hasNonZeroSizeDrive)
{
smart.Close();
return null;
}
name = null;
firmwareRevision = null;
}
if (string.IsNullOrEmpty(name))
name = string.IsNullOrEmpty(storageInfo.Name) ? "Generic Hard Disk" : storageInfo.Name;
if (string.IsNullOrEmpty(firmwareRevision))
firmwareRevision = string.IsNullOrEmpty(storageInfo.Revision) ? "Unknown" : storageInfo.Revision;
foreach (Type type in _hddTypes)
}
else
{
string[] logicalDrives = WindowsStorage.GetLogicalDrives(storageInfo.Index);
if (logicalDrives == null || logicalDrives.Length == 0)
{
// get the array of the required SMART attributes for the current type
// check if all required attributes are present
bool allAttributesFound = true;
if (type.GetCustomAttributes(typeof(RequireSmartAttribute), true) is RequireSmartAttribute[] requiredAttributes)
{
foreach (RequireSmartAttribute requireAttribute in requiredAttributes)
{
bool attributeFound = false;
foreach (Kernel32.SMART_ATTRIBUTE value in smartAttributes)
{
if (value.Id == requireAttribute.AttributeId)
{
attributeFound = true;
break;
}
}
if (!attributeFound)
{
allAttributesFound = false;
break;
}
}
}
// if an attribute is missing, then try the next type
if (!allAttributesFound)
continue;
// check if there is a matching name prefix for this type
if (type.GetCustomAttributes(typeof(NamePrefixAttribute), true) is NamePrefixAttribute[] namePrefixes)
{
foreach (NamePrefixAttribute prefix in namePrefixes)
{
if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture))
{
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
return Activator.CreateInstance(type, flags, null, new object[] { storageInfo, smart, name, firmwareRevision, storageInfo.Index, settings }, null) as AtaStorage;
}
}
}
smart.Close();
return null;
}
// no matching type has been found
smart.Close();
return null;
bool hasNonZeroSizeDrive = false;
foreach (string logicalDrive in logicalDrives)
{
try
{
var driveInfo = new DriveInfo(logicalDrive);
if (driveInfo.TotalSize > 0)
{
hasNonZeroSizeDrive = true;
break;
}
}
catch (ArgumentException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
}
if (!hasNonZeroSizeDrive)
{
smart.Close();
return null;
}
}
protected sealed override void CreateSensors()
if (string.IsNullOrEmpty(name))
name = string.IsNullOrEmpty(storageInfo.Name) ? "Generic Hard Disk" : storageInfo.Name;
if (string.IsNullOrEmpty(firmwareRevision))
firmwareRevision = string.IsNullOrEmpty(storageInfo.Revision) ? "Unknown" : storageInfo.Revision;
foreach (Type type in _hddTypes)
{
_sensors = new Dictionary<SmartAttribute, Sensor>();
// get the array of the required SMART attributes for the current type
if (Smart.IsValid)
// check if all required attributes are present
bool allAttributesFound = true;
if (type.GetCustomAttributes(typeof(RequireSmartAttribute), true) is RequireSmartAttribute[] requiredAttributes)
{
byte[] smartIds = Smart.ReadSmartData().Select(x => x.Id).ToArray();
// unique attributes by SensorType and SensorChannel.
IEnumerable<SmartAttribute> smartAttributes = SmartAttributes
.Where(x => x.SensorType.HasValue && smartIds.Contains(x.Id))
.GroupBy(x => new { x.SensorType.Value, x.SensorChannel })
.Select(x => x.First());
_sensors = smartAttributes.ToDictionary(attribute => attribute,
attribute => new Sensor(attribute.SensorName,
attribute.SensorChannel,
attribute.DefaultHiddenSensor,
attribute.SensorType.GetValueOrDefault(),
this,
attribute.ParameterDescriptions,
_settings));
foreach (KeyValuePair<SmartAttribute, Sensor> sensor in _sensors)
ActivateSensor(sensor.Value);
}
base.CreateSensors();
}
protected virtual void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values) { }
protected override void UpdateSensors()
{
if (Smart.IsValid)
{
Kernel32.SMART_ATTRIBUTE[] smartAttributes = Smart.ReadSmartData();
foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in _sensors)
foreach (RequireSmartAttribute requireAttribute in requiredAttributes)
{
SmartAttribute attribute = keyValuePair.Key;
bool attributeFound = false;
foreach (Kernel32.SMART_ATTRIBUTE value in smartAttributes)
{
if (value.Id == attribute.Id)
if (value.Id == requireAttribute.AttributeId)
{
Sensor sensor = keyValuePair.Value;
sensor.Value = attribute.ConvertValue(value, sensor.Parameters);
}
}
}
UpdateAdditionalSensors(smartAttributes);
}
}
protected override void GetReport(StringBuilder r)
{
if (Smart.IsValid)
{
Kernel32.SMART_ATTRIBUTE[] values = Smart.ReadSmartData();
Kernel32.SMART_THRESHOLD[] thresholds = Smart.ReadSmartThresholds();
if (values.Length > 0)
{
r.AppendFormat(CultureInfo.InvariantCulture,
" {0}{1}{2}{3}{4}{5}{6}{7}",
"Id".PadRight(3),
"Description".PadRight(35),
"Raw Value".PadRight(13),
"Worst".PadRight(6),
"Value".PadRight(6),
"Threshold".PadRight(6),
"Physical".PadRight(8),
Environment.NewLine);
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
if (value.Id == 0x00)
attributeFound = true;
break;
byte? threshold = null;
foreach (Kernel32.SMART_THRESHOLD t in thresholds)
{
if (t.Id == value.Id)
{
threshold = t.Threshold;
}
}
string description = "Unknown";
float? physical = null;
foreach (SmartAttribute a in SmartAttributes)
{
if (a.Id == value.Id)
{
description = a.Name;
if (a.HasRawValueConversion | a.SensorType.HasValue)
physical = a.ConvertValue(value, null);
else
physical = null;
}
}
string raw = BitConverter.ToString(value.RawValue);
r.AppendFormat(CultureInfo.InvariantCulture,
" {0}{1}{2}{3}{4}{5}{6}{7}",
value.Id.ToString("X2").PadRight(3),
description.PadRight(35),
raw.Replace("-", string.Empty).PadRight(13),
value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
value.CurrentValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
(threshold.HasValue
? threshold.Value.ToString(CultureInfo.InvariantCulture)
: "-").PadRight(6),
(physical.HasValue ? physical.Value.ToString(CultureInfo.InvariantCulture) : "-").PadRight(8),
Environment.NewLine);
}
r.AppendLine();
if (!attributeFound)
{
allAttributesFound = false;
break;
}
}
}
// if an attribute is missing, then try the next type
if (!allAttributesFound)
continue;
// check if there is a matching name prefix for this type
if (type.GetCustomAttributes(typeof(NamePrefixAttribute), true) is NamePrefixAttribute[] namePrefixes)
{
foreach (NamePrefixAttribute prefix in namePrefixes)
{
if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture))
{
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
return Activator.CreateInstance(type, flags, null, new object[] { storageInfo, smart, name, firmwareRevision, storageInfo.Index, settings }, null) as AtaStorage;
}
}
}
}
protected static float RawToInt(byte[] raw, byte value, IReadOnlyList<IParameter> parameters)
// no matching type has been found
smart.Close();
return null;
}
protected sealed override void CreateSensors()
{
_sensors = new Dictionary<SmartAttribute, Sensor>();
if (Smart.IsValid)
{
return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
byte[] smartIds = Smart.ReadSmartData().Select(x => x.Id).ToArray();
// unique attributes by SensorType and SensorChannel.
IEnumerable<SmartAttribute> smartAttributes = SmartAttributes
.Where(x => x.SensorType.HasValue && smartIds.Contains(x.Id))
.GroupBy(x => new { x.SensorType.Value, x.SensorChannel })
.Select(x => x.First());
_sensors = smartAttributes.ToDictionary(attribute => attribute,
attribute => new Sensor(attribute.SensorName,
attribute.SensorChannel,
attribute.DefaultHiddenSensor,
attribute.SensorType.GetValueOrDefault(),
this,
attribute.ParameterDescriptions,
_settings));
foreach (KeyValuePair<SmartAttribute, Sensor> sensor in _sensors)
ActivateSensor(sensor.Value);
}
public override void Close()
base.CreateSensors();
}
protected virtual void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values) { }
protected override void UpdateSensors()
{
if (Smart.IsValid)
{
Smart.Close();
base.Close();
Kernel32.SMART_ATTRIBUTE[] smartAttributes = Smart.ReadSmartData();
foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in _sensors)
{
SmartAttribute attribute = keyValuePair.Key;
foreach (Kernel32.SMART_ATTRIBUTE value in smartAttributes)
{
if (value.Id == attribute.Id)
{
Sensor sensor = keyValuePair.Value;
sensor.Value = attribute.ConvertValue(value, sensor.Parameters);
}
}
}
UpdateAdditionalSensors(smartAttributes);
}
}
}
protected override void GetReport(StringBuilder r)
{
if (Smart.IsValid)
{
Kernel32.SMART_ATTRIBUTE[] values = Smart.ReadSmartData();
Kernel32.SMART_THRESHOLD[] thresholds = Smart.ReadSmartThresholds();
if (values.Length > 0)
{
r.AppendFormat(CultureInfo.InvariantCulture,
" {0}{1}{2}{3}{4}{5}{6}{7}",
"Id".PadRight(3),
"Description".PadRight(35),
"Raw Value".PadRight(13),
"Worst".PadRight(6),
"Value".PadRight(6),
"Threshold".PadRight(6),
"Physical".PadRight(8),
Environment.NewLine);
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
if (value.Id == 0x00)
break;
byte? threshold = null;
foreach (Kernel32.SMART_THRESHOLD t in thresholds)
{
if (t.Id == value.Id)
{
threshold = t.Threshold;
}
}
string description = "Unknown";
float? physical = null;
foreach (SmartAttribute a in SmartAttributes)
{
if (a.Id == value.Id)
{
description = a.Name;
if (a.HasRawValueConversion | a.SensorType.HasValue)
physical = a.ConvertValue(value, null);
else
physical = null;
}
}
string raw = BitConverter.ToString(value.RawValue);
r.AppendFormat(CultureInfo.InvariantCulture,
" {0}{1}{2}{3}{4}{5}{6}{7}",
value.Id.ToString("X2").PadRight(3),
description.PadRight(35),
raw.Replace("-", string.Empty).PadRight(13),
value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
value.CurrentValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
(threshold.HasValue
? threshold.Value.ToString(CultureInfo.InvariantCulture)
: "-").PadRight(6),
(physical.HasValue ? physical.Value.ToString(CultureInfo.InvariantCulture) : "-").PadRight(8),
Environment.NewLine);
}
r.AppendLine();
}
}
}
protected static float RawToInt(byte[] raw, byte value, IReadOnlyList<IParameter> parameters)
{
return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
}
public override void Close()
{
Smart.Close();
base.Close();
}
}
@@ -12,287 +12,286 @@ using System.Management;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public abstract class AbstractStorage : Hardware
{
public abstract class AbstractStorage : Hardware
private readonly PerformanceValue _perfTotal = new();
private readonly PerformanceValue _perfWrite = new();
private readonly StorageInfo _storageInfo;
private readonly TimeSpan _updateInterval = TimeSpan.FromSeconds(60);
private ulong _lastReadRateCounter;
private double _lastTime;
private DateTime _lastUpdate = DateTime.MinValue;
private ulong _lastWriteRateCounter;
private Sensor _sensorDiskReadRate;
private Sensor _sensorDiskTotalActivity;
private Sensor _sensorDiskWriteActivity;
private Sensor _sensorDiskWriteRate;
private Sensor _usageSensor;
private int _wmiFailureCount;
internal AbstractStorage(StorageInfo storageInfo, string name, string firmwareRevision, string id, int index, ISettings settings)
: base(name, new Identifier(id, index.ToString(CultureInfo.InvariantCulture)), settings)
{
private readonly PerformanceValue _perfTotal = new();
private readonly PerformanceValue _perfWrite = new();
private readonly StorageInfo _storageInfo;
private readonly TimeSpan _updateInterval = TimeSpan.FromSeconds(60);
_storageInfo = storageInfo;
FirmwareRevision = firmwareRevision;
Index = index;
private ulong _lastReadRateCounter;
private double _lastTime;
private DateTime _lastUpdate = DateTime.MinValue;
private ulong _lastWriteRateCounter;
private Sensor _sensorDiskReadRate;
private Sensor _sensorDiskTotalActivity;
private Sensor _sensorDiskWriteActivity;
private Sensor _sensorDiskWriteRate;
private Sensor _usageSensor;
private int _wmiFailureCount;
string[] logicalDrives = WindowsStorage.GetLogicalDrives(index);
var driveInfoList = new List<DriveInfo>(logicalDrives.Length);
internal AbstractStorage(StorageInfo storageInfo, string name, string firmwareRevision, string id, int index, ISettings settings)
: base(name, new Identifier(id, index.ToString(CultureInfo.InvariantCulture)), settings)
foreach (string logicalDrive in logicalDrives)
{
_storageInfo = storageInfo;
FirmwareRevision = firmwareRevision;
Index = index;
string[] logicalDrives = WindowsStorage.GetLogicalDrives(index);
var driveInfoList = new List<DriveInfo>(logicalDrives.Length);
foreach (string logicalDrive in logicalDrives)
try
{
try
var di = new DriveInfo(logicalDrive);
if (di.TotalSize > 0)
driveInfoList.Add(new DriveInfo(logicalDrive));
}
catch (ArgumentException)
{ }
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
DriveInfos = driveInfoList.ToArray();
}
public DriveInfo[] DriveInfos { get; }
public string FirmwareRevision { get; }
public override HardwareType HardwareType => HardwareType.Storage;
public int Index { get; }
public static AbstractStorage CreateInstance(string deviceId, uint driveNumber, ulong diskSize, int scsiPort, ISettings settings)
{
StorageInfo info = WindowsStorage.GetStorageInfo(deviceId, driveNumber);
if (info == null)
return null;
info.DiskSize = diskSize;
info.DeviceId = deviceId;
info.Scsi = $@"\\.\SCSI{scsiPort}:";
if (info.Removable || info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeVirtual or Kernel32.STORAGE_BUS_TYPE.BusTypeFileBackedVirtual)
return null;
//fallback, when it is not possible to read out with the nvme implementation,
//try it with the sata smart implementation
if (info.BusType == Kernel32.STORAGE_BUS_TYPE.BusTypeNvme)
{
AbstractStorage x = NVMeGeneric.CreateInstance(info, settings);
if (x != null)
return x;
}
return info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeAta or Kernel32.STORAGE_BUS_TYPE.BusTypeSata or Kernel32.STORAGE_BUS_TYPE.BusTypeNvme
? AtaStorage.CreateInstance(info, settings)
: StorageGeneric.CreateInstance(info, settings);
}
protected virtual void CreateSensors()
{
if (DriveInfos.Length > 0)
{
_usageSensor = new Sensor("Used Space", 0, SensorType.Load, this, _settings);
ActivateSensor(_usageSensor);
}
_sensorDiskWriteActivity = new Sensor("Write Activity", 32, SensorType.Load, this, _settings);
ActivateSensor(_sensorDiskWriteActivity);
_sensorDiskTotalActivity = new Sensor("Total Activity", 33, SensorType.Load, this, _settings);
ActivateSensor(_sensorDiskTotalActivity);
_sensorDiskReadRate = new Sensor("Read Rate", 34, SensorType.Throughput, this, _settings);
ActivateSensor(_sensorDiskReadRate);
_sensorDiskWriteRate = new Sensor("Write Rate", 35, SensorType.Throughput, this, _settings);
ActivateSensor(_sensorDiskWriteRate);
}
protected abstract void UpdateSensors();
private void UpdateStatisticsFromWmi(int driveIndex)
{
string query = $"SELECT * FROM Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name LIKE \"{driveIndex}%\"";
using var perfData = new ManagementObjectSearcher(query) { Options = { Timeout = TimeSpan.FromSeconds(7.5) } };
using ManagementObjectCollection collection = perfData.Get();
using ManagementObject data = collection.OfType<ManagementObject>().FirstOrDefault();
if (data == null)
return;
ulong value = (ulong)data.Properties["PercentDiskWriteTime"].Value;
ulong valueBase = (ulong)data.Properties["PercentDiskWriteTime_Base"].Value;
_perfWrite.Update(value, valueBase);
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
value = (ulong)data.Properties["PercentIdleTime"].Value;
valueBase = (ulong)data.Properties["PercentIdleTime_Base"].Value;
_perfTotal.Update(value, valueBase);
_sensorDiskTotalActivity.Value = (float)(100.0 - _perfTotal.Result);
ulong readRateCounter = (ulong)data.Properties["DiskReadBytesPerSec"].Value;
ulong readRate = readRateCounter - _lastReadRateCounter;
_lastReadRateCounter = readRateCounter;
ulong writeRateCounter = (ulong)data.Properties["DiskWriteBytesPerSec"].Value;
ulong writeRate = writeRateCounter - _lastWriteRateCounter;
_lastWriteRateCounter = writeRateCounter;
ulong timestampPerfTime = (ulong)data.Properties["Timestamp_PerfTime"].Value;
ulong frequencyPerfTime = (ulong)data.Properties["Frequency_Perftime"].Value;
double currentTime = (double)timestampPerfTime / frequencyPerfTime;
double timeDeltaSeconds = currentTime - _lastTime;
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
{
double writeSpeed = writeRate * (1 / timeDeltaSeconds);
_sensorDiskWriteRate.Value = (float)writeSpeed;
double readSpeed = readRate * (1 / timeDeltaSeconds);
_sensorDiskReadRate.Value = (float)readSpeed;
}
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
_lastTime = currentTime;
}
public override void Update()
{
const int wmiRetries = 10;
//update statistics from WMI on every update
if (_storageInfo != null && _wmiFailureCount <= wmiRetries)
{
try
{
UpdateStatisticsFromWmi(_storageInfo.Index);
_wmiFailureCount = 0;
}
catch
{
if (++_wmiFailureCount == wmiRetries)
{
var di = new DriveInfo(logicalDrive);
if (di.TotalSize > 0)
driveInfoList.Add(new DriveInfo(logicalDrive));
DeactivateSensor(_sensorDiskTotalActivity);
DeactivateSensor(_sensorDiskWriteActivity);
DeactivateSensor(_sensorDiskReadRate);
DeactivateSensor(_sensorDiskWriteRate);
}
catch (ArgumentException)
{ }
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
DriveInfos = driveInfoList.ToArray();
}
public DriveInfo[] DriveInfos { get; }
public string FirmwareRevision { get; }
public override HardwareType HardwareType => HardwareType.Storage;
public int Index { get; }
public static AbstractStorage CreateInstance(string deviceId, uint driveNumber, ulong diskSize, int scsiPort, ISettings settings)
//read out with updateInterval
TimeSpan tDiff = DateTime.UtcNow - _lastUpdate;
if (tDiff > _updateInterval)
{
StorageInfo info = WindowsStorage.GetStorageInfo(deviceId, driveNumber);
if (info == null)
return null;
_lastUpdate = DateTime.UtcNow;
info.DiskSize = diskSize;
info.DeviceId = deviceId;
info.Scsi = $@"\\.\SCSI{scsiPort}:";
UpdateSensors();
if (info.Removable || info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeVirtual or Kernel32.STORAGE_BUS_TYPE.BusTypeFileBackedVirtual)
return null;
//fallback, when it is not possible to read out with the nvme implementation,
//try it with the sata smart implementation
if (info.BusType == Kernel32.STORAGE_BUS_TYPE.BusTypeNvme)
if (_usageSensor != null)
{
AbstractStorage x = NVMeGeneric.CreateInstance(info, settings);
if (x != null)
return x;
}
long totalSize = 0;
long totalFreeSpace = 0;
return info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeAta or Kernel32.STORAGE_BUS_TYPE.BusTypeSata or Kernel32.STORAGE_BUS_TYPE.BusTypeNvme
? AtaStorage.CreateInstance(info, settings)
: StorageGeneric.CreateInstance(info, settings);
}
protected virtual void CreateSensors()
{
if (DriveInfos.Length > 0)
{
_usageSensor = new Sensor("Used Space", 0, SensorType.Load, this, _settings);
ActivateSensor(_usageSensor);
}
_sensorDiskWriteActivity = new Sensor("Write Activity", 32, SensorType.Load, this, _settings);
ActivateSensor(_sensorDiskWriteActivity);
_sensorDiskTotalActivity = new Sensor("Total Activity", 33, SensorType.Load, this, _settings);
ActivateSensor(_sensorDiskTotalActivity);
_sensorDiskReadRate = new Sensor("Read Rate", 34, SensorType.Throughput, this, _settings);
ActivateSensor(_sensorDiskReadRate);
_sensorDiskWriteRate = new Sensor("Write Rate", 35, SensorType.Throughput, this, _settings);
ActivateSensor(_sensorDiskWriteRate);
}
protected abstract void UpdateSensors();
private void UpdateStatisticsFromWmi(int driveIndex)
{
string query = $"SELECT * FROM Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name LIKE \"{driveIndex}%\"";
using var perfData = new ManagementObjectSearcher(query) { Options = { Timeout = TimeSpan.FromSeconds(7.5) } };
using ManagementObjectCollection collection = perfData.Get();
using ManagementObject data = collection.OfType<ManagementObject>().FirstOrDefault();
if (data == null)
return;
ulong value = (ulong)data.Properties["PercentDiskWriteTime"].Value;
ulong valueBase = (ulong)data.Properties["PercentDiskWriteTime_Base"].Value;
_perfWrite.Update(value, valueBase);
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
value = (ulong)data.Properties["PercentIdleTime"].Value;
valueBase = (ulong)data.Properties["PercentIdleTime_Base"].Value;
_perfTotal.Update(value, valueBase);
_sensorDiskTotalActivity.Value = (float)(100.0 - _perfTotal.Result);
ulong readRateCounter = (ulong)data.Properties["DiskReadBytesPerSec"].Value;
ulong readRate = readRateCounter - _lastReadRateCounter;
_lastReadRateCounter = readRateCounter;
ulong writeRateCounter = (ulong)data.Properties["DiskWriteBytesPerSec"].Value;
ulong writeRate = writeRateCounter - _lastWriteRateCounter;
_lastWriteRateCounter = writeRateCounter;
ulong timestampPerfTime = (ulong)data.Properties["Timestamp_PerfTime"].Value;
ulong frequencyPerfTime = (ulong)data.Properties["Frequency_Perftime"].Value;
double currentTime = (double)timestampPerfTime / frequencyPerfTime;
double timeDeltaSeconds = currentTime - _lastTime;
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
{
double writeSpeed = writeRate * (1 / timeDeltaSeconds);
_sensorDiskWriteRate.Value = (float)writeSpeed;
double readSpeed = readRate * (1 / timeDeltaSeconds);
_sensorDiskReadRate.Value = (float)readSpeed;
}
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
_lastTime = currentTime;
}
public override void Update()
{
const int wmiRetries = 10;
//update statistics from WMI on every update
if (_storageInfo != null && _wmiFailureCount <= wmiRetries)
{
try
for (int i = 0; i < DriveInfos.Length; i++)
{
UpdateStatisticsFromWmi(_storageInfo.Index);
_wmiFailureCount = 0;
}
catch
{
if (++_wmiFailureCount == wmiRetries)
if (!DriveInfos[i].IsReady)
continue;
try
{
DeactivateSensor(_sensorDiskTotalActivity);
DeactivateSensor(_sensorDiskWriteActivity);
DeactivateSensor(_sensorDiskReadRate);
DeactivateSensor(_sensorDiskWriteRate);
totalSize += DriveInfos[i].TotalSize;
totalFreeSpace += DriveInfos[i].TotalFreeSpace;
}
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
}
//read out with updateInterval
TimeSpan tDiff = DateTime.UtcNow - _lastUpdate;
if (tDiff > _updateInterval)
{
_lastUpdate = DateTime.UtcNow;
UpdateSensors();
if (_usageSensor != null)
{
long totalSize = 0;
long totalFreeSpace = 0;
for (int i = 0; i < DriveInfos.Length; i++)
{
if (!DriveInfos[i].IsReady)
continue;
try
{
totalSize += DriveInfos[i].TotalSize;
totalFreeSpace += DriveInfos[i].TotalFreeSpace;
}
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
if (totalSize > 0)
_usageSensor.Value = 100.0f - ((100.0f * totalFreeSpace) / totalSize);
else
_usageSensor.Value = null;
}
}
}
protected abstract void GetReport(StringBuilder r);
public override string GetReport()
{
var r = new StringBuilder();
r.AppendLine("Storage");
r.AppendLine();
r.AppendLine("Drive Name: " + _name);
r.AppendLine("Firmware Version: " + FirmwareRevision);
r.AppendLine();
GetReport(r);
foreach (DriveInfo di in DriveInfos)
{
if (!di.IsReady)
continue;
try
{
r.AppendLine("Logical Drive Name: " + di.Name);
r.AppendLine("Format: " + di.DriveFormat);
r.AppendLine("Total Size: " + di.TotalSize);
r.AppendLine("Total Free Space: " + di.TotalFreeSpace);
r.AppendLine();
}
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
return r.ToString();
}
public override void Traverse(IVisitor visitor)
{
foreach (ISensor sensor in Sensors)
sensor.Accept(visitor);
}
/// <summary>
/// Helper to calculate the disk performance with base timestamps
/// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-perfrawdata
/// </summary>
private class PerformanceValue
{
public double Result { get; private set; }
private ulong Time { get; set; }
private ulong Value { get; set; }
public void Update(ulong val, ulong valBase)
{
ulong diffValue = val - Value;
ulong diffTime = valBase - Time;
Value = val;
Time = valBase;
Result = 100.0 / diffTime * diffValue;
//sometimes it is possible that diff_value > diff_timebase
//limit result to 100%, this is because timing issues during read from pcie controller an latency between IO operation
if (Result > 100)
Result = 100;
if (totalSize > 0)
_usageSensor.Value = 100.0f - ((100.0f * totalFreeSpace) / totalSize);
else
_usageSensor.Value = null;
}
}
}
}
protected abstract void GetReport(StringBuilder r);
public override string GetReport()
{
var r = new StringBuilder();
r.AppendLine("Storage");
r.AppendLine();
r.AppendLine("Drive Name: " + _name);
r.AppendLine("Firmware Version: " + FirmwareRevision);
r.AppendLine();
GetReport(r);
foreach (DriveInfo di in DriveInfos)
{
if (!di.IsReady)
continue;
try
{
r.AppendLine("Logical Drive Name: " + di.Name);
r.AppendLine("Format: " + di.DriveFormat);
r.AppendLine("Total Size: " + di.TotalSize);
r.AppendLine("Total Free Space: " + di.TotalFreeSpace);
r.AppendLine();
}
catch (IOException)
{ }
catch (UnauthorizedAccessException)
{ }
}
return r.ToString();
}
public override void Traverse(IVisitor visitor)
{
foreach (ISensor sensor in Sensors)
sensor.Accept(visitor);
}
/// <summary>
/// Helper to calculate the disk performance with base timestamps
/// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-perfrawdata
/// </summary>
private class PerformanceValue
{
public double Result { get; private set; }
private ulong Time { get; set; }
private ulong Value { get; set; }
public void Update(ulong val, ulong valBase)
{
ulong diffValue = val - Value;
ulong diffTime = valBase - Time;
Value = val;
Time = valBase;
Result = 100.0 / diffTime * diffValue;
//sometimes it is possible that diff_value > diff_timebase
//limit result to 100%, this is because timing issues during read from pcie controller an latency between IO operation
if (Result > 100)
Result = 100;
}
}
}
@@ -6,84 +6,83 @@
using System.Collections.Generic;
namespace LibreHardwareMonitor.Hardware.Storage
{
[NamePrefix("")]
public class GenericHardDisk : AtaStorage
{
private static readonly List<SmartAttribute> _smartAttributes = new()
{
new SmartAttribute(0x01, SmartNames.ReadErrorRate),
new SmartAttribute(0x02, SmartNames.ThroughputPerformance),
new SmartAttribute(0x03, SmartNames.SpinUpTime),
new SmartAttribute(0x04, SmartNames.StartStopCount, RawToInt),
new SmartAttribute(0x05, SmartNames.ReallocatedSectorsCount),
new SmartAttribute(0x06, SmartNames.ReadChannelMargin),
new SmartAttribute(0x07, SmartNames.SeekErrorRate),
new SmartAttribute(0x08, SmartNames.SeekTimePerformance),
new SmartAttribute(0x09, SmartNames.PowerOnHours, RawToInt),
new SmartAttribute(0x0A, SmartNames.SpinRetryCount),
new SmartAttribute(0x0B, SmartNames.RecalibrationRetries),
new SmartAttribute(0x0C, SmartNames.PowerCycleCount, RawToInt),
new SmartAttribute(0x0D, SmartNames.SoftReadErrorRate),
new SmartAttribute(0xAA, SmartNames.Unknown),
new SmartAttribute(0xAB, SmartNames.Unknown),
new SmartAttribute(0xAC, SmartNames.Unknown),
new SmartAttribute(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new SmartAttribute(0xB8, SmartNames.EndToEndError),
new SmartAttribute(0xB9, SmartNames.HeadStability),
new SmartAttribute(0xBA, SmartNames.InducedOpVibrationDetection),
new SmartAttribute(0xBB, SmartNames.ReportedUncorrectableErrors, RawToInt),
new SmartAttribute(0xBC, SmartNames.CommandTimeout, RawToInt),
new SmartAttribute(0xBD, SmartNames.HighFlyWrites),
new SmartAttribute(0xBF, SmartNames.GSenseErrorRate),
new SmartAttribute(0xC0, SmartNames.EmergencyRetractCycleCount),
new SmartAttribute(0xC1, SmartNames.LoadCycleCount),
new SmartAttribute(0xC3, SmartNames.HardwareEccRecovered),
new SmartAttribute(0xC4, SmartNames.ReallocationEventCount),
new SmartAttribute(0xC5, SmartNames.CurrentPendingSectorCount),
new SmartAttribute(0xC6, SmartNames.UncorrectableSectorCount),
new SmartAttribute(0xC7, SmartNames.UltraDmaCrcErrorCount),
new SmartAttribute(0xC8, SmartNames.WriteErrorRate),
new SmartAttribute(0xCA, SmartNames.DataAddressMarkErrors),
new SmartAttribute(0xCB, SmartNames.RunOutCancel),
new SmartAttribute(0xCC, SmartNames.SoftEccCorrection),
new SmartAttribute(0xCD, SmartNames.ThermalAsperityRate),
new SmartAttribute(0xCE, SmartNames.FlyingHeight),
new SmartAttribute(0xCF, SmartNames.SpinHighCurrent),
new SmartAttribute(0xD0, SmartNames.SpinBuzz),
new SmartAttribute(0xD1, SmartNames.OfflineSeekPerformance),
new SmartAttribute(0xD3, SmartNames.VibrationDuringWrite),
new SmartAttribute(0xD4, SmartNames.ShockDuringWrite),
new SmartAttribute(0xDC, SmartNames.DiskShift),
new SmartAttribute(0xDD, SmartNames.AlternativeGSenseErrorRate),
new SmartAttribute(0xDE, SmartNames.LoadedHours),
new SmartAttribute(0xDF, SmartNames.LoadUnloadRetryCount),
new SmartAttribute(0xE0, SmartNames.LoadFriction),
new SmartAttribute(0xE1, SmartNames.LoadUnloadCycleCount),
new SmartAttribute(0xE2, SmartNames.LoadInTime),
new SmartAttribute(0xE3, SmartNames.TorqueAmplificationCount),
new SmartAttribute(0xE4, SmartNames.PowerOffRetractCycle),
new SmartAttribute(0xE6, SmartNames.GmrHeadAmplitude),
new SmartAttribute(0xE8, SmartNames.EnduranceRemaining),
new SmartAttribute(0xE9, SmartNames.PowerOnHours),
new SmartAttribute(0xF0, SmartNames.HeadFlyingHours),
new SmartAttribute(0xF1, SmartNames.TotalLbasWritten),
new SmartAttribute(0xF2, SmartNames.TotalLbasRead),
new SmartAttribute(0xFA, SmartNames.ReadErrorRetryRate),
new SmartAttribute(0xFE, SmartNames.FreeFallProtection),
new SmartAttribute(0xC2, SmartNames.Temperature, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, SmartNames.Temperature, false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) }),
new SmartAttribute(0xE7, SmartNames.Temperature, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, SmartNames.Temperature, false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) }),
new SmartAttribute(0xBE, SmartNames.TemperatureDifferenceFrom100, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, "Temperature", false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) })
};
namespace LibreHardwareMonitor.Hardware.Storage;
internal GenericHardDisk(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "hdd", index, _smartAttributes, settings) { }
}
[NamePrefix("")]
public class GenericHardDisk : AtaStorage
{
private static readonly List<SmartAttribute> _smartAttributes = new()
{
new SmartAttribute(0x01, SmartNames.ReadErrorRate),
new SmartAttribute(0x02, SmartNames.ThroughputPerformance),
new SmartAttribute(0x03, SmartNames.SpinUpTime),
new SmartAttribute(0x04, SmartNames.StartStopCount, RawToInt),
new SmartAttribute(0x05, SmartNames.ReallocatedSectorsCount),
new SmartAttribute(0x06, SmartNames.ReadChannelMargin),
new SmartAttribute(0x07, SmartNames.SeekErrorRate),
new SmartAttribute(0x08, SmartNames.SeekTimePerformance),
new SmartAttribute(0x09, SmartNames.PowerOnHours, RawToInt),
new SmartAttribute(0x0A, SmartNames.SpinRetryCount),
new SmartAttribute(0x0B, SmartNames.RecalibrationRetries),
new SmartAttribute(0x0C, SmartNames.PowerCycleCount, RawToInt),
new SmartAttribute(0x0D, SmartNames.SoftReadErrorRate),
new SmartAttribute(0xAA, SmartNames.Unknown),
new SmartAttribute(0xAB, SmartNames.Unknown),
new SmartAttribute(0xAC, SmartNames.Unknown),
new SmartAttribute(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new SmartAttribute(0xB8, SmartNames.EndToEndError),
new SmartAttribute(0xB9, SmartNames.HeadStability),
new SmartAttribute(0xBA, SmartNames.InducedOpVibrationDetection),
new SmartAttribute(0xBB, SmartNames.ReportedUncorrectableErrors, RawToInt),
new SmartAttribute(0xBC, SmartNames.CommandTimeout, RawToInt),
new SmartAttribute(0xBD, SmartNames.HighFlyWrites),
new SmartAttribute(0xBF, SmartNames.GSenseErrorRate),
new SmartAttribute(0xC0, SmartNames.EmergencyRetractCycleCount),
new SmartAttribute(0xC1, SmartNames.LoadCycleCount),
new SmartAttribute(0xC3, SmartNames.HardwareEccRecovered),
new SmartAttribute(0xC4, SmartNames.ReallocationEventCount),
new SmartAttribute(0xC5, SmartNames.CurrentPendingSectorCount),
new SmartAttribute(0xC6, SmartNames.UncorrectableSectorCount),
new SmartAttribute(0xC7, SmartNames.UltraDmaCrcErrorCount),
new SmartAttribute(0xC8, SmartNames.WriteErrorRate),
new SmartAttribute(0xCA, SmartNames.DataAddressMarkErrors),
new SmartAttribute(0xCB, SmartNames.RunOutCancel),
new SmartAttribute(0xCC, SmartNames.SoftEccCorrection),
new SmartAttribute(0xCD, SmartNames.ThermalAsperityRate),
new SmartAttribute(0xCE, SmartNames.FlyingHeight),
new SmartAttribute(0xCF, SmartNames.SpinHighCurrent),
new SmartAttribute(0xD0, SmartNames.SpinBuzz),
new SmartAttribute(0xD1, SmartNames.OfflineSeekPerformance),
new SmartAttribute(0xD3, SmartNames.VibrationDuringWrite),
new SmartAttribute(0xD4, SmartNames.ShockDuringWrite),
new SmartAttribute(0xDC, SmartNames.DiskShift),
new SmartAttribute(0xDD, SmartNames.AlternativeGSenseErrorRate),
new SmartAttribute(0xDE, SmartNames.LoadedHours),
new SmartAttribute(0xDF, SmartNames.LoadUnloadRetryCount),
new SmartAttribute(0xE0, SmartNames.LoadFriction),
new SmartAttribute(0xE1, SmartNames.LoadUnloadCycleCount),
new SmartAttribute(0xE2, SmartNames.LoadInTime),
new SmartAttribute(0xE3, SmartNames.TorqueAmplificationCount),
new SmartAttribute(0xE4, SmartNames.PowerOffRetractCycle),
new SmartAttribute(0xE6, SmartNames.GmrHeadAmplitude),
new SmartAttribute(0xE8, SmartNames.EnduranceRemaining),
new SmartAttribute(0xE9, SmartNames.PowerOnHours),
new SmartAttribute(0xF0, SmartNames.HeadFlyingHours),
new SmartAttribute(0xF1, SmartNames.TotalLbasWritten),
new SmartAttribute(0xF2, SmartNames.TotalLbasRead),
new SmartAttribute(0xFA, SmartNames.ReadErrorRetryRate),
new SmartAttribute(0xFE, SmartNames.FreeFallProtection),
new SmartAttribute(0xC2, SmartNames.Temperature, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, SmartNames.Temperature, false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) }),
new SmartAttribute(0xE7, SmartNames.Temperature, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, SmartNames.Temperature, false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) }),
new SmartAttribute(0xBE, SmartNames.TemperatureDifferenceFrom100, (r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature, 0, "Temperature", false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\n" + "Temperature = Value + Offset.", 0) })
};
internal GenericHardDisk(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "hdd", index, _smartAttributes, settings) { }
}
@@ -6,14 +6,13 @@
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal interface INVMeDrive
{
internal interface INVMeDrive
{
SafeHandle Identify(StorageInfo storageInfo);
SafeHandle Identify(StorageInfo storageInfo);
bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data);
bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data);
bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data);
}
}
bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data);
}
@@ -7,20 +7,19 @@
using System;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public interface ISmart : IDisposable
{
public interface ISmart : IDisposable
{
bool IsValid { get; }
bool IsValid { get; }
void Close();
void Close();
bool EnableSmart();
bool EnableSmart();
Kernel32.SMART_ATTRIBUTE[] ReadSmartData();
Kernel32.SMART_ATTRIBUTE[] ReadSmartData();
Kernel32.SMART_THRESHOLD[] ReadSmartThresholds();
Kernel32.SMART_THRESHOLD[] ReadSmartThresholds();
bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision);
}
}
bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision);
}
@@ -7,182 +7,181 @@ using System.Collections.Generic;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public sealed class NVMeGeneric : AbstractStorage
{
public sealed class NVMeGeneric : AbstractStorage
private const ulong Scale = 1000000;
private const ulong Units = 512;
private readonly NVMeInfo _info;
private readonly List<NVMeSensor> _sensors = new();
/// <summary>
/// Gets the SMART data.
/// </summary>
public NVMeSmart Smart { get; }
private NVMeGeneric(StorageInfo storageInfo, NVMeInfo info, int index, ISettings settings)
: base(storageInfo, info.Model, info.Revision, "nvme", index, settings)
{
private const ulong Scale = 1000000;
private const ulong Units = 512;
private readonly NVMeInfo _info;
private readonly List<NVMeSensor> _sensors = new();
Smart = new NVMeSmart(storageInfo);
_info = info;
CreateSensors();
}
/// <summary>
/// Gets the SMART data.
/// </summary>
public NVMeSmart Smart { get; }
private static NVMeInfo GetDeviceInfo(StorageInfo storageInfo)
{
var smart = new NVMeSmart(storageInfo);
return smart.GetInfo();
}
private NVMeGeneric(StorageInfo storageInfo, NVMeInfo info, int index, ISettings settings)
: base(storageInfo, info.Model, info.Revision, "nvme", index, settings)
internal static AbstractStorage CreateInstance(StorageInfo storageInfo, ISettings settings)
{
NVMeInfo nvmeInfo = GetDeviceInfo(storageInfo);
return nvmeInfo == null ? null : new NVMeGeneric(storageInfo, nvmeInfo, storageInfo.Index, settings);
}
protected override void CreateSensors()
{
NVMeHealthInfo log = Smart.GetHealthInfo();
if (log != null)
{
Smart = new NVMeSmart(storageInfo);
_info = info;
CreateSensors();
}
AddSensor("Temperature", 0, false, SensorType.Temperature, health => health.Temperature);
AddSensor("Available Spare", 1, false, SensorType.Level, health => health.AvailableSpare);
AddSensor("Available Spare Threshold", 2, false, SensorType.Level, health => health.AvailableSpareThreshold);
AddSensor("Percentage Used", 3, false, SensorType.Level, health => health.PercentageUsed);
AddSensor("Data Read", 4, false, SensorType.Data, health => UnitsToData(health.DataUnitRead));
AddSensor("Data Written", 5, false, SensorType.Data, health => UnitsToData(health.DataUnitWritten));
private static NVMeInfo GetDeviceInfo(StorageInfo storageInfo)
{
var smart = new NVMeSmart(storageInfo);
return smart.GetInfo();
}
internal static AbstractStorage CreateInstance(StorageInfo storageInfo, ISettings settings)
{
NVMeInfo nvmeInfo = GetDeviceInfo(storageInfo);
return nvmeInfo == null ? null : new NVMeGeneric(storageInfo, nvmeInfo, storageInfo.Index, settings);
}
protected override void CreateSensors()
{
NVMeHealthInfo log = Smart.GetHealthInfo();
if (log != null)
int sensorIdx = 6;
for (int i = 0; i < log.TemperatureSensors.Length; i++)
{
AddSensor("Temperature", 0, false, SensorType.Temperature, health => health.Temperature);
AddSensor("Available Spare", 1, false, SensorType.Level, health => health.AvailableSpare);
AddSensor("Available Spare Threshold", 2, false, SensorType.Level, health => health.AvailableSpareThreshold);
AddSensor("Percentage Used", 3, false, SensorType.Level, health => health.PercentageUsed);
AddSensor("Data Read", 4, false, SensorType.Data, health => UnitsToData(health.DataUnitRead));
AddSensor("Data Written", 5, false, SensorType.Data, health => UnitsToData(health.DataUnitWritten));
int sensorIdx = 6;
for (int i = 0; i < log.TemperatureSensors.Length; i++)
int idx = i;
if (log.TemperatureSensors[idx] > short.MinValue)
{
int idx = i;
if (log.TemperatureSensors[idx] > short.MinValue)
{
AddSensor("Temperature " + (idx + 1), sensorIdx, false, SensorType.Temperature, health => health.TemperatureSensors[idx]);
sensorIdx++;
}
AddSensor("Temperature " + (idx + 1), sensorIdx, false, SensorType.Temperature, health => health.TemperatureSensors[idx]);
sensorIdx++;
}
}
base.CreateSensors();
}
private void AddSensor(string name, int index, bool defaultHidden, SensorType sensorType, GetSensorValue getValue)
base.CreateSensors();
}
private void AddSensor(string name, int index, bool defaultHidden, SensorType sensorType, GetSensorValue getValue)
{
var sensor = new NVMeSensor(name, index, defaultHidden, sensorType, this, _settings, getValue)
{
var sensor = new NVMeSensor(name, index, defaultHidden, sensorType, this, _settings, getValue)
{
Value = 0
};
ActivateSensor(sensor);
_sensors.Add(sensor);
Value = 0
};
ActivateSensor(sensor);
_sensors.Add(sensor);
}
private static float UnitsToData(ulong u)
{
// one unit is 512 * 1000 bytes, return in GB (not GiB)
return Units * u / Scale;
}
protected override void UpdateSensors()
{
NVMeHealthInfo health = Smart.GetHealthInfo();
if (health == null)
return;
foreach (NVMeSensor sensor in _sensors)
sensor.Update(health);
}
protected override void GetReport(StringBuilder r)
{
if (_info == null)
return;
r.AppendLine("PCI Vendor ID: 0x" + _info.VID.ToString("x04"));
if (_info.VID != _info.SSVID)
r.AppendLine("PCI Subsystem Vendor ID: 0x" + _info.VID.ToString("x04"));
r.AppendLine("IEEE OUI Identifier: 0x" + _info.IEEE[2].ToString("x02") + _info.IEEE[1].ToString("x02") + _info.IEEE[0].ToString("x02"));
r.AppendLine("Total NVM Capacity: " + _info.TotalCapacity);
r.AppendLine("Unallocated NVM Capacity: " + _info.UnallocatedCapacity);
r.AppendLine("Controller ID: " + _info.ControllerId);
r.AppendLine("Number of Namespaces: " + _info.NumberNamespaces);
NVMeHealthInfo health = Smart.GetHealthInfo();
if (health == null)
return;
if (health.CriticalWarning == Kernel32.NVME_CRITICAL_WARNING.None)
r.AppendLine("Critical Warning: -");
else
{
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.AvailableSpaceLow) != 0)
r.AppendLine("Critical Warning: the available spare space has fallen below the threshold.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.TemperatureThreshold) != 0)
r.AppendLine("Critical Warning: a temperature is above an over temperature threshold or below an under temperature threshold.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.ReliabilityDegraded) != 0)
r.AppendLine("Critical Warning: the device reliability has been degraded due to significant media related errors or any internal error that degrades device reliability.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.ReadOnly) != 0)
r.AppendLine("Critical Warning: the media has been placed in read only mode.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.VolatileMemoryBackupDeviceFailed) != 0)
r.AppendLine("Critical Warning: the volatile memory backup device has failed.");
}
private static float UnitsToData(ulong u)
r.AppendLine("Temperature: " + health.Temperature + " Celsius");
r.AppendLine("Available Spare: " + health.AvailableSpare + "%");
r.AppendLine("Available Spare Threshold: " + health.AvailableSpareThreshold + "%");
r.AppendLine("Percentage Used: " + health.PercentageUsed + "%");
r.AppendLine("Data Units Read: " + health.DataUnitRead);
r.AppendLine("Data Units Written: " + health.DataUnitWritten);
r.AppendLine("Host Read Commands: " + health.HostReadCommands);
r.AppendLine("Host Write Commands: " + health.HostWriteCommands);
r.AppendLine("Controller Busy Time: " + health.ControllerBusyTime);
r.AppendLine("Power Cycles: " + health.PowerCycle);
r.AppendLine("Power On Hours: " + health.PowerOnHours);
r.AppendLine("Unsafe Shutdowns: " + health.UnsafeShutdowns);
r.AppendLine("Media Errors: " + health.MediaErrors);
r.AppendLine("Number of Error Information Log Entries: " + health.ErrorInfoLogEntryCount);
r.AppendLine("Warning Composite Temperature Time: " + health.WarningCompositeTemperatureTime);
r.AppendLine("Critical Composite Temperature Time: " + health.CriticalCompositeTemperatureTime);
for (int i = 0; i < health.TemperatureSensors.Length; i++)
{
// one unit is 512 * 1000 bytes, return in GB (not GiB)
return Units * u / Scale;
if (health.TemperatureSensors[i] > short.MinValue)
r.AppendLine("Temperature Sensor " + (i + 1) + ": " + health.TemperatureSensors[i] + " Celsius");
}
}
public override void Close()
{
Smart?.Close();
base.Close();
}
private delegate float GetSensorValue(NVMeHealthInfo health);
private class NVMeSensor : Sensor
{
private readonly GetSensorValue _getValue;
public NVMeSensor(string name, int index, bool defaultHidden, SensorType sensorType, Hardware hardware, ISettings settings, GetSensorValue getValue)
: base(name, index, defaultHidden, sensorType, hardware, null, settings)
{
_getValue = getValue;
}
protected override void UpdateSensors()
public void Update(NVMeHealthInfo health)
{
NVMeHealthInfo health = Smart.GetHealthInfo();
if (health == null)
float v = _getValue(health);
if (SensorType == SensorType.Temperature && v is < -1000 or > 1000)
return;
foreach (NVMeSensor sensor in _sensors)
sensor.Update(health);
}
protected override void GetReport(StringBuilder r)
{
if (_info == null)
return;
r.AppendLine("PCI Vendor ID: 0x" + _info.VID.ToString("x04"));
if (_info.VID != _info.SSVID)
r.AppendLine("PCI Subsystem Vendor ID: 0x" + _info.VID.ToString("x04"));
r.AppendLine("IEEE OUI Identifier: 0x" + _info.IEEE[2].ToString("x02") + _info.IEEE[1].ToString("x02") + _info.IEEE[0].ToString("x02"));
r.AppendLine("Total NVM Capacity: " + _info.TotalCapacity);
r.AppendLine("Unallocated NVM Capacity: " + _info.UnallocatedCapacity);
r.AppendLine("Controller ID: " + _info.ControllerId);
r.AppendLine("Number of Namespaces: " + _info.NumberNamespaces);
NVMeHealthInfo health = Smart.GetHealthInfo();
if (health == null)
return;
if (health.CriticalWarning == Kernel32.NVME_CRITICAL_WARNING.None)
r.AppendLine("Critical Warning: -");
else
{
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.AvailableSpaceLow) != 0)
r.AppendLine("Critical Warning: the available spare space has fallen below the threshold.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.TemperatureThreshold) != 0)
r.AppendLine("Critical Warning: a temperature is above an over temperature threshold or below an under temperature threshold.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.ReliabilityDegraded) != 0)
r.AppendLine("Critical Warning: the device reliability has been degraded due to significant media related errors or any internal error that degrades device reliability.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.ReadOnly) != 0)
r.AppendLine("Critical Warning: the media has been placed in read only mode.");
if ((health.CriticalWarning & Kernel32.NVME_CRITICAL_WARNING.VolatileMemoryBackupDeviceFailed) != 0)
r.AppendLine("Critical Warning: the volatile memory backup device has failed.");
}
r.AppendLine("Temperature: " + health.Temperature + " Celsius");
r.AppendLine("Available Spare: " + health.AvailableSpare + "%");
r.AppendLine("Available Spare Threshold: " + health.AvailableSpareThreshold + "%");
r.AppendLine("Percentage Used: " + health.PercentageUsed + "%");
r.AppendLine("Data Units Read: " + health.DataUnitRead);
r.AppendLine("Data Units Written: " + health.DataUnitWritten);
r.AppendLine("Host Read Commands: " + health.HostReadCommands);
r.AppendLine("Host Write Commands: " + health.HostWriteCommands);
r.AppendLine("Controller Busy Time: " + health.ControllerBusyTime);
r.AppendLine("Power Cycles: " + health.PowerCycle);
r.AppendLine("Power On Hours: " + health.PowerOnHours);
r.AppendLine("Unsafe Shutdowns: " + health.UnsafeShutdowns);
r.AppendLine("Media Errors: " + health.MediaErrors);
r.AppendLine("Number of Error Information Log Entries: " + health.ErrorInfoLogEntryCount);
r.AppendLine("Warning Composite Temperature Time: " + health.WarningCompositeTemperatureTime);
r.AppendLine("Critical Composite Temperature Time: " + health.CriticalCompositeTemperatureTime);
for (int i = 0; i < health.TemperatureSensors.Length; i++)
{
if (health.TemperatureSensors[i] > short.MinValue)
r.AppendLine("Temperature Sensor " + (i + 1) + ": " + health.TemperatureSensors[i] + " Celsius");
}
}
public override void Close()
{
Smart?.Close();
base.Close();
}
private delegate float GetSensorValue(NVMeHealthInfo health);
private class NVMeSensor : Sensor
{
private readonly GetSensorValue _getValue;
public NVMeSensor(string name, int index, bool defaultHidden, SensorType sensorType, Hardware hardware, ISettings settings, GetSensorValue getValue)
: base(name, index, defaultHidden, sensorType, hardware, null, settings)
{
_getValue = getValue;
}
public void Update(NVMeHealthInfo health)
{
float v = _getValue(health);
if (SensorType == SensorType.Temperature && v is < -1000 or > 1000)
return;
Value = v;
}
Value = v;
}
}
}
@@ -5,44 +5,43 @@
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public abstract class NVMeHealthInfo
{
public abstract class NVMeHealthInfo
{
public byte AvailableSpare { get; protected set; }
public byte AvailableSpare { get; protected set; }
public byte AvailableSpareThreshold { get; protected set; }
public byte AvailableSpareThreshold { get; protected set; }
public ulong ControllerBusyTime { get; protected set; }
public ulong ControllerBusyTime { get; protected set; }
public uint CriticalCompositeTemperatureTime { get; protected set; }
public uint CriticalCompositeTemperatureTime { get; protected set; }
public Kernel32.NVME_CRITICAL_WARNING CriticalWarning { get; protected set; }
public Kernel32.NVME_CRITICAL_WARNING CriticalWarning { get; protected set; }
public ulong DataUnitRead { get; protected set; }
public ulong DataUnitRead { get; protected set; }
public ulong DataUnitWritten { get; protected set; }
public ulong DataUnitWritten { get; protected set; }
public ulong ErrorInfoLogEntryCount { get; protected set; }
public ulong ErrorInfoLogEntryCount { get; protected set; }
public ulong HostReadCommands { get; protected set; }
public ulong HostReadCommands { get; protected set; }
public ulong HostWriteCommands { get; protected set; }
public ulong HostWriteCommands { get; protected set; }
public ulong MediaErrors { get; protected set; }
public ulong MediaErrors { get; protected set; }
public byte PercentageUsed { get; protected set; }
public byte PercentageUsed { get; protected set; }
public ulong PowerCycle { get; protected set; }
public ulong PowerCycle { get; protected set; }
public ulong PowerOnHours { get; protected set; }
public ulong PowerOnHours { get; protected set; }
public short Temperature { get; protected set; }
public short Temperature { get; protected set; }
public short[] TemperatureSensors { get; protected set; }
public short[] TemperatureSensors { get; protected set; }
public ulong UnsafeShutdowns { get; protected set; }
public ulong UnsafeShutdowns { get; protected set; }
public uint WarningCompositeTemperatureTime { get; protected set; }
}
}
public uint WarningCompositeTemperatureTime { get; protected set; }
}
@@ -3,30 +3,29 @@
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public abstract class NVMeInfo
{
public abstract class NVMeInfo
{
public ushort ControllerId { get; protected set; }
public ushort ControllerId { get; protected set; }
public byte[] IEEE { get; protected set; }
public byte[] IEEE { get; protected set; }
public int Index { get; protected set; }
public int Index { get; protected set; }
public string Model { get; protected set; }
public string Model { get; protected set; }
public uint NumberNamespaces { get; protected set; }
public uint NumberNamespaces { get; protected set; }
public string Revision { get; protected set; }
public string Revision { get; protected set; }
public string Serial { get; protected set; }
public string Serial { get; protected set; }
public ushort SSVID { get; protected set; }
public ushort SSVID { get; protected set; }
public ulong TotalCapacity { get; protected set; }
public ulong TotalCapacity { get; protected set; }
public ulong UnallocatedCapacity { get; protected set; }
public ulong UnallocatedCapacity { get; protected set; }
public ushort VID { get; protected set; }
}
}
public ushort VID { get; protected set; }
}
@@ -8,149 +8,148 @@ using System.Runtime.InteropServices;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class NVMeIntel : INVMeDrive
{
internal class NVMeIntel : INVMeDrive
//intel nvme access
public SafeHandle Identify(StorageInfo storageInfo)
{
//intel nvme access
return NVMeWindows.IdentifyDevice(storageInfo);
}
public SafeHandle Identify(StorageInfo storageInfo)
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
return NVMeWindows.IdentifyDevice(storageInfo);
}
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
int finalSize = Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>());
Kernel32.RtlZeroMemory(ptr, finalSize);
int len = Math.Min(finalSize, passThrough.DataBuffer.Length);
Kernel32.RtlCopyMemory(ptr, newPtr, (uint)len);
Marshal.FreeHGlobal(buffer);
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(ptr);
Marshal.FreeHGlobal(ptr);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd[0] = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage; // GetLogPage
passThrough.NVMeCmd[1] = 0xFFFFFFFF; // address
passThrough.NVMeCmd[10] = 0x007f0002; // uint cdw10 = 0x000000002 | (((size / 4) - 1) << 16);
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
if (handle?.IsInvalid != false)
return null;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
int finalSize = Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>());
Kernel32.RtlZeroMemory(ptr, finalSize);
int len = Math.Min(finalSize, passThrough.DataBuffer.Length);
Kernel32.RtlCopyMemory(ptr, newPtr, (uint)len);
Marshal.FreeHGlobal(buffer);
if (validTransfer) { }
else
{
handle.Close();
handle = null;
}
return handle;
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(ptr);
Marshal.FreeHGlobal(ptr);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd[0] = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage; // GetLogPage
passThrough.NVMeCmd[1] = 0xFFFFFFFF; // address
passThrough.NVMeCmd[10] = 0x007f0002; // uint cdw10 = 0x000000002 | (((size / 4) - 1) << 16);
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
if (handle?.IsInvalid != false)
return null;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature1);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer) { }
else
{
handle.Close();
handle = null;
}
return handle;
}
}
@@ -8,150 +8,149 @@ using System.Runtime.InteropServices;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class NVMeIntelRst : INVMeDrive
{
internal class NVMeIntelRst : INVMeDrive
//intel RST (raid) nvme access
public SafeHandle Identify(StorageInfo storageInfo)
{
//intel RST (raid) nvme access
return NVMeWindows.IdentifyDevice(storageInfo);
}
public SafeHandle Identify(StorageInfo storageInfo)
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
return NVMeWindows.IdentifyDevice(storageInfo);
}
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
int finalSize = Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>());
Kernel32.RtlZeroMemory(ptr, finalSize);
int len = Math.Min(finalSize, passThrough.DataBuffer.Length);
Kernel32.RtlCopyMemory(ptr, newPtr, (uint)len);
Marshal.FreeHGlobal(buffer);
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(ptr);
Marshal.FreeHGlobal(ptr);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd[0] = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage; // GetLogPage
passThrough.NVMeCmd[1] = 0xFFFFFFFF; // address
passThrough.NVMeCmd[10] = 0x007f0002; // uint cdw10 = 0x000000002 | (((size / 4) - 1) << 16);
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
if (handle?.IsInvalid != false)
return null;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
int finalSize = Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>());
Kernel32.RtlZeroMemory(ptr, finalSize);
int len = Math.Min(finalSize, passThrough.DataBuffer.Length);
Kernel32.RtlCopyMemory(ptr, newPtr, (uint)len);
Marshal.FreeHGlobal(buffer);
if (validTransfer) { }
else
{
handle.Close();
handle = null;
}
return handle;
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(ptr);
Marshal.FreeHGlobal(ptr);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd[0] = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage; // GetLogPage
passThrough.NVMeCmd[1] = 0xFFFFFFFF; // address
passThrough.NVMeCmd[10] = 0x007f0002; // uint cdw10 = 0x000000002 | (((size / 4) - 1) << 16);
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.NVME_PASS_THROUGH_IOCTL>(nameof(Kernel32.NVME_PASS_THROUGH_IOCTL.DataBuffer));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
if (handle?.IsInvalid != false)
return null;
Kernel32.NVME_PASS_THROUGH_IOCTL passThrough = Kernel32.CreateStruct<Kernel32.NVME_PASS_THROUGH_IOCTL>();
passThrough.srb.HeaderLenght = (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.srb.Signature = Encoding.ASCII.GetBytes(Kernel32.IntelNVMeMiniPortSignature2);
passThrough.srb.Timeout = 10;
passThrough.srb.ControlCode = Kernel32.NVME_PASS_THROUGH_SRB_IO_CODE;
passThrough.srb.ReturnCode = 0;
passThrough.srb.Length = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>() - (uint)Marshal.SizeOf<Kernel32.SRB_IO_CONTROL>();
passThrough.NVMeCmd = new uint[16];
passThrough.NVMeCmd[0] = 6; //identify
passThrough.NVMeCmd[10] = 1; //return to host
passThrough.Direction = Kernel32.NVME_DIRECTION.NVME_FROM_DEV_TO_HOST;
passThrough.QueueId = 0;
passThrough.DataBufferLen = (uint)passThrough.DataBuffer.Length;
passThrough.MetaDataLen = 0;
passThrough.ReturnBufferLen = (uint)Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
int length = Marshal.SizeOf<Kernel32.NVME_PASS_THROUGH_IOCTL>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(passThrough, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_MINIPORT, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer) { }
else
{
handle.Close();
handle = null;
}
return handle;
}
}
@@ -8,104 +8,56 @@ using System.Linq;
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class NVMeSamsung : INVMeDrive
{
internal class NVMeSamsung : INVMeDrive
//samsung nvme access
//https://github.com/hiyohiyo/CrystalDiskInfo
//https://github.com/hiyohiyo/CrystalDiskInfo/blob/master/AtaSmart.cpp
public SafeHandle Identify(StorageInfo storageInfo)
{
//samsung nvme access
//https://github.com/hiyohiyo/CrystalDiskInfo
//https://github.com/hiyohiyo/CrystalDiskInfo/blob/master/AtaSmart.cpp
return NVMeWindows.IdentifyDevice(storageInfo);
}
public SafeHandle Identify(StorageInfo storageInfo)
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40; // Transfer Length
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 1;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer)
{
return NVMeWindows.IdentifyDevice(storageInfo);
}
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40; // Transfer Length
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 1;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer)
{
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 2; // Transfer Length (high)
buffers.Spt.Cdb[9] = 0; // Transfer Length (low)
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer && buffers.DataBuf.Any(x => x != 0))
{
IntPtr offset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
@@ -116,75 +68,151 @@ namespace LibreHardwareMonitor.Hardware.Storage
buffers.Spt.DataBufferOffset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 6; // Log Data
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40; // Transfer Length
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 2;
buffers.DataBuf[4] = 0xff;
buffers.DataBuf[5] = 0xff;
buffers.DataBuf[6] = 0xff;
buffers.DataBuf[7] = 0xff;
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 2; // Transfer Length (high)
buffers.Spt.Cdb[9] = 0; // Transfer Length (low)
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer)
validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer && buffers.DataBuf.Any(x => x != 0))
{
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 6; // Log Data
buffers.Spt.Cdb[8] = 2; // Transfer Length (high)
buffers.Spt.Cdb[9] = 0; // Transfer Length (low)
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
IntPtr offset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 6; // Log Data
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40; // Transfer Length
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 2;
buffers.DataBuf[4] = 0xff;
buffers.DataBuf[5] = 0xff;
buffers.DataBuf[6] = 0xff;
buffers.DataBuf[7] = 0xff;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
if (handle?.IsInvalid != false)
return null;
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 6; // Log Data
buffers.Spt.Cdb[8] = 2; // Transfer Length (high)
buffers.Spt.Cdb[9] = 0; // Transfer Length (low)
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
IntPtr offset = Marshal.OffsetOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
if (handle?.IsInvalid != false)
return null;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40;
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 1;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
if (validTransfer)
{
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
@@ -195,66 +223,37 @@ namespace LibreHardwareMonitor.Hardware.Storage
buffers.Spt.DataBufferOffset = Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 0; // Transfer Length
buffers.Spt.Cdb[9] = 0x40;
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_OUT;
buffers.DataBuf[0] = 1;
buffers.Spt.Cdb[8] = 2; // Transfer Length
buffers.Spt.Cdb[9] = 0;
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
int length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
IntPtr buffer = Marshal.AllocHGlobal(length);
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
Marshal.FreeHGlobal(buffer);
validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
//read data from samsung SSD
buffers = Kernel32.CreateStruct<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffers.Spt.Length = (ushort)Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH>();
buffers.Spt.PathId = 0;
buffers.Spt.TargetId = 0;
buffers.Spt.Lun = 0;
buffers.Spt.SenseInfoLength = 24;
buffers.Spt.DataTransferLength = (uint)buffers.DataBuf.Length;
buffers.Spt.TimeOutValue = 2;
buffers.Spt.DataBufferOffset = Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.DataBuf));
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS), nameof(Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS.SenseBuf));
buffers.Spt.CdbLength = 16;
buffers.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
buffers.Spt.Cdb[1] = 0xFE; // Samsung Protocol
buffers.Spt.Cdb[3] = 5; // Identify
buffers.Spt.Cdb[8] = 2; // Transfer Length
buffers.Spt.Cdb[9] = 0;
buffers.Spt.DataIn = (byte)Kernel32.SCSI_IOCTL_DATA.SCSI_IOCTL_DATA_IN;
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS result = Marshal.PtrToStructure<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(buffer);
length = Marshal.SizeOf<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>();
buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(buffers, buffer, false);
validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_SCSI_PASS_THROUGH, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS result = Marshal.PtrToStructure<Kernel32.SCSI_PASS_THROUGH_WITH_BUFFERS>(buffer);
if (result.DataBuf.All(x => x == 0))
{
handle.Close();
handle = null;
}
}
else
if (result.DataBuf.All(x => x == 0))
{
handle.Close();
handle = null;
}
Marshal.FreeHGlobal(buffer);
}
else
{
handle.Close();
handle = null;
}
return handle;
Marshal.FreeHGlobal(buffer);
}
return handle;
}
}
}
@@ -8,184 +8,183 @@ using System.Runtime.InteropServices;
using System.Text;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public class NVMeSmart : IDisposable
{
public class NVMeSmart : IDisposable
private readonly int _driveNumber;
private readonly SafeHandle _handle;
internal NVMeSmart(StorageInfo storageInfo)
{
private readonly int _driveNumber;
private readonly SafeHandle _handle;
_driveNumber = storageInfo.Index;
NVMeDrive = null;
string name = storageInfo.Name;
internal NVMeSmart(StorageInfo storageInfo)
// Test Samsung protocol.
if (NVMeDrive == null && name.IndexOf("Samsung", StringComparison.OrdinalIgnoreCase) > -1)
{
_driveNumber = storageInfo.Index;
NVMeDrive = null;
string name = storageInfo.Name;
// Test Samsung protocol.
if (NVMeDrive == null && name.IndexOf("Samsung", StringComparison.OrdinalIgnoreCase) > -1)
_handle = NVMeSamsung.IdentifyDevice(storageInfo);
if (_handle != null)
{
_handle = NVMeSamsung.IdentifyDevice(storageInfo);
if (_handle != null)
NVMeDrive = new NVMeSamsung();
if (!NVMeDrive.IdentifyController(_handle, out _))
{
NVMeDrive = new NVMeSamsung();
if (!NVMeDrive.IdentifyController(_handle, out _))
{
NVMeDrive = null;
}
}
}
// Test Intel protocol.
if (NVMeDrive == null && name.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) > -1)
{
_handle = NVMeIntel.IdentifyDevice(storageInfo);
if (_handle != null)
{
NVMeDrive = new NVMeIntel();
}
}
// Test Intel raid protocol.
if (NVMeDrive == null && name.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) > -1)
{
_handle = NVMeIntelRst.IdentifyDevice(storageInfo);
if (_handle != null)
{
NVMeDrive = new NVMeIntelRst();
}
}
// Test Windows generic driver protocol.
if (NVMeDrive == null)
{
_handle = NVMeWindows.IdentifyDevice(storageInfo);
if (_handle != null)
{
NVMeDrive = new NVMeWindows();
NVMeDrive = null;
}
}
}
public bool IsValid
// Test Intel protocol.
if (NVMeDrive == null && name.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) > -1)
{
get
_handle = NVMeIntel.IdentifyDevice(storageInfo);
if (_handle != null)
{
return _handle is { IsInvalid: false };
NVMeDrive = new NVMeIntel();
}
}
internal INVMeDrive NVMeDrive { get; }
public void Dispose()
// Test Intel raid protocol.
if (NVMeDrive == null && name.IndexOf("Intel", StringComparison.OrdinalIgnoreCase) > -1)
{
Close();
}
private static string GetString(byte[] s)
{
return Encoding.ASCII.GetString(s).Trim('\t', '\n', '\r', ' ', '\0');
}
private static short KelvinToCelsius(ushort k)
{
return (short)(k > 0 ? k - 273 : short.MinValue);
}
private static short KelvinToCelsius(byte[] k)
{
return KelvinToCelsius(BitConverter.ToUInt16(k, 0));
}
public void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && _handle is { IsClosed: false })
_handle = NVMeIntelRst.IdentifyDevice(storageInfo);
if (_handle != null)
{
_handle.Close();
NVMeDrive = new NVMeIntelRst();
}
}
public Storage.NVMeInfo GetInfo()
// Test Windows generic driver protocol.
if (NVMeDrive == null)
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_IDENTIFY_CONTROLLER_DATA();
if (NVMeDrive != null)
valid = NVMeDrive.IdentifyController(_handle, out data);
if (!valid)
return null;
return new NVMeInfo(_driveNumber, data);
}
public Storage.NVMeHealthInfo GetHealthInfo()
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_HEALTH_INFO_LOG();
if (NVMeDrive != null)
valid = NVMeDrive.HealthInfoLog(_handle, out data);
if (!valid)
return null;
return new NVMeHealthInfo(data);
}
private class NVMeInfo : Storage.NVMeInfo
{
public NVMeInfo(int index, Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
_handle = NVMeWindows.IdentifyDevice(storageInfo);
if (_handle != null)
{
Index = index;
VID = data.VID;
SSVID = data.SSVID;
Serial = GetString(data.SN);
Model = GetString(data.MN);
Revision = GetString(data.FR);
IEEE = data.IEEE;
TotalCapacity = BitConverter.ToUInt64(data.TNVMCAP, 0); // 128bit little endian
UnallocatedCapacity = BitConverter.ToUInt64(data.UNVMCAP, 0);
ControllerId = data.CNTLID;
NumberNamespaces = data.NN;
}
}
private class NVMeHealthInfo : Storage.NVMeHealthInfo
{
public NVMeHealthInfo(Kernel32.NVME_HEALTH_INFO_LOG log)
{
CriticalWarning = (Kernel32.NVME_CRITICAL_WARNING)log.CriticalWarning;
Temperature = KelvinToCelsius(log.CompositeTemp);
AvailableSpare = log.AvailableSpare;
AvailableSpareThreshold = log.AvailableSpareThreshold;
PercentageUsed = log.PercentageUsed;
DataUnitRead = BitConverter.ToUInt64(log.DataUnitRead, 0);
DataUnitWritten = BitConverter.ToUInt64(log.DataUnitWritten, 0);
HostReadCommands = BitConverter.ToUInt64(log.HostReadCommands, 0);
HostWriteCommands = BitConverter.ToUInt64(log.HostWriteCommands, 0);
ControllerBusyTime = BitConverter.ToUInt64(log.ControllerBusyTime, 0);
PowerCycle = BitConverter.ToUInt64(log.PowerCycles, 0);
PowerOnHours = BitConverter.ToUInt64(log.PowerOnHours, 0);
UnsafeShutdowns = BitConverter.ToUInt64(log.UnsafeShutdowns, 0);
MediaErrors = BitConverter.ToUInt64(log.MediaAndDataIntegrityErrors, 0);
ErrorInfoLogEntryCount = BitConverter.ToUInt64(log.NumberErrorInformationLogEntries, 0);
WarningCompositeTemperatureTime = log.WarningCompositeTemperatureTime;
CriticalCompositeTemperatureTime = log.CriticalCompositeTemperatureTime;
TemperatureSensors = new short[log.TemperatureSensor.Length];
for (int i = 0; i < TemperatureSensors.Length; i++)
TemperatureSensors[i] = KelvinToCelsius(log.TemperatureSensor[i]);
NVMeDrive = new NVMeWindows();
}
}
}
}
public bool IsValid
{
get
{
return _handle is { IsInvalid: false };
}
}
internal INVMeDrive NVMeDrive { get; }
public void Dispose()
{
Close();
}
private static string GetString(byte[] s)
{
return Encoding.ASCII.GetString(s).Trim('\t', '\n', '\r', ' ', '\0');
}
private static short KelvinToCelsius(ushort k)
{
return (short)(k > 0 ? k - 273 : short.MinValue);
}
private static short KelvinToCelsius(byte[] k)
{
return KelvinToCelsius(BitConverter.ToUInt16(k, 0));
}
public void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && _handle is { IsClosed: false })
{
_handle.Close();
}
}
public Storage.NVMeInfo GetInfo()
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_IDENTIFY_CONTROLLER_DATA();
if (NVMeDrive != null)
valid = NVMeDrive.IdentifyController(_handle, out data);
if (!valid)
return null;
return new NVMeInfo(_driveNumber, data);
}
public Storage.NVMeHealthInfo GetHealthInfo()
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_HEALTH_INFO_LOG();
if (NVMeDrive != null)
valid = NVMeDrive.HealthInfoLog(_handle, out data);
if (!valid)
return null;
return new NVMeHealthInfo(data);
}
private class NVMeInfo : Storage.NVMeInfo
{
public NVMeInfo(int index, Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
Index = index;
VID = data.VID;
SSVID = data.SSVID;
Serial = GetString(data.SN);
Model = GetString(data.MN);
Revision = GetString(data.FR);
IEEE = data.IEEE;
TotalCapacity = BitConverter.ToUInt64(data.TNVMCAP, 0); // 128bit little endian
UnallocatedCapacity = BitConverter.ToUInt64(data.UNVMCAP, 0);
ControllerId = data.CNTLID;
NumberNamespaces = data.NN;
}
}
private class NVMeHealthInfo : Storage.NVMeHealthInfo
{
public NVMeHealthInfo(Kernel32.NVME_HEALTH_INFO_LOG log)
{
CriticalWarning = (Kernel32.NVME_CRITICAL_WARNING)log.CriticalWarning;
Temperature = KelvinToCelsius(log.CompositeTemp);
AvailableSpare = log.AvailableSpare;
AvailableSpareThreshold = log.AvailableSpareThreshold;
PercentageUsed = log.PercentageUsed;
DataUnitRead = BitConverter.ToUInt64(log.DataUnitRead, 0);
DataUnitWritten = BitConverter.ToUInt64(log.DataUnitWritten, 0);
HostReadCommands = BitConverter.ToUInt64(log.HostReadCommands, 0);
HostWriteCommands = BitConverter.ToUInt64(log.HostWriteCommands, 0);
ControllerBusyTime = BitConverter.ToUInt64(log.ControllerBusyTime, 0);
PowerCycle = BitConverter.ToUInt64(log.PowerCycles, 0);
PowerOnHours = BitConverter.ToUInt64(log.PowerOnHours, 0);
UnsafeShutdowns = BitConverter.ToUInt64(log.UnsafeShutdowns, 0);
MediaErrors = BitConverter.ToUInt64(log.MediaAndDataIntegrityErrors, 0);
ErrorInfoLogEntryCount = BitConverter.ToUInt64(log.NumberErrorInformationLogEntries, 0);
WarningCompositeTemperatureTime = log.WarningCompositeTemperatureTime;
CriticalCompositeTemperatureTime = log.CriticalCompositeTemperatureTime;
TemperatureSensors = new short[log.TemperatureSensor.Length];
for (int i = 0; i < TemperatureSensors.Length; i++)
TemperatureSensors[i] = KelvinToCelsius(log.TemperatureSensor[i]);
}
}
}
@@ -7,122 +7,121 @@ using System;
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class NVMeWindows : INVMeDrive
{
internal class NVMeWindows : INVMeDrive
//windows generic driver nvme access
public SafeHandle Identify(StorageInfo storageInfo)
{
//windows generic driver nvme access
public SafeHandle Identify(StorageInfo storageInfo)
{
return IdentifyDevice(storageInfo);
}
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeIdentify;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.STORAGE_PROTOCOL_NVME_PROTOCOL_DATA_REQUEST_VALUE.NVMeIdentifyCnsController;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
//map NVME_IDENTIFY_CONTROLLER_DATA to nptwb.Buffer
IntPtr offset = Marshal.OffsetOf<Kernel32.STORAGE_QUERY_BUFFER>(nameof(Kernel32.STORAGE_QUERY_BUFFER.Buffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.NVME_LOG_PAGES.NVME_LOG_PAGE_HEALTH_INFO;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
//map NVME_HEALTH_INFO_LOG to nptwb.Buffer
IntPtr offset = Marshal.OffsetOf<Kernel32.STORAGE_QUERY_BUFFER>(nameof(Kernel32.STORAGE_QUERY_BUFFER.Buffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
if (handle?.IsInvalid != false)
return null;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeIdentify;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.STORAGE_PROTOCOL_NVME_PROTOCOL_DATA_REQUEST_VALUE.NVMeIdentifyCnsController;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
Marshal.FreeHGlobal(buffer);
}
else
{
Marshal.FreeHGlobal(buffer);
handle.Close();
handle = null;
}
return handle;
}
return IdentifyDevice(storageInfo);
}
}
public bool IdentifyController(SafeHandle hDevice, out Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeIdentify;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.STORAGE_PROTOCOL_NVME_PROTOCOL_DATA_REQUEST_VALUE.NVMeIdentifyCnsController;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
//map NVME_IDENTIFY_CONTROLLER_DATA to nptwb.Buffer
IntPtr offset = Marshal.OffsetOf<Kernel32.STORAGE_QUERY_BUFFER>(nameof(Kernel32.STORAGE_QUERY_BUFFER.Buffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_IDENTIFY_CONTROLLER_DATA>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public bool HealthInfoLog(SafeHandle hDevice, out Kernel32.NVME_HEALTH_INFO_LOG data)
{
data = Kernel32.CreateStruct<Kernel32.NVME_HEALTH_INFO_LOG>();
if (hDevice?.IsInvalid != false)
return false;
bool result = false;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.NVME_LOG_PAGES.NVME_LOG_PAGE_HEALTH_INFO;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(hDevice, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
//map NVME_HEALTH_INFO_LOG to nptwb.Buffer
IntPtr offset = Marshal.OffsetOf<Kernel32.STORAGE_QUERY_BUFFER>(nameof(Kernel32.STORAGE_QUERY_BUFFER.Buffer));
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
data = Marshal.PtrToStructure<Kernel32.NVME_HEALTH_INFO_LOG>(newPtr);
Marshal.FreeHGlobal(buffer);
result = true;
}
else
{
Marshal.FreeHGlobal(buffer);
}
return result;
}
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
{
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
if (handle?.IsInvalid != false)
return null;
Kernel32.STORAGE_QUERY_BUFFER nptwb = Kernel32.CreateStruct<Kernel32.STORAGE_QUERY_BUFFER>();
nptwb.ProtocolSpecific.ProtocolType = Kernel32.STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
nptwb.ProtocolSpecific.DataType = (uint)Kernel32.STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeIdentify;
nptwb.ProtocolSpecific.ProtocolDataRequestValue = (uint)Kernel32.STORAGE_PROTOCOL_NVME_PROTOCOL_DATA_REQUEST_VALUE.NVMeIdentifyCnsController;
nptwb.ProtocolSpecific.ProtocolDataOffset = (uint)Marshal.SizeOf<Kernel32.STORAGE_PROTOCOL_SPECIFIC_DATA>();
nptwb.ProtocolSpecific.ProtocolDataLength = (uint)nptwb.Buffer.Length;
nptwb.PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
nptwb.QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery;
int length = Marshal.SizeOf<Kernel32.STORAGE_QUERY_BUFFER>();
IntPtr buffer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(nptwb, buffer, false);
bool validTransfer = Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, buffer, length, buffer, length, out _, IntPtr.Zero);
if (validTransfer)
{
Marshal.FreeHGlobal(buffer);
}
else
{
Marshal.FreeHGlobal(buffer);
handle.Close();
handle = null;
}
return handle;
}
}
@@ -6,16 +6,15 @@
using System;
namespace LibreHardwareMonitor.Hardware.Storage
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal class NamePrefixAttribute : Attribute
{
public NamePrefixAttribute(string namePrefix)
{
Prefix = namePrefix;
}
namespace LibreHardwareMonitor.Hardware.Storage;
public string Prefix { get; }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal class NamePrefixAttribute : Attribute
{
public NamePrefixAttribute(string namePrefix)
{
Prefix = namePrefix;
}
}
public string Prefix { get; }
}
@@ -6,16 +6,15 @@
using System;
namespace LibreHardwareMonitor.Hardware.Storage
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal class RequireSmartAttribute : Attribute
{
public RequireSmartAttribute(byte attributeId)
{
AttributeId = attributeId;
}
namespace LibreHardwareMonitor.Hardware.Storage;
public byte AttributeId { get; }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal class RequireSmartAttribute : Attribute
{
public RequireSmartAttribute(byte attributeId)
{
AttributeId = attributeId;
}
}
public byte AttributeId { get; }
}
@@ -7,97 +7,96 @@
using System.Collections.Generic;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
public class SmartAttribute
{
public class SmartAttribute
private readonly RawValueConversion _rawValueConversion;
public delegate float RawValueConversion(byte[] rawValue, byte value, IReadOnlyList<IParameter> parameters);
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
public SmartAttribute(byte id, string name) : this(id, name, null, null, 0, null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="rawValueConversion">
/// A delegate for converting the raw byte
/// array into a value (or null to use the attribute value).
/// </param>
public SmartAttribute(byte id, string name, RawValueConversion rawValueConversion) : this(id, name, rawValueConversion, null, 0, null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="rawValueConversion">
/// A delegate for converting the raw byte
/// array into a value (or null to use the attribute value).
/// </param>
/// <param name="sensorType">
/// Type of the sensor or null if no sensor is to
/// be created.
/// </param>
/// <param name="sensorChannel">
/// If there exists more than one attribute with
/// the same sensor channel and type, then a sensor is created only for the
/// first attribute.
/// </param>
/// <param name="sensorName">
/// The name to be used for the sensor, or null if
/// no sensor is created.
/// </param>
/// <param name="defaultHiddenSensor">True to hide the sensor initially.</param>
/// <param name="parameterDescriptions">
/// Description for the parameters of the sensor
/// (or null).
/// </param>
public SmartAttribute(byte id, string name, RawValueConversion rawValueConversion, SensorType? sensorType, int sensorChannel, string sensorName, bool defaultHiddenSensor = false, ParameterDescription[] parameterDescriptions = null)
{
private readonly RawValueConversion _rawValueConversion;
public delegate float RawValueConversion(byte[] rawValue, byte value, IReadOnlyList<IParameter> parameters);
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
public SmartAttribute(byte id, string name) : this(id, name, null, null, 0, null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="rawValueConversion">
/// A delegate for converting the raw byte
/// array into a value (or null to use the attribute value).
/// </param>
public SmartAttribute(byte id, string name, RawValueConversion rawValueConversion) : this(id, name, rawValueConversion, null, 0, null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SmartAttribute" /> class.
/// </summary>
/// <param name="id">The SMART id of the attribute.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="rawValueConversion">
/// A delegate for converting the raw byte
/// array into a value (or null to use the attribute value).
/// </param>
/// <param name="sensorType">
/// Type of the sensor or null if no sensor is to
/// be created.
/// </param>
/// <param name="sensorChannel">
/// If there exists more than one attribute with
/// the same sensor channel and type, then a sensor is created only for the
/// first attribute.
/// </param>
/// <param name="sensorName">
/// The name to be used for the sensor, or null if
/// no sensor is created.
/// </param>
/// <param name="defaultHiddenSensor">True to hide the sensor initially.</param>
/// <param name="parameterDescriptions">
/// Description for the parameters of the sensor
/// (or null).
/// </param>
public SmartAttribute(byte id, string name, RawValueConversion rawValueConversion, SensorType? sensorType, int sensorChannel, string sensorName, bool defaultHiddenSensor = false, ParameterDescription[] parameterDescriptions = null)
{
Id = id;
Name = name;
_rawValueConversion = rawValueConversion;
SensorType = sensorType;
SensorChannel = sensorChannel;
SensorName = sensorName;
DefaultHiddenSensor = defaultHiddenSensor;
ParameterDescriptions = parameterDescriptions;
}
public bool DefaultHiddenSensor { get; }
public bool HasRawValueConversion => _rawValueConversion != null;
/// <summary>
/// Gets the SMART identifier.
/// </summary>
public byte Id { get; }
public string Name { get; }
public ParameterDescription[] ParameterDescriptions { get; }
public int SensorChannel { get; }
public string SensorName { get; }
public SensorType? SensorType { get; }
internal float ConvertValue(Kernel32.SMART_ATTRIBUTE value, IReadOnlyList<IParameter> parameters)
{
if (_rawValueConversion == null)
return value.CurrentValue;
return _rawValueConversion(value.RawValue, value.CurrentValue, parameters);
}
Id = id;
Name = name;
_rawValueConversion = rawValueConversion;
SensorType = sensorType;
SensorChannel = sensorChannel;
SensorName = sensorName;
DefaultHiddenSensor = defaultHiddenSensor;
ParameterDescriptions = parameterDescriptions;
}
}
public bool DefaultHiddenSensor { get; }
public bool HasRawValueConversion => _rawValueConversion != null;
/// <summary>
/// Gets the SMART identifier.
/// </summary>
public byte Id { get; }
public string Name { get; }
public ParameterDescription[] ParameterDescriptions { get; }
public int SensorChannel { get; }
public string SensorName { get; }
public SensorType? SensorType { get; }
internal float ConvertValue(Kernel32.SMART_ATTRIBUTE value, IReadOnlyList<IParameter> parameters)
{
if (_rawValueConversion == null)
return value.CurrentValue;
return _rawValueConversion(value.RawValue, value.CurrentValue, parameters);
}
}
@@ -4,261 +4,260 @@
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
/// <summary>
/// Localization class for SMART attribute names.
/// </summary>
public static class SmartNames
{
/// <summary>
/// Localization class for SMART attribute names.
/// </summary>
public static class SmartNames
{
public static string AirflowTemperature => "Airflow Temperature";
public static string AirflowTemperature => "Airflow Temperature";
public static string AlternativeEraseFailCount => "Alternative Erase Fail Count";
public static string AlternativeEraseFailCount => "Alternative Erase Fail Count";
public static string AlternativeGSenseErrorRate => "Alternative G-Sense Error Rate";
public static string AlternativeGSenseErrorRate => "Alternative G-Sense Error Rate";
public static string AlternativeProgramFailCount => "Alternative Program Fail Count";
public static string AlternativeProgramFailCount => "Alternative Program Fail Count";
public static string AvailableReservedSpace => "Available Reserved Space";
public static string AvailableReservedSpace => "Available Reserved Space";
public static string AverageEraseCount => "Average Erase Count";
public static string AverageEraseCount => "Average Erase Count";
public static string BadBlockFullFlag => "Bad Block Full Flag";
public static string BadBlockFullFlag => "Bad Block Full Flag";
public static string BitErrors => "Bit Errors";
public static string BitErrors => "Bit Errors";
public static string CalibrationRetryCount => "Calibration Retry Count";
public static string CalibrationRetryCount => "Calibration Retry Count";
public static string CommandTimeout => "Command Timeout";
public static string CommandTimeout => "Command Timeout";
public static string ControllerWritesToNand => "Controller Writes to NAND";
public static string ControllerWritesToNand => "Controller Writes to NAND";
public static string CorrectedErrors => "Corrected Errors";
public static string CorrectedErrors => "Corrected Errors";
public static string CrcErrorCount => "CRC Error Count";
public static string CrcErrorCount => "CRC Error Count";
public static string CurrentPendingSectorCount => "Current Pending Sector Count";
public static string CurrentPendingSectorCount => "Current Pending Sector Count";
public static string DataAddressMarkErrors => "Data Address Mark errors";
public static string DataAddressMarkErrors => "Data Address Mark errors";
public static string DiskShift => "Disk Shift";
public static string DiskShift => "Disk Shift";
public static string DriveTemperature => "Drive Temperature";
public static string DriveTemperature => "Drive Temperature";
public static string EccRate => "ECC Rate";
public static string EccRate => "ECC Rate";
public static string EmergencyRetractCycleCount => "Emergency Retract Cycle Count";
public static string EmergencyRetractCycleCount => "Emergency Retract Cycle Count";
public static string EndToEndError => "End-to-End error";
public static string EndToEndError => "End-to-End error";
public static string EnduranceRemaining => "Endurance Remaining";
public static string EnduranceRemaining => "Endurance Remaining";
public static string EraseFailCount => "Erase Fail Count";
public static string EraseFailCount => "Erase Fail Count";
public static string EraseFailCountChip => "Erase Fail Count (Chip)";
public static string EraseFailCountChip => "Erase Fail Count (Chip)";
public static string EraseFailCountTotal => "Erase Fail Count (Total)";
public static string EraseFailCountTotal => "Erase Fail Count (Total)";
public static string EraseFailure => "Erase Failure";
public static string EraseFailure => "Erase Failure";
public static string ErrorCorrectionCount => "Error Correction Count";
public static string ErrorCorrectionCount => "Error Correction Count";
public static string ExceptionModeStatus => "Exception Mode Status";
public static string ExceptionModeStatus => "Exception Mode Status";
public static string FactoryBadBlockCount => "Factory Bad Block Count";
public static string FactoryBadBlockCount => "Factory Bad Block Count";
public static string FlyingHeight => "Flying Height";
public static string FlyingHeight => "Flying Height";
public static string FreeFallProtection => "Free Fall Protection";
public static string FreeFallProtection => "Free Fall Protection";
public static string FTLProgramNANDPagesCount => "FTL Program NAND Pages Count";
public static string FTLProgramNANDPagesCount => "FTL Program NAND Pages Count";
public static string GmrHeadAmplitude => "GMR Head Amplitude";
public static string GmrHeadAmplitude => "GMR Head Amplitude";
public static string GSenseErrorRate => "G-sense Error Rate";
public static string GSenseErrorRate => "G-sense Error Rate";
public static string HardwareEccRecovered => "Hardware ECC Recovered";
public static string HardwareEccRecovered => "Hardware ECC Recovered";
public static string HeadFlyingHours => "Head Flying Hours";
public static string HeadFlyingHours => "Head Flying Hours";
public static string HeadStability => "Head Stability";
public static string HeadStability => "Head Stability";
public static string HighFlyWrites => "High Fly Writes";
public static string HighFlyWrites => "High Fly Writes";
public static string HostProgramNANDPagesCount => "Host Program NAND Pages Count";
public static string HostProgramNANDPagesCount => "Host Program NAND Pages Count";
public static string HostReads => "Host Reads";
public static string HostReads => "Host Reads";
public static string HostWrites => "Host Writes";
public static string HostWrites => "Host Writes";
public static string HostWritesToController => "Host Writes to Controller";
public static string HostWritesToController => "Host Writes to Controller";
public static string InducedOpVibrationDetection => "Induced Op-Vibration Detection";
public static string InducedOpVibrationDetection => "Induced Op-Vibration Detection";
public static string InitialBadBlockCount => "Initial Bad Block Count";
public static string InitialBadBlockCount => "Initial Bad Block Count";
public static string LoadCycleCount => "Load Cycle Count";
public static string LoadCycleCount => "Load Cycle Count";
public static string LoadedHours => "Loaded Hours";
public static string LoadedHours => "Loaded Hours";
public static string LoadFriction => "Load Friction";
public static string LoadFriction => "Load Friction";
public static string LoadInTime => "Load 'In'-time";
public static string LoadInTime => "Load 'In'-time";
public static string LoadUnloadCycleCount => "Load/Unload Cycle Count";
public static string LoadUnloadCycleCount => "Load/Unload Cycle Count";
public static string LoadUnloadCycleCountFujitsu => "Load/Unload Cycle Count (Fujitus)";
public static string LoadUnloadCycleCountFujitsu => "Load/Unload Cycle Count (Fujitus)";
public static string LoadUnloadRetryCount => "Load/Unload Retry Count";
public static string LoadUnloadRetryCount => "Load/Unload Retry Count";
public static string MaxCellCycles => "Max Cell Cycles";
public static string MaxCellCycles => "Max Cell Cycles";
public static string MaxErase => "Max Erase";
public static string MaxErase => "Max Erase";
public static string MediaWearOutIndicator => "Media Wear Out Indicator";
public static string MediaWearOutIndicator => "Media Wear Out Indicator";
public static string MinErase => "Min Erase";
public static string MinErase => "Min Erase";
public static string MultiZoneErrorRate => "Multi-Zone Error Rate";
public static string MultiZoneErrorRate => "Multi-Zone Error Rate";
public static string NewFailingBlockCount => "New Failing Block Count";
public static string NewFailingBlockCount => "New Failing Block Count";
public static string Non4KAlignedAccess => "Non-4k Aligned Access";
public static string Non4KAlignedAccess => "Non-4k Aligned Access";
public static string OfflineSeekPerformance => "Offline Seek Performance";
public static string OfflineSeekPerformance => "Offline Seek Performance";
public static string OffLineUncorrectableErrorCount => "Off-Line Uncorrectable Error Count";
public static string OffLineUncorrectableErrorCount => "Off-Line Uncorrectable Error Count";
public static string PowerCycleCount => "Power Cycle Count";
public static string PowerCycleCount => "Power Cycle Count";
public static string PowerOffRetractCycle => "Power-Off Retract Cycle";
public static string PowerOffRetractCycle => "Power-Off Retract Cycle";
public static string PowerOnHours => "Power-On Hours (POH)";
public static string PowerOnHours => "Power-On Hours (POH)";
public static string PowerRecoveryCount => "Power Recovery Count";
public static string PowerRecoveryCount => "Power Recovery Count";
public static string ProgramFailCount => "Program Fail Count";
public static string ProgramFailCount => "Program Fail Count";
public static string ProgramFailCountChip => "Program Fail Count (Chip)";
public static string ProgramFailCountChip => "Program Fail Count (Chip)";
public static string ProgramFailCountTotal => "Program Fail Count (Total)";
public static string ProgramFailCountTotal => "Program Fail Count (Total)";
public static string ProgramFailure => "Program Failure";
public static string ProgramFailure => "Program Failure";
public static string RawReadErrorRate => "Raw Read Error Rate";
public static string RawReadErrorRate => "Raw Read Error Rate";
public static string ReadChannelMargin => "Read Channel Margin";
public static string ReadChannelMargin => "Read Channel Margin";
public static string ReadCommands => "Read Commands";
public static string ReadCommands => "Read Commands";
public static string ReadErrorRate => "Read Error Rate";
public static string ReadErrorRate => "Read Error Rate";
public static string ReadErrorRetryRate => "Read Error Retry Rate";
public static string ReadErrorRetryRate => "Read Error Retry Rate";
public static string ReadFailure => "Read Failure";
public static string ReadFailure => "Read Failure";
public static string ReallocatedNANDBlockCount => "Reallocated NAND Block Count";
public static string ReallocatedNANDBlockCount => "Reallocated NAND Block Count";
public static string ReallocatedSectorsCount => "Reallocated Sectors Count";
public static string ReallocatedSectorsCount => "Reallocated Sectors Count";
public static string ReallocationEventCount => "Reallocation Event Count";
public static string ReallocationEventCount => "Reallocation Event Count";
public static string RecalibrationRetries => "Recalibration Retries";
public static string RecalibrationRetries => "Recalibration Retries";
public static string RemainingLife => "Remaining Life";
public static string RemainingLife => "Remaining Life";
public static string ReportedUncorrectableErrors => "Reported Uncorrectable Errors";
public static string ReportedUncorrectableErrors => "Reported Uncorrectable Errors";
public static string RetiredBlockCount => "Retired Block Count";
public static string RetiredBlockCount => "Retired Block Count";
public static string RunOutCancel => "Run Out Cancel";
public static string RunOutCancel => "Run Out Cancel";
public static string RuntimeBadBlockTotal => "Runtime Bad Block Total";
public static string RuntimeBadBlockTotal => "Runtime Bad Block Total";
public static string SataDownshiftErrorCount => "SATA Downshift Error Count";
public static string SataDownshiftErrorCount => "SATA Downshift Error Count";
public static string SataErrorCountCrc => "SATA Error Count CRC";
public static string SataErrorCountCrc => "SATA Error Count CRC";
public static string SataErrorCountHandshake => "SATA Error Count Handshake";
public static string SataErrorCountHandshake => "SATA Error Count Handshake";
public static string SectorsRead => "Sectors Read";
public static string SectorsRead => "Sectors Read";
public static string SectorsWritten => "Sectors Written";
public static string SectorsWritten => "Sectors Written";
public static string SeekErrorRate => "Seek Error Rate";
public static string SeekErrorRate => "Seek Error Rate";
public static string SeekTimePerformance => "Seek Time Performance";
public static string SeekTimePerformance => "Seek Time Performance";
public static string ShockDuringWrite => "Shock During Write";
public static string ShockDuringWrite => "Shock During Write";
public static string SoftEccCorrection => "Soft ECC Correction";
public static string SoftEccCorrection => "Soft ECC Correction";
public static string SoftReadErrorRate => "Soft Read Error Rate";
public static string SoftReadErrorRate => "Soft Read Error Rate";
public static string SpinBuzz => "Spin Buzz";
public static string SpinBuzz => "Spin Buzz";
public static string SpinHighCurrent => "Spin High Current";
public static string SpinHighCurrent => "Spin High Current";
public static string SpinRetryCount => "Spin Retry Count";
public static string SpinRetryCount => "Spin Retry Count";
public static string SpinUpTime => "Spin-Up Time";
public static string SpinUpTime => "Spin-Up Time";
public static string StartStopCount => "Start/Stop Count";
public static string StartStopCount => "Start/Stop Count";
public static string SuccessfulRAINRecoveryCount => "Successful RAIN Recovery Count";
public static string SuccessfulRAINRecoveryCount => "Successful RAIN Recovery Count";
public static string SupercapStatus => "Supercap Status";
public static string SupercapStatus => "Supercap Status";
public static string TaCounterDetected => "TA Counter Detected";
public static string TaCounterDetected => "TA Counter Detected";
public static string Temperature => "Temperature";
public static string Temperature => "Temperature";
public static string TemperatureDifferenceFrom100 => "Temperature Difference from 100";
public static string TemperatureDifferenceFrom100 => "Temperature Difference from 100";
public static string TemperatureExceedCount => "Temperature Exceed Count";
public static string TemperatureExceedCount => "Temperature Exceed Count";
public static string ThermalAsperityRate => "Thermal Asperity Rate (TAR)";
public static string ThermalAsperityRate => "Thermal Asperity Rate (TAR)";
public static string ThroughputPerformance => "Throughput Performance";
public static string ThroughputPerformance => "Throughput Performance";
public static string TorqueAmplificationCount => "Torque Amplification Count";
public static string TorqueAmplificationCount => "Torque Amplification Count";
public static string TotalLbasRead => "Total LBAs Read";
public static string TotalLbasRead => "Total LBAs Read";
public static string TotalLbasWritten => "Total LBAs Written";
public static string TotalLbasWritten => "Total LBAs Written";
public static string TransferErrorRate => "Transfer Error Rate";
public static string TransferErrorRate => "Transfer Error Rate";
public static string UltraDmaCrcErrorCount => "UltraDMA CRC Error Count";
public static string UltraDmaCrcErrorCount => "UltraDMA CRC Error Count";
public static string UncorrectableErrorCount => "Uncorrectable Error Count";
public static string UncorrectableErrorCount => "Uncorrectable Error Count";
public static string UncorrectableSectorCount => "Uncorrectable Sector Count";
public static string UncorrectableSectorCount => "Uncorrectable Sector Count";
public static string UnexpectedPowerLossCount => "Unexpected Power Loss Count";
public static string UnexpectedPowerLossCount => "Unexpected Power Loss Count";
public static string Unknown => "Unknown";
public static string Unknown => "Unknown";
public static string UnknownUnique => "Unknown Unique";
public static string UnknownUnique => "Unknown Unique";
public static string UnrecoverableEcc => "Unrecoverable ECC";
public static string UnrecoverableEcc => "Unrecoverable ECC";
public static string UnsafeShutdownCount => "Unsafe Shutdown Count";
public static string UnsafeShutdownCount => "Unsafe Shutdown Count";
public static string UnusedReserveNANDBlocks => "Unused Reserve NAND Blocks";
public static string UnusedReserveNANDBlocks => "Unused Reserve NAND Blocks";
public static string UsedReservedBlockCountChip => "Used Reserved Block Count (Chip)";
public static string UsedReservedBlockCountChip => "Used Reserved Block Count (Chip)";
public static string UsedReservedBlockCountTotal => "Used Reserved Block Count (Total)";
public static string UsedReservedBlockCountTotal => "Used Reserved Block Count (Total)";
public static string VibrationDuringWrite => "Vibration During Write";
public static string VibrationDuringWrite => "Vibration During Write";
public static string WearLevelingCount => "Wear Leveling Count";
public static string WearLevelingCount => "Wear Leveling Count";
public static string WearRangeDelta => "Wear Range Delta";
public static string WearRangeDelta => "Wear Range Delta";
public static string WriteCommands => "Write Commands";
public static string WriteCommands => "Write Commands";
public static string WriteErrorRate => "Write Error Rate";
}
}
public static string WriteErrorRate => "Write Error Rate";
}
@@ -6,39 +6,38 @@
using System.Collections.Generic;
namespace LibreHardwareMonitor.Hardware.Storage
{
[NamePrefix(""), RequireSmart(0x01), RequireSmart(0x09), RequireSmart(0x0C), RequireSmart(0xD1), RequireSmart(0xCE), RequireSmart(0xCF)]
internal class SsdIndilinx : AtaStorage
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x01, SmartNames.ReadErrorRate),
new(0x09, SmartNames.PowerOnHours),
new(0x0C, SmartNames.PowerCycleCount),
new(0xB8, SmartNames.InitialBadBlockCount),
new(0xC3, SmartNames.ProgramFailure),
new(0xC4, SmartNames.EraseFailure),
new(0xC5, SmartNames.ReadFailure),
new(0xC6, SmartNames.SectorsRead),
new(0xC7, SmartNames.SectorsWritten),
new(0xC8, SmartNames.ReadCommands),
new(0xC9, SmartNames.WriteCommands),
new(0xCA, SmartNames.BitErrors),
new(0xCB, SmartNames.CorrectedErrors),
new(0xCC, SmartNames.BadBlockFullFlag),
new(0xCD, SmartNames.MaxCellCycles),
new(0xCE, SmartNames.MinErase),
new(0xCF, SmartNames.MaxErase),
new(0xD0, SmartNames.AverageEraseCount),
new(0xD1, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xD2, SmartNames.UnknownUnique),
new(0xD3, SmartNames.SataErrorCountCrc),
new(0xD4, SmartNames.SataErrorCountHandshake)
};
namespace LibreHardwareMonitor.Hardware.Storage;
public SsdIndilinx(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
[NamePrefix(""), RequireSmart(0x01), RequireSmart(0x09), RequireSmart(0x0C), RequireSmart(0xD1), RequireSmart(0xCE), RequireSmart(0xCF)]
internal class SsdIndilinx : AtaStorage
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x01, SmartNames.ReadErrorRate),
new(0x09, SmartNames.PowerOnHours),
new(0x0C, SmartNames.PowerCycleCount),
new(0xB8, SmartNames.InitialBadBlockCount),
new(0xC3, SmartNames.ProgramFailure),
new(0xC4, SmartNames.EraseFailure),
new(0xC5, SmartNames.ReadFailure),
new(0xC6, SmartNames.SectorsRead),
new(0xC7, SmartNames.SectorsWritten),
new(0xC8, SmartNames.ReadCommands),
new(0xC9, SmartNames.WriteCommands),
new(0xCA, SmartNames.BitErrors),
new(0xCB, SmartNames.CorrectedErrors),
new(0xCC, SmartNames.BadBlockFullFlag),
new(0xCD, SmartNames.MaxCellCycles),
new(0xCE, SmartNames.MinErase),
new(0xCF, SmartNames.MaxErase),
new(0xD0, SmartNames.AverageEraseCount),
new(0xD1, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xD2, SmartNames.UnknownUnique),
new(0xD3, SmartNames.SataErrorCountCrc),
new(0xD4, SmartNames.SataErrorCountHandshake)
};
public SsdIndilinx(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
@@ -6,45 +6,44 @@
using System.Collections.Generic;
namespace LibreHardwareMonitor.Hardware.Storage
{
[NamePrefix("INTEL SSD"), RequireSmart(0xE1), RequireSmart(0xE8), RequireSmart(0xE9)]
internal class SsdIntel : AtaStorage
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x01, SmartNames.ReadErrorRate),
new(0x03, SmartNames.SpinUpTime),
new(0x04, SmartNames.StartStopCount, RawToInt),
new(0x05, SmartNames.ReallocatedSectorsCount),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAA, SmartNames.AvailableReservedSpace),
new(0xAB, SmartNames.ProgramFailCount),
new(0xAC, SmartNames.EraseFailCount),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new(0xB8, SmartNames.EndToEndError),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xBE,
SmartNames.Temperature,
(r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.AirflowTemperature,
false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC0, SmartNames.UnsafeShutdownCount),
new(0xC7, SmartNames.CrcErrorCount, RawToInt),
new(0xE1, SmartNames.HostWrites, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 0, SmartNames.HostWrites),
new(0xE8, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xE9, SmartNames.MediaWearOutIndicator),
new(0xF1, SmartNames.HostWrites, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 0, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 1, SmartNames.HostReads)
};
namespace LibreHardwareMonitor.Hardware.Storage;
public SsdIntel(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
[NamePrefix("INTEL SSD"), RequireSmart(0xE1), RequireSmart(0xE8), RequireSmart(0xE9)]
internal class SsdIntel : AtaStorage
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x01, SmartNames.ReadErrorRate),
new(0x03, SmartNames.SpinUpTime),
new(0x04, SmartNames.StartStopCount, RawToInt),
new(0x05, SmartNames.ReallocatedSectorsCount),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAA, SmartNames.AvailableReservedSpace),
new(0xAB, SmartNames.ProgramFailCount),
new(0xAC, SmartNames.EraseFailCount),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new(0xB8, SmartNames.EndToEndError),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xBE,
SmartNames.Temperature,
(r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.AirflowTemperature,
false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC0, SmartNames.UnsafeShutdownCount),
new(0xC7, SmartNames.CrcErrorCount, RawToInt),
new(0xE1, SmartNames.HostWrites, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 0, SmartNames.HostWrites),
new(0xE8, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xE9, SmartNames.MediaWearOutIndicator),
new(0xF1, SmartNames.HostWrites, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 0, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, (r, v, p) => RawToInt(r, v, p) / 0x20, SensorType.Data, 1, SmartNames.HostReads)
};
public SsdIntel(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
@@ -7,109 +7,108 @@
using System.Collections.Generic;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
[NamePrefix(""), RequireSmart(0xAB), RequireSmart(0xAC), RequireSmart(0xAD), RequireSmart(0xAE), RequireSmart(0xC4), RequireSmart(0xCA), RequireSmart(0xCE)]
internal class SsdMicron : AtaStorage
{
[NamePrefix(""), RequireSmart(0xAB), RequireSmart(0xAC), RequireSmart(0xAD), RequireSmart(0xAE), RequireSmart(0xC4), RequireSmart(0xCA), RequireSmart(0xCE)]
internal class SsdMicron : AtaStorage
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
new(0x01, SmartNames.ReadErrorRate, RawToInt),
new(0x05, SmartNames.ReallocatedNANDBlockCount, RawToInt),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAA, SmartNames.NewFailingBlockCount, RawToInt),
new(0xAB, SmartNames.ProgramFailCount, RawToInt),
new(0xAC, SmartNames.EraseFailCount, RawToInt),
new(0xAD, SmartNames.WearLevelingCount, RawToInt),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB4, SmartNames.UnusedReserveNANDBlocks, RawToInt),
new(0xB5, SmartNames.Non4KAlignedAccess, (raw, _, _) => 6e4f * ((raw[5] << 8) | raw[4])),
new(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new(0xB8, SmartNames.ErrorCorrectionCount, RawToInt),
new(0xBB, SmartNames.ReportedUncorrectableErrors, RawToInt),
new(0xBC, SmartNames.CommandTimeout, RawToInt),
new(0xBD, SmartNames.FactoryBadBlockCount, RawToInt),
new(0xC2, SmartNames.Temperature, RawToInt),
new(0xC4, SmartNames.ReallocationEventCount, RawToInt),
new(0xC5, SmartNames.CurrentPendingSectorCount),
new(0xC6, SmartNames.OffLineUncorrectableErrorCount, RawToInt),
new(0xC7, SmartNames.UltraDmaCrcErrorCount, RawToInt),
new(0xCA, SmartNames.RemainingLife, (raw, value, p) => 100 - RawToInt(raw, value, p), SensorType.Level, 0, SmartNames.RemainingLife),
new(0xCE, SmartNames.WriteErrorRate, (raw, _, _) => 6e4f * ((raw[1] << 8) | raw[0])),
new(0xD2, SmartNames.SuccessfulRAINRecoveryCount, RawToInt),
new(0xF6,
SmartNames.TotalLbasWritten,
(r, _, _) => (((long)r[5] << 40) |
((long)r[4] << 32) |
((long)r[3] << 24) |
((long)r[2] << 16) |
((long)r[1] << 8) |
r[0]) *
(512.0f / 1024 / 1024 / 1024),
SensorType.Data,
0,
"Total Bytes Written"),
new(0xF7, SmartNames.HostProgramNANDPagesCount, RawToInt),
new(0xF8, SmartNames.FTLProgramNANDPagesCount, RawToInt)
};
private readonly Sensor _temperature;
private readonly Sensor _writeAmplification;
public SsdMicron(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{
_temperature = new Sensor("Temperature",
0,
false,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("Offset [°C]",
"Temperature offset of the thermal sensor.\n" +
"Temperature = Value + Offset.",
0)
},
settings);
_writeAmplification = new Sensor("Write Amplification",
0,
SensorType.Factor,
this,
settings);
}
/// <inheritdoc />
protected override void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values)
{
float? hostProgramPagesCount = null;
float? ftlProgramPagesCount = null;
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
new(0x01, SmartNames.ReadErrorRate, RawToInt),
new(0x05, SmartNames.ReallocatedNANDBlockCount, RawToInt),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAA, SmartNames.NewFailingBlockCount, RawToInt),
new(0xAB, SmartNames.ProgramFailCount, RawToInt),
new(0xAC, SmartNames.EraseFailCount, RawToInt),
new(0xAD, SmartNames.WearLevelingCount, RawToInt),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB4, SmartNames.UnusedReserveNANDBlocks, RawToInt),
new(0xB5, SmartNames.Non4KAlignedAccess, (raw, _, _) => 6e4f * ((raw[5] << 8) | raw[4])),
new(0xB7, SmartNames.SataDownshiftErrorCount, RawToInt),
new(0xB8, SmartNames.ErrorCorrectionCount, RawToInt),
new(0xBB, SmartNames.ReportedUncorrectableErrors, RawToInt),
new(0xBC, SmartNames.CommandTimeout, RawToInt),
new(0xBD, SmartNames.FactoryBadBlockCount, RawToInt),
new(0xC2, SmartNames.Temperature, RawToInt),
new(0xC4, SmartNames.ReallocationEventCount, RawToInt),
new(0xC5, SmartNames.CurrentPendingSectorCount),
new(0xC6, SmartNames.OffLineUncorrectableErrorCount, RawToInt),
new(0xC7, SmartNames.UltraDmaCrcErrorCount, RawToInt),
new(0xCA, SmartNames.RemainingLife, (raw, value, p) => 100 - RawToInt(raw, value, p), SensorType.Level, 0, SmartNames.RemainingLife),
new(0xCE, SmartNames.WriteErrorRate, (raw, _, _) => 6e4f * ((raw[1] << 8) | raw[0])),
new(0xD2, SmartNames.SuccessfulRAINRecoveryCount, RawToInt),
new(0xF6,
SmartNames.TotalLbasWritten,
(r, _, _) => (((long)r[5] << 40) |
((long)r[4] << 32) |
((long)r[3] << 24) |
((long)r[2] << 16) |
((long)r[1] << 8) |
r[0]) *
(512.0f / 1024 / 1024 / 1024),
SensorType.Data,
0,
"Total Bytes Written"),
new(0xF7, SmartNames.HostProgramNANDPagesCount, RawToInt),
new(0xF8, SmartNames.FTLProgramNANDPagesCount, RawToInt)
};
if (value.Id == 0xF7)
hostProgramPagesCount = RawToInt(value.RawValue, value.CurrentValue, null);
private readonly Sensor _temperature;
private readonly Sensor _writeAmplification;
if (value.Id == 0xF8)
ftlProgramPagesCount = RawToInt(value.RawValue, value.CurrentValue, null);
public SsdMicron(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{
_temperature = new Sensor("Temperature",
0,
false,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("Offset [°C]",
"Temperature offset of the thermal sensor.\n" +
"Temperature = Value + Offset.",
0)
},
settings);
if (value.Id == 0xC2)
{
_temperature.Value = value.RawValue[0] + _temperature.Parameters[0].Value;
_writeAmplification = new Sensor("Write Amplification",
0,
SensorType.Factor,
this,
settings);
if (value.RawValue[0] != 0)
ActivateSensor(_temperature);
}
}
/// <inheritdoc />
protected override void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values)
if (hostProgramPagesCount.HasValue && ftlProgramPagesCount.HasValue)
{
float? hostProgramPagesCount = null;
float? ftlProgramPagesCount = null;
_writeAmplification.Value = hostProgramPagesCount.Value > 0 ? (hostProgramPagesCount.Value + ftlProgramPagesCount) / hostProgramPagesCount.Value : 0;
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
if (value.Id == 0xF7)
hostProgramPagesCount = RawToInt(value.RawValue, value.CurrentValue, null);
if (value.Id == 0xF8)
ftlProgramPagesCount = RawToInt(value.RawValue, value.CurrentValue, null);
if (value.Id == 0xC2)
{
_temperature.Value = value.RawValue[0] + _temperature.Parameters[0].Value;
if (value.RawValue[0] != 0)
ActivateSensor(_temperature);
}
}
if (hostProgramPagesCount.HasValue && ftlProgramPagesCount.HasValue)
{
_writeAmplification.Value = hostProgramPagesCount.Value > 0 ? (hostProgramPagesCount.Value + ftlProgramPagesCount) / hostProgramPagesCount.Value : 0;
ActivateSensor(_writeAmplification);
}
ActivateSensor(_writeAmplification);
}
}
}
@@ -6,26 +6,25 @@
using System.Collections.Generic;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
[NamePrefix("PLEXTOR")]
internal class SsdPlextor : AtaStorage
{
[NamePrefix("PLEXTOR")]
internal class SsdPlextor : AtaStorage
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xF1, SmartNames.HostWrites, RawToGb, SensorType.Data, 0, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, RawToGb, SensorType.Data, 1, SmartNames.HostReads)
};
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xF1, SmartNames.HostWrites, RawToGb, SensorType.Data, 0, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, RawToGb, SensorType.Data, 1, SmartNames.HostReads)
};
public SsdPlextor(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
public SsdPlextor(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
private static float RawToGb(byte[] rawValue, byte value, IReadOnlyList<IParameter> parameters)
{
return RawToInt(rawValue, value, parameters) / 32;
}
private static float RawToGb(byte[] rawValue, byte value, IReadOnlyList<IParameter> parameters)
{
return RawToInt(rawValue, value, parameters) / 32;
}
}
@@ -6,53 +6,52 @@
using System.Collections.Generic;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
[NamePrefix(""), RequireSmart(0xB1), RequireSmart(0xB3), RequireSmart(0xB5), RequireSmart(0xB6), RequireSmart(0xB7), RequireSmart(0xBB), RequireSmart(0xC3), RequireSmart(0xC7)]
internal class SsdSamsung : AtaStorage
{
[NamePrefix(""), RequireSmart(0xB1), RequireSmart(0xB3), RequireSmart(0xB5), RequireSmart(0xB6), RequireSmart(0xB7), RequireSmart(0xBB), RequireSmart(0xC3), RequireSmart(0xC7)]
internal class SsdSamsung : AtaStorage
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x05, SmartNames.ReallocatedSectorsCount),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAF, SmartNames.ProgramFailCountChip, RawToInt),
new(0xB0, SmartNames.EraseFailCountChip, RawToInt),
new(0xB1, SmartNames.WearLevelingCount, RawToInt),
new(0xB2, SmartNames.UsedReservedBlockCountChip, RawToInt),
new(0xB3, SmartNames.UsedReservedBlockCountTotal, RawToInt),
new(0x05, SmartNames.ReallocatedSectorsCount),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAF, SmartNames.ProgramFailCountChip, RawToInt),
new(0xB0, SmartNames.EraseFailCountChip, RawToInt),
new(0xB1, SmartNames.WearLevelingCount, RawToInt),
new(0xB2, SmartNames.UsedReservedBlockCountChip, RawToInt),
new(0xB3, SmartNames.UsedReservedBlockCountTotal, RawToInt),
// Unused Reserved Block Count (Total)
new(0xB4, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xB5, SmartNames.ProgramFailCountTotal, RawToInt),
new(0xB6, SmartNames.EraseFailCountTotal, RawToInt),
new(0xB7, SmartNames.RuntimeBadBlockTotal, RawToInt),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xBE,
SmartNames.Temperature,
(r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.Temperature,
false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC2, SmartNames.AirflowTemperature),
new(0xC3, SmartNames.EccRate),
new(0xC6, SmartNames.OffLineUncorrectableErrorCount, RawToInt),
new(0xC7, SmartNames.CrcErrorCount, RawToInt),
new(0xC9, SmartNames.SupercapStatus),
new(0xCA, SmartNames.ExceptionModeStatus),
new(0xEB, SmartNames.PowerRecoveryCount),
new(0xF1,
SmartNames.TotalLbasWritten,
(r, _, _) => (((long)r[5] << 40) | ((long)r[4] << 32) | ((long)r[3] << 24) | ((long)r[2] << 16) | ((long)r[1] << 8) | r[0]) * (512.0f / 1024 / 1024 / 1024),
SensorType.Data,
0,
"Total Bytes Written")
};
// Unused Reserved Block Count (Total)
new(0xB4, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xB5, SmartNames.ProgramFailCountTotal, RawToInt),
new(0xB6, SmartNames.EraseFailCountTotal, RawToInt),
new(0xB7, SmartNames.RuntimeBadBlockTotal, RawToInt),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xBE,
SmartNames.Temperature,
(r, _, p) => r[0] + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.Temperature,
false,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC2, SmartNames.AirflowTemperature),
new(0xC3, SmartNames.EccRate),
new(0xC6, SmartNames.OffLineUncorrectableErrorCount, RawToInt),
new(0xC7, SmartNames.CrcErrorCount, RawToInt),
new(0xC9, SmartNames.SupercapStatus),
new(0xCA, SmartNames.ExceptionModeStatus),
new(0xEB, SmartNames.PowerRecoveryCount),
new(0xF1,
SmartNames.TotalLbasWritten,
(r, _, _) => (((long)r[5] << 40) | ((long)r[4] << 32) | ((long)r[3] << 24) | ((long)r[2] << 16) | ((long)r[1] << 8) | r[0]) * (512.0f / 1024 / 1024 / 1024),
SensorType.Data,
0,
"Total Bytes Written")
};
public SsdSamsung(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
public SsdSamsung(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{ }
}
@@ -7,71 +7,70 @@
using System.Collections.Generic;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
[NamePrefix(""), RequireSmart(0xAB), RequireSmart(0xB1)]
internal class SsdSandforce : AtaStorage
{
[NamePrefix(""), RequireSmart(0xAB), RequireSmart(0xB1)]
internal class SsdSandforce : AtaStorage
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
private static readonly IReadOnlyList<SmartAttribute> _smartAttributes = new List<SmartAttribute>
{
new(0x01, SmartNames.RawReadErrorRate),
new(0x05, SmartNames.RetiredBlockCount, RawToInt),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAB, SmartNames.ProgramFailCount, RawToInt),
new(0xAC, SmartNames.EraseFailCount, RawToInt),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB1, SmartNames.WearRangeDelta, RawToInt),
new(0xB5, SmartNames.AlternativeProgramFailCount, RawToInt),
new(0xB6, SmartNames.AlternativeEraseFailCount, RawToInt),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xC2,
SmartNames.Temperature,
(_, value, p) => value + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.Temperature,
true,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC3, SmartNames.UnrecoverableEcc),
new(0xC4, SmartNames.ReallocationEventCount, RawToInt),
new(0xE7, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xE9, SmartNames.ControllerWritesToNand, RawToInt, SensorType.Data, 0, SmartNames.ControllerWritesToNand),
new(0xEA, SmartNames.HostWritesToController, RawToInt, SensorType.Data, 1, SmartNames.HostWritesToController),
new(0xF1, SmartNames.HostWrites, RawToInt, SensorType.Data, 1, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, RawToInt, SensorType.Data, 2, SmartNames.HostReads)
};
new(0x01, SmartNames.RawReadErrorRate),
new(0x05, SmartNames.RetiredBlockCount, RawToInt),
new(0x09, SmartNames.PowerOnHours, RawToInt),
new(0x0C, SmartNames.PowerCycleCount, RawToInt),
new(0xAB, SmartNames.ProgramFailCount, RawToInt),
new(0xAC, SmartNames.EraseFailCount, RawToInt),
new(0xAE, SmartNames.UnexpectedPowerLossCount, RawToInt),
new(0xB1, SmartNames.WearRangeDelta, RawToInt),
new(0xB5, SmartNames.AlternativeProgramFailCount, RawToInt),
new(0xB6, SmartNames.AlternativeEraseFailCount, RawToInt),
new(0xBB, SmartNames.UncorrectableErrorCount, RawToInt),
new(0xC2,
SmartNames.Temperature,
(_, value, p) => value + (p?[0].Value ?? 0),
SensorType.Temperature,
0,
SmartNames.Temperature,
true,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", 0) }),
new(0xC3, SmartNames.UnrecoverableEcc),
new(0xC4, SmartNames.ReallocationEventCount, RawToInt),
new(0xE7, SmartNames.RemainingLife, null, SensorType.Level, 0, SmartNames.RemainingLife),
new(0xE9, SmartNames.ControllerWritesToNand, RawToInt, SensorType.Data, 0, SmartNames.ControllerWritesToNand),
new(0xEA, SmartNames.HostWritesToController, RawToInt, SensorType.Data, 1, SmartNames.HostWritesToController),
new(0xF1, SmartNames.HostWrites, RawToInt, SensorType.Data, 1, SmartNames.HostWrites),
new(0xF2, SmartNames.HostReads, RawToInt, SensorType.Data, 2, SmartNames.HostReads)
};
private readonly Sensor _writeAmplification;
private readonly Sensor _writeAmplification;
public SsdSandforce(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
public SsdSandforce(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
{
_writeAmplification = new Sensor("Write Amplification", 1, SensorType.Factor, this, settings);
}
protected override void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values)
{
float? controllerWritesToNand = null;
float? hostWritesToController = null;
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
_writeAmplification = new Sensor("Write Amplification", 1, SensorType.Factor, this, settings);
if (value.Id == 0xE9)
controllerWritesToNand = RawToInt(value.RawValue, value.CurrentValue, null);
if (value.Id == 0xEA)
hostWritesToController = RawToInt(value.RawValue, value.CurrentValue, null);
}
protected override void UpdateAdditionalSensors(Kernel32.SMART_ATTRIBUTE[] values)
if (controllerWritesToNand.HasValue && hostWritesToController.HasValue)
{
float? controllerWritesToNand = null;
float? hostWritesToController = null;
foreach (Kernel32.SMART_ATTRIBUTE value in values)
{
if (value.Id == 0xE9)
controllerWritesToNand = RawToInt(value.RawValue, value.CurrentValue, null);
if (hostWritesToController.Value > 0)
_writeAmplification.Value = controllerWritesToNand.Value / hostWritesToController.Value;
else
_writeAmplification.Value = 0;
if (value.Id == 0xEA)
hostWritesToController = RawToInt(value.RawValue, value.CurrentValue, null);
}
if (controllerWritesToNand.HasValue && hostWritesToController.HasValue)
{
if (hostWritesToController.Value > 0)
_writeAmplification.Value = controllerWritesToNand.Value / hostWritesToController.Value;
else
_writeAmplification.Value = 0;
ActivateSensor(_writeAmplification);
}
ActivateSensor(_writeAmplification);
}
}
}
@@ -6,25 +6,24 @@
using System.Text;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal sealed class StorageGeneric : AbstractStorage
{
internal sealed class StorageGeneric : AbstractStorage
private StorageGeneric(StorageInfo storageInfo, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, name, firmwareRevision, "hdd", index, settings)
{
private StorageGeneric(StorageInfo storageInfo, string name, string firmwareRevision, int index, ISettings settings)
: base(storageInfo, name, firmwareRevision, "hdd", index, settings)
{
CreateSensors();
}
public static AbstractStorage CreateInstance(StorageInfo info, ISettings settings)
{
string name = string.IsNullOrEmpty(info.Name) ? "Generic Hard Disk" : info.Name;
string firmwareRevision = string.IsNullOrEmpty(info.Revision) ? "Unknown" : info.Revision;
return new StorageGeneric(info, name, firmwareRevision, info.Index, settings);
}
protected override void UpdateSensors() { }
protected override void GetReport(StringBuilder r) { }
CreateSensors();
}
}
public static AbstractStorage CreateInstance(StorageInfo info, ISettings settings)
{
string name = string.IsNullOrEmpty(info.Name) ? "Generic Hard Disk" : info.Name;
string firmwareRevision = string.IsNullOrEmpty(info.Revision) ? "Unknown" : info.Revision;
return new StorageGeneric(info, name, firmwareRevision, info.Index, settings);
}
protected override void UpdateSensors() { }
protected override void GetReport(StringBuilder r) { }
}
@@ -8,48 +8,47 @@ using System;
using System.Collections.Generic;
using System.Management;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class StorageGroup : IGroup
{
internal class StorageGroup : IGroup
private readonly List<AbstractStorage> _hardware = new();
public StorageGroup(ISettings settings)
{
private readonly List<AbstractStorage> _hardware = new();
if (Software.OperatingSystem.IsUnix)
return;
public StorageGroup(ISettings settings)
//https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-diskdrive
using var diskDriveSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive") { Options = { Timeout = TimeSpan.FromSeconds(10) } };
foreach (ManagementBaseObject diskDrive in diskDriveSearcher.Get())
{
if (Software.OperatingSystem.IsUnix)
return;
string deviceId = (string)diskDrive.Properties["DeviceId"].Value; // is \\.\PhysicalDrive0..n
uint idx = Convert.ToUInt32(diskDrive.Properties["Index"].Value);
ulong diskSize = Convert.ToUInt64(diskDrive.Properties["Size"].Value);
int scsi = Convert.ToInt32(diskDrive.Properties["SCSIPort"].Value);
//https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-diskdrive
using var diskDriveSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive") { Options = { Timeout = TimeSpan.FromSeconds(10) } };
foreach (ManagementBaseObject diskDrive in diskDriveSearcher.Get())
if (deviceId != null)
{
string deviceId = (string)diskDrive.Properties["DeviceId"].Value; // is \\.\PhysicalDrive0..n
uint idx = Convert.ToUInt32(diskDrive.Properties["Index"].Value);
ulong diskSize = Convert.ToUInt64(diskDrive.Properties["Size"].Value);
int scsi = Convert.ToInt32(diskDrive.Properties["SCSIPort"].Value);
if (deviceId != null)
var instance = AbstractStorage.CreateInstance(deviceId, idx, diskSize, scsi, settings);
if (instance != null)
{
var instance = AbstractStorage.CreateInstance(deviceId, idx, diskSize, scsi, settings);
if (instance != null)
{
_hardware.Add(instance);
}
_hardware.Add(instance);
}
}
}
public IReadOnlyList<IHardware> Hardware => _hardware;
public string GetReport()
{
return null;
}
public void Close()
{
foreach (AbstractStorage storage in _hardware)
storage.Close();
}
}
}
public IReadOnlyList<IHardware> Hardware => _hardware;
public string GetReport()
{
return null;
}
public void Close()
{
foreach (AbstractStorage storage in _hardware)
storage.Close();
}
}
@@ -6,32 +6,31 @@
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal abstract class StorageInfo
{
internal abstract class StorageInfo
{
public Kernel32.STORAGE_BUS_TYPE BusType { get; protected set; }
public Kernel32.STORAGE_BUS_TYPE BusType { get; protected set; }
public string DeviceId { get; set; }
public string DeviceId { get; set; }
public ulong DiskSize { get; set; }
public ulong DiskSize { get; set; }
public int Index { get; protected set; }
public int Index { get; protected set; }
public string Name => (Vendor + " " + Product).Trim();
public string Name => (Vendor + " " + Product).Trim();
public string Product { get; protected set; }
public string Product { get; protected set; }
public byte[] RawData { get; protected set; }
public byte[] RawData { get; protected set; }
public bool Removable { get; protected set; }
public bool Removable { get; protected set; }
public string Revision { get; protected set; }
public string Revision { get; protected set; }
public string Scsi { get; set; }
public string Scsi { get; set; }
public string Serial { get; protected set; }
public string Serial { get; protected set; }
public string Vendor { get; protected set; }
}
}
public string Vendor { get; protected set; }
}
@@ -10,176 +10,175 @@ using System.IO;
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal class WindowsSmart : ISmart
{
internal class WindowsSmart : ISmart
private readonly int _driveNumber;
private readonly SafeHandle _handle;
public WindowsSmart(int driveNumber)
{
private readonly int _driveNumber;
private readonly SafeHandle _handle;
_driveNumber = driveNumber;
_handle = Kernel32.CreateFile(@"\\.\PhysicalDrive" + driveNumber, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
}
public WindowsSmart(int driveNumber)
public bool IsValid => !_handle.IsInvalid && !_handle.IsClosed;
public void Dispose()
{
Close();
}
public void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public bool EnableSmart()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
_driveNumber = driveNumber;
_handle = Kernel32.CreateFile(@"\\.\PhysicalDrive" + driveNumber, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
}
bDriveNumber = (byte)_driveNumber,
irDriveRegs = { bFeaturesReg = Kernel32.SMART_FEATURES.ENABLE_SMART, bCylLowReg = Kernel32.SMART_LBA_MID, bCylHighReg = Kernel32.SMART_LBA_HI, bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART}
};
public bool IsValid => !_handle.IsInvalid && !_handle.IsClosed;
return Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_SEND_DRIVE_COMMAND, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.SENDCMDOUTPARAMS _, Marshal.SizeOf<Kernel32.SENDCMDOUTPARAMS>(), out _, IntPtr.Zero);
}
public void Dispose()
public Kernel32.SMART_ATTRIBUTE[] ReadSmartData()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
Close();
}
public void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public bool EnableSmart()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber,
irDriveRegs = { bFeaturesReg = Kernel32.SMART_FEATURES.ENABLE_SMART, bCylLowReg = Kernel32.SMART_LBA_MID, bCylHighReg = Kernel32.SMART_LBA_HI, bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART}
};
return Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_SEND_DRIVE_COMMAND, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.SENDCMDOUTPARAMS _, Marshal.SizeOf<Kernel32.SENDCMDOUTPARAMS>(), out _, IntPtr.Zero);
}
public Kernel32.SMART_ATTRIBUTE[] ReadSmartData()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber, irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.SMART_READ_DATA,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.ATTRIBUTECMDOUTPARAMS result, Marshal.SizeOf<Kernel32.ATTRIBUTECMDOUTPARAMS>(), out _, IntPtr.Zero);
return isValid ? result.Attributes : Array.Empty<Kernel32.SMART_ATTRIBUTE>();
}
public Kernel32.SMART_THRESHOLD[] ReadSmartThresholds()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber, irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.READ_THRESHOLDS,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.THRESHOLDCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.THRESHOLDCMDOUTPARAMS>(), out _, IntPtr.Zero);
return isValid ? result.Thresholds : Array.Empty<Kernel32.SMART_THRESHOLD>();
}
public bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision)
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber,
irDriveRegs = { bCommandReg = Kernel32.ATA_COMMAND.ATA_IDENTIFY_DEVICE }
};
bool valid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.IDENTIFYCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.IDENTIFYCMDOUTPARAMS>(), out _, IntPtr.Zero);
if (!valid)
{
name = null;
firmwareRevision = null;
return false;
bDriveNumber = (byte)_driveNumber, irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.SMART_READ_DATA,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
name = GetString(result.Identify.ModelNumber);
firmwareRevision = GetString(result.Identify.FirmwareRevision);
return true;
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.ATTRIBUTECMDOUTPARAMS result, Marshal.SizeOf<Kernel32.ATTRIBUTECMDOUTPARAMS>(), out _, IntPtr.Zero);
return isValid ? result.Attributes : Array.Empty<Kernel32.SMART_ATTRIBUTE>();
}
public Kernel32.SMART_THRESHOLD[] ReadSmartThresholds()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber, irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.READ_THRESHOLDS,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.THRESHOLDCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.THRESHOLDCMDOUTPARAMS>(), out _, IntPtr.Zero);
return isValid ? result.Thresholds : Array.Empty<Kernel32.SMART_THRESHOLD>();
}
public bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision)
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber,
irDriveRegs = { bCommandReg = Kernel32.ATA_COMMAND.ATA_IDENTIFY_DEVICE }
};
bool valid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_RECEIVE_DRIVE_DATA, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.IDENTIFYCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.IDENTIFYCMDOUTPARAMS>(), out _, IntPtr.Zero);
if (!valid)
{
name = null;
firmwareRevision = null;
return false;
}
/// <summary>
/// Reads Smart health status of the drive
/// </summary>
/// <returns>True, if drive is healthy; False, if unhealthy; Null, if it cannot be read</returns>
public bool? ReadSmartHealth()
name = GetString(result.Identify.ModelNumber);
firmwareRevision = GetString(result.Identify.FirmwareRevision);
return true;
}
/// <summary>
/// Reads Smart health status of the drive
/// </summary>
/// <returns>True, if drive is healthy; False, if unhealthy; Null, if it cannot be read</returns>
public bool? ReadSmartHealth()
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
if (_handle.IsClosed)
throw new ObjectDisposedException(nameof(WindowsSmart));
var parameter = new Kernel32.SENDCMDINPARAMS
{
bDriveNumber = (byte)_driveNumber,
irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.RETURN_SMART_STATUS,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_SEND_DRIVE_COMMAND, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.STATUSCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.STATUSCMDOUTPARAMS>(), out _, IntPtr.Zero);
if (!isValid)
{
return null;
bDriveNumber = (byte)_driveNumber,
irDriveRegs = {
bFeaturesReg = Kernel32.SMART_FEATURES.RETURN_SMART_STATUS,
bCylLowReg = Kernel32.SMART_LBA_MID,
bCylHighReg = Kernel32.SMART_LBA_HI,
bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
}
};
// reference: https://github.com/smartmontools/smartmontools/blob/master/smartmontools/atacmds.cpp
if (Kernel32.SMART_LBA_HI == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID == result.irDriveRegs.bCylLowReg)
{
// high and mid registers are unchanged, which means that the drive is healthy
return true;
}
bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_SEND_DRIVE_COMMAND, ref parameter, Marshal.SizeOf(parameter),
out Kernel32.STATUSCMDOUTPARAMS result, Marshal.SizeOf<Kernel32.STATUSCMDOUTPARAMS>(), out _, IntPtr.Zero);
if (Kernel32.SMART_LBA_HI_EXCEEDED == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID_EXCEEDED == result.irDriveRegs.bCylLowReg)
{
// high and mid registers are exceeded, which means that the drive is unhealthy
return false;
}
// response is not clear
if (!isValid)
{
return null;
}
protected void Dispose(bool disposing)
// reference: https://github.com/smartmontools/smartmontools/blob/master/smartmontools/atacmds.cpp
if (Kernel32.SMART_LBA_HI == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID == result.irDriveRegs.bCylLowReg)
{
if (disposing && !_handle.IsClosed)
{
_handle.Close();
}
// high and mid registers are unchanged, which means that the drive is healthy
return true;
}
private static string GetString(IReadOnlyList<byte> bytes)
if (Kernel32.SMART_LBA_HI_EXCEEDED == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID_EXCEEDED == result.irDriveRegs.bCylLowReg)
{
char[] chars = new char[bytes.Count];
for (int i = 0; i < bytes.Count; i += 2)
{
chars[i] = (char)bytes[i + 1];
chars[i + 1] = (char)bytes[i];
}
return new string(chars).Trim(' ', '\0');
// high and mid registers are exceeded, which means that the drive is unhealthy
return false;
}
// response is not clear
return null;
}
protected void Dispose(bool disposing)
{
if (disposing && !_handle.IsClosed)
{
_handle.Close();
}
}
private static string GetString(IReadOnlyList<byte> bytes)
{
char[] chars = new char[bytes.Count];
for (int i = 0; i < bytes.Count; i += 2)
{
chars[i] = (char)bytes[i + 1];
chars[i + 1] = (char)bytes[i];
}
return new string(chars).Trim(' ', '\0');
}
}
@@ -9,90 +9,89 @@ using System.Management;
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
namespace LibreHardwareMonitor.Hardware.Storage
namespace LibreHardwareMonitor.Hardware.Storage;
internal static class WindowsStorage
{
internal static class WindowsStorage
public static Storage.StorageInfo GetStorageInfo(string deviceId, uint driveIndex)
{
public static Storage.StorageInfo GetStorageInfo(string deviceId, uint driveIndex)
using SafeHandle handle = Kernel32.OpenDevice(deviceId);
if (handle?.IsInvalid != false)
return null;
var query = new Kernel32.STORAGE_PROPERTY_QUERY { PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageDeviceProperty, QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery };
if (!Kernel32.DeviceIoControl(handle,
Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY,
ref query,
Marshal.SizeOf(query),
out Kernel32.STORAGE_DEVICE_DESCRIPTOR_HEADER header,
Marshal.SizeOf<Kernel32.STORAGE_DEVICE_DESCRIPTOR_HEADER>(),
out _,
IntPtr.Zero))
{
using SafeHandle handle = Kernel32.OpenDevice(deviceId);
if (handle?.IsInvalid != false)
return null;
var query = new Kernel32.STORAGE_PROPERTY_QUERY { PropertyId = Kernel32.STORAGE_PROPERTY_ID.StorageDeviceProperty, QueryType = Kernel32.STORAGE_QUERY_TYPE.PropertyStandardQuery };
if (!Kernel32.DeviceIoControl(handle,
Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY,
ref query,
Marshal.SizeOf(query),
out Kernel32.STORAGE_DEVICE_DESCRIPTOR_HEADER header,
Marshal.SizeOf<Kernel32.STORAGE_DEVICE_DESCRIPTOR_HEADER>(),
out _,
IntPtr.Zero))
{
return null;
}
IntPtr descriptorPtr = Marshal.AllocHGlobal((int)header.Size);
try
{
if (!Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, ref query, Marshal.SizeOf(query), descriptorPtr, header.Size, out _, IntPtr.Zero))
return null;
return new StorageInfo((int)driveIndex, descriptorPtr);
}
finally
{
Marshal.FreeHGlobal(descriptorPtr);
}
return null;
}
public static string[] GetLogicalDrives(int driveIndex)
IntPtr descriptorPtr = Marshal.AllocHGlobal((int)header.Size);
try
{
var list = new List<string>();
if (!Kernel32.DeviceIoControl(handle, Kernel32.IOCTL.IOCTL_STORAGE_QUERY_PROPERTY, ref query, Marshal.SizeOf(query), descriptorPtr, header.Size, out _, IntPtr.Zero))
return null;
try
return new StorageInfo((int)driveIndex, descriptorPtr);
}
finally
{
Marshal.FreeHGlobal(descriptorPtr);
}
}
public static string[] GetLogicalDrives(int driveIndex)
{
var list = new List<string>();
try
{
using var s = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskPartition " + "WHERE DiskIndex = " + driveIndex);
foreach (ManagementBaseObject o in s.Get())
{
using var s = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskPartition " + "WHERE DiskIndex = " + driveIndex);
foreach (ManagementBaseObject o in s.Get())
if (o is ManagementObject dp)
{
if (o is ManagementObject dp)
foreach (ManagementBaseObject ld in dp.GetRelated("Win32_LogicalDisk"))
{
foreach (ManagementBaseObject ld in dp.GetRelated("Win32_LogicalDisk"))
{
list.Add(((string)ld["Name"]).TrimEnd(':'));
}
list.Add(((string)ld["Name"]).TrimEnd(':'));
}
}
}
catch
{
// Ignored.
}
return list.ToArray();
}
catch
{
// Ignored.
}
private class StorageInfo : Storage.StorageInfo
{
public StorageInfo(int index, IntPtr descriptorPtr)
{
Kernel32.STORAGE_DEVICE_DESCRIPTOR descriptor = Marshal.PtrToStructure<Kernel32.STORAGE_DEVICE_DESCRIPTOR>(descriptorPtr);
Index = index;
Vendor = GetString(descriptorPtr, descriptor.VendorIdOffset);
Product = GetString(descriptorPtr, descriptor.ProductIdOffset);
Revision = GetString(descriptorPtr, descriptor.ProductRevisionOffset);
Serial = GetString(descriptorPtr, descriptor.SerialNumberOffset);
BusType = descriptor.BusType;
Removable = descriptor.RemovableMedia;
RawData = new byte[descriptor.Size];
Marshal.Copy(descriptorPtr, RawData, 0, RawData.Length);
}
return list.ToArray();
}
private static string GetString(IntPtr descriptorPtr, uint offset)
{
return offset > 0 ? Marshal.PtrToStringAnsi(new IntPtr(descriptorPtr.ToInt64() + offset))?.Trim() : string.Empty;
}
private class StorageInfo : Storage.StorageInfo
{
public StorageInfo(int index, IntPtr descriptorPtr)
{
Kernel32.STORAGE_DEVICE_DESCRIPTOR descriptor = Marshal.PtrToStructure<Kernel32.STORAGE_DEVICE_DESCRIPTOR>(descriptorPtr);
Index = index;
Vendor = GetString(descriptorPtr, descriptor.VendorIdOffset);
Product = GetString(descriptorPtr, descriptor.ProductIdOffset);
Revision = GetString(descriptorPtr, descriptor.ProductRevisionOffset);
Serial = GetString(descriptorPtr, descriptor.SerialNumberOffset);
BusType = descriptor.BusType;
Removable = descriptor.RemovableMedia;
RawData = new byte[descriptor.Size];
Marshal.Copy(descriptorPtr, RawData, 0, RawData.Length);
}
private static string GetString(IntPtr descriptorPtr, uint offset)
{
return offset > 0 ? Marshal.PtrToStringAnsi(new IntPtr(descriptorPtr.ToInt64() + offset))?.Trim() : string.Empty;
}
}
}