Updated attribute maps for the 4 SSD controllers we figured out so far.
authorpaulwerelds
Sun, 17 Oct 2010 19:25:50 +0000
changeset 233c5139c236200
parent 232 475b7160e3bc
child 234 99fad54de9f7
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.
Hardware/HDD/HDD.cs
Hardware/HDD/HDDGroup.cs
Hardware/HDD/SMART.cs
     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) {