Sending ATA commands directly to device in Windows

2019-01-22 03:46发布

问题:

I’m trying to send ATA commands to a physical disk in Windows, and get the response from the device.

Note: In this case I want to send the IDENTIFY DEVICE (0xEC) command. The device will respond with a 512-byte block of data. (In particular I’m interested in bit 0 of word 119 - the device’s support for the TRIM command).

I know that I need to use CreateFile to open the device:

handle = CreateFile(
    "\\.\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, 
    nil,            // no security attributes
    OPEN_EXISTING,
    0,              // flags and attributes
    nil             // no template file
);

But after this I’m stymied about what to do.

I thought about sending 0xEC using [DeviceIoControl][4]:

// const ATACommand_IdentifyDevice = 0xEC;
uint bytesReturned = 0;

DeviceIoControl(handle, 
    0xEC,               // IO Control Code
    nil,                // input buffer not needed
    0,                  // input buffer is zero bytes
    @buffer,            // output buffer to store the returned 512-bytes
    512,                // output buffer is 512 bytes long
    out bytesReturned, 
    nil                 // not an overlapped operation
);

But this is completely wrong. An IoControlCode sent to DeviceIoControl must be a valid IO_CTL, which are built using the macro:

#define CTL_CODE(DeviceType, Function, Method, Access) (
   ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)

Looking at the SDK, there are a number of valid Disk Management Control Codes, e.g.:

  • IOCTL_DISK_CREATE_DISK
  • IOCTL_DISK_GET_DRIVE_GEOMETRY
  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  • IOCTL_DISK_GET_PARTITION_INFO
  • IOCTL_STORAGE_QUERY_PROPERTY

But none of them are IDENTIFY DEVICE command, or return anything it returns.

So I believe I have to use some “raw” method of sending commands.


Searching around, I came across and undocumented IOCTL

#define  DFP_RECEIVE_DRIVE_DATA   0x0007c088   

Which when you break down the IOCTL pieces, means:

Custom: (0)
Device Type: (7) FILE_DEVICE_DISK
Required Access: (3) METHOD_NEITHER
Custom: (0)
Function Code: (34)
Transfer Type: (0)

But there is no documentation anywhere on what the inputBuffer must contain, its size, and what its outputBuffer will contain, or its required. Nor can I figure out what functionCode 34 (0x22) is.


My question: How do I send raw ATA commands (e.g. 0xEC) to an ATA device, and read its response?

See also

  • IOCTL_ATA_PASS_THROUGH Control Code
  • IOCTL_ATA_PASS_THROUGH_DIRECT Control Code
  • ATA_PASS_THROUGH_EX Structure

Answer pieces

Open the drive with ReadWrite access:

handle = CreateFile(
    "\\.\PhysicalDrive0", 
    GENERIC_READ or GENERIC_WRITE, // IOCTL_ATA_PASS_THROUGH requires read-write
    FILE_SHARE_READ, 
    nil,            // no security attributes
    OPEN_EXISTING,
    0,              // flags and attributes
    nil             // no template file
);

Setup an ATA_PASS_THROUGH_EX structure as our input buffer to use with IOCTL_ATA_PASS_THROUGH IO control code:

ATA_PASS_THROUGH_EX inputBuffer;
inputBuffer.Length = sizeof(ATA_PASS_THROUGH_EX);
inputBuffer.AtaFlags = ATA_FLAGS_DATA_IN;
inputBuffer.DataTransferLength = 0;
inputBuffer.DataBufferOffset = 0;
// todo: put the ATA command (e.g. 0xEC) somewhere

uint inputBufferSize = sizeof(ATA_PASS_THROUGH_EX);

Setup an output buffer to hold our expected 512-byte response from the drive:

Byte[] outputBuffer = new Byte[512];
uint outputBufferSize = 512;

Call DeviceIoControl:

int ioControlCode = IOCTL_ATA_PASS_THROUGH; // or maybe IOCTL_ATA_PASS_THROUGH_DIRECT
uint bytesReturned = 0;

DeviceIoControl(handle, ioControlCode,
    inputBuffer, inputBufferSize,
    outputBuffer, outputBufferSize,
    out bytesReturned,
    nil      // not an overlapped operation    
);

Close the file handle:

handle.Close();

回答1:

