mirror of
https://github.com/openhardwaremonitor/openhardwaremonitor
synced 2025-08-29 13:28:04 +00:00
Updated attribute maps for the 4 SSD controllers we figured out so far.
Also switched back to an array of DriveAttribute rather than a generic list.
This commit is contained in:
parent
22d714c976
commit
ecefde3ac4
@ -119,30 +119,29 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
|
|
||||||
public void Update() {
|
public void Update() {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
List<SMART.DriveAttribute> attributes = SMART.ReadSmart(handle, drive);
|
SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
|
||||||
|
|
||||||
if (temperatureID != SMART.AttributeID.None &&
|
if (temperatureID != SMART.AttributeID.None &&
|
||||||
attributes.Exists(attr => attr.ID == temperatureID))
|
Array.Exists(attributes, attr => attr.ID == temperatureID))
|
||||||
{
|
{
|
||||||
temperatureSensor.Value = attributes
|
temperatureSensor.Value = Array
|
||||||
.Find(attr => attr.ID == temperatureID)
|
.Find(attributes, attr => attr.ID == temperatureID)
|
||||||
.RawValue[0];
|
.RawValue[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lifeID != SMART.AttributeID.None &&
|
if (lifeID != SMART.AttributeID.None &&
|
||||||
attributes.Exists(attr => attr.ID == lifeID))
|
Array.Exists(attributes, attr => attr.ID == lifeID))
|
||||||
{
|
{
|
||||||
lifeSensor.Value = attributes
|
lifeSensor.Value = Array
|
||||||
.Find(attr => attr.ID == lifeID)
|
.Find(attributes, attr => attr.ID == lifeID)
|
||||||
.AttrValue;
|
.AttrValue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (temperatureID != SMART.AttributeID.None) {
|
if (temperatureID != SMART.AttributeID.None)
|
||||||
temperatureSensor.Value = temperatureSensor.Value;
|
temperatureSensor.Value = temperatureSensor.Value;
|
||||||
}
|
|
||||||
|
|
||||||
if (lifeID != SMART.AttributeID.None) {
|
if (lifeID != SMART.AttributeID.None)
|
||||||
lifeSensor.Value = lifeSensor.Value;
|
lifeSensor.Value = lifeSensor.Value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
count++; count %= UPDATE_DIVIDER;
|
count++; count %= UPDATE_DIVIDER;
|
||||||
|
@ -68,10 +68,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SMART.DriveAttribute> attributes =
|
SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
|
||||||
new List<SMART.DriveAttribute>(SMART.ReadSmart(handle, drive));
|
|
||||||
|
|
||||||
if (!(attributes.Count > 0)) {
|
if (attributes.Length < 1) {
|
||||||
SMART.CloseHandle(handle);
|
SMART.CloseHandle(handle);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -95,28 +94,29 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SMART.AttributeID GetSSDLifeID(List<SMART.DriveAttribute> attributes) {
|
private SMART.AttributeID GetSSDLifeID(SMART.DriveAttribute[] attributes) {
|
||||||
// ID E9 is present on Intel, JM, SF and Samsung
|
// ID E9 is present on Intel, JM, SF and Samsung (different meanings)
|
||||||
// ID D2 is present on Indilinx
|
// ID D2 is present on Indilinx
|
||||||
// Neither ID has been found on a mechanical hard drive (yet),
|
// Neither ID has been found on a mechanical hard drive (yet),
|
||||||
// So this seems like a good way to check if it's an SSD.
|
// so this seems like a good way to check if it's an SSD.
|
||||||
bool isKnownSSD = (
|
bool isKnownSSD = (
|
||||||
attributes.Exists(attr => attr.ID == new SMART.AttributeID(0xE9)) ||
|
Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xE9)) ||
|
||||||
attributes.Exists(attr => attr.ID == new SMART.AttributeID(0xD2))
|
Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xD2))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isKnownSSD) return SMART.AttributeID.None;
|
if (!isKnownSSD) return SMART.AttributeID.None;
|
||||||
|
|
||||||
// We start with a traditional loop, because there are 4 unique ID's
|
// We start with a traditional loop, because there are 4 unique ID's
|
||||||
// that potentially identify one of the vendors
|
// that potentially identify one of the vendors
|
||||||
for (int i = 0; i < attributes.Count; i++) {
|
for (int i = 0; i < attributes.Length; i++) {
|
||||||
|
|
||||||
if (attributes[i].ID == SMART.SamsungAttributes.RemainingLife)
|
if (attributes[i].ID == SMART.SamsungAttributes.RemainingLife)
|
||||||
return SMART.SamsungAttributes.RemainingLife;
|
return SMART.SamsungAttributes.RemainingLife;
|
||||||
else if (attributes[i].ID == new SMART.AttributeID(0xAB))
|
|
||||||
|
if (attributes[i].ID == SMART.SandForceAttributes.ProgramFailCount)
|
||||||
return SMART.SandForceAttributes.RemainingLife;
|
return SMART.SandForceAttributes.RemainingLife;
|
||||||
else if (attributes[i].ID == new SMART.AttributeID(0xD2))
|
|
||||||
return SMART.IndilinxAttributes.RemainingLife;
|
if (attributes[i].ID == SMART.IndilinxAttributes.UnknownUnique)
|
||||||
|
return SMART.IndilinxAttributes.RemainingLife;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Find out JMicron's Life attribute ID; their unique ID = 0xE4
|
// TODO: Find out JMicron's Life attribute ID; their unique ID = 0xE4
|
||||||
@ -126,9 +126,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
// is whether we can find all 3; pointless to use Exists()
|
// is whether we can find all 3; pointless to use Exists()
|
||||||
int intelRegisterCount = 0;
|
int intelRegisterCount = 0;
|
||||||
foreach (SMART.DriveAttribute attribute in attributes) {
|
foreach (SMART.DriveAttribute attribute in attributes) {
|
||||||
if (attribute.ID == new SMART.AttributeID(0xE1) ||
|
if (attribute.ID == SMART.IntelAttributes.HostWrites ||
|
||||||
attribute.ID == new SMART.AttributeID(0xE8) ||
|
attribute.ID == SMART.IntelAttributes.RemainingLife ||
|
||||||
attribute.ID == new SMART.AttributeID(0xE9)
|
attribute.ID == SMART.IntelAttributes.MediaWearOutIndicator
|
||||||
)
|
)
|
||||||
intelRegisterCount++;
|
intelRegisterCount++;
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SMART.AttributeID GetTemperatureIndex(
|
private SMART.AttributeID GetTemperatureIndex(
|
||||||
List<SMART.DriveAttribute> attributes)
|
SMART.DriveAttribute[] attributes)
|
||||||
{
|
{
|
||||||
SMART.AttributeID[] validIds = new[] {
|
SMART.AttributeID[] validIds = new[] {
|
||||||
SMART.CommonAttributes.Temperature,
|
SMART.CommonAttributes.Temperature,
|
||||||
@ -149,7 +149,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
|
|
||||||
foreach (SMART.AttributeID validId in validIds) {
|
foreach (SMART.AttributeID validId in validIds) {
|
||||||
SMART.AttributeID id = validId;
|
SMART.AttributeID id = validId;
|
||||||
if (attributes.Exists(attr => attr.ID == id))
|
if (Array.Exists(attributes, attr => attr.ID == id))
|
||||||
return validId;
|
return validId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,9 +188,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SMART.DriveAttribute> attributes = SMART.ReadSmart(handle, drive);
|
SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
|
||||||
|
|
||||||
if (attributes != null) {
|
if (attributes.Length > 0) {
|
||||||
r.AppendLine("Drive name: " + name);
|
r.AppendLine("Drive name: " + name);
|
||||||
r.AppendLine();
|
r.AppendLine();
|
||||||
r.AppendFormat(CultureInfo.InvariantCulture, " {0}{1}{2}{3}{4}{5}",
|
r.AppendFormat(CultureInfo.InvariantCulture, " {0}{1}{2}{3}{4}{5}",
|
||||||
|
@ -81,72 +81,160 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
public static readonly AttributeID None = new AttributeID(0x00);
|
public static readonly AttributeID None = new AttributeID(0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common SMART attributes
|
// These are the more-or-less standard S.M.A.R.T attributes
|
||||||
public static class CommonAttributes {
|
// TODO: Filter out unused/obscure ones; some are interpreted differently
|
||||||
public static readonly AttributeID
|
// between manufacturers
|
||||||
ReadErrorRate = new AttributeID(0x01);
|
public static class CommonAttributes {
|
||||||
public static readonly AttributeID
|
public static readonly AttributeID
|
||||||
ThroughputPerformance = new AttributeID(0x02);
|
ReadErrorRate = new AttributeID(0x01),
|
||||||
public static readonly AttributeID
|
ThroughputPerformance = new AttributeID(0x02),
|
||||||
SpinUpTime = new AttributeID(0x03);
|
SpinUpTime = new AttributeID(0x03),
|
||||||
public static readonly AttributeID
|
StartStopCount = new AttributeID(0x04),
|
||||||
StartStopCount = new AttributeID(0x04);
|
ReallocatedSectorsCount = new AttributeID(0x05),
|
||||||
public static readonly AttributeID
|
ReadChannelMargin = new AttributeID(0x06),
|
||||||
ReallocatedSectorsCount = new AttributeID(0x05);
|
SeekErrorRate = new AttributeID(0x07),
|
||||||
public static readonly AttributeID
|
SeekTimePerformance = new AttributeID(0x08),
|
||||||
ReadChannelMargin = new AttributeID(0x06);
|
PowerOnHours = new AttributeID(0x09),
|
||||||
public static readonly AttributeID
|
SpinRetryCount = new AttributeID(0x0A),
|
||||||
SeekErrorRate = new AttributeID(0x07);
|
RecalibrationRetries = new AttributeID(0x0B),
|
||||||
public static readonly AttributeID
|
PowerCycleCount = new AttributeID(0x0C),
|
||||||
SeekTimePerformance = new AttributeID(0x08);
|
SoftReadErrorRate = new AttributeID(0x0D),
|
||||||
public static readonly AttributeID
|
SataDownshiftErrorCount = new AttributeID(0xB7),
|
||||||
PowerOnHours = new AttributeID(0x09);
|
EndToEndError = new AttributeID(0xB8),
|
||||||
public static readonly AttributeID
|
HeadStability = new AttributeID(0xB9),
|
||||||
SpinRetryCount = new AttributeID(0x0A);
|
InducedOpVibrationDetection = new AttributeID(0xBA),
|
||||||
public static readonly AttributeID
|
ReportedUncorrectableErrors = new AttributeID(0xBB),
|
||||||
RecalibrationRetries = new AttributeID(0x0B);
|
CommandTimeout = new AttributeID(0xBC),
|
||||||
public static readonly AttributeID
|
HighFlyWrites = new AttributeID(0xBD),
|
||||||
PowerCycleCount = new AttributeID(0x0C);
|
AirflowTemperature = new AttributeID(0xBE),
|
||||||
public static readonly AttributeID
|
GSenseErrorRate = new AttributeID(0xBF),
|
||||||
SoftReadErrorRate = new AttributeID(0x0D);
|
PowerOffRetractCount = new AttributeID(0xC0),
|
||||||
public static readonly AttributeID
|
LoadCycleCount = new AttributeID(0xC1),
|
||||||
AirflowTemperature = new AttributeID(0xBE);
|
Temperature = new AttributeID(0xC2),
|
||||||
public static readonly AttributeID
|
HardwareEccRecovered = new AttributeID(0xC3),
|
||||||
Temperature = new AttributeID(0xC2);
|
ReallocationEventCount = new AttributeID(0xC4),
|
||||||
public static readonly AttributeID
|
CurrentPendingSectorCount = new AttributeID(0xC5),
|
||||||
HardwareECCRecovered = new AttributeID(0xC3);
|
UncorrectableSectorCount = new AttributeID(0xC6),
|
||||||
public static readonly AttributeID
|
UltraDmaCrcErrorCount = new AttributeID(0xC7),
|
||||||
ReallocationEventCount = new AttributeID(0xC4);
|
WriteErrorRate = new AttributeID(0xC8),
|
||||||
public static readonly AttributeID
|
DataAddressMarkerrors = new AttributeID(0xCA),
|
||||||
CurrentPendingSectorCount = new AttributeID(0xC5);
|
RunOutCancel = new AttributeID(0xCB),
|
||||||
public static readonly AttributeID
|
SoftEccCorrection = new AttributeID(0xCC),
|
||||||
UncorrectableSectorCount = new AttributeID(0xC6);
|
ThermalAsperityRate = new AttributeID(0xCD),
|
||||||
public static readonly AttributeID
|
FlyingHeight = new AttributeID(0xCE),
|
||||||
UltraDMACRCErrorCount = new AttributeID(0xC7);
|
SpinHighCurrent = new AttributeID(0xCF),
|
||||||
public static readonly AttributeID
|
SpinBuzz = new AttributeID(0xD0),
|
||||||
WriteErrorRate = new AttributeID(0xC8);
|
OfflineSeekPerformance = new AttributeID(0xD1),
|
||||||
public static readonly AttributeID
|
VibrationDuringWrite = new AttributeID(0xD3),
|
||||||
DriveTemperature = new AttributeID(0xE7);
|
ShockDuringWrite = new AttributeID(0xD4),
|
||||||
|
DiskShift = new AttributeID(0xDC),
|
||||||
|
GSenseErrorRateAlt = new AttributeID(0xDD), // Alternative to 0xBF
|
||||||
|
LoadedHours = new AttributeID(0xDE),
|
||||||
|
LoadUnloadRetryCount = new AttributeID(0xDF),
|
||||||
|
LoadFriction = new AttributeID(0xE0),
|
||||||
|
LoadUnloadCycleCount = new AttributeID(0xE1),
|
||||||
|
LoadInTime = new AttributeID(0xE2),
|
||||||
|
TorqueAmplificationCount = new AttributeID(0xE3),
|
||||||
|
PowerOffRetractCycle = new AttributeID(0xE4),
|
||||||
|
GMRHeadAmplitude = new AttributeID(0xE6),
|
||||||
|
DriveTemperature = new AttributeID(0xE7),
|
||||||
|
HeadFlyingHours = new AttributeID(0xF0),
|
||||||
|
LBAsWrittenTotal = new AttributeID(0xF1),
|
||||||
|
LBAsReadTotal = new AttributeID(0xF2),
|
||||||
|
ReadErrorRetryRate = new AttributeID(0xFA),
|
||||||
|
FreeFallProtection = new AttributeID(0xFE)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indilinx SSD SMART attributes
|
// Indilinx SSD SMART attributes
|
||||||
public static class IndilinxAttributes {
|
// TODO: Find out the purpose of attribute 0xD2
|
||||||
public static readonly AttributeID RemainingLife = new AttributeID(0xD1);
|
// Seems to be unique to Indilinx drives, hence its name of UnknownUnique.
|
||||||
|
public static class IndilinxAttributes {
|
||||||
|
public static readonly AttributeID
|
||||||
|
ReadErrorRate = CommonAttributes.ReadErrorRate,
|
||||||
|
PowerOnHours = CommonAttributes.PowerOnHours,
|
||||||
|
PowerCycleCount = CommonAttributes.PowerCycleCount,
|
||||||
|
InitialBadBlockCount = new AttributeID(0xB8),
|
||||||
|
RemainingLife = new AttributeID(0xD1),
|
||||||
|
ProgramFailure = new AttributeID(0xC3),
|
||||||
|
EraseFailure = new AttributeID(0xC4),
|
||||||
|
ReadFailure = new AttributeID(0xC5),
|
||||||
|
SectorsRead = new AttributeID(0xC6),
|
||||||
|
SectorsWritten = new AttributeID(0xC7),
|
||||||
|
ReadCommands = new AttributeID(0xC8),
|
||||||
|
WriteCommands = new AttributeID(0xC9),
|
||||||
|
BitErrors = new AttributeID(0xCA),
|
||||||
|
CorrectedErrors = new AttributeID(0xCB),
|
||||||
|
BadBlockFullFlag = new AttributeID(0xCC),
|
||||||
|
MaxCellcycles = new AttributeID(0xCD),
|
||||||
|
MinErase = new AttributeID(0xCE),
|
||||||
|
MaxErase = new AttributeID(0xCF),
|
||||||
|
AverageEraseCount = new AttributeID(0xD0),
|
||||||
|
UnknownUnique = new AttributeID(0xD2),
|
||||||
|
SataErrorCountCRC = new AttributeID(0xD3),
|
||||||
|
SataErrorCountHandshake = new AttributeID(0xD4)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intel SSD SMART attributes
|
// Intel SSD SMART attributes
|
||||||
public static class IntelAttributes {
|
// TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4
|
||||||
public static readonly AttributeID RemainingLife = new AttributeID(0xE8);
|
public static class IntelAttributes {
|
||||||
|
public static readonly AttributeID
|
||||||
|
ReadErrorRate = CommonAttributes.ReadErrorRate,
|
||||||
|
SpinUpTime = CommonAttributes.SpinUpTime,
|
||||||
|
StartStopCount = CommonAttributes.StartStopCount,
|
||||||
|
ReallocatedSectorsCount = CommonAttributes.ReallocatedSectorsCount,
|
||||||
|
PowerOnHours = CommonAttributes.PowerOnHours,
|
||||||
|
PowerCycleCount = CommonAttributes.PowerCycleCount,
|
||||||
|
EndToEndError = CommonAttributes.EndToEndError, // Only on G2 drives!
|
||||||
|
|
||||||
|
// Different from the common attribute PowerOffRetractCount, same ID
|
||||||
|
UnsafeShutdownCount = new AttributeID(0xC0),
|
||||||
|
HostWrites = new AttributeID(0xE1),
|
||||||
|
RemainingLife = new AttributeID(0xE8),
|
||||||
|
MediaWearOutIndicator = new AttributeID(0xE9)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Samsung SSD SMART attributes
|
// Samsung SSD SMART attributes
|
||||||
public static class SamsungAttributes {
|
// TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9
|
||||||
public static readonly AttributeID RemainingLife = new AttributeID(0xB4);
|
public static class SamsungAttributes {
|
||||||
|
public static readonly AttributeID
|
||||||
|
PowerOnHours = CommonAttributes.PowerOnHours,
|
||||||
|
PowerCycleCount = CommonAttributes.PowerCycleCount,
|
||||||
|
UsedReservedBlockCountChip = new AttributeID(0xB2), // Unique
|
||||||
|
UsedReservedBlockCountTotal = new AttributeID(0xB3), // Unique
|
||||||
|
RemainingLife = new AttributeID(0xB4), // Unique
|
||||||
|
RuntimeBadBlockTotal = new AttributeID(0xB7)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SandForce SSD SMART attributes
|
// SandForce SSD SMART attributes
|
||||||
public static class SandForceAttributes {
|
// Note: 0xE9 and 0xEA are reserved attributes and unique
|
||||||
public static readonly AttributeID RemainingLife = new AttributeID(0xE7);
|
public static class SandForceAttributes {
|
||||||
|
public static readonly AttributeID
|
||||||
|
ReadErrorRate = CommonAttributes.ReadErrorRate,
|
||||||
|
RetiredBlockCount = new AttributeID(0x05),
|
||||||
|
PowerOnHours = CommonAttributes.PowerOnHours,
|
||||||
|
PowerCycleCount = CommonAttributes.PowerCycleCount,
|
||||||
|
ProgramFailCount = new AttributeID(0xAB), // Unique
|
||||||
|
EraseFailCount = new AttributeID(0xAC), // Unique
|
||||||
|
UnexpectedPowerLossCount = new AttributeID(0xAE), // Unique
|
||||||
|
WearRangeDelta = new AttributeID(0xB1), // Unique
|
||||||
|
ProgramFailCountAlt = new AttributeID(0xB5), // Same as 0xAB
|
||||||
|
EraseFailCountAlt = new AttributeID(0xB6), // Same as 0xAC
|
||||||
|
ReportedUncorrectableErrors =
|
||||||
|
CommonAttributes.ReportedUncorrectableErrors,
|
||||||
|
|
||||||
|
Temperature = CommonAttributes.Temperature, // SF-1500 only!
|
||||||
|
|
||||||
|
// Opposite of the common attribute HardwareECCRecovered
|
||||||
|
UnrecoverableECC = new AttributeID(0xC3),
|
||||||
|
ReallocationEventCount = new AttributeID(0xC4),
|
||||||
|
RemainingLife = new AttributeID(0xE7),
|
||||||
|
LifetimeWrites = new AttributeID(0xF1),
|
||||||
|
LifetimeReads = new AttributeID(0xF2)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
@ -329,7 +417,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
IntPtr.Zero);
|
IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<DriveAttribute> ReadSmart(IntPtr handle,
|
public static DriveAttribute[] ReadSmart(IntPtr handle,
|
||||||
int driveNumber)
|
int driveNumber)
|
||||||
{
|
{
|
||||||
DriveCommandParameter parameter = new DriveCommandParameter();
|
DriveCommandParameter parameter = new DriveCommandParameter();
|
||||||
@ -347,9 +435,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
|||||||
out result, Marshal.SizeOf(typeof(DriveSmartReadResult)),
|
out result, Marshal.SizeOf(typeof(DriveSmartReadResult)),
|
||||||
out bytesReturned, IntPtr.Zero);
|
out bytesReturned, IntPtr.Zero);
|
||||||
|
|
||||||
return (isValid)
|
return (isValid) ? result.Attributes : new DriveAttribute[0];
|
||||||
? new List<DriveAttribute>(result.Attributes)
|
|
||||||
: new List<DriveAttribute>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadName(IntPtr handle, int driveNumber) {
|
public static string ReadName(IntPtr handle, int driveNumber) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user