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