You need to use IOCTL_ATA_PASS_THROUGH/IOCTL_ATA_PASS_THROUGH_DIRECT, these are quite well documented. Also, you need GENERIC_READ|GENERIC_WRITE access for CreateFile.

Be aware that pre XP SP2 does not support these properly. Also, if you have a nForce based MB with nvidia drivers, your SATA drives will appear as SCSI and you can't use this passthrough.

In some cases, the SMART IOCTL's (e.g. SMART_RCV_DRIVE_DATA) will work on nForce drivers. You can use these to get IDENTIFY and SMART data, but not much else.

The open source smartmontools is a good place to start looking for sample code.

EDIT: Sample from an app talking to ATA devices.

EResult DeviceOperationManagerWin::executeATACommandIndirect(ATACommand & Cmd) {
    const uint32 FillerSize = 0;
    Utils::ByteBuffer B;
    B.reserve(sizeof(ATA_PASS_THROUGH_EX) + 4 + Cmd.bufferSize());
    ATA_PASS_THROUGH_EX & PTE = * (ATA_PASS_THROUGH_EX *) B.appendPointer(sizeof(ATA_PASS_THROUGH_EX) + FillerSize + Cmd.bufferSize());
    uint8 * DataPtr = ((uint8 *) &PTE) + sizeof(ATA_PASS_THROUGH_EX) + FillerSize;

    memset(&PTE, 0, sizeof(ATA_PASS_THROUGH_EX) + FillerSize);
    PTE.Length = sizeof(PTE);
    PTE.AtaFlags = 0;
    PTE.AtaFlags |= Cmd.requiresDRDY() ? ATA_FLAGS_DRDY_REQUIRED : 0;
    switch (Cmd.dataDirection()) {
    case ddFromDevice: 
        PTE.AtaFlags |= ATA_FLAGS_DATA_IN; 
        break;
    case ddToDevice:
        PTE.AtaFlags |= ATA_FLAGS_DATA_OUT;
        memcpy(DataPtr, Cmd.buffer(), Cmd.bufferSize());
        break;
    default:
        break;
    }
    PTE.AtaFlags |= Cmd.is48Bit() ? ATA_FLAGS_48BIT_COMMAND : 0;
    PTE.AtaFlags |= Cmd.isDMA() ? ATA_FLAGS_USE_DMA : 0;
    PTE.DataTransferLength = Cmd.bufferSize();
    PTE.TimeOutValue = Cmd.timeout();
    PTE.DataBufferOffset = sizeof(PTE) + FillerSize;
    PTE.DataTransferLength = Cmd.bufferSize();
    PTE.CurrentTaskFile[0] = Cmd.taskFileIn0().Features;
    PTE.CurrentTaskFile[1] = Cmd.taskFileIn0().Count;
    PTE.CurrentTaskFile[2] = Cmd.taskFileIn0().LBALow;
    PTE.CurrentTaskFile[3] = Cmd.taskFileIn0().LBAMid;
    PTE.CurrentTaskFile[4] = Cmd.taskFileIn0().LBAHigh;
    PTE.CurrentTaskFile[5] = Cmd.taskFileIn0().Device;
    PTE.CurrentTaskFile[6] = Cmd.taskFileIn0().Command;
    PTE.CurrentTaskFile[7] = 0;
    if (Cmd.is48Bit()) {
        PTE.PreviousTaskFile[0] = Cmd.taskFileIn1().Features;
        PTE.PreviousTaskFile[1] = Cmd.taskFileIn1().Count;
        PTE.PreviousTaskFile[2] = Cmd.taskFileIn1().LBALow;
        PTE.PreviousTaskFile[3] = Cmd.taskFileIn1().LBAMid;
        PTE.PreviousTaskFile[4] = Cmd.taskFileIn1().LBAHigh;
        PTE.PreviousTaskFile[5] = Cmd.taskFileIn1().Device;
        PTE.PreviousTaskFile[6] = 0;
        PTE.PreviousTaskFile[7] = 0;
    }

    DWORD BR; 
    if (!DeviceIoControl(FHandle, IOCTL_ATA_PASS_THROUGH, &PTE, B.size(), &PTE, B.size(), &BR, 0)) {
        FLastOSError = GetLastError();
        LOG_W << "ioctl ATA_PT failed for " << Cmd << ": " << FLastOSError << " (" << Utils::describeOSError(FLastOSError) << ")";
        return Utils::mapOSError(FLastOSError);
    }
    Cmd.taskFileOut0().Error = PTE.CurrentTaskFile[0];
    Cmd.taskFileOut0().Count = PTE.CurrentTaskFile[1];
    Cmd.taskFileOut0().LBALow = PTE.CurrentTaskFile[2];
    Cmd.taskFileOut0().LBAMid = PTE.CurrentTaskFile[3];
    Cmd.taskFileOut0().LBAHigh = PTE.CurrentTaskFile[4];
    Cmd.taskFileOut0().Device = PTE.CurrentTaskFile[5];
    Cmd.taskFileOut0().Status = PTE.CurrentTaskFile[6];
    Cmd.taskFileOut1().Error = PTE.PreviousTaskFile[0];
    Cmd.taskFileOut1().Count = PTE.PreviousTaskFile[1];
    Cmd.taskFileOut1().LBALow = PTE.PreviousTaskFile[2];
    Cmd.taskFileOut1().LBAMid = PTE.PreviousTaskFile[3];
    Cmd.taskFileOut1().LBAHigh = PTE.PreviousTaskFile[4];
    Cmd.taskFileOut1().Device = PTE.PreviousTaskFile[5];
    Cmd.taskFileOut1().Status = PTE.PreviousTaskFile[6];
    if (Cmd.dataDirection() == ddFromDevice) {
        memcpy(Cmd.buffer(), DataPtr, Cmd.bufferSize());
    }
    return resOK;
    }

