]> rtime.felk.cvut.cz Git - mf6xx.git/commitdiff
Diploma thesis text.
authorRostislav Lisovy <lisovy@gmail.com>
Sat, 2 Apr 2011 15:12:05 +0000 (17:12 +0200)
committerRostislav Lisovy <lisovy@gmail.com>
Sat, 2 Apr 2011 15:12:05 +0000 (17:12 +0200)
doc/diploma_thesis/text/dip_text.tex

index 4287f79210f2db31a44a464c533c2e31d085b458..54be174dce4428d7a3f2b474657f6a189a97b921 100644 (file)
@@ -57,20 +57,29 @@ Jak je možné ovládat hardware pomocí programu (software) je nejsnazší uká
 
 }
 
-V případě, že budeme chtít změnit hodnotu GPIO pinu, je první možností provedení operace zápisu na určitou adresu v paměťovém adresním prostoru (ta je pevně daná a liší mezi jednotlivými architekturami mikrokontrolérů), tato adresa odpovídá \textbf{registru}\footnote{Registr může být pro zjednodušení považován za malou paměťovou buňku. Změna její hodnoty přímo ovliňuje stav hardware. V dokumentaci ke konkrétnímu mikrokontrolerů/\-mikroprocesoru/\-programovatelnému integrovanému obvodu je uvedeno, jakou funkci mají jednotlivé bity registru.} GPIO pinu. Vnitřní uspořádání mikrokontroleru, dle adresy na kterou jsme zapisovali, rozpozná, že provedená operace zápisu nebyla určena pro změnu hodnoty vnitřní paměti, ale je určena pro změnu hodnoty registru a z toho plynoucí změny stavu určité části hardwaru. Námi zapsaná hodnota se tedy projeví změnou výstupní hodnoty GPIO pinu. Tato možnost je nejjednodušší a je možná v případě, že jsou hardwarové periferie mapovány do určité části tzv. \textbf{paměťového prostoru}.
+V případě, že budeme chtít změnit hodnotu GPIO pinu, je první možností provedení operace zápisu na určitou adresu v paměťovém adresním prostoru (ta je pevně daná a liší mezi jednotlivými architekturami mikrokontrolérů), tato adresa odpovídá \textbf{registru}\footnote{Registr může být pro zjednodušení považován za malou paměťovou buňku. Změna její hodnoty přímo ovliňuje stav hardware. V dokumentaci ke konkrétnímu mikrokontrolerů/\-mikroprocesoru/\-programovatelnému integrovanému obvodu je uvedeno, jakou funkci mají jednotlivé bity registru.} GPIO pinu. Vnitřní uspořádání mikrokontroleru, dle adresy na kterou jsme zapisovali, rozpozná, že provedená operace zápisu nebyla určena pro změnu hodnoty vnitřní paměti, ale je určena pro změnu hodnoty registru a z toho plynoucí změny stavu určité části hardwaru. Námi zapsaná hodnota se tedy projeví změnou výstupní hodnoty GPIO pinu. Tato možnost je nejjednodušší a je možná v případě, že jsou hardwarové periferie mapovány do určité části tzv. \textbf{paměťového prostoru}.\footnote{Také označováno jako MMIO -- \textit{Memory-mapped input/output}}
 
 \begin{figure}[h!]
        \begin{center}
        \includegraphics[width=100mm]{img/mmio.pdf}
-       \caption{V případě architektury IA-32 (označovanou také jako x86) máme k dispozici paměťový a vstupně-výstupní adresní prostor. Vstupně výstupní adresní prostor je pouze 16bitový, zatímco paměťový je (\textit{pro zjednodušení nebereme ohled na PAE -- Physical Address Extension}) 32bitový. Toto rozdělení přetrvává z historických důvodů -- i přesto je již možné některá zařízení mapovat do paměťového prostoru.}
+       \caption{V případě architektury IA-32 (označované také jako x86) máme k dispozici paměťový a vstupně-výstupní adresní prostor. Vstupně výstupní adresní prostor je pouze 16bitový, zatímco paměťový je (\textit{pro zjednodušení nebereme ohled na PAE -- Physical Address Extension}) 32bitový. Toto rozdělení přetrvává z historických důvodů -- i přesto je již možné některá zařízení mapovat do paměťového prostoru.}
        \label{mmio}
        \end{center}
 \end{figure}
 
