unit smart_drv;
interface
{$IFDEF KOL_MCK}
uses Windows, Messages, ShellAPI,
KOL {$IFNDEF KOL_MCK}, mirror, Classes, Controls, mckControls, mckObjs, Graphics,
mckObjs
{$ENDIF};
{$ELSE}
{$I uses.inc}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
{$ENDIF}
procedure OpenSMART;
procedure ReadSMART;
procedure CloseSMART;
function GetATTRName(attr:byte;
var comm:
string):
string;
function GetFlags(flags:word):
string;
procedure SwapBytes(
var buf; count : integer );
Type
TIDSECTOR =
packed record
wGenConfig:word;
wNumCyls:word;
{ wReserved:word;}
wNumHeads:word;
wBytesPerTrack:word;
wBytesPerSector:word;
wSectorsPerTrack:word;
wVendorUnique :
array[0..2]
of word;
sSerialNumber:
array[0..19]
of char;
wBufferType:word;
wBufferSize:word;
wECCSize:word;
sFirmwareRev:
array[0..7]
of char;
sModelNumber:
array[0..39]
of char;
wMoreVendorUnique:word;
wDoubleWordIO:word;
wCapabilities:word;
wReserved1:word;
wPIOTiming:word;
wDMATiming:word;
wBS:word;
wNumCurrentCyls:word;
wNumCurrentHeads:word;
wNumCurrentSectorsPerTrack:word;
ulCurrentSectorCapacity:dword;
wMultSectorStuff:word;
ulTotalAddressableSectors:dword;
wSingleWordDMA:word;
wMultiWordDMA:word;
hz :
array[1..72]
of byte;
LBA48 :
array[0..5]
of byte;
bReserved:
array[0..127]
of byte;
end;
TGETVERSIONOUTPARAMS =
packed record
version : byte;
revision : byte;
reserved : byte;
IDEDevMap : byte;
Capabilities : dword;
reserved1 : dword;
reserved2 : dword;
reserved3 : dword;
reserved4 : dword;
End;
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;
end;
// reserved for future use. Must be zero.
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..3]
of byte;
// Reserved for future expansion.
dwReserved:
array [0..4]
of dword;
// For future use.
bBuffer:
array[0..1024*8]
of byte;
// Input buffer.
end;
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..3]
of byte;
// Reserved for future expansion.
dwReserved:
array[0..1]
of dword;
// Reserved for future expansion.
end;
TDRIVEATTRIBUTE =
packed record
bAttrID:byte;
// Identifies which attribute
wStatusFlags:word;
// see bit definitions below
Value:byte;
// Current normalized value
Worst:byte;
// How bad has it ever been?
Raw:
array[0..5]
of byte;
// Un-normalized value
bReserved:byte;
// ...
end;
TATTRTHRESHOLD =
packed record
bAttrID : byte;
bWarrantyThreshold : byte;
reserved :
array[0..9]
of byte;
end;
TSENDCMDOUTPARAMS =
packed record
cBufferSize : DWord;
// Size of bBuffer in bytes
DriverStatus : TDriverStatus;
// Driver status structure.
attr :
array[0..255]
of TDRIVEATTRIBUTE;
// Buffer of arbitrary length in which to
store the data
read from the
// drive.
end;
TSMARTATTRNAME =
record
value : word;
name :
string[40];
comm :
string[160];
end;
THDDInfo =
record
Model :
string;
FW :
string;
SN :
string;
LBABits : integer;
LBASize : int64;
Cache : integer;
End;
var
VersParams : TGETVERSIONOUTPARAMS;
SCOP :
array[0..3]
of TSENDCMDOUTPARAMS;
AttrCnt :
array[0..3]
of word;
hSMARTIOCTL :
array[0..3]
of thandle;
IDSECTOR :
array[0..3]
of TIDSECTOR;
Thresholds :
array[0..3,0..255]
of TATTRTHRESHOLD;
HDDInfo :
array[0..3]
of THDDInfo;
const
PRE_FAILURE_WARRANTY = $01;
ON_LINE_COLLECTION = $02;
PERFORMANCE_ATTRIBUTE = $04;
ERROR_RATE_ATTRIBUTE = $08;
EVENT_COUNT_ATTRIBUTE = $10;
SELF_PRESERVING_ATTRIBUTE = $20;
IDENTIFY_BUFFER_SIZE = 512;
READ_THRESHOLD_BUFFER_SIZE = 512;
SMART_READ_ATTRIBUTE_THRESHOLDS = $D1;
IDE_ATAPI_ID = $A1;
// Returns ID sector for ATAPI.
IDE_ID_FUNCTION = $
EC;
// Returns ID sector for ATA.
var
SCIP : TSENDCMDINPARAMS;
const
DFP_GET_VERSION = $00074080;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
DFP_SEND_DRIVE_COMMAND = $0007c084;
SMART_ENABLE_SMART_OPERATIONS = $D8;
SMART_DISABLE_SMART_OPERATIONS = $D9;
SMART_RETURN_SMART_STATUS = $DA;
SMART_CYL_LOW = $4F;
SMART_CYL_HI = $C2;
IDE_EXECUTE_SMART_FUNCTION = $B0;
READ_ATTRIBUTE_BUFFER_SIZE = 512;
SMART_READ_ATTRIBUTE_VALUES = $D0;
// ATA4: Renamed
AttrCount = 45;
SmartAttrNames :
array[1..AttrCount]
of TSMARTAttrName=
(
(Value:1;
Name:'
Raw Read Error Rate';Comm:'
Frequency of errors appearance while reading RAW data from a disk'),
(Value:2;
Name:'
Throughput Performance';Comm:'
The average efficiency of hard disk'),
(Value:3;
Name:'
Spin Up Time';Comm:'
Time needed by spindle to spin-up'),
(Value:4;
Name:'
Start/Stop Count';Comm:'
Number of start/stop cycles of spindle'),
(Value:5;
Name:'
Reallocated Sector Count';Comm:'
Quantity of remapped sectors'),
(Value:6;
Name:'
Read Channel Margin';Comm:'
Reserve of channel while reading'),
(Value:7;
Name:'
Seek Error Rate';Comm:'
Frequency of errors appearance while positioning'),
(Value:8;
Name:'
Seek Time Performance';Comm:'
The average efficiency of operations while positioning'),
(Value:9;
Name:'
Power-On Hours Count';Comm:'
Quantity of elapsed hours in the switched-on state'),
(Value:10;
Name:'
Spin-up Retry Count';Comm:'
Number of attempts to start a spindle of a disk'),
(Value:11;
Name:'
Calibration Retry Count';Comm:'
Number of attempts to calibrate a drive'),
(Value:12;
Name:'
Power Cycle Count';Comm:'
Number of complete start/stop cycles of hard disk'),
(Value:13;
Name:'
Soft Read Error Rate';Comm:'
Frequency of "program" errors appearance while reading data from a disk'),
(Value:191;
Name:'
G-Sense Error Rate';Comm:'
Frequency of mistakes appearance as a result of impact loads'),
(Value:192;
Name:'
Power-Off Retract Cycle';Comm:'
Number of the fixed "turning off" drive cycles (Fujitsu: Emergency
Retract Cycle Count)'),
(Value:193;
Name:'
Load/Unload Cycle Count';Comm:'
Number of cycles into Landing Zone position'),
(Value:194;
Name:'
HDD Temperature';Comm:'
Temperature of a Hard Disk Assembly'),
(Value:195;
Name:'
Hardware ECC Recovered';Comm:'
Frequency of the on the fly errors (Fujitsu: ECC On The Fly Count)'),
(Value:196;
Name:'
Reallocated Event Count';Comm:'
Quantity of remapping operations'),
(Value:197;
Name:'
Current Pending Sector Count';Comm:'
Current quantity of unstable sectors (waiting for remapping)'),
(Value:198;
Name:'
Off-line Scan Uncorrectable Count';Comm:'
Quantity of uncorrected errors'),
(Value:199;
Name:'
UltraDMA CRC Error Rate';Comm:'
Total quantity of errors CRC during UltraDMA mode'),
(Value:200;
Name:'
Write Error Rate';Comm:'
Frequency of errors appearance while recording data into disk (Western
Digital: Multi Zone Error Rate)'),
(Value:201;
Name:'
Soft Read Error Rate';Comm:'
Frequency of the off track errors (Maxtor: Off Track Errors)'),
(Value:202;
Name:'
Data Address Mark Errors';Comm:'
Frequency of the Data Address Mark errors'),
(Value:203;
Name:'
Run Out Cancel';Comm:'
Frequency of the ECC errors (Maxtor: ECC Errors)'),
(Value:204;
Name:'
Soft ECC Correction';Comm:'
Quantity of errors corrected by software ECC'),
(Value:205;
Name:'
Thermal Asperity Rate';Comm:'
Frequency of the thermal asperity errors'),
(Value:206;
Name:'
Flying Height';Comm:'
The height of the disk heads above the disk surface'),
(Value:207;
Name:'
Spin High Current';Comm:'
Quantity of used high current to spin up drive'),
(Value:208;
Name:'
Spin Buzz';Comm:'
Quantity of used buzz routines to spin up drive'),
(Value:209;
Name:'
Offline Seek Performance';Comm:'
Drives seek performance during offline operations'),
(Value:220;
Name:'
Disk Shift';Comm:'
Shift of disk is possible as a result of strong shock loading in the store, as a
result of it`s falling or for other reasons'),
(Value:221;
Name:'
G-Sense Error Rate';Comm:'
This attribute is an indication of shock-sensitive sensor - total quantity
of errors appearance as a result of impact loads '),
(Value:222;
Name:'
Loaded Hours';Comm:'
Loading on drive caused by the general operating time of hours it stores'),
(Value:223;
Name:'
Load/Unload Retry Count';Comm:'
Loading on drive caused by numerous recurrences of operations like:
reading, recording, positioning of heads, etc.'),
(Value:224;
Name:'
Load Friction';Comm:'
Loading on drive caused by friction in mechanical parts of the store'),
(Value:225;
Name:'
Load/Unload Cycle Count';Comm:'
Total of cycles of loading on drive'),
(Value:226;
Name:'
Load-in Time';Comm:'
General time of loading for drive'),
(Value:227;
Name:'
Torque Amplification Count';Comm:'
Quantity efforts of the rotating moment of a drive'),
(Value:228;
Name:'
Power-Off Retract Count';Comm:'
Quantity of the fixed turning off`s a drive'),
(Value:230;
Name:'
GMR Head Amplitude';Comm:'
Amplitude of heads trembling (GMR-head) in running mode'),
(Value:231;
Name:'
Temperature';Comm:'
Temperature of a drive'),
(Value:240;
Name:'
Head Flying Hours';Comm:'
Time while head is positioning'),
(Value:250;
Name:'
Read Error Retry Rate';Comm:'
Frequency of errors appearance while reading data from a disk')
);
flagnames :
array[0..5]
of string[2] = ('
PF','
OC','
PA','
ER','
EC','
SP');
implementation
{------------------------------------------------------------------}
function GetFlags(flags:word):
string;
var
i : integer;
s :
string;
Begin
s := '
';
for i := 0
to 5
do
if flags
and (1
shl i) <> 0
then
s := s + flagnames[i]+#32;
GetFlags := s;
End;
{------------------------------------------------------------------}
function GetATTRName(attr:byte;
var comm:
string):
string;
var
i : integer;
begin
for i := 1
to attrCount
do
begin
if attr = SmartAttrNames[i].Value
then
begin
GetAttrName := SmartAttrNames[i].
name;
Comm := SmartAttrNames[i].comm;
exit;
end;
end;
GetAttrName := '
Unknown';
end;
{------------------------------------------------------------------}
procedure SwapBytes(
var buf; count : integer );
Assembler;
Asm
pushad
mov esi, buf
mov edi, esi
mov ecx, count
shr ecx, 1
test ecx, ecx
jz @@exit
@@rep:
lodsw
xchg ah, al
stosw
dec ecx
jnz @@rep
@@exit:
popad
End;
{------------------------------------------------------------------}
procedure OpenSMART;
var
i : integer;
c : dword;
s, s1, s2 :
string;
os : TOSVERSIONINFO;
osver : (wvNT3,wvNT4,wvW2k,wvXP,wv95,wv98,wvME);
begin
for i := 0
to 3
do
begin
os.dwPlatformId := 0;
os.dwOSVersionInfoSize := sizeof(OSVERSIONINFO);
GetVersionEx(
os );
osver := wv98;
case OS.DwMajorVersion
of
3: osver := wvNT3;
4:
case OS.DwMinorVersion
of
0:
if OS.dwPlatformId = VER_PLATFORM_WIN32_NT
then osver := wvNT4
else osver := wv95;
10: osver := wv98;
90: osver := wvME;
end;
5:
case OS.DwMinorVersion
of
0: osver := wvW2K;
1: osver := wvXP;
end;
end;
hSMARTIOCTL[i] := 0;
if (osver = wv98)
or (osver = wv95)
or (osver=wvME)
then
hSMARTIOCTL[i] := CreateFile('
\\.\SMARTVSD', 0,0,
NIL,
CREATE_NEW, 0, 0)
else
hSMARTIOCTL[i] := CreateFile(pchar( '
\\.\PhysicalDrive'+format('
%d',[i])),
GENERIC_READ
or GENERIC_WRITE,
FILE_SHARE_READ
or FILE_SHARE_WRITE,
NIL,
OPEN_EXISTING,
0,
0);
if hSMARTIOCTL[i] = INVALID_HANDLE_VALUE
then continue;
DeviceIoControl(hSMARTIOCTL[i], DFP_GET_VERSION,
NIL,
0,
@VersParams,
sizeof(VersParams),
c,
NIL) ;
FillChar( SCIP, SizeOf( SCIP ), 0 );
SCIP.cBufferSize := 0;
SCIP.irDriveRegs.bFeaturesReg := SMART_ENABLE_SMART_OPERATIONS;
SCIP.irDriveRegs.bSectorCountReg := 1;
SCIP.irDriveRegs.bSectorNumberReg := 1;
SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
//
// Compute the drive number.
//
SCIP.irDriveRegs.bDriveHeadReg := $A0
or (( i
and 1)
shl 4);
SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
SCIP.bDriveNumber := i;
DeviceIoControl(hSMARTIOCTL[i], DFP_SEND_DRIVE_COMMAND,
@SCIP, sizeof(TSENDCMDINPARAMS) - 1,
@SCOP[i], sizeof(TSENDCMDOUTPARAMS) - 1,
c,
NIL);
SCIP.cBufferSize := IDENTIFY_BUFFER_SIZE;
SCIP.irDriveRegs.bFeaturesReg := 0;
SCIP.irDriveRegs.bSectorCountReg := 1;
SCIP.irDriveRegs.bSectorNumberReg := 1;
SCIP.irDriveRegs.bCylLowReg := 0;
SCIP.irDriveRegs.bCylHighReg := 0;
SCIP.irDriveRegs.bDriveHeadReg := $A0
or ((i
and 1)
shl 4);
SCIP.irDriveRegs.bCommandReg := IDE_ID_FUNCTION
{ or IDE_ATAPI_ID};
SCIP.bDriveNumber := i;
SCIP.cBufferSize := IDENTIFY_BUFFER_SIZE;
DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
@SCIP, sizeof(TSENDCMDINPARAMS) - 1,
@SCOP[i], sizeof(TSENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
c,
NIL) ;
Move(SCOP[i].attr, IDSECTOR[i], SizeOf( IDSECTOR[i] ) );
SCIP.cBufferSize := READ_THRESHOLD_BUFFER_SIZE;
SCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_THRESHOLDS;
SCIP.irDriveRegs.bSectorCountReg := 1;
SCIP.irDriveRegs.bSectorNumberReg := 1;
SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
SCIP.irDriveRegs.bDriveHeadReg := $A0
or ((i
and 1)
shl 4);
SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
SCIP.bDriveNumber := i;
DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
@SCIP, sizeof(TSENDCMDINPARAMS) - 1,
@SCOP[i], sizeof(TSENDCMDOUTPARAMS) + READ_THRESHOLD_BUFFER_SIZE - 1,
c,
NIL) ;
Move( SCOP[ i ].attr, Thresholds[ i ], SizeOf( Thresholds[ i ] ) );
HDDInfo[ i ].LBASize := 0;
HDDInfo[ i ].LBABits := 28;
if idsector[i].ulTotalAddressableSectors = $FFFFFFF
then
begin
move( idsector[i].lba48,HDDInfo[ i ].LBASize,6);
HDDInfo[ i ].LBABits := 48;
end
else
move( idsector[i].ulTotalAddressableSectors,HDDInfo[ i ].LBASize,4);
s := IDSECTOR[i].sModelNumber +#32;
SwapBytes( s[1], Length(s) );
While (Length(s)<>0)
and (s[Length(s)-1]=#32)
do
SetLength( s, length(s)-1);
HDDInfo[ i ].Model := s;
s := IDSECTOR[i].sFirmwareRev +#32;
SwapBytes( s[1], Length(s) );
HDDInfo[ i ].FW := s;
s := IDSECTOR[i].sSerialNumber+#32;
SwapBytes( s[1], Length(s) );
While (Length(s)<>0)
and (s[1]=#32)
do
delete(s,1,1);
HDDInfo[ i ].SN := s;
HDDInfo[ i ].Cache := IDSECTOR[i].wBufferSIze
div 2;
end;
end;
{------------------------------------------------------------------}
procedure CloseSMART;
var
i : integer;
Begin
for i := 0
to 3
do
CloseHandle(hSMARTIOCTL[i]);
End;
{------------------------------------------------------------------}
procedure ReadSMART;
var
i,j : integer;
c : dword;
Begin
OpenSMART;
for i := 0
to 3
do
begin
if hSMARTIOCTL[i] = INVALID_HANDLE_VALUE
then continue;
SCIP.cBufferSize := READ_ATTRIBUTE_BUFFER_SIZE;
SCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_VALUES;
SCIP.irDriveRegs.bSectorCountReg := 1;
SCIP.irDriveRegs.bSectorNumberReg := 1;
SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
//
// Compute the drive number.
//
SCIP.irDriveRegs.bDriveHeadReg := $A0
or (( i
and 1)
shl 4);
SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
SCIP.bDriveNumber := i;
FillChar( SCOP[i], SizeOf( SCOP[i] ), 0 );
DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
@SCIP, sizeof(TSENDCMDINPARAMS) - 1,
@SCOP[ i ], sizeof(TSENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE,
c,
NIL);
AttrCnt[ i ] := 0;
for j := 0
to 255
do
if SCOP[ i ].attr[ j ].bAttrID = 0
then
begin
AttrCnt[ i ] := j;
break;
end;
end;
CloseSMART;
End;
{------------------------------------------------------------------}
end.