moel@324: /*
moel@324:  
moel@344:   This Source Code Form is subject to the terms of the Mozilla Public
moel@344:   License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344:   file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@324:  
moel@344:   Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344: 	Copyright (C) 2010 Paul Werelds
moel@344:   Copyright (C) 2011 Roland Reinl <roland-reinl@gmx.de>
moel@344: 	
moel@324: */
moel@324: 
moel@324: using System;
moel@324: using System.Collections.Generic;
moel@324: using System.Runtime.InteropServices;
moel@324: 
moel@324: namespace OpenHardwareMonitor.Hardware.HDD {
moel@324: 
moel@324:   internal class WindowsSmart : ISmart {
moel@324:     [Flags]
moel@324:     protected enum AccessMode : uint {     
moel@324:       Read = 0x80000000,    
moel@324:       Write = 0x40000000,     
moel@324:       Execute = 0x20000000,     
moel@324:       All = 0x10000000
moel@324:     }
moel@324: 
moel@324:     [Flags]
moel@324:     protected enum ShareMode : uint {
moel@324:       None = 0,     
moel@324:       Read = 1,     
moel@324:       Write = 2,    
moel@324:       Delete = 4
moel@324:     }
moel@324: 
moel@324:     protected enum CreationMode : uint {
moel@324:       New = 1,
moel@324:       CreateAlways = 2,    
moel@324:       OpenExisting = 3,    
moel@324:       OpenAlways = 4,    
moel@324:       TruncateExisting = 5
moel@324:     }
moel@324: 
moel@324:     [Flags]
moel@324:     protected enum FileAttribute : uint {
moel@324:       Readonly = 0x00000001,
moel@324:       Hidden = 0x00000002,
moel@324:       System = 0x00000004,
moel@324:       Directory = 0x00000010,
moel@324:       Archive = 0x00000020,
moel@324:       Device = 0x00000040,
moel@324:       Normal = 0x00000080,
moel@324:       Temporary = 0x00000100,
moel@324:       SparseFile = 0x00000200,
moel@324:       ReparsePoint = 0x00000400,
moel@324:       Compressed = 0x00000800,
moel@324:       Offline = 0x00001000,
moel@324:       NotContentIndexed = 0x00002000,
moel@324:       Encrypted = 0x00004000,
moel@324:     }
moel@324: 
moel@324:     protected enum DriveCommand : uint {
moel@324:       GetVersion = 0x00074080,
moel@324:       SendDriveCommand = 0x0007c084,
moel@324:       ReceiveDriveData = 0x0007c088
moel@324:     }
moel@324: 
moel@324:     protected enum RegisterCommand : byte {
moel@324:       /// <summary>
moel@324:       /// SMART data requested.
moel@324:       /// </summary>
moel@324:       SmartCmd = 0xB0,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Identify data is requested.
moel@324:       /// </summary>
moel@324:       IdCmd = 0xEC,
moel@324:     }
moel@324: 
moel@324:     protected enum RegisterFeature : byte {
moel@324:       /// <summary>
moel@324:       /// Read SMART data.
moel@324:       /// </summary>
moel@324:       SmartReadData = 0xD0,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Read SMART thresholds.
moel@324:       /// </summary>
moel@324:       SmartReadThresholds = 0xD1, /* obsolete */
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Autosave SMART data.
moel@324:       /// </summary>
moel@324:       SmartAutosave = 0xD2,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Save SMART attributes.
moel@324:       /// </summary>
moel@324:       SmartSaveAttr = 0xD3,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Set SMART to offline immediately.
moel@324:       /// </summary>
moel@324:       SmartImmediateOffline = 0xD4,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Read SMART log.
moel@324:       /// </summary>
moel@324:       SmartReadLog = 0xD5,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Write SMART log.
moel@324:       /// </summary>
moel@324:       SmartWriteLog = 0xD6,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Write SMART thresholds.
moel@324:       /// </summary>
moel@324:       SmartWriteThresholds = 0xD7, /* obsolete */
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Enable SMART.
moel@324:       /// </summary>
moel@324:       SmartEnableOperations = 0xD8,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Disable SMART.
moel@324:       /// </summary>
moel@324:       SmartDisableOperations = 0xD9,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Get SMART status.
moel@324:       /// </summary>
moel@324:       SmartStatus = 0xDA,
moel@324: 
moel@324:       /// <summary>
moel@324:       /// Set SMART to offline automatically.
moel@324:       /// </summary>
moel@324:       SmartAutoOffline = 0xDB, /* obsolete */
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct CommandBlockRegisters {
moel@324:       public RegisterFeature Features;         
moel@324:       public byte SectorCount;      
moel@324:       public byte LBALow;       
moel@324:       public byte LBAMid;           
moel@324:       public byte LBAHigh;        
moel@324:       public byte Device;
moel@324:       public RegisterCommand Command;           
moel@324:       public byte Reserved;                  
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriveCommandParameter {
moel@324:       public uint BufferSize;           
moel@324:       public CommandBlockRegisters Registers;           
moel@324:       public byte DriveNumber;   
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
moel@324:       public byte[] Reserved;                                
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriverStatus {
moel@324:       public byte DriverError;   
moel@324:       public byte IDEError;             
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
moel@324:       public byte[] Reserved;               
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriveCommandResult {
moel@324:       public uint BufferSize;
moel@324:       public DriverStatus DriverStatus;
moel@324:     } 
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriveSmartReadDataResult {
moel@324:       public uint BufferSize;           
moel@324:       public DriverStatus DriverStatus;
moel@324:       public byte Version;
moel@324:       public byte Reserved;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
moel@324:       public DriveAttributeValue[] Attributes;                                                                                       
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriveSmartReadThresholdsResult {
moel@324:       public uint BufferSize;
moel@324:       public DriverStatus DriverStatus;
moel@324:       public byte Version;
moel@324:       public byte Reserved;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
moel@324:       public DriveThresholdValue[] Thresholds;
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct Identify {
moel@324:       public ushort GeneralConfiguration;
moel@324:       public ushort NumberOfCylinders;
moel@324:       public ushort Reserved;
moel@324:       public ushort NumberOfHeads;
moel@324:       public ushort UnformattedBytesPerTrack;
moel@324:       public ushort UnformattedBytesPerSector;
moel@324:       public ushort SectorsPerTrack;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
moel@324:       public ushort[] VendorUnique;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
moel@324:       public byte[] SerialNumber;
moel@324:       public ushort BufferType;
moel@324:       public ushort BufferSectorSize;
moel@324:       public ushort NumberOfEccBytes;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
moel@324:       public byte[] FirmwareRevision;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
moel@324:       public byte[] ModelNumber;
moel@324:       public ushort MoreVendorUnique;
moel@324:       public ushort DoubleWordIo;
moel@324:       public ushort Capabilities;
moel@324:       public ushort MoreReserved;
moel@324:       public ushort PioCycleTimingMode;
moel@324:       public ushort DmaCycleTimingMode;
moel@324:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
moel@324:       public byte[] More;
moel@324:     }
moel@324: 
moel@324:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@324:     protected struct DriveIdentifyResult {
moel@324:       public uint BufferSize;
moel@324:       public DriverStatus DriverStatus;
moel@324:       public Identify Identify;
moel@324:     }
moel@324: 
moel@324:     public IntPtr InvalidHandle { get { return (IntPtr)(-1); } }
moel@324: 
moel@324:     private const byte SMART_LBA_MID = 0x4F;
moel@324:     private const byte SMART_LBA_HI = 0xC2;
moel@324: 
moel@324:     private const int MAX_DRIVE_ATTRIBUTES = 512;
moel@324: 
moel@324:     public IntPtr OpenDrive(int driveNumber) {
moel@324:       return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber,
moel@324:         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
moel@324:         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
moel@324:         IntPtr.Zero);
moel@324:     }
moel@324: 
moel@324:     public bool EnableSmart(IntPtr handle, int driveNumber) {
moel@324:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@324:       DriveCommandResult result;
moel@324:       uint bytesReturned;
moel@324: 
moel@324:       parameter.DriveNumber = (byte)driveNumber;
moel@324:       parameter.Registers.Features = RegisterFeature.SmartEnableOperations;
moel@324:       parameter.Registers.LBAMid = SMART_LBA_MID;
moel@324:       parameter.Registers.LBAHigh = SMART_LBA_HI;
moel@324:       parameter.Registers.Command = RegisterCommand.SmartCmd;
moel@324: 
moel@324:       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
moel@324:         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
moel@324:         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
moel@324:         IntPtr.Zero);
moel@324:     }
moel@324: 
moel@324:     public DriveAttributeValue[] ReadSmartData(IntPtr handle, int driveNumber) {
moel@324:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@324:       DriveSmartReadDataResult result;
moel@324:       uint bytesReturned;
moel@324: 
moel@324:       parameter.DriveNumber = (byte)driveNumber;
moel@324:       parameter.Registers.Features = RegisterFeature.SmartReadData;
moel@324:       parameter.Registers.LBAMid = SMART_LBA_MID;
moel@324:       parameter.Registers.LBAHigh = SMART_LBA_HI;
moel@324:       parameter.Registers.Command = RegisterCommand.SmartCmd;
moel@324: 
moel@324:       bool isValid = NativeMethods.DeviceIoControl(handle, 
moel@324:         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
moel@324:         out result, Marshal.SizeOf(typeof(DriveSmartReadDataResult)), 
moel@324:         out bytesReturned, IntPtr.Zero);
moel@324: 
moel@324:       return (isValid) ? result.Attributes : new DriveAttributeValue[0];
moel@324:     }
moel@324: 
moel@324:     public DriveThresholdValue[] ReadSmartThresholds(IntPtr handle,
moel@324:       int driveNumber) 
moel@324:     {
moel@324:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@324:       DriveSmartReadThresholdsResult result;
moel@324:       uint bytesReturned = 0;
moel@324: 
moel@324:       parameter.DriveNumber = (byte)driveNumber;
moel@324:       parameter.Registers.Features = RegisterFeature.SmartReadThresholds;
moel@324:       parameter.Registers.LBAMid = SMART_LBA_MID;
moel@324:       parameter.Registers.LBAHigh = SMART_LBA_HI;
moel@324:       parameter.Registers.Command = RegisterCommand.SmartCmd;
moel@324: 
moel@324:       bool isValid = NativeMethods.DeviceIoControl(handle,
moel@324:         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter),
moel@324:         out result, Marshal.SizeOf(typeof(DriveSmartReadThresholdsResult)), 
moel@324:         out bytesReturned, IntPtr.Zero); 
moel@324: 
moel@324:       return (isValid) ? result.Thresholds : new DriveThresholdValue[0];
moel@325:     }
moel@324: 
moel@325:     private string GetString(byte[] bytes) {   
moel@325:       char[] chars = new char[bytes.Length];
moel@325:       for (int i = 0; i < bytes.Length; i += 2) {
moel@325:         chars[i] = (char)bytes[i + 1];
moel@325:         chars[i + 1] = (char)bytes[i];
moel@325:       }
moel@325:       return new string(chars).Trim(new char[] { ' ', '\0' });
moel@325:     }
moel@325: 
moel@325:     public bool ReadNameAndFirmwareRevision(IntPtr handle, int driveNumber, 
moel@325:       out string name, out string firmwareRevision) 
moel@325:     {
moel@324:       DriveCommandParameter parameter = new DriveCommandParameter();
moel@324:       DriveIdentifyResult result;
moel@324:       uint bytesReturned;
moel@324: 
moel@324:       parameter.DriveNumber = (byte)driveNumber;
moel@324:       parameter.Registers.Command = RegisterCommand.IdCmd;
moel@324: 
moel@324:       bool valid = NativeMethods.DeviceIoControl(handle, 
moel@324:         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
moel@324:         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
moel@324:         out bytesReturned, IntPtr.Zero);
moel@324: 
moel@325:       if (!valid) {
moel@325:         name = null;
moel@325:         firmwareRevision = null;
moel@325:         return false;
moel@325:       }
moel@324: 
moel@325:       name = GetString(result.Identify.ModelNumber);
moel@325:       firmwareRevision = GetString(result.Identify.FirmwareRevision);
moel@325:       return true;
moel@324:     }
moel@324: 
moel@324:     public void CloseHandle(IntPtr handle) {
moel@324:       NativeMethods.CloseHandle(handle);
moel@324:     }
moel@324: 
moel@324:     protected static class NativeMethods {
moel@324:       private const string KERNEL = "kernel32.dll";
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
moel@324:         CharSet = CharSet.Unicode)]
moel@324:       public static extern IntPtr CreateFile(string fileName,
moel@324:         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
moel@324:         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
moel@324:         IntPtr templateFilehandle);
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324:       public static extern int CloseHandle(IntPtr handle);
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@324:       public static extern bool DeviceIoControl(IntPtr handle,
moel@324:         DriveCommand command, ref DriveCommandParameter parameter,
moel@324:         int parameterSize, out DriveSmartReadDataResult result, int resultSize,
moel@324:         out uint bytesReturned, IntPtr overlapped);
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@324:       public static extern bool DeviceIoControl(IntPtr handle,
moel@324:         DriveCommand command, ref DriveCommandParameter parameter,
moel@324:         int parameterSize, out DriveSmartReadThresholdsResult result, 
moel@324:         int resultSize, out uint bytesReturned, IntPtr overlapped);
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@324:       public static extern bool DeviceIoControl(IntPtr handle,
moel@324:         DriveCommand command, ref DriveCommandParameter parameter,
moel@324:         int parameterSize, out DriveCommandResult result, int resultSize,
moel@324:         out uint bytesReturned, IntPtr overlapped);
moel@324: 
moel@324:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@324:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@324:       public static extern bool DeviceIoControl(IntPtr handle,
moel@324:         DriveCommand command, ref DriveCommandParameter parameter,
moel@324:         int parameterSize, out DriveIdentifyResult result, int resultSize,
moel@324:         out uint bytesReturned, IntPtr overlapped);
moel@324:     }    
moel@324:   }
moel@324: }