Доброго времени суток, возник вопрос с корректным вклинивание в очередь команд hdd. То есть, есть некий хард работающий в режиме PIO, и происходит обмен информацией между ним и ОС. Нужно в случайный момент времени выполнить мою команду, как правильно дождаться что б hdd был готов к исполнению команды? У меня есть 2 варианта, этот я взял из книги Кулакова: Код (Text): debug 15h @@WaitHDReady: ; Проверить время ожидания mov EAX,ds:[046Ch] sub EAX,ds:[OPERATION_TIMER] cmp EAX,BSY_WAIT_TIME ja @@Err1 ;ошибка тайм-аута ; Прочитать регистр состояния in AL,DX ; Проверить состояние сигнала BSY test AL,80h jnz @@WaitHDReady ; Проверить состояние сигнала DRQ test AL,08h jnz @@WaitHDReady debug 16h Этот я написал опираясь на спецификацию ATA: Код (Text): debug 15h push 500h @@WaitHDReady: ; Проверить время ожидания pop eax dec eax cmp EAX, 0 je @@Error1 ;ошибка тайм-аута push eax ; Прочитать регистр состояния in AL,DX ; Проверить состояние сигнала BSY test AL,80h jnz @@WaitHDReady ;Проверить состояние сигнала DRDY test AL,40h jz @@WaitHDReady pop eax debug 16h Какой из них более верный, или как это ожидание надо организовывать в принципе? Просто просматривая форум я не нашел однозначного ответа, нашел много споров как надо, а результат выцепить не смог(
Сегодня почитаю, а в чем код не правильный? по логике я жду установки флага готовности устройства к приему команды. А выполнение цикла 500 раз это ограничение что бы не зависало, потом вставлю что нибудь более адекватное, просто int 15h я не могу, а другие варианты пока даже не искал
Я почитал 9 главу, сложно конечно, но мои вопросы она не особо убрала. Зачада чуть чуть видо изменилась: необходимо определить момент когда хард не занят и тогда его использовать. А какое состояние флагов этому соответствует? BSY = 0 & DRQ = 0 & DRDY = 1? И как определить что диск работате в UDMA и что он свободен?
drem1lin Можно глянуть в исходники UniATA драйвера. Вот быстренько набросал: Код (Text): // Файл id_probe.cpp CheckDevice SelectDrive(chan, deviceNumber); //AtapiWritePort1(chan, IDX_IO1_o_DriveSelect, (unit) ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1); // Пишем в порт (cmd_port + HD_CURRENT) значение (deviceNumber << 4). UniataAnybodyHome // метод определения существования устройства на контроллере от автора UniATA statusByte = WaitOnBaseBusyLong(chan); // ожидаем в цикле статуса IDE_STATUS_BUSY //GetBaseStatus // Читаем из порта (cmd_port + HD_STATUS) и ожидаем бита IDE_STATUS_BUSY //AtapiStallExecution // Вызов функции ScsiPortStallExecution (ХЗ чем её можно заменить) GetBaseStatus(chan, statusByte); // Читаем из порта (cmd_port + HD_STATUS) if (SATA_Controller) UniataSataClearErr ...... // с SATA пока не разбирался ... if(((statusByte | IDE_STATUS_BUSY) == 0xff) || (statusByte & IDE_STATUS_BUSY)) { KdPrint2((PRINT_PREFIX "CheckDevice: busy => return\n")); UniataForgetDevice(LunExt); // LunExt->DeviceFlags &= DFLAGS_HIDDEN; return 0; } // Читаем из HD_LCYL и HD_HCYL и по "магическим" значениям отличаем ATAPI от IDE. // Впервые вижу такой кодес signatureLow = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow); signatureHigh = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh); if (signatureLow == ATAPI_MAGIC_LSB && signatureHigh == ATAPI_MAGIC_MSB) { KdPrint2((PRINT_PREFIX "CheckDevice: ATAPI signature found\n")); ... ... } else { KdPrint2((PRINT_PREFIX "CheckDevice: IDE device check\n")); if (IssueIdentify(hw, deviceNumber, lChannel, IDE_COMMAND_IDENTIFY, FALSE) { // Indicate IDE - not ATAPI device. RetVal = DFLAGS_DEVICE_PRESENT; } GetBaseStatus(chan, statusByte); } -------------------------- //Файл id_ata.cpp IssueIdentify SelectDrive(chan, deviceNumber); AtapiStallExecution(10); statusByte = WaitOnBusyLong(chan); // Check that the status register makes sense. GetBaseStatus(chan, statusByte2); UniataDumpATARegs(chan); // ещё не осмыслил .... if (Command == IDE_COMMAND_IDENTIFY) { statusByte = UniataIsIdle(deviceExtension, statusByte & ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX)); if(statusByte != IDE_STATUS_IDLE) { SelectDrive(chan, deviceNumber); WaitOnBusyLong(chan); // тут опять магическая проверка на ATAPI устройство // We really should wait up to 31 seconds // The ATA spec. allows device 0 to come back from BUSY in 31 seconds! // (30 seconds for device 1) do { // Wait for Busy to drop. AtapiStallExecution(100); GetStatus(chan, statusByte); // Читаем из порта (cmd_port + HD_ALTSTATUS) } while ((statusByte & IDE_STATUS_BUSY) && waitCount--); GetBaseStatus(chan, statusByte2); // Читаем из порта (cmd_port + HD_STATUS) SelectDrive(chan, DeviceNumber); } // и снова магическая проверка на ATAPI устройство statusByte = UniataIsIdle(deviceExtension, statusByte) & ~IDE_STATUS_INDEX; if (statusByte != IDE_STATUS_IDLE) { // Give up on this. KdPrint2((PRINT_PREFIX "IssueIdentify: no dev (ldev %d)\n", ldev)); return FALSE; } } // Далее в цикле вызывается AtaCommand -> AtaCommand48 и куча всего прочего // Тут много проверок на DRQ statusByte = WaitForDrq(chan); // Читаем из порта (cmd_port + HD_ALTSTATUS) и ожидаем бита IDE_STATUS_DRQ statusByte = WaitOnBusyLong(chan); if (!(statusByte & IDE_STATUS_DRQ)) { KdPrint2((PRINT_PREFIX "IssueIdentify: !IDE_STATUS_DRQ (2) (%#x)\n", statusByte)); GetBaseStatus(chan, statusByte); return FALSE; } GetBaseStatus(chan, statusByte); // Ну а далее читают первый сектор ReadBuffer(chan, (PUSHORT)&deviceExtension->FullIdentifyData, 256, PIO0_TIMING); // ReadBuffer2(chan, (PUSHORT)&deviceExtension->FullIdentifyData, 256/2, PIO0_TIMING);
Я жду 31 секунду, пока в альтернативном статусе не пропадет BSY флаг. И маленький прикол тебе. Если ОС работает с прерыванием от HDD, то тебя ждет большой сюрприз. И нужно быть уверенным, что винт не уснул. Это второй сюрприз.
ну что винт не уснул я уверен, а вот что такое прерывания и с чем их едят я пока не нашел( Спецификация здоровая штука
Обычное прерывание, точнее IRQ. Нужно быть уверенным, что ОС не ведет никаких операций с тем же винтом, но используя прерывания. А на счет сна, ты оптимистичен.
Очень сложный, и не однозначный вопрос. Но цифры ты верно уловил. Правда к винтам, это ни как не относится, это в контролерах разбираться нужно.
Взял из UniATA функции CheckDevice,IssueIdentify,AtaCommand48,UniataAnybodyHome и добавил всё это дело в свою тестовую прожку (на Delphi). Читаю просто нулевой сектор. Работает этот CheckDevice намного лучше, чем старый код (позаимствованый у Pavia). Правда всё же на VIA чипсете удалось однажды словить зависон компа (на WinXP тестирую), но это пустяк. Но в функции IssueIdentify после чтения 512 байтиков есть вызов функции WaitForDrq (ожидание бита DRQ), которая в 90% случаев завершается по таймауту, и из-за этого "сканирование" выполняется не быстро. Поэтому у себя все выходы по таймауту сделал более "быстрыми".
А как определить в каком режиме работает хард? Просто мой код должен либо дождаться когда хард свободен, либо забить другие обращения к харду и выполнить мои. Это на данный момент основной вопрос.
Все зависит от операционной системы. Или вы должны гарантировать, что операционная система не будет общаться с устройством, или должны пользоваться только средствами ОС.
Понятно, гарантии такие есть, если хард не используется ОС, то и не начнет, тут вопрос в том как это узнать, как узнать что хард не используется. Какие биты он ставит или какие еще признаки есть?