diff --git a/LibreHardwareMonitorLib/Hardware/Storage/WindowsSmart.cs b/LibreHardwareMonitorLib/Hardware/Storage/WindowsSmart.cs
index 9a0f339..52125ed 100644
--- a/LibreHardwareMonitorLib/Hardware/Storage/WindowsSmart.cs
+++ b/LibreHardwareMonitorLib/Hardware/Storage/WindowsSmart.cs
@@ -119,6 +119,52 @@ namespace LibreHardwareMonitor.Hardware.Storage
return true;
}
+ ///
+ /// Reads Smart health status of the drive
+ ///
+ /// True, if drive is healthy; False, if unhealthy; Null, if it cannot be read
+ public bool? ReadSmartHealth()
+ {
+ if (_handle.IsClosed)
+ throw new ObjectDisposedException(nameof(WindowsSmart));
+
+ var parameter = new Kernel32.SENDCMDINPARAMS
+ {
+ bDriveNumber = (byte)_driveNumber,
+ irDriveRegs = {
+ bFeaturesReg = Kernel32.SMART_FEATURES.RETURN_SMART_STATUS,
+ bCylLowReg = Kernel32.SMART_LBA_MID,
+ bCylHighReg = Kernel32.SMART_LBA_HI,
+ bCommandReg = Kernel32.ATA_COMMAND.ATA_SMART
+ }
+ };
+
+ bool isValid = Kernel32.DeviceIoControl(_handle, Kernel32.DFP.DFP_SEND_DRIVE_COMMAND, ref parameter, Marshal.SizeOf(parameter),
+ out Kernel32.STATUSCMDOUTPARAMS result, Marshal.SizeOf(), out _, IntPtr.Zero);
+
+ if (!isValid)
+ {
+ return null;
+ }
+
+ // reference: https://github.com/smartmontools/smartmontools/blob/master/smartmontools/atacmds.cpp
+ if (Kernel32.SMART_LBA_HI == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID == result.irDriveRegs.bCylLowReg)
+ {
+ // high and mid registers are unchanged, which means that the drive is healthy
+ return true;
+ }
+ else if (Kernel32.SMART_LBA_HI_EXCEEDED == result.irDriveRegs.bCylHighReg && Kernel32.SMART_LBA_MID_EXCEEDED == result.irDriveRegs.bCylLowReg)
+ {
+ // high and mid registers are exceeded, which means that the drive is unhealthy
+ return false;
+ }
+ else
+ {
+ // response is not clear
+ return null;
+ }
+ }
+
protected void Dispose(bool disposing)
{
if (disposing)
diff --git a/LibreHardwareMonitorLib/Interop/Kernel32.cs b/LibreHardwareMonitorLib/Interop/Kernel32.cs
index 187d41f..5f37e5b 100644
--- a/LibreHardwareMonitorLib/Interop/Kernel32.cs
+++ b/LibreHardwareMonitorLib/Interop/Kernel32.cs
@@ -24,6 +24,8 @@ namespace LibreHardwareMonitor.Interop
internal const uint NVME_PASS_THROUGH_SRB_IO_CODE = 0xe0002000;
internal const byte SMART_LBA_HI = 0xC2;
internal const byte SMART_LBA_MID = 0x4F;
+ internal const byte SMART_LBA_HI_EXCEEDED = 0x2C;
+ internal const byte SMART_LBA_MID_EXCEEDED = 0xF4;
private const string DllName = "kernel32.dll";
[Flags]
@@ -149,6 +151,19 @@ namespace LibreHardwareMonitor.Interop
out uint lpBytesReturned,
IntPtr lpOverlapped);
+ [DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool DeviceIoControl
+ (
+ SafeHandle hDevice,
+ DFP dwIoControlCode,
+ ref SENDCMDINPARAMS lpInBuffer,
+ int nInBufferSize,
+ out STATUSCMDOUTPARAMS lpOutBuffer,
+ int nOutBufferSize,
+ out uint lpBytesReturned,
+ IntPtr lpOverlapped);
+
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl
@@ -589,6 +604,14 @@ namespace LibreHardwareMonitor.Interop
public SMART_THRESHOLD[] Thresholds;
}
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal struct STATUSCMDOUTPARAMS
+ {
+ public uint cBufferSize;
+ public DRIVERSTATUS DriverStatus;
+ public IDEREGS irDriveRegs;
+ }
+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct IDENTIFY_DATA
{