EDIT: Sample without external dependencies.

IDENTIFY requires a 512 byte buffer for data:

unsigned char Buffer[512 + sizeof(ATA_PASS_THROUGH_EX)] = { 0 };
ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer;
PTE.Length = sizeof(PTE);
PTE.TimeOutValue = 10;
PTE.DataTransferLength = 512;
PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);

Set up the IDE registers as specified in ATA spec.

IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile;
ir->bCommandReg = 0xEC;
ir->bSectorCountReg = 1;

IDENTIFY is neither 48-bit nor DMA, it reads from the device:

PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;

Do the ioctl:

DeviceIOControl(Handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0);

Here you should insert error checking, both from DeviceIOControl and by looking at IDEREGS for device reported errors.

Get the IDENTIFY data, assuming you have defined a struct IdentifyData

IdentifyData * IDData = (IdentifyData *) (Buffer + sizeof(ATA_PASS_THROUGH_EX));


回答2:

Based on the answer https://stackoverflow.com/a/5071027/15485 by Erik I wrote the following self-contained code. I tested it on a DELL laptop with an SSD disk and running Windows 7.

// Sending ATA commands directly to device in Windows?
// https://stackoverflow.com/questions/5070987/sending-ata-commands-directly-to-device-in-windows

#include <Windows.h>
#include <ntddscsi.h> // for ATA_PASS_THROUGH_EX
#include <iostream>

