do spisu tresci tematu 7

7.1.3 Porty wejscia-wyjscia (IO ports)


Spis tresci


Wprowadzenie

Porty wejscia-wyjscia stanowia interfejs miedzy programem obslugi a fizycznym urzadzeniem. Sa to adresy z przestrzeni adresowej jadra, odpowiadajace specjalnym rejestrom sluzacym do sterowania praca urzadzen. Numery portow (od 0 do 65536) odpowiadajacych poszczegolnym urzadzeniom zaleza od konfiguracji sprzetu, tzn. nie mozna ich ustawic programowo. Sterownik urzadzenia musi podczas swej inicjalizacji "znalezc" (probe) porty odpowiadajace fizycznemu urzadzeniu ktore ma obslugiwac. W tym celu program obslugi przechodzi adresy portow, pod ktorymi spodziewa sie znalezc urzadzenie, zapisujac do nich kody polecen dla urzadzenia i oczekujac przez pewien czas na sensowna odpowiedz oznaczajaca, ze zostalo ono odnalezione.


Rezerwowanie portow wejscia-wyjscia

Oczywiscie wpisywanie wartosci kontrolnych do portow zajetych juz przez inne urzadzenie spowodowaloby prawdopodobnie jego bledne dzialanie, istnieja wiec struktury danych pozwalajace na rezerwowanie obszarow portow wejscia-wyjscia. Rozpoczynajacy prace sterownik moze bezpiecznie poszukiwac swojego urzadzenia tylko w obszarach, ktore nie zostaly jeszcze zarezerwowane.

Zarezerwowany obszar (region) portow wejscia-wyjscia opisuje struktura resource_entry_t zdefiniowana w pliku kernel/resource.c

struct resource_entry_t {
        u_long from,num;
        const char *name;
        struct resource_entry_t *next;
}
from
adres pierwszego zarezerwowanego portu
num
dlugosc zarezerwowanego obszaru
name
nazwa urzadzenia ktore rezerwuje porty

Struktury resource_entry_t zawarte sa w tablicy iotable o rozmiarze ograniczonym do IOTABLE_SIZE czyli 64 elementow, i polaczone w uporzadkowana liste iolist.

Funkcje dzialajace na liscie iolist (zdefiniowane w kernel/resource.c):

int check_region(unsigned int from, unsigned int num )
sprawdza czy obszar [from, from+num-1] jest zarezerwowany, jesli tak to program obslugi innego urzadzenia nie powinien nic zapisywac do portow w tych numerach.
void request_region( unsigned int from, unsigned int num, const char *name)
rezerwuje obszar portow (jesli nie pokrywa sie z uprzednio zarezerwowanymi), dodajac go do listy.
void release_region( unsigned int from, unsigned int num)
usuwa obszar z listy, powinna byc wywolana jesli program obslugi konczy prace, w przeciwnym przypadku portow nie bedzie mozna uzyc ponownie.

Liste zarezerwowanych obszarow portow wraz z nazwami urzadzen mozna obejrzec czytajac plik /proc/ioports.

Zarejestrowanie obszaru portow nie jest konieczne aby moc z nich korzystac. Przy wpisywaniu i odczytywaniu wartosci z portow, system nie sprawdza w zaden sposob, czy porty te sa zarezerwowane i przez jakie urzadzenie, i nie generuje wyjatku, ale oczywiscie wpisanie nieodpowiedniej wartosci do portu moze spowodowac blad w pracy korzystajacego z portu urzadzenia.

Istnieje niebezpieczenstwo, ze program obslugi, szczegolnie inicjalizowany podczas startu systemu, kiedy jest jeszcze duzo niezarejestrowanych urzadzen, pomyli jedno z nich z urzadzeniem ktorego szuka, dlatego kazdy sterownik powinien dawac uzytkownikowi mozliwosc okreslenia ktorych portow uzywa urzadzenie i w ten sposob unikniecia szukania


Korzystanie z portow wejscia-wyjscia

Do zapisywania i odczytywania wartosci do (i z) portow sluza funkcje inline zdefiniowane w pliku include/asm/io.h

outb,outw,outl
zapisuja do portu - jako parametr przyjmuja numer portu i wartosc odpowiednio: typu char, typu short i typu int
inb,inw,inl
czytaja z portu - jako parametr przyjmuja numer portu, zwracaja wartosc typu char, short lub int
outsb,outsw,outsl
zapisuja ciag wartosci z pod wskazanego adresu do portu, parametrami sa: numer portu, adres w pamieci, ilosc slow (char,short,int)
insb,insw,insl
analogiczne wersje procedur sluzacych do czytania z portow
outb_p,outw_p,outl_p,inb_p,inw_p,inl_p
funkcje z przyrostkiem _p robia "pauze" po zapisaniu lub odczytaniu wartosci do (lub z) portu a przed powrotem z funkcji, aby umozliwic pobranie tych wartosci sprzetowi, ktory zwykle nie nadaza za procesorem.

Dostep do wszystkich 65536 portow mozna rowniez uzyskac za posrednictwem specjalnego pliku znakowego /dev/port - wykonujac na nim operacje lseek, read i write. Jest to oczywiscie sposob wolniejszy niz uzywanie wyzej opisanych funkcji.


Zrodla informacji

  1. Pliki zrodlowe:
  2. Linux Journal - artykuly w dziale Kernel Korner
  3. Michael K. Johnson: Artykuly dotyczace programow obslugi urzadzen w Kernel Hackers' Guide
  4. Michael K. Johnson: Wrirting Linux Device Drivers


Artur Zawlocki