unit Ioctl;



interface

uses
           Windows;

type
           USHORT = Word;
           DEVICE_TYPE = ULONG;

//=============================================================
// Define the various device type values.
// Note that values used by Microsoft Corporation are in the range 0-32767,
// and 32768-65535 are reserved for use by customers.
const
  MAX_IDE_DRIVES = 8;
  NUM_ATTRIBUTE_STRUCTS = 30;

           FILE_DEVICE_BEEP                = $00000001;
           FILE_DEVICE_CD_ROM              = $00000002;
           FILE_DEVICE_CD_ROM_FILE_SYSTEM  = $00000003;
           FILE_DEVICE_CONTROLLER          = $00000004;
           FILE_DEVICE_DATALINK            = $00000005;
           FILE_DEVICE_DFS                = $00000006;
           FILE_DEVICE_DISK                = $00000007;
           FILE_DEVICE_DISK_FILE_SYSTEM    = $00000008;
           FILE_DEVICE_FILE_SYSTEM        = $00000009;
           FILE_DEVICE_INPORT_PORT        = $0000000a;
           FILE_DEVICE_KEYBOARD            = $0000000b;
           FILE_DEVICE_MAILSLOT            = $0000000c;
           FILE_DEVICE_MIDI_IN            = $0000000d;
           FILE_DEVICE_MIDI_OUT            = $0000000e;
           FILE_DEVICE_MOUSE              = $0000000f;
           FILE_DEVICE_MULTI_UNC_PROVIDER  = $00000010;
           FILE_DEVICE_NAMED_PIPE          = $00000011;
           FILE_DEVICE_NETWORK            = $00000012;
           FILE_DEVICE_NETWORK_BROWSER    = $00000013;
           FILE_DEVICE_NETWORK_FILE_SYSTEM = $00000014;
           FILE_DEVICE_NULL                = $00000015;
           FILE_DEVICE_PARALLEL_PORT      = $00000016;
           FILE_DEVICE_PHYSICAL_NETCARD    = $00000017;
           FILE_DEVICE_PRINTER            = $00000018;
           FILE_DEVICE_SCANNER            = $00000019;
           FILE_DEVICE_SERIAL_MOUSE_PORT  = $0000001a;
           FILE_DEVICE_SERIAL_PORT        = $0000001b;
           FILE_DEVICE_SCREEN              = $0000001c;
           FILE_DEVICE_SOUND              = $0000001d;
           FILE_DEVICE_STREAMS            = $0000001e;
           FILE_DEVICE_TAPE                = $0000001f;
           FILE_DEVICE_TAPE_FILE_SYSTEM    = $00000020;
           FILE_DEVICE_TRANSPORT          = $00000021;
           FILE_DEVICE_UNKNOWN            = $00000022;
           FILE_DEVICE_VIDEO              = $00000023;
           FILE_DEVICE_VIRTUAL_DISK        = $00000024;
           FILE_DEVICE_WAVE_IN            = $00000025;
           FILE_DEVICE_WAVE_OUT            = $00000026;
           FILE_DEVICE_8042_PORT          = $00000027;
           FILE_DEVICE_NETWORK_REDIRECTOR  = $00000028;
           FILE_DEVICE_BATTERY            = $00000029;
           FILE_DEVICE_BUS_EXTENDER        = $0000002a;
           FILE_DEVICE_MODEM              = $0000002b;
           FILE_DEVICE_VDM                = $0000002c;
           FILE_DEVICE_MASS_STORAGE        = $0000002d;
           FILE_DEVICE_SMB                = $0000002e;
           FILE_DEVICE_KS                  = $0000002f;
           FILE_DEVICE_CHANGER            = $00000030;
           FILE_DEVICE_SMARTCARD          = $00000031;
           FILE_DEVICE_ACPI                = $00000032;
           FILE_DEVICE_DVD                = $00000033;
           FILE_DEVICE_FULLSCREEN_VIDEO    = $00000034;
           FILE_DEVICE_DFS_FILE_SYSTEM    = $00000035;
           FILE_DEVICE_DFS_VOLUME          = $00000036;
           FILE_DEVICE_SERENUM            = $00000037;
           FILE_DEVICE_TERMSRV            = $00000038;
           FILE_DEVICE_KSEC                = $00000039;

const
           READ_ATTRIBUTE_BUFFER_SIZE = 512;
           IDENTIFY_BUFFER_SIZE      = 512;
           READ_THRESHOLD_BUFFER_SIZE = 512;


const
// SMART IOCTL commands
           DFP_GET_VERSION        = $00074080;
           DFP_SEND_DRIVE_COMMAND = $0007c084;
           DFP_RECEIVE_DRIVE_DATA = $0007c088;

const
           IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
           IOCTL_SCSI_BASE    = FILE_DEVICE_CONTROLLER;

// Define the method codes for how buffers are passed for I/O and FS controls
           METHOD_BUFFERED  = 0;
           METHOD_IN_DIRECT  = 1;
           METHOD_OUT_DIRECT = 2;
           METHOD_NEITHER    = 3;

// Define the access check value for any access
  FILE_ANY_ACCESS    = 0;
  FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS;
  FILE_READ_ACCESS    = $0001; // file & pipe
  FILE_WRITE_ACCESS  = $0002; // file & pipe