// I have copied the struct declaration from 
// "IDENTIFY_DEVICE_DATA structure" http://msdn.microsoft.com/en-us/library/windows/hardware/ff559006(v=vs.85).aspx
// I think it is better to include the suitable header (MSDN says the header is Ata.h and suggests to include Irb.h)
typedef struct _IDENTIFY_DEVICE_DATA {
   struct {
      USHORT Reserved1  :1;
      USHORT Retired3  :1;
      USHORT ResponseIncomplete  :1;
      USHORT Retired2  :3;
      USHORT FixedDevice  :1;
      USHORT RemovableMedia  :1;
      USHORT Retired1  :7;
      USHORT DeviceType  :1;
   } GeneralConfiguration;
   USHORT NumCylinders;
   USHORT ReservedWord2;
   USHORT NumHeads;
   USHORT Retired1[2];
   USHORT NumSectorsPerTrack;
   USHORT VendorUnique1[3];
   UCHAR  SerialNumber[20];
   USHORT Retired2[2];
   USHORT Obsolete1;
   UCHAR  FirmwareRevision[8];
   UCHAR  ModelNumber[40];
   UCHAR  MaximumBlockTransfer;
   UCHAR  VendorUnique2;
   USHORT ReservedWord48;
   struct {
      UCHAR  ReservedByte49;
      UCHAR  DmaSupported  :1;
      UCHAR  LbaSupported  :1;
      UCHAR  IordyDisable  :1;
      UCHAR  IordySupported  :1;
      UCHAR  Reserved1  :1;
      UCHAR  StandybyTimerSupport  :1;
      UCHAR  Reserved2  :2;
      USHORT ReservedWord50;
   } Capabilities;
   USHORT ObsoleteWords51[2];
   USHORT TranslationFieldsValid  :3;
   USHORT Reserved3  :13;
   USHORT NumberOfCurrentCylinders;
   USHORT NumberOfCurrentHeads;
   USHORT CurrentSectorsPerTrack;
   ULONG  CurrentSectorCapacity;
   UCHAR  CurrentMultiSectorSetting;
   UCHAR  MultiSectorSettingValid  :1;
   UCHAR  ReservedByte59  :7;
   ULONG  UserAddressableSectors;
   USHORT ObsoleteWord62;
   USHORT MultiWordDMASupport  :8;
   USHORT MultiWordDMAActive  :8;
   USHORT AdvancedPIOModes  :8;
   USHORT ReservedByte64  :8;
   USHORT MinimumMWXferCycleTime;
   USHORT RecommendedMWXferCycleTime;
   USHORT MinimumPIOCycleTime;
   USHORT MinimumPIOCycleTimeIORDY;
   USHORT ReservedWords69[6];
   USHORT QueueDepth  :5;
   USHORT ReservedWord75  :11;
   USHORT ReservedWords76[4];
   USHORT MajorRevision;
   USHORT MinorRevision;
   struct {
      USHORT SmartCommands  :1;
      USHORT SecurityMode  :1;
      USHORT RemovableMediaFeature  :1;
      USHORT PowerManagement  :1;
      USHORT Reserved1  :1;
      USHORT WriteCache  :1;
      USHORT LookAhead  :1;
      USHORT ReleaseInterrupt  :1;
      USHORT ServiceInterrupt  :1;
      USHORT DeviceReset  :1;
      USHORT HostProtectedArea  :1;
      USHORT Obsolete1  :1;
      USHORT WriteBuffer  :1;
      USHORT ReadBuffer  :1;
      USHORT Nop  :1;
      USHORT Obsolete2  :1;
      USHORT DownloadMicrocode  :1;
      USHORT DmaQueued  :1;
      USHORT Cfa  :1;
      USHORT AdvancedPm  :1;
      USHORT Msn  :1;
      USHORT PowerUpInStandby  :1;
      USHORT ManualPowerUp  :1;
      USHORT Reserved2  :1;
      USHORT SetMax  :1;
      USHORT Acoustics  :1;
      USHORT BigLba  :1;
      USHORT DeviceConfigOverlay  :1;
      USHORT FlushCache  :1;
      USHORT FlushCacheExt  :1;
      USHORT Resrved3  :2;
      USHORT SmartErrorLog  :1;
      USHORT SmartSelfTest  :1;
      USHORT MediaSerialNumber  :1;
      USHORT MediaCardPassThrough  :1;
      USHORT StreamingFeature  :1;
      USHORT GpLogging  :1;
      USHORT WriteFua  :1;
      USHORT WriteQueuedFua  :1;
      USHORT WWN64Bit  :1;
      USHORT URGReadStream  :1;
      USHORT URGWriteStream  :1;
      USHORT ReservedForTechReport  :2;
      USHORT IdleWithUnloadFeature  :1;
      USHORT Reserved4  :2;
   } CommandSetSupport;
   struct {
      USHORT SmartCommands  :1;
      USHORT SecurityMode  :1;
      USHORT RemovableMediaFeature  :1;
      USHORT PowerManagement  :1;
      USHORT Reserved1  :1;
      USHORT WriteCache  :1;
      USHORT LookAhead  :1;
      USHORT ReleaseInterrupt  :1;
      USHORT ServiceInterrupt  :1;
      USHORT DeviceReset  :1;
      USHORT HostProtectedArea  :1;
      USHORT Obsolete1  :1;
      USHORT WriteBuffer  :1;
      USHORT ReadBuffer  :1;
      USHORT Nop  :1;
      USHORT Obsolete2  :1;
      USHORT DownloadMicrocode  :1;
      USHORT DmaQueued  :1;
      USHORT Cfa  :1;
      USHORT AdvancedPm  :1;
      USHORT Msn  :1;
      USHORT PowerUpInStandby  :1;
      USHORT ManualPowerUp  :1;
      USHORT Reserved2  :1;
      USHORT SetMax  :1;
      USHORT Acoustics  :1;
      USHORT BigLba  :1;
      USHORT DeviceConfigOverlay  :1;
      USHORT FlushCache  :1;
      USHORT FlushCacheExt  :1;
      USHORT Resrved3  :2;
      USHORT SmartErrorLog  :1;
      USHORT SmartSelfTest  :1;
      USHORT MediaSerialNumber  :1;
      USHORT MediaCardPassThrough  :1;
      USHORT StreamingFeature  :1;
      USHORT GpLogging  :1;
      USHORT WriteFua  :1;
      USHORT WriteQueuedFua  :1;
      USHORT WWN64Bit  :1;
      USHORT URGReadStream  :1;
      USHORT URGWriteStream  :1;
      USHORT ReservedForTechReport  :2;
      USHORT IdleWithUnloadFeature  :1;
      USHORT Reserved4  :2;
   } CommandSetActive;
   USHORT UltraDMASupport  :8;
   USHORT UltraDMAActive  :8;
   USHORT ReservedWord89[4];
   USHORT HardwareResetResult;
   USHORT CurrentAcousticValue  :8;
   USHORT RecommendedAcousticValue  :8;
   USHORT ReservedWord95[5];
   ULONG  Max48BitLBA[2];
   USHORT StreamingTransferTime;
   USHORT ReservedWord105;
   struct {
      USHORT LogicalSectorsPerPhysicalSector  :4;
      USHORT Reserved0  :8;
      USHORT LogicalSectorLongerThan256Words  :1;
      USHORT MultipleLogicalSectorsPerPhysicalSector  :1;
      USHORT Reserved1  :2;
   } PhysicalLogicalSectorSize;
   USHORT InterSeekDelay;
   USHORT WorldWideName[4];
   USHORT ReservedForWorldWideName128[4];
   USHORT ReservedForTlcTechnicalReport;
   USHORT WordsPerLogicalSector[2];
   struct {
      USHORT ReservedForDrqTechnicalReport  :1;
      USHORT WriteReadVerifySupported  :1;
      USHORT Reserved01  :11;
      USHORT Reserved1  :2;
   } CommandSetSupportExt;
   struct {
      USHORT ReservedForDrqTechnicalReport  :1;
      USHORT WriteReadVerifyEnabled  :1;
      USHORT Reserved01  :11;
      USHORT Reserved1  :2;
   } CommandSetActiveExt;
   USHORT ReservedForExpandedSupportandActive[6];
   USHORT MsnSupport  :2;
   USHORT ReservedWord1274  :14;
   struct {
      USHORT SecuritySupported  :1;
      USHORT SecurityEnabled  :1;
      USHORT SecurityLocked  :1;
      USHORT SecurityFrozen  :1;
      USHORT SecurityCountExpired  :1;
      USHORT EnhancedSecurityEraseSupported  :1;
      USHORT Reserved0  :2;
      USHORT SecurityLevel  :1;
      USHORT Reserved1  :7;
   } SecurityStatus;
   USHORT ReservedWord129[31];
   struct {
      USHORT MaximumCurrentInMA2  :12;
      USHORT CfaPowerMode1Disabled  :1;
      USHORT CfaPowerMode1Required  :1;
      USHORT Reserved0  :1;
      USHORT Word160Supported  :1;
   } CfaPowerModel;
   USHORT ReservedForCfaWord161[8];
   struct {
      USHORT SupportsTrim  :1;
      USHORT Reserved0  :15;
   } DataSetManagementFeature;
   USHORT ReservedForCfaWord170[6];
   USHORT CurrentMediaSerialNumber[30];
   USHORT ReservedWord206;
   USHORT ReservedWord207[2];
   struct {
      USHORT AlignmentOfLogicalWithinPhysical  :14;
      USHORT Word209Supported  :1;
      USHORT Reserved0  :1;
   } BlockAlignment;
   USHORT WriteReadVerifySectorCountMode3Only[2];
   USHORT WriteReadVerifySectorCountMode2Only[2];
   struct {
      USHORT NVCachePowerModeEnabled  :1;
      USHORT Reserved0  :3;
      USHORT NVCacheFeatureSetEnabled  :1;
      USHORT Reserved1  :3;
      USHORT NVCachePowerModeVersion  :4;
      USHORT NVCacheFeatureSetVersion  :4;
   } NVCacheCapabilities;
   USHORT NVCacheSizeLSW;
   USHORT NVCacheSizeMSW;
   USHORT NominalMediaRotationRate;
   USHORT ReservedWord218;
   struct {
      UCHAR NVCacheEstimatedTimeToSpinUpInSeconds;
      UCHAR Reserved;
   } NVCacheOptions;
   USHORT ReservedWord220[35];
   USHORT Signature  :8;
   USHORT CheckSum  :8;
} IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA;

