web-dev-qa-db-ja.com

WindowsのデバイスにATAコマンドを直接送信しますか?

Windowsの物理ディスクにATAコマンドを送信し、デバイスから応答を取得しようとしています。

注:この場合、 IDENTIFY DEVICE (0xEC)コマンドを送信します。デバイスは512バイトのデータブロックで応答します。 (特に、Word 119のビット0-デバイスの TRIMコマンドのサポート に興味があります)。

デバイスを開くには CreateFile を使用する必要があることを知っています。

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

しかし、この後、私は何をすべきかについて悩まされています。

0xECを使用して[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
);

しかし、これは完全に間違っています。 DeviceIoControl に送信されるIoControlCodeは、有効なIO_CTLである必要があります。これは マクロを使用して構築されます

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

SDKを見ると、有効な ディスク管理制御コード がいくつかあります。例:

  • IOCTL_DISK_CREATE_DISK
  • IOCTL_DISK_GET_DRIVE_GEOMETRY
  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  • IOCTL_DISK_GET_PARTITION_INFO
  • IOCTL_STORAGE_QUERY_PROPERTY

しかし、それらのどれもIDENTIFY DEVICEコマンドではなく、それが返すものは何でも返しません。

したがって、コマンドを送信するには「生の」方法を使用する必要があると思います。


探し回って、私は出くわし、文書化されていないIOCTL

#define  DFP_RECEIVE_DRIVE_DATA   0x0007c088   

これは、IOCTLの断片を分解すると、次のことを意味します。

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

しかし、inputBufferに何を含める必要があるか、そのサイズ、およびそのoutputBufferに何を含めるか、またはその必要性についてのドキュメントはどこにもありません。また、functionCode 34(0x22)が何であるかを理解することもできません。


私の質問:生のATAコマンド(0xECなど)をATAデバイスに送信し、その応答を読み取るにはどうすればよいですか?

も参照してください


回答ピース

読み取り/書き込みアクセスでドライブを開きます。

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
);

ATA_PASS_THROUGH_EX IO制御コード:)で使用する入力バッファーとしてIOCTL_ATA_PASS_THROUGH構造体をセットアップします。

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);

ドライブからの予想される512バイトの応答を保持するように出力バッファを設定します。

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

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    
);

ファイルハンドルを閉じます。

handle.Close();
18
Ian Boyd

IOCTL_ATA_PASS_THROUGH/IOCTL_ATA_PASS_THROUGH_DIRECTを使用する必要があります。これらは、十分に文書化されています。また、CreateFileにはGENERIC_READ | GENERIC_WRITEアクセスが必要です。

Pre XP SP2はこれらを適切にサポートしていないことに注意してください。また、nvidiaドライバーを備えたnForceベースのMBがある場合、SATAドライブはSCSIとして表示され、このパススルーを使用できません。

場合によっては、SMART IOCTL(SMART_RCV_DRIVE_DATAなど)はnForceドライバーで機能します。これらを使用してIDENTIFYおよびSMARTデータを取得できますが、それ以外はあまり使用できません。 。

オープンソースのsmartmontoolsは、サンプルコードを探し始めるのに適した場所です。

編集:ATAデバイスと通信するアプリからのサンプル。

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;
    }

編集:外部依存関係のないサンプル。

IDENTIFYには、データ用に512バイトのバッファーが必要です。

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);

ATA仕様で指定されているようにIDEレジスタを設定します。

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

IDENTIFYは48ビットでもDMAでもないため、デバイスから読み取ります。

PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;

Ioctlを実行します。

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

ここでは、DeviceIOControlから、およびIDEREGSでデバイスから報告されたエラーを確認することにより、エラーチェックを挿入する必要があります。

構造体IdentifyDataを定義したと仮定して、IDENTIFYデータを取得します

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

答えに基づいて https://stackoverflow.com/a/5071027/15485 by Erik 私は次の自己完結型のコードを書きました。 SSDディスクを搭載しWindows7を実行しているDellラップトップでテストしました。

// 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;
}
5
1
Simon Mourier