在 Windows 中将 ATA 命令直接发送到设备?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5070987/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sending ATA commands directly to device in Windows?
提问by Ian Boyd
I'm trying to send ATA commands to a physical disk in Windows, and get the response from the device.
我正在尝试将 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 theTRIM
command).
注意:在这种情况下,我想发送
IDENTIFY DEVICE
(0xEC) 命令。设备将以 512 字节的数据块进行响应。(特别是我对字 119 的位 0 感兴趣 - 设备对TRIM
命令的支持)。
I know that I need to use CreateFile
to open the device:
我知道我需要使用CreateFile
来打开设备:
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]
:
我想过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
);
But this is completely wrong. An IoControlCode sent to DeviceIoControlmust be a valid IO_CTL, which are built using the macro:
但这是完全错误的。发送到DeviceIoControl的 IoControlCode必须是有效的 IO_CTL,它是使用宏构建的:
#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.:
查看 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
- 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.
但是它们都不是IDENTIFY DEVICE
命令,也不是返回它返回的任何内容。
So I believe I have to use some “raw” method of sending commands.
所以我相信我必须使用一些“原始”的发送命令的方法。
Searching around, I came across and undocumented IOCTL
四处搜索,我遇到了未记录的 IOCTL
#define DFP_RECEIVE_DRIVE_DATA 0x0007c088
Which when you break down the IOCTL pieces, means:
当您分解 IOCTL 部分时,这意味着:
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.
但是在任何地方都没有关于inputBuffer
必须包含什么、它的大小、outputBuffer
它将包含什么或它的要求的文档。我也无法弄清楚functionCode
34 (0x22) 是什么。
My question: How do I send raw ATA commands (e.g. 0xEC) to an ATA device, and read its response?
我的问题:如何将原始 ATA 命令(例如 0xEC)发送到 ATA 设备,并读取其响应?
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
结构作为我们的输入缓冲区以与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);
Setup an output buffer to hold our expected 512-byte response from the drive:
设置一个输出缓冲区来保存我们预期的来自驱动器的 512 字节响应:
Byte[] outputBuffer = new Byte[512];
uint outputBufferSize = 512;
Call DeviceIoControl
:
电话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();
采纳答案by Erik
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.
您需要使用 IOCTL_ATA_PASS_THROUGH/IOCTL_ATA_PASS_THROUGH_DIRECT,这些都有很好的文档记录。此外,您需要 GENERIC_READ|GENERIC_WRITE 访问 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.
请注意,pre XP SP2 不正确支持这些。此外,如果您有一个带有 nvidia 驱动程序的基于 nForce 的 MB,您的 SATA 驱动器将显示为 SCSI,并且您不能使用此直通。
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.
在某些情况下,SMART IOCTL(例如 SMART_RCV_DRIVE_DATA)将适用于 nForce 驱动程序。您可以使用这些来获取 IDENTIFY 和 SMART 数据,但仅此而已。
The open source smartmontools is a good place to start looking for sample code.
开源 smartmontools 是开始寻找示例代码的好地方。
EDIT: Sample from an app talking to ATA devices.
编辑:来自与 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;
}
EDIT: Sample without external dependencies.
编辑:没有外部依赖的示例。
IDENTIFY requires a 512 byte buffer for data:
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);
Set up the IDE registers as specified in ATA spec.
按照 ATA 规范中的规定设置 IDE 寄存器。
IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile;
ir->bCommandReg = 0xEC;
ir->bSectorCountReg = 1;
IDENTIFY is neither 48-bit nor DMA, it reads from the device:
IDENTIFY 既不是 48 位也不是 DMA,它从设备读取:
PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;
Do the ioctl:
执行 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.
您应该在这里插入错误检查,从 DeviceIOControl 和查看 IDEREGS 以获取设备报告的错误。
Get the IDENTIFY data, assuming you have defined a struct IdentifyData
获取 IDENTIFY 数据,假设您已经定义了一个 struct IdentificationData
IdentifyData * IDData = (IdentifyData *) (Buffer + sizeof(ATA_PASS_THROUGH_EX));
回答by Alessandro Jacopson
Based on the answer https://stackoverflow.com/a/5071027/15485by ErikI wrote the following self-contained code. I tested it on a DELL laptop with an SSD disk and running Windows 7.
根据Erik的回答https://stackoverflow.com/a/5071027/15485,我编写了以下自包含代码。我在带有 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] = '##代码##';
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] = '##代码##';
}
// 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] = '##代码##';
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;
}