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: 
paulwerelds@218:   Contributor(s): Paul Werelds
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;
paulwerelds@218: 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@231:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@231:     public struct AttributeID {
moel@231:       private byte value;
moel@231: 
moel@231:       public AttributeID(byte value) {
moel@231:         this.value = value;
moel@231:       }
moel@231: 
moel@231:       public override bool Equals(Object obj) {
moel@231:         return obj is AttributeID && this == (AttributeID)obj;
moel@231:       }
moel@231:       public override int GetHashCode() {
moel@231:         return value.GetHashCode() ^ value.GetHashCode();
moel@231:       }
moel@231:       public static bool operator ==(AttributeID a, AttributeID b) {
moel@231:         return a.value == b.value;
moel@231:       }
moel@231:       public static bool operator !=(AttributeID a, AttributeID b) {
moel@231:         return !(a == b);
moel@231:       }
moel@231: 
moel@231:       public string ToString(string format) {
moel@231:         return value.ToString(format);
moel@231:       }
moel@231: 
moel@231:       public static readonly AttributeID None = new AttributeID(0x00);
moel@1:     }
moel@1: 
paulwerelds@233:     // These are the more-or-less standard S.M.A.R.T attributes
paulwerelds@233:     // TODO: Filter out unused/obscure ones; some are interpreted differently
paulwerelds@233:     // between manufacturers
paulwerelds@233:     public static class CommonAttributes {
paulwerelds@233:       public static readonly AttributeID
paulwerelds@233:         ReadErrorRate = new AttributeID(0x01),
paulwerelds@233:         ThroughputPerformance = new AttributeID(0x02),
paulwerelds@233:         SpinUpTime = new AttributeID(0x03),
paulwerelds@233:         StartStopCount = new AttributeID(0x04),
paulwerelds@233:         ReallocatedSectorsCount = new AttributeID(0x05),
paulwerelds@233:         ReadChannelMargin = new AttributeID(0x06),
paulwerelds@233:         SeekErrorRate = new AttributeID(0x07),
paulwerelds@233:         SeekTimePerformance = new AttributeID(0x08),
paulwerelds@233:         PowerOnHours = new AttributeID(0x09),
paulwerelds@233:         SpinRetryCount = new AttributeID(0x0A),
paulwerelds@233:         RecalibrationRetries = new AttributeID(0x0B),
paulwerelds@233:         PowerCycleCount = new AttributeID(0x0C),
paulwerelds@233:         SoftReadErrorRate = new AttributeID(0x0D),
paulwerelds@233:         SataDownshiftErrorCount = new AttributeID(0xB7),
paulwerelds@233:         EndToEndError = new AttributeID(0xB8),
paulwerelds@233:         HeadStability = new AttributeID(0xB9),
paulwerelds@233:         InducedOpVibrationDetection = new AttributeID(0xBA),
paulwerelds@233:         ReportedUncorrectableErrors = new AttributeID(0xBB),
paulwerelds@233:         CommandTimeout = new AttributeID(0xBC),
paulwerelds@233:         HighFlyWrites = new AttributeID(0xBD),
paulwerelds@233:         AirflowTemperature = new AttributeID(0xBE),
paulwerelds@233:         GSenseErrorRate = new AttributeID(0xBF),
paulwerelds@233:         PowerOffRetractCount = new AttributeID(0xC0),
paulwerelds@233:         LoadCycleCount = new AttributeID(0xC1),
paulwerelds@233:         Temperature = new AttributeID(0xC2),
paulwerelds@233:         HardwareEccRecovered = new AttributeID(0xC3),
paulwerelds@233:         ReallocationEventCount = new AttributeID(0xC4),
paulwerelds@233:         CurrentPendingSectorCount = new AttributeID(0xC5),
paulwerelds@233:         UncorrectableSectorCount = new AttributeID(0xC6),
paulwerelds@233:         UltraDmaCrcErrorCount = new AttributeID(0xC7),
paulwerelds@233:         WriteErrorRate = new AttributeID(0xC8),
paulwerelds@233:         DataAddressMarkerrors = new AttributeID(0xCA),
paulwerelds@233:         RunOutCancel = new AttributeID(0xCB),
paulwerelds@233:         SoftEccCorrection = new AttributeID(0xCC),
paulwerelds@233:         ThermalAsperityRate = new AttributeID(0xCD),
paulwerelds@233:         FlyingHeight = new AttributeID(0xCE),
paulwerelds@233:         SpinHighCurrent = new AttributeID(0xCF),
paulwerelds@233:         SpinBuzz = new AttributeID(0xD0),
paulwerelds@233:         OfflineSeekPerformance = new AttributeID(0xD1),
paulwerelds@233:         VibrationDuringWrite = new AttributeID(0xD3),
paulwerelds@233:         ShockDuringWrite = new AttributeID(0xD4),
paulwerelds@233:         DiskShift = new AttributeID(0xDC),
paulwerelds@233:         GSenseErrorRateAlt = new AttributeID(0xDD), // Alternative to 0xBF
paulwerelds@233:         LoadedHours = new AttributeID(0xDE),
paulwerelds@233:         LoadUnloadRetryCount = new AttributeID(0xDF),
paulwerelds@233:         LoadFriction = new AttributeID(0xE0),
paulwerelds@233:         LoadUnloadCycleCount = new AttributeID(0xE1),
paulwerelds@233:         LoadInTime = new AttributeID(0xE2),
paulwerelds@233:         TorqueAmplificationCount = new AttributeID(0xE3),
paulwerelds@233:         PowerOffRetractCycle = new AttributeID(0xE4),
paulwerelds@233:         GMRHeadAmplitude = new AttributeID(0xE6),
paulwerelds@233:         DriveTemperature = new AttributeID(0xE7),
paulwerelds@233:         HeadFlyingHours = new AttributeID(0xF0),
paulwerelds@233:         LBAsWrittenTotal = new AttributeID(0xF1),
paulwerelds@233:         LBAsReadTotal = new AttributeID(0xF2),
paulwerelds@233:         ReadErrorRetryRate = new AttributeID(0xFA),
paulwerelds@233:         FreeFallProtection = new AttributeID(0xFE)
paulwerelds@233:       ;
moel@231:     }
moel@231: 
moel@231:     // Indilinx SSD SMART attributes
paulwerelds@233:     // TODO: Find out the purpose of attribute 0xD2
paulwerelds@233:     // Seems to be unique to Indilinx drives, hence its name of UnknownUnique.
paulwerelds@233:     public static class IndilinxAttributes {
paulwerelds@233:       public static readonly AttributeID
paulwerelds@233:         ReadErrorRate = CommonAttributes.ReadErrorRate,
paulwerelds@233:         PowerOnHours = CommonAttributes.PowerOnHours,
paulwerelds@233:         PowerCycleCount = CommonAttributes.PowerCycleCount,
paulwerelds@233:         InitialBadBlockCount = new AttributeID(0xB8),
paulwerelds@233:         RemainingLife = new AttributeID(0xD1),
paulwerelds@233:         ProgramFailure = new AttributeID(0xC3),
paulwerelds@233:         EraseFailure = new AttributeID(0xC4),
paulwerelds@233:         ReadFailure = new AttributeID(0xC5),
paulwerelds@233:         SectorsRead = new AttributeID(0xC6),
paulwerelds@233:         SectorsWritten = new AttributeID(0xC7),
paulwerelds@233:         ReadCommands = new AttributeID(0xC8),
paulwerelds@233:         WriteCommands = new AttributeID(0xC9),
paulwerelds@233:         BitErrors = new AttributeID(0xCA),
paulwerelds@233:         CorrectedErrors = new AttributeID(0xCB),
paulwerelds@233:         BadBlockFullFlag = new AttributeID(0xCC),
paulwerelds@233:         MaxCellcycles = new AttributeID(0xCD),
paulwerelds@233:         MinErase = new AttributeID(0xCE),
paulwerelds@233:         MaxErase = new AttributeID(0xCF),
paulwerelds@233:         AverageEraseCount = new AttributeID(0xD0),
paulwerelds@233:         UnknownUnique = new AttributeID(0xD2),
paulwerelds@233:         SataErrorCountCRC = new AttributeID(0xD3),
paulwerelds@233:         SataErrorCountHandshake = new AttributeID(0xD4)
paulwerelds@233:       ;
moel@231:     }
moel@231: 
moel@231:     // Intel SSD SMART attributes
paulwerelds@233:     // TODO: Find out the meaning behind 0xE2, 0xE3 and 0xE4
paulwerelds@233:     public static class IntelAttributes {
paulwerelds@233:       public static readonly AttributeID
paulwerelds@233:         ReadErrorRate = CommonAttributes.ReadErrorRate,
paulwerelds@233:         SpinUpTime = CommonAttributes.SpinUpTime,
paulwerelds@233:         StartStopCount = CommonAttributes.StartStopCount,
paulwerelds@233:         ReallocatedSectorsCount = CommonAttributes.ReallocatedSectorsCount,
paulwerelds@233:         PowerOnHours = CommonAttributes.PowerOnHours,
paulwerelds@233:         PowerCycleCount = CommonAttributes.PowerCycleCount,
paulwerelds@233:         EndToEndError = CommonAttributes.EndToEndError, // Only on G2 drives!
paulwerelds@233: 
paulwerelds@233:         // Different from the common attribute PowerOffRetractCount, same ID
paulwerelds@233:         UnsafeShutdownCount = new AttributeID(0xC0),
paulwerelds@233:         HostWrites = new AttributeID(0xE1),
paulwerelds@233:         RemainingLife = new AttributeID(0xE8),
paulwerelds@233:         MediaWearOutIndicator = new AttributeID(0xE9)
paulwerelds@233:       ;
moel@231:     }
moel@231: 
moel@231:     // Samsung SSD SMART attributes
paulwerelds@233:     // TODO: AF, B0, B1, B5, B6, BB, C3, C6, C7, E8, E9
paulwerelds@233:     public static class SamsungAttributes {
paulwerelds@233:       public static readonly AttributeID
paulwerelds@233:         PowerOnHours = CommonAttributes.PowerOnHours,
paulwerelds@233:         PowerCycleCount = CommonAttributes.PowerCycleCount,
paulwerelds@233:         UsedReservedBlockCountChip = new AttributeID(0xB2), // Unique
paulwerelds@233:         UsedReservedBlockCountTotal = new AttributeID(0xB3), // Unique
paulwerelds@233:         RemainingLife = new AttributeID(0xB4), // Unique
paulwerelds@233:         RuntimeBadBlockTotal = new AttributeID(0xB7)
paulwerelds@233:       ;
moel@231:     }
moel@231: 
moel@231:     // SandForce SSD SMART attributes
paulwerelds@233:     // Note: 0xE9 and 0xEA are reserved attributes and unique
paulwerelds@233:     public static class SandForceAttributes {
paulwerelds@233:       public static readonly AttributeID
paulwerelds@233:         ReadErrorRate = CommonAttributes.ReadErrorRate,
paulwerelds@233:         RetiredBlockCount = new AttributeID(0x05),
paulwerelds@233:         PowerOnHours = CommonAttributes.PowerOnHours,
paulwerelds@233:         PowerCycleCount = CommonAttributes.PowerCycleCount,
paulwerelds@233:         ProgramFailCount = new AttributeID(0xAB), // Unique
paulwerelds@233:         EraseFailCount = new AttributeID(0xAC), // Unique
paulwerelds@233:         UnexpectedPowerLossCount = new AttributeID(0xAE), // Unique
paulwerelds@233:         WearRangeDelta = new AttributeID(0xB1), // Unique
paulwerelds@233:         ProgramFailCountAlt = new AttributeID(0xB5), // Same as 0xAB
paulwerelds@233:         EraseFailCountAlt = new AttributeID(0xB6), // Same as 0xAC
paulwerelds@233:         ReportedUncorrectableErrors =
paulwerelds@233:           CommonAttributes.ReportedUncorrectableErrors,
paulwerelds@233:         
paulwerelds@233:         Temperature = CommonAttributes.Temperature, // SF-1500 only!
paulwerelds@233:         
paulwerelds@233:         // Opposite of the common attribute HardwareECCRecovered
paulwerelds@233:         UnrecoverableECC = new AttributeID(0xC3),
paulwerelds@233:         ReallocationEventCount = new AttributeID(0xC4),
paulwerelds@233:         RemainingLife = new AttributeID(0xE7),
paulwerelds@233:         LifetimeWrites = new AttributeID(0xF1),
paulwerelds@233:         LifetimeReads = new AttributeID(0xF2)
paulwerelds@233:       ;
paulwerelds@218:     }
paulwerelds@218: 
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@195:     protected 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@195:     protected enum ShareMode : uint {
moel@1:       None = 0,     
moel@1:       Read = 1,     
moel@1:       Write = 2,    
moel@1:       Delete = 4
moel@1:     }
moel@1: 
moel@195:     protected 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@195:     protected 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@195:     protected 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@195:     protected 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@195:     protected struct DriveCommandParameter {
moel@195:       public uint BufferSize;           
moel@1:       public CommandBlockRegisters Registers;           
moel@1:       public byte DriveNumber;   
moel@188:       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
moel@1:       public byte[] Reserved;                                
moel@1:     }
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@195:     protected 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@195:     protected struct DriveCommandResult {
moel@1:       public uint BufferSize;
moel@1:       public DriverStatus DriverStatus;
moel@1:     } 
moel@1: 
moel@1:     [StructLayout(LayoutKind.Sequential, Pack = 1)]
moel@195:     protected 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@195:     protected 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@195:     protected 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@167:     private SMART() { }
moel@1: 
moel@1:     public static IntPtr OpenPhysicalDrive(int driveNumber) {
moel@167:       return NativeMethods.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@167:       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
moel@167:         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
moel@1:         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
moel@1:         IntPtr.Zero);
moel@1:     }
moel@1: 
paulwerelds@233:     public static DriveAttribute[] ReadSmart(IntPtr handle,
paulwerelds@218:       int driveNumber)
paulwerelds@218:     {
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: 
paulwerelds@218:       bool isValid = NativeMethods.DeviceIoControl(handle, 
moel@167:         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
moel@167:         out result, Marshal.SizeOf(typeof(DriveSmartReadResult)), 
moel@167:         out bytesReturned, IntPtr.Zero);
moel@1: 
paulwerelds@233:       return (isValid) ? result.Attributes : new DriveAttribute[0];
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@167:       bool valid = NativeMethods.DeviceIoControl(handle, 
moel@167:         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
moel@167:         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
moel@167:         out bytesReturned, 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@167:     public static int CloseHandle(IntPtr handle) {
moel@167:       return NativeMethods.CloseHandle(handle);
moel@167:     }
moel@167: 
moel@195:     protected static class NativeMethods {
moel@167:       private const string KERNEL = "kernel32.dll";
moel@167: 
moel@167:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
moel@167:         CharSet = CharSet.Unicode)]
moel@167:       public static extern IntPtr CreateFile(string fileName,
moel@167:         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
moel@167:         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
moel@167:         IntPtr templateFilehandle);
moel@167: 
moel@167:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@167:       public static extern int CloseHandle(IntPtr handle);
moel@167: 
moel@167:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@167:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@167:       public static extern bool DeviceIoControl(IntPtr handle,
moel@167:         DriveCommand command, ref DriveCommandParameter parameter,
moel@167:         int parameterSize, out DriveSmartReadResult result, int resultSize,
moel@167:         out uint bytesReturned, IntPtr overlapped);
moel@167: 
moel@167:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@167:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@167:       public static extern bool DeviceIoControl(IntPtr handle,
moel@167:         DriveCommand command, ref DriveCommandParameter parameter,
moel@167:         int parameterSize, out DriveCommandResult result, int resultSize,
moel@167:         out uint bytesReturned, IntPtr overlapped);
moel@167: 
moel@167:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@167:       [return: MarshalAsAttribute(UnmanagedType.Bool)]
moel@167:       public static extern bool DeviceIoControl(IntPtr handle,
moel@167:         DriveCommand command, ref DriveCommandParameter parameter,
moel@167:         int parameterSize, out DriveIdentifyResult result, int resultSize,
moel@167:         out uint bytesReturned, IntPtr overlapped);
moel@167:     }    
moel@1:   }
moel@1: }