Отправлять команды ATA напрямую на устройство в Windows?

17

Я пытаюсь отправить команды ATA на физический диск в Windows и получить ответ от устройства.

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

Я знаю, что мне нужно использовать 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
);

Но это совершенно неправильно. Код IoControlCode, отправленный на DeviceIoControl , должен быть действительным 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 и прочитать его ответ?

Смотрите также

Ответы на вопросы

Откройте диск с доступом ReadWrite:

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 в качестве входного буфера для использования с управляющим кодом IOCTL_ATA_PASS_THROUGH IO:

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();
    
задан Ian Boyd 21.02.2011 в 21:36
источник
  • Я не могу поверить, что потратил 8 часов, моего выходного дня, на это. я просто хотел позвонить в ЕС и посмотреть на результаты. –  Ian Boyd 21.02.2011 в 22:37
  • что такое «\\. \ physicaldrive0». Предполагается, что это файл вашего жесткого диска или что-то еще? Я также пытаюсь выдать команды ATA, и я попробовал ваш код, но у меня нет ничего, что возвращалось бы. stackoverflow.com/questions/12901284/... –  Nick 16.10.2012 в 01:50
  • @Nick Это жесткий диск или что-то в этом роде. См. «Именование файлов, пути и пространства имен», которые связаны с CreateFile –  Ian Boyd 16.10.2012 в 04:50
  • , когда вы упомянули // todo .. поместите команду ATA EC где-нибудь? Что ты сделал? –  Nick 17.10.2012 в 21:50
  • @ Ник мой ответ stackoverflow.com/a/23939263/15485 –  Alessandro Jacopson 29.05.2014 в 19:27

3 ответа

11

Вам нужно использовать IOCTL_ATA_PASS_THROUGH / IOCTL_ATA_PASS_THROUGH_DIRECT, это довольно хорошо задокументировано. Кроме того, вам нужен доступ GENERIC_READ | GENERIC_WRITE для CreateFile.

Имейте в виду, что pre XP SP2 не поддерживает их должным образом. Кроме того, если у вас есть основанный на nForce MB с драйверами nvidia, ваши диски SATA будут отображаться как SCSI, и вы не сможете использовать этот проход.

В некоторых случаях SMART IOCTL (например, SMART_RCV_DRIVE_DATA) будет работать с драйверами nForce. Вы можете использовать их для получения ИДЕНТИФИКАЦИИ и 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);

Настройте регистры IDE, как указано в спецификации ATA.

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 на наличие сообщений об ошибках, обнаруженных устройством.

Получите данные IDENTIFY, предполагая, что вы определили структуру IdentifyData

IdentifyData * IDData = (IdentifyData *) (Buffer + sizeof(ATA_PASS_THROUGH_EX));
    
ответ дан Erik 21.02.2011 в 21:41
  • Документы говорят: «Если команде вызывающего абонента может потребоваться прямой доступ к памяти, вместо этого используйте IOCTL_ATA_PASS_THROUGH_DIRECT». Как я могу узнать, может ли моя команда требовать прямого доступа к памяти? –  Ian Boyd 21.02.2011 в 21:45
  • IDENTIFY не будет. Я использую их взаимозаменяемо, но версия DIRECT будет быстрее для команд передачи данных (READ / WRITE) –  Erik 21.02.2011 в 21:47
  • Если вам нужны только данные IDENTIFY / SMART, перейдите к SMART ioctl, его проще использовать и работать с большим количеством драйверов. Если вы планируете выйти за пределы IDENTIFY / SMART, пойдите для ATA ioctls и живите с раздражением nForce. –  Erik 21.02.2011 в 21:49
  • Кроме того, и не придираться, но я не вижу никакого места в структуре ATA_PASS_THROUGH_EX, чтобы поместить фактический код команды. –  Ian Boyd 21.02.2011 в 21:49
  • Что такое ioctl SMART ioctl? Кроме того, я бы не подумал, что флэш-устройства ATA будут поддерживать SMART, поскольку это не имеет никакого смысла. –  Ian Boyd 21.02.2011 в 21:51
Показать остальные комментарии
5

На основании ответа Ссылка Эрик Я написал следующий автономный код. Я протестировал его на ноутбуке DELL с SSD-диском и под управлением 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] = '%pr_e%';
         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] = '%pr_e%';
}

// 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] = '%pr_e%';
   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;
}
    
ответ дан Alessandro Jacopson 29.05.2014 в 19:25
  • , пожалуйста, кто может объяснить мне, почему этот код работает на некоторых машинах Win7, а на других - с кодом ошибки 1? Большое спасибо! –  leonp 09.08.2015 в 11:08
  • @leonp: Вы запускаете этот exe из учетной записи администратора / повышенного контекста? –  lolando 12.10.2015 в 11:52
  • @leonp Главное не возвращает 1. Что вы видели в stdout? –  Alessandro Jacopson 12.10.2015 в 13:21
1
ответ дан Simon Mourier 21.02.2011 в 21:44
  • Что бы я передал в качестве входного буфера (или где во входном буфере я установил 0xEC?) –  Ian Boyd 21.02.2011 в 21:55