unit HDDIO;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm9 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form9: TForm9;
implementation
{$R *.dfm}
//
const
FILE_DEVICE_CONTROLLER = $00000004;
//
const
// 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_READ_ACCESS = 1;
FILE_WRITE_ACCESS = 2;
//以下定义摘自ntdddisk.h
//
// IDE registers
//
type
PIdeRegs = ^TIdeRegs;
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;
const
READ_ATTRIBUTE_BUFFER_SIZE = 512;
IDENTIFY_BUFFER_SIZE = 512;
READ_THRESHOLD_BUFFER_SIZE = 512;
//以下定义摘自ntddscsi.h
const
IOCTL_SCSI_BASE = FILE_DEVICE_CONTROLLER;
IOCTL_IDE_PASS_THROUGH = (IOCTL_SCSI_BASE shl 16) or
($040A shl 2) or
METHOD_BUFFERED or
((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14);
IOCTL_SCSI_RESCAN_BUS = (IOCTL_SCSI_BASE shl 16) or
($0407 shl 2) or
METHOD_BUFFERED or
(FILE_ANY_ACCESS shl 14);
type
PATA_PASS_THROUGH = ^TATA_PASS_THROUGH;
TATA_PASS_THROUGH = packed record
IdeReg : TIdeRegs;
DataBufferSize : ULONG;
DataBuffer : array[0..0] of Byte;
end;
//
type
PIdenetifyParam = ^TIdenetifyParam;
TIdenetifyParam = 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;
bReserved : array[0..127] of Byte;
end;
//ATA标准的IDE命令码, 参见SCSI总线和IDE接口:协议、应用和编程(第二版)
const
IDE_STANDBY_IMMEDIATE = $94;
//$E0
IDE_IDLE_IMMEDIATE = $95;
//$E1
IDE_STANDBY = $96;
//$E2
IDE_IDLE = $97;
//$E3
IDE_CHECK_POWER_MODE = $98;
//$E5
IDE_SLEEP = $99;
//$E6
IDE_IDENETIFY_DEVICE = $EC;
//Check Power Mode Normal Output,
//摘自 Information technology - AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
const
IDE_STANDBY_MODE = $00;
//00h Device is in Standby mode.
IDE_SPUN_DOWN = $40;
//40h Device is in NV Cache Power Mode and the spindle is spundo
wn or
//spinningdo
wn.
IDE_SPUN_UP = $41;
//41h device is in NV Cache Power Mode and the spindle is spun up or
//spinning up.
IDE_IDLE_MODE = $80;
//80h Device is in Idle mode.
IDE_ACTIVE_OR_IDEL = $FF;
//FFh Device is in Active mode or Idle mode.
type
TIdeDisk = class(TObject)
private
FDrive : Integer;
FHandle : THandle;
protected
function IOCtrl(var Regs: TIdeRegs;
var Buf;
Count: UINT): UINT;
function Open: Boolean;
function Close: Boolean;
public
constructor Create(aDrive: Integer);
destructor Destroy;
override;
function DiskID: String;
function DiskPowerMode: Integer;
procedure DiskStandby;
procedure DiskIdle;
procedure DiskSleep;
end;
procedure ChangeByteOrder(var Buffer;
Count: Integer);
var
p : PChar;
i : Integer;
c : Char;
begin
p := @Buffer;
for i := 0 to (Count div 2)-1do
begin
c := p^;
p^ := (p+1)^;
(p+1)^ := c;
Inc(p,2);
end;
end;
constructor TIdeDisk.Create(aDrive: Integer);
begin
FDrive := aDrive;
Open;
end;
destructor TIdeDisk.Destroy;
begin
Close;
inherited Destroy;
end;
function TIdeDisk.DiskID;
var
r : TIdeRegs;
b : array[0..IDENTIFY_BUFFER_SIZE-1] of Byte;
id: TIdenetifyParam absolute b;
begin
Result := '';
ZeroMemory(@r, SizeOf(r));
ZeroMemory(@b, SizeOf(b));
r.bCommandReg := IDE_IDENETIFY_DEVICE;
r.bDriveHeadReg := $A0;
if IOCtrl(r, b, SizeOf(b)) > 0 then
begin
ChangeByteOrder(b, SizeOf(b));
(PChar(@id.sSerialNumber)+SizeOf(id.sSerialNumber))^ := #0;
Result := PChar(@id.sSerialNumber);
end;
end;
function TIdeDisk.DiskPowerMode: Integer;
var
r : TIdeRegs;
b : array[0..511] of Byte;
begin
Result := 0;
ZeroMemory(@r, SizeOf(r));
ZeroMemory(@b, SizeOf(b));
r.bCommandReg := IDE_CHECK_POWER_MODE;
if IOCtrl(r, b, 0) > 0 then
begin
Result := r.bSectorCountReg;
end;
end;
procedure TIdeDisk.DiskStandby;
var
r : TIdeRegs;
b : array[0..511] of Byte;
begin
ZeroMemory(@r, SizeOf(r));
ZeroMemory(@b, SizeOf(b));
r.bCommandReg := IDE_STANDBY_IMMEDIATE;
if IOCtrl(r, b, 0) > 0 then
begin
//
end;
end;
procedure TIdeDisk.DiskSleep;
var
r : TIdeRegs;
b : array[0..511] of Byte;
begin
ZeroMemory(@r, SizeOf(r));
ZeroMemory(@b, SizeOf(b));
r.bCommandReg := IDE_SLEEP;
if IOCtrl(r, b, 0) > 0 then
begin
//
end;
end;
procedure TIdeDisk.DiskIdle;
var
r : TIdeRegs;
b : array[0..511] of Byte;
begin
ZeroMemory(@r, SizeOf(r));
ZeroMemory(@b, SizeOf(b));
r.bCommandReg := IDE_IDLE_IMMEDIATE;
if IOCtrl(r, b, 0) > 0 then
begin
//
end;
end;
function TIdeDisk.Open: Boolean;
var
S: String;
begin
S := Format('//./PhysicalDrive%d', [FDrive]);
FHandle := CreateFile(
PChar(S),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,
0,
0
);
Result := FHandle <> INVALID_HANDLE_VALUE;
end;
function TIdeDisk.Close: Boolean;
begin
Result := False;
if FHandle <> INVALID_HANDLE_VALUE then
Result := CloseHandle(FHandle);
end;
function TIdeDisk.IOCtrl(var Regs: TIdeRegs;
var Buf;
Count: UINT): UINT;
var
pAPT : PATA_PASS_THROUGH;
Size : UINT;
Status: Bool;
begin
Result := 0;
Size := SizeOf(TATA_PASS_THROUGH) + Count;
pAPT := AllocMem(Size);
if pAPT <> nil then
begin
pAPT^.DataBufferSize := Count;
pAPT^.IdeReg := Regs;
Move(Buf, pAPT^.DataBuffer, Count);
if (pAPT^.IdeReg.bCommandReg = IDE_IDENETIFY_DEVICE) then
begin
DeviceIoControl(FHandle, IOCTL_SCSI_RESCAN_BUS, nil, 0, nil, 0, Result, nil);
Sleep(500);
end;
Status := DeviceIoControl(FHandle, IOCTL_IDE_PASS_THROUGH,
pAPT, Size, pAPT, Size, Result, nil);
if Status then
begin
Regs := pAPT^.IdeReg;
if Result > SizeOf(TATA_PASS_THROUGH) then
begin
Result := Result - SizeOf(TATA_PASS_THROUGH);
if Result > Count then
Result := Count;
Move(pAPT^.DataBuffer, Buf, Result);
end;
end
else
begin
Result := 0;
end;
FreeMem(pAPT);
end;
end;
procedure TForm9.Button1Click(Sender: TObject);
var
ide : TIdeDisk;
begin
ide := TIdeDisk.Create(0);
Memo1.Lines.Add(ide.DiskID);
Memo1.Lines.Add(inttostr(ide.DiskPowerMode));
ide.DiskIdle;
Memo1.Lines.Add(inttostr(ide.DiskPowerMode));
ide.DiskStandby;
Memo1.Lines.Add(inttostr(ide.DiskPowerMode));
ide.DiskSleep;
Memo1.Lines.Add(inttostr(ide.DiskPowerMode));
ide.Free;
end;
end.