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() {
|
||||
if (count == 0) {
|
||||
List<SMART.DriveAttribute> attributes = SMART.ReadSmart(handle, drive);
|
||||
SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
|
||||
|
||||
if (temperatureID != SMART.AttributeID.None &&
|
||||
attributes.Exists(attr => attr.ID == temperatureID))
|
||||
Array.Exists(attributes, attr => attr.ID == temperatureID))
|
||||
{
|
||||
temperatureSensor.Value = attributes
|
||||
.Find(attr => attr.ID == temperatureID)
|
||||
temperatureSensor.Value = Array
|
||||
.Find(attributes, attr => attr.ID == temperatureID)
|
||||
.RawValue[0];
|
||||
}
|
||||
|
||||
if (lifeID != SMART.AttributeID.None &&
|
||||
attributes.Exists(attr => attr.ID == lifeID))
|
||||
Array.Exists(attributes, attr => attr.ID == lifeID))
|
||||
{
|
||||
lifeSensor.Value = attributes
|
||||
.Find(attr => attr.ID == lifeID)
|
||||
lifeSensor.Value = Array
|
||||
.Find(attributes, attr => attr.ID == lifeID)
|
||||
.AttrValue;
|
||||
}
|
||||
} else {
|
||||
if (temperatureID != SMART.AttributeID.None) {
|
||||
if (temperatureID != SMART.AttributeID.None)
|
||||
temperatureSensor.Value = temperatureSensor.Value;
|
||||
}
|
||||
|
||||
if (lifeID != SMART.AttributeID.None) {
|
||||
if (lifeID != SMART.AttributeID.None)
|
||||
lifeSensor.Value = lifeSensor.Value;
|
||||
}
|
||||
}
|
||||
|
||||
count++; count %= UPDATE_DIVIDER;
|
||||
|
@ -68,10 +68,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<SMART.DriveAttribute> attributes =
|
||||
new List<SMART.DriveAttribute>(SMART.ReadSmart(handle, drive));
|
||||
SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
|
||||
|
||||
if (!(attributes.Count > 0)) {
|
||||
if (attributes.Length < 1) {
|
||||
SMART.CloseHandle(handle);
|
||||
continue;
|
||||
}
|
||||
@ -95,27 +94,28 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
}
|
||||
}
|
||||
|
||||
private SMART.AttributeID GetSSDLifeID(List<SMART.DriveAttribute> attributes) {
|
||||
// ID E9 is present on Intel, JM, SF and Samsung
|
||||
private SMART.AttributeID GetSSDLifeID(SMART.DriveAttribute[] attributes) {
|
||||
// ID E9 is present on Intel, JM, SF and Samsung (different meanings)
|
||||
// ID D2 is present on Indilinx
|
||||
// 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 = (
|
||||
attributes.Exists(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(0xE9)) ||
|
||||
Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xD2))
|
||||
);
|
||||
|
||||
if (!isKnownSSD) return SMART.AttributeID.None;
|
||||
|
||||
// We start with a traditional loop, because there are 4 unique ID's
|
||||
// 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)
|
||||
return SMART.SamsungAttributes.RemainingLife;
|
||||
else if (attributes[i].ID == new SMART.AttributeID(0xAB))
|
||||
|
||||
if (attributes[i].ID == SMART.SandForceAttributes.ProgramFailCount)
|
||||
return SMART.SandForceAttributes.RemainingLife;
|
||||
else if (attributes[i].ID == new SMART.AttributeID(0xD2))
|
||||
|
||||
if (attributes[i].ID == SMART.IndilinxAttributes.UnknownUnique)
|
||||
return SMART.IndilinxAttributes.RemainingLife;
|
||||
}
|
||||
|
||||
@ -126,9 +126,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
// is whether we can find all 3; pointless to use Exists()
|
||||
int intelRegisterCount = 0;
|
||||
foreach (SMART.DriveAttribute attribute in attributes) {
|
||||
if (attribute.ID == new SMART.AttributeID(0xE1) ||
|
||||
attribute.ID == new SMART.AttributeID(0xE8) ||
|
||||
attribute.ID == new SMART.AttributeID(0xE9)
|
||||
if (attribute.ID == SMART.IntelAttributes.HostWrites ||
|
||||
attribute.ID == SMART.IntelAttributes.RemainingLife ||
|
||||
attribute.ID == SMART.IntelAttributes.MediaWearOutIndicator
|
||||
)
|
||||
intelRegisterCount++;
|
||||
}
|
||||
@ -139,7 +139,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
}
|
||||
|
||||
private SMART.AttributeID GetTemperatureIndex(
|
||||
List<SMART.DriveAttribute> attributes)
|
||||
SMART.DriveAttribute[] attributes)
|
||||
{
|
||||
SMART.AttributeID[] validIds = new[] {
|
||||
SMART.CommonAttributes.Temperature,
|
||||
@ -149,7 +149,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
|
||||
foreach (SMART.AttributeID validId in validIds) {
|
||||
SMART.AttributeID id = validId;
|
||||
if (attributes.Exists(attr => attr.ID == id))
|
||||
if (Array.Exists(attributes, attr => attr.ID == id))
|
||||
return validId;
|
||||
}
|
||||
|
||||
@ -188,9 +188,9 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
// Common SMART attributes
|
||||
// These are the more-or-less standard S.M.A.R.T attributes
|
||||
// TODO: Filter out unused/obscure ones; some are interpreted differently
|
||||
// between manufacturers
|
||||
public static class CommonAttributes {
|
||||
public static readonly AttributeID
|
||||
ReadErrorRate = new AttributeID(0x01);
|
||||
public static readonly AttributeID
|
||||
ThroughputPerformance = new AttributeID(0x02);
|
||||
public static readonly AttributeID
|
||||
SpinUpTime = new AttributeID(0x03);
|
||||
public static readonly AttributeID
|
||||
StartStopCount = new AttributeID(0x04);
|
||||
public static readonly AttributeID
|
||||
ReallocatedSectorsCount = new AttributeID(0x05);
|
||||
public static readonly AttributeID
|
||||
ReadChannelMargin = new AttributeID(0x06);
|
||||
public static readonly AttributeID
|
||||
SeekErrorRate = new AttributeID(0x07);
|
||||
public static readonly AttributeID
|
||||
SeekTimePerformance = new AttributeID(0x08);
|
||||
public static readonly AttributeID
|
||||
PowerOnHours = new AttributeID(0x09);
|
||||
public static readonly AttributeID
|
||||
SpinRetryCount = new AttributeID(0x0A);
|
||||
public static readonly AttributeID
|
||||
RecalibrationRetries = new AttributeID(0x0B);
|
||||
public static readonly AttributeID
|
||||
PowerCycleCount = new AttributeID(0x0C);
|
||||
public static readonly AttributeID
|
||||
SoftReadErrorRate = new AttributeID(0x0D);
|
||||
public static readonly AttributeID
|
||||
AirflowTemperature = new AttributeID(0xBE);
|
||||
public static readonly AttributeID
|
||||
Temperature = new AttributeID(0xC2);
|
||||
public static readonly AttributeID
|
||||
HardwareECCRecovered = new AttributeID(0xC3);
|
||||
public static readonly AttributeID
|
||||
ReallocationEventCount = new AttributeID(0xC4);
|
||||
public static readonly AttributeID
|
||||
CurrentPendingSectorCount = new AttributeID(0xC5);
|
||||
public static readonly AttributeID
|
||||
UncorrectableSectorCount = new AttributeID(0xC6);
|
||||
public static readonly AttributeID
|
||||
UltraDMACRCErrorCount = new AttributeID(0xC7);
|
||||
public static readonly AttributeID
|
||||
WriteErrorRate = new AttributeID(0xC8);
|
||||
public static readonly AttributeID
|
||||
DriveTemperature = new AttributeID(0xE7);
|
||||
ReadErrorRate = new AttributeID(0x01),
|
||||
ThroughputPerformance = new AttributeID(0x02),
|
||||
SpinUpTime = new AttributeID(0x03),
|
||||
StartStopCount = new AttributeID(0x04),
|
||||
ReallocatedSectorsCount = new AttributeID(0x05),
|
||||
ReadChannelMargin = new AttributeID(0x06),
|
||||
SeekErrorRate = new AttributeID(0x07),
|
||||
SeekTimePerformance = new AttributeID(0x08),
|
||||
PowerOnHours = new AttributeID(0x09),
|
||||
SpinRetryCount = new AttributeID(0x0A),
|
||||
RecalibrationRetries = new AttributeID(0x0B),
|
||||
PowerCycleCount = new AttributeID(0x0C),
|
||||
SoftReadErrorRate = new AttributeID(0x0D),
|
||||
SataDownshiftErrorCount = new AttributeID(0xB7),
|
||||
EndToEndError = new AttributeID(0xB8),
|
||||
HeadStability = new AttributeID(0xB9),
|
||||
InducedOpVibrationDetection = new AttributeID(0xBA),
|
||||
ReportedUncorrectableErrors = new AttributeID(0xBB),
|
||||
CommandTimeout = new AttributeID(0xBC),
|
||||
HighFlyWrites = new AttributeID(0xBD),
|
||||
AirflowTemperature = new AttributeID(0xBE),
|
||||
GSenseErrorRate = new AttributeID(0xBF),
|
||||
PowerOffRetractCount = new AttributeID(0xC0),
|
||||
LoadCycleCount = new AttributeID(0xC1),
|
||||
Temperature = new AttributeID(0xC2),
|
||||
HardwareEccRecovered = new AttributeID(0xC3),
|
||||
ReallocationEventCount = new AttributeID(0xC4),
|
||||
CurrentPendingSectorCount = new AttributeID(0xC5),
|
||||
UncorrectableSectorCount = new AttributeID(0xC6),
|
||||
UltraDmaCrcErrorCount = new AttributeID(0xC7),
|
||||
WriteErrorRate = new AttributeID(0xC8),
|
||||
DataAddressMarkerrors = new AttributeID(0xCA),
|
||||
RunOutCancel = new AttributeID(0xCB),
|
||||
SoftEccCorrection = new AttributeID(0xCC),
|
||||
ThermalAsperityRate = new AttributeID(0xCD),
|
||||
FlyingHeight = new AttributeID(0xCE),
|
||||
SpinHighCurrent = new AttributeID(0xCF),
|
||||
SpinBuzz = new AttributeID(0xD0),
|
||||
OfflineSeekPerformance = new AttributeID(0xD1),
|
||||
VibrationDuringWrite = new AttributeID(0xD3),
|
||||
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
|
||||
// TODO: Find out the purpose of attribute 0xD2
|
||||
// Seems to be unique to Indilinx drives, hence its name of UnknownUnique.
|
||||
public static class IndilinxAttributes {
|
||||
public static readonly AttributeID RemainingLife = new AttributeID(0xD1);
|
||||
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
|
||||
// TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4
|
||||
public static class IntelAttributes {
|
||||
public static readonly AttributeID RemainingLife = new AttributeID(0xE8);
|
||||
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
|
||||
// TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9
|
||||
public static class SamsungAttributes {
|
||||
public static readonly AttributeID RemainingLife = new AttributeID(0xB4);
|
||||
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
|
||||
// Note: 0xE9 and 0xEA are reserved attributes and unique
|
||||
public static class SandForceAttributes {
|
||||
public static readonly AttributeID RemainingLife = new AttributeID(0xE7);
|
||||
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)]
|
||||
@ -329,7 +417,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
public static List<DriveAttribute> ReadSmart(IntPtr handle,
|
||||
public static DriveAttribute[] ReadSmart(IntPtr handle,
|
||||
int driveNumber)
|
||||
{
|
||||
DriveCommandParameter parameter = new DriveCommandParameter();
|
||||
@ -347,9 +435,7 @@ namespace OpenHardwareMonitor.Hardware.HDD {
|
||||
out result, Marshal.SizeOf(typeof(DriveSmartReadResult)),
|
||||
out bytesReturned, IntPtr.Zero);
|
||||
|
||||
return (isValid)
|
||||
? new List<DriveAttribute>(result.Attributes)
|
||||
: new List<DriveAttribute>();
|
||||
return (isValid) ? result.Attributes : new DriveAttribute[0];
|
||||
}
|
||||
|
||||
public static string ReadName(IntPtr handle, int driveNumber) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user