Hardware/HDD/WindowsSmart.cs
author moel.mich
Mon, 02 Jan 2012 18:44:19 +0000
changeset 328 f837f9f0973e
parent 324 c6ee430d6995
child 344 3145aadca3d2
permissions -rw-r--r--
Added SMART support for Samsung SSDs.
     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-2012
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s): 
    23     Paul Werelds
    24     Roland Reinl <roland-reinl@gmx.de>
    25 
    26   Alternatively, the contents of this file may be used under the terms of
    27   either the GNU General Public License Version 2 or later (the "GPL"), or
    28   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    29   in which case the provisions of the GPL or the LGPL are applicable instead
    30   of those above. If you wish to allow use of your version of this file only
    31   under the terms of either the GPL or the LGPL, and not to allow others to
    32   use your version of this file under the terms of the MPL, indicate your
    33   decision by deleting the provisions above and replace them with the notice
    34   and other provisions required by the GPL or the LGPL. If you do not delete
    35   the provisions above, a recipient may use your version of this file under
    36   the terms of any one of the MPL, the GPL or the LGPL.
    37  
    38 */
    39 
    40 using System;
    41 using System.Collections.Generic;
    42 using System.Runtime.InteropServices;
    43 
    44 namespace OpenHardwareMonitor.Hardware.HDD {
    45 
    46   internal class WindowsSmart : ISmart {
    47     [Flags]
    48     protected enum AccessMode : uint {     
    49       Read = 0x80000000,    
    50       Write = 0x40000000,     
    51       Execute = 0x20000000,     
    52       All = 0x10000000
    53     }
    54 
    55     [Flags]
    56     protected enum ShareMode : uint {
    57       None = 0,     
    58       Read = 1,     
    59       Write = 2,    
    60       Delete = 4
    61     }
    62 
    63     protected enum CreationMode : uint {
    64       New = 1,
    65       CreateAlways = 2,    
    66       OpenExisting = 3,    
    67       OpenAlways = 4,    
    68       TruncateExisting = 5
    69     }
    70 
    71     [Flags]
    72     protected enum FileAttribute : uint {
    73       Readonly = 0x00000001,
    74       Hidden = 0x00000002,
    75       System = 0x00000004,
    76       Directory = 0x00000010,
    77       Archive = 0x00000020,
    78       Device = 0x00000040,
    79       Normal = 0x00000080,
    80       Temporary = 0x00000100,
    81       SparseFile = 0x00000200,
    82       ReparsePoint = 0x00000400,
    83       Compressed = 0x00000800,
    84       Offline = 0x00001000,
    85       NotContentIndexed = 0x00002000,
    86       Encrypted = 0x00004000,
    87     }
    88 
    89     protected enum DriveCommand : uint {
    90       GetVersion = 0x00074080,
    91       SendDriveCommand = 0x0007c084,
    92       ReceiveDriveData = 0x0007c088
    93     }
    94 
    95     protected enum RegisterCommand : byte {
    96       /// <summary>
    97       /// SMART data requested.
    98       /// </summary>
    99       SmartCmd = 0xB0,
   100 
   101       /// <summary>
   102       /// Identify data is requested.
   103       /// </summary>
   104       IdCmd = 0xEC,
   105     }
   106 
   107     protected enum RegisterFeature : byte {
   108       /// <summary>
   109       /// Read SMART data.
   110       /// </summary>
   111       SmartReadData = 0xD0,
   112 
   113       /// <summary>
   114       /// Read SMART thresholds.
   115       /// </summary>
   116       SmartReadThresholds = 0xD1, /* obsolete */
   117 
   118       /// <summary>
   119       /// Autosave SMART data.
   120       /// </summary>
   121       SmartAutosave = 0xD2,
   122 
   123       /// <summary>
   124       /// Save SMART attributes.
   125       /// </summary>
   126       SmartSaveAttr = 0xD3,
   127 
   128       /// <summary>
   129       /// Set SMART to offline immediately.
   130       /// </summary>
   131       SmartImmediateOffline = 0xD4,
   132 
   133       /// <summary>
   134       /// Read SMART log.
   135       /// </summary>
   136       SmartReadLog = 0xD5,
   137 
   138       /// <summary>
   139       /// Write SMART log.
   140       /// </summary>
   141       SmartWriteLog = 0xD6,
   142 
   143       /// <summary>
   144       /// Write SMART thresholds.
   145       /// </summary>
   146       SmartWriteThresholds = 0xD7, /* obsolete */
   147 
   148       /// <summary>
   149       /// Enable SMART.
   150       /// </summary>
   151       SmartEnableOperations = 0xD8,
   152 
   153       /// <summary>
   154       /// Disable SMART.
   155       /// </summary>
   156       SmartDisableOperations = 0xD9,
   157 
   158       /// <summary>
   159       /// Get SMART status.
   160       /// </summary>
   161       SmartStatus = 0xDA,
   162 
   163       /// <summary>
   164       /// Set SMART to offline automatically.
   165       /// </summary>
   166       SmartAutoOffline = 0xDB, /* obsolete */
   167     }
   168 
   169     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   170     protected struct CommandBlockRegisters {
   171       public RegisterFeature Features;         
   172       public byte SectorCount;      
   173       public byte LBALow;       
   174       public byte LBAMid;           
   175       public byte LBAHigh;        
   176       public byte Device;
   177       public RegisterCommand Command;           
   178       public byte Reserved;                  
   179     }
   180 
   181     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   182     protected struct DriveCommandParameter {
   183       public uint BufferSize;           
   184       public CommandBlockRegisters Registers;           
   185       public byte DriveNumber;   
   186       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   187       public byte[] Reserved;                                
   188     }
   189 
   190     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   191     protected struct DriverStatus {
   192       public byte DriverError;   
   193       public byte IDEError;             
   194       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   195       public byte[] Reserved;               
   196     }
   197 
   198     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   199     protected struct DriveCommandResult {
   200       public uint BufferSize;
   201       public DriverStatus DriverStatus;
   202     } 
   203 
   204     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   205     protected struct DriveSmartReadDataResult {
   206       public uint BufferSize;           
   207       public DriverStatus DriverStatus;
   208       public byte Version;
   209       public byte Reserved;
   210       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   211       public DriveAttributeValue[] Attributes;                                                                                       
   212     }
   213 
   214     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   215     protected struct DriveSmartReadThresholdsResult {
   216       public uint BufferSize;
   217       public DriverStatus DriverStatus;
   218       public byte Version;
   219       public byte Reserved;
   220       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   221       public DriveThresholdValue[] Thresholds;
   222     }
   223 
   224     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   225     protected struct Identify {
   226       public ushort GeneralConfiguration;
   227       public ushort NumberOfCylinders;
   228       public ushort Reserved;
   229       public ushort NumberOfHeads;
   230       public ushort UnformattedBytesPerTrack;
   231       public ushort UnformattedBytesPerSector;
   232       public ushort SectorsPerTrack;
   233       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
   234       public ushort[] VendorUnique;
   235       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   236       public byte[] SerialNumber;
   237       public ushort BufferType;
   238       public ushort BufferSectorSize;
   239       public ushort NumberOfEccBytes;
   240       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   241       public byte[] FirmwareRevision;
   242       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
   243       public byte[] ModelNumber;
   244       public ushort MoreVendorUnique;
   245       public ushort DoubleWordIo;
   246       public ushort Capabilities;
   247       public ushort MoreReserved;
   248       public ushort PioCycleTimingMode;
   249       public ushort DmaCycleTimingMode;
   250       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
   251       public byte[] More;
   252     }
   253 
   254     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   255     protected struct DriveIdentifyResult {
   256       public uint BufferSize;
   257       public DriverStatus DriverStatus;
   258       public Identify Identify;
   259     }
   260 
   261     public IntPtr InvalidHandle { get { return (IntPtr)(-1); } }
   262 
   263     private const byte SMART_LBA_MID = 0x4F;
   264     private const byte SMART_LBA_HI = 0xC2;
   265 
   266     private const int MAX_DRIVE_ATTRIBUTES = 512;
   267 
   268     public IntPtr OpenDrive(int driveNumber) {
   269       return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber,
   270         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
   271         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
   272         IntPtr.Zero);
   273     }
   274 
   275     public bool EnableSmart(IntPtr handle, int driveNumber) {
   276       DriveCommandParameter parameter = new DriveCommandParameter();
   277       DriveCommandResult result;
   278       uint bytesReturned;
   279 
   280       parameter.DriveNumber = (byte)driveNumber;
   281       parameter.Registers.Features = RegisterFeature.SmartEnableOperations;
   282       parameter.Registers.LBAMid = SMART_LBA_MID;
   283       parameter.Registers.LBAHigh = SMART_LBA_HI;
   284       parameter.Registers.Command = RegisterCommand.SmartCmd;
   285 
   286       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
   287         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
   288         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
   289         IntPtr.Zero);
   290     }
   291 
   292     public DriveAttributeValue[] ReadSmartData(IntPtr handle, int driveNumber) {
   293       DriveCommandParameter parameter = new DriveCommandParameter();
   294       DriveSmartReadDataResult result;
   295       uint bytesReturned;
   296 
   297       parameter.DriveNumber = (byte)driveNumber;
   298       parameter.Registers.Features = RegisterFeature.SmartReadData;
   299       parameter.Registers.LBAMid = SMART_LBA_MID;
   300       parameter.Registers.LBAHigh = SMART_LBA_HI;
   301       parameter.Registers.Command = RegisterCommand.SmartCmd;
   302 
   303       bool isValid = NativeMethods.DeviceIoControl(handle, 
   304         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   305         out result, Marshal.SizeOf(typeof(DriveSmartReadDataResult)), 
   306         out bytesReturned, IntPtr.Zero);
   307 
   308       return (isValid) ? result.Attributes : new DriveAttributeValue[0];
   309     }
   310 
   311     public DriveThresholdValue[] ReadSmartThresholds(IntPtr handle,
   312       int driveNumber) 
   313     {
   314       DriveCommandParameter parameter = new DriveCommandParameter();
   315       DriveSmartReadThresholdsResult result;
   316       uint bytesReturned = 0;
   317 
   318       parameter.DriveNumber = (byte)driveNumber;
   319       parameter.Registers.Features = RegisterFeature.SmartReadThresholds;
   320       parameter.Registers.LBAMid = SMART_LBA_MID;
   321       parameter.Registers.LBAHigh = SMART_LBA_HI;
   322       parameter.Registers.Command = RegisterCommand.SmartCmd;
   323 
   324       bool isValid = NativeMethods.DeviceIoControl(handle,
   325         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter),
   326         out result, Marshal.SizeOf(typeof(DriveSmartReadThresholdsResult)), 
   327         out bytesReturned, IntPtr.Zero); 
   328 
   329       return (isValid) ? result.Thresholds : new DriveThresholdValue[0];
   330     }
   331 
   332     private string GetString(byte[] bytes) {   
   333       char[] chars = new char[bytes.Length];
   334       for (int i = 0; i < bytes.Length; i += 2) {
   335         chars[i] = (char)bytes[i + 1];
   336         chars[i + 1] = (char)bytes[i];
   337       }
   338       return new string(chars).Trim(new char[] { ' ', '\0' });
   339     }
   340 
   341     public bool ReadNameAndFirmwareRevision(IntPtr handle, int driveNumber, 
   342       out string name, out string firmwareRevision) 
   343     {
   344       DriveCommandParameter parameter = new DriveCommandParameter();
   345       DriveIdentifyResult result;
   346       uint bytesReturned;
   347 
   348       parameter.DriveNumber = (byte)driveNumber;
   349       parameter.Registers.Command = RegisterCommand.IdCmd;
   350 
   351       bool valid = NativeMethods.DeviceIoControl(handle, 
   352         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   353         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
   354         out bytesReturned, IntPtr.Zero);
   355 
   356       if (!valid) {
   357         name = null;
   358         firmwareRevision = null;
   359         return false;
   360       }
   361 
   362       name = GetString(result.Identify.ModelNumber);
   363       firmwareRevision = GetString(result.Identify.FirmwareRevision);
   364       return true;
   365     }
   366 
   367     public void CloseHandle(IntPtr handle) {
   368       NativeMethods.CloseHandle(handle);
   369     }
   370 
   371     protected static class NativeMethods {
   372       private const string KERNEL = "kernel32.dll";
   373 
   374       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
   375         CharSet = CharSet.Unicode)]
   376       public static extern IntPtr CreateFile(string fileName,
   377         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   378         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
   379         IntPtr templateFilehandle);
   380 
   381       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   382       public static extern int CloseHandle(IntPtr handle);
   383 
   384       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   385       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   386       public static extern bool DeviceIoControl(IntPtr handle,
   387         DriveCommand command, ref DriveCommandParameter parameter,
   388         int parameterSize, out DriveSmartReadDataResult result, int resultSize,
   389         out uint bytesReturned, IntPtr overlapped);
   390 
   391       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   392       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   393       public static extern bool DeviceIoControl(IntPtr handle,
   394         DriveCommand command, ref DriveCommandParameter parameter,
   395         int parameterSize, out DriveSmartReadThresholdsResult result, 
   396         int resultSize, out uint bytesReturned, IntPtr overlapped);
   397 
   398       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   399       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   400       public static extern bool DeviceIoControl(IntPtr handle,
   401         DriveCommand command, ref DriveCommandParameter parameter,
   402         int parameterSize, out DriveCommandResult result, int resultSize,
   403         out uint bytesReturned, IntPtr overlapped);
   404 
   405       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   406       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   407       public static extern bool DeviceIoControl(IntPtr handle,
   408         DriveCommand command, ref DriveCommandParameter parameter,
   409         int parameterSize, out DriveIdentifyResult result, int resultSize,
   410         out uint bytesReturned, IntPtr overlapped);
   411     }    
   412   }
   413 }