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.
1.1 --- a/Hardware/HDD/HDD.cs Sun Oct 17 19:13:26 2010 +0000
1.2 +++ b/Hardware/HDD/HDD.cs Sun Oct 17 19:25:50 2010 +0000
1.3 @@ -119,30 +119,29 @@
1.4
1.5 public void Update() {
1.6 if (count == 0) {
1.7 - List<SMART.DriveAttribute> attributes = SMART.ReadSmart(handle, drive);
1.8 + SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
1.9 +
1.10 if (temperatureID != SMART.AttributeID.None &&
1.11 - attributes.Exists(attr => attr.ID == temperatureID))
1.12 + Array.Exists(attributes, attr => attr.ID == temperatureID))
1.13 {
1.14 - temperatureSensor.Value = attributes
1.15 - .Find(attr => attr.ID == temperatureID)
1.16 + temperatureSensor.Value = Array
1.17 + .Find(attributes, attr => attr.ID == temperatureID)
1.18 .RawValue[0];
1.19 }
1.20
1.21 if (lifeID != SMART.AttributeID.None &&
1.22 - attributes.Exists(attr => attr.ID == lifeID))
1.23 + Array.Exists(attributes, attr => attr.ID == lifeID))
1.24 {
1.25 - lifeSensor.Value = attributes
1.26 - .Find(attr => attr.ID == lifeID)
1.27 + lifeSensor.Value = Array
1.28 + .Find(attributes, attr => attr.ID == lifeID)
1.29 .AttrValue;
1.30 }
1.31 } else {
1.32 - if (temperatureID != SMART.AttributeID.None) {
1.33 + if (temperatureID != SMART.AttributeID.None)
1.34 temperatureSensor.Value = temperatureSensor.Value;
1.35 - }
1.36
1.37 - if (lifeID != SMART.AttributeID.None) {
1.38 + if (lifeID != SMART.AttributeID.None)
1.39 lifeSensor.Value = lifeSensor.Value;
1.40 - }
1.41 }
1.42
1.43 count++; count %= UPDATE_DIVIDER;
2.1 --- a/Hardware/HDD/HDDGroup.cs Sun Oct 17 19:13:26 2010 +0000
2.2 +++ b/Hardware/HDD/HDDGroup.cs Sun Oct 17 19:25:50 2010 +0000
2.3 @@ -68,10 +68,9 @@
2.4 continue;
2.5 }
2.6
2.7 - List<SMART.DriveAttribute> attributes =
2.8 - new List<SMART.DriveAttribute>(SMART.ReadSmart(handle, drive));
2.9 + SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
2.10
2.11 - if (!(attributes.Count > 0)) {
2.12 + if (attributes.Length < 1) {
2.13 SMART.CloseHandle(handle);
2.14 continue;
2.15 }
2.16 @@ -95,28 +94,29 @@
2.17 }
2.18 }
2.19
2.20 - private SMART.AttributeID GetSSDLifeID(List<SMART.DriveAttribute> attributes) {
2.21 - // ID E9 is present on Intel, JM, SF and Samsung
2.22 + private SMART.AttributeID GetSSDLifeID(SMART.DriveAttribute[] attributes) {
2.23 + // ID E9 is present on Intel, JM, SF and Samsung (different meanings)
2.24 // ID D2 is present on Indilinx
2.25 // Neither ID has been found on a mechanical hard drive (yet),
2.26 - // So this seems like a good way to check if it's an SSD.
2.27 + // so this seems like a good way to check if it's an SSD.
2.28 bool isKnownSSD = (
2.29 - attributes.Exists(attr => attr.ID == new SMART.AttributeID(0xE9)) ||
2.30 - attributes.Exists(attr => attr.ID == new SMART.AttributeID(0xD2))
2.31 + Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xE9)) ||
2.32 + Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xD2))
2.33 );
2.34
2.35 if (!isKnownSSD) return SMART.AttributeID.None;
2.36
2.37 // We start with a traditional loop, because there are 4 unique ID's
2.38 // that potentially identify one of the vendors
2.39 - for (int i = 0; i < attributes.Count; i++) {
2.40 -
2.41 + for (int i = 0; i < attributes.Length; i++) {
2.42 if (attributes[i].ID == SMART.SamsungAttributes.RemainingLife)
2.43 return SMART.SamsungAttributes.RemainingLife;
2.44 - else if (attributes[i].ID == new SMART.AttributeID(0xAB))
2.45 +
2.46 + if (attributes[i].ID == SMART.SandForceAttributes.ProgramFailCount)
2.47 return SMART.SandForceAttributes.RemainingLife;
2.48 - else if (attributes[i].ID == new SMART.AttributeID(0xD2))
2.49 - return SMART.IndilinxAttributes.RemainingLife;
2.50 +
2.51 + if (attributes[i].ID == SMART.IndilinxAttributes.UnknownUnique)
2.52 + return SMART.IndilinxAttributes.RemainingLife;
2.53 }
2.54
2.55 // TODO: Find out JMicron's Life attribute ID; their unique ID = 0xE4
2.56 @@ -126,9 +126,9 @@
2.57 // is whether we can find all 3; pointless to use Exists()
2.58 int intelRegisterCount = 0;
2.59 foreach (SMART.DriveAttribute attribute in attributes) {
2.60 - if (attribute.ID == new SMART.AttributeID(0xE1) ||
2.61 - attribute.ID == new SMART.AttributeID(0xE8) ||
2.62 - attribute.ID == new SMART.AttributeID(0xE9)
2.63 + if (attribute.ID == SMART.IntelAttributes.HostWrites ||
2.64 + attribute.ID == SMART.IntelAttributes.RemainingLife ||
2.65 + attribute.ID == SMART.IntelAttributes.MediaWearOutIndicator
2.66 )
2.67 intelRegisterCount++;
2.68 }
2.69 @@ -139,7 +139,7 @@
2.70 }
2.71
2.72 private SMART.AttributeID GetTemperatureIndex(
2.73 - List<SMART.DriveAttribute> attributes)
2.74 + SMART.DriveAttribute[] attributes)
2.75 {
2.76 SMART.AttributeID[] validIds = new[] {
2.77 SMART.CommonAttributes.Temperature,
2.78 @@ -149,7 +149,7 @@
2.79
2.80 foreach (SMART.AttributeID validId in validIds) {
2.81 SMART.AttributeID id = validId;
2.82 - if (attributes.Exists(attr => attr.ID == id))
2.83 + if (Array.Exists(attributes, attr => attr.ID == id))
2.84 return validId;
2.85 }
2.86
2.87 @@ -188,9 +188,9 @@
2.88 continue;
2.89 }
2.90
2.91 - List<SMART.DriveAttribute> attributes = SMART.ReadSmart(handle, drive);
2.92 + SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
2.93
2.94 - if (attributes != null) {
2.95 + if (attributes.Length > 0) {
2.96 r.AppendLine("Drive name: " + name);
2.97 r.AppendLine();
2.98 r.AppendFormat(CultureInfo.InvariantCulture, " {0}{1}{2}{3}{4}{5}",
3.1 --- a/Hardware/HDD/SMART.cs Sun Oct 17 19:13:26 2010 +0000
3.2 +++ b/Hardware/HDD/SMART.cs Sun Oct 17 19:25:50 2010 +0000
3.3 @@ -81,72 +81,160 @@
3.4 public static readonly AttributeID None = new AttributeID(0x00);
3.5 }
3.6
3.7 - // Common SMART attributes
3.8 - public static class CommonAttributes {
3.9 - public static readonly AttributeID
3.10 - ReadErrorRate = new AttributeID(0x01);
3.11 - public static readonly AttributeID
3.12 - ThroughputPerformance = new AttributeID(0x02);
3.13 - public static readonly AttributeID
3.14 - SpinUpTime = new AttributeID(0x03);
3.15 - public static readonly AttributeID
3.16 - StartStopCount = new AttributeID(0x04);
3.17 - public static readonly AttributeID
3.18 - ReallocatedSectorsCount = new AttributeID(0x05);
3.19 - public static readonly AttributeID
3.20 - ReadChannelMargin = new AttributeID(0x06);
3.21 - public static readonly AttributeID
3.22 - SeekErrorRate = new AttributeID(0x07);
3.23 - public static readonly AttributeID
3.24 - SeekTimePerformance = new AttributeID(0x08);
3.25 - public static readonly AttributeID
3.26 - PowerOnHours = new AttributeID(0x09);
3.27 - public static readonly AttributeID
3.28 - SpinRetryCount = new AttributeID(0x0A);
3.29 - public static readonly AttributeID
3.30 - RecalibrationRetries = new AttributeID(0x0B);
3.31 - public static readonly AttributeID
3.32 - PowerCycleCount = new AttributeID(0x0C);
3.33 - public static readonly AttributeID
3.34 - SoftReadErrorRate = new AttributeID(0x0D);
3.35 - public static readonly AttributeID
3.36 - AirflowTemperature = new AttributeID(0xBE);
3.37 - public static readonly AttributeID
3.38 - Temperature = new AttributeID(0xC2);
3.39 - public static readonly AttributeID
3.40 - HardwareECCRecovered = new AttributeID(0xC3);
3.41 - public static readonly AttributeID
3.42 - ReallocationEventCount = new AttributeID(0xC4);
3.43 - public static readonly AttributeID
3.44 - CurrentPendingSectorCount = new AttributeID(0xC5);
3.45 - public static readonly AttributeID
3.46 - UncorrectableSectorCount = new AttributeID(0xC6);
3.47 - public static readonly AttributeID
3.48 - UltraDMACRCErrorCount = new AttributeID(0xC7);
3.49 - public static readonly AttributeID
3.50 - WriteErrorRate = new AttributeID(0xC8);
3.51 - public static readonly AttributeID
3.52 - DriveTemperature = new AttributeID(0xE7);
3.53 + // These are the more-or-less standard S.M.A.R.T attributes
3.54 + // TODO: Filter out unused/obscure ones; some are interpreted differently
3.55 + // between manufacturers
3.56 + public static class CommonAttributes {
3.57 + public static readonly AttributeID
3.58 + ReadErrorRate = new AttributeID(0x01),
3.59 + ThroughputPerformance = new AttributeID(0x02),
3.60 + SpinUpTime = new AttributeID(0x03),
3.61 + StartStopCount = new AttributeID(0x04),
3.62 + ReallocatedSectorsCount = new AttributeID(0x05),
3.63 + ReadChannelMargin = new AttributeID(0x06),
3.64 + SeekErrorRate = new AttributeID(0x07),
3.65 + SeekTimePerformance = new AttributeID(0x08),
3.66 + PowerOnHours = new AttributeID(0x09),
3.67 + SpinRetryCount = new AttributeID(0x0A),
3.68 + RecalibrationRetries = new AttributeID(0x0B),
3.69 + PowerCycleCount = new AttributeID(0x0C),
3.70 + SoftReadErrorRate = new AttributeID(0x0D),
3.71 + SataDownshiftErrorCount = new AttributeID(0xB7),
3.72 + EndToEndError = new AttributeID(0xB8),
3.73 + HeadStability = new AttributeID(0xB9),
3.74 + InducedOpVibrationDetection = new AttributeID(0xBA),
3.75 + ReportedUncorrectableErrors = new AttributeID(0xBB),
3.76 + CommandTimeout = new AttributeID(0xBC),
3.77 + HighFlyWrites = new AttributeID(0xBD),
3.78 + AirflowTemperature = new AttributeID(0xBE),
3.79 + GSenseErrorRate = new AttributeID(0xBF),
3.80 + PowerOffRetractCount = new AttributeID(0xC0),
3.81 + LoadCycleCount = new AttributeID(0xC1),
3.82 + Temperature = new AttributeID(0xC2),
3.83 + HardwareEccRecovered = new AttributeID(0xC3),
3.84 + ReallocationEventCount = new AttributeID(0xC4),
3.85 + CurrentPendingSectorCount = new AttributeID(0xC5),
3.86 + UncorrectableSectorCount = new AttributeID(0xC6),
3.87 + UltraDmaCrcErrorCount = new AttributeID(0xC7),
3.88 + WriteErrorRate = new AttributeID(0xC8),
3.89 + DataAddressMarkerrors = new AttributeID(0xCA),
3.90 + RunOutCancel = new AttributeID(0xCB),
3.91 + SoftEccCorrection = new AttributeID(0xCC),
3.92 + ThermalAsperityRate = new AttributeID(0xCD),
3.93 + FlyingHeight = new AttributeID(0xCE),
3.94 + SpinHighCurrent = new AttributeID(0xCF),
3.95 + SpinBuzz = new AttributeID(0xD0),
3.96 + OfflineSeekPerformance = new AttributeID(0xD1),
3.97 + VibrationDuringWrite = new AttributeID(0xD3),
3.98 + ShockDuringWrite = new AttributeID(0xD4),
3.99 + DiskShift = new AttributeID(0xDC),
3.100 + GSenseErrorRateAlt = new AttributeID(0xDD), // Alternative to 0xBF
3.101 + LoadedHours = new AttributeID(0xDE),
3.102 + LoadUnloadRetryCount = new AttributeID(0xDF),
3.103 + LoadFriction = new AttributeID(0xE0),
3.104 + LoadUnloadCycleCount = new AttributeID(0xE1),
3.105 + LoadInTime = new AttributeID(0xE2),
3.106 + TorqueAmplificationCount = new AttributeID(0xE3),
3.107 + PowerOffRetractCycle = new AttributeID(0xE4),
3.108 + GMRHeadAmplitude = new AttributeID(0xE6),
3.109 + DriveTemperature = new AttributeID(0xE7),
3.110 + HeadFlyingHours = new AttributeID(0xF0),
3.111 + LBAsWrittenTotal = new AttributeID(0xF1),
3.112 + LBAsReadTotal = new AttributeID(0xF2),
3.113 + ReadErrorRetryRate = new AttributeID(0xFA),
3.114 + FreeFallProtection = new AttributeID(0xFE)
3.115 + ;
3.116 }
3.117
3.118 // Indilinx SSD SMART attributes
3.119 - public static class IndilinxAttributes {
3.120 - public static readonly AttributeID RemainingLife = new AttributeID(0xD1);
3.121 + // TODO: Find out the purpose of attribute 0xD2
3.122 + // Seems to be unique to Indilinx drives, hence its name of UnknownUnique.
3.123 + public static class IndilinxAttributes {
3.124 + public static readonly AttributeID
3.125 + ReadErrorRate = CommonAttributes.ReadErrorRate,
3.126 + PowerOnHours = CommonAttributes.PowerOnHours,
3.127 + PowerCycleCount = CommonAttributes.PowerCycleCount,
3.128 + InitialBadBlockCount = new AttributeID(0xB8),
3.129 + RemainingLife = new AttributeID(0xD1),
3.130 + ProgramFailure = new AttributeID(0xC3),
3.131 + EraseFailure = new AttributeID(0xC4),
3.132 + ReadFailure = new AttributeID(0xC5),
3.133 + SectorsRead = new AttributeID(0xC6),
3.134 + SectorsWritten = new AttributeID(0xC7),
3.135 + ReadCommands = new AttributeID(0xC8),
3.136 + WriteCommands = new AttributeID(0xC9),
3.137 + BitErrors = new AttributeID(0xCA),
3.138 + CorrectedErrors = new AttributeID(0xCB),
3.139 + BadBlockFullFlag = new AttributeID(0xCC),
3.140 + MaxCellcycles = new AttributeID(0xCD),
3.141 + MinErase = new AttributeID(0xCE),
3.142 + MaxErase = new AttributeID(0xCF),
3.143 + AverageEraseCount = new AttributeID(0xD0),
3.144 + UnknownUnique = new AttributeID(0xD2),
3.145 + SataErrorCountCRC = new AttributeID(0xD3),
3.146 + SataErrorCountHandshake = new AttributeID(0xD4)
3.147 + ;
3.148 }
3.149
3.150 // Intel SSD SMART attributes
3.151 - public static class IntelAttributes {
3.152 - public static readonly AttributeID RemainingLife = new AttributeID(0xE8);
3.153 + // TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4
3.154 + public static class IntelAttributes {
3.155 + public static readonly AttributeID
3.156 + ReadErrorRate = CommonAttributes.ReadErrorRate,
3.157 + SpinUpTime = CommonAttributes.SpinUpTime,
3.158 + StartStopCount = CommonAttributes.StartStopCount,
3.159 + ReallocatedSectorsCount = CommonAttributes.ReallocatedSectorsCount,
3.160 + PowerOnHours = CommonAttributes.PowerOnHours,
3.161 + PowerCycleCount = CommonAttributes.PowerCycleCount,
3.162 + EndToEndError = CommonAttributes.EndToEndError, // Only on G2 drives!
3.163 +
3.164 + // Different from the common attribute PowerOffRetractCount, same ID
3.165 + UnsafeShutdownCount = new AttributeID(0xC0),
3.166 + HostWrites = new AttributeID(0xE1),
3.167 + RemainingLife = new AttributeID(0xE8),
3.168 + MediaWearOutIndicator = new AttributeID(0xE9)
3.169 + ;
3.170 }
3.171
3.172 // Samsung SSD SMART attributes
3.173 - public static class SamsungAttributes {
3.174 - public static readonly AttributeID RemainingLife = new AttributeID(0xB4);
3.175 + // TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9
3.176 + public static class SamsungAttributes {
3.177 + public static readonly AttributeID
3.178 + PowerOnHours = CommonAttributes.PowerOnHours,
3.179 + PowerCycleCount = CommonAttributes.PowerCycleCount,
3.180 + UsedReservedBlockCountChip = new AttributeID(0xB2), // Unique
3.181 + UsedReservedBlockCountTotal = new AttributeID(0xB3), // Unique
3.182 + RemainingLife = new AttributeID(0xB4), // Unique
3.183 + RuntimeBadBlockTotal = new AttributeID(0xB7)
3.184 + ;
3.185 }
3.186
3.187 // SandForce SSD SMART attributes
3.188 - public static class SandForceAttributes {
3.189 - public static readonly AttributeID RemainingLife = new AttributeID(0xE7);
3.190 + // Note: 0xE9 and 0xEA are reserved attributes and unique
3.191 + public static class SandForceAttributes {
3.192 + public static readonly AttributeID
3.193 + ReadErrorRate = CommonAttributes.ReadErrorRate,
3.194 + RetiredBlockCount = new AttributeID(0x05),
3.195 + PowerOnHours = CommonAttributes.PowerOnHours,
3.196 + PowerCycleCount = CommonAttributes.PowerCycleCount,
3.197 + ProgramFailCount = new AttributeID(0xAB), // Unique
3.198 + EraseFailCount = new AttributeID(0xAC), // Unique
3.199 + UnexpectedPowerLossCount = new AttributeID(0xAE), // Unique
3.200 + WearRangeDelta = new AttributeID(0xB1), // Unique
3.201 + ProgramFailCountAlt = new AttributeID(0xB5), // Same as 0xAB
3.202 + EraseFailCountAlt = new AttributeID(0xB6), // Same as 0xAC
3.203 + ReportedUncorrectableErrors =
3.204 + CommonAttributes.ReportedUncorrectableErrors,
3.205 +
3.206 + Temperature = CommonAttributes.Temperature, // SF-1500 only!
3.207 +
3.208 + // Opposite of the common attribute HardwareECCRecovered
3.209 + UnrecoverableECC = new AttributeID(0xC3),
3.210 + ReallocationEventCount = new AttributeID(0xC4),
3.211 + RemainingLife = new AttributeID(0xE7),
3.212 + LifetimeWrites = new AttributeID(0xF1),
3.213 + LifetimeReads = new AttributeID(0xF2)
3.214 + ;
3.215 }
3.216
3.217 [StructLayout(LayoutKind.Sequential, Pack = 1)]
3.218 @@ -329,7 +417,7 @@
3.219 IntPtr.Zero);
3.220 }
3.221
3.222 - public static List<DriveAttribute> ReadSmart(IntPtr handle,
3.223 + public static DriveAttribute[] ReadSmart(IntPtr handle,
3.224 int driveNumber)
3.225 {
3.226 DriveCommandParameter parameter = new DriveCommandParameter();
3.227 @@ -347,9 +435,7 @@
3.228 out result, Marshal.SizeOf(typeof(DriveSmartReadResult)),
3.229 out bytesReturned, IntPtr.Zero);
3.230
3.231 - return (isValid)
3.232 - ? new List<DriveAttribute>(result.Attributes)
3.233 - : new List<DriveAttribute>();
3.234 + return (isValid) ? result.Attributes : new DriveAttribute[0];
3.235 }
3.236
3.237 public static string ReadName(IntPtr handle, int driveNumber) {