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 . 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@165: moel@165: internal 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: }