// Taken from smartmontools
// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
// bytes.
static void swapbytes(char * out, const char * in, size_t n)
{
   for (size_t i = 0; i < n; i += 2) {
      out[i]   = in[i+1];
      out[i+1] = in[i];
   }
}

// Taken from smartmontools
// Copies in to out, but removes leading and trailing whitespace.
static void trim(char * out, const char * in)
{
   // Find the first non-space character (maybe none).
   int first = -1;
   int i;
   for (i = 0; in[i]; i++)
      if (!isspace((int)in[i])) {
         first = i;
         break;
      }

      if (first == -1) {
         // There are no non-space characters.
         out[0] = '\0';
         return;
      }

      // Find the last non-space character.
      for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--)
         ;
      int last = i;

      strncpy(out, in+first, last-first+1);
      out[last-first+1] = '\0';
}

// Taken from smartmontools
// Convenience function for formatting strings from ata_identify_device
void ata_format_id_string(char * out, const unsigned char * in, int n)
{
   bool must_swap = true;
#ifdef __NetBSD__
   /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */
   // TODO: Handle NetBSD case in os_netbsd.cpp
   if (isbigendian())
      must_swap = !must_swap;
#endif

   char tmp[65];
   n = n > 64 ? 64 : n;
   if (!must_swap)
      strncpy(tmp, (const char *)in, n);
   else
      swapbytes(tmp, (const char *)in, n);
   tmp[n] = '\0';
   trim(out, tmp);
}

