Может кто-либо поделится информацией по поводу реализации взаимодействия с IDE / SATA жесткими дисками? А имеено: как получить список подключенных устройств, как прочитать данные с диска и т.д.
// не будем плодить темы ... продолжим мысль ... Заинтересовала недавно меня работа с устройствами через порты в/в. Сканирование устройств на шине PCI освоил. Освоил работу с SMBus (правда там конкретика по железу). Решил и работу с IDE (SATA) устройствами освоить. Полазил по wasm'у и вышел на статейку http://ru.osdev.wikia.com/wiki/Работа_с_жесткими_дисками_и_их_контроллерами Там же есть исходник на асме http://ru.osdev.wikia.com/wiki/Исходный_код:Поиск_HDD_и_CD Ну решил попробовать данный код в деле. Писал код дома (там стоит комп с мамкой SIS 6xx) и всё работало (за искл. одного нюанса, см. ниже). Попробовал данный код на чипе VIA VT8235 и понял что код не совсем и универсальный. Пришёл к такому выводу, т.к. команда EXECUTE DEVICE DIAGNOSTIC не работает на южнике VT8235, на котором висит сигейтовский хард (PATA). Вот сам код процедуры идентификации устройства dev на канале С: Код (Text): // buf = 512 bytes; Result = IDE_SIGNATURE_XXXXXX function IDETestDevice(ide: PIDEBusInfo; dev: Byte; buf: PChar; ScanAsDISK: Boolean): DWORD; type TIDEAddr = packed record case Byte of 1: (Arr: array [0..3] of Byte); 2: (DW: DWORD); end; var i, a: DWORD; b, e, cmd, dv, de: Byte; pcmd, pcnt, w: Word; ia: TIDEAddr; res: DWORD; bufw: array [0..255] of Word; ict: TIDEControllerType; begin Result := 0; res := 0; pcmd := ide^.PortCMD; // порт команд if pcmd = 0 then Exit; pcnt := ide^.PortCNT; // а он и не нужет тут ict := ide^.Ctrl; // контроллер IDE, RAID, ACHI if ict = ictEmpty then Exit; if dev > 1 then Exit; // Master or Slave de := dev shl 4; dv := $A0 + de; // CHS адрессация if ScanAsDISK then res := IDE_SIGNATURE_DISK; // принудительно выставляем тип девайса 00000101h if not ScanAsDISK then begin // "точное" определение через WIN_DIAGNOSE, либо WIN_DEVICE_RESET PortOutB(Driver, pcmd + HD_CURRENT, dv); // +6 PortOutB(Driver, pcmd + HD_NSECTOR, dv); // +2 PortOutB(Driver, pcmd + HD_SECTOR, dv); // +3 PortOutB(Driver, pcmd + HD_LCYL, dv); // +4 PortOutB(Driver, pcmd + HD_HCYL, dv); // +5 PortOutB(Driver, pcmd + HD_COMMAND, WIN_DIAGNOSE); // +7 // EXECUTE DEVICE DIAGNOSTIC for i:=$FFFF downto 0 do begin NEWIODELAY; // PortOutB(Driver, $00EB, 0); NEWIODELAY; NEWIODELAY; e := PortInB(Driver, pcmd + HD_STATUS); // +7 if (e and IDE_BUSY_STAT = 0) then begin b := PortInB(Driver, pcmd + HD_ERROR); // +1 {log} Form1.Memo1.Lines.Add('ST = '+IntToHex(e, 2)+' ER = '+IntToHex(b, 2)); if ((dev = 0) and (b = 1)) or ((dev = 1) and ((b = 1) or (b = $80))) then begin ia.Arr[3] := PortInB(Driver, pcmd + HD_HCYL); // +5 ia.Arr[2] := PortInB(Driver, pcmd + HD_LCYL); // +4 ia.Arr[1] := PortInB(Driver, pcmd + HD_SECTOR); // +3 ia.Arr[0] := PortInB(Driver, pcmd + HD_NSECTOR); // +2 {log} Form1.Memo1.Lines.Add('DevID = '+IntToHex(ia.DW, 8)); if ia.DW = IDE_SIGNATURE_DISK then res := IDE_SIGNATURE_DISK; // $00000101 if ia.DW = IDE_SIGNATURE_ATAPI then res := IDE_SIGNATURE_ATAPI; // $EB140101 end; Break; end; end; NEWIODELAY; if res = 0 then begin // тут мы удостоверимся в отстствии/присутствии ATAPI устройства PortOutB(Driver, pcmd + HD_CURRENT, dv); // +6 PortOutB(Driver, pcmd + HD_HCYL, 0); PortOutB(Driver, pcmd + HD_LCYL, 0); PortOutB(Driver, pcmd + HD_SECTOR, 0); PortOutB(Driver, pcmd + HD_NSECTOR, 0); PortOutB(Driver, pcmd + HD_COMMAND, WIN_DEVICE_RESET); // +7 // DEVICE RESET = $08 for i:=$FFFF downto 0 do begin NEWIODELAY; b := PortInB(Driver, pcmd + HD_STATUS); // +7 if (b and IDE_BUSY_STAT) = 0 then Break; if i = 0 then Exit; // Timeout end; b := PortInB(Driver, pcmd + HD_ERROR); // +1 if ((dev = 0) and (b <> 1)) or ((dev = 1) and (b <> 1) and (b <> $80)) then begin PortOutB(Driver, pcmd + HD_CURRENT, dv); // +6 Exit; end; ia.Arr[3] := PortInB(Driver, pcmd + HD_HCYL); // +5 ia.Arr[2] := PortInB(Driver, pcmd + HD_LCYL); // +4 ia.Arr[1] := PortInB(Driver, pcmd + HD_SECTOR); // +3 ia.Arr[0] := PortInB(Driver, pcmd + HD_NSECTOR); // +2 if ia.DW = IDE_SIGNATURE_ATAPI then res := IDE_SIGNATURE_ATAPI; end; end; // end of DIAG device PortOutB(Driver, pcmd + HD_CURRENT, dv); // +6 if res = 0 then Exit; // сваливаем коли не поняли с кем пытаемся работать if (res = IDE_SIGNATURE_DISK) or (res = IDE_SIGNATURE_ATAPI) then begin if res = IDE_SIGNATURE_DISK then cmd := WIN_IDENTIFY else // $EC if res = IDE_SIGNATURE_ATAPI then cmd := WIN_PIDENTIFY; // $A1 PortOutB(Driver, pcmd + HD_COMMAND, cmd); // +7 for i:=$FFFF downto 0 do begin NEWIODELAY; b := PortInB(Driver, pcmd + HD_STATUS); // +7 if (b and IDE_BUSY_STAT = 0) and (b and IDE_DRQ_STAT <> 0) then Break; if ScanAsDISK and (b = $50) then Exit; // явно это не HDD // IDE_SEEK_STAT + IDE_READY_STAT if i = 0 then begin {log} Form1.Memo1.Lines.Add('IDENT ST = '+IntToHex(b, 2)); Exit; // Timeout end; end; for i:=0 to 255 do begin // читаем IDENTIFY Info (нулевой сектор) w := PortInW2(Driver, pcmd + HD_DATA); // +0 bufw[i] := w; if buf <> nil then PWord(@buf[i*2])^ := w; end; for i:=0 to 254 do begin if bufw[i] <> bufw[i+1] then Break; if i = 254 then Exit; // в том случае, когда все слова в буфере одинаковые end; end; Result := res; end; Как можно догадаться, работа с портами осуществляется из ring3 через драйвер. Вот результат работы на 3 разным компах: 1) SIS 6xx, только IDE контроллер, HDD seagate (Pri Master), CDROM NEC (Sec Slave) Команда WIN_DIAGNOSE нормально отрабатывает. Правда в 1 случае из 10 на Primary канале обнаруживается Slave HDD, которого на самом деле нет (даже WIN_IDENTIFY выполняется, правда мусор возвращается). 2) VIA KT400A (VT8235), только IDE контроллер, HDD seagate (Pri Slave), CDROM NEC (Sec Slave) Для Primary канала (для обоих устройств): После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 10h (ST = 10h). Для Secondary канала (для обоих устройств): После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 7Fh (ST = 7Fh). CDROM находится только через DEVICE RESET. Если аргумент ScanAsDISK = True (алго несколько упрощённый становится), то после вызова WIN_IDENTIFY получаю данные с HDD. 3) nForce 570 (MCP55P), IDE + SATA, три HDD висят на SATA контроллерах, CDROM NEC (IDE Pri Slave) Для SATA контроллеров (эмуляция IDE): Команда WIN_DIAGNOSE нормально отрабатывает для трёх HDD. И WIN_IDENTIFY корректно возвращает информацию. Для Primary канала IDE: Master: После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 7Fh (ST = 7Fh). Slave : После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0. Читаем ER=01h. Но идентификатор устройства получаем 7F7F7F7F (за место EB140101). Для Secondary канала IDE (для обоих устройств): Комады WIN_DIAGNOSE отрабатывает корректно (а Secondary то наверное и вовсе нету). Поэтому возникли вопросы: 1) Может все проблеммы из-за того что пытаюсь работать с портами из ring3 , без синхронизации работы с драйверами Windows (XP SP2) ? И какие могут быть проблеммы при "такой" работе с портами? 2) Почему такое "разное поведение" команды EXECUTE DEVICE DIAGNOSTIC ? 3) Можно ли вызывать WIN_IDENTIFY для абсолютно любого устройства, обрабатывая после ошибки? И чем череват её вызов для ATAPI устройств?
Можно. Сошлюсь на коллег с osdev.org они только так и делают. Не вызывают EXECUTE DEVICE DIAGNOSTIC, а вызывают сразу IDENTIFY DEVICE и IDENTIFY PACKET DEVICE. ATAPI устройства в ответ на IDENTIFY DEVICE выставит ошибку. Проблема будет если у тебя на одном шлейфе и жесткий и сидиром. Так как команды IDENTIFY DEVICE и IDENTIFY PACKET DEVICE по особому реагируют на бит мастер\слейв. То будет такое жесткий вернет данные, а сидюк выставит в тоже время вернет код ошибки. 1) Этого можно не бояться. Периодически ОС читает на диски пишет. Поэтому срабатывают прерывания жестких дисков чревато тем что твоя команда выполниться с ошибкой. ОС это не затрагивает пока ты в прерывания не вмешиваешься. В большинстве случаев ошибок не будет. 2) Это риторический вопрос на него нет ответа. Есть около 8 версий спецификации. Плюс диски работают по разному. Во общем. У меня тоже были подобные результаты. Так что можно еще код линукса по изучать его интеловци писали, а они вроде качественно пишут. Хотя у них тоже видать не все касяки решины.
Продолжаем тему... Интересует следующий вопрос. Каким образом можно получше организовать работу с жестким диском в услових одновременной работы с ОС. Написал код, который может находить устройства, читать с них и т.д. Но есть проблема. Если во время работы моей программы жесткий диск сильно нагружен, то чтение может и удастся, так как устройство будет занято... Точно так же иногда идентификация завершается с ошибкой, так как диск не отозвался в течении некоторого времени. К тому же, команды чтения иногда дают неверный результат (примерно 50 раз на 1000000 попыток чтения). Это связано с тем, что моя программа и ОС одновременно обращаются к диску (я записал номер сектора, ОС записала свой, я прочитал чужие данные). Данная проблема была решена поднятием IRQL на все процессорах на высокий уровень. Однако, проблема с чтением все же осталась. Если устройство занято, то чтение завершается с ошибкой по таймауту. Эту проблему можно решить, повторив попытку чтения еще раз. Может кто-нибудь подскажет, как получше все это организовать? Например, стоит ли повышать IRQL на время всей операции или только до того, как получим, что диск будет готов к обмену даными (по идее в этом случае его уже никто не сможет занять, пока он не освободится). Да, кстати, ОС - Windows.
Интересный вопрос. Тож когда-то пытался читать параллельно WinXP через DMA. Время от времени в EventLog появлялись неприятные восклицательные знаки от atapi. Что-то типа "ошибка утройства". При этом канал переключался в PIO с невозможностью вернуть DMA из ГУЙ диспетчера устройств. Сделать надежную параллельную с atapi работу при сильной загрузке диска так и не смог.