io programming pl 2 TUGB2VUMWRW76P3DCRZDXEVQB5TIS32OZ7ARFJA


Linux I/O port programming mini-HOWTO: Używanie rejestrów wejścia/wyjścia w programach napisanych w C Następna strona Poprzednia strona Spis treści 2. Używanie rejestrów wejścia/wyjścia w programach napisanych w C 2.1 Metoda zwykła Procedury dostępu do portów (rejestrów) we/wy umieszczone są w /usr/include/asm/io.h (lub w linux/include/asm-i386/io.h w źródłach jądra) Procedury te występują w formie makr do wstawienia a więc wystarczy że zrobisz #include <asm/io.h>; nie potrzebujesz już żadnych dodatkowych bibliotek. Z powodu ograniczeń w gcc (obecna wersja 2.7.2.3 i poniżej) oraz egcs (wszystkie wersje) programy wykorzystujące te procedury muszą być kompilowane z włączoną optymalizacją (gcc -O1 lub więcej) lub możesz też zrobić #define extern bez parametru (puste) zanim włączysz <asm/io.h>. Do odpluskwiania (debugging) możesz użyć gcc -g -O (przynajmniej w nowoczesnych wersjach gcc), chociaż optymalizacja może spowodować, iż debugger będzie zachowywał się trochę dziwnie. Jeśli to sprawia Ci kłopot, procedury korzystające z rejestrów we/wy wstaw do osobnego pliku i tylko ten plik poddaj optymalizacji podczas kompilacji. Zanim dostaniesz się do jakiegokolwiek portu we/wy, musisz nadać swemu programowi uprawnienia do tego. Robi się to wywołując funkcję ioperm() (zdeklarowaną w unistd.h i zdefiniowaną w jądrze) gdzieś w okolicach początku programu (przed jakimkolwiek dostępem do portów) Składnia tego polecenia to ioperm(skąd,ile,włacz) gdzie skąd jest pierwszym portem do którego chcesz mieć dostęp a ile liczbą kolejnych portów do których chcesz mieć dostęp. Dla przykładu ioperm(0x300, 5, 1) da ci dostęp do portów od 0x300 do 0x304 (w sumie pięc portów). Ostatni argument to wartośc logiczna mówiąca o tym czy program otrzyma dostęp do portów (1 - prawda) bądź nie (0 - fałsz). Możesz wywoływać ioperm() wiele razy aby włączyć wiele nieciągłych obszarów rejestrów. Zajrzyj do ioperm(2) w podręczniku systemowym man po szczegóły odnośnie składni. Wywołanie ioperm() wymaga aby twój program miał uprawnienia root'a: wobec czego albo musisz uruchamiać go jako root albo dać mu suid root'a. Po wywołaniu ioperm() możesz już zrezygnować z uprawnień root'a. Nie wymaga się abyś w sposób jawny wyłączał uprzywilejowany dostęp do portów (czyli ioperm(....,0)) na końcu programu; robi się to automatycznie wraz z zakończeniem procesu. Funkcja setuid() w wypadku użytkownika bez przywilejów root'a nie odbiera dostępu do portów przydzielonego przez ioperm() ale funkcja fork() już tak. (proces potomny nie dostaje dostępu ale proces-rodzic go zachowuje) ioperm() może jedynie umożliwić dostęp do portów od 0x000 do 0x3FF. Jeśli chcesz używać innych portów (wyższych) musisz użyć funkcji iopl() (która daje ci dostęp do wszystkich portów od razu) Użyj argumentu 3 (czyli iopl(3)) aby dać swemu programowi dostęp do wszystkich portów (bądź ostrożny - dostęp do niewłaściwych portów może wywołać najróżniejsze usterki komputera). Musisz mieć uprawnienia root'a aby wywołać iopl(). Po szczególy zajrzyj do podręcznika systemowego man: iopl(2) Teraz właściwy dostęp do portów.. Aby odczytać bajt (8 bitów) z portu, wywołaj funkcję inb(port), zwraca ona odczytaną wartość. Aby zapisać bajt do portu wywołaj outb(wartość,port) (zwróć uwagę na kolejność argumentów) Aby odczytać słowo (16 bitów) z portów x i x+1 (jeden bajt z każdego łączone w słowo za pomocą asemblerowej instrukcji inw) wywołaj inw(x) Aby zapisać słowo do tych dwóch portów użyj outw(wartość,x) Jeśli nie jesteś pewien której instrukcji użyć (operującej bajtem czy słowem) prawdopodobnie będziesz chciał użyć inb() i outb() - większość urządzeń zaprojektowana jest z bajtowo-zorientowanym dostępem do portów. Zauważ, że wykonanie każdej instrukcji operującej na portach zajmuje co najmniej mikrosekundę. Makra inb_p(),outb_p(),inw_p() i outw_p() działają identycznie z tymi powyżej ale dodatkowo wykonują opóźnienie (około 1 mikrosekundy) po dostępie do portu. Możesz spowodować że opóźnienie będzie wartości ok 4 mikrosekund jeśli zrobisz #define REALLY_SLOW_IO zanim włączysz <asm/io.h> Makra te zwykle (chyba że zrobisz #define SLOW_IO_BY_JUMPING, które jest raczej mniej dokładne) używają zapisu do portu 0x80 zby uzyskać opóźnienie, wobec czego musisz najpierw dać dostęp do portu 0x80 za pomocą ioperm(). Zapisy do portu 0x80 nie powinny mieć wpływu na żadną częsć systemu. Jeśli chcesz poznać bardziej wszechstronne metody dostępu do portów czytaj dalej. W stosunkowo nowych dystrybucjach podręcznika systemowego man znajdują się strony ioperm(2), iopl(2) oraz powyższych makr. 2.2 Metoda alternatywna: /dev/port Innym sposobem dostępu do rejestrów I/O jest otworzenie urządzenia /dev/port do zapisu lub/i odczytu za pomocą funkcji open() (/dev/port to urządzenie znakowe, numer główny 1, poboczny 4) (Funkcje f*() z biblioteki stdio mają wewnętrzne buforowanie więc ich unikaj) Następnie wywołaj lseek() do odpowiedniego bajtu w tym pliku (pozycja 0 = port 0x00 pozycja 1 = port 0x01 itd) i czytaj (read()) lub zapisuj (write()) bajt lub słowo. Oczywiście aby to zadziałało twój program musi mieć możliwośc czytania i zapisywania do /dev/port. Metoda ta jest prawdopodobnie wolniejsza od metody normalnej pokazanej powyżej ale nie potrzebuje ani optymalizacji programu ani wywoływania ioperm(). Nie potrzebuje też uprawnień root'a jeśli nadasz zwykłym użytkownikom lub jakiejś grupie prawa dostępu do /dev/port - jest to jednak nieporządane z punktu widzenia bezpieczeństwa systemu, jako że możliwe jest wtedy uszkodzenie systemu, a może nawet zdobycie przywilejów root'a przez bezpośredni dostęp za pomocą /dev/port do dysków, kart sieciowych itp. Następna strona Poprzednia strona Spis treści

Wyszukiwarka