moel@1: /*
moel@1:   
moel@1:   Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@1: 
moel@1:   The contents of this file are subject to the Mozilla Public License Version
moel@1:   1.1 (the "License"); you may not use this file except in compliance with
moel@1:   the License. You may obtain a copy of the License at
moel@1:  
moel@1:   http://www.mozilla.org/MPL/
moel@1: 
moel@1:   Software distributed under the License is distributed on an "AS IS" basis,
moel@1:   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@1:   for the specific language governing rights and limitations under the License.
moel@1: 
moel@1:   The Original Code is the Open Hardware Monitor code.
moel@1: 
moel@1:   The Initial Developer of the Original Code is 
moel@1:   Michael Möller <m.moeller@gmx.ch>.
moel@1:   Portions created by the Initial Developer are Copyright (C) 2009-2010
moel@1:   the Initial Developer. All Rights Reserved.
moel@1: 
moel@1:   Contributor(s):
moel@1: 
moel@1:   Alternatively, the contents of this file may be used under the terms of
moel@1:   either the GNU General Public License Version 2 or later (the "GPL"), or
moel@1:   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@1:   in which case the provisions of the GPL or the LGPL are applicable instead
moel@1:   of those above. If you wish to allow use of your version of this file only
moel@1:   under the terms of either the GPL or the LGPL, and not to allow others to
moel@1:   use your version of this file under the terms of the MPL, indicate your
moel@1:   decision by deleting the provisions above and replace them with the notice
moel@1:   and other provisions required by the GPL or the LGPL. If you do not delete
moel@1:   the provisions above, a recipient may use your version of this file under
moel@1:   the terms of any one of the MPL, the GPL or the LGPL.
moel@1:  
moel@1: */
moel@1: 
moel@1: using System;
moel@1: using System.Collections.Generic;
moel@1: using System.Runtime.InteropServices;
moel@1: 
moel@1: namespace OpenHardwareMonitor.Hardware.HDD {
moel@1:     
moel@1:   public class SMART {
moel@1: 
moel@1:     [Flags]
moel@1:     public enum Status : ushort {
moel@1:       PreFailureWarranty = 0x01,
moel@1:       OnLineCollection = 0x02,
moel@1:       Performance = 0x04,
moel@1:       ErrorRate = 0x08,
moel@1:       EventCount = 0x10,
moel@1:       SelfPreserving = 0x20
moel@1:     }
moel@1: 
moel@1:     public enum AttributeID : byte {
moel@1:       ReadErrorRate = 0x01,
moel@1:       ThroughputPerformance = 0x02,
moel@1:       SpinUpTime = 0x03,
moel@1:       StartStopCount = 0x04,
moel@1:       ReallocatedSectorsCount = 0x05,
moel@1:       ReadChannelMargin = 0x06,
moel@1:       SeekErrorRate = 0x07,
moel@1:       SeekTimePerformance = 0x08,
moel@1:       PowerOnHours = 0x09,
moel@1:       SpinRetryCount = 0x0A,
moel@1:       RecalibrationRetries = 0x0B,
moel@1:       PowerCycleCount = 0x0C,
moel@1:       SoftReadErrorRate = 0x0D,
moel@48:       AirflowTemperature = 0xBE,
moel@1:       Temperature = 0xC2,
moel@1:       HardwareECCRecovered = 0xC3,
moel@1:       ReallocationEventCount = 0xC4,
moel@1:       CurrentPendingSectorCount = 0xC5,
moel@1:       UncorrectableSectorCount = 0xC6,
moel@1:       UltraDMACRCErrorCount = 0xC7,
moel@48:       WriteErrorRate = 0xC8,
moel@48:       DriveTemperature = 0xE7
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     public struct DriveAttribute {
moel@1:       public AttributeID ID;
moel@1:       public Status StatusFlags;
moel@1:       public byte AttrValue;
moel@1:       public byte WorstValue;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
moel@1:       public byte[] RawValue;
moel@1:       public byte Reserved;
moel@1:     };
moel@1: 
moel@1:     [Flags]
moel@1:     private enum AccessMode : uint {     
moel@1:       Read = 0x80000000,    
moel@1:       Write = 0x40000000,     
moel@1:       Execute = 0x20000000,     
moel@1:       All = 0x10000000
moel@1:     }
moel@1: 
moel@1:     [Flags]
moel@1:     private enum ShareMode : uint {
moel@1:       None = 0,     
moel@1:       Read = 1,     
moel@1:       Write = 2,    
moel@1:       Delete = 4
moel@1:     }
moel@1: 
moel@1:     private enum CreationMode : uint {
moel@1:       New = 1,
moel@1:       CreateAlways = 2,    
moel@1:       OpenExisting = 3,    
moel@1:       OpenAlways = 4,    
moel@1:       TruncateExisting = 5
moel@1:     }
moel@1: 
moel@1:     [Flags]
moel@1:     private enum FileAttribute : uint {
moel@1:       Readonly = 0x00000001,
moel@1:       Hidden = 0x00000002,
moel@1:       System = 0x00000004,
moel@1:       Directory = 0x00000010,
moel@1:       Archive = 0x00000020,
moel@1:       Device = 0x00000040,
moel@1:       Normal = 0x00000080,
moel@1:       Temporary = 0x00000100,
moel@1:       SparseFile = 0x00000200,
moel@1:       ReparsePoint = 0x00000400,
moel@1:       Compressed = 0x00000800,
moel@1:       Offline = 0x00001000,
moel@1:       NotContentIndexed = 0x00002000,
moel@1:       Encrypted = 0x00004000,
moel@1:     }
moel@1: 
moel@1:     private enum DriveCommand : uint {
moel@1:       GetVersion = 0x00074080,
moel@1:       SendDriveCommand = 0x0007c084,
moel@1:       ReceiveDriveData = 0x0007c088
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct CommandBlockRegisters {
moel@1:       public byte Features;         
moel@1:       public byte SectorCount;      
moel@1:       public byte LBALow;       
moel@1:       public byte LBAMid;           
moel@1:       public byte LBAHigh;        
moel@1:       public byte Device;       
moel@1:       public byte Command;           
moel@1:       public byte Reserved;                  
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct DriveCommandParameter {
moel@1:       private uint BufferSize;           
moel@1:       public CommandBlockRegisters Registers;           
moel@1:       public byte DriveNumber;   
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)]
moel@1:       public byte[] Reserved;                                
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct DriverStatus {
moel@1:       public byte DriverError;   
moel@1:       public byte IDEError;             
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
moel@1:       public byte[] Reserved;               
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct DriveCommandResult {
moel@1:       public uint BufferSize;
moel@1:       public DriverStatus DriverStatus;
moel@1:     } 
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct DriveSmartReadResult {
moel@1:       public uint BufferSize;           
moel@1:       public DriverStatus DriverStatus;
moel@1:       public byte Version;
moel@1:       public byte Reserved;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
moel@1:       public DriveAttribute[] Attributes;                                                                                       
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct Identify {
moel@1:       public ushort GeneralConfiguration;
moel@1:       public ushort NumberOfCylinders;
moel@1:       public ushort Reserved;
moel@1:       public ushort NumberOfHeads;
moel@1:       public ushort UnformattedBytesPerTrack;
moel@1:       public ushort UnformattedBytesPerSector;
moel@1:       public ushort SectorsPerTrack;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
moel@1:       public ushort[] VendorUnique;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
moel@1:       public byte[] SerialNumber;
moel@1:       public ushort BufferType;
moel@1:       public ushort BufferSectorSize;
moel@1:       public ushort NumberOfEccBytes;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
moel@1:       public byte[] FirmwareRevision;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
moel@1:       public byte[] ModelNumber;
moel@1:       public ushort MoreVendorUnique;
moel@1:       public ushort DoubleWordIo;
moel@1:       public ushort Capabilities;
moel@1:       public ushort MoreReserved;
moel@1:       public ushort PioCycleTimingMode;
moel@1:       public ushort DmaCycleTimingMode;
moel@1:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
moel@1:       public byte[] More;
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@1:     private struct DriveIdentifyResult {
moel@1:       public uint BufferSize;
moel@1:       public DriverStatus DriverStatus;
moel@1:       public Identify Identify;
moel@1:     } 
moel@1: 
moel@1:     public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
moel@1: 
moel@1:     private const byte SMART_CMD = 0xB0;
moel@1:     private const byte ID_CMD = 0xEC;
moel@1:     
moel@1:     private const byte SMART_READ_DATA = 0xD0;
moel@1:     private const byte SMART_ENABLE_OPERATIONS = 0xD8;
moel@1:     
moel@1:     private const byte SMART_LBA_MID = 0x4F;
moel@1:     private const byte SMART_LBA_HI = 0xC2;
moel@1: 
moel@1:     private const int MAX_DRIVE_ATTRIBUTES = 512;
moel@1: 
moel@1:     private const string KERNEL = "kernel32.dll";
moel@1: 
moel@1:     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@1:     private static extern IntPtr CreateFile(string fileName, 
moel@1:       AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
moel@1:       CreationMode creationDisposition, FileAttribute flagsAndAttributes, 
moel@1:       IntPtr templateFilehandle);
moel@1: 
moel@1:     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@1:     public static extern int CloseHandle(IntPtr handle);
moel@1: 
moel@1:     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@1:     private static extern bool DeviceIoControl(IntPtr handle,
moel@1:       DriveCommand command, ref DriveCommandParameter parameter,
moel@1:       int parameterSize, out DriveSmartReadResult result, int resultSize, 
moel@1:       out uint bytesReturned, IntPtr overlapped);
moel@1: 
moel@1:     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@1:     private static extern bool DeviceIoControl(IntPtr handle,
moel@1:       DriveCommand command, ref DriveCommandParameter parameter,
moel@1:       int parameterSize, out DriveCommandResult result, int resultSize,
moel@1:       out uint bytesReturned, IntPtr overlapped);
moel@1: 
moel@1:     [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@1:     private static extern bool DeviceIoControl(IntPtr handle,
moel@1:       DriveCommand command, ref DriveCommandParameter parameter,
moel@1:       int parameterSize, out DriveIdentifyResult result, int resultSize,
moel@1:       out uint bytesReturned, IntPtr overlapped);
moel@1: 
moel@1:     public static IntPtr OpenPhysicalDrive(int driveNumber) {
moel@1:       return CreateFile(@"\\.\PhysicalDrive" + driveNumber,
moel@1:         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
moel@1:         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
moel@1:         IntPtr.Zero);
moel@1:     }
moel@1: 
moel@1:     public static bool EnableSmart(IntPtr handle, int driveNumber) {
moel@1:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@1:       DriveCommandResult result;
moel@1:       uint bytesReturned;
moel@1: 
moel@1:       parameter.DriveNumber = (byte)driveNumber;
moel@1:       parameter.Registers.Features = SMART_ENABLE_OPERATIONS;
moel@1:       parameter.Registers.LBAMid = SMART_LBA_MID;
moel@1:       parameter.Registers.LBAHigh = SMART_LBA_HI;
moel@1:       parameter.Registers.Command = SMART_CMD;
moel@1: 
moel@1:       return DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
moel@1:         ref parameter, Marshal.SizeOf(parameter), out result,
moel@1:         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
moel@1:         IntPtr.Zero);
moel@1:     }
moel@1: 
moel@1:     public static DriveAttribute[] ReadSmart(IntPtr handle, int driveNumber) {
moel@1:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@1:       DriveSmartReadResult result;
moel@1:       uint bytesReturned;
moel@1: 
moel@1:       parameter.DriveNumber = (byte)driveNumber;
moel@1:       parameter.Registers.Features = SMART_READ_DATA;
moel@1:       parameter.Registers.LBAMid = SMART_LBA_MID;
moel@1:       parameter.Registers.LBAHigh = SMART_LBA_HI;
moel@1:       parameter.Registers.Command = SMART_CMD;
moel@1: 
moel@1:       bool valid = DeviceIoControl(handle, DriveCommand.ReceiveDriveData,
moel@1:         ref parameter, Marshal.SizeOf(parameter), out result,
moel@1:         Marshal.SizeOf(typeof(DriveSmartReadResult)), out bytesReturned,
moel@1:         IntPtr.Zero);
moel@1: 
moel@1:       if (!valid)
moel@1:         return null;
moel@1:       else
moel@1:         return result.Attributes;
moel@1:     }
moel@1: 
moel@1:     public static string ReadName(IntPtr handle, int driveNumber) {
moel@1:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@1:       DriveIdentifyResult result;
moel@1:       uint bytesReturned;
moel@1: 
moel@1:       parameter.DriveNumber = (byte)driveNumber;
moel@1:       parameter.Registers.Command = ID_CMD;
moel@1: 
moel@1:       bool valid = DeviceIoControl(handle, DriveCommand.ReceiveDriveData,
moel@1:         ref parameter, Marshal.SizeOf(parameter), out result,
moel@1:         Marshal.SizeOf(typeof(DriveIdentifyResult)), out bytesReturned,
moel@1:         IntPtr.Zero);
moel@1: 
moel@1:       if (!valid)
moel@1:         return null;
moel@1:       else {
moel@1: 
moel@1:         byte[] bytes = result.Identify.ModelNumber;
moel@1:         char[] chars = new char[bytes.Length];
moel@1:         for (int i = 0; i < bytes.Length; i += 2) {
moel@1:           chars[i] = (char)bytes[i + 1];
moel@1:           chars[i + 1] = (char)bytes[i];
moel@1:         }
moel@1: 
moel@1:         return new string(chars).Trim();
moel@1:       }
moel@1:     }
moel@1: 
moel@1:   }
moel@1: }