Hardware/HDD/SMART.cs
author moel.mich
Sun, 08 Aug 2010 13:57:26 +0000
changeset 165 813d8bc3192f
parent 48 eb04985b7b6a
child 167 b7cc9d09aefe
permissions -rw-r--r--
Refactored the hardware monitoring code into a library (Issue 101).
     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):
    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     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    82     public struct DriveAttribute {
    83       public AttributeID ID;
    84       public Status StatusFlags;
    85       public byte AttrValue;
    86       public byte WorstValue;
    87       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    88       public byte[] RawValue;
    89       public byte Reserved;
    90     };
    91 
    92     [Flags]
    93     private enum AccessMode : uint {     
    94       Read = 0x80000000,    
    95       Write = 0x40000000,     
    96       Execute = 0x20000000,     
    97       All = 0x10000000
    98     }
    99 
   100     [Flags]
   101     private enum ShareMode : uint {
   102       None = 0,     
   103       Read = 1,     
   104       Write = 2,    
   105       Delete = 4
   106     }
   107 
   108     private enum CreationMode : uint {
   109       New = 1,
   110       CreateAlways = 2,    
   111       OpenExisting = 3,    
   112       OpenAlways = 4,    
   113       TruncateExisting = 5
   114     }
   115 
   116     [Flags]
   117     private enum FileAttribute : uint {
   118       Readonly = 0x00000001,
   119       Hidden = 0x00000002,
   120       System = 0x00000004,
   121       Directory = 0x00000010,
   122       Archive = 0x00000020,
   123       Device = 0x00000040,
   124       Normal = 0x00000080,
   125       Temporary = 0x00000100,
   126       SparseFile = 0x00000200,
   127       ReparsePoint = 0x00000400,
   128       Compressed = 0x00000800,
   129       Offline = 0x00001000,
   130       NotContentIndexed = 0x00002000,
   131       Encrypted = 0x00004000,
   132     }
   133 
   134     private enum DriveCommand : uint {
   135       GetVersion = 0x00074080,
   136       SendDriveCommand = 0x0007c084,
   137       ReceiveDriveData = 0x0007c088
   138     }
   139 
   140     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   141     private struct CommandBlockRegisters {
   142       public byte Features;         
   143       public byte SectorCount;      
   144       public byte LBALow;       
   145       public byte LBAMid;           
   146       public byte LBAHigh;        
   147       public byte Device;       
   148       public byte Command;           
   149       public byte Reserved;                  
   150     }
   151 
   152     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   153     private struct DriveCommandParameter {
   154       private uint BufferSize;           
   155       public CommandBlockRegisters Registers;           
   156       public byte DriveNumber;   
   157       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)]
   158       public byte[] Reserved;                                
   159     }
   160 
   161     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   162     private struct DriverStatus {
   163       public byte DriverError;   
   164       public byte IDEError;             
   165       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   166       public byte[] Reserved;               
   167     }
   168 
   169     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   170     private struct DriveCommandResult {
   171       public uint BufferSize;
   172       public DriverStatus DriverStatus;
   173     } 
   174 
   175     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   176     private struct DriveSmartReadResult {
   177       public uint BufferSize;           
   178       public DriverStatus DriverStatus;
   179       public byte Version;
   180       public byte Reserved;
   181       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   182       public DriveAttribute[] Attributes;                                                                                       
   183     }
   184 
   185     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   186     private struct Identify {
   187       public ushort GeneralConfiguration;
   188       public ushort NumberOfCylinders;
   189       public ushort Reserved;
   190       public ushort NumberOfHeads;
   191       public ushort UnformattedBytesPerTrack;
   192       public ushort UnformattedBytesPerSector;
   193       public ushort SectorsPerTrack;
   194       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
   195       public ushort[] VendorUnique;
   196       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   197       public byte[] SerialNumber;
   198       public ushort BufferType;
   199       public ushort BufferSectorSize;
   200       public ushort NumberOfEccBytes;
   201       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   202       public byte[] FirmwareRevision;
   203       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
   204       public byte[] ModelNumber;
   205       public ushort MoreVendorUnique;
   206       public ushort DoubleWordIo;
   207       public ushort Capabilities;
   208       public ushort MoreReserved;
   209       public ushort PioCycleTimingMode;
   210       public ushort DmaCycleTimingMode;
   211       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
   212       public byte[] More;
   213     }
   214 
   215     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   216     private struct DriveIdentifyResult {
   217       public uint BufferSize;
   218       public DriverStatus DriverStatus;
   219       public Identify Identify;
   220     } 
   221 
   222     public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
   223 
   224     private const byte SMART_CMD = 0xB0;
   225     private const byte ID_CMD = 0xEC;
   226     
   227     private const byte SMART_READ_DATA = 0xD0;
   228     private const byte SMART_ENABLE_OPERATIONS = 0xD8;
   229     
   230     private const byte SMART_LBA_MID = 0x4F;
   231     private const byte SMART_LBA_HI = 0xC2;
   232 
   233     private const int MAX_DRIVE_ATTRIBUTES = 512;
   234 
   235     private const string KERNEL = "kernel32.dll";
   236 
   237     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   238     private static extern IntPtr CreateFile(string fileName, 
   239       AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   240       CreationMode creationDisposition, FileAttribute flagsAndAttributes, 
   241       IntPtr templateFilehandle);
   242 
   243     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   244     public static extern int CloseHandle(IntPtr handle);
   245 
   246     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   247     private static extern bool DeviceIoControl(IntPtr handle,
   248       DriveCommand command, ref DriveCommandParameter parameter,
   249       int parameterSize, out DriveSmartReadResult result, int resultSize, 
   250       out uint bytesReturned, IntPtr overlapped);
   251 
   252     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   253     private static extern bool DeviceIoControl(IntPtr handle,
   254       DriveCommand command, ref DriveCommandParameter parameter,
   255       int parameterSize, out DriveCommandResult result, int resultSize,
   256       out uint bytesReturned, IntPtr overlapped);
   257 
   258     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   259     private static extern bool DeviceIoControl(IntPtr handle,
   260       DriveCommand command, ref DriveCommandParameter parameter,
   261       int parameterSize, out DriveIdentifyResult result, int resultSize,
   262       out uint bytesReturned, IntPtr overlapped);
   263 
   264     public static IntPtr OpenPhysicalDrive(int driveNumber) {
   265       return CreateFile(@"\\.\PhysicalDrive" + driveNumber,
   266         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
   267         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
   268         IntPtr.Zero);
   269     }
   270 
   271     public static bool EnableSmart(IntPtr handle, int driveNumber) {
   272       DriveCommandParameter parameter = new DriveCommandParameter();
   273       DriveCommandResult result;
   274       uint bytesReturned;
   275 
   276       parameter.DriveNumber = (byte)driveNumber;
   277       parameter.Registers.Features = SMART_ENABLE_OPERATIONS;
   278       parameter.Registers.LBAMid = SMART_LBA_MID;
   279       parameter.Registers.LBAHigh = SMART_LBA_HI;
   280       parameter.Registers.Command = SMART_CMD;
   281 
   282       return DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
   283         ref parameter, Marshal.SizeOf(parameter), out result,
   284         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
   285         IntPtr.Zero);
   286     }
   287 
   288     public static DriveAttribute[] ReadSmart(IntPtr handle, int driveNumber) {
   289       DriveCommandParameter parameter = new DriveCommandParameter();
   290       DriveSmartReadResult result;
   291       uint bytesReturned;
   292 
   293       parameter.DriveNumber = (byte)driveNumber;
   294       parameter.Registers.Features = SMART_READ_DATA;
   295       parameter.Registers.LBAMid = SMART_LBA_MID;
   296       parameter.Registers.LBAHigh = SMART_LBA_HI;
   297       parameter.Registers.Command = SMART_CMD;
   298 
   299       bool valid = DeviceIoControl(handle, DriveCommand.ReceiveDriveData,
   300         ref parameter, Marshal.SizeOf(parameter), out result,
   301         Marshal.SizeOf(typeof(DriveSmartReadResult)), out bytesReturned,
   302         IntPtr.Zero);
   303 
   304       if (!valid)
   305         return null;
   306       else
   307         return result.Attributes;
   308     }
   309 
   310     public static string ReadName(IntPtr handle, int driveNumber) {
   311       DriveCommandParameter parameter = new DriveCommandParameter();
   312       DriveIdentifyResult result;
   313       uint bytesReturned;
   314 
   315       parameter.DriveNumber = (byte)driveNumber;
   316       parameter.Registers.Command = ID_CMD;
   317 
   318       bool valid = DeviceIoControl(handle, DriveCommand.ReceiveDriveData,
   319         ref parameter, Marshal.SizeOf(parameter), out result,
   320         Marshal.SizeOf(typeof(DriveIdentifyResult)), out bytesReturned,
   321         IntPtr.Zero);
   322 
   323       if (!valid)
   324         return null;
   325       else {
   326 
   327         byte[] bytes = result.Identify.ModelNumber;
   328         char[] chars = new char[bytes.Length];
   329         for (int i = 0; i < bytes.Length; i += 2) {
   330           chars[i] = (char)bytes[i + 1];
   331           chars[i + 1] = (char)bytes[i];
   332         }
   333 
   334         return new string(chars).Trim();
   335       }
   336     }
   337 
   338   }
   339 }