# HG changeset patch # User paulwerelds # Date 1287343550 0 # Node ID c5139c236200af146d2870881dfcde012c39d380 # Parent 475b7160e3bcc8731c2227504ed9c023800f3c0c 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. diff -r 475b7160e3bc -r c5139c236200 Hardware/HDD/HDD.cs --- a/Hardware/HDD/HDD.cs Sun Oct 17 19:13:26 2010 +0000 +++ b/Hardware/HDD/HDD.cs Sun Oct 17 19:25:50 2010 +0000 @@ -119,30 +119,29 @@ public void Update() { if (count == 0) { - List 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; diff -r 475b7160e3bc -r c5139c236200 Hardware/HDD/HDDGroup.cs --- a/Hardware/HDD/HDDGroup.cs Sun Oct 17 19:13:26 2010 +0000 +++ b/Hardware/HDD/HDDGroup.cs Sun Oct 17 19:25:50 2010 +0000 @@ -68,10 +68,9 @@ continue; } - List attributes = - new List(SMART.ReadSmart(handle, drive)); + SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive); - if (!(attributes.Count > 0)) { + if (attributes.Length < 1) { SMART.CloseHandle(handle); continue; } @@ -95,28 +94,29 @@ } } - private SMART.AttributeID GetSSDLifeID(List 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)) - 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 @@ -126,9 +126,9 @@ // 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 @@ } private SMART.AttributeID GetTemperatureIndex( - List attributes) + SMART.DriveAttribute[] attributes) { SMART.AttributeID[] validIds = new[] { SMART.CommonAttributes.Temperature, @@ -149,7 +149,7 @@ 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 @@ continue; } - List 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}", diff -r 475b7160e3bc -r c5139c236200 Hardware/HDD/SMART.cs --- a/Hardware/HDD/SMART.cs Sun Oct 17 19:13:26 2010 +0000 +++ b/Hardware/HDD/SMART.cs Sun Oct 17 19:25:50 2010 +0000 @@ -81,72 +81,160 @@ public static readonly AttributeID None = new AttributeID(0x00); } - // Common SMART attributes - 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); + // 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), + 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 - public static class IndilinxAttributes { - public static readonly AttributeID RemainingLife = new AttributeID(0xD1); + // 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 + 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 - public static class IntelAttributes { - public static readonly AttributeID RemainingLife = new AttributeID(0xE8); + // TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4 + 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 - public static class SamsungAttributes { - public static readonly AttributeID RemainingLife = new AttributeID(0xB4); + // TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9 + 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 - public static class SandForceAttributes { - public static readonly AttributeID RemainingLife = new AttributeID(0xE7); + // Note: 0xE9 and 0xEA are reserved attributes and unique + 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)] @@ -329,7 +417,7 @@ IntPtr.Zero); } - public static List ReadSmart(IntPtr handle, + public static DriveAttribute[] ReadSmart(IntPtr handle, int driveNumber) { DriveCommandParameter parameter = new DriveCommandParameter(); @@ -347,9 +435,7 @@ out result, Marshal.SizeOf(typeof(DriveSmartReadResult)), out bytesReturned, IntPtr.Zero); - return (isValid) - ? new List(result.Attributes) - : new List(); + return (isValid) ? result.Attributes : new DriveAttribute[0]; } public static string ReadName(IntPtr handle, int driveNumber) {