Hardware/HDD/WindowsSmart.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
child 325 4c31341a4800
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
     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-2011
    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     public string ReadName(IntPtr handle, int driveNumber) {
   333       DriveCommandParameter parameter = new DriveCommandParameter();
   334       DriveIdentifyResult result;
   335       uint bytesReturned;
   336 
   337       parameter.DriveNumber = (byte)driveNumber;
   338       parameter.Registers.Command = RegisterCommand.IdCmd;
   339 
   340       bool valid = NativeMethods.DeviceIoControl(handle, 
   341         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   342         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
   343         out bytesReturned, IntPtr.Zero);
   344 
   345       if (!valid)
   346         return null;
   347       else {
   348 
   349         byte[] bytes = result.Identify.ModelNumber;
   350         char[] chars = new char[bytes.Length];
   351         for (int i = 0; i < bytes.Length; i += 2) {
   352           chars[i] = (char)bytes[i + 1];
   353           chars[i + 1] = (char)bytes[i];
   354         }
   355 
   356         return new string(chars).Trim(new char[] {' ', '\0'});
   357       }
   358     }
   359 
   360     public void CloseHandle(IntPtr handle) {
   361       NativeMethods.CloseHandle(handle);
   362     }
   363 
   364     protected static class NativeMethods {
   365       private const string KERNEL = "kernel32.dll";
   366 
   367       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
   368         CharSet = CharSet.Unicode)]
   369       public static extern IntPtr CreateFile(string fileName,
   370         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   371         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
   372         IntPtr templateFilehandle);
   373 
   374       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   375       public static extern int CloseHandle(IntPtr handle);
   376 
   377       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   378       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   379       public static extern bool DeviceIoControl(IntPtr handle,
   380         DriveCommand command, ref DriveCommandParameter parameter,
   381         int parameterSize, out DriveSmartReadDataResult result, int resultSize,
   382         out uint bytesReturned, IntPtr overlapped);
   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 DriveSmartReadThresholdsResult result, 
   389         int resultSize, 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 DriveCommandResult result, int resultSize,
   396         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 DriveIdentifyResult result, int resultSize,
   403         out uint bytesReturned, IntPtr overlapped);
   404     }    
   405   }
   406 }