Hardware/HDD/SMART.cs
author paulwerelds
Sun, 17 Oct 2010 19:25:50 +0000
changeset 233 c5139c236200
parent 231 30f5a06f5d8a
child 308 d882720734bf
permissions -rw-r--r--
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 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2010
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s): Paul Werelds
    23 
    24   Alternatively, the contents of this file may be used under the terms of
    25   either the GNU General Public License Version 2 or later (the "GPL"), or
    26   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    27   in which case the provisions of the GPL or the LGPL are applicable instead
    28   of those above. If you wish to allow use of your version of this file only
    29   under the terms of either the GPL or the LGPL, and not to allow others to
    30   use your version of this file under the terms of the MPL, indicate your
    31   decision by deleting the provisions above and replace them with the notice
    32   and other provisions required by the GPL or the LGPL. If you do not delete
    33   the provisions above, a recipient may use your version of this file under
    34   the terms of any one of the MPL, the GPL or the LGPL.
    35  
    36 */
    37 
    38 using System;
    39 using System.Collections.Generic;
    40 using System.Runtime.InteropServices;
    41 
    42 namespace OpenHardwareMonitor.Hardware.HDD {
    43 
    44   internal class SMART {
    45 
    46     [Flags]
    47     public enum Status : ushort {
    48       PreFailureWarranty = 0x01,
    49       OnLineCollection = 0x02,
    50       Performance = 0x04,
    51       ErrorRate = 0x08,
    52       EventCount = 0x10,
    53       SelfPreserving = 0x20
    54     }
    55 
    56     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    57     public struct AttributeID {
    58       private byte value;
    59 
    60       public AttributeID(byte value) {
    61         this.value = value;
    62       }
    63 
    64       public override bool Equals(Object obj) {
    65         return obj is AttributeID && this == (AttributeID)obj;
    66       }
    67       public override int GetHashCode() {
    68         return value.GetHashCode() ^ value.GetHashCode();
    69       }
    70       public static bool operator ==(AttributeID a, AttributeID b) {
    71         return a.value == b.value;
    72       }
    73       public static bool operator !=(AttributeID a, AttributeID b) {
    74         return !(a == b);
    75       }
    76 
    77       public string ToString(string format) {
    78         return value.ToString(format);
    79       }
    80 
    81       public static readonly AttributeID None = new AttributeID(0x00);
    82     }
    83 
    84     // These are the more-or-less standard S.M.A.R.T attributes
    85     // TODO: Filter out unused/obscure ones; some are interpreted differently
    86     // between manufacturers
    87     public static class CommonAttributes {
    88       public static readonly AttributeID
    89         ReadErrorRate = new AttributeID(0x01),
    90         ThroughputPerformance = new AttributeID(0x02),
    91         SpinUpTime = new AttributeID(0x03),
    92         StartStopCount = new AttributeID(0x04),
    93         ReallocatedSectorsCount = new AttributeID(0x05),
    94         ReadChannelMargin = new AttributeID(0x06),
    95         SeekErrorRate = new AttributeID(0x07),
    96         SeekTimePerformance = new AttributeID(0x08),
    97         PowerOnHours = new AttributeID(0x09),
    98         SpinRetryCount = new AttributeID(0x0A),
    99         RecalibrationRetries = new AttributeID(0x0B),
   100         PowerCycleCount = new AttributeID(0x0C),
   101         SoftReadErrorRate = new AttributeID(0x0D),
   102         SataDownshiftErrorCount = new AttributeID(0xB7),
   103         EndToEndError = new AttributeID(0xB8),
   104         HeadStability = new AttributeID(0xB9),
   105         InducedOpVibrationDetection = new AttributeID(0xBA),
   106         ReportedUncorrectableErrors = new AttributeID(0xBB),
   107         CommandTimeout = new AttributeID(0xBC),
   108         HighFlyWrites = new AttributeID(0xBD),
   109         AirflowTemperature = new AttributeID(0xBE),
   110         GSenseErrorRate = new AttributeID(0xBF),
   111         PowerOffRetractCount = new AttributeID(0xC0),
   112         LoadCycleCount = new AttributeID(0xC1),
   113         Temperature = new AttributeID(0xC2),
   114         HardwareEccRecovered = new AttributeID(0xC3),
   115         ReallocationEventCount = new AttributeID(0xC4),
   116         CurrentPendingSectorCount = new AttributeID(0xC5),
   117         UncorrectableSectorCount = new AttributeID(0xC6),
   118         UltraDmaCrcErrorCount = new AttributeID(0xC7),
   119         WriteErrorRate = new AttributeID(0xC8),
   120         DataAddressMarkerrors = new AttributeID(0xCA),
   121         RunOutCancel = new AttributeID(0xCB),
   122         SoftEccCorrection = new AttributeID(0xCC),
   123         ThermalAsperityRate = new AttributeID(0xCD),
   124         FlyingHeight = new AttributeID(0xCE),
   125         SpinHighCurrent = new AttributeID(0xCF),
   126         SpinBuzz = new AttributeID(0xD0),
   127         OfflineSeekPerformance = new AttributeID(0xD1),
   128         VibrationDuringWrite = new AttributeID(0xD3),
   129         ShockDuringWrite = new AttributeID(0xD4),
   130         DiskShift = new AttributeID(0xDC),
   131         GSenseErrorRateAlt = new AttributeID(0xDD), // Alternative to 0xBF
   132         LoadedHours = new AttributeID(0xDE),
   133         LoadUnloadRetryCount = new AttributeID(0xDF),
   134         LoadFriction = new AttributeID(0xE0),
   135         LoadUnloadCycleCount = new AttributeID(0xE1),
   136         LoadInTime = new AttributeID(0xE2),
   137         TorqueAmplificationCount = new AttributeID(0xE3),
   138         PowerOffRetractCycle = new AttributeID(0xE4),
   139         GMRHeadAmplitude = new AttributeID(0xE6),
   140         DriveTemperature = new AttributeID(0xE7),
   141         HeadFlyingHours = new AttributeID(0xF0),
   142         LBAsWrittenTotal = new AttributeID(0xF1),
   143         LBAsReadTotal = new AttributeID(0xF2),
   144         ReadErrorRetryRate = new AttributeID(0xFA),
   145         FreeFallProtection = new AttributeID(0xFE)
   146       ;
   147     }
   148 
   149     // Indilinx SSD SMART attributes
   150     // TODO: Find out the purpose of attribute 0xD2
   151     // Seems to be unique to Indilinx drives, hence its name of UnknownUnique.
   152     public static class IndilinxAttributes {
   153       public static readonly AttributeID
   154         ReadErrorRate = CommonAttributes.ReadErrorRate,
   155         PowerOnHours = CommonAttributes.PowerOnHours,
   156         PowerCycleCount = CommonAttributes.PowerCycleCount,
   157         InitialBadBlockCount = new AttributeID(0xB8),
   158         RemainingLife = new AttributeID(0xD1),
   159         ProgramFailure = new AttributeID(0xC3),
   160         EraseFailure = new AttributeID(0xC4),
   161         ReadFailure = new AttributeID(0xC5),
   162         SectorsRead = new AttributeID(0xC6),
   163         SectorsWritten = new AttributeID(0xC7),
   164         ReadCommands = new AttributeID(0xC8),
   165         WriteCommands = new AttributeID(0xC9),
   166         BitErrors = new AttributeID(0xCA),
   167         CorrectedErrors = new AttributeID(0xCB),
   168         BadBlockFullFlag = new AttributeID(0xCC),
   169         MaxCellcycles = new AttributeID(0xCD),
   170         MinErase = new AttributeID(0xCE),
   171         MaxErase = new AttributeID(0xCF),
   172         AverageEraseCount = new AttributeID(0xD0),
   173         UnknownUnique = new AttributeID(0xD2),
   174         SataErrorCountCRC = new AttributeID(0xD3),
   175         SataErrorCountHandshake = new AttributeID(0xD4)
   176       ;
   177     }
   178 
   179     // Intel SSD SMART attributes
   180     // TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4
   181     public static class IntelAttributes {
   182       public static readonly AttributeID
   183         ReadErrorRate = CommonAttributes.ReadErrorRate,
   184         SpinUpTime = CommonAttributes.SpinUpTime,
   185         StartStopCount = CommonAttributes.StartStopCount,
   186         ReallocatedSectorsCount = CommonAttributes.ReallocatedSectorsCount,
   187         PowerOnHours = CommonAttributes.PowerOnHours,
   188         PowerCycleCount = CommonAttributes.PowerCycleCount,
   189         EndToEndError = CommonAttributes.EndToEndError, // Only on G2 drives!
   190 
   191         // Different from the common attribute PowerOffRetractCount, same ID
   192         UnsafeShutdownCount = new AttributeID(0xC0),
   193         HostWrites = new AttributeID(0xE1),
   194         RemainingLife = new AttributeID(0xE8),
   195         MediaWearOutIndicator = new AttributeID(0xE9)
   196       ;
   197     }
   198 
   199     // Samsung SSD SMART attributes
   200     // TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9
   201     public static class SamsungAttributes {
   202       public static readonly AttributeID
   203         PowerOnHours = CommonAttributes.PowerOnHours,
   204         PowerCycleCount = CommonAttributes.PowerCycleCount,
   205         UsedReservedBlockCountChip = new AttributeID(0xB2), // Unique
   206         UsedReservedBlockCountTotal = new AttributeID(0xB3), // Unique
   207         RemainingLife = new AttributeID(0xB4), // Unique
   208         RuntimeBadBlockTotal = new AttributeID(0xB7)
   209       ;
   210     }
   211 
   212     // SandForce SSD SMART attributes
   213     // Note: 0xE9 and 0xEA are reserved attributes and unique
   214     public static class SandForceAttributes {
   215       public static readonly AttributeID
   216         ReadErrorRate = CommonAttributes.ReadErrorRate,
   217         RetiredBlockCount = new AttributeID(0x05),
   218         PowerOnHours = CommonAttributes.PowerOnHours,
   219         PowerCycleCount = CommonAttributes.PowerCycleCount,
   220         ProgramFailCount = new AttributeID(0xAB), // Unique
   221         EraseFailCount = new AttributeID(0xAC), // Unique
   222         UnexpectedPowerLossCount = new AttributeID(0xAE), // Unique
   223         WearRangeDelta = new AttributeID(0xB1), // Unique
   224         ProgramFailCountAlt = new AttributeID(0xB5), // Same as 0xAB
   225         EraseFailCountAlt = new AttributeID(0xB6), // Same as 0xAC
   226         ReportedUncorrectableErrors =
   227           CommonAttributes.ReportedUncorrectableErrors,
   228         
   229         Temperature = CommonAttributes.Temperature, // SF-1500 only!
   230         
   231         // Opposite of the common attribute HardwareECCRecovered
   232         UnrecoverableECC = new AttributeID(0xC3),
   233         ReallocationEventCount = new AttributeID(0xC4),
   234         RemainingLife = new AttributeID(0xE7),
   235         LifetimeWrites = new AttributeID(0xF1),
   236         LifetimeReads = new AttributeID(0xF2)
   237       ;
   238     }
   239 
   240     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   241     public struct DriveAttribute {
   242       public AttributeID ID;
   243       public Status StatusFlags;
   244       public byte AttrValue;
   245       public byte WorstValue;
   246       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
   247       public byte[] RawValue;
   248       public byte Reserved;
   249     };
   250 
   251     [Flags]
   252     protected enum AccessMode : uint {     
   253       Read = 0x80000000,    
   254       Write = 0x40000000,     
   255       Execute = 0x20000000,     
   256       All = 0x10000000
   257     }
   258 
   259     [Flags]
   260     protected enum ShareMode : uint {
   261       None = 0,     
   262       Read = 1,     
   263       Write = 2,    
   264       Delete = 4
   265     }
   266 
   267     protected enum CreationMode : uint {
   268       New = 1,
   269       CreateAlways = 2,    
   270       OpenExisting = 3,    
   271       OpenAlways = 4,    
   272       TruncateExisting = 5
   273     }
   274 
   275     [Flags]
   276     protected enum FileAttribute : uint {
   277       Readonly = 0x00000001,
   278       Hidden = 0x00000002,
   279       System = 0x00000004,
   280       Directory = 0x00000010,
   281       Archive = 0x00000020,
   282       Device = 0x00000040,
   283       Normal = 0x00000080,
   284       Temporary = 0x00000100,
   285       SparseFile = 0x00000200,
   286       ReparsePoint = 0x00000400,
   287       Compressed = 0x00000800,
   288       Offline = 0x00001000,
   289       NotContentIndexed = 0x00002000,
   290       Encrypted = 0x00004000,
   291     }
   292 
   293     protected enum DriveCommand : uint {
   294       GetVersion = 0x00074080,
   295       SendDriveCommand = 0x0007c084,
   296       ReceiveDriveData = 0x0007c088
   297     }
   298 
   299     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   300     protected struct CommandBlockRegisters {
   301       public byte Features;         
   302       public byte SectorCount;      
   303       public byte LBALow;       
   304       public byte LBAMid;           
   305       public byte LBAHigh;        
   306       public byte Device;       
   307       public byte Command;           
   308       public byte Reserved;                  
   309     }
   310 
   311     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   312     protected struct DriveCommandParameter {
   313       public uint BufferSize;           
   314       public CommandBlockRegisters Registers;           
   315       public byte DriveNumber;   
   316       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   317       public byte[] Reserved;                                
   318     }
   319 
   320     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   321     protected struct DriverStatus {
   322       public byte DriverError;   
   323       public byte IDEError;             
   324       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   325       public byte[] Reserved;               
   326     }
   327 
   328     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   329     protected struct DriveCommandResult {
   330       public uint BufferSize;
   331       public DriverStatus DriverStatus;
   332     } 
   333 
   334     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   335     protected struct DriveSmartReadResult {
   336       public uint BufferSize;           
   337       public DriverStatus DriverStatus;
   338       public byte Version;
   339       public byte Reserved;
   340       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   341       public DriveAttribute[] Attributes;                                                                                       
   342     }
   343 
   344     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   345     protected struct Identify {
   346       public ushort GeneralConfiguration;
   347       public ushort NumberOfCylinders;
   348       public ushort Reserved;
   349       public ushort NumberOfHeads;
   350       public ushort UnformattedBytesPerTrack;
   351       public ushort UnformattedBytesPerSector;
   352       public ushort SectorsPerTrack;
   353       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
   354       public ushort[] VendorUnique;
   355       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   356       public byte[] SerialNumber;
   357       public ushort BufferType;
   358       public ushort BufferSectorSize;
   359       public ushort NumberOfEccBytes;
   360       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   361       public byte[] FirmwareRevision;
   362       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
   363       public byte[] ModelNumber;
   364       public ushort MoreVendorUnique;
   365       public ushort DoubleWordIo;
   366       public ushort Capabilities;
   367       public ushort MoreReserved;
   368       public ushort PioCycleTimingMode;
   369       public ushort DmaCycleTimingMode;
   370       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
   371       public byte[] More;
   372     }
   373 
   374     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   375     protected struct DriveIdentifyResult {
   376       public uint BufferSize;
   377       public DriverStatus DriverStatus;
   378       public Identify Identify;
   379     } 
   380 
   381     public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
   382 
   383     private const byte SMART_CMD = 0xB0;
   384     private const byte ID_CMD = 0xEC;
   385     
   386     private const byte SMART_READ_DATA = 0xD0;
   387     private const byte SMART_ENABLE_OPERATIONS = 0xD8;
   388     
   389     private const byte SMART_LBA_MID = 0x4F;
   390     private const byte SMART_LBA_HI = 0xC2;
   391 
   392     private const int MAX_DRIVE_ATTRIBUTES = 512;
   393 
   394     private SMART() { }
   395 
   396     public static IntPtr OpenPhysicalDrive(int driveNumber) {
   397       return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber,
   398         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
   399         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
   400         IntPtr.Zero);
   401     }
   402 
   403     public static bool EnableSmart(IntPtr handle, int driveNumber) {
   404       DriveCommandParameter parameter = new DriveCommandParameter();
   405       DriveCommandResult result;
   406       uint bytesReturned;
   407 
   408       parameter.DriveNumber = (byte)driveNumber;
   409       parameter.Registers.Features = SMART_ENABLE_OPERATIONS;
   410       parameter.Registers.LBAMid = SMART_LBA_MID;
   411       parameter.Registers.LBAHigh = SMART_LBA_HI;
   412       parameter.Registers.Command = SMART_CMD;
   413 
   414       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
   415         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
   416         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
   417         IntPtr.Zero);
   418     }
   419 
   420     public static DriveAttribute[] ReadSmart(IntPtr handle,
   421       int driveNumber)
   422     {
   423       DriveCommandParameter parameter = new DriveCommandParameter();
   424       DriveSmartReadResult result;
   425       uint bytesReturned;
   426 
   427       parameter.DriveNumber = (byte)driveNumber;
   428       parameter.Registers.Features = SMART_READ_DATA;
   429       parameter.Registers.LBAMid = SMART_LBA_MID;
   430       parameter.Registers.LBAHigh = SMART_LBA_HI;
   431       parameter.Registers.Command = SMART_CMD;
   432 
   433       bool isValid = NativeMethods.DeviceIoControl(handle, 
   434         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   435         out result, Marshal.SizeOf(typeof(DriveSmartReadResult)), 
   436         out bytesReturned, IntPtr.Zero);
   437 
   438       return (isValid) ? result.Attributes : new DriveAttribute[0];
   439     }
   440 
   441     public static string ReadName(IntPtr handle, int driveNumber) {
   442       DriveCommandParameter parameter = new DriveCommandParameter();
   443       DriveIdentifyResult result;
   444       uint bytesReturned;
   445 
   446       parameter.DriveNumber = (byte)driveNumber;
   447       parameter.Registers.Command = ID_CMD;
   448 
   449       bool valid = NativeMethods.DeviceIoControl(handle, 
   450         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   451         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
   452         out bytesReturned, IntPtr.Zero);
   453 
   454       if (!valid)
   455         return null;
   456       else {
   457 
   458         byte[] bytes = result.Identify.ModelNumber;
   459         char[] chars = new char[bytes.Length];
   460         for (int i = 0; i < bytes.Length; i += 2) {
   461           chars[i] = (char)bytes[i + 1];
   462           chars[i + 1] = (char)bytes[i];
   463         }
   464 
   465         return new string(chars).Trim();
   466       }
   467     }
   468 
   469     public static int CloseHandle(IntPtr handle) {
   470       return NativeMethods.CloseHandle(handle);
   471     }
   472 
   473     protected static class NativeMethods {
   474       private const string KERNEL = "kernel32.dll";
   475 
   476       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
   477         CharSet = CharSet.Unicode)]
   478       public static extern IntPtr CreateFile(string fileName,
   479         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   480         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
   481         IntPtr templateFilehandle);
   482 
   483       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   484       public static extern int CloseHandle(IntPtr handle);
   485 
   486       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   487       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   488       public static extern bool DeviceIoControl(IntPtr handle,
   489         DriveCommand command, ref DriveCommandParameter parameter,
   490         int parameterSize, out DriveSmartReadResult result, int resultSize,
   491         out uint bytesReturned, IntPtr overlapped);
   492 
   493       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   494       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   495       public static extern bool DeviceIoControl(IntPtr handle,
   496         DriveCommand command, ref DriveCommandParameter parameter,
   497         int parameterSize, out DriveCommandResult result, int resultSize,
   498         out uint bytesReturned, IntPtr overlapped);
   499 
   500       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   501       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   502       public static extern bool DeviceIoControl(IntPtr handle,
   503         DriveCommand command, ref DriveCommandParameter parameter,
   504         int parameterSize, out DriveIdentifyResult result, int resultSize,
   505         out uint bytesReturned, IntPtr overlapped);
   506     }    
   507   }
   508 }