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