Sending ATA commands directly to device in Windows? Sending ATA commands directly to device in Windows? windows windows

Sending ATA commands directly to device in Windows?


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


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