unit smart;

interface

uses
  sysutils, windows;
// Miscellaneous
const
  MAX_IDE_DRIVES             = 4; // Max number of drives assuming primary/secondary, master/slave topology
  READ_ATTRIBUTE_BUFFER_SIZE = 512;
  IDENTIFY_BUFFER_SIZE	     = 512;
  READ_THRESHOLD_BUFFER_SIZE = 512;

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

//---------------------------------------------------------------------
// GETVERSIONOUTPARAMS contains the data returned from the
// Get Driver Version function.
//---------------------------------------------------------------------
type
  GETVERSIONOUTPARAMS = 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}
  PGETVERSIONOUTPARAMS = ^GETVERSIONOUTPARAMS;

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

//---------------------------------------------------------------------
// IDE registers
//---------------------------------------------------------------------
type
  IDEREGS = 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}
  PIDEREGS = ^IDEREGS;

//---------------------------------------------------------------------
// SENDCMDINPARAMS contains the input parameters for the
// Send Command to Drive function.
//---------------------------------------------------------------------
type
  SENDCMDINPARAMS = packed record
    cBufferSize:  DWORD;   // Buffer size in bytes
    irDriveRegs:  IDEREGS; // 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}
  PSENDCMDINPARAMS = ^SENDCMDINPARAMS;

//
// 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
//
const
  SMART_CYL_LOW = $4F;
  SMART_CYL_HI  = $C2;

//---------------------------------------------------------------------
// Status returned from driver
//---------------------------------------------------------------------
type
  DRIVERSTATUS = 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}
  PDRIVERSTATUS = ^DRIVERSTATUS;

//
// 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
  SENDCMDOUTPARAMS = packed record
    cBufferSize:  DWORD;          // Size of bBuffer in bytes
    DriverStatus: DRIVERSTATUS;   // 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}
  PSENDCMDOUTPARAMS = ^SENDCMDOUTPARAMS;

//---------------------------------------------------------------------
// 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
  DRIVEATTRIBUTE = 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..5] of Byte; // Un-normalized value
    bReserved:    Byte; // ...
  end; {DRIVEATTRIBUTE}
  PDRIVEATTRIBUTE = ^DRIVEATTRIBUTE;

//---------------------------------------------------------------------
// The following structure defines the structure of a Warranty Threshold
// Obsoleted in ATA4!
//---------------------------------------------------------------------
type
  ATTRTHRESHOLD = packed record
    bAttrID: Byte;            // Identifies which attribute
    bWarrantyThreshold: Byte; // Triggering value
    bReserved: array [0..9] of Byte; // ...
//    bReserved: string [10]; // ...
  end; {ATTRTHRESHOLD}
  PATTRTHRESHOLD = ^ATTRTHRESHOLD;

//---------------------------------------------------------------------
// The following struct defines the interesting part of the IDENTIFY
// buffer:
//---------------------------------------------------------------------
type
  USHORT = Word;
  
  IDSECTOR = 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;
//    sSerialNumber: string [20];
    wBufferType:   USHORT;
    wBufferSize:   USHORT;
    wECCSize:      USHORT;
    sFirmwareRev:  array [0..7] of CHAR;
//    sFirmwareRev:  string [8];
    sModelNumber:  array [0..39] of CHAR;
//    sModelNumber:  string [40];

    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;
//    bReserved: string [128];
  end; {IDSECTOR}
  PIDSECTOR = ^IDSECTOR;

//---------------------------------------------------------------------
// Valid Attribute IDs
//---------------------------------------------------------------------
const
  ATTR_INVALID                 = $00;
  ATTR_READ_ERROR_RATE         = $01;
  ATTR_THROUGHPUT_PERF         = $02;
  ATTR_SPIN_UP_TIME            = $03;
  ATTR_START_STOP_COUNT        = $04;
  ATTR_REALLOC_SECTOR_COUNT    = $05;
  ATTR_READ_CHANNEL_MARGIN     = $06;
  ATTR_SEEK_ERROR_RATE         = $07;
  ATTR_SEEK_TIME_PERF          = $08;
  ATTR_POWER_ON_HRS_COUNT      = $09;
  ATTR_SPIN_RETRY_COUNT        = $0A;
  ATTR_CALIBRATION_RETRY_COUNT = $0B;
  ATTR_POWER_CYCLE_COUNT       = $0C;
  ATTR_TEMPERATURE             = $C2;
//
  ATTR_REALLOCATED_EVENT_COUNT = $C4;
  ATTR_CURR_PENDING_SECTOR     = $C5;
  ATTR_OFFLINE_UNCORRECTABLE   = $C6;
  ATTR_UDMA_CRC_ERROR_COUNT    = $C7;
  ATTR_WRITE_ERROR_COUNT       = $C8;
//
//

//---------------------------------------------------------------------
// Status Flags Values
//---------------------------------------------------------------------
const
  PRE_FAILURE_WARRANTY      = $01;
  ON_LINE_COLLECTION        = $02;
  PERFORMANCE_ATTRIBUTE     = $04;
  ERROR_RATE_ATTRIBUTE      = $08;
  EVENT_COUNT_ATTRIBUTE     = $10;
  SELF_PRESERVING_ATTRIBUTE = $20;

  NUM_ATTRIBUTE_STRUCTS     = 30;

function StatFlagsToStars (const aFlags: Byte;
                           const aSep: String = ''): String;
function AttribName (const anId: Integer): String;

implementation

function StatFlagsToStars (const aFlags: Byte;
                           const aSep: String = ''): String;
begin
  if (PRE_FAILURE_WARRANTY and aFlags) <> 0 then
    Result := '*' + aSep
  else
    Result := ' ' + aSep;

  if (ON_LINE_COLLECTION and aFlags) <> 0 then
    Result := Result + '*' + aSep
  else
    Result := Result + ' ' + aSep;

  if (PERFORMANCE_ATTRIBUTE and aFlags) <> 0 then
    Result := Result + '*' + aSep
  else
    Result := Result + ' ' + aSep;

  if (ERROR_RATE_ATTRIBUTE and aFlags) <> 0 then
    Result := Result + '*' + aSep
  else
    Result := Result + ' ' + aSep;

  if (EVENT_COUNT_ATTRIBUTE and aFlags) <> 0 then
    Result := Result + '*' + aSep
  else
    Result := Result + ' ' + aSep;

  if (SELF_PRESERVING_ATTRIBUTE and aFlags) <> 0 then
    Result := Result + '*' + aSep
  else
    Result := Result + ' ' + aSep;
end;

function AttribName (const anId: Integer): String;
begin
  case anId of
      0: Result := 'No Attribute Here       ';
      1: Result := 'Raw Read Error Rate     ';
      2: Result := 'Throughput Performance  ';
      3: Result := 'Spin Up Time            ';
      4: Result := 'Start/Stop Count        ';
      5: Result := 'Reallocated Sector Count';
      6: Result := 'Read Channel Margin     ';
      7: Result := 'Seek Error Rate         ';
      8: Result := 'Seek Time Performance   ';
      9: Result := 'Power On Hours Count    ';
     10: Result := 'Spin Retry Count        ';
     11: Result := 'Calibration Retry Count ';
     12: Result := 'Power Cycle Count       ';
    196: Result := 'Reallocated Event Count ';
    194: Result := 'Temperature             ';
    197: Result := 'Current Pending Sector  ';
    198: Result := 'Offline Uncorrectable   ';
    199: Result := 'UDMA CRC Error Count    ';
    200: Result := 'Write Error Count       '
    else
      Result := '(Unknown attribute)     ';
  end; {case}
end; {AttribName}

end.
