Replace Storage implementation with CrystalDiskInfo based implementation (#2015)
* Replace Storage implementation with CrystalDiskInfo based implementation. * Formatting. * Code review. * Code review. * Increase space for attribute name in report. * Implement dynamic disk handling for usage sensor. Disable unused warnings. * Further cleanup in NativeMethods.txt and referenced.
This commit is contained in:
@@ -436,11 +436,11 @@ public sealed partial class MainForm : Form
|
||||
switch (_throttleAtaUpdate.Value)
|
||||
{
|
||||
case true:
|
||||
AtaStorage.ThrottleInterval = TimeSpan.FromSeconds(30);
|
||||
StorageDevice.ThrottleInterval = TimeSpan.FromSeconds(30);
|
||||
break;
|
||||
|
||||
case false:
|
||||
AtaStorage.ThrottleInterval = TimeSpan.Zero;
|
||||
StorageDevice.ThrottleInterval = TimeSpan.Zero;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,8 +50,10 @@ internal class MemoryGroup : IGroup, IHardwareChanged
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 67
|
||||
public event HardwareEventHandler HardwareAdded;
|
||||
public event HardwareEventHandler HardwareRemoved;
|
||||
#pragma warning restore 67
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
|
||||
@@ -94,11 +94,13 @@ public class Motherboard : IHardware
|
||||
SubHardware = subHardwareList.ToArray();
|
||||
}
|
||||
|
||||
#pragma warning disable 67
|
||||
/// <inheritdoc />
|
||||
public event SensorEventHandler SensorAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event SensorEventHandler SensorRemoved;
|
||||
#pragma warning restore 67
|
||||
|
||||
/// <inheritdoc />
|
||||
public HardwareType HardwareType => HardwareType.Motherboard;
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
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;
|
||||
|
||||
private static TimeSpan _throttleInterval = TimeSpan.Zero;
|
||||
private DateTime _lastUpdate = DateTime.MinValue;
|
||||
|
||||
/// <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();
|
||||
|
||||
SmartAttributes = smartAttributes;
|
||||
CreateSensors();
|
||||
}
|
||||
|
||||
internal static AbstractStorage CreateInstance(StorageInfo storageInfo, ISettings settings)
|
||||
{
|
||||
ISmart smart = new WindowsSmart(storageInfo.Index);
|
||||
string name = null;
|
||||
string firmwareRevision = null;
|
||||
AtaSmart.SMART_ATTRIBUTE[] smartAttributes = [];
|
||||
|
||||
if (smart.IsValid)
|
||||
{
|
||||
bool nameValid = smart.ReadNameAndFirmwareRevision(out name, out firmwareRevision);
|
||||
bool smartEnabled = smart.EnableSmart();
|
||||
|
||||
if (smartEnabled)
|
||||
smartAttributes = smart.ReadSmartData();
|
||||
|
||||
if (!nameValid)
|
||||
{
|
||||
name = null;
|
||||
firmwareRevision = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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 (AtaSmart.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, [storageInfo, smart, name, firmwareRevision, storageInfo.Index, settings], null) as AtaStorage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no matching type has been found
|
||||
smart.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
protected sealed override void CreateSensors()
|
||||
{
|
||||
_sensors = new Dictionary<SmartAttribute, Sensor>();
|
||||
|
||||
if (Smart.IsValid)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
public static TimeSpan ThrottleInterval
|
||||
{
|
||||
get
|
||||
{
|
||||
return _throttleInterval;
|
||||
}
|
||||
set
|
||||
{
|
||||
_throttleInterval = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateAdditionalSensors(AtaSmart.SMART_ATTRIBUTE[] values) { }
|
||||
|
||||
protected override void UpdateSensors()
|
||||
{
|
||||
if (DateTime.UtcNow - _lastUpdate < ThrottleInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_lastUpdate = DateTime.UtcNow;
|
||||
|
||||
if (Smart.IsValid)
|
||||
{
|
||||
AtaSmart.SMART_ATTRIBUTE[] smartAttributes = Smart.ReadSmartData();
|
||||
|
||||
foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in _sensors)
|
||||
{
|
||||
SmartAttribute attribute = keyValuePair.Key;
|
||||
foreach (AtaSmart.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 unsafe void GetReport(StringBuilder r)
|
||||
{
|
||||
if (Smart.IsValid)
|
||||
{
|
||||
AtaSmart.SMART_ATTRIBUTE[] values = Smart.ReadSmartData();
|
||||
AtaSmart.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 (AtaSmart.SMART_ATTRIBUTE value in values)
|
||||
{
|
||||
if (value.Id == 0x00)
|
||||
break;
|
||||
|
||||
byte? threshold = null;
|
||||
foreach (AtaSmart.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;
|
||||
}
|
||||
}
|
||||
|
||||
Span<byte> rawValue = new(value.RawValue, 6);
|
||||
string raw = BitConverter.ToString(rawValue.ToArray());
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public abstract class AbstractStorage : Hardware
|
||||
{
|
||||
private readonly PerformanceValue _perfRead = new();
|
||||
private readonly PerformanceValue _perfTotal = new();
|
||||
private readonly PerformanceValue _perfWrite = new();
|
||||
private readonly StorageInfo _storageInfo;
|
||||
|
||||
private long _lastReadCount;
|
||||
private long _lastTime;
|
||||
private long _lastWriteCount;
|
||||
private Sensor _sensorDiskReadActivity;
|
||||
private Sensor _sensorDiskReadRate;
|
||||
private Sensor _sensorDiskTotalActivity;
|
||||
private Sensor _sensorDiskWriteActivity;
|
||||
private Sensor _sensorDiskWriteRate;
|
||||
private Sensor _usageSensor;
|
||||
|
||||
internal AbstractStorage(StorageInfo storageInfo, string name, string firmwareRevision, string id, int index, ISettings settings)
|
||||
: base(name, new Identifier(id, index.ToString(CultureInfo.InvariantCulture)), settings)
|
||||
{
|
||||
_storageInfo = storageInfo;
|
||||
FirmwareRevision = firmwareRevision;
|
||||
Index = index;
|
||||
|
||||
string[] logicalDrives = WindowsStorage.GetLogicalDrives(index);
|
||||
var driveInfoList = new List<DriveInfo>(logicalDrives.Length);
|
||||
|
||||
foreach (string logicalDrive in logicalDrives)
|
||||
{
|
||||
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; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close()
|
||||
{
|
||||
_storageInfo.Handle?.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public static AbstractStorage CreateInstance(string deviceId, uint driveNumber, ulong diskSize, int scsiPort, ISettings settings)
|
||||
{
|
||||
StorageInfo info = WindowsStorage.GetStorageInfo(deviceId, driveNumber);
|
||||
if (info == null || info.Removable || info.BusType is STORAGE_BUS_TYPE.BusTypeVirtual or STORAGE_BUS_TYPE.BusTypeFileBackedVirtual)
|
||||
return null;
|
||||
|
||||
info.DiskSize = diskSize;
|
||||
info.DeviceId = deviceId;
|
||||
info.Handle = PInvoke.CreateFile(deviceId,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
info.Scsi = $@"\\.\SCSI{scsiPort}:";
|
||||
|
||||
//fallback, when it is not possible to read out with the nvme implementation,
|
||||
//try it with the sata smart implementation
|
||||
if (info.BusType == STORAGE_BUS_TYPE.BusTypeNvme)
|
||||
{
|
||||
AbstractStorage x = NVMeGeneric.CreateInstance(info, settings);
|
||||
if (x != null)
|
||||
return x;
|
||||
}
|
||||
|
||||
return info.BusType is STORAGE_BUS_TYPE.BusTypeAta or STORAGE_BUS_TYPE.BusTypeSata or 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);
|
||||
}
|
||||
|
||||
_sensorDiskReadActivity = new Sensor("Read Activity", 31, SensorType.Load, this, _settings);
|
||||
ActivateSensor(_sensorDiskReadActivity);
|
||||
|
||||
_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();
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// Update statistics.
|
||||
if (_storageInfo != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdatePerformanceSensors();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void UpdatePerformanceSensors()
|
||||
{
|
||||
DISK_PERFORMANCE diskPerformance = new();
|
||||
|
||||
uint bytesReturned;
|
||||
if (!PInvoke.DeviceIoControl((HANDLE)_storageInfo.Handle.DangerousGetHandle(), PInvoke.IOCTL_DISK_PERFORMANCE, null, 0, &diskPerformance, (uint)sizeof(DISK_PERFORMANCE), &bytesReturned, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_perfRead.Update(diskPerformance.ReadTime, diskPerformance.QueryTime);
|
||||
_sensorDiskReadActivity.Value = (float)_perfRead.Result;
|
||||
|
||||
_perfWrite.Update(diskPerformance.WriteTime, diskPerformance.QueryTime);
|
||||
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
|
||||
|
||||
_perfTotal.Update(diskPerformance.IdleTime, diskPerformance.QueryTime);
|
||||
_sensorDiskTotalActivity.Value = (float)(100 - _perfTotal.Result);
|
||||
|
||||
long readCount = diskPerformance.BytesRead;
|
||||
long readDiff = readCount - _lastReadCount;
|
||||
_lastReadCount = readCount;
|
||||
|
||||
long writeCount = diskPerformance.BytesWritten;
|
||||
long writeDiff = writeCount - _lastWriteCount;
|
||||
_lastWriteCount = writeCount;
|
||||
|
||||
long currentTime = Stopwatch.GetTimestamp();
|
||||
if (_lastTime != 0)
|
||||
{
|
||||
double timeDeltaSeconds = TimeSpan.FromTicks(currentTime - _lastTime).TotalSeconds;
|
||||
|
||||
double writeSpeed = writeDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskWriteRate.Value = (float)writeSpeed;
|
||||
|
||||
double readSpeed = readDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskReadRate.Value = (float)readSpeed;
|
||||
}
|
||||
|
||||
_lastTime = currentTime;
|
||||
}
|
||||
|
||||
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 long Time { get; set; }
|
||||
|
||||
private long Value { get; set; }
|
||||
|
||||
public void Update(long val, long valBase)
|
||||
{
|
||||
long diffValue = val - Value;
|
||||
long 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
#if DEBUG
|
||||
|
||||
using System;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage
|
||||
{
|
||||
internal class DebugSmart : ISmart
|
||||
{
|
||||
private readonly Drive[] _drives =
|
||||
[
|
||||
new("KINGSTON SNV425S264GB", null, 16,
|
||||
@" 01 000000000000 100 100
|
||||
02 000000000000 100 100
|
||||
03 000000000000 100 100
|
||||
05 000000000000 100 100
|
||||
07 000000000000 100 100
|
||||
08 000000000000 100 100
|
||||
09 821E00000000 100 100
|
||||
0A 000000000000 100 100
|
||||
0C 950200000000 100 100
|
||||
A8 000000000000 100 100
|
||||
AF 000000000000 100 100
|
||||
C0 000000000000 100 100
|
||||
C2 290014002B00 100 41
|
||||
C5 000000000000 100 100
|
||||
F0 000000000000 100 100
|
||||
AA 07007B000000 100 100
|
||||
AD 0E1E71304919 100 100"),
|
||||
new("PLEXTOR PX-128M2S", "1.03", 16,
|
||||
@" 01 000000000000 100 100 0
|
||||
03 000000000000 100 100 0
|
||||
04 000000000000 100 100 0
|
||||
05 000000000000 100 100 0
|
||||
09 250100000000 100 100 0
|
||||
0A 000000000000 100 100 0
|
||||
0C D10000000000 100 100 0
|
||||
B2 000000000000 100 100 0
|
||||
BB 000000000000 100 100 0
|
||||
BE 000000000000 100 100 0
|
||||
C0 000000000000 100 100 0
|
||||
C1 000000000000 100 100 0
|
||||
C2 000000000000 100 100 0
|
||||
C3 000000000000 100 100 0
|
||||
C5 000000000000 100 100 0
|
||||
C6 000000000000 100 100 0
|
||||
C7 000000000000 100 100 0"),
|
||||
new("OCZ-VERTEX2", "1.25", 16,
|
||||
@" 01 DADAD5000000 100 106 50
|
||||
05 000000000000 100 100 3
|
||||
09 DF0900004A2F 100 100 0
|
||||
0C FC0100000000 100 100 0
|
||||
AB 000000000000 0 0 0
|
||||
AC 000000000000 0 0 0
|
||||
AE 1F0000000000 0 0 0
|
||||
B1 000000000000 0 0 0
|
||||
B5 000000000000 0 0 0
|
||||
B6 000000000000 0 0 0
|
||||
BB 000000000000 100 100 0
|
||||
C2 010081007F00 129 1 0
|
||||
C3 DADAD5000000 100 106 0
|
||||
C4 000000000000 100 100 0
|
||||
E7 000000000000 100 100 10
|
||||
E9 800400000000 0 0 0
|
||||
EA 000600000000 0 0 0
|
||||
F1 000600000000 0 0 0
|
||||
F2 801200000000 0 0 0"),
|
||||
new("WDC WD5000AADS-00S9B0", null, 10,
|
||||
@" 1 000000000000 200 200
|
||||
3 820D00000000 149 150
|
||||
4 610800000000 98 98
|
||||
5 000000000000 200 200
|
||||
7 000000000000 253 100
|
||||
9 0F1F00000000 90 90
|
||||
10 000000000000 100 100
|
||||
11 000000000000 100 100
|
||||
12 880200000000 100 100
|
||||
192 6B0000000000 200 200
|
||||
193 E9CB03000000 118 118
|
||||
194 280000000000 94 103
|
||||
196 000000000000 200 200
|
||||
197 000000000000 200 200
|
||||
198 000000000000 200 200
|
||||
199 000000000000 200 200
|
||||
200 000000000000 200 200
|
||||
130 7B0300010002 1 41
|
||||
5 000000000000 0 0
|
||||
1 000000000000 0 0"),
|
||||
new("INTEL SSDSA2M080G2GC", null, 10,
|
||||
@" 3 000000000000 100 100
|
||||
4 000000000000 100 100
|
||||
5 010000000000 100 100
|
||||
9 B10B00000000 100 100
|
||||
12 DD0300000000 100 100
|
||||
192 480000000000 100 100
|
||||
225 89DB00000000 200 200
|
||||
226 3D1B00000000 100 100
|
||||
227 030000000000 100 100
|
||||
228 7F85703C0000 100 100
|
||||
232 000000000000 99 99
|
||||
233 000000000000 98 98
|
||||
184 000000000000 100 100
|
||||
1 000000000000 0 0"),
|
||||
new("OCZ-VERTEX", null, 10,
|
||||
@" 1 000000000000 0 8
|
||||
9 000000000000 30 99
|
||||
12 000000000000 0 15
|
||||
184 000000000000 0 7
|
||||
195 000000000000 0 0
|
||||
196 000000000000 0 2
|
||||
197 000000000000 0 0
|
||||
198 B9ED00000000 214 176
|
||||
199 352701000000 143 185
|
||||
200 B10500000000 105 55
|
||||
201 F40A00000000 238 194
|
||||
202 020000000000 137 35
|
||||
203 020000000000 125 63
|
||||
204 000000000000 0 0
|
||||
205 000000000000 19 136
|
||||
206 000000000000 22 54
|
||||
207 010000000000 113 226
|
||||
208 000000000000 49 232
|
||||
209 000000000000 0 98
|
||||
211 000000000000 0 0
|
||||
212 000000000000 0 0
|
||||
213 000000000000 0 0"),
|
||||
new("INTEL SSDSA2CW120G3", null, 16,
|
||||
@"03 000000000000 100 100 0
|
||||
04 000000000000 100 100 0
|
||||
05 000000000000 100 100 0
|
||||
09 830200000000 100 100 0
|
||||
0C 900100000000 100 100 0
|
||||
AA 000000000000 100 100 0
|
||||
AB 000000000000 100 100 0
|
||||
AC 000000000000 100 100 0
|
||||
B8 000000000000 100 100 0
|
||||
BB 000000000000 100 100 0
|
||||
C0 040000000000 100 100 0
|
||||
E1 FF4300000000 100 100 0
|
||||
E2 E57D14000000 100 100 0
|
||||
E3 000000000000 100 100 0
|
||||
E4 E39600000000 100 100 0
|
||||
E8 000000000000 100 100 0
|
||||
E9 000000000000 100 100 0
|
||||
F1 FF4300000000 100 100 0
|
||||
F2 264F00000000 100 100 0"),
|
||||
new("CORSAIR CMFSSD-128GBG2D", "VBM19C1Q", 16,
|
||||
@"09 100900000000 99 99 0
|
||||
0C 560200000000 99 99 0
|
||||
AF 000000000000 100 100 10
|
||||
B0 000000000000 100 100 10
|
||||
B1 2A0000000000 99 99 17
|
||||
B2 180000000000 60 60 10
|
||||
B3 4B0000000000 98 98 10
|
||||
B4 B50E00000000 98 98 10
|
||||
B5 000000000000 100 100 10
|
||||
B6 000000000000 100 100 10
|
||||
B7 000000000000 100 100 10
|
||||
BB 000000000000 100 100 0
|
||||
C3 000000000000 200 200 0
|
||||
C6 000000000000 100 100 0
|
||||
C7 810100000000 253 253 0
|
||||
E8 240000000000 60 60 10
|
||||
E9 630594120000 92 92 0"),
|
||||
new("Maxtor 6L300R0", null, 10,
|
||||
@"3 9E5500000000 183 193
|
||||
4 0A0D00000000 252 252
|
||||
5 010000000000 253 253
|
||||
6 000000000000 253 253
|
||||
7 000000000000 252 253
|
||||
8 DFA700000000 224 245
|
||||
9 CE5700000000 155 155
|
||||
10 000000000000 252 253
|
||||
11 000000000000 252 253
|
||||
12 BA0400000000 250 250
|
||||
192 000000000000 253 253
|
||||
193 000000000000 253 253
|
||||
194 3D0000000000 253 42
|
||||
195 5D1F00000000 252 253
|
||||
196 000000000000 253 253
|
||||
197 010000000000 253 253
|
||||
198 000000000000 253 253
|
||||
199 030000000000 196 199
|
||||
200 000000000000 252 253
|
||||
201 000000000000 252 253
|
||||
202 000000000000 252 253
|
||||
203 000000000000 252 253
|
||||
204 000000000000 252 253
|
||||
205 000000000000 252 253
|
||||
207 000000000000 252 253
|
||||
208 000000000000 252 253
|
||||
209 EA0000000000 234 234
|
||||
210 000000000000 252 253
|
||||
211 000000000000 252 253
|
||||
212 000000000000 252 253
|
||||
130 5B0300010002 1 9
|
||||
59 FC3203030100 205 0
|
||||
1 000000000000 0 0
|
||||
144 000000000000 0 34 "),
|
||||
new("M4-CT256M4SSD2", "0309", 16,
|
||||
@"01 000000000000 100 100 50
|
||||
05 000000000000 100 100 10
|
||||
09 AB0100000000 100 100 1
|
||||
0C 6E0000000000 100 100 1
|
||||
AA 000000000000 100 100 10
|
||||
AB 000000000000 100 100 1
|
||||
AC 000000000000 100 100 1
|
||||
AD 060000000000 100 100 10
|
||||
AE 000000000000 100 100 1
|
||||
B5 79003D00B700 100 100 1
|
||||
B7 000000000000 100 100 1
|
||||
B8 000000000000 100 100 50
|
||||
BB 000000000000 100 100 1
|
||||
BC 000000000000 100 100 1
|
||||
BD 5B0000000000 100 100 1
|
||||
C2 000000000000 100 100 0
|
||||
C3 000000000000 100 100 1
|
||||
C4 000000000000 100 100 1
|
||||
C5 000000000000 100 100 1
|
||||
C6 000000000000 100 100 1
|
||||
C7 000000000000 100 100 1
|
||||
CA 000000000000 100 100 1
|
||||
CE 000000000000 100 100 1 "),
|
||||
new("C300-CTFDDAC256MAG", "0007", 16,
|
||||
@"01 000000000000 100 100 0
|
||||
05 000000000000 100 100 0
|
||||
09 4C0A00000000 100 100 0
|
||||
0C 0F0100000000 100 100 0
|
||||
AA 000000000000 100 100 0
|
||||
AB 000000000000 100 100 0
|
||||
AC 000000000000 100 100 0
|
||||
AD 1B0000000000 100 100 0
|
||||
AE 000000000000 100 100 0
|
||||
B5 D30357012B05 100 100 0
|
||||
B7 000000000000 100 100 0
|
||||
B8 000000000000 100 100 0
|
||||
BB 000000000000 100 100 0
|
||||
BC 000000000000 100 100 0
|
||||
BD C60100000000 100 100 0
|
||||
C3 000000000000 100 100 0
|
||||
C4 000000000000 100 100 0
|
||||
C5 000000000000 100 100 0
|
||||
C6 000000000000 100 100 0
|
||||
C7 000000000000 100 100 0
|
||||
CA 000000000000 100 100 0
|
||||
CE 000000000000 100 100 0"),
|
||||
new("M4-CT064M4SSD2", "0009", 16,
|
||||
@"01 000000000000 100 100 50
|
||||
05 000000000000 100 100 10
|
||||
09 260000000000 100 100 1
|
||||
0C 5A0000000000 100 100 1
|
||||
AA 000000000000 100 100 10
|
||||
AB 000000000000 100 100 1
|
||||
AC 000000000000 100 100 1
|
||||
AD 010000000000 100 100 10
|
||||
AE 000000000000 100 100 1
|
||||
B5 2B000E003A00 100 100 1
|
||||
B7 000000000000 100 100 1
|
||||
B8 000000000000 100 100 50
|
||||
BB 000000000000 100 100 1
|
||||
BC 000000000000 100 100 1
|
||||
BD 310000000000 100 100 1
|
||||
C2 000000000000 100 100 0
|
||||
C3 000000000000 100 100 1
|
||||
C4 000000000000 100 100 1
|
||||
C5 000000000000 100 100 1
|
||||
C6 000000000000 100 100 1
|
||||
C7 000000000000 100 100 1
|
||||
CA 000000000000 100 100 1
|
||||
CE 000000000000 100 100 1"),
|
||||
new("M4-CT128M4SSD2", "000F", 16,
|
||||
@"01 000000000000 100 100 50
|
||||
05 000000000000 100 100 10
|
||||
09 CA1400000000 100 100 1
|
||||
0C A30200000000 100 100 1
|
||||
AA 000000000000 100 100 10
|
||||
AB 000000000000 100 100 1
|
||||
AC 000000000000 100 100 1
|
||||
AD 1F0000000000 99 99 10
|
||||
AE 140000000000 100 100 1
|
||||
B5 12037C028E05 100 100 1
|
||||
B7 000000000000 100 100 1
|
||||
B8 000000000000 100 100 50
|
||||
BB 000000000000 100 100 1
|
||||
BC 000000000000 100 100 1
|
||||
BD 510000000000 100 100 1
|
||||
C2 000000000000 100 100 0
|
||||
C3 000000000000 100 100 1
|
||||
C4 000000000000 100 100 1
|
||||
C5 000000000000 100 100 1
|
||||
C6 000000000000 100 100 1
|
||||
C7 000000000000 100 100 1
|
||||
CA 010000000000 99 99 1
|
||||
CE 000000000000 100 100 1 "),
|
||||
new("Samsung SSD 840 PRO Series", "DXM05B0Q", 16,
|
||||
@"05 000000000000 100 100 10
|
||||
09 541200000000 99 99 0
|
||||
0C 820500000000 98 98 0
|
||||
B1 B90200000000 80 80 0
|
||||
B3 000000000000 100 100 10
|
||||
B5 000000000000 100 100 10
|
||||
B6 000000000000 100 100 10
|
||||
B7 000000000000 100 100 10
|
||||
BB 000000000000 100 100 0
|
||||
BE 1C0000000000 48 72 0
|
||||
C3 000000000000 200 200 0
|
||||
C7 020000000000 99 99 0
|
||||
EB 690000000000 99 99 0
|
||||
F1 A56AA1F60200 99 99 0")
|
||||
];
|
||||
|
||||
private int _driveNumber;
|
||||
|
||||
public DebugSmart(int driveNumber)
|
||||
{
|
||||
_driveNumber = driveNumber;
|
||||
}
|
||||
|
||||
public bool IsValid => true;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public bool EnableSmart()
|
||||
{
|
||||
if (_driveNumber < 0)
|
||||
throw new ObjectDisposedException(nameof(DebugSmart));
|
||||
return true;
|
||||
}
|
||||
|
||||
public AtaSmart.SMART_ATTRIBUTE[] ReadSmartData()
|
||||
{
|
||||
if (_driveNumber < 0)
|
||||
throw new ObjectDisposedException(nameof(DebugSmart));
|
||||
return _drives[_driveNumber].DriveAttributeValues;
|
||||
}
|
||||
|
||||
public AtaSmart.SMART_THRESHOLD[] ReadSmartThresholds()
|
||||
{
|
||||
if (_driveNumber < 0)
|
||||
throw new ObjectDisposedException(nameof(DebugSmart));
|
||||
return _drives[_driveNumber].DriveThresholdValues;
|
||||
}
|
||||
|
||||
public bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision)
|
||||
{
|
||||
if (_driveNumber < 0)
|
||||
throw new ObjectDisposedException(nameof(DebugSmart));
|
||||
name = _drives[_driveNumber].Name;
|
||||
firmwareRevision = _drives[_driveNumber].FirmwareVersion;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_driveNumber = -1;
|
||||
}
|
||||
|
||||
private class Drive
|
||||
{
|
||||
public unsafe Drive(string name, string firmware, int idBase, string value)
|
||||
{
|
||||
Name = name;
|
||||
FirmwareVersion = firmware;
|
||||
|
||||
string[] lines = value.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
|
||||
DriveAttributeValues = new AtaSmart.SMART_ATTRIBUTE[lines.Length];
|
||||
var thresholds = new System.Collections.Generic.List<AtaSmart.SMART_THRESHOLD>();
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
string[] array = lines[i].Split([' '], StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (array.Length is not 4 and not 5)
|
||||
throw new Exception();
|
||||
|
||||
var v = new AtaSmart.SMART_ATTRIBUTE { Id = Convert.ToByte(array[0], idBase) };
|
||||
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
v.RawValue[j] = Convert.ToByte(array[1].Substring(2 * j, 2), 16);
|
||||
}
|
||||
|
||||
v.WorstValue = Convert.ToByte(array[2], 10);
|
||||
v.CurrentValue = Convert.ToByte(array[3], 10);
|
||||
|
||||
DriveAttributeValues[i] = v;
|
||||
|
||||
if (array.Length == 5)
|
||||
{
|
||||
var t = new AtaSmart.SMART_THRESHOLD { Id = v.Id, Threshold = Convert.ToByte(array[4], 10) };
|
||||
thresholds.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
DriveThresholdValues = thresholds.ToArray();
|
||||
}
|
||||
|
||||
public AtaSmart.SMART_ATTRIBUTE[] DriveAttributeValues { get; }
|
||||
|
||||
public AtaSmart.SMART_THRESHOLD[] DriveThresholdValues { get; }
|
||||
|
||||
public string FirmwareVersion { get; }
|
||||
|
||||
public string Name { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,88 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.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) })
|
||||
};
|
||||
|
||||
internal GenericHardDisk(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
|
||||
: base(storageInfo, smart, name, firmwareRevision, "hdd", index, _smartAttributes, settings) { }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal interface INVMeDrive
|
||||
{
|
||||
SafeHandle Identify(StorageInfo storageInfo);
|
||||
|
||||
bool IdentifyController(SafeHandle hDevice, out NVME_IDENTIFY_CONTROLLER_DATA data);
|
||||
|
||||
bool HealthInfoLog(SafeHandle hDevice, out NVME_HEALTH_INFO_LOG data);
|
||||
}
|
||||
@@ -4,22 +4,14 @@
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public interface ISmart : IDisposable
|
||||
public interface ISmart
|
||||
{
|
||||
bool IsValid { get; }
|
||||
|
||||
void Close();
|
||||
|
||||
bool EnableSmart();
|
||||
|
||||
AtaSmart.SMART_ATTRIBUTE[] ReadSmartData();
|
||||
|
||||
AtaSmart.SMART_THRESHOLD[] ReadSmartThresholds();
|
||||
|
||||
bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision);
|
||||
/// <summary>
|
||||
/// Gets all available smart attributes.
|
||||
/// </summary>
|
||||
IReadOnlyList<SmartAttribute> Attributes { get; }
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
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)
|
||||
{
|
||||
Smart = new NVMeSmart(storageInfo);
|
||||
_info = info;
|
||||
CreateSensors();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var sensor = new NVMeSensor(name, index, defaultHidden, sensorType, this, _settings, getValue)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public abstract class NVMeHealthInfo
|
||||
{
|
||||
public byte AvailableSpare { get; protected set; }
|
||||
|
||||
public byte AvailableSpareThreshold { get; protected set; }
|
||||
|
||||
public ulong ControllerBusyTime { get; protected set; }
|
||||
|
||||
public uint CriticalCompositeTemperatureTime { get; protected set; }
|
||||
|
||||
public ulong DataUnitRead { get; protected set; }
|
||||
|
||||
public ulong DataUnitWritten { get; protected set; }
|
||||
|
||||
public ulong ErrorInfoLogEntryCount { get; protected set; }
|
||||
|
||||
public ulong HostReadCommands { get; protected set; }
|
||||
|
||||
public ulong HostWriteCommands { get; protected set; }
|
||||
|
||||
public ulong MediaErrors { get; protected set; }
|
||||
|
||||
public byte PercentageUsed { get; protected set; }
|
||||
|
||||
public ulong PowerCycle { get; protected set; }
|
||||
|
||||
public ulong PowerOnHours { get; protected set; }
|
||||
|
||||
public short Temperature { get; protected set; }
|
||||
|
||||
public short[] TemperatureSensors { get; protected set; }
|
||||
|
||||
public ulong UnsafeShutdowns { get; protected set; }
|
||||
|
||||
public uint WarningCompositeTemperatureTime { get; protected set; }
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public abstract class NVMeInfo
|
||||
{
|
||||
public ushort ControllerId { get; protected set; }
|
||||
|
||||
public byte[] IEEE { get; protected set; }
|
||||
|
||||
public int Index { get; protected set; }
|
||||
|
||||
public string Model { get; protected set; }
|
||||
|
||||
public uint NumberNamespaces { get; protected set; }
|
||||
|
||||
public string Revision { get; protected set; }
|
||||
|
||||
public string Serial { get; protected set; }
|
||||
|
||||
public ushort SSVID { get; protected set; }
|
||||
|
||||
public ulong TotalCapacity { get; protected set; }
|
||||
|
||||
public ulong UnallocatedCapacity { get; protected set; }
|
||||
|
||||
public ushort VID { get; protected set; }
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.Storage.IscsiDisc;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal class NVMeIntel : INVMeDrive
|
||||
{
|
||||
//intel nvme access
|
||||
|
||||
public SafeHandle Identify(StorageInfo storageInfo)
|
||||
{
|
||||
return NVMeWindows.IdentifyDevice(storageInfo);
|
||||
}
|
||||
|
||||
public unsafe bool IdentifyController(SafeHandle hDevice, out NVME_IDENTIFY_CONTROLLER_DATA data)
|
||||
{
|
||||
data = new NVME_IDENTIFY_CONTROLLER_DATA();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "NvmeMini"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = 6; //identify
|
||||
passThrough.NVMeCmd[10] = 1; //return to host
|
||||
passThrough.Direction = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.NVME_PASS_THROUGH_IOCTL>(nameof(AtaSmart.NVME_PASS_THROUGH_IOCTL.DataBuffer));
|
||||
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = *(NVME_IDENTIFY_CONTROLLER_DATA*)newPtr;
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe bool HealthInfoLog(SafeHandle hDevice, out NVME_HEALTH_INFO_LOG data)
|
||||
{
|
||||
data = new NVME_HEALTH_INFO_LOG();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "NvmeMini"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = (uint)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 = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.NVME_PASS_THROUGH_IOCTL>(nameof(AtaSmart.NVME_PASS_THROUGH_IOCTL.DataBuffer));
|
||||
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = *(NVME_HEALTH_INFO_LOG*)newPtr;
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static unsafe SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeFileHandle handle = PInvoke.CreateFile(storageInfo.Scsi,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "NvmeMini"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = 6; //identify
|
||||
passThrough.NVMeCmd[10] = 1; //return to host
|
||||
passThrough.Direction = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
if (validTransfer)
|
||||
{ }
|
||||
else
|
||||
{
|
||||
handle.Close();
|
||||
handle = null;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.Storage.IscsiDisc;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal class NVMeIntelRst : INVMeDrive
|
||||
{
|
||||
//intel RST (raid) nvme access
|
||||
|
||||
public SafeHandle Identify(StorageInfo storageInfo)
|
||||
{
|
||||
return NVMeWindows.IdentifyDevice(storageInfo);
|
||||
}
|
||||
|
||||
public unsafe bool IdentifyController(SafeHandle hDevice, out NVME_IDENTIFY_CONTROLLER_DATA data)
|
||||
{
|
||||
data = new NVME_IDENTIFY_CONTROLLER_DATA();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "IntelNvm"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = 6; //identify
|
||||
passThrough.NVMeCmd[10] = 1; //return to host
|
||||
passThrough.Direction = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.NVME_PASS_THROUGH_IOCTL>(nameof(AtaSmart.NVME_PASS_THROUGH_IOCTL.DataBuffer));
|
||||
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = *(NVME_IDENTIFY_CONTROLLER_DATA*)newPtr;
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe bool HealthInfoLog(SafeHandle hDevice, out NVME_HEALTH_INFO_LOG data)
|
||||
{
|
||||
data = new NVME_HEALTH_INFO_LOG();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "IntelNvm"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = (uint)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 = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.NVME_PASS_THROUGH_IOCTL>(nameof(AtaSmart.NVME_PASS_THROUGH_IOCTL.DataBuffer));
|
||||
var newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = *(NVME_HEALTH_INFO_LOG*)newPtr;
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static unsafe SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeFileHandle handle = PInvoke.CreateFile(storageInfo.Scsi,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
AtaSmart.NVME_PASS_THROUGH_IOCTL passThrough = new();
|
||||
passThrough.SrbIoCtrl.HeaderLength = (uint)sizeof(SRB_IO_CONTROL);
|
||||
|
||||
ReadOnlySpan<byte> signature = "IntelNvm"u8;
|
||||
for (int i = 0; i < signature.Length; i++)
|
||||
passThrough.SrbIoCtrl.Signature[i] = signature[i];
|
||||
|
||||
passThrough.SrbIoCtrl.Timeout = 10;
|
||||
passThrough.SrbIoCtrl.ControlCode = AtaSmart.NVME_PASS_THROUGH_SRB_IO_CODE;
|
||||
passThrough.SrbIoCtrl.ReturnCode = 0;
|
||||
passThrough.SrbIoCtrl.Length = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL) - (uint)sizeof(SRB_IO_CONTROL);
|
||||
passThrough.NVMeCmd[0] = 6; //identify
|
||||
passThrough.NVMeCmd[10] = 1; //return to host
|
||||
passThrough.Direction = AtaSmart.NVME_DATA_IN;
|
||||
passThrough.QueueId = 0;
|
||||
passThrough.DataBufferLen = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
passThrough.MetaDataLen = 0;
|
||||
passThrough.ReturnBufferLen = (uint)sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
|
||||
int length = sizeof(AtaSmart.NVME_PASS_THROUGH_IOCTL);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(passThrough, buffer, false);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_SCSI_MINIPORT, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
if (validTransfer)
|
||||
{ }
|
||||
else
|
||||
{
|
||||
handle.Close();
|
||||
handle = null;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.Storage.IscsiDisc;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
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)
|
||||
{
|
||||
return NVMeWindows.IdentifyDevice(storageInfo);
|
||||
}
|
||||
|
||||
public unsafe bool IdentifyController(SafeHandle hDevice, out NVME_IDENTIFY_CONTROLLER_DATA data)
|
||||
{
|
||||
data = new NVME_IDENTIFY_CONTROLLER_DATA();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
AtaSmart.SCSI_PASS_THROUGH buffers = new();
|
||||
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(AtaSmart.SCSI_PASS_THROUGH), nameof(AtaSmart.SCSI_PASS_THROUGH.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)PInvoke.SCSI_IOCTL_DATA_OUT;
|
||||
buffers.DataBuf[0] = 1;
|
||||
|
||||
int length = (int)(Marshal.OffsetOf(typeof(AtaSmart.SCSI_PASS_THROUGH), nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)).ToInt32() + buffers.Spt.DataTransferLength);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (validTransfer)
|
||||
{
|
||||
//read data from samsung SSD
|
||||
buffers = new AtaSmart.SCSI_PASS_THROUGH();
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.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] = 1; // Transfer Length (high)
|
||||
buffers.Spt.Cdb[9] = 0; // Transfer Length (low)
|
||||
buffers.Spt.DataIn = (byte)PInvoke.SCSI_IOCTL_DATA_IN;
|
||||
|
||||
buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
|
||||
validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
|
||||
buffers = Marshal.PtrToStructure<AtaSmart.SCSI_PASS_THROUGH>(buffer);
|
||||
|
||||
if (validTransfer && !IsAllZero(buffers.DataBuf, AtaSmart.IOCTL_BUFFER_SIZE))
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf));
|
||||
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = Marshal.PtrToStructure<NVME_IDENTIFY_CONTROLLER_DATA>(newPtr);
|
||||
result = true;
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe bool HealthInfoLog(SafeHandle hDevice, out NVME_HEALTH_INFO_LOG data)
|
||||
{
|
||||
data = new NVME_HEALTH_INFO_LOG();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
AtaSmart.SCSI_PASS_THROUGH buffers = new();
|
||||
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.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)PInvoke.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<AtaSmart.SCSI_PASS_THROUGH>();
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
if (validTransfer)
|
||||
{
|
||||
//read data from samsung SSD
|
||||
buffers = new AtaSmart.SCSI_PASS_THROUGH();
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.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)PInvoke.SCSI_IOCTL_DATA_IN;
|
||||
|
||||
length = Marshal.SizeOf<AtaSmart.SCSI_PASS_THROUGH>();
|
||||
buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
|
||||
validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
IntPtr offset = Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf));
|
||||
IntPtr newPtr = IntPtr.Add(buffer, offset.ToInt32());
|
||||
data = Marshal.PtrToStructure<NVME_HEALTH_INFO_LOG>(newPtr);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static unsafe SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeFileHandle handle = PInvoke.CreateFile(storageInfo.DeviceId,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
AtaSmart.SCSI_PASS_THROUGH buffers = new();
|
||||
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(AtaSmart.SCSI_PASS_THROUGH), nameof(AtaSmart.SCSI_PASS_THROUGH.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)PInvoke.SCSI_IOCTL_DATA_OUT;
|
||||
buffers.DataBuf[0] = 1;
|
||||
|
||||
int length = Marshal.SizeOf<AtaSmart.SCSI_PASS_THROUGH>();
|
||||
IntPtr buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
if (validTransfer)
|
||||
{
|
||||
//read data from samsung SSD
|
||||
buffers = new AtaSmart.SCSI_PASS_THROUGH();
|
||||
buffers.Spt.Length = (ushort)sizeof(SCSI_PASS_THROUGH);
|
||||
buffers.Spt.PathId = 0;
|
||||
buffers.Spt.TargetId = 0;
|
||||
buffers.Spt.Lun = 0;
|
||||
buffers.Spt.SenseInfoLength = 24;
|
||||
buffers.Spt.DataTransferLength = AtaSmart.IOCTL_BUFFER_SIZE;
|
||||
buffers.Spt.TimeOutValue = 2;
|
||||
buffers.Spt.DataBufferOffset = new UIntPtr((ulong)Marshal.OffsetOf<AtaSmart.SCSI_PASS_THROUGH>(nameof(AtaSmart.SCSI_PASS_THROUGH.DataBuf)));
|
||||
buffers.Spt.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(AtaSmart.SCSI_PASS_THROUGH), nameof(AtaSmart.SCSI_PASS_THROUGH.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)PInvoke.SCSI_IOCTL_DATA_IN;
|
||||
|
||||
length = Marshal.SizeOf<AtaSmart.SCSI_PASS_THROUGH>();
|
||||
buffer = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(buffers, buffer, false);
|
||||
|
||||
validTransfer = PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_SCSI_PASS_THROUGH, (void*)buffer, (uint)length, (void*)buffer, (uint)length, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
AtaSmart.SCSI_PASS_THROUGH result = Marshal.PtrToStructure<AtaSmart.SCSI_PASS_THROUGH>(buffer);
|
||||
|
||||
if (IsAllZero(result.DataBuf, AtaSmart.IOCTL_BUFFER_SIZE))
|
||||
{
|
||||
handle.Close();
|
||||
handle = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.Close();
|
||||
handle = null;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
private static unsafe bool IsAllZero(byte* bytes, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (bytes[i] != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public class NVMeSmart : IDisposable
|
||||
{
|
||||
private readonly int _driveNumber;
|
||||
private readonly SafeHandle _handle;
|
||||
|
||||
internal NVMeSmart(StorageInfo storageInfo)
|
||||
{
|
||||
_driveNumber = storageInfo.Index;
|
||||
NVMeDrive = null;
|
||||
string name = storageInfo.Name;
|
||||
|
||||
// Test Windows generic driver protocol.
|
||||
if (NVMeDrive == null)
|
||||
{
|
||||
_handle = NVMeWindows.IdentifyDevice(storageInfo);
|
||||
if (_handle != null)
|
||||
{
|
||||
NVMeDrive = new NVMeWindows();
|
||||
}
|
||||
}
|
||||
|
||||
// Test Samsung protocol.
|
||||
if (NVMeDrive == null && name.IndexOf("Samsung", StringComparison.OrdinalIgnoreCase) > -1)
|
||||
{
|
||||
_handle = NVMeSamsung.IdentifyDevice(storageInfo);
|
||||
if (_handle != null)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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, NVME_IDENTIFY_CONTROLLER_DATA data)
|
||||
{
|
||||
Index = index;
|
||||
VID = data.VID;
|
||||
SSVID = data.SSVID;
|
||||
Serial = GetString(data.SN.ToArray());
|
||||
Model = GetString(data.MN.ToArray());
|
||||
Revision = GetString(data.FR.ToArray());
|
||||
IEEE = data.IEEE.ToArray();
|
||||
TotalCapacity = BitConverter.ToUInt64(data.TNVMCAP.ToArray(), 0); // 128bit little endian
|
||||
UnallocatedCapacity = BitConverter.ToUInt64(data.UNVMCAP.ToArray(), 0);
|
||||
ControllerId = data.CNTLID;
|
||||
NumberNamespaces = data.NN;
|
||||
}
|
||||
}
|
||||
|
||||
private class NVMeHealthInfo : Storage.NVMeHealthInfo
|
||||
{
|
||||
public NVMeHealthInfo(NVME_HEALTH_INFO_LOG log)
|
||||
{
|
||||
Temperature = KelvinToCelsius(log.Temperature.ToArray());
|
||||
AvailableSpare = log.AvailableSpare;
|
||||
AvailableSpareThreshold = log.AvailableSpareThreshold;
|
||||
PercentageUsed = log.PercentageUsed;
|
||||
DataUnitRead = BitConverter.ToUInt64(log.DataUnitRead.ToArray(), 0);
|
||||
DataUnitWritten = BitConverter.ToUInt64(log.DataUnitWritten.ToArray(), 0);
|
||||
HostReadCommands = BitConverter.ToUInt64(log.HostReadCommands.ToArray(), 0);
|
||||
HostWriteCommands = BitConverter.ToUInt64(log.HostWrittenCommands.ToArray(), 0);
|
||||
ControllerBusyTime = BitConverter.ToUInt64(log.ControllerBusyTime.ToArray(), 0);
|
||||
PowerCycle = BitConverter.ToUInt64(log.PowerCycle.ToArray(), 0);
|
||||
PowerOnHours = BitConverter.ToUInt64(log.PowerOnHours.ToArray(), 0);
|
||||
UnsafeShutdowns = BitConverter.ToUInt64(log.UnsafeShutdowns.ToArray(), 0);
|
||||
MediaErrors = BitConverter.ToUInt64(log.MediaErrors.ToArray(), 0);
|
||||
ErrorInfoLogEntryCount = BitConverter.ToUInt64(log.ErrorInfoLogEntryCount.ToArray(), 0);
|
||||
WarningCompositeTemperatureTime = log.WarningCompositeTemperatureTime;
|
||||
CriticalCompositeTemperatureTime = log.CriticalCompositeTemperatureTime;
|
||||
|
||||
TemperatureSensors = new short[8];
|
||||
TemperatureSensors[0] = KelvinToCelsius(log.TemperatureSensor1);
|
||||
TemperatureSensors[1] = KelvinToCelsius(log.TemperatureSensor2);
|
||||
TemperatureSensors[2] = KelvinToCelsius(log.TemperatureSensor3);
|
||||
TemperatureSensors[3] = KelvinToCelsius(log.TemperatureSensor4);
|
||||
TemperatureSensors[4] = KelvinToCelsius(log.TemperatureSensor5);
|
||||
TemperatureSensors[5] = KelvinToCelsius(log.TemperatureSensor6);
|
||||
TemperatureSensors[6] = KelvinToCelsius(log.TemperatureSensor7);
|
||||
TemperatureSensors[7] = KelvinToCelsius(log.TemperatureSensor8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.Storage.Nvme;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal class NVMeWindows : INVMeDrive
|
||||
{
|
||||
//windows generic driver nvme access
|
||||
public SafeHandle Identify(StorageInfo storageInfo)
|
||||
{
|
||||
return IdentifyDevice(storageInfo);
|
||||
}
|
||||
|
||||
public unsafe bool IdentifyController(SafeHandle hDevice, out NVME_IDENTIFY_CONTROLLER_DATA data)
|
||||
{
|
||||
data = new NVME_IDENTIFY_CONTROLLER_DATA();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
int cb = sizeof(STORAGE_PROPERTY_QUERY) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
|
||||
IntPtr ptr = Marshal.AllocHGlobal(cb);
|
||||
Marshal.Copy(new byte[cb], 0, ptr, cb); // Zero memory.
|
||||
|
||||
STORAGE_PROPERTY_QUERY* query = (STORAGE_PROPERTY_QUERY*)ptr;
|
||||
query->PropertyId = STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
|
||||
query->QueryType = STORAGE_QUERY_TYPE.PropertyStandardQuery;
|
||||
|
||||
STORAGE_PROTOCOL_SPECIFIC_DATA* protocolData = (STORAGE_PROTOCOL_SPECIFIC_DATA*)(&query->AdditionalParameters);
|
||||
protocolData->ProtocolType = STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
|
||||
protocolData->DataType = (uint)STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeIdentify;
|
||||
protocolData->ProtocolDataRequestValue = 1;
|
||||
protocolData->ProtocolDataOffset = (uint)sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
|
||||
protocolData->ProtocolDataLength = (uint)sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_STORAGE_QUERY_PROPERTY, (void*)ptr, (uint)cb, (void*)ptr, (uint)cb, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
var dataDescriptor = (STORAGE_PROTOCOL_DATA_DESCRIPTOR*)ptr;
|
||||
protocolData = &dataDescriptor->ProtocolSpecificData;
|
||||
data = *(NVME_IDENTIFY_CONTROLLER_DATA*)((byte*)protocolData + protocolData->ProtocolDataOffset);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe bool HealthInfoLog(SafeHandle hDevice, out NVME_HEALTH_INFO_LOG data)
|
||||
{
|
||||
data = new NVME_HEALTH_INFO_LOG();
|
||||
if (hDevice?.IsInvalid != false)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
|
||||
int cb = sizeof(STORAGE_PROPERTY_QUERY) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + sizeof(NVME_HEALTH_INFO_LOG);
|
||||
IntPtr ptr = Marshal.AllocHGlobal(cb);
|
||||
Marshal.Copy(new byte[cb], 0, ptr, cb); // Zero memory.
|
||||
|
||||
STORAGE_PROPERTY_QUERY* query = (STORAGE_PROPERTY_QUERY*)ptr;
|
||||
query->PropertyId = STORAGE_PROPERTY_ID.StorageAdapterProtocolSpecificProperty;
|
||||
query->QueryType = STORAGE_QUERY_TYPE.PropertyStandardQuery;
|
||||
|
||||
STORAGE_PROTOCOL_SPECIFIC_DATA* protocolData = (STORAGE_PROTOCOL_SPECIFIC_DATA*)(&query->AdditionalParameters);
|
||||
protocolData->ProtocolType = STORAGE_PROTOCOL_TYPE.ProtocolTypeNvme;
|
||||
protocolData->DataType = (uint)STORAGE_PROTOCOL_NVME_DATA_TYPE.NVMeDataTypeLogPage;
|
||||
protocolData->ProtocolDataRequestValue = (uint)NVME_LOG_PAGES.NVME_LOG_PAGE_HEALTH_INFO;
|
||||
protocolData->ProtocolDataOffset = (uint)sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
|
||||
protocolData->ProtocolDataLength = (uint)sizeof(NVME_HEALTH_INFO_LOG);
|
||||
|
||||
bool validTransfer = PInvoke.DeviceIoControl((HANDLE)hDevice.DangerousGetHandle(), PInvoke.IOCTL_STORAGE_QUERY_PROPERTY, (void*)ptr, (uint)cb, (void*)ptr, (uint)cb, null, null);
|
||||
if (validTransfer)
|
||||
{
|
||||
var dataDescriptor = (STORAGE_PROTOCOL_DATA_DESCRIPTOR*)ptr;
|
||||
protocolData = &dataDescriptor->ProtocolSpecificData;
|
||||
|
||||
data = *(NVME_HEALTH_INFO_LOG*)((byte*)protocolData + protocolData->ProtocolDataOffset);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeFileHandle handle = PInvoke.CreateFile(storageInfo.DeviceId,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
NVMeWindows nvme = new();
|
||||
if (nvme.IdentifyController(handle, out _))
|
||||
return handle;
|
||||
|
||||
handle.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
internal class NamePrefixAttribute : Attribute
|
||||
{
|
||||
public NamePrefixAttribute(string namePrefix)
|
||||
{
|
||||
Prefix = namePrefix;
|
||||
}
|
||||
|
||||
public string Prefix { get; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
internal class RequireSmartAttribute : Attribute
|
||||
{
|
||||
public RequireSmartAttribute(byte attributeId)
|
||||
{
|
||||
AttributeId = attributeId;
|
||||
}
|
||||
|
||||
public byte AttributeId { get; }
|
||||
}
|
||||
@@ -4,48 +4,14 @@
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public class SmartAttribute
|
||||
public sealed 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.
|
||||
/// 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="smartAttribute">The SMART attribute.</param>
|
||||
/// <param name="sensorType">
|
||||
/// Type of the sensor or null if no sensor is to
|
||||
/// be created.
|
||||
@@ -60,47 +26,25 @@ public class SmartAttribute
|
||||
/// 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)
|
||||
public SmartAttribute(DiskInfoToolkit.SmartAttribute smartAttribute, SensorType? sensorType, int sensorChannel, string sensorName, bool defaultHiddenSensor = false)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
_rawValueConversion = rawValueConversion;
|
||||
Attribute = smartAttribute;
|
||||
SensorType = sensorType;
|
||||
SensorChannel = sensorChannel;
|
||||
SensorName = sensorName;
|
||||
DefaultHiddenSensor = defaultHiddenSensor;
|
||||
ParameterDescriptions = parameterDescriptions;
|
||||
SensorName = sensorName ?? Name;
|
||||
IsHiddenByDefault = defaultHiddenSensor;
|
||||
}
|
||||
|
||||
public bool DefaultHiddenSensor { get; }
|
||||
public DiskInfoToolkit.SmartAttribute Attribute { get; internal set; }
|
||||
|
||||
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 byte Id => Attribute.Info.ID;
|
||||
public string Name => Attribute.Info.Name;
|
||||
|
||||
public SensorType? SensorType { get; }
|
||||
public int SensorChannel { get; }
|
||||
public string SensorName { get; }
|
||||
public bool IsHiddenByDefault { get; }
|
||||
|
||||
internal unsafe float ConvertValue(AtaSmart.SMART_ATTRIBUTE value, IReadOnlyList<IParameter> parameters)
|
||||
{
|
||||
if (_rawValueConversion == null)
|
||||
return value.CurrentValue;
|
||||
|
||||
Span<byte> rawValue = new(value.RawValue, 6);
|
||||
return _rawValueConversion(rawValue.ToArray(), value.CurrentValue, parameters);
|
||||
}
|
||||
public float Value => Attribute.Attribute.RawValueULong;
|
||||
public byte Threshold => Attribute.Attribute.Threshold;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,263 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
/// <summary>
|
||||
/// Localization class for SMART attribute names.
|
||||
/// </summary>
|
||||
public static class SmartNames
|
||||
{
|
||||
public static string AirflowTemperature => "Airflow Temperature";
|
||||
|
||||
public static string AlternativeEraseFailCount => "Alternative Erase Fail Count";
|
||||
|
||||
public static string AlternativeGSenseErrorRate => "Alternative G-Sense Error Rate";
|
||||
|
||||
public static string AlternativeProgramFailCount => "Alternative Program Fail Count";
|
||||
|
||||
public static string AvailableReservedSpace => "Available Reserved Space";
|
||||
|
||||
public static string AverageEraseCount => "Average Erase Count";
|
||||
|
||||
public static string BadBlockFullFlag => "Bad Block Full Flag";
|
||||
|
||||
public static string BitErrors => "Bit Errors";
|
||||
|
||||
public static string CalibrationRetryCount => "Calibration Retry Count";
|
||||
|
||||
public static string CommandTimeout => "Command Timeout";
|
||||
|
||||
public static string ControllerWritesToNand => "Controller Writes to NAND";
|
||||
|
||||
public static string CorrectedErrors => "Corrected Errors";
|
||||
|
||||
public static string CrcErrorCount => "CRC Error Count";
|
||||
|
||||
public static string CurrentPendingSectorCount => "Current Pending Sector Count";
|
||||
|
||||
public static string DataAddressMarkErrors => "Data Address Mark errors";
|
||||
|
||||
public static string DiskShift => "Disk Shift";
|
||||
|
||||
public static string DriveTemperature => "Drive Temperature";
|
||||
|
||||
public static string EccRate => "ECC Rate";
|
||||
|
||||
public static string EmergencyRetractCycleCount => "Emergency Retract Cycle Count";
|
||||
|
||||
public static string EndToEndError => "End-to-End error";
|
||||
|
||||
public static string EnduranceRemaining => "Endurance Remaining";
|
||||
|
||||
public static string EraseFailCount => "Erase Fail Count";
|
||||
|
||||
public static string EraseFailCountChip => "Erase Fail Count (Chip)";
|
||||
|
||||
public static string EraseFailCountTotal => "Erase Fail Count (Total)";
|
||||
|
||||
public static string EraseFailure => "Erase Failure";
|
||||
|
||||
public static string ErrorCorrectionCount => "Error Correction Count";
|
||||
|
||||
public static string ExceptionModeStatus => "Exception Mode Status";
|
||||
|
||||
public static string FactoryBadBlockCount => "Factory Bad Block Count";
|
||||
|
||||
public static string FlyingHeight => "Flying Height";
|
||||
|
||||
public static string FreeFallProtection => "Free Fall Protection";
|
||||
|
||||
public static string FTLProgramNANDPagesCount => "FTL Program NAND Pages Count";
|
||||
|
||||
public static string GmrHeadAmplitude => "GMR Head Amplitude";
|
||||
|
||||
public static string GSenseErrorRate => "G-sense Error Rate";
|
||||
|
||||
public static string HardwareEccRecovered => "Hardware ECC Recovered";
|
||||
|
||||
public static string HeadFlyingHours => "Head Flying Hours";
|
||||
|
||||
public static string HeadStability => "Head Stability";
|
||||
|
||||
public static string HighFlyWrites => "High Fly Writes";
|
||||
|
||||
public static string HostProgramNANDPagesCount => "Host Program NAND Pages Count";
|
||||
|
||||
public static string HostReads => "Host Reads";
|
||||
|
||||
public static string HostWrites => "Host Writes";
|
||||
|
||||
public static string HostWritesToController => "Host Writes to Controller";
|
||||
|
||||
public static string InducedOpVibrationDetection => "Induced Op-Vibration Detection";
|
||||
|
||||
public static string InitialBadBlockCount => "Initial Bad Block Count";
|
||||
|
||||
public static string LoadCycleCount => "Load Cycle Count";
|
||||
|
||||
public static string LoadedHours => "Loaded Hours";
|
||||
|
||||
public static string LoadFriction => "Load Friction";
|
||||
|
||||
public static string LoadInTime => "Load 'In'-time";
|
||||
|
||||
public static string LoadUnloadCycleCount => "Load/Unload Cycle Count";
|
||||
|
||||
public static string LoadUnloadCycleCountFujitsu => "Load/Unload Cycle Count (Fujitus)";
|
||||
|
||||
public static string LoadUnloadRetryCount => "Load/Unload Retry Count";
|
||||
|
||||
public static string MaxCellCycles => "Max Cell Cycles";
|
||||
|
||||
public static string MaxErase => "Max Erase";
|
||||
|
||||
public static string MediaWearOutIndicator => "Media Wear Out Indicator";
|
||||
|
||||
public static string MinErase => "Min Erase";
|
||||
|
||||
public static string MultiZoneErrorRate => "Multi-Zone Error Rate";
|
||||
|
||||
public static string NewFailingBlockCount => "New Failing Block Count";
|
||||
|
||||
public static string Non4KAlignedAccess => "Non-4k Aligned Access";
|
||||
|
||||
public static string OfflineSeekPerformance => "Offline Seek Performance";
|
||||
|
||||
public static string OffLineUncorrectableErrorCount => "Off-Line Uncorrectable Error Count";
|
||||
|
||||
public static string PowerCycleCount => "Power Cycle Count";
|
||||
|
||||
public static string PowerOffRetractCycle => "Power-Off Retract Cycle";
|
||||
|
||||
public static string PowerOnHours => "Power-On Hours (POH)";
|
||||
|
||||
public static string PowerRecoveryCount => "Power Recovery Count";
|
||||
|
||||
public static string ProgramFailCount => "Program Fail Count";
|
||||
|
||||
public static string ProgramFailCountChip => "Program Fail Count (Chip)";
|
||||
|
||||
public static string ProgramFailCountTotal => "Program Fail Count (Total)";
|
||||
|
||||
public static string ProgramFailure => "Program Failure";
|
||||
|
||||
public static string RawReadErrorRate => "Raw Read Error Rate";
|
||||
|
||||
public static string ReadChannelMargin => "Read Channel Margin";
|
||||
|
||||
public static string ReadCommands => "Read Commands";
|
||||
|
||||
public static string ReadErrorRate => "Read Error Rate";
|
||||
|
||||
public static string ReadErrorRetryRate => "Read Error Retry Rate";
|
||||
|
||||
public static string ReadFailure => "Read Failure";
|
||||
|
||||
public static string ReallocatedNANDBlockCount => "Reallocated NAND Block Count";
|
||||
|
||||
public static string ReallocatedSectorsCount => "Reallocated Sectors Count";
|
||||
|
||||
public static string ReallocationEventCount => "Reallocation Event Count";
|
||||
|
||||
public static string RecalibrationRetries => "Recalibration Retries";
|
||||
|
||||
public static string RemainingLife => "Remaining Life";
|
||||
|
||||
public static string ReportedUncorrectableErrors => "Reported Uncorrectable Errors";
|
||||
|
||||
public static string RetiredBlockCount => "Retired Block Count";
|
||||
|
||||
public static string RunOutCancel => "Run Out Cancel";
|
||||
|
||||
public static string RuntimeBadBlockTotal => "Runtime Bad Block Total";
|
||||
|
||||
public static string SataDownshiftErrorCount => "SATA Downshift Error Count";
|
||||
|
||||
public static string SataErrorCountCrc => "SATA Error Count CRC";
|
||||
|
||||
public static string SataErrorCountHandshake => "SATA Error Count Handshake";
|
||||
|
||||
public static string SectorsRead => "Sectors Read";
|
||||
|
||||
public static string SectorsWritten => "Sectors Written";
|
||||
|
||||
public static string SeekErrorRate => "Seek Error Rate";
|
||||
|
||||
public static string SeekTimePerformance => "Seek Time Performance";
|
||||
|
||||
public static string ShockDuringWrite => "Shock During Write";
|
||||
|
||||
public static string SoftEccCorrection => "Soft ECC Correction";
|
||||
|
||||
public static string SoftReadErrorRate => "Soft Read Error Rate";
|
||||
|
||||
public static string SpinBuzz => "Spin Buzz";
|
||||
|
||||
public static string SpinHighCurrent => "Spin High Current";
|
||||
|
||||
public static string SpinRetryCount => "Spin Retry Count";
|
||||
|
||||
public static string SpinUpTime => "Spin-Up Time";
|
||||
|
||||
public static string StartStopCount => "Start/Stop Count";
|
||||
|
||||
public static string SuccessfulRAINRecoveryCount => "Successful RAIN Recovery Count";
|
||||
|
||||
public static string SupercapStatus => "Supercap Status";
|
||||
|
||||
public static string TaCounterDetected => "TA Counter Detected";
|
||||
|
||||
public static string Temperature => "Temperature";
|
||||
|
||||
public static string TemperatureDifferenceFrom100 => "Temperature Difference from 100";
|
||||
|
||||
public static string TemperatureExceedCount => "Temperature Exceed Count";
|
||||
|
||||
public static string ThermalAsperityRate => "Thermal Asperity Rate (TAR)";
|
||||
|
||||
public static string ThroughputPerformance => "Throughput Performance";
|
||||
|
||||
public static string TorqueAmplificationCount => "Torque Amplification Count";
|
||||
|
||||
public static string TotalLbasRead => "Total LBAs Read";
|
||||
|
||||
public static string TotalLbasWritten => "Total LBAs Written";
|
||||
|
||||
public static string TransferErrorRate => "Transfer Error Rate";
|
||||
|
||||
public static string UltraDmaCrcErrorCount => "UltraDMA CRC Error Count";
|
||||
|
||||
public static string UncorrectableErrorCount => "Uncorrectable Error Count";
|
||||
|
||||
public static string UncorrectableSectorCount => "Uncorrectable Sector Count";
|
||||
|
||||
public static string UnexpectedPowerLossCount => "Unexpected Power Loss Count";
|
||||
|
||||
public static string Unknown => "Unknown";
|
||||
|
||||
public static string UnknownUnique => "Unknown Unique";
|
||||
|
||||
public static string UnrecoverableEcc => "Unrecoverable ECC";
|
||||
|
||||
public static string UnsafeShutdownCount => "Unsafe Shutdown Count";
|
||||
|
||||
public static string UnusedReserveNANDBlocks => "Unused Reserve NAND Blocks";
|
||||
|
||||
public static string UsedReservedBlockCountChip => "Used Reserved Block Count (Chip)";
|
||||
|
||||
public static string UsedReservedBlockCountTotal => "Used Reserved Block Count (Total)";
|
||||
|
||||
public static string VibrationDuringWrite => "Vibration During Write";
|
||||
|
||||
public static string WearLevelingCount => "Wear Leveling Count";
|
||||
|
||||
public static string WearRangeDelta => "Wear Range Delta";
|
||||
|
||||
public static string WriteCommands => "Write Commands";
|
||||
|
||||
public static string WriteErrorRate => "Write Error Rate";
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.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)
|
||||
};
|
||||
|
||||
public SsdIndilinx(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
|
||||
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
|
||||
{ }
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.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)
|
||||
};
|
||||
|
||||
public SsdIntel(StorageInfo storageInfo, ISmart smart, string name, string firmwareRevision, int index, ISettings settings)
|
||||
: base(storageInfo, smart, name, firmwareRevision, "ssd", index, _smartAttributes, settings)
|
||||
{ }
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
[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>
|
||||
{
|
||||
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 unsafe void UpdateAdditionalSensors(AtaSmart.SMART_ATTRIBUTE[] values)
|
||||
{
|
||||
float? hostProgramPagesCount = null;
|
||||
float? ftlProgramPagesCount = null;
|
||||
|
||||
foreach (AtaSmart.SMART_ATTRIBUTE value in values)
|
||||
{
|
||||
byte[] rawValue = new Span<byte>(value.RawValue, 6).ToArray();
|
||||
|
||||
if (value.Id == 0xF7)
|
||||
hostProgramPagesCount = RawToInt(rawValue, value.CurrentValue, null);
|
||||
|
||||
if (value.Id == 0xF8)
|
||||
ftlProgramPagesCount = RawToInt(rawValue, value.CurrentValue, null);
|
||||
|
||||
if (value.Id == 0xC2)
|
||||
{
|
||||
_temperature.Value = rawValue[0] + _temperature.Parameters[0].Value;
|
||||
|
||||
if (rawValue[0] != 0)
|
||||
ActivateSensor(_temperature);
|
||||
}
|
||||
}
|
||||
|
||||
if (hostProgramPagesCount.HasValue && ftlProgramPagesCount.HasValue)
|
||||
{
|
||||
_writeAmplification.Value = hostProgramPagesCount.Value > 0 ? (hostProgramPagesCount.Value + ftlProgramPagesCount) / hostProgramPagesCount.Value : 0;
|
||||
|
||||
ActivateSensor(_writeAmplification);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
[NamePrefix("PLEXTOR")]
|
||||
internal class SsdPlextor : AtaStorage
|
||||
{
|
||||
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)
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
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
|
||||
{
|
||||
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),
|
||||
|
||||
// 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)
|
||||
{ }
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
[NamePrefix(""), RequireSmart(0xAB), RequireSmart(0xB1)]
|
||||
internal class SsdSandforce : AtaStorage
|
||||
{
|
||||
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)
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
_writeAmplification = new Sensor("Write Amplification", 1, SensorType.Factor, this, settings);
|
||||
}
|
||||
|
||||
protected override unsafe void UpdateAdditionalSensors(AtaSmart.SMART_ATTRIBUTE[] values)
|
||||
{
|
||||
float? controllerWritesToNand = null;
|
||||
float? hostWritesToController = null;
|
||||
foreach (AtaSmart.SMART_ATTRIBUTE value in values)
|
||||
{
|
||||
byte[] rawValue = new Span<byte>(value.RawValue, 6).ToArray();
|
||||
|
||||
if (value.Id == 0xE9)
|
||||
controllerWritesToNand = RawToInt(rawValue, value.CurrentValue, null);
|
||||
|
||||
if (value.Id == 0xEA)
|
||||
hostWritesToController = RawToInt(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BlackSharp.Core.Converters;
|
||||
using DiskInfoToolkit.Interop.Enums;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
public sealed class StorageDevice : Hardware, ISmart
|
||||
{
|
||||
private readonly PerformanceValue _perfRead = new();
|
||||
private readonly PerformanceValue _perfTotal = new();
|
||||
private readonly PerformanceValue _perfWrite = new();
|
||||
|
||||
private readonly DiskInfoToolkit.Storage _storage;
|
||||
|
||||
private long _lastReadCount;
|
||||
private long _lastTime;
|
||||
private long _lastWriteCount;
|
||||
|
||||
private static DateTime _lastUpdate;
|
||||
|
||||
private readonly List<StorageDeviceSensor> _sensors = new();
|
||||
|
||||
private readonly List<SmartAttribute> _attributes = new();
|
||||
|
||||
private Sensor _sensorDiskReadActivity;
|
||||
private Sensor _sensorDiskReadRate;
|
||||
private Sensor _sensorDiskTotalActivity;
|
||||
private Sensor _sensorDiskWriteActivity;
|
||||
private Sensor _sensorDiskWriteRate;
|
||||
private Sensor _usageSensor;
|
||||
|
||||
public StorageDevice(DiskInfoToolkit.Storage storage, string id, ISettings settings)
|
||||
: base(storage.Model, new Identifier(id, storage.DriveNumber.ToString(CultureInfo.InvariantCulture)), settings)
|
||||
{
|
||||
_storage = storage;
|
||||
|
||||
CreateAttributes();
|
||||
|
||||
CreateSensors();
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Storage;
|
||||
|
||||
internal DiskInfoToolkit.Storage Storage => _storage;
|
||||
|
||||
public IReadOnlyList<SmartAttribute> Attributes => _attributes;
|
||||
|
||||
public static TimeSpan ThrottleInterval { get; set; }
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (DateTime.UtcNow - _lastUpdate < ThrottleInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastUpdate = DateTime.UtcNow;
|
||||
|
||||
//Toggle usage sensor
|
||||
ToggleUsageSensor();
|
||||
|
||||
//Update performance sensors
|
||||
UpdatePerformanceSensors();
|
||||
|
||||
//Update storage object
|
||||
_storage.Update();
|
||||
|
||||
//Update usage sensors
|
||||
UpdateUsageSensor();
|
||||
|
||||
//Update general sensors
|
||||
_sensors.ForEach(s => s.Update(_storage));
|
||||
|
||||
//Update attributes
|
||||
foreach (var attribute in _storage.Smart.SmartAttributes)
|
||||
{
|
||||
//Try to find attribute
|
||||
var found = _attributes.Find(sa => sa.Id == attribute.Info.ID);
|
||||
|
||||
//Found attribute, update it
|
||||
if (found != null)
|
||||
{
|
||||
found.Attribute = attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
var r = new StringBuilder();
|
||||
r.AppendLine("Storage");
|
||||
r.AppendLine();
|
||||
r.AppendLine($"Drive Name: {_storage.Model}");
|
||||
r.AppendLine($"Firmware Version and Revision: {_storage.Firmware}; {_storage.FirmwareRev}");
|
||||
r.AppendLine();
|
||||
r.AppendLine("Smart Attributes:");
|
||||
r.AppendLine("ID, Description, Value, Threshold");
|
||||
|
||||
foreach (var attribute in _attributes)
|
||||
{
|
||||
r.AppendLine($"{attribute.Id,3}, {attribute.Name,60}, {attribute.Value,18}, {attribute.Threshold,3}");
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
|
||||
if (!_storage.IsDynamicDisk)
|
||||
{
|
||||
r.AppendLine("Partitions:");
|
||||
|
||||
foreach (var partition in _storage.Partitions)
|
||||
{
|
||||
r.AppendLine($"Partition #{partition.PartitionNumber}");
|
||||
|
||||
if (partition.DriveLetter != null)
|
||||
{
|
||||
r.AppendLine($"Drive Letter: {partition.DriveLetter}");
|
||||
}
|
||||
|
||||
if (partition.AvailableFreeSpace != null)
|
||||
{
|
||||
r.AppendLine($"Available Free Space: {partition.AvailableFreeSpace}");
|
||||
}
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
|
||||
r.AppendLine($"Total Free Size: {_storage.TotalFreeSize}");
|
||||
}
|
||||
|
||||
r.AppendLine($"Total Size: {_storage.TotalSize}");
|
||||
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public override void Traverse(IVisitor visitor)
|
||||
{
|
||||
foreach (ISensor sensor in Sensors)
|
||||
sensor.Accept(visitor);
|
||||
}
|
||||
|
||||
private void CreateAttributes()
|
||||
{
|
||||
_attributes.Clear();
|
||||
|
||||
var attributes = SmartAttributeTranslator.GetAttributesFor(_storage);
|
||||
|
||||
_attributes.AddRange(attributes.Where(a => a != null));
|
||||
}
|
||||
|
||||
private void CreateSensors()
|
||||
{
|
||||
_usageSensor = new Sensor("Used Space", 0, SensorType.Load, this, _settings);
|
||||
ToggleUsageSensor();
|
||||
|
||||
int index = 0;
|
||||
|
||||
AddSensor("Temperature", index++, false, SensorType.Temperature, s => s.Smart.Temperature.GetValueOrDefault());
|
||||
|
||||
if (_storage.Smart.Life.HasValue)
|
||||
{
|
||||
AddSensor("Life", index++, false, SensorType.Level, s => s.Smart.Life.GetValueOrDefault());
|
||||
}
|
||||
|
||||
if (_storage.Smart.HostReads.HasValue)
|
||||
{
|
||||
AddSensor("Data read", index++, false, SensorType.Data, s => s.Smart.HostReads.GetValueOrDefault());
|
||||
}
|
||||
|
||||
if (_storage.Smart.HostWrites.HasValue)
|
||||
{
|
||||
AddSensor("Data written", index++, false, SensorType.Data, s => s.Smart.HostWrites.GetValueOrDefault());
|
||||
}
|
||||
|
||||
AddSensor("Power on count", index++, false, SensorType.Factor, s => s.Smart.PowerOnCount);
|
||||
AddSensor("Power on hours", index++, false, SensorType.Factor, s => Math.Max(s.Smart.MeasuredPowerOnHours, s.Smart.DetectedPowerOnHours));
|
||||
|
||||
if (_storage.IsNVMe)
|
||||
{
|
||||
AddSensor("Temperature warning", index++, false, SensorType.Factor, s => s.Smart.TemperatureWarning.GetValueOrDefault());
|
||||
AddSensor("Temperature critical", index++, false, SensorType.Factor, s => s.Smart.TemperatureCritical.GetValueOrDefault());
|
||||
|
||||
TryAddTemperatureSensor(index++, false, 1, SmartAttributeType.TemperatureSensor1);
|
||||
TryAddTemperatureSensor(index++, false, 2, SmartAttributeType.TemperatureSensor2);
|
||||
TryAddTemperatureSensor(index++, false, 3, SmartAttributeType.TemperatureSensor3);
|
||||
TryAddTemperatureSensor(index++, false, 4, SmartAttributeType.TemperatureSensor4);
|
||||
TryAddTemperatureSensor(index++, false, 5, SmartAttributeType.TemperatureSensor5);
|
||||
TryAddTemperatureSensor(index++, false, 6, SmartAttributeType.TemperatureSensor6);
|
||||
TryAddTemperatureSensor(index++, false, 7, SmartAttributeType.TemperatureSensor7);
|
||||
TryAddTemperatureSensor(index++, false, 8, SmartAttributeType.TemperatureSensor8);
|
||||
}
|
||||
|
||||
_sensorDiskReadActivity = new Sensor("Read Activity", 51, SensorType.Load, this, _settings);
|
||||
ActivateSensor(_sensorDiskReadActivity);
|
||||
|
||||
_sensorDiskWriteActivity = new Sensor("Write Activity", 52, SensorType.Load, this, _settings);
|
||||
ActivateSensor(_sensorDiskWriteActivity);
|
||||
|
||||
_sensorDiskTotalActivity = new Sensor("Total Activity", 53, SensorType.Load, this, _settings);
|
||||
ActivateSensor(_sensorDiskTotalActivity);
|
||||
|
||||
_sensorDiskReadRate = new Sensor("Read Rate", 54, SensorType.Throughput, this, _settings);
|
||||
ActivateSensor(_sensorDiskReadRate);
|
||||
|
||||
_sensorDiskWriteRate = new Sensor("Write Rate", 55, SensorType.Throughput, this, _settings);
|
||||
ActivateSensor(_sensorDiskWriteRate);
|
||||
|
||||
AddSmartAttributeSensors();
|
||||
}
|
||||
|
||||
private void TryAddTemperatureSensor(int index, bool defaultHidden, int thermalSensorIndex, SmartAttributeType type)
|
||||
{
|
||||
var attr = GetSmartAttribute(type);
|
||||
|
||||
//Does the sensor have a value ?
|
||||
if (attr != null && attr.Attribute.RawValueULong > 0)
|
||||
{
|
||||
AddSensor($"Temperature {thermalSensorIndex}", index, defaultHidden, SensorType.Temperature, s =>
|
||||
{
|
||||
var a = GetSmartAttribute(type);
|
||||
if (a != null)
|
||||
{
|
||||
return TemperatureConverter.KelvinToCelsius(a.Attribute.RawValueULong);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private DiskInfoToolkit.SmartAttribute GetSmartAttribute(SmartAttributeType type)
|
||||
{
|
||||
return _storage.Smart.SmartAttributes.FirstOrDefault(sa => sa.Info.Type == type);
|
||||
}
|
||||
|
||||
private void AddSensor(string name, int index, bool defaultHidden, SensorType sensorType, GetStorageDeviceSensorValue getValue)
|
||||
{
|
||||
var sensor = new StorageDeviceSensor(name, index, defaultHidden, sensorType, this, _settings, getValue)
|
||||
{
|
||||
Value = 0
|
||||
};
|
||||
|
||||
ActivateSensor(sensor);
|
||||
_sensors.Add(sensor);
|
||||
}
|
||||
|
||||
private unsafe void UpdatePerformanceSensors()
|
||||
{
|
||||
DISK_PERFORMANCE diskPerformance = new();
|
||||
|
||||
using var handle = PInvoke.CreateFile(_storage.PhysicalPath,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
|
||||
uint bytesReturned;
|
||||
if (!PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_DISK_PERFORMANCE, null, 0, &diskPerformance, (uint)sizeof(DISK_PERFORMANCE), &bytesReturned, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_perfRead.Update(diskPerformance.ReadTime, diskPerformance.QueryTime);
|
||||
_sensorDiskReadActivity.Value = (float)_perfRead.Result;
|
||||
|
||||
_perfWrite.Update(diskPerformance.WriteTime, diskPerformance.QueryTime);
|
||||
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
|
||||
|
||||
_perfTotal.Update(diskPerformance.IdleTime, diskPerformance.QueryTime);
|
||||
_sensorDiskTotalActivity.Value = (float)(100 - _perfTotal.Result);
|
||||
|
||||
long readCount = diskPerformance.BytesRead;
|
||||
long readDiff = readCount - _lastReadCount;
|
||||
_lastReadCount = readCount;
|
||||
|
||||
long writeCount = diskPerformance.BytesWritten;
|
||||
long writeDiff = writeCount - _lastWriteCount;
|
||||
_lastWriteCount = writeCount;
|
||||
|
||||
long currentTime = Stopwatch.GetTimestamp();
|
||||
if (_lastTime != 0)
|
||||
{
|
||||
double timeDeltaSeconds = TimeSpan.FromTicks(currentTime - _lastTime).TotalSeconds;
|
||||
|
||||
double writeSpeed = writeDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskWriteRate.Value = (float)writeSpeed;
|
||||
|
||||
double readSpeed = readDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskReadRate.Value = (float)readSpeed;
|
||||
}
|
||||
|
||||
_lastTime = currentTime;
|
||||
}
|
||||
|
||||
private void ToggleUsageSensor()
|
||||
{
|
||||
if (!_storage.IsDynamicDisk)
|
||||
{
|
||||
ActivateSensor(_usageSensor);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_usageSensor);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUsageSensor()
|
||||
{
|
||||
if (_storage.TotalSize > 0)
|
||||
{
|
||||
//Set sensor value
|
||||
_usageSensor.Value = 100.0f - (100.0f * _storage.TotalFreeSize / _storage.TotalSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
_usageSensor.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSmartAttributeSensors()
|
||||
{
|
||||
//Unique attributes by sensor- type and channel
|
||||
var attributes = Attributes.Where(sa => sa.SensorType.HasValue)
|
||||
.GroupBy(sa => new { sa.SensorType.Value, sa.SensorChannel })
|
||||
.Select(sa => sa.First());
|
||||
|
||||
foreach (var attr in attributes)
|
||||
{
|
||||
AddSensor(attr.SensorName,
|
||||
attr.SensorChannel,
|
||||
attr.IsHiddenByDefault,
|
||||
attr.SensorType.Value,
|
||||
s => attr.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 long Time { get; set; }
|
||||
|
||||
private long Value { get; set; }
|
||||
|
||||
public void Update(long val, long valBase)
|
||||
{
|
||||
long diffValue = val - Value;
|
||||
long 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal delegate float GetStorageDeviceSensorValue(DiskInfoToolkit.Storage storage);
|
||||
|
||||
internal class StorageDeviceSensor : Sensor
|
||||
{
|
||||
private readonly GetStorageDeviceSensorValue _getValue;
|
||||
|
||||
public StorageDeviceSensor(string name, int index, bool defaultHidden, SensorType sensorType, Hardware hardware, ISettings settings, GetStorageDeviceSensorValue getValue)
|
||||
: base(name, index, defaultHidden, sensorType, hardware, null, settings)
|
||||
{
|
||||
_getValue = getValue;
|
||||
}
|
||||
|
||||
public void Update(DiskInfoToolkit.Storage storage)
|
||||
{
|
||||
var value = _getValue(storage);
|
||||
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal sealed class StorageGeneric : AbstractStorage
|
||||
{
|
||||
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) { }
|
||||
}
|
||||
@@ -4,163 +4,82 @@
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management;
|
||||
using System.Linq;
|
||||
using DiskInfoToolkit;
|
||||
using DiskInfoToolkit.Events;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
#pragma warning disable CA1416 // Validate platform compatibility
|
||||
|
||||
internal class StorageGroup : IGroup
|
||||
internal class StorageGroup : IGroup, IHardwareChanged
|
||||
{
|
||||
private readonly List<AbstractStorage> _hardware = new();
|
||||
private readonly List<StorageDevice> _hardware = new();
|
||||
|
||||
private readonly ISettings _settings;
|
||||
|
||||
public event HardwareEventHandler HardwareAdded;
|
||||
public event HardwareEventHandler HardwareRemoved;
|
||||
|
||||
public StorageGroup(ISettings settings)
|
||||
{
|
||||
if (Software.OperatingSystem.IsUnix)
|
||||
return;
|
||||
|
||||
Dictionary<uint, List<(uint, ulong)>> storageSpaceDiskToPhysicalDiskMap = GetStorageSpaceDiskToPhysicalDiskMap();
|
||||
AddHardware(settings, storageSpaceDiskToPhysicalDiskMap);
|
||||
_settings = settings;
|
||||
|
||||
AddHardware(settings);
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the hardware.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <param name="storageSpaceDiskToPhysicalDiskMap">The storage space disk to physical disk map.</param>
|
||||
private void AddHardware(ISettings settings, Dictionary<uint, List<(uint, ulong)>> storageSpaceDiskToPhysicalDiskMap)
|
||||
private void AddHardware(ISettings settings)
|
||||
{
|
||||
try
|
||||
StorageManager.StoragesChanged -= OnStoragesChanged;
|
||||
|
||||
//Reload storage devices
|
||||
StorageManager.ReloadStorages();
|
||||
|
||||
//Transform storage device to hardware
|
||||
_hardware.AddRange(StorageManager.Storages.Select(s => new StorageDevice(s, GetID(s), settings)));
|
||||
|
||||
StorageManager.StoragesChanged += OnStoragesChanged;
|
||||
}
|
||||
|
||||
private void OnStoragesChanged(StoragesChangedEventArgs e)
|
||||
{
|
||||
StorageDevice storageDevice = null;
|
||||
|
||||
switch (e.StorageChangeIdentifier)
|
||||
{
|
||||
// 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) } };
|
||||
case StorageChangeIdentifier.Added:
|
||||
storageDevice = new StorageDevice(e.Storage, GetID(e.Storage), _settings);
|
||||
|
||||
foreach (ManagementBaseObject diskDrive in diskDriveSearcher.Get())
|
||||
{
|
||||
string deviceId = (string)diskDrive.Properties["DeviceId"].Value; // is \\.\PhysicalDrive0..n
|
||||
uint index = Convert.ToUInt32(diskDrive.Properties["Index"].Value);
|
||||
ulong diskSize = Convert.ToUInt64(diskDrive.Properties["Size"].Value);
|
||||
int scsi = Convert.ToInt32(diskDrive.Properties["SCSIPort"].Value);
|
||||
_hardware.Add(storageDevice);
|
||||
HardwareAdded?.Invoke(storageDevice);
|
||||
break;
|
||||
case StorageChangeIdentifier.Removed:
|
||||
storageDevice = _hardware.Find(sd => sd.Storage == e.Storage);
|
||||
|
||||
if (deviceId != null)
|
||||
if (storageDevice != null)
|
||||
{
|
||||
var instance = AbstractStorage.CreateInstance(deviceId, index, diskSize, scsi, settings);
|
||||
if (instance != null)
|
||||
_hardware.Add(instance);
|
||||
|
||||
if (storageSpaceDiskToPhysicalDiskMap.ContainsKey(index))
|
||||
{
|
||||
foreach ((uint, ulong) physicalDisk in storageSpaceDiskToPhysicalDiskMap[index])
|
||||
{
|
||||
var physicalDiskInstance = AbstractStorage.CreateInstance(@$"\\.\PHYSICALDRIVE{physicalDisk.Item1}", physicalDisk.Item1, physicalDisk.Item2, scsi, settings);
|
||||
if (physicalDiskInstance != null)
|
||||
_hardware.Add(physicalDiskInstance);
|
||||
}
|
||||
}
|
||||
_hardware.Remove(storageDevice);
|
||||
HardwareRemoved?.Invoke(storageDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps each StorageSpace to the PhysicalDisks it is composed of.
|
||||
/// </summary>
|
||||
private static Dictionary<uint, List<(uint, ulong)>> GetStorageSpaceDiskToPhysicalDiskMap()
|
||||
private string GetID(DiskInfoToolkit.Storage storage)
|
||||
{
|
||||
var diskToPhysicalDisk = new Dictionary<uint, List<(uint, ulong)>>();
|
||||
|
||||
if (!Software.OperatingSystem.IsWindows8OrGreater)
|
||||
return diskToPhysicalDisk;
|
||||
|
||||
try
|
||||
{
|
||||
ManagementScope scope = new(@"\root\Microsoft\Windows\Storage");
|
||||
|
||||
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-disk
|
||||
// Lists all the disks visible to your system, the output is the same as Win32_DiskDrive.
|
||||
// If you're using a storage Space, the "hidden" disks which compose your storage space will not be listed.
|
||||
using var diskSearcher = new ManagementObjectSearcher(scope, new ObjectQuery("SELECT * FROM MSFT_Disk"));
|
||||
|
||||
foreach (ManagementBaseObject disk in diskSearcher.Get())
|
||||
{
|
||||
try
|
||||
{
|
||||
List<(uint, ulong)> map = MapDiskToPhysicalDisk(disk, scope);
|
||||
if (map.Count > 0)
|
||||
diskToPhysicalDisk[(uint)disk["Number"]] = map;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
|
||||
return diskToPhysicalDisk;
|
||||
if (storage.IsNVMe)
|
||||
return "nvme";
|
||||
else if (storage.IsSSD)
|
||||
return "ssd";
|
||||
else
|
||||
return "hdd";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a disk to a physical disk.
|
||||
/// </summary>
|
||||
/// <param name="disk">The disk.</param>
|
||||
/// <param name="scope">The scope.</param>
|
||||
private static List<(uint, ulong)> MapDiskToPhysicalDisk(ManagementBaseObject disk, ManagementScope scope)
|
||||
{
|
||||
var map = new List<(uint, ulong)>();
|
||||
public void Close() { }
|
||||
|
||||
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-virtualdisk
|
||||
// Maps the current Disk to its corresponding VirtualDisk. If the current Disk is not a storage space, it does not have a corresponding VirtualDisk.
|
||||
// Each Disk maps to one or zero VirtualDisk.
|
||||
using var toVirtualDisk = new ManagementObjectSearcher(scope, new ObjectQuery(FollowAssociationQuery("MSFT_Disk", (string)disk["ObjectId"], "MSFT_VirtualDiskToDisk")));
|
||||
|
||||
foreach (ManagementBaseObject virtualDisk in toVirtualDisk.Get())
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-physicaldisk
|
||||
// Maps the current VirtualDisk to the PhysicalDisk it is composed of.
|
||||
// Each VirtualDisk maps to one or more PhysicalDisk.
|
||||
|
||||
using var toPhysicalDisk = new ManagementObjectSearcher(scope,
|
||||
new ObjectQuery(FollowAssociationQuery("MSFT_VirtualDisk",
|
||||
(string)virtualDisk["ObjectId"],
|
||||
"MSFT_VirtualDiskToPhysicalDisk")));
|
||||
|
||||
foreach (ManagementBaseObject physicalDisk in toPhysicalDisk.Get())
|
||||
{
|
||||
ulong physicalDiskSize = (ulong)physicalDisk["Size"];
|
||||
|
||||
if (uint.TryParse((string)physicalDisk["DeviceId"], out uint physicalDiskId))
|
||||
map.Add((physicalDiskId, physicalDiskSize));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static string FollowAssociationQuery(string source, string objectId, string associationClass)
|
||||
{
|
||||
return @$"ASSOCIATORS OF {{{source}.ObjectId=""{objectId.Replace(@"\", @"\\").Replace(@"""", @"\""")}""}} WHERE AssocClass = {associationClass}";
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (AbstractStorage storage in _hardware)
|
||||
storage.Close();
|
||||
}
|
||||
public string GetReport() => null;
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal abstract class StorageInfo
|
||||
{
|
||||
public STORAGE_BUS_TYPE BusType { get; protected set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public ulong DiskSize { get; set; }
|
||||
|
||||
public SafeFileHandle Handle { get; set; }
|
||||
|
||||
public int Index { get; protected set; }
|
||||
|
||||
public string Name => (Vendor + " " + Product).Trim();
|
||||
|
||||
public string Product { get; protected set; }
|
||||
|
||||
public byte[] RawData { get; protected set; }
|
||||
|
||||
public bool Removable { get; protected set; }
|
||||
|
||||
public string Revision { get; protected set; }
|
||||
|
||||
public string Scsi { get; set; }
|
||||
|
||||
public string Serial { get; protected set; }
|
||||
|
||||
public string Vendor { get; protected set; }
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
using static LibreHardwareMonitor.Interop.AtaSmart;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal class WindowsSmart : ISmart
|
||||
{
|
||||
private readonly int _driveNumber;
|
||||
private readonly SafeHandle _handle;
|
||||
|
||||
public WindowsSmart(int driveNumber)
|
||||
{
|
||||
_driveNumber = driveNumber;
|
||||
_handle = PInvoke.CreateFile(@"\\.\PhysicalDrive" + driveNumber, (uint)FileAccess.ReadWrite, FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, null);
|
||||
}
|
||||
|
||||
public bool IsValid => !_handle.IsInvalid && !_handle.IsClosed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public unsafe bool EnableSmart()
|
||||
{
|
||||
if (_handle.IsClosed)
|
||||
throw new ObjectDisposedException(nameof(WindowsSmart));
|
||||
|
||||
var parameter = new SENDCMDINPARAMS
|
||||
{
|
||||
bDriveNumber = (byte)_driveNumber,
|
||||
irDriveRegs =
|
||||
{
|
||||
bFeaturesReg = 0xD8,
|
||||
bCylLowReg = (byte)PInvoke.SMART_CYL_LOW,
|
||||
bCylHighReg = (byte)PInvoke.SMART_CYL_HI,
|
||||
bCommandReg = (byte) PInvoke.SMART_CMD
|
||||
}
|
||||
};
|
||||
|
||||
var result = new SENDCMDOUTPARAMS();
|
||||
|
||||
return PInvoke.DeviceIoControl((HANDLE)_handle.DangerousGetHandle(),
|
||||
PInvoke.SMART_SEND_DRIVE_COMMAND,
|
||||
¶meter,
|
||||
(uint)sizeof(SENDCMDINPARAMS),
|
||||
&result,
|
||||
(uint)sizeof(SENDCMDOUTPARAMS),
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
public unsafe SMART_ATTRIBUTE[] ReadSmartData()
|
||||
{
|
||||
if (_handle.IsClosed)
|
||||
throw new ObjectDisposedException(nameof(WindowsSmart));
|
||||
|
||||
var parameter = new SENDCMDINPARAMS
|
||||
{
|
||||
bDriveNumber = (byte)_driveNumber,
|
||||
irDriveRegs =
|
||||
{
|
||||
bFeaturesReg = (byte)PInvoke.READ_ATTRIBUTES,
|
||||
bCylLowReg = (byte)PInvoke.SMART_CYL_LOW,
|
||||
bCylHighReg = (byte)PInvoke.SMART_CYL_HI,
|
||||
bCommandReg = (byte) PInvoke.SMART_CMD
|
||||
}
|
||||
};
|
||||
|
||||
int cb = sizeof(SENDCMDOUTPARAMS) + 512; // 512 bytes buffer.
|
||||
IntPtr buffer = Marshal.AllocHGlobal(cb);
|
||||
|
||||
bool isValid = PInvoke.DeviceIoControl((HANDLE)_handle.DangerousGetHandle(),
|
||||
PInvoke.SMART_RCV_DRIVE_DATA,
|
||||
¶meter,
|
||||
(uint)sizeof(SENDCMDINPARAMS),
|
||||
(void*)buffer,
|
||||
(uint)cb,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
var attributes = new SMART_ATTRIBUTE[30]; // A maximum of 30 are returned.
|
||||
|
||||
var sendCmdOutParams = (SENDCMDOUTPARAMS*)buffer;
|
||||
fixed (byte* pBuffer = &sendCmdOutParams->bBuffer[0])
|
||||
{
|
||||
var pAttribute = (SMART_ATTRIBUTE*)(pBuffer + 2); // + 2 padding.
|
||||
|
||||
for (int i = 0; i < attributes.Length; i++)
|
||||
attributes[i] = pAttribute[i];
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public unsafe SMART_THRESHOLD[] ReadSmartThresholds()
|
||||
{
|
||||
if (_handle.IsClosed)
|
||||
throw new ObjectDisposedException(nameof(WindowsSmart));
|
||||
|
||||
var parameter = new SENDCMDINPARAMS
|
||||
{
|
||||
bDriveNumber = (byte)_driveNumber,
|
||||
irDriveRegs =
|
||||
{
|
||||
bFeaturesReg = (byte)PInvoke.READ_THRESHOLDS,
|
||||
bCylLowReg = (byte)PInvoke.SMART_CYL_LOW,
|
||||
bCylHighReg = (byte)PInvoke.SMART_CYL_HI,
|
||||
bCommandReg = (byte) PInvoke.SMART_CMD
|
||||
}
|
||||
};
|
||||
|
||||
int cb = sizeof(SENDCMDOUTPARAMS) + 512; // 2 bytes padding + 512 bytes buffer.
|
||||
IntPtr buffer = Marshal.AllocHGlobal(cb);
|
||||
bool isValid = PInvoke.DeviceIoControl((HANDLE)_handle.DangerousGetHandle(),
|
||||
PInvoke.SMART_RCV_DRIVE_DATA,
|
||||
¶meter,
|
||||
(uint)sizeof(SENDCMDINPARAMS),
|
||||
(void*)buffer,
|
||||
(uint)cb,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
var thresholds = new SMART_THRESHOLD[30]; // A maximum of 30 are returned.
|
||||
|
||||
var sendCmdOutParams = (SENDCMDOUTPARAMS*)buffer;
|
||||
fixed (byte* pBuffer = &sendCmdOutParams->bBuffer[0])
|
||||
{
|
||||
var pThreshold = (SMART_THRESHOLD*)(pBuffer + 2); // + 2 padding.
|
||||
|
||||
for (int i = 0; i < thresholds.Length; i++)
|
||||
thresholds[i] = pThreshold[i];
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return thresholds;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return null;
|
||||
}
|
||||
|
||||
public unsafe bool ReadNameAndFirmwareRevision(out string name, out string firmwareRevision)
|
||||
{
|
||||
if (_handle.IsClosed)
|
||||
throw new ObjectDisposedException(nameof(WindowsSmart));
|
||||
|
||||
var parameter = new SENDCMDINPARAMS
|
||||
{
|
||||
bDriveNumber = (byte)_driveNumber,
|
||||
irDriveRegs =
|
||||
{
|
||||
bCommandReg = 0xEC
|
||||
}
|
||||
};
|
||||
|
||||
int cb = sizeof(SENDCMDOUTPARAMS) + sizeof(IDENTIFY_DEVICE_DATA);
|
||||
IntPtr buffer = Marshal.AllocHGlobal(cb);
|
||||
|
||||
bool valid = PInvoke.DeviceIoControl((HANDLE)_handle.DangerousGetHandle(),
|
||||
PInvoke.SMART_RCV_DRIVE_DATA,
|
||||
¶meter,
|
||||
(uint)sizeof(SENDCMDINPARAMS),
|
||||
(void*)buffer,
|
||||
(uint)cb,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
name = null;
|
||||
firmwareRevision = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
IDENTIFY_DEVICE_DATA identity = *(IDENTIFY_DEVICE_DATA*)((byte*)buffer + (int)Marshal.OffsetOf<SENDCMDOUTPARAMS>(nameof(SENDCMDOUTPARAMS.bBuffer)));
|
||||
|
||||
byte* p = identity.ModelNumber;
|
||||
for (int i = 0; i < 40; i += 2)
|
||||
{
|
||||
(p[i], p[i + 1]) = (p[i + 1], p[i]);
|
||||
}
|
||||
|
||||
p = identity.FirmwareRevision;
|
||||
for (int i = 0; i < 8; i += 2)
|
||||
{
|
||||
(p[i], p[i + 1]) = (p[i + 1], p[i]);
|
||||
}
|
||||
|
||||
name = Encoding.ASCII.GetString(identity.ModelNumber, 40).Trim();
|
||||
firmwareRevision = Encoding.ASCII.GetString(identity.FirmwareRevision, 8);
|
||||
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && !_handle.IsClosed)
|
||||
{
|
||||
_handle.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.System.Ioctl;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
internal static class WindowsStorage
|
||||
{
|
||||
public static unsafe Storage.StorageInfo GetStorageInfo(string deviceId, uint driveIndex)
|
||||
{
|
||||
using SafeFileHandle handle = PInvoke.CreateFile(deviceId,
|
||||
(uint)FileAccess.ReadWrite,
|
||||
FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE,
|
||||
null,
|
||||
FILE_CREATION_DISPOSITION.OPEN_EXISTING,
|
||||
FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
|
||||
null);
|
||||
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
var query = new STORAGE_PROPERTY_QUERY { PropertyId = STORAGE_PROPERTY_ID.StorageDeviceProperty, QueryType = STORAGE_QUERY_TYPE.PropertyStandardQuery };
|
||||
STORAGE_DESCRIPTOR_HEADER result = new();
|
||||
|
||||
if (!PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_STORAGE_QUERY_PROPERTY, &query, (uint)sizeof(STORAGE_PROPERTY_QUERY), &result, (uint)sizeof(STORAGE_DESCRIPTOR_HEADER), null, null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IntPtr descriptorPtr = Marshal.AllocHGlobal((int)result.Size);
|
||||
|
||||
try
|
||||
{
|
||||
return PInvoke.DeviceIoControl((HANDLE)handle.DangerousGetHandle(), PInvoke.IOCTL_STORAGE_QUERY_PROPERTY, &query, (uint)sizeof(STORAGE_PROPERTY_QUERY), (void*)descriptorPtr, result.Size, null, null)
|
||||
? new StorageInfo((int)driveIndex, descriptorPtr)
|
||||
: null;
|
||||
}
|
||||
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())
|
||||
{
|
||||
if (o is ManagementObject dp)
|
||||
{
|
||||
foreach (ManagementBaseObject ld in dp.GetRelated("Win32_LogicalDisk"))
|
||||
list.Add(((string)ld["Name"]).TrimEnd(':'));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private class StorageInfo : Storage.StorageInfo
|
||||
{
|
||||
public unsafe StorageInfo(int index, IntPtr descriptorPtr)
|
||||
{
|
||||
STORAGE_DEVICE_DESCRIPTOR descriptor = *(STORAGE_DEVICE_DESCRIPTOR*)descriptorPtr;
|
||||
Index = index;
|
||||
Vendor = GetString(descriptorPtr, descriptor.VendorIdOffset, descriptor.Size);
|
||||
Product = GetString(descriptorPtr, descriptor.ProductIdOffset, descriptor.Size);
|
||||
Revision = GetString(descriptorPtr, descriptor.ProductRevisionOffset, descriptor.Size);
|
||||
Serial = GetString(descriptorPtr, descriptor.SerialNumberOffset, descriptor.Size);
|
||||
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, uint size)
|
||||
{
|
||||
return offset > 0 && offset < size ? Marshal.PtrToStringAnsi(IntPtr.Add(descriptorPtr, (int)offset))?.Trim() : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Storage.IscsiDisc;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace LibreHardwareMonitor.Interop;
|
||||
|
||||
public unsafe class AtaSmart
|
||||
{
|
||||
internal const int IOCTL_BUFFER_SIZE = 4096;
|
||||
internal const int SCSI_IOCTL_SENSE_SIZE = 24;
|
||||
internal const int NVME_IOCTL_CMD_DW_SIZE = 16;
|
||||
internal const int NVME_IOCTL_COMPLETE_DW_SIZE = 4;
|
||||
internal const int NVME_IOCTL_VENDOR_SPECIFIC_DW_SIZE = 6;
|
||||
|
||||
internal const int NVME_DATA_OUT = 1;
|
||||
internal const int NVME_DATA_IN = 2;
|
||||
|
||||
internal const uint NVME_PASS_THROUGH_SRB_IO_CODE = 0xe0002000;
|
||||
|
||||
internal const byte SMART_LBA_HI = 0xC2;
|
||||
internal const byte SMART_LBA_MID = 0x4F;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SMART_ATTRIBUTE
|
||||
{
|
||||
internal byte Id;
|
||||
internal ushort Flags;
|
||||
internal byte CurrentValue;
|
||||
internal byte WorstValue;
|
||||
internal fixed byte RawValue[6];
|
||||
internal byte Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SMART_THRESHOLD
|
||||
{
|
||||
public byte Id;
|
||||
public byte Threshold;
|
||||
public fixed byte Reserved[10];
|
||||
}
|
||||
|
||||
internal struct IDENTIFY_DEVICE_DATA
|
||||
{
|
||||
public ushort GeneralConfiguration;
|
||||
public ushort NumberOfCylinders;
|
||||
public ushort Reserved1;
|
||||
public ushort NumberOfHeads;
|
||||
public ushort UnformattedBytesPerTrack;
|
||||
public ushort UnformattedBytesPerSector;
|
||||
public ushort SectorsPerTrack;
|
||||
public fixed ushort VendorUnique[3];
|
||||
public fixed byte SerialNumber[20];
|
||||
public ushort BufferType;
|
||||
public ushort BufferSectorSize;
|
||||
public ushort NumberOfEccBytes;
|
||||
public fixed byte FirmwareRevision[8];
|
||||
public fixed byte ModelNumber[40];
|
||||
public byte MaximumBlockTransfer;
|
||||
public byte VendorUnique2;
|
||||
public ushort DoubleWordIo;
|
||||
public ushort Capabilities;
|
||||
public ushort Reserved2;
|
||||
public byte VendorUnique3;
|
||||
public byte PioCycleTimingMode;
|
||||
public byte VendorUnique4;
|
||||
public byte DmaCycleTimingMode;
|
||||
public ushort TranslationFieldsValid;
|
||||
public ushort NumberOfCurrentCylinders;
|
||||
public ushort NumberOfCurrentHeads;
|
||||
public ushort CurrentSectorsPerTrack;
|
||||
public uint CurrentSectorCapacity;
|
||||
public fixed ushort Reserved3[197];
|
||||
}
|
||||
|
||||
internal struct NVME_PASS_THROUGH_IOCTL
|
||||
{
|
||||
public SRB_IO_CONTROL SrbIoCtrl;
|
||||
public fixed uint VendorSpecific[NVME_IOCTL_VENDOR_SPECIFIC_DW_SIZE];
|
||||
public fixed uint NVMeCmd[NVME_IOCTL_CMD_DW_SIZE];
|
||||
public fixed uint CplEntry[NVME_IOCTL_COMPLETE_DW_SIZE];
|
||||
public uint Direction;
|
||||
public uint QueueId;
|
||||
public uint DataBufferLen;
|
||||
public uint MetaDataLen;
|
||||
public uint ReturnBufferLen;
|
||||
public fixed byte DataBuffer[IOCTL_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
internal struct SCSI_PASS_THROUGH
|
||||
{
|
||||
public Windows.Win32.Storage.IscsiDisc.SCSI_PASS_THROUGH Spt;
|
||||
public fixed byte SenseBuf[SCSI_IOCTL_SENSE_SIZE];
|
||||
public fixed byte DataBuf[IOCTL_BUFFER_SIZE];
|
||||
}
|
||||
}
|
||||
@@ -6,40 +6,10 @@ namespace LibreHardwareMonitor.Interop;
|
||||
|
||||
internal static class ByteExtensions
|
||||
{
|
||||
public static byte[] ToArray(this __byte_2 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_3 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_4 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_8 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_16 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_20 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this __byte_40 bytes)
|
||||
{
|
||||
return bytes.AsReadOnlySpan().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -55,12 +55,13 @@
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiskInfoToolkit" Version="1.0.1" />
|
||||
<PackageReference Include="HidSharp" Version="2.6.4" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.242">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="RAMSPDToolkit-NDD" Version="1.3.2" />
|
||||
<PackageReference Include="RAMSPDToolkit-NDD" Version="1.4.0" />
|
||||
<PackageReference Include="System.Management" Version="9.0.10" />
|
||||
<PackageReference Include="System.Memory" Version="4.6.3" Condition="'$(TargetFramework)' == 'net472'" />
|
||||
<PackageReference Include="System.Threading.AccessControl" Version="9.0.10" />
|
||||
|
||||
@@ -27,35 +27,12 @@ GROUP_AFFINITY
|
||||
SetThreadGroupAffinity
|
||||
EnumSystemFirmwareTables
|
||||
GetSystemFirmwareTable
|
||||
STORAGE_PROTOCOL_TYPE
|
||||
STORAGE_BUS_TYPE
|
||||
STORAGE_PROTOCOL_NVME_DATA_TYPE
|
||||
STORAGE_PROPERTY_ID
|
||||
STORAGE_PROTOCOL_SPECIFIC_DATA
|
||||
STORAGE_QUERY_TYPE
|
||||
STORAGE_DESCRIPTOR_HEADER
|
||||
STORAGE_DEVICE_DESCRIPTOR
|
||||
STORAGE_PROPERTY_QUERY
|
||||
STORAGE_PROTOCOL_SPECIFIC_DATA
|
||||
STORAGE_PROTOCOL_DATA_DESCRIPTOR
|
||||
SRB_IO_CONTROL
|
||||
SCSI_PASS_THROUGH
|
||||
NVME_LOG_PAGES
|
||||
NVME_POWER_STATE_DESC
|
||||
NVME_HEALTH_INFO_LOG
|
||||
NVME_IDENTIFY_CONTROLLER_DATA
|
||||
DISK_PERFORMANCE
|
||||
DRIVERSTATUS
|
||||
SENDCMDINPARAMS
|
||||
SENDCMDOUTPARAMS
|
||||
IDEREGS
|
||||
BATTERY_QUERY_INFORMATION
|
||||
BATTERY_INFORMATION
|
||||
BATTERY_SYSTEM_BATTERY
|
||||
BATTERY_STATUS
|
||||
BATTERY_WAIT_STATUS
|
||||
SMART_SEND_DRIVE_COMMAND
|
||||
SMART_RCV_DRIVE_DATA
|
||||
CreateFile
|
||||
VirtualFree
|
||||
VirtualAlloc
|
||||
@@ -64,10 +41,4 @@ IOCTL_*
|
||||
LoadLibrary
|
||||
FreeLibrary
|
||||
GetProcAddress
|
||||
SCSI_IOCTL_*
|
||||
SMART_CMD
|
||||
BATTERY_UNKNOWN_*
|
||||
SMART_CYL_LOW
|
||||
SMART_CYL_HI
|
||||
READ_ATTRIBUTES
|
||||
READ_THRESHOLDS
|
||||
|
||||
Reference in New Issue
Block a user