编程设置硬盘的工作模式 ( 积分: 100 )

  • 主题发起人 主题发起人 laichanghe
  • 开始时间 开始时间
L

laichanghe

Unregistered / Unconfirmed
GUEST, unregistred user!
1.现在的硬盘,都支持ATA-7指令,而且硬盘有四种工作模式,每个硬盘都有自已的寄存器组,要让做什么操作,按照ATA-7指令要求读写相应的硬盘寄存器即可。

2.我们的平台是windows2000或windows Xp.
在用户模式,应用程序不能直接访问硬盘的寄存器。但是WIN32/NF下应该有很多方式,可以实现读写硬盘寄存器。(例如像杀毒软件实时监控,它就能进入内核工作模式,但应该没这个复杂)

3.现在要实现的功能是通过读写硬盘寄存器,分别设置硬盘的工作模式(四种模式任一种).
 
直接对硬件访问,操作系统一般不允许.
 
直接对硬件访问,操作系统一般不允许.
但最终还是可以做到的,请各位大侠,赐教些有建议性的意见!
 
到网上找一下ASPI,Torry's Delphi Pages 好像有Delphi的
 
ASPI,我找了一段时间,但是没有相关的函数。
不知道,tseug,能否帮忙,举手之功。
 
top一下
关注
 
网上有windows2000/XP下直接访问端口的控件
你搜一下试试
 
正常是不行的.除非进入ring0.
写驱动,或者用其他方式进入ring0
 
昨晚翻了一下书,一早上班写了几行代码,不过匆匆忙忙,没有经过仔细测试,肯定
有很多特殊情况没有考虑到,如果哪位兄弟能有空完善之后,方便的话给我发一份。
tseug@263.net
代码:
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.
 
你回复的代码,我已经做了相应的测试,基本能满足我的需求,但是现在碰到一个问题,因为我们做的产品电脑上经常要接上八个硬盘,IDE上接四个,然后在PCI槽上接一IDE扩展卡,也接四个硬盘,在测试时发现设置硬盘的工作模式,对IDE上的硬盘有效,而对扩展卡上的硬盘却无效。在之前,我已有一个功能也是利DeviceIoControl函数,获取硬盘的SMART信息,且八个硬盘,都可以正常获取,如果需要这部分代码,可以邮件给你参考,现在我个人认为,是不是针对扩展卡的硬盘,DeviceIoControl函数中命令码,参数是不一样呢?
期昐回复!
 
接受答案了.
 

Similar threads

D
回复
0
查看
753
DelphiTeacher的专栏
D
D
回复
0
查看
759
DelphiTeacher的专栏
D
D
回复
0
查看
837
DelphiTeacher的专栏
D
D
回复
0
查看
843
DelphiTeacher的专栏
D
后退
顶部