-Jiným přístupem je při zápisu/čtení do/ze zařízení použití jiné instrukce než kterou používáme pro paměťové operace -- tj. místo zápisu na adresu v paměťovém prostoru vyhrazenou pro GPIO, provedeme zápis do tzv. \textbf{vstupně-výstupního prostoru} na adresu (v tomto případě označovanou jako \textbf{port}) odpovídající registru GPIO pinů. Adresy paměťového a vstupně-výstupního prostoru\footnote{dále v textu bude pro zkrácení občas použito označení \textit{I/O prostor} (z anglického Input/Output)} spolu nijak nesouvisí. V případě zápisu a čtení do/z portu I/O adresního prostoru je potřeba z dokumentace přesně vědět jak široká (kolikabitová) slova smíme zapisovat/číst. 
+Jiným přístupem je při zápisu/čtení do/ze zařízení použití jiné instrukce než kterou používáme pro paměťové operace -- tj. místo zápisu na adresu v paměťovém prostoru vyhrazenou pro GPIO, provedeme zápis do tzv. \textbf{vstupně-výstupního prostoru}\footnote{Také označován zkratkou PIO -- \textit{Programmed input/output} nebo jako I/O adresní prostor} na adresu (v tomto případě označovanou jako \textbf{port}) odpovídající registru GPIO pinů. Adresy paměťového a vstupně-výstupního prostoru spolu nijak nesouvisí. V případě zápisu a čtení do/z portu I/O adresního prostoru je potřeba z dokumentace přesně vědět jak široká (kolikabitová) slova smíme zapisovat/číst. 
+
+\ibox{
+Hlavní rozdíly mezi chováním paměťové buňky a registru zařízení jsou:
+\begin{itemize}
+\item Zápisem do registru můžeme měnit stav zařízení odpovídajícího registru.
+\item V případě zápisu do registru a jeho okamžitém čtení, nemusí být přečtená hodnota shodná se zapisovanou -- v tom případě byla hodnota registru změněna hardwarem.
+\item V případě čtení hodnoty registru může být spuštěn tzv. \textit{side effect}, kdy hardware na toto čtení reaguje změnou stavu, podobně jako by byl proveden zápis do registru (Příklad: Ihned po vyčtení hodnoty registru A/D převodníku se spustí nový převod a původní hodnota se přepíše novou).
+\item Při zápisu a čtení do/z registru si musíme být vědomi toho, kolikabitové operace zápisu/čtení smíme používat (8-, 16-, 32bitové).
+\end{itemize}
+}
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\newpage
-\section{PCI sběrnice a komunikační protokol}
+\section{PCI sběrnice a komunikační protokol}\label{pcich}
 Přesný popis PCI sběrnice je mimo rozsah a zaměření této práce. Pokusím se však zmínit a názorně vysvětlit principy využívané touto sběrnicí, které je nutné alespoň částečně znát při implementaci ovladačů PCI zařízení.
 
 \begin{figure}[h!]
@@ -99,16 +108,18 @@ Informaci o tom, kolik (a jaké) paměti karta bude potřebovat má před nakonf
        \end{center}
 \end{figure}
 
-Zmínil jsem 6 BAR registrů, které každá z karet obsahuje. Mezi další registry, které budeme při implementaci ovladačů používat, patří:
+Zmínil jsem 6 BAR registrů, které každá z karet obsahuje. Mezi další registry, které je potřeba při implementaci ovladačů znát, patří:
 \begin{description}
-\item[Vendor ID] -- Obsahuje unikátní číslo identifikující výrobce zařízení. Za poplatek je udělováno PCI-SIG organizací.\footnote{V Debianu, po nainstalování balíčku \texttt{hwdata}, se seznam těchto identifikátorů nachází v souboru \texttt{/usr/share/hwdata/pci.ids}}
-\item[Device ID] -- Obsahuje číslo identifikující model zařízení.
-\item[Class code] -- Označuje druh zařízení (zda se jedná o grafickou kartu, zvukovou kartu nebo kartu zpracovávající signál)
+\item[Vendor ID] -- Obsahuje unikátní 16bitové číslo identifikující výrobce zařízení. Za poplatek je udělováno PCI-SIG (\textit{PCI Special Interest Group}) organizací.\footnote{V Debianu, po nainstalování balíčku \texttt{hwdata}, se seznam těchto identifikátorů nachází v souboru \texttt{/usr/share/hwdata/pci.ids}}
+\item[Device ID] -- Obsahuje 16bitové číslo identifikující model zařízení. Hodnotu tohoto identifikátoru si volí sám výrobce zařízení.
+\item[Class code] -- Označuje druh zařízení -- zda se jedná např. o grafickou kartu, zvukovou kartu nebo kartu zpracovávající signál.
 \item[Subsystem Vendor ID] -- Podobá se \texttt{Vendor ID}. V případě, že naše karta využívá PCI řadič třetí strany, jako \texttt{Vendor ID} se zobrazí ID výrobce tohoto řadiče. Abychom byli schopni naše zařízení odlišit od jiného, které využívá stejný řadič, ID našeho zařízení bude uloženo v tomot registru. (V případě, že v registru \texttt{Vendor ID} je skutečně naše ID, může být opět i v tomto registru).
 \item[Subsystem ID] -- Opět se jedná o údaj podobný \texttt{Device ID}. 
 \end{description}
 
