Я пытаюсь отправить команды ATA на физический диск в Windows и получить ответ от устройства.
Примечание: В этом случае я хочу отправить
IDENTIFY DEVICE
(0xEC) команда. Устройство ответит блок данных из 512 байт. (В особенно меня интересует бит 0 из слово 119 - устройства поддерживает команда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
);
Но это совершенно неправильно. IoControlCode, отправленный в DeviceIoControl, должен быть допустимым IO_CTL, которые построены с использованием макроса:
#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)
Глядя на SDK, есть несколько допустимых Коды управления дисками, например:
Но ни один из них не является Команда 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
:
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();