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.
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@324
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2011
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@324
   330
    } 
moel@324
   331
moel@324
   332
    public string ReadName(IntPtr handle, int driveNumber) {
moel@324
   333
      DriveCommandParameter parameter = new DriveCommandParameter();
moel@324
   334
      DriveIdentifyResult result;
moel@324
   335
      uint bytesReturned;
moel@324
   336
moel@324
   337
      parameter.DriveNumber = (byte)driveNumber;
moel@324
   338
      parameter.Registers.Command = RegisterCommand.IdCmd;
moel@324
   339
moel@324
   340
      bool valid = NativeMethods.DeviceIoControl(handle, 
moel@324
   341
        DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
moel@324
   342
        out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
moel@324
   343
        out bytesReturned, IntPtr.Zero);
moel@324
   344
moel@324
   345
      if (!valid)
moel@324
   346
        return null;
moel@324
   347
      else {
moel@324
   348
moel@324
   349
        byte[] bytes = result.Identify.ModelNumber;
moel@324
   350
        char[] chars = new char[bytes.Length];
moel@324
   351
        for (int i = 0; i < bytes.Length; i += 2) {
moel@324
   352
          chars[i] = (char)bytes[i + 1];
moel@324
   353
          chars[i + 1] = (char)bytes[i];
moel@324
   354
        }
moel@324
   355
moel@324
   356
        return new string(chars).Trim(new char[] {' ', '\0'});
moel@324
   357
      }
moel@324
   358
    }
moel@324
   359
moel@324
   360
    public void CloseHandle(IntPtr handle) {
moel@324
   361
      NativeMethods.CloseHandle(handle);
moel@324
   362
    }
moel@324
   363
moel@324
   364
    protected static class NativeMethods {
moel@324
   365
      private const string KERNEL = "kernel32.dll";
moel@324
   366
moel@324
   367
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
moel@324
   368
        CharSet = CharSet.Unicode)]
moel@324
   369
      public static extern IntPtr CreateFile(string fileName,
moel@324
   370
        AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
moel@324
   371
        CreationMode creationDisposition, FileAttribute flagsAndAttributes,
moel@324
   372
        IntPtr templateFilehandle);
moel@324
   373
moel@324
   374
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324
   375
      public static extern int CloseHandle(IntPtr handle);
moel@324
   376
moel@324
   377
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324
   378
      [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@324
   379
      public static extern bool DeviceIoControl(IntPtr handle,
moel@324
   380
        DriveCommand command, ref DriveCommandParameter parameter,
moel@324
   381
        int parameterSize, out DriveSmartReadDataResult result, int resultSize,
moel@324
   382
        out uint bytesReturned, IntPtr overlapped);
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 DriveSmartReadThresholdsResult result, 
moel@324
   389
        int resultSize, 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 DriveCommandResult result, int resultSize,
moel@324
   396
        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 DriveIdentifyResult result, int resultSize,
moel@324
   403
        out uint bytesReturned, IntPtr overlapped);
moel@324
   404
    }    
moel@324
   405
  }
moel@324
   406
}