-Výše popsané registry (a ostatní, které nejsou pro čtenáře podstatné) se nacházejí v tzv. \textbf{konfiguračním adresním prostoru} karty (\textit{ano, po paměťovém a vstupně-výstupním adresním prostoru je zde třetí -- konfigurační -- adresní prostor}). Do toho je možné na architektuře IA-32 přistupovat pomocí zapsání adresy (kam chceme v konfiguračním prostoru zapisovat) a dat (která chceme do konfiguračního prostoru zapsat) do dvou I/O portů s pevně danou adresou.
+Tyto registry slouží operačnímu systému k jednoznačné identifikaci zažízení, při volbě správného ovladače.
+
+Výše popsané registry (a ostatní, které nejsou pro čtenáře podstatné) se nacházejí v 256bitovém tzv. \textbf{konfiguračním adresním prostoru} karty (\textit{po paměťovém a vstupně-výstupním adresním prostoru je zde třetí -- konfigurační -- adresní prostor}). Přístup do konfiguračního adresního prostoru je na architektuře IA-32 možný pomocí zapsání adresy (\textit{kam chceme v konfiguračním prostoru zapisovat}) a dat (\textit{která chceme do konfiguračního prostoru zapsat}) do dvou speciálních I/O portů, které jsou pro tuto operaci vyhrazeny.
 
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -123,12 +134,12 @@ Výše popsané registry (a ostatní, které nejsou pro čtenáře podstatné) s
 
 Měřící karta Humusoft MF624 (dále jen MF624), připojitelná k počítači pomocí PCI sběrnice, má pro účely výkladu psaní ovladačů několik nesporných výhod:
 \begin{itemize}
-\item Komunikace (na úrovni ovladače) s kartou probíhá snadno pochopitelným, přímočarým způsobem (bude vysvětleno dále).
-\item Snadno si ověříme funkci námi napsaného ovladače -- např. připojením LED diody k digitálnímu výstupu.
+\item Komunikace (na úrovni ovladače) s kartou probíhá snadno pochopitelným, přímo\-čarým způsobem (bude vysvětleno dále).
+\item Je snadné si ověřit funkci napsaného ovladače -- např. připojením LED diody k digitálnímu výstupu.
 \end{itemize}
 
-Karta MF624 najde své uplatnění hlavně v laboratorním prostředí -- v případech, když potřebujeme vytvořit styk mezi počítačem a senzorem/jiným zařízením, které poskytuje analogový, resp. digitální signál (v tom případě využijeme A/D převodníků, resp. digitálních vstupů). Kartu je možné použít pro řízení akčního členu/zařízení -- na výběr máme D/A převodníky a digitální výstupy.
-Kromě výše popsaných funkcí disponuje karta dalšími funkcemi (v popisu implementace ovladačů se omezíme pouze na ADC, DAC a DIO):
+Karta MF624 najde své uplatnění hlavně v laboratorním prostředí -- v případech, když je potřeba vytvořit styk mezi počítačem a senzorem/jiným zařízením, které poskytuje analogový, resp. digitální signál (v tom případě využijeme A/D převodníků, resp. digitálních vstupů). Kartu je možné použít pro řízení akčního členu/zařízení -- na výběr máme D/A převodníky a digitální výstupy.
+Kromě výše popsaných funkcí disponuje karta dalšími funkcemi (v popisu implementace ovladačů se omezím pouze na A/D, D/A převodníky a digitální vstupy/výstupy):
 \begin{itemize}
 \item Časovač/čítač
 \item Vstupy inkrementálních snímačů
@@ -141,7 +152,7 @@ Komunikace s kartou probíhá celkem přímočarým způsobem:
 \item V případě čtení A/D převodníku nejprve zapíšeme do konfiguračního registru A/D převodníku slovo odpovídající požadované konfiguraci. Poté již z registru náležícího A/D převodníku vyčteme požadovanou hodnotu.
 \end{itemize}
 
-Abychom zjistili, které registry karta obsahuje, jakou mají funkci a kde jsou umístěny je nejprve potřeba si z oficiálních stránek výrobce stáhnout\footnote{\url{http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf}} manál ke kartě.
+Abychom zjistili, které registry karta obsahuje, jakou mají funkci a kde jsou umístěny je nejprve potřeba si z oficiálních stránek výrobce stáhnout\footnote{\url{http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf}} manuál ke kartě.
 
 Na straně 11 je k vidění první důležitá tabulka (zde tab. \ref{tab_bar}):
 
@@ -159,7 +170,7 @@ Na straně 11 je k vidění první důležitá tabulka (zde tab. \ref{tab_bar}):
        \end{center}
 \end{table}
 
