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:
Paul Werelds 2010-10-17 19:25:50 +00:00
parent 22d714c976
commit ecefde3ac4
3 changed files with 174 additions and 89 deletions

View File

@ -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;

View File

@ -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}",

View File

@ -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) {