//#define IOCTL_STORAGE_GET_MEDIA_TYPES  CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_STORAGE_GET_MEDIA_TYPES = (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0300 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_STORAGE_GET_MEDIA_TYPES_EX = (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0301 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_STORAGE_GET_DEVICE_NUMBER CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_STORAGE_GET_DEVICE_NUMBER = (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0420 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_PASS_THROUGH        CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
           IOCTL_SCSI_PASS_THROUGH = (IOCTL_SCSI_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or ($0401 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_MINIPORT            CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
           IOCTL_SCSI_MINIPORT = (IOCTL_SCSI_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or ($0402 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_GET_INQUIRY_DATA    CTL_CODE(IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_GET_INQUIRY_DATA = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0403 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_GET_CAPABILITIES    CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_GET_CAPABILITIES = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0404 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_PASS_THROUGH_DIRECT  CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
           IOCTL_SCSI_PASS_THROUGH_DIRECT = (IOCTL_SCSI_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or ($0405 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_GET_ADDRESS          CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_GET_ADDRESS = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0406 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_RESCAN_BUS          CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_RESCAN_BUS = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0407 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_GET_DUMP_POINTERS    CTL_CODE(IOCTL_SCSI_BASE, 0x0408, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_GET_DUMP_POINTERS = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0408 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_SCSI_FREE_DUMP_POINTERS  CTL_CODE(IOCTL_SCSI_BASE, 0x0409, METHOD_BUFFERED, FILE_ANY_ACCESS)
           IOCTL_SCSI_GET_FREE_DUMP_POINTERS = (IOCTL_SCSI_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or ($0409 shl 2) or (METHOD_BUFFERED);

//#define IOCTL_IDE_PASS_THROUGH          CTL_CODE(IOCTL_SCSI_BASE, 0x040a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
           IOCTL_IDE_PASS_THROUGH = (IOCTL_SCSI_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or ($040a shl 2) or (METHOD_BUFFERED);

//-------------------------------------------------------------
// GETVERSIONOUTPARAMS contains the data returned from the
// Get Driver Version function.
//-------------------------------------------------------------
type
           TGetVersionOutParams = packed record
                     bVersion      : BYTE;                // Binary driver version.
                     bRevision    : BYTE;                // Binary driver revision.
                     bReserved    : BYTE;                // Not used.
                     bIDEDeviceMap : BYTE;                // Bit map of IDE devices.
                     fCapabilities : DWORD;                // Bit mask of driver capabilities.
                     dwReserved    : Array[0..3] of DWORD; // For future use.
           end;
           GETVERSIONOUTPARAMS = TGetVersionOutParams;
           GETVERSIONINPARAMS = TGetVersionOutParams;
           PGetVersionOutParams = ^TGetVersionOutParams;
           LPGetVersionOutParams = ^TGetVersionOutParams;

// Bits returned in the fCapabilities member of GETVERSIONOUTPARAMS
// ATA ID command supported
const
           CAP_IDE_ID_FUNCTION            = 1; // ATAPI ID command supported
           CAP_IDE_ATAPI_ID              = 2; // SMART commannds supported
           CAP_IDE_EXECUTE_SMART_FUNCTION = 4;

//---------------------------------------------------------------------
// IDE registers
//---------------------------------------------------------------------
type
           TIDERegs = packed record
                     bFeaturesReg    : BYTE; // Used for specifying SMART "commands".
                     bSectorCountReg  : BYTE; // IDE sector count register
                     bSectorNumberReg : BYTE; // IDE sector number register
                     bCylLowReg      : BYTE; // IDE low order cylinder value
                     bCylHighReg      : BYTE; // IDE high order cylinder value
                     bDriveHeadReg    : BYTE; // IDE drive/head register
                     bCommandReg      : BYTE; // Actual IDE command.
                     bReserved        : BYTE; // reserved for future use.  Must be zero.
           end;
           IDEREGS  = TIDERegs;
           PIDERegs  = ^TIDERegs;
           LPIDERegs = PIDERegs;
           _IDEREGS  = TIDERegs;

// Valid values for the bCommandReg member of IDEREGS.
const
           IDE_ATAPI_ID              = $A1; // Returns ID sector for ATAPI.
           IDE_ID_FUNCTION            = $EC; // Returns ID sector for ATA.
           IDE_EXECUTE_SMART_FUNCTION = $B0; // Performs SMART cmd. Requires valid bFeaturesReg, bCylLowReg, and bCylHighReg

// Cylinder register values required when issuing SMART command
           SMART_CYL_LOW = $4F;
           SMART_CYL_HI  = $C2;

//---------------------------------------------------------------------
// SENDCMDINPARAMS contains the input parameters for the
// Send Command to Drive function.
//---------------------------------------------------------------------
type
           TSendCmdInParams = packed record
                     cBufferSize  : DWORD;                // Buffer size in bytes
                     irDriveRegs  : TIDERegs;            // Structure with drive register values.
                     bDriveNumber : BYTE;                // Physical drive number to send command to (0,1,2,3).
                     bReserved    : Array[0..2] of Byte;  // Reserved for future expansion.
                     dwReserved  : Array[0..3] of DWORD; // For future use.
                     bBuffer      : Array[0..0] of Byte;  // Input buffer.
           end;
           SENDCMDINPARAMS  = TSendCmdInParams;
           PSendCmdInParams  = ^TSendCmdInParams;
           LPSendCmdInParams = PSendCmdInParams;
           _SENDCMDINPARAMS  = TSendCmdInParams;

//---------------------------------------------------------------------
// Status returned from driver
//---------------------------------------------------------------------
type
           TDriverStatus = packed record
                     bDriverError : Byte;                // Error code from driver, or 0 if no error.
                     bIDEStatus  : Byte;                // Contents of IDE Error register. Only valid when bDriverError is SMART_IDE_ERROR.
                     bReserved    : Array[0..1] of Byte;  // Reserved for future expansion.
                     dwReserved  : Array[0..1] of DWORD; // Reserved for future expansion.
           end;
           DRIVERSTATUS  = TDriverStatus;
           PDriverStatus  = ^TDriverStatus;
           LPDriverStatus = TDriverStatus;
           _DRIVERSTATUS  = TDriverStatus;

// bDriverError values
const
           SMART_NO_ERROR        = 0; // No error
           SMART_IDE_ERROR        = 1; // Error from IDE controller
           SMART_INVALID_FLAG    = 2; // Invalid command flag
           SMART_INVALID_COMMAND  = 3; // Invalid command byte
           SMART_INVALID_BUFFER  = 4; // Bad buffer (null, invalid addr..)
           SMART_INVALID_DRIVE    = 5; // Drive number not valid
           SMART_INVALID_IOCTL    = 6; // Invalid IOCTL
           SMART_ERROR_NO_MEM    = 7; // Could not lock user's buffer
           SMART_INVALID_REGISTER = 8; // Some IDE Register not valid
           SMART_NOT_SUPPORTED    = 9; // Invalid cmd flag set
           SMART_NO_IDE_DEVICE    =10; // Cmd issued to device not present although drive number is valid
           // 11-255 reserved

//---------------------------------------------------------------------
// Structure returned by SMART IOCTL for several commands
//---------------------------------------------------------------------
type
           TSendCmdOutParams = packed record
                     cBufferSize  : DWORD;              // Size of bBuffer in bytes
                     DriverStatus : TDriverStatus;      // Driver status structure.
                     bBuffer      : Array[0..0] of BYTE; // Buffer of arbitrary length in which to store the data read from the drive.
           end;
           SENDCMDOUTPARAMS  = TSendCmdOutParams;
           PSendCmdOutParams  = ^TSendCmdOutParams;
           LPSendCmdOutParams = PSendCmdOutParams;
           _SENDCMDOUTPARAMS  = TSendCmdOutParams;

//---------------------------------------------------------------------
// Feature register defines for SMART "sub commands"
//---------------------------------------------------------------------
const
           SMART_READ_ATTRIBUTE_VALUES            = $D0; // ATA4: Renamed
                                                         // SMART READ DATA
           SMART_READ_ATTRIBUTE_THRESHOLDS        = $D1; // Obsoleted in ATA4!
           SMART_ENABLE_DISABLE_ATTRIBUTE_AUTOSAVE = $D2;
           SMART_SAVE_ATTRIBUTE_VALUES            = $D3;
           SMART_EXECUTE_OFFLINE_IMMEDIATE        = $D4; // ATA4
           // Vendor specific commands:
           SMART_ENABLE_SMART_OPERATIONS          = $D8;
           SMART_DISABLE_SMART_OPERATIONS          = $D9;
           SMART_RETURN_SMART_STATUS              = $DA;

//---------------------------------------------------------------------
// The following structure defines the structure of a Drive Attribute
//---------------------------------------------------------------------
type
           TDriveAttribute = packed record
                     bAttrID      : BYTE;                // Identifies which attribute
                     wStatusFlags : WORD;                // see bit definitions below
                     bAttrValue  : BYTE;                // Current normalized value
                     bWorstValue  : BYTE;                // How bad has it ever been?
                     bRawValue    : Array[0..6] of BYTE; // Un-normalized value
                     bReserved    : BYTE;                // ...
           end;
           DRIVEATTRIBUTE  = TDriveAttribute;
           PDriveAttribute  = ^TDriveAttribute;
           LPDriveAttribute = PDriveAttribute;

//---------------------------------------------------------------------
// The following structure defines the structure of a Warranty Threshold
// Obsoleted in ATA4!
//---------------------------------------------------------------------
           TAttrThreshold = packed record
                     bAttrID : BYTE;                  // Identifies which attribute
                     bWarrantyThreshold : BYTE;      // Triggering value
                     bReserved : Array[0..9] of BYTE; // ...
           end;
           ATTRTHRESHOLD  = TAttrThreshold;
           PAttrThreshold  = ^TAttrThreshold;
           LPAttrThreshold = PAttrThreshold;

//---------------------------------------------------------------------
// The following struct defines the interesting part of the IDENTIFY buffer:
//---------------------------------------------------------------------
           TIdSector = packed record
                     wGenConfig                : USHORT;
                     wNumCyls                  : USHORT;
                     wReserved                  : USHORT;
                     wNumHeads                  : USHORT;
                     wBytesPerTrack            : USHORT;
                     wBytesPerSector            : USHORT;
                     wSectorsPerTrack          : USHORT;
                     wVendorUnique              : Array[0..2] of USHORT;
                     sSerialNumber              : Array[0..19] of CHAR;
                     wBufferType                : USHORT;
                     wBufferSize                : USHORT;
                     wECCSize                  : USHORT;
                     sFirmwareRev              : Array[0..7] of CHAR;
                     sModelNumber              : Array[0..39] of CHAR;
                     wMoreVendorUnique          : USHORT;
                     wDoubleWordIO              : USHORT;
                     wCapabilities              : USHORT;
                     wReserved1                : USHORT;
                     wPIOTiming                : USHORT;
                     wDMATiming                : USHORT;
                     wBS                        : USHORT;
                     wNumCurrentCyls            : USHORT;
                     wNumCurrentHeads          : USHORT;
                     wNumCurrentSectorsPerTrack : USHORT;
                     ulCurrentSectorCapacity    : ULONG;
                     wMultSectorStuff          : USHORT;
                     ulTotalAddressableSectors  : ULONG;
                     wSingleWordDMA            : USHORT;
                     wMultiWordDMA              : USHORT;
                     bReserved                  : Array[0..127] of BYTE;
           end;
           PIdSector = ^TIdSector;
           IDSECTOR  = TIdSector;
           _IDSECTOR = TIdSector;

//---------------------------------------------------------------------
// Valid Attribute IDs
//---------------------------------------------------------------------
const
           ATTR_INVALID                =  0;
           ATTR_READ_ERROR_RATE        =  1;
           ATTR_THROUGHPUT_PERF        =  2;
           ATTR_SPIN_UP_TIME            =  3;
           ATTR_START_STOP_COUNT        =  4;
           ATTR_REALLOC_SECTOR_COUNT    =  5;
           ATTR_READ_CHANNEL_MARGIN    =  6;
           ATTR_SEEK_ERROR_RATE        =  7;
           ATTR_SEEK_TIME_PERF          =  8;
           ATTR_POWER_ON_HRS_COUNT      =  9;
           ATTR_SPIN_RETRY_COUNT        = 10;
           ATTR_CALIBRATION_RETRY_COUNT = 11;
           ATTR_POWER_CYCLE_COUNT      = 12;
//---------------------------------------------------------------------
// Status Flags Values
//---------------------------------------------------------------------
           PRE_FAILURE_WARRANTY      = $1;
           ON_LINE_COLLECTION        = $2;
           PERFORMANCE_ATTRIBUTE    = $4;
           ERROR_RATE_ATTRIBUTE      = $8;
           EVENT_COUNT_ATTRIBUTE    = $10;
           SELF_PRESERVING_ATTRIBUTE = $20;

//=============================================================
// IOCTL_STORAGE_GET_DEVICE_NUMBER
//
// input - none
//
// output - STORAGE_DEVICE_NUMBER structure
//          The values in the STORAGE_DEVICE_NUMBER structure are guaranteed
//          to remain unchanged until the system is rebooted.  They are not
//          guaranteed to be persistant across boots.
type
           TStorageDeviceNumber = packed record
                     // The FILE_DEVICE_XXX type for this device.
                     DeviceType      : DEVICE_TYPE;

                     // The number of this device
                     DeviceNumber    : ULONG;

                     // If the device is partitionable, the partition number of the device.
                     // Otherwise -1
                     PartitionNumber : ULONG;
           end;
           STORAGE_DEVICE_NUMBER = TStorageDeviceNumber;
           PStorageDeviceNumber = ^TStorageDeviceNumber;


//=============================================================
// IOCTL_STORAGE_GET_MEDIA_TYPES_EX will return an array of DEVICE_MEDIA_INFO
// structures, one per supported type, embedded in the GET_MEDIA_TYPES struct.

type
           STORAGE_MEDIA_TYPE = DWORD;
const
// Following are defined in ntdddisk.h in the MEDIA_TYPE enum
           Unknown            =  0; // Format is unknown
           F5_1Pt2_512        =  1; // 5.25", 1.2MB,  512 bytes/sector
           F3_1Pt44_512      =  2; // 3.5",  1.44MB, 512 bytes/sector
           F3_2Pt88_512      =  3; // 3.5",  2.88MB, 512 bytes/sector
           F3_20Pt8_512      =  4; // 3.5",  20.8MB, 512 bytes/sector
           F3_720_512        =  5; // 3.5",  720KB,  512 bytes/sector
           F5_360_512        =  6; // 5.25", 360KB,  512 bytes/sector
           F5_320_512        =  7; // 5.25", 320KB,  512 bytes/sector
           F5_320_1024        =  8; // 5.25", 320KB,  1024 bytes/sector
           F5_180_512        =  9; // 5.25", 180KB,  512 bytes/sector
           F5_160_512        = 10; // 5.25", 160KB,  512 bytes/sector
           RemovableMedia    = 11; // Removable media other than floppy
           FixedMedia        = 12; // Fixed hard disk media
           F3_120M_512        = 13; // 3.5", 120M Floppy
           F3_640_512        = 14; // 3.5" ,  640KB,  512 bytes/sector
           F5_640_512        = 15; // 5.25",  640KB,  512 bytes/sector
           F5_720_512        = 16; // 5.25",  720KB,  512 bytes/sector
           F3_1Pt2_512        = 17; // 3.5" ,  1.2Mb,  512 bytes/sector
           F3_1Pt23_1024      = 18; // 3.5" ,  1.23Mb, 1024 bytes/sector
           F5_1Pt23_1024      = 19; // 5.25",  1.23MB, 1024 bytes/sector
           F3_128Mb_512      = 20; // 3.5" MO 128Mb  512 bytes/sector
           F3_230Mb_512      = 21; // 3.5" MO 230Mb  512 bytes/sector
           F8_256_128        = 22; // 8",    256KB,  128 bytes/sector

// STORAGE_MEDIA_TYPE
           DDS_4mm            = 32; // Tape - DAT DDS1,2,... (all vendors)
           MiniQic            = 33; // Tape - miniQIC Tape
           Travan            = 34; // Tape - Travan TR-1,2,3,...
           QIC                = 35; // Tape - QIC
           MP_8mm            = 36; // Tape - 8mm Exabyte Metal Particle
           AME_8mm            = 37; // Tape - 8mm Exabyte Advanced Metal Evap
           AIT1_8mm          = 38; // Tape - 8mm Sony AIT1
           DLT                = 39; // Tape - DLT Compact IIIxt, IV
           NCTP              = 40; // Tape - Philips NCTP
           IBM_3480          = 41; // Tape - IBM 3480
           IBM_3490E          = 42; // Tape - IBM 3490E
           IBM_Magstar_3590  = 43; // Tape - IBM Magstar 3590
           IBM_Magstar_MP    = 44; // Tape - IBM Magstar MP
           STK_DATA_D3        = 45; // Tape - STK Data D3
           SONY_DTF          = 46; // Tape - Sony DTF
           DV_6mm            = 47; // Tape - 6mm Digital Video
           DMI                = 48; // Tape - Exabyte DMI and compatibles
           SONY_D2            = 49; // Tape - Sony D2S and D2L
           CLEANER_CARTRIDGE  = 50; // Cleaner - All Drive types that support Drive Cleaners
           CD_ROM            = 51; // Opt_Disk - CD
           CD_R              = 52; // Opt_Disk - CD-Recordable (Write Once)
           CD_RW              = 53; // Opt_Disk - CD-Rewriteable
           DVD_ROM            = 54; // Opt_Disk - DVD-ROM
           DVD_R              = 55; // Opt_Disk - DVD-Recordable (Write Once)
           DVD_RW            = 56; // Opt_Disk - DVD-Rewriteable
           MO_3_RW            = 57; // Opt_Disk - 3.5" Rewriteable MO Disk
           MO_5_WO            = 58; // Opt_Disk - MO 5.25" Write Once
           MO_5_RW            = 59; // Opt_Disk - MO 5.25" Rewriteable (not LIMDOW)
           MO_5_LIMDOW        = 60; // Opt_Disk - MO 5.25" Rewriteable (LIMDOW)
           PC_5_WO            = 61; // Opt_Disk - Phase Change 5.25" Write Once Optical
           PC_5_RW            = 62; // Opt_Disk - Phase Change 5.25" Rewriteable
           PD_5_RW            = 63; // Opt_Disk - PhaseChange Dual Rewriteable
           ABL_5_WO          = 64; // Opt_Disk - Ablative 5.25" Write Once Optical
           PINNACLE_APEX_5_RW = 65; // Opt_Disk - Pinnacle Apex 4.6GB Rewriteable Optical
           SONY_12_WO        = 66; // Opt_Disk - Sony 12" Write Once
           PHILIPS_12_WO      = 67; // Opt_Disk - Philips/LMS 12" Write Once
           HITACHI_12_WO      = 68; // Opt_Disk - Hitachi 12" Write Once
           CYGNET_12_WO      = 69; // Opt_Disk - Cygnet/ATG 12" Write Once
           KODAK_14_WO        = 70; // Opt_Disk - Kodak 14" Write Once
           MO_NFR_525        = 71; // Opt_Disk - Near Field Recording (Terastor)
           NIKON_12_RW        = 72; // Opt_Disk - Nikon 12" Rewriteable
           IOMEGA_ZIP        = 73; // Mag_Disk - Iomega Zip
           IOMEGA_JAZ        = 74; // Mag_Disk - Iomega Jaz
           SYQUEST_EZ135      = 75; // Mag_Disk - Syquest EZ135
           SYQUEST_EZFLYER    = 76; // Mag_Disk - Syquest EzFlyer
           SYQUEST_SYJET      = 77; // Mag_Disk - Syquest SyJet
           AVATAR_F2          = 78; // Mag_Disk - 2.5" Floppy
           MP2_8mm            = 79; // Tape - 8mm Hitachi
           DST_S              = 80; // Ampex DST Small Tapes
           DST_M              = 81; // Ampex DST Medium Tapes
           DST_L              = 82; // Ampex DST Large Tapes
           VXATape_1          = 83; // Ecrix 8mm Tape
           VXATape_2          = 84; // Ecrix 8mm Tape
           STK_EAGLE          = 85; // STK Eagle
           LTO_Ultrium        = 86; // IBM, HP, Seagate LTO Ultrium
           LTO_Accelis        = 87; // IBM, HP, Seagate LTO Accelis

//-------------------------------------------------------------
           MEDIA_ERASEABLE        = $00000001;
           MEDIA_WRITE_ONCE        = $00000002;
           MEDIA_READ_ONLY        = $00000004;
           MEDIA_READ_WRITE        = $00000008;
           MEDIA_WRITE_PROTECTED  = $00000100;
           MEDIA_CURRENTLY_MOUNTED = $80000000;

//-------------------------------------------------------------
// Define the different storage bus types
// Bus types below 128 (0x80) are reserved for Microsoft use
type
           TStorageBusType = ( BusTypeUnknown, BusTypeScsi, BusTypeAtapi, BusTypeAta,
                     BusType1394, BusTypeSsa, BusTypeFibre, BusTypeUsb, BusTypeRAID );
//  BusTypeMaxReserved = 0x7F
           STORAGE_BUS_TYPE = TStorageBusType;
           PStorageBusType = ^TStorageBusType;

           TDeviceMediaInfo = packed record
                     DeviceSpecific : packed record
                     case byte of
                     0 :
                               ( DiskInfo : packed record
                                                   Cylinders            : LARGE_INTEGER;
                                                   MediaType            : STORAGE_MEDIA_TYPE;
                                                   TracksPerCylinder    : ULONG;
                                                   SectorsPerTrack      : ULONG;
                                                   BytesPerSector      : ULONG;
                                                   NumberMediaSides    : ULONG;
                                                   MediaCharacteristics : ULONG; // Bitmask of MEDIA_XXX values.
                                         end );
                     1 :
                               ( RemovableDiskInfo : packed record
                                                   Cylinders            : LARGE_INTEGER;
                                                   MediaType            : STORAGE_MEDIA_TYPE;
                                                   TracksPerCylinder    : ULONG;
                                                   SectorsPerTrack      : ULONG;
                                                   BytesPerSector      : ULONG;
                                                   NumberMediaSides    : ULONG;
                                                   MediaCharacteristics : ULONG; // Bitmask of MEDIA_XXX values.
                                         end );
                     2 :
                               ( TapeInfo : packed record
                                                   MediaType            : STORAGE_MEDIA_TYPE;
                                                   MediaCharacteristics : ULONG; // Bitmask of MEDIA_XXX values.
                                                   CurrentBlockSize    : ULONG;
                                                   BusType              : STORAGE_BUS_TYPE;
                                                   BusSpecificData : packed record // Bus specific information describing the medium supported.
                                                             case longint of
                                                             0 :
                                                                       ( ScsiInformation : packed record
                                                                                           MediumType : UCHAR;
                                                                                           DensityCode : UCHAR;
                                                                                 end );
                                                             end;
                                         end );
                     end;
           end;
           DEVICE_MEDIA_INFO = TDeviceMediaInfo;
           PDeviceMediaInfo = ^TDeviceMediaInfo;

           TGetMediaTypes = packed record
                     DeviceType    : DEVICE_TYPE; // FILE_DEVICE_XXX values
                     MediaInfoCount : ULONG;
                     MediaInfo      : Array[0..0] of DEVICE_MEDIA_INFO;
           end;
           GET_MEDIA_TYPES = TGetMediaTypes;
           PGetMediaTypes  = ^TGetMediaTypes;


//=============================================================
// IOCTL_SCSI_MINIPORT

// Define header for I/O control SRB. }
type
           TSrbIoControl = packed record
                     HeaderLength : ULONG;  // Is sizeof(SRB_IO_CONTROL).

                     // Identifies the application-dedicated, target HBA for this request.
                     // This signature is used to prevent conflicts in ControlCode values between vendors.
                     // It should be a string of ASCII characters.
                     // If a miniport driver does not recognize the input Signature value,
                     // it must complete the request with a status of SRB_STATUS_INVALID_REQUEST.
                     Signature    : Array[0..7] of Char;

                     // Indicates the interval in seconds that the request can execute
                     // before the OS-specific port driver might consider it timed out.
                     // Miniport drivers are not required to time requests because the port driver does.
                     Timeout      : ULONG;

                     // Indicates the operation to be performed. There are no system-defined operations.
                     // Values must be defined by the driver as a set of private I/O control codes
                     // with which the application can make requests by calling the Win32R DeviceIoControl function.
                     ControlCode  : ULONG;

                     // Returns a status code for examination by the requesting application.
                     ReturnCode  : ULONG;

                     // Indicates the size in bytes of the immediately following data area.
                     // This area can be divided for the particular operation into input and output areas.
                     // For input requests, the contents of the DataBuffer will be copied
                       // to the requestor up to the returned value of DataTransferLength.
                     Length      : ULONG;
           end;
           SRB_IO_CONTROL = TSrbIoControl;
           PSrbIoControl = ^TSrbIoControl;

// SCSI IO Device Control Codes
const
           FILE_DEVICE_SCSI = $0000001b;

           IOCTL_SCSI_EXECUTE_IN  = (FILE_DEVICE_SCSI shl 16) or $0011;
           IOCTL_SCSI_EXECUTE_OUT  = (FILE_DEVICE_SCSI shl 16) or $0012;
           IOCTL_SCSI_EXECUTE_NONE = (FILE_DEVICE_SCSI shl 16) or $0013;

// SMART support in atapi
           IOCTL_SCSI_MINIPORT_SMART_VERSION              = (FILE_DEVICE_SCSI shl 16) or $0500;
           IOCTL_SCSI_MINIPORT_IDENTIFY                    = (FILE_DEVICE_SCSI shl 16) or $0501;
           IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS          = (FILE_DEVICE_SCSI shl 16) or $0502;
           IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS      = (FILE_DEVICE_SCSI shl 16) or $0503;
           IOCTL_SCSI_MINIPORT_ENABLE_SMART                = (FILE_DEVICE_SCSI shl 16) or $0504;
           IOCTL_SCSI_MINIPORT_DISABLE_SMART              = (FILE_DEVICE_SCSI shl 16) or $0505;
           IOCTL_SCSI_MINIPORT_RETURN_STATUS              = (FILE_DEVICE_SCSI shl 16) or $0506;
           IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE    = (FILE_DEVICE_SCSI shl 16) or $0507;
           IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES      = (FILE_DEVICE_SCSI shl 16) or $0508;
           IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS      = (FILE_DEVICE_SCSI shl 16) or $0509;
           IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE = (FILE_DEVICE_SCSI shl 16) or $050a;

//=============================================================
// Used with the IOCTL_SCSI_GET_INQUIRY_DATA IOCTL.
type
           // Define SCSI adapter bus information.
{$ALIGN ON}
           TScsiBusData = record  // warning! dword alignment
                     NumberOfLogicalUnits : Byte;
                     InitiatorBusId      : Byte;
                     InquiryDataOffset    : ULONG;
           end;
           SCSI_BUS_DATA = TScsiBusData;
           PScsiBusData = ^TScsiBusData;

           // Define SCSI adapter bus information structure..
           TScsiAdapterBusInfo = record // warning! dword alignment
                     NumberOfBuses : Byte;
                     BusData : Array[0..0] of SCSI_BUS_DATA;
           end;
           SCSI_ADAPTER_BUS_INFO = TScsiAdapterBusInfo;
           PScsiAdapterBusInfo = ^TScsiAdapterBusInfo;

           TScsiInquiryData = record // warning! dword alignment
                     PathId                : Byte;
                     TargetId              : Byte;
                     Lun                  : Byte;
                     DeviceClaimed        : Boolean;
                     InquiryDataLength    : ULONG;
                     NextInquiryDataOffset : ULONG;
                     InquiryData          : Array[0..0] of Byte;
           end;
           SCSI_INQUIRY_DATA = TScsiInquiryData;
           PScsiInquiryData = ^TScsiInquiryData;
{$ALIGN OFF}


//=============================================================

// Open device to allow DeviceIoControl communications.
function GetPhysicalDriveHandle( DriveNum : Byte; DesireAccess : ACCESS_MASK ) : THandle;

// Get SCSI/IDE port handle.
function GetScsiPortHandle( PortNum : Byte; DesireAccess : ACCESS_MASK ) : THandle;

function GetScsiInquiryData( hDevice : THandle; var InquiryData : TScsiAdapterBusInfo; var dwSize : DWORD ) : BOOL;

function GetMediaType( hDevice : THandle ) : DEVICE_TYPE;

function GetMediaTypes( hDevice : THandle; var MediaTypes : TGetMediaTypes; var dwSize : DWORD ) : BOOL;

// Send an IDENTIFY command to the drive bDriveNum = 0-3
// bIDCmd = IDE_ID_FUNCTION or IDE_ATAPI_ID
function SmartIdentifyDirect(hDevice: THandle; bDriveNum: Byte;
  bIDCmd: Byte; var IdSector: TIdSector; SCIP: TSendCmdInParams;
  SCOP: TSendCmdOutParams; var IdSectorSize: LongInt; var dwBytesReturned: DWORD): BOOL;
function SmartIdentifyMiniport( hDevice : THandle; bTargetId : Byte; bIDCmd : Byte; var IdSector : TIdSector; var IdSectorSize : LongInt ) : BOOL;

// Send a SMART_ENABLE_SMART_OPERATIONS command to the drive bDriveNum = 0-3
function DoEnableSMART( hDevice : THandle; bDriveNum : Byte; var SCOP : TSendCmdOutParams; var dwBytesReturned : DWORD ) : BOOL;

// Send a SMART_READ_ATTRIBUTE_VALUES command to the drive bDriveNum = 0-3
function DoReadAttributesCmd(hDevice: THandle; SCIP: TSendCmdInParams;
  var SCOP: TSendCmdOutParams;           bDriveNum: Byte): BOOL;
function DoReadThresholdsCmd(hDevice: THandle; SCIP: TSendCmdInParams;
  var SCOP: TSendCmdOutParams; bDriveNum: Byte): BOOL;
var dwBytesReturned : DWORD;

// Change the WORD array to a BYTE array
procedure ChangeByteOrder( var Data; Size : Integer );


//=============================================================
implementation

var OSVersionInfo: TOSVersionInfo;
//---------------------------------------------------------------------
// Return INVALID_HANDLE_VALUE on error.
// Note: under NT/2000 Administrative priveleges are required
function GetPhysicalDriveHandle( DriveNum : Byte; DesireAccess : ACCESS_MASK ) : THandle;
var s : String;
begin
           OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
           GetVersionEx(OSVersionInfo);
           if OSVersionInfo.dwPlatformId=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
                     begin
                               Str(DriveNum,s); // avoid SysUtils
                               Result := CreateFile( PChar('\\.\PhysicalDrive'+s), DesireAccess, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
                     end
           else // Windows 95 OSR2, Windows 98
                     Result := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 );
end;
//-------------------------------------------------------------
// Get SCSI/IDE port handle.
// Windows NT/2000 only.
// Note: Administrative priveleges are NOT required
function GetScsiPortHandle( PortNum : Byte; DesireAccess : ACCESS_MASK ) : THandle;
var s : String;
begin
           Str(PortNum,s); // avoid SysUtils
           Result := CreateFile( PChar('\\.\Scsi'+s+':'), DesireAccess, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
end;
//-------------------------------------------------------------
function GetScsiInquiryData( hDevice : THandle; var InquiryData : TScsiAdapterBusInfo; var dwSize : DWORD ) : BOOL;
begin
           FillChar(InquiryData,dwSize,#0);
           Result := DeviceIoControl( hDevice, IOCTL_SCSI_GET_INQUIRY_DATA, nil, 0, @InquiryData, dwSize, dwSize, nil );
end;
//-------------------------------------------------------------
function GetMediaTypes( hDevice : THandle; var MediaTypes : TGetMediaTypes; var dwSize : DWORD ) : BOOL;
begin
           FillChar(MediaTypes,dwSize,#0);
           Result := DeviceIoControl( hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, nil, 0, @MediaTypes, dwSize, dwSize, nil );
end;
//-------------------------------------------------------------
function GetMediaType( hDevice : THandle ) : STORAGE_MEDIA_TYPE;
const Size = 1024;
var dwBytesReturned : DWORD;
                     bMediaTypes : Array[0..Size-1] of Byte;
                     rMediaTypes : TGetMediaTypes absolute bMediaTypes;
begin
           FillChar(bMediaTypes,Size,#0);
           Result := 0;
           if DeviceIoControl( hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, nil, 0, @bMediaTypes, Size, dwBytesReturned, nil ) then with rMediaTypes do
                     if MediaInfoCount>0 then with MediaInfo[0].DeviceSpecific do
                               case DeviceType of
                               FILE_DEVICE_CD_ROM,
                               FILE_DEVICE_DVD:
                                         Result := RemovableDiskInfo.MediaType;
                               FILE_DEVICE_DISK:
                                         Result := DiskInfo.MediaType;
                               FILE_DEVICE_TAPE:
                                         Result := TapeInfo.MediaType;
                               end;
end;
//-------------------------------------------------------------
// SmartIdentifyDirect
//
// FUNCTION: Send an IDENTIFY command to the drive bDriveNum = 0-3
// bIDCmd = IDE_ID_FUNCTION or IDE_ATAPI_ID
//
// Note: work only with IDE device handle.

function SmartIdentifyDirect(hDevice: THandle; bDriveNum: Byte;
  bIDCmd: Byte; var IdSector: TIdSector; SCIP: TSendCmdInParams;
  SCOP: TSendCmdOutParams; var IdSectorSize: LongInt; var dwBytesReturned: DWORD): BOOL;
const
  BufferSize = SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1;
var
                     Buffer: Array [0..BufferSize-1] of Byte;
                     MCOP: TSendCmdOutParams absolute Buffer;
                     //dwBytesReturned: DWORD;
begin
           FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0);
           FillChar(Buffer,BufferSize,#0);
           dwBytesReturned := 0;
           IdSectorSize := 0;
           // Set up data structures for IDENTIFY command.
           with SCIP do
           begin
                     cBufferSize  := IDENTIFY_BUFFER_SIZE;
                     bDriveNumber := bDriveNum;
                     with irDriveRegs do
                     begin
                               bFeaturesReg    := 0;
                               bSectorCountReg  := 1;
                               bSectorNumberReg := 1;
                               bCylLowReg      := 0;
                               bCylHighReg      := 0;
                               bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
                               bCommandReg      := bIDCmd;           // The command can either be IDE identify or ATAPI identify.
                     end;
           end;
           Result:= DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP,
    SizeOf(TSendCmdInParams)-1, @MCOP, BufferSize, dwBytesReturned, nil);
           if Result then
           begin
                     IdSectorSize:= dwBytesReturned-SizeOf(TSendCmdOutParams)+1;
                     if (IdSectorSize <= 0) then
      IdSectorSize:= 0
    else
      System.Move(MCOP.bBuffer, IdSector, IdSectorSize);
           end;
  SCOP:= MCOP;
end;
//-------------------------------------------------------------
// Same as above but
//  - work only NT;
//  - work with cotroller or device handle.
// Note: Administrator priveleges are not required to open controller handle.
function SmartIdentifyMiniport( hDevice : THandle; bTargetId : Byte; bIDCmd : Byte; var IdSector : TIdSector; var IdSectorSize : LongInt ) : BOOL;
const
           DataLength = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
           BufferLength = SizeOf(SRB_IO_CONTROL)+DataLength;
var           cbBytesReturned : DWORD;
                     pData : PSendCmdInParams;
                     Buffer : Array[0..BufferLength] of Byte;
                     srbControl : TSrbIoControl absolute Buffer;
begin
           // fill in srbControl fields
           FillChar(Buffer,BufferLength,#0);
           srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
           System.Move('SCSIDISK',srbControl.Signature,8);
           srbControl.Timeout      := 2;
           srbControl.Length      := DataLength;
           srbControl.ControlCode  := IOCTL_SCSI_MINIPORT_IDENTIFY;
           pData := PSendCmdInParams(PChar(@Buffer)+SizeOf(SRB_IO_CONTROL));
           with pData^ do
           begin
                     cBufferSize  := IDENTIFY_BUFFER_SIZE;
                     bDriveNumber := bTargetId;
                     with irDriveRegs do
                     begin
                               bFeaturesReg    := 0;
                               bSectorCountReg  := 1;
                               bSectorNumberReg := 1;
                               bCylLowReg      := 0;
                               bCylHighReg      := 0;
                               bDriveHeadReg := $A0 or ((bTargetId and 1) shl 4);
                               bCommandReg      := bIDCmd;           // The command can either be IDE identify or ATAPI identify.
                     end;
           end;
           Result := DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT, @Buffer, BufferLength, @Buffer, BufferLength, cbBytesReturned, nil );
           if Result then
           begin
                     IdSectorSize := cbBytesReturned-SizeOf(SRB_IO_CONTROL)-SizeOf(TSendCmdOutParams)+1;
                     if IdSectorSize<=0 then IdSectorSize := 0
                     else
                               begin
                                         if IdSectorSize>IDENTIFY_BUFFER_SIZE then IdSectorSize := IDENTIFY_BUFFER_SIZE;
                                         System.Move( PSendCmdOutParams(pData)^.bBuffer,IdSector,IdSectorSize);
                               end;
           end;
end;
//-------------------------------------------------------------
// DoEnableSMART
//
// FUNCTION: Send a SMART_ENABLE_SMART_OPERATIONS command to the drive bDriveNum = 0-3
//
function DoEnableSMART(hDevice: THandle; bDriveNum : Byte; var SCOP : TSendCmdOutParams; var dwBytesReturned : DWORD ) : BOOL;
var SCIP : TSendCmdInParams;
begin
           FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0);
           FillChar(SCOP,SizeOf(TSendCmdOutParams)-1,#0);
           dwBytesReturned := 0;

           // Set up data structures for Enable SMART Command.
           with SCIP do
           begin
                     cBufferSize  := 0;
                     bDriveNumber := bDriveNum;
                     with irDriveRegs do
                     begin
                               bFeaturesReg    := SMART_ENABLE_SMART_OPERATIONS;
                               bSectorCountReg  := 1;
                               bSectorNumberReg := 1;
                               bCylLowReg      := SMART_CYL_LOW;
                               bCylHighReg      := SMART_CYL_HI;
                               bDriveHeadReg    := $A0 or ((bDriveNum and 1) shl 4); // Compute the drive number.
                               bCommandReg      := IDE_EXECUTE_SMART_FUNCTION;
                     end;
           end;
           Result := DeviceIoControl( hDevice, DFP_SEND_DRIVE_COMMAND, @SCIP, SizeOf(TSendCmdInParams)-1,
                                                                       @SCOP, SizeOf(TSendCmdOutParams)-1, dwBytesReturned, nil );
end;
//-------------------------------------------------------------
// DoReadAttributesCmd
//
// FUNCTION: Send a SMART_READ_ATTRIBUTE_VALUES command to the drive bDriveNum = 0-3
//
function DoReadAttributesCmd(hDevice : THandle;
  SCIP: TSendCmdInParams; var SCOP: TSendCmdOutParams;
  bDriveNum: Byte): BOOL;
var
  dwBytesReturned: DWORD;
begin
           // Set up data structures for Read Attributes SMART Command.
           with SCIP do
           begin
                     cBufferSize:= READ_ATTRIBUTE_BUFFER_SIZE;
                     bDriveNumber:= bDriveNum;
                     with irDriveRegs do
                     begin
                               bFeaturesReg:= SMART_READ_ATTRIBUTE_VALUES;
                               bSectorCountReg:= 1;
                               bSectorNumberReg:= 1;
                               bCylLowReg:= SMART_CYL_LOW;
                               bCylHighReg:= SMART_CYL_HI;
                               bDriveHeadReg:= $A0 or ((bDriveNum and 1) shl 4); // Compute the drive number.
                               bCommandReg:= IDE_EXECUTE_SMART_FUNCTION;
                     end;
           end;
           Result:= DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA,
    @SCIP, SizeOf(TSendCmdInParams)-1, @SCOP,
    SizeOf(TSendCmdOutParams) + READ_ATTRIBUTE_BUFFER_SIZE-1,
    dwBytesReturned, nil);
end;
//-------------------------------------------------------------
// DoReadThresholdsCmd
//
// FUNCTION: Send a SMART_READ_ATTRIBUTE_THRESHOLDS command to the drive bDriveNum = 0-3
//
function DoReadThresholdsCmd( hDevice: THandle; SCIP: TSendCmdInParams;
  var SCOP: TSendCmdOutParams; bDriveNum: Byte): BOOL;
var
  dwBytesReturned: DWORD;
begin
           // Set up data structures for Read Thresholds SMART Command.
           with SCIP do
           begin
                     cBufferSize:= READ_THRESHOLD_BUFFER_SIZE;
                     bDriveNumber:= bDriveNum;
                     with irDriveRegs do
                     begin
                               bFeaturesReg    := SMART_READ_ATTRIBUTE_THRESHOLDS;
                               bSectorCountReg  := 1;
                               bSectorNumberReg := 1;
                               bCylLowReg      := SMART_CYL_LOW;
                               bCylHighReg      := SMART_CYL_HI;
                               bDriveHeadReg    := $A0 or ((bDriveNum and 1) shl 4); // Compute the drive number.
                               bCommandReg      := IDE_EXECUTE_SMART_FUNCTION;
                     end;
           end;
           Result := DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP,
    SizeOf(TSendCmdInParams)-1, @SCOP, SizeOf(TSendCmdOutParams) +
    READ_THRESHOLD_BUFFER_SIZE - 1, dwBytesReturned, nil );
end;
//---------------------------------------------------------------------
// Change the WORD array to a BYTE array
//---------------------------------------------------------------------
procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
                     i : Integer;
                     c : Char;
begin
           ptr := @Data;
           for i := 0 to (Size shr 1)-1 do
           begin
                     c := ptr^;
                     ptr^ := (ptr+1)^;
                     (ptr+1)^ := c;
                     Inc(ptr,2);
           end;
end;


end.