-Z ní je patrné, že karta využívá 3 paměťové regiony mapované do paměťovém adresním prostoru (sloupec 1) -- o velikostech 32, 128 a 128 bajtů (sloupce 3). Pro čtení/zápis z/do nich je potřeba používat 32-, 16-, 32bitové operace (sloupec 4).\footnote{V manuálu je uvedeno, že za určitých podmínek je možné k BAR1 přistupovat i pomocí 32bitových operací. V této práci bych se tomuto složitějšímu přístupu rád vyhnul. Částečná implementace karty MF624 do emulátoru Qemu umožnuje \textbf{pouze} 16bitový přístup do BAR1 paměťového regionu.}
+Z ní je patrné, že karta využívá 3 regiony mapované do paměťovém adresním prostoru (sloupec 1) -- o velikostech 32, 128 a 128 bajtů (sloupce 3). Pro čtení/zápis z/do nich je potřeba používat 32-, 16-, 32bitové operace (sloupec 4).\footnote{V manuálu je uvedeno, že za určitých podmínek je možné k BAR1 přistupovat i pomocí 32bitových operací. V této práci bych se tomuto složitějšímu přístupu rád vyhnul. Částečná implementace karty MF624 do emulátoru Qemu (Popsaná v příloze \ref{qemu}) umožňuje \textbf{pouze} 16bitový přístup do BAR1 paměťového regionu.}
 
 \subsection{Digitální vstupy a výstupy}
 Z tabulky \ref{tab_bar} jsme se navíc dozvěděli informaci, že registry ovládající digitální vstupy a výstupy budou ležet zřejmě v regionu BAR1 (sloupec 2). Podíváme se na přehled registrů náležejících tomuto paměťovému regionu (v oficiálním manuálu na straně 12) -- tomu odpovídá tabulka \ref{tab_bar1}.
@@ -274,7 +285,7 @@ Pro jednoduchost můžeme s funkcí \texttt{printk()} pracovat jako s pro nás z
 \end{description}
 
 \subsection{Kompilace modulu}
-Jednoduchý jaderný modul již máme. Teď je potřeba ho přeložit\footnote{Před samotným překladem jádra je potřeba mít k dispozici zdrojové kódy jádra. Ty buď stáhneme přímo z \texttt{kernel.org} nebo v distribuci Debian nainstalujeme pomocí příkazu\\\texttt{apt-get install ... FIXME}}. K tomu nám poslouží následující \texttt{Makefile}:
+Nyní je potřeba jaderný modul přeložit\footnote{Před samotným překladem jádra je potřeba mít k dispozici zdrojové kódy jádra. Ty buď stáhneme přímo z \texttt{kernel.org} nebo v distribuci Debian nainstalujeme pomocí příkazu\\\texttt{apt-get install ... FIXME}}. K tomu nám poslouží následující \texttt{Makefile}:
 \begin{verbatim}
 1 |  KERNEL_VER=`uname -r`
 2 |  obj-m += hello.o
