Hardware/HDD/SMART.cs
author paulwerelds
Fri, 08 Oct 2010 12:18:32 +0000
changeset 218 194186efdde9
parent 195 0ee888c485d5
child 231 30f5a06f5d8a
permissions -rw-r--r--
Added initial SSD support for Intel, Indilinx, SandForce and Samsung drives. Experimental feature for now!
     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     public enum AttributeID : byte {
    57       ReadErrorRate = 0x01,
    58       ThroughputPerformance = 0x02,
    59       SpinUpTime = 0x03,
    60       StartStopCount = 0x04,
    61       ReallocatedSectorsCount = 0x05,
    62       ReadChannelMargin = 0x06,
    63       SeekErrorRate = 0x07,
    64       SeekTimePerformance = 0x08,
    65       PowerOnHours = 0x09,
    66       SpinRetryCount = 0x0A,
    67       RecalibrationRetries = 0x0B,
    68       PowerCycleCount = 0x0C,
    69       SoftReadErrorRate = 0x0D,
    70       AirflowTemperature = 0xBE,
    71       Temperature = 0xC2,
    72       HardwareECCRecovered = 0xC3,
    73       ReallocationEventCount = 0xC4,
    74       CurrentPendingSectorCount = 0xC5,
    75       UncorrectableSectorCount = 0xC6,
    76       UltraDMACRCErrorCount = 0xC7,
    77       WriteErrorRate = 0xC8,
    78       DriveTemperature = 0xE7
    79     }
    80 
    81     public enum SSDLifeID {
    82       None = 0x00,
    83       Indilinx = 0xD1,
    84       Intel = 0xE8,
    85       Samsung = 0xB4,
    86       SandForce = 0xE7
    87     }
    88 
    89     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    90     public struct DriveAttribute {
    91       public AttributeID ID;
    92       public Status StatusFlags;
    93       public byte AttrValue;
    94       public byte WorstValue;
    95       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    96       public byte[] RawValue;
    97       public byte Reserved;
    98     };
    99 
   100     [Flags]
   101     protected enum AccessMode : uint {     
   102       Read = 0x80000000,    
   103       Write = 0x40000000,     
   104       Execute = 0x20000000,     
   105       All = 0x10000000
   106     }
   107 
   108     [Flags]
   109     protected enum ShareMode : uint {
   110       None = 0,     
   111       Read = 1,     
   112       Write = 2,    
   113       Delete = 4
   114     }
   115 
   116     protected enum CreationMode : uint {
   117       New = 1,
   118       CreateAlways = 2,    
   119       OpenExisting = 3,    
   120       OpenAlways = 4,    
   121       TruncateExisting = 5
   122     }
   123 
   124     [Flags]
   125     protected enum FileAttribute : uint {
   126       Readonly = 0x00000001,
   127       Hidden = 0x00000002,
   128       System = 0x00000004,
   129       Directory = 0x00000010,
   130       Archive = 0x00000020,
   131       Device = 0x00000040,
   132       Normal = 0x00000080,
   133       Temporary = 0x00000100,
   134       SparseFile = 0x00000200,
   135       ReparsePoint = 0x00000400,
   136       Compressed = 0x00000800,
   137       Offline = 0x00001000,
   138       NotContentIndexed = 0x00002000,
   139       Encrypted = 0x00004000,
   140     }
   141 
   142     protected enum DriveCommand : uint {
   143       GetVersion = 0x00074080,
   144       SendDriveCommand = 0x0007c084,
   145       ReceiveDriveData = 0x0007c088
   146     }
   147 
   148     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   149     protected struct CommandBlockRegisters {
   150       public byte Features;         
   151       public byte SectorCount;      
   152       public byte LBALow;       
   153       public byte LBAMid;           
   154       public byte LBAHigh;        
   155       public byte Device;       
   156       public byte Command;           
   157       public byte Reserved;                  
   158     }
   159 
   160     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   161     protected struct DriveCommandParameter {
   162       public uint BufferSize;           
   163       public CommandBlockRegisters Registers;           
   164       public byte DriveNumber;   
   165       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   166       public byte[] Reserved;                                
   167     }
   168 
   169     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   170     protected struct DriverStatus {
   171       public byte DriverError;   
   172       public byte IDEError;             
   173       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   174       public byte[] Reserved;               
   175     }
   176 
   177     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   178     protected struct DriveCommandResult {
   179       public uint BufferSize;
   180       public DriverStatus DriverStatus;
   181     } 
   182 
   183     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   184     protected struct DriveSmartReadResult {
   185       public uint BufferSize;           
   186       public DriverStatus DriverStatus;
   187       public byte Version;
   188       public byte Reserved;
   189       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   190       public DriveAttribute[] Attributes;                                                                                       
   191     }
   192 
   193     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   194     protected struct Identify {
   195       public ushort GeneralConfiguration;
   196       public ushort NumberOfCylinders;
   197       public ushort Reserved;
   198       public ushort NumberOfHeads;
   199       public ushort UnformattedBytesPerTrack;
   200       public ushort UnformattedBytesPerSector;
   201       public ushort SectorsPerTrack;
   202       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
   203       public ushort[] VendorUnique;
   204       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   205       public byte[] SerialNumber;
   206       public ushort BufferType;
   207       public ushort BufferSectorSize;
   208       public ushort NumberOfEccBytes;
   209       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   210       public byte[] FirmwareRevision;
   211       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
   212       public byte[] ModelNumber;
   213       public ushort MoreVendorUnique;
   214       public ushort DoubleWordIo;
   215       public ushort Capabilities;
   216       public ushort MoreReserved;
   217       public ushort PioCycleTimingMode;
   218       public ushort DmaCycleTimingMode;
   219       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
   220       public byte[] More;
   221     }
   222 
   223     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   224     protected struct DriveIdentifyResult {
   225       public uint BufferSize;
   226       public DriverStatus DriverStatus;
   227       public Identify Identify;
   228     } 
   229 
   230     public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
   231 
   232     private const byte SMART_CMD = 0xB0;
   233     private const byte ID_CMD = 0xEC;
   234     
   235     private const byte SMART_READ_DATA = 0xD0;
   236     private const byte SMART_ENABLE_OPERATIONS = 0xD8;
   237     
   238     private const byte SMART_LBA_MID = 0x4F;
   239     private const byte SMART_LBA_HI = 0xC2;
   240 
   241     private const int MAX_DRIVE_ATTRIBUTES = 512;
   242 
   243     private SMART() { }
   244 
   245     public static IntPtr OpenPhysicalDrive(int driveNumber) {
   246       return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber,
   247         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
   248         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
   249         IntPtr.Zero);
   250     }
   251 
   252     public static bool EnableSmart(IntPtr handle, int driveNumber) {
   253       DriveCommandParameter parameter = new DriveCommandParameter();
   254       DriveCommandResult result;
   255       uint bytesReturned;
   256 
   257       parameter.DriveNumber = (byte)driveNumber;
   258       parameter.Registers.Features = SMART_ENABLE_OPERATIONS;
   259       parameter.Registers.LBAMid = SMART_LBA_MID;
   260       parameter.Registers.LBAHigh = SMART_LBA_HI;
   261       parameter.Registers.Command = SMART_CMD;
   262 
   263       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
   264         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
   265         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
   266         IntPtr.Zero);
   267     }
   268 
   269     public static List<DriveAttribute> ReadSmart(IntPtr handle,
   270       int driveNumber)
   271     {
   272       DriveCommandParameter parameter = new DriveCommandParameter();
   273       DriveSmartReadResult result;
   274       uint bytesReturned;
   275 
   276       parameter.DriveNumber = (byte)driveNumber;
   277       parameter.Registers.Features = SMART_READ_DATA;
   278       parameter.Registers.LBAMid = SMART_LBA_MID;
   279       parameter.Registers.LBAHigh = SMART_LBA_HI;
   280       parameter.Registers.Command = SMART_CMD;
   281 
   282       bool isValid = NativeMethods.DeviceIoControl(handle, 
   283         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   284         out result, Marshal.SizeOf(typeof(DriveSmartReadResult)), 
   285         out bytesReturned, IntPtr.Zero);
   286 
   287       return (isValid)
   288         ? new List<DriveAttribute>(result.Attributes)
   289         : new List<DriveAttribute>();
   290     }
   291 
   292     public static string ReadName(IntPtr handle, int driveNumber) {
   293       DriveCommandParameter parameter = new DriveCommandParameter();
   294       DriveIdentifyResult result;
   295       uint bytesReturned;
   296 
   297       parameter.DriveNumber = (byte)driveNumber;
   298       parameter.Registers.Command = ID_CMD;
   299 
   300       bool valid = NativeMethods.DeviceIoControl(handle, 
   301         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   302         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
   303         out bytesReturned, IntPtr.Zero);
   304 
   305       if (!valid)
   306         return null;
   307       else {
   308 
   309         byte[] bytes = result.Identify.ModelNumber;
   310         char[] chars = new char[bytes.Length];
   311         for (int i = 0; i < bytes.Length; i += 2) {
   312           chars[i] = (char)bytes[i + 1];
   313           chars[i + 1] = (char)bytes[i];
   314         }
   315 
   316         return new string(chars).Trim();
   317       }
   318     }
   319 
   320     public static int CloseHandle(IntPtr handle) {
   321       return NativeMethods.CloseHandle(handle);
   322     }
   323 
   324     protected static class NativeMethods {
   325       private const string KERNEL = "kernel32.dll";
   326 
   327       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
   328         CharSet = CharSet.Unicode)]
   329       public static extern IntPtr CreateFile(string fileName,
   330         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   331         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
   332         IntPtr templateFilehandle);
   333 
   334       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   335       public static extern int CloseHandle(IntPtr handle);
   336 
   337       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   338       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   339       public static extern bool DeviceIoControl(IntPtr handle,
   340         DriveCommand command, ref DriveCommandParameter parameter,
   341         int parameterSize, out DriveSmartReadResult result, int resultSize,
   342         out uint bytesReturned, IntPtr overlapped);
   343 
   344       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   345       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   346       public static extern bool DeviceIoControl(IntPtr handle,
   347         DriveCommand command, ref DriveCommandParameter parameter,
   348         int parameterSize, out DriveCommandResult result, int resultSize,
   349         out uint bytesReturned, IntPtr overlapped);
   350 
   351       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   352       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   353       public static extern bool DeviceIoControl(IntPtr handle,
   354         DriveCommand command, ref DriveCommandParameter parameter,
   355         int parameterSize, out DriveIdentifyResult result, int resultSize,
   356         out uint bytesReturned, IntPtr overlapped);
   357     }    
   358   }
   359 }