int main(int argc, char* argv[])
{
   HANDLE handle = ::CreateFileA(
      "\\\\.\\PhysicalDrive0", 
      GENERIC_READ | GENERIC_WRITE, //IOCTL_ATA_PASS_THROUGH requires read-write
      FILE_SHARE_READ, 
      0,            //no security attributes
      OPEN_EXISTING,
      0,              //flags and attributes
      0             //no template file
      );

   if ( handle == INVALID_HANDLE_VALUE ) {
      std::cout << "Invalid handle\n";
   }

   // IDENTIFY command requires a 512 byte buffer for data:
   const unsigned int IDENTIFY_buffer_size = 512;
   const BYTE IDENTIFY_command_ID =  0xEC;
   unsigned char Buffer[IDENTIFY_buffer_size + sizeof(ATA_PASS_THROUGH_EX)] = { 0 };
   ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer;
   PTE.Length = sizeof(PTE);
   PTE.TimeOutValue = 10;
   PTE.DataTransferLength = 512;
   PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);

   // Set up the IDE registers as specified in ATA spec.
   IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile;
   ir->bCommandReg = IDENTIFY_command_ID;
   ir->bSectorCountReg = 1;

   // IDENTIFY is neither 48-bit nor DMA, it reads from the device:
   PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;

   DWORD BR = 0;
   BOOL b = ::DeviceIoControl(handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0);
   if ( b == 0 ) {
      std::cout << "Invalid call\n";
   }

   IDENTIFY_DEVICE_DATA * data = (IDENTIFY_DEVICE_DATA *) (Buffer + sizeof(ATA_PASS_THROUGH_EX));

   // Nota Bene: I think some endianness control is needed
   char model[40+1];
   ata_format_id_string(model, data->ModelNumber, sizeof(model)-1);

   char serial[20+1];
   ata_format_id_string(serial, data->SerialNumber, sizeof(serial)-1);

   char firmware[8+1];
   ata_format_id_string(firmware, data->FirmwareRevision, sizeof(firmware)-1);

   std::cout << "ModelNumber:      " << model << "\n";
   std::cout << "SerialNumber:     " << serial << "\n";
   std::cout << "FirmwareRevision: " << firmware << "\n";
   return 0;
}


回答3:

You need IOCTL_ATA_PASS_THROUGH Control Code