@@ -287,7 +298,7 @@ Jednoduchý jaderný modul již máme. Teď je potřeba ho přeložit\footnote{P
 Linux využívá při kompilaci systému \texttt{KBUILD}. Ten je tvořen množstvím Makefile souborů a jeho smyslem je umožnit uživateli snadnou konfiguraci před kompilací -- určující, které čáasti se do jádra zakompilují a které nikoliv. Popis toho systému je mimo rozsah této práce. Stručný popis výše zmíněného Makefile souboru:
 
 \begin{description}
-\item[Na prvním řádku] se do proměnné \texttt{KERNEL\_VAR} přiřadí verze aktuálně běžícího jádra (po zavolání příkazu \texttt{uname -r}, který tuto informaci vrací)
+\item[Na prvním řádku] se do proměnné \texttt{KERNEL\_VER} přiřadí verze aktuálně běžícího jádra (po zavolání příkazu \texttt{uname -r}, který tuto informaci vrací)
 \item[Druhý řádek] nám říká, že chceme vytvořit modul ze zdrojového souboru \texttt{hello.c} (pod tímto názvem jsme uložili náš ukázkový modul)
 \item[Na pátém řádku] (uvozeném tabelátorem) se volá (pomocí přepínače \texttt{-C}) Makefile ze systému \texttt{KBUILD}, který se nachází v adresáří spolu se zdrojovými kódy jádra. Parametrem \texttt{M} říkáme, které moduly si přejeme vytvořit -- v tomto případě jsou to ty, jejichž zdrojové soubory jsou v aktuálním adresáři (tj. \texttt{PWD}).
 \end{description}
@@ -304,7 +315,7 @@ make[1]: Entering directory `/usr/src/linux-headers-2.6.35-28-generic'
   LD [M]  /tmp/hello.ko
 make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-28-generic'
 \end{verbatim}
-A v aktuálním adresáři míme kromě různých souborů, které vzniky při překladu i potřebný \texttt{hello.ko}
+A v aktuálním adresáři se nachází kromě různých souborů, které vzniky při překladu, i potřebný \texttt{hello.ko}
 
 \begin{verbatim}
 $ ls
@@ -313,19 +324,19 @@ Makefile  modules.order  Module.symvers
 \end{verbatim}
 
 \subsection{Zavedení modulu}
-Jaderný modul již máme zkompilovaný a nic nám nebrání v jeho zavedení do jádra. To se provede programem \texttt{insmod} -- ten musí být spouštěn se superuživatelským oprávněním:
+Po úspěšném zkompilování jaderného modulu již pouze zbývá ho zavést do jádra. To se provede programem \texttt{insmod} -- ten musí být spouštěn se superuživatelským oprávněním:
 \begin{verbatim}
 $ sudo insmod ./hello.ko 
 \end{verbatim}
 
-V případě, že vše proběhlo správně, měl by být v logu jádra text vypisovaný modulem po jeho zavedení. To ihned zjistíme:
+V případě, že vše proběhlo správně, měl by být v logu jádra text vypisovaný modulem po jeho zavedení. To je možné ověřit:
 \begin{verbatim}
 $ dmesg | tail -1
 [ 9245.757491] Hello, world!
 \end{verbatim}
-A skutečně je posledním řádkem v logu náš text.
+A skutečně je posledním řádkem v logu text vypsaný zavedeným modulem.
 
-Abychom plně otestovali funkčnost našeho prvního modulu, zkusíme ho nyní z jádra uvolnit. To uděláme programem \texttt{rmmod} (opšt spouštíme se superuživatelskými privilegii).
+Pro plné otestování funkčnosti ukázkového modulu, je potřeba ho z jádra uvolnit. K tomu slouží program \texttt{rmmod} (opět je potřeba spouštět se superuživatelskými privilegii).
 \begin{verbatim}
 $ sudo rmmod hello 
 
@@ -338,36 +349,191 @@ V logu je opět nachází text vypisovaný modulem při uvolňování z jádra.
 \ibox{V případě, že se chystáme do jádra zavést modul, jehož jsme autoři a nejsme si jisti jeho funkčností, doporučuji si veškerou důležitou práci uložit a před zavedením/uvolněním modulu do/z jádra spustit program \texttt{sync}, který uloží obsah diskových bufferů na disky.}
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 \section{Ovladače PCI zařízení}
-Než začneme implementovat skutečný ovladač pro kartu MF624 je potřeba si vysvětlit základní funkce pro práci s PCI zařízeními a vstupně-výstupním a paměťovým adresním prostorem.
+%Než začneme implementovat skutečný ovladač pro kartu MF624 je potřeba si vysvětlit základní funkce pro práci s PCI zařízeními a vstupně-výstupním a paměťovým adresním prostorem.
+\ibox{Následující popis není zcela vyčerpávající a obsahuje pouze to nejnutnější pro implementaci základního ovladače. Zájemce o problematiku mohu odkázat na literaturu FIXME nebo zdrojové kódy jádra\footnote{Pro prohlížení zdrojových kódů jádra mohu doporučit online \textit{the Linux Cross Reference} -- \url{http://lxr.linux.no/} FIXME footnote se nezobrazuje}}
+
+První věc, kterou ovladač PCI zařízení potřebuje udělat, aby se stal součástí systému, je registrace do seznamu všech PCI ovladačů v operačním systému. To se provede voláním funkce \texttt{pci\_register\_driver()}, které se jako parametr předá ukazatel na strukturu \texttt{struct pci\_driver}. 
+
+\subsection*{Struktura \texttt{struct pci\_driver}}
+Tato struktura obsahuje základní informace o našem ovladači. Mezi hlavní položky patří:
+\begin{description}
+\item[\texttt{const char name*}] -- Název ovladače. Tento název by měl být unikátní mezi všemi ovladači PCI zařízení. Většinou je totožný s názvem modulu.
+\item[\texttt{const struct pci\_device\_id *id\_table}] -- Pole struktur popisujících, pro která zařízení je ovladač vytvořen (jednotlivé položky struktury jsou popsány níže).
+\item[\texttt{int (*probe) (struct pci\_dev *dev, const struct pci\_device\_id *id)}] -- \\Ukazatel na funkci, která je volána PCI subsystémem, v případě, že je přítomno zařízení, pro které je tento ovladač vytvořen.
+\item[\texttt{void (*remove) (struct pci\_dev *dev)}] -- Ukazatel na funkci, která je volána poté, co je tento ovladač odstraňován ze seznamu ovladačů aktuálně používaných PCI subsystémem.
+\end{description}
+
+Příklad, jak může být struktura \texttt{pci\_driver} inicializována a následně zaregistrována:
+\begin{verbatim}
+1 |  static struct pci_driver mf624_pci_driver = {
+2 |      .name = "mf624",
+3 |      .id_table = mf624_pci_id,
+4 |      .probe = mf624_pci_probe, 
+5 |     .remove = mf624_pci_remove,
+6 |  };
+pci_register_driver(&mf624_pci_driver);
+\end{verbatim}
+
+\subsection*{Struktura \texttt{struct pci\_device\_id}}
+Jak již bylo zmíněno, struktura \texttt{struct pci\_device\_id} slouží k identifikaci, pro která zařízení je ovladač určen. Mezi hlavní položky struktury patří \texttt{vendor}, \texttt{device}, \texttt{subvendor}, \texttt{subdevice} (typu \texttt{\_\_u32}) -- jejichž hodnota odpovídá stejnojmenným registrům v konfiguračním prostoru PCI zařízení. Jelikož může být ovladač napsán pro více zařízení, je tato struktura inicializována jako prvek pole, které je zakončeno prvkem prázdným. Možná inicializace vypadá následovně:
+
+\begin{verbatim}
+ 1 |  #define PCI_VENDOR_ID_HUMUSOFT          0x186c
+ 2 |  #define PCI_DEVICE_ID_MF624             0x0624
+ 3 |  #define PCI_SUBVENDOR_ID_HUMUSOFT       0x186c
+ 4 |  #define PCI_SUBDEVICE_DEVICE            0x0624
+ 5 |  
+ 6 |  static struct pci_device_id mf624_pci_id[] = {
+ 7 |      {
+ 8 |          .vendor = PCI_VENDOR_ID_HUMUSOFT,
+ 9 |          .device = PCI_DEVICE_ID_MF624,
+10 |          .subvendor = PCI_SUBVENDOR_ID_HUMUSOFT,
+11 |          .subdevice = PCI_SUBDEVICE_DEVICE,
+12 |      },
+13 |      { 0, } /* seznam je vždy zakončen prázdným prvkem */
+14 |  };
+\end{verbatim}
+
+
+\subsection*{Funkce \texttt{probe()}}
+\ibox{\texttt{int probe(struct pci\_dev *dev, const struct pci\_device\_id *id)}}
+
+Funkce \texttt{probe()} náležící danému ovladači zařízení je volána poté, co subsystém PCI zařízení zjistí, že se v systému nachází zařízení, pro které je tento ovladač určen. Tato funkce má na starosti inicializaci zařízení.
+
+V prvním parametru PCI subsystém předává strukturu \texttt{struct pci\_dev}, která repre\-zentuje fyzické zařízení. V druhém parametru je předána struktura, na základě byl zvolen daný ovladač.
+
+\ibox{\texttt{pci\_enable\_device(struct pci\_dev *dev);}}
+
+V rámci inicializace je nejprve potřeba zavolat funkci \texttt{pci\_enable\_device()} -- ta se postará o inicializaci karty na úrovni hardware -- např.: přiřazení linky přerušení, zresetování registrů karty a její probuzení. Poté již můžeme začít přistupovat ke zdrojům zařízení.
+
+\subsection*{Přístup ke zdrojům karty}
+Jak bylo popsáno v kapitole \ref{pcich}, PCI zařízení může využívat až 6 paměťových nebo vstupně-výstupních regionů (označovaných jako \textit{zdroje} karty). Jejich alokace do paměťového nebo I/O prostoru počítače je zajištěna dynamicky PCI mostem. Pro přístup do nich si musí ovladač zařízení zjistit jejich adresu a vyžádat si u operačního systému výlučný přístup. 
+
+\ibox{\texttt{int pci\_request\_regions(struct pci\_dev *pdev, const char *res\_name);}}
+
+Nejprve je potřeba operační systém požádat o výlučný přístup ke zdrojům zařízení. To se provede voláním funkce \texttt{pci\_request\_regions()}. V případě, že ovladači není přístup umožněn (jiný ovladač přistupuje ke stejné kartě nebo po jeho odstranění nedošlo k uvolnění zdrojů karty), ovladač by měl korektním způsobem ukončit svoji funkci a nesnažit se k zařízení přistupovat.
+
+\ibox{\texttt{unsigned long pci\_resource\_start(struct pci\_dev *dev, int bar);}}
+
+V případě, že volání \texttt{pci\_request\_regions()} proběhlo úspěšně, je již možné získat přístup přímo k jednotlivým regionům karty. Fyzickou adresu jednotlivých regionů zjistíme voláním funkce \texttt{pci\_resource\_start()}, kde se jako druhý parametr uvede číslo BAR registru určujícího region (tj. 0--5).
+
+\ibox{\texttt{unsigned long pci\_resource\_len(struct pci\_dev *dev, int bar);}}
 
-\texttt{pci\_enable\_device()}
-\texttt{pci\_disable\_device()}
+V případě, že je potřeba zjistit velikost daného paměťového nebo I/O regionu, slouží k tomu funkce \texttt{pci\_resource\_len()}.
 
-\texttt{pci\_request\_regions()}
-\texttt{pci\_release\_regions()}
+\ibox{\texttt{void \_\_iomem *pci\_ioremap\_bar(struct pci\_dev *pdev, int bar);}}
 
+S ukazatelem, který vrátí funkce \texttt{pci\_request\_regions()} však není možné přímo pracovat -- je to toiž \textbf{fyzická adresa} daného regionu, ke které neumí procesor přímo přistupovat. Aby tato fyzická adresa byla přemapována na adresu \textbf{virtuální}, je potřeba zavolat funkci \texttt{pci\_ioremap\_bar()}.
 
-\texttt{pci\_resource\_start(dev, 4)}
-\texttt{pci\_resource\_len(dev, 4)}
+K ukazateli, který vrátí volání \texttt{pci\_ioremap\_bar()} je již možné pomocí speciálních funkcí přistupovat.
 
-\texttt{pci\_ioremap\_bar(dev, 4);}
-\texttt{iounmap()}
+\ibox{FIXME povídání o out-of-order execution a cache a proč musíme používat speciální funkce}
 
-\texttt{struct pci\_device\_id}
+\subsection*{Funkce \texttt{remove()}}
+
+\ibox{\texttt{void remove(struct pci\_dev *dev);}}
+
+Funkce je volána, když PCI subsystém ze svého seznamu odstraňuje strukturu \texttt{struct pci\_dev} reprezentující dané zařízení, nebo v případě, že dochází k uvolnění modulu.
+
+Tato funkce by se měla postarat o úklid všech naalokovaných prostředků apod. Měla by obsahovat volání:
+\begin{description}
+\item[\texttt{iounmap()}] -- Uvolnění virtuální paměti namapované voláním \texttt{pci\_ioremap\_bar()}.
+\item[\texttt{pci\_release\_regions()}] -- Uvolnění zdrojů karty, které byly zarezervovány voláním \texttt{pci\_request\_regions()}.
+\item[\texttt{pci\_disable\_device()}] -- Opak k volání \texttt{pci\_enable\_device()}.
+\end{description}
 
  
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 \section{UIO ovladač}
-V kapitole \ref{hum_komunikace} jsme si vysvětlili, jak je z manuálu možné zjistit, jak s kartou správně komunikovat a nyní již víme které registry je potřeba ovládat. 
+V případě, že je vytvářen ovladač pro linuxové jádro, mělo by být promyšleno, kterého subsystému se stane součástí -- např. zda jde o jednoduché znakové zařízení, síťovou kartu nebo zvukovou kartu.
+
+V případě, že jde o PCI zařízení, které nelze snadno zařadit do žádné kategorie, je možné použít tzv. UIO ovladač. Tento ovladač se skládá ze dvou částí: jaderného modulu a aplikace v uživatelském prostoru. Mezi jeho hlavní výhody patří to, že v jádře je obsažena pouze malá obecná část, která zpřístupňuje zdroje zařízení do uživatelského prostoru (její implementace je poměrně snadná).
+
+Druhou částí je aplikace v uživateském prosotru, která přistupuje k jednotlivým zdrojům karty a tvoří hlavní logiku ovladače. Většina vývoje tedy probíhá v uživatelském prostoru, čímž klesá riziko narušení stability jádra.
 
 \subsection{Jaderný modul}
-\subsection{Uživatelský program}
+Jaderný modul UIO ovladače obsahuje:
+\begin{itemize}
+\item Funkci volanou PCI subsystémem při registraci ovladače
+\item Volání funkcí pro zpřístupnění regionů zařízení
+\item Registraci do UIO subsystému
+\item Funkce pro \textit{úklid} a uvolnění zdrojů karty
+\end{itemize}
+
+Většina z těchto úkonů byla již popsána v kapitole \ref{pcich} a jsou zcela standardní pro jakýkoliv ovladač PCI zažízení, navíc je zde pouze \textit{registraci do UIO subsystému}.
+
+\ibox{\texttt{int uio\_register\_device(struct device *parent, struct uio\_info *info);}}
+
+\ibox{FIXME co je \&pci\_device-dev?}
+Ta se provede zavoláním funkce \texttt{uio\_register\_device()}, kde se jako druhý parametr předá ukazatel na strukturu \texttt{struct uio\_info}.
+
+\subsection*{Struktura \texttt{struct uio\_info}}
+Mezi její hlavní položky patří:
+\begin{description}
+\item[\texttt{const char *name}] -- Název ovladače
+\item[\texttt{const char *version}] -- Verze ovladače
+\item[\texttt{struct uio\_mem mem[MAX\_UIO\_MAPS]}] -- Pole struktur obsahujících informace o regionech PCI zařízení mapovaných do paměťového prostoru
+\item[\texttt{struct uio\_port port[MAX\_UIO\_PORT\_REGIONS]}] -- Pole struktur obsahujících informace o regionech PCI zařízení mapovaných do vstupně-výstupního prostoru.
+%\item[\texttt{}]
+\end{description}
+
+\subsection*{Struktura \texttt{struct uio\_mem} a \texttt{struct uio\_port}}
+Tyto struktury obsahují imformace o regionech zařízení -- která ze dvou struktur bude inicializována se rozhoduje na základě toho, zda karta mapuje regiony do paměťového nebo vstupně-výstupního prostoru.
+
+Struktura \texttt{struct uio\_mem} obsahuje položky:
+\begin{description}
+\item[\texttt{const char *name}] -- Textový popis daného regionu (viditelný z uživatelského prostoru).
+\item[\texttt{unsigned long addr}] -- Fyzická adresa regionu získaná voláním \texttt{pci\_resource\_start()}.
+\item[\texttt{unsigned long size}] -- Délka regionu. Nejsnáze získaná voláním \texttt{pci\_resource\_len()}.
+\item[\texttt{int memtype}] -- Typ paměti. Pro fyzickou paměť na zařízení se použije \texttt{UIO\_MEM\_PHYS}.
+\item[\texttt{void \_\_iomem *internal\_addr}] -- Virtuální adresa získaná voláním \texttt{pci\_ioremap\_bar()}
+\end{description}
+
+Struktura \texttt{struct uio\_port} obsahuje položky:
+\begin{description}
+\item[\texttt{const char *name}] -- Textový popis daného regionu (viditelný z uživatelského prostoru).
+\item[\texttt{unsigned long start}] -- Fyzická adresa regionu získaná voláním \texttt{pci\_resource\_start()}.
+\item[\texttt{unsigned long size}] -- Délka regionu. Nejsnáze získaná voláním \texttt{pci\_resource\_len()}.
+\item[\texttt{int porttype}] -- Typ portu. Pro porty na architektuře IA-32 se použije \texttt{UIO\_PORT\_X86}
+\end{description}
+
+~\\
+
+Příklad, jak taková jednoduchá inicializace struktury \texttt{struct uio\_info} včetně registrace může vypadat (bez ošetření chybných stavů):
+\begin{verbatim}
+ 1 |  /* struct pci_dev *dev */
+ 2 |  struct uio_info *info;
+ 3 |  info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+ 4 |  
+ 5 |  info->name = "mf624";
+ 6 |  info->version = "0.0.1";
+ 7 |  
+ 8 |  info->mem[0].name = "PCI chipset, ...";
+ 9 |  info->mem[0].addr = pci_resource_start(dev, 0);
+10 |  info->mem[0].size = pci_resource_len(dev, 0);
+11 |  info->mem[0].memtype = UIO_MEM_PHYS;
+12 |  info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
+13 |  
+14 |  info->port[0].name = "Board programming registers";
+15 |  info->port[0].porttype = UIO_PORT_X86;
+16 |  info->port[0].start = pci_resource_start(dev, 1);
+17 |  info->port[0].size = pci_resource_len(dev, 1);
+18 |  
+19 |  uio_register_device(&dev->dev, info);
+20 |  pci_set_drvdata(dev, info);
+\end{verbatim}
+
+\ibox{\texttt{void pci\_set\_drvdata(struct pci\_dev *pdev, void *data)}}
+
+Na posledním řádku je, dosud nepopsané, volání \texttt{pci\_set\_drvdata()}. To (v tomto případě) zajistí, že struktura \texttt{struct uio\_info} se stane součástí struktury reprezentující zařízení (\texttt{struct pci\_dev}) -- což umožní přístup ke struktuče \texttt{struct uio\_info} z funkcí jako je například funkce \texttt{remove()}, která jako parametr získá ukazatel na strukturu \texttt{struct pci\_dev}
+
+\subsection{Program v uživatelském prostoru}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 \section{Comedi ovladač}
 
 \appendix
-\chapter{Qemu a Humusoft MF624}
+\chapter{Qemu a Humusoft MF624}\label{qemu}
 Měřící karta Humusoft MF624 je hardware vhodný pro výklad implementace ovladače, je ale poměrně drahý. Pro účely výuky proto byly do emulačního software Qemu implementovány základní funkce této měřící karty -- konkrétně se jedná o A/D převodníky, D/A převodníky a digitální vstupy a výstupy. 
 \end{document}