Files
CarlMonitor/LibreHardwareMonitor-0.9.4/LibreHardwareMonitorLib/Hardware/Storage/NVMeSmart.cs
2025-04-07 07:44:27 -07:00

190 lines
5.9 KiB
C#

// 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 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 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();
}
}
// Test Windows generic driver protocol.
if (NVMeDrive == null)
{
_handle = NVMeWindows.IdentifyDevice(storageInfo);
if (_handle != null)
{
NVMeDrive = new NVMeWindows();
}
}
}
public bool IsValid
{
get
{
return _handle is { IsInvalid: false };
}
}
internal INVMeDrive NVMeDrive { get; }
public void Dispose()
{
Close();
}
private static string GetString(byte[] s)
{
return Encoding.ASCII.GetString(s).Trim('\t', '\n', '\r', ' ', '\0');
}
private static short KelvinToCelsius(ushort k)
{
return (short)(k > 0 ? k - 273 : short.MinValue);
}
private static short KelvinToCelsius(byte[] k)
{
return KelvinToCelsius(BitConverter.ToUInt16(k, 0));
}
public void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing && _handle is { IsClosed: false })
{
_handle.Close();
}
}
public Storage.NVMeInfo GetInfo()
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_IDENTIFY_CONTROLLER_DATA();
if (NVMeDrive != null)
valid = NVMeDrive.IdentifyController(_handle, out data);
if (!valid)
return null;
return new NVMeInfo(_driveNumber, data);
}
public Storage.NVMeHealthInfo GetHealthInfo()
{
if (_handle?.IsClosed != false)
return null;
bool valid = false;
var data = new Kernel32.NVME_HEALTH_INFO_LOG();
if (NVMeDrive != null)
valid = NVMeDrive.HealthInfoLog(_handle, out data);
if (!valid)
return null;
return new NVMeHealthInfo(data);
}
private class NVMeInfo : Storage.NVMeInfo
{
public NVMeInfo(int index, Kernel32.NVME_IDENTIFY_CONTROLLER_DATA data)
{
Index = index;
VID = data.VID;
SSVID = data.SSVID;
Serial = GetString(data.SN);
Model = GetString(data.MN);
Revision = GetString(data.FR);
IEEE = data.IEEE;
TotalCapacity = BitConverter.ToUInt64(data.TNVMCAP, 0); // 128bit little endian
UnallocatedCapacity = BitConverter.ToUInt64(data.UNVMCAP, 0);
ControllerId = data.CNTLID;
NumberNamespaces = data.NN;
}
}
private class NVMeHealthInfo : Storage.NVMeHealthInfo
{
public NVMeHealthInfo(Kernel32.NVME_HEALTH_INFO_LOG log)
{
CriticalWarning = (Kernel32.NVME_CRITICAL_WARNING)log.CriticalWarning;
Temperature = KelvinToCelsius(log.CompositeTemp);
AvailableSpare = log.AvailableSpare;
AvailableSpareThreshold = log.AvailableSpareThreshold;
PercentageUsed = log.PercentageUsed;
DataUnitRead = BitConverter.ToUInt64(log.DataUnitRead, 0);
DataUnitWritten = BitConverter.ToUInt64(log.DataUnitWritten, 0);
HostReadCommands = BitConverter.ToUInt64(log.HostReadCommands, 0);
HostWriteCommands = BitConverter.ToUInt64(log.HostWriteCommands, 0);
ControllerBusyTime = BitConverter.ToUInt64(log.ControllerBusyTime, 0);
PowerCycle = BitConverter.ToUInt64(log.PowerCycles, 0);
PowerOnHours = BitConverter.ToUInt64(log.PowerOnHours, 0);
UnsafeShutdowns = BitConverter.ToUInt64(log.UnsafeShutdowns, 0);
MediaErrors = BitConverter.ToUInt64(log.MediaAndDataIntegrityErrors, 0);
ErrorInfoLogEntryCount = BitConverter.ToUInt64(log.NumberErrorInformationLogEntries, 0);
WarningCompositeTemperatureTime = log.WarningCompositeTemperatureTime;
CriticalCompositeTemperatureTime = log.CriticalCompositeTemperatureTime;
TemperatureSensors = new short[log.TemperatureSensor.Length];
for (int i = 0; i < TemperatureSensors.Length; i++)
TemperatureSensors[i] = KelvinToCelsius(log.TemperatureSensor[i]);
}
}
}