1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 \chapter{Motivace, cíl}
3 Zadání této práce vzešlo z akademického prostředí a reaguje na nedostatek studijních materiálů v českém jazyce pro začátečníky, popisujících vývoj ovladačů (v tomto případě PCI zařízení) pro operační systém GNU/Linux.
5 Text obsahuje pouze nezbytné množství teorie, která je podložená četnými příklady pro snadnější pochopení čtenářem.
7 Pro čtenáře neznalého psaní programů těsně svázaných s hardwarem se snažím názorně vysvětlit základní principy a úskalí tohoto druhu programování.
9 V případě dalšího zájmu o problematiku může čtenář sáhnout po
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 \section{Komunikace s hardwarem}
13 Jak je možné ovládat hardware pomocí programu (software) je nejsnazší ukázat na příkladu jednočipového počítače.
15 \ibox{Mikrokontrolér, neboli jednočipový počítač, má velikost pouze jednoho čipu. Obsahuje přitom procesor, paměť, vstupně-výstupní zařízení a jiné. Příklady zařízení obsažených v mikrokontroléru:
17 \item[GPIO piny (General Purpose Input/Output)] -- u těch je možné nastavit, zda chceme jejich hodnotu číst (a je k nim tedy připojeno jiné zařízení) nebo zda chceme jejich hodnotu nastavovat.
18 \item[A/D převodník] -- Převádí analogový (spojitý) signál na diskrétní (nespojitý). Příklad: Na vstup A/D převodníku přivedeme napětí 2,4 V. Z registru odpovídajícímu tomuto převodníku si v digitální podobě mikroprocesor vyčte hodnotu odpovídající 2,4.
19 \item[D/A převodník] -- Opačně od A/D převodníku převádí digitální signál na analogový. Příklad: Do registru odpovídajícímu D/A převodníku zapíše mikroprocesor hodnotu odpovídající např. 3,5 V. Na výstupu bude možné změřit napětí 3,5 V (s určitou malou odchylkou).
25 % \includegraphics[width=80mm]{img/msp430g2x01.png}
26 % \caption{Architektura velmi jednoduchého mikroprocesoru MSP430G2x01 firmy Texas Instruments.}
27 % \label{microcontroller}
33 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. 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}}
37 \includegraphics[width=100mm]{img/mmio.pdf}
38 \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.}
43 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.
46 Hlavní rozdíly mezi chováním paměťové buňky a registru zařízení jsou:
48 \item Zápisem do registru můžeme měnit stav zařízení odpovídajícího registru.
49 \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.
50 \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).
51 \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é).
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55 \section{PCI sběrnice}\label{pcich}
56 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í.
60 \begin{minipage}[b]{0.4\linewidth}
61 \includegraphics[width=50mm]{img/pci_schema2.png}
63 \begin{minipage}[b]{0.4\linewidth}
64 \includegraphics[width=50mm]{img/pci2.jpg}
66 \caption{\textit{Vlevo}: Schéma znázorňující rozdíly mezi konektory pro karty s napájením 3,3 V a 5 V. \textit{Vpravo}: Reálná fotografie PCI konektorů}
71 PCI (\textit{Peripheral Component Interconnect}) je standard paralelní sběrnice využívaný v počítačích nejrůznějších architektur. Mezi její hlavní výhody (oproti její předchůdkyni -- sběrnicic ISA) patří dynamická konfigurace připojených zařízení: Ve většině případů probíhá komunikace mezi hostitelským systémem a připojenou (a nakonfigurovanou) PCI kartou zápisem/čtením do určité paměťové (nebo vstupně-výstupní) oblasti. U starší sběrnice ISA si každá karta pevně určila, kam se její část paměti namapuje -- v takovém případě mohl nastat problém, že více než jedna karta mapovala svoji paměť na stejnou adresu (nebo se jednolivá mapování překrývala). PCI sběrnice tomuto problému předchází takovým způsobem, že každá z karet nese informaci o tom, kolik jak velkých paměťových/I/O regionů potřebuje namapovat -- o samotné mapování se poté postará PCI most.
73 Informaci o tom, kolik (a jaké) paměti karta bude potřebovat má před nakonfigurováním uloženu v tzv. \textbf{Base Address Registrech} -- BAR0--BAR5\footnote{FIXME Ve skutečnosti funguje vyčtení požadované velikosti z registru karty PCI mostem takovým způsobem, že se PCI most snaží do každého BAR registru zapsat 0xFF a poté zapsanou hodnotu přečíst -- do BAR registru je možné zapsat pouze .... bla bla}. Poté co se PCI mostu podaří tuto hodnotu přečíst a požadovanou paměť alokovat, zapíše zpět do daného registru adresu, na které se alokovaná paměť nachází. Tu si pro potřeby komunikace vyčte oprační systém.
78 \includegraphics[width=100mm]{img/pci-config-space2.pdf}
79 \caption{Obsah 256 bajtů konfiguračního prostoru PCI karty (zvýrazněny jsou ty registry, jejichž znalost využijeme při vývoji ovladačů)}
84 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ří:
86 \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}}
87 \item[Device ID]~\\Obsahuje 16bitové číslo identifikující model zařízení. Hodnotu tohoto identifikátoru si volí sám výrobce zařízení.
88 \item[Class code]~\\Označuje druh zařízení -- zda se jedná např. o grafickou kartu, zvukovou kartu nebo kartu zpracovávající signál.
89 \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).
90 \item[Subsystem ID]~\\Opět se jedná o údaj podobný \texttt{Device ID}.
93 Tyto registry slouží operačnímu systému k jednoznačné identifikaci zažízení, při volbě správného ovladače.
95 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.
98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 \section{Humusoft MF624}
102 \includegraphics[width=150mm]{img/mf624.jpg}
103 \caption{Měřící karta Humusoft MF624}
108 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:
110 \item Komunikace (na úrovni ovladače) s kartou probíhá snadno pochopitelným, přímo\-čarým způsobem (bude vysvětleno dále).
111 \item Je snadné si ověřit správnou funkci napsaného ovladače -- např. připojením LED diody k digitálnímu výstupu.
114 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.
115 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):
118 \item Vstupy inkrementálních snímačů
121 \subsection{Komunikace s kartou}\label{hum_komunikace}
122 Komunikace s kartou probíhá celkem přímočarým způsobem:
124 \item V případě, že chceme číst hodnoty digitálních vstupů, přečteme hodnotu registru určeného právě digitálním vstupům -- v případě zápisu na digitální výstupy, zapíšeme do registru určeného digitálním výstupům
125 \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.
128 Které registry karta obsahuje, jakou mají funkci a kde jsou umístěny je možné zjistit z manuálu ke kartě -- ten je možné stáhnout z oficiálních internetových stránek výrobce.\footnote{\url{http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf}}
130 Na straně 11 je k vidění první důležitá tabulka (zde tab. \ref{tab_bar}):
134 \begin{tabular}{|p{2cm}|p{4cm}|c|c|}
135 \hline \textbf{Region} & \textbf{Function} & \textbf{Size (bytes)} & \textbf{Width (bytes)} \\
136 \hline BADR0 (memory mapped) & PCI chipset, interrupts, status bits, special functions & 32 & 32 \\
137 \hline BADR1 (memory mapped) & A/D, D/A, digital I/O & 128 & 16/32 \\
138 \hline BADR2 (memory mapped) & Counter/timer chip & 128 & 32 \\
141 \caption{Paměťové regiony, které PCI karta využívá}
146 Z ní je patrné, že karta využívá 3 regiony\footnote{V manuálu je uvedeno, že se jedná o regiony odpovídající BAR0, BAR1 a BAR2 registrům -- skutečnost je však taková, že karta využívá regiony odpovídající registrům BAR0, BAR2 a BAR4. Rozdíl mezi skutečností a manuálem spočívá v tom, že v muálu jsou registry popsány tak, jak je vidí operační systém Microsoft Windows. Na tuto skutečnost se musí při implementaci ovladače brát zřetel. FIXME} mapované do paměťového adresního 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.}
148 \subsection{Digitální vstupy a výstupy}
149 Z tabulky \ref{tab_bar} lze vyčíst informaci, že registry ovládající digitální vstupy a výstupy budou ležet zřejmě v regionu BAR1 (sloupec 2). Dále je potřeba se podívat 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 (s menšími úpravami) \ref{tab_bar1}.
153 \begin{tabular}{|p{17mm}|c|c|}
154 \hline \textbf{Address (BADR1 offset)} & \textbf{Read} & \textbf{Write} \\
155 \hline 0x00 & \textbf{ADDATA} -- A/D data & ADCTRL -- A/D control \\
156 \hline 0x02 & \textbf{ADDATA} -- A/D data mirror & \\
157 \hline 0x04 & \textbf{ADDATA} -- A/D data mirror & \\
158 \hline 0x06 & \textbf{ADDATA} -- A/D data mirror & \\
159 \hline 0x08 & \textbf{ADDATA} -- A/D data mirror & \\
160 \hline 0x0A & \textbf{ADDATA} -- A/D data mirror & \\
161 \hline 0x0C & \textbf{ADDATA} -- A/D data mirror & \\
162 \hline 0x0E & \textbf{ADDATA} -- A/D data mirror & \\
163 \hline 0x10 & \textbf{DIN} -- Digital input & \textbf{DOUT} -- Digital output \\
164 \hline 0x20 & \textbf{ADSTART} -- A/D SW trigger & \textbf{DA0} -- D/A 0 data \\
165 \hline 0x22 & & \textbf{DA1} -- D/A 1 data \\
166 \hline 0x24 & & \textbf{DA2} -- D/A 2 data \\
167 \hline 0x26 & & \textbf{DA3} -- D/A 3 data \\
168 \hline 0x28 & & \textbf{DA4} -- D/A 4 data \\
169 \hline 0x2A & & \textbf{DA5} -- D/A 5 data \\
170 \hline 0x2C & & \textbf{DA6} -- D/A 6 data \\
171 \hline 0x2E & & \textbf{DA7} -- D/A 7 data \\
174 \caption{Registry karty obsažené v regionu BAR1}
179 Na devátém řádku je napsáno \textit{DIN -- Digital input, DOUT -- Digital output} -- to značí registr, který se stará o nastavování digitálních výstupů a čtení digitálních vstupů (Všimněme si, že registr DIN a DOUT mají stejnou adresu i přesto, že na kartě jsou vstupy a výstupy realizovány oddělenými vodiči).
181 Jak jsou data v registrech reprezentována, je možné si přečíst (v oficiálním manuálu) na straně 16, kde jsou tyto dva registry podrobně popsány (zde tabulka \ref{tab_din} a \ref{tab_dout}).
182 První sloupec určuje, kterých bitů se daný řádek týká. V druhém sloupci je informace o funkci. Třetí sloupec udává výchozí hodnotu. Z toho, co je v tabulkách uvedeno, plyne, že pro čtení 8bitového digitálního vstupu stačí přečíst spodních 8 bitů DIN registru, horních 8 bitů je potřeba ignorovat. Stejně tak pro nastavení 8bitového digitálního výstupu se zapíše požadovaná hodnota do spodních 8bitů registru DOUT, horních 8 bitů je potřeba ignorovat.
186 \begin{tabular}{|c|c|c|}
187 \hline \textbf{Bit} & \textbf{Description} & \textbf{Default} \\
188 \hline 7:0 & \textbf{Digital input 7:0.} Reads digital input port. & 1 \\
189 \hline 15:8 & Reserved & N/A \\
192 \caption{DIN -- Digital Input Register Format}
199 \begin{tabular}{|c|c|c|}
200 \hline \textbf{Bit} & \textbf{Description} & \textbf{Default} \\
201 \hline 7:0 & \textbf{Digital output 7:0.} Writes to digital output port. & 0 \\
202 \hline 15:8 & Reserved & N/A \\
205 \caption{DOUT -- Digital Output Register Format}
211 \subsection{A/D převodníky}
212 Karta MF624 obsahuje osm 14bitových A/D převodníků s pevně stanoveným rozsahem $\pm$10 V. Jejich vyčtení může probíhat následujícím způsobem:
215 \item Nejprve se v registru ADCTRL zvolí, které A/D převodníky mají být čteny.
216 Každý z A/D převodníků je reprezentován jedním bitem. Zápisem 1 do daného bitu se nastaví, že bude daný A/D převodník aktivní -- 0 ho deaktivuje.
217 \item Čtením registru ADSTART se spustí převod na zvolených A/D převod\-nících. Přečtená hodnota se dále nepoužívá.
218 \item V případě, že se provedl převod na všech zvolených A/D převodnících, je EOLC bit GPIOC registru nastaven na 0 (jinak je v 1).
219 \item Výslednou hodnotu je možné přečíst z registru ADDATA typu FIFO nebo mnohem jednodušší je čtení z jeho zrcadlených hodnot v registrech ADDATA0--ADDATA7 (v manuálu jsou označeny pouze jako \textit{BADR1 + 0x02} až \textit{BADR1 + 0x0E}).
222 Hodnota vyčtená z A/D převodníků je ve formátu dvojkového doplňku.
224 \subsection{D/A převodníky}
225 Karta MF624 obsahuje také osm 14bitových D/A převodníků s rozsahem $\pm$10 V.
227 Nastavení výstupních hodnot D/A převodníků může probíhat následujícícm způsobem:
230 \item Hodnota v aditivním kódu (tabulka 23 v originálním manuálu) se zapíše do jednoho z osmi registrů DA0--DA7 odpovídajícího D/A převodníku, který chceme nastavovat
231 \item Bit DACEN registru GPIOC je potřeba nastavit na 0, jinak jsou výstupy D/A převodníků připojeny na \textit{zem}.
232 \item Bit LDAC registru GPIOC je potřeba nastavit na 0, aby byl spuštěn samotný převod D/A převodníků.
237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 \chapter{Implementace ovladačů}
239 \section{Operační systém GNU/Linux}
240 Jako cílový operační systém, na kterém bude vysvětlena implementace základních ovladačů, byl zvolen GNU/Linux\footnote{Operační systém sestávající z GNU nástrojů a jádra Linux je označován jako GNU/Linux.}. Hlavním důvodem je jeho otevřenost (a z toho plynoucí velké množství kvalitní dokumentace), rozšířenost (hlavně mezi vestavěnými zařízeními) a vysoká kvalita. Distribucí použitou při vývoji byl Debian GNU/Linux (verze jádra Linux 2.6.35) -- popsané postupy by však měly fungovat i pro jiné distribuce.
242 \subsection{Principy psaní jaderných modulů}
243 Jádro operačního systému GNU/Linux je monolitické -- to znamená, že po zkompilování a slinkvání je tvořeno jedním kusem kódu. Tento druh jádra je léty prověřen a mezi výhody patří jeho snadná implementace. Aby běžící jádro nemuselo obsahovat veškeré dostupné ovladače zařízení (nebo abychom v případě potřeby přidat do jádra ovladač pro nový hardware nemuseli celé jádro znovu kompilovat), existuje mechanismus načítání jaderných modulů za běhu, tzv. LKM -- Loadable Kernel Module. V praxi to vypadá tak, že jsou v běžícím jádře zakompilovány pouze nejnutnější ovladače, všechny ostatní si může systém nebo uživatel za běhu do jádra načíst -- v případě, že již nejsou potřeba, je možné je z jádra uvolnit.
245 Jak se takový jaderný modul může vypadat, je nejlepší si ukázat na příkladu:
247 1 | #include <linux/init.h>
248 2 | #include <linux/module.h>
250 4 | static int hello_init(void)
252 6 | printk("Hello, world!\n");
256 10 | static void hello_exit(void)
258 12 | printk("Goodbye, cruel world!\n");
261 15 | module_init(hello_init);
262 16 | module_exit(hello_exit);
264 18 | MODULE_LICENSE("Dual BSD/GPL");
267 První věc, na kterou je potřeba upozornit je, že většina jaderných modulů (stejně jako zdrojových kódů jádra samotného) je psána v programovacím jazyce C (jedná se o mírně modifikovaný standard C90).
269 \item[Řádky 1 a 2] obsahují vložení hlavičkových souborů -- obsahují prototypy volaných funkcí a jsou nutné pro tvorbu jaderného modulu.
270 \item[Na řádcích 4--8] je funkce, která bude spuštěna ihned po zavedení našeho modulu do jádra. Ta obsahuje pouze volání funkce \texttt{printk()}.
272 Pro jednoduchost můžeme s funkcí \texttt{printk()} pracovat jako s, pro čtenáře známou, funkcí \texttt{printf()} -- narozdíl od standardního výstupu se však text vypsaný funkcí \texttt{printk()} zapíše do \textit{logu} jádra. Jedním ze způsobů, jak ho zobrazit je pomocí programu \texttt{dmesg}.\\Jak se zařídí, že se tato funkce vykoná ihned po zavedení modulu do jádra? O to se postará příkaz na $\rightarrow$
273 \item[řádku 15] -- ten obsahuje makro \texttt{module\_init()}, kterému řekneme právě to, která funkce se má po načtení spustit.
274 \item[Řádek 16] obsahuje naopak makro, které udává, která funkce se má zavolat v případě, že se bude modul uvolňovat z jádra. V našem případě je to funkce na $\rightarrow$
275 \item[řádcích 10--13.] Tato funkce nemá na starost nic jiného než výpis krátkého textu do logu jádra.
278 \subsection{Kompilace modulu}
279 Dále 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 poslouží následující \texttt{Makefile}:
281 1 | KERNEL_VER=`uname -r`
285 5 | make -C /lib/modules/$(KERNEL_VER)/build M=$(PWD) modules
287 7 | make -C /lib/modules/$(KERNEL_VER)/build M=$(PWD) clean
289 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:
292 \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í)
293 \item[Druhý řádek] říká, že modul bude vytvářen ze zdrojového souboru \texttt{hello.c} (pod tímto názvem jsme uložili náš ukázkový modul)
294 \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}).
297 V případě, že v adresáři, ve kterém se nachází zdrojový soubor modulu \texttt{hello.c} a výše popsaný soubor \texttt{Makefile}, spustí příkaz \texttt{make}, měl by proběhnout samotný překlad:
300 make -C /lib/modules/`uname -r`/build M=/tmp/kernel_module_example modules
301 make[1]: Entering directory `/usr/src/linux-headers-2.6.35-28-generic'
302 CC [M] /tmp/kernel_module_example/hello.o
303 Building modules, stage 2.
307 make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-28-generic'
309 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}
313 hello.c hello.ko hello.mod.c hello.mod.o hello.o
314 Makefile modules.order Module.symvers
317 \subsection{Zavedení modulu}
318 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:
320 $ sudo insmod ./hello.ko
323 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:
326 [ 9245.757491] Hello, world!
328 A skutečně je posledním řádkem v logu text vypsaný zavedeným modulem.
330 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).
335 [ 9612.256929] Goodbye, cruel world!
337 V logu je opět nachází text vypisovaný modulem při uvolňování z jádra.
340 \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.}
341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 \section{Ovladače PCI zařízení}
343 %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.
344 \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}}
346 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}.
348 \subsection{Struktura \texttt{struct pci\_driver}}
349 Tato struktura obsahuje základní informace o našem ovladači. Mezi hlavní položky patří:
351 \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.
352 \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).
353 \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.
354 \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.
357 Příklad, jak může být struktura \texttt{pci\_driver} inicializována a následně zaregistrována:
359 1 | static struct pci_driver mf624_pci_driver = {
361 3 | .id_table = mf624_pci_id,
362 4 | .probe = mf624_pci_probe,
363 5 | .remove = mf624_pci_remove,
365 pci_register_driver(&mf624_pci_driver);
368 \subsection{Struktura \texttt{struct pci\_device\_id}}
369 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ě:
372 1 | #define PCI_VENDOR_ID_HUMUSOFT 0x186c
373 2 | #define PCI_DEVICE_ID_MF624 0x0624
374 3 | #define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
375 4 | #define PCI_SUBDEVICE_DEVICE 0x0624
377 6 | static struct pci_device_id mf624_pci_id[] = {
379 8 | .vendor = PCI_VENDOR_ID_HUMUSOFT,
380 9 | .device = PCI_DEVICE_ID_MF624,
381 10 | .subvendor = PCI_SUBVENDOR_ID_HUMUSOFT,
382 11 | .subdevice = PCI_SUBDEVICE_DEVICE,
384 13 | { 0, } /* seznam je vždy zakončen prázdným prvkem */
389 \subsection{Funkce \texttt{probe()}}
390 \ibox{\texttt{int probe(struct pci\_dev *dev, const struct pci\_device\_id *id);}}
392 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í.
394 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č.
396 \ibox{\texttt{pci\_enable\_device(struct pci\_dev *dev);}}
398 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í.
400 \subsection{Přístup ke zdrojům karty}
401 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.
403 \ibox{\texttt{int pci\_request\_regions(struct pci\_dev *pdev, const char *res\_name);}}
405 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.
407 \ibox{\texttt{unsigned long pci\_resource\_start(struct pci\_dev *dev, int bar);}}
409 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).
411 \ibox{\texttt{unsigned long pci\_resource\_len(struct pci\_dev *dev, int bar);}}
413 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()}.
415 \ibox{\texttt{void \_\_iomem *pci\_ioremap\_bar(struct pci\_dev *pdev, int bar);}}
417 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()}.
419 K ukazateli, který vrátí volání \texttt{pci\_ioremap\_bar()} je již možné pomocí speciálních funkcí přistupovat.
421 \ibox{FIXME povídání o out-of-order execution a cache a proč musíme používat speciální funkce}
423 \subsection{Funkce \texttt{remove()}}
425 \ibox{\texttt{void remove(struct pci\_dev *dev);}}
427 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.
429 Tato funkce by se měla postarat o úklid všech naalokovaných prostředků apod. Měla by obsahovat volání:
431 \item[\texttt{iounmap()}]~\\Uvolnění virtuální paměti namapované voláním \texttt{pci\_ioremap\_bar()}.
432 \item[\texttt{pci\_release\_regions()}]~\\Uvolnění zdrojů karty, které byly zarezervovány voláním \texttt{pci\_request\_regions()}.
433 \item[\texttt{pci\_disable\_device()}]~\\Opak k volání \texttt{pci\_enable\_device()}.
438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439 \section{UIO ovladač}
440 V případě, že je vytvářen ovladač pro linuxové jádro, mělo by být rozhodnuto, kterého subsystému se stane součástí -- např. zda jde o jednoduché znakové zařízení, síťovou kartu nebo zvukovou kartu. Tato volba uřčí, kterou sadu pomocných funkcí bude moci ovladač používat a jakým způsobem bude zpřístupněno zařízení do uživatelského prostoru.
442 V případě, že jde o PCI zařízení, které nelze snadno zařadit do žádné kategorie (jedná-li se například o neobvyklou průmyslovou kartu), je možné použít tzv. UIO (\textit{Userspace I/O}) 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á).
444 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.
446 \subsection{Jaderný modul}
447 Jaderný modul UIO ovladače by měl obsahovat:
449 \item Funkci volanou PCI subsystémem při registraci ovladače
450 \item Volání funkcí pro namapování regionů zařízení
451 \item Inicializaci struktury \texttt{struct uio\_info} a registraci do UIO subsystému
452 \item Funkce pro \textit{úklid} a uvolnění regionů karty
455 Většina z těchto úkonů již byla popsána v kapitole \ref{pcich} a jsou zcela standardní pro jakýkoliv ovladač PCI zažízení, navíc je zde pouze \textit{registrace do UIO subsystému}.
457 \ibox{\texttt{int uio\_register\_device(struct device *parent, struct uio\_info *info);}}
459 Ta se provede zavoláním funkce \texttt{uio\_register\_device()}, které se jako první parametr předá \textit{rodič} struktury \texttt{struct pci\_dev} struktury -- tj. ukazatel na její položku \texttt{dev}.\footnote{FIXME; WTF is this?} Druhý parametr předá ukazatel na strukturu \texttt{struct uio\_info}.
461 \subsubsection{Struktura \texttt{struct uio\_info}}
462 Mezi její hlavní položky patří:
464 \item[\texttt{const char *name}]~\\Název ovladače
465 \item[\texttt{const char *version}]~\\Verze ovladače
466 \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
467 \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.
471 \subsubsection{Struktura \texttt{struct uio\_mem} a \texttt{struct uio\_port}}
472 Tyto struktury obsahují imformace o regionech zařízení. Které (a kolik) z těchto dvou struktur budou inicializovány záleží na tom, zda karta mapuje regiony do paměťového nebo vstupně-výstupního prostoru.
474 Struktura \texttt{struct uio\_mem} obsahuje položky:
476 \item[\texttt{const char *name}]~\\Textový popis daného regionu (viditelný z uživatelského prostoru).
477 \item[\texttt{unsigned long addr}]~\\Fyzická adresa regionu získaná voláním \texttt{pci\_resource\_start()}.
478 \item[\texttt{unsigned long size}]~\\Délka regionu. Nejsnáze získaná voláním \texttt{pci\_resource\_len()}.
479 \item[\texttt{int memtype}]~\\Typ paměti. Pro fyzickou paměť na zařízení se použije \texttt{UIO\_MEM\_PHYS}.
480 \item[\texttt{void \_\_iomem *internal\_addr}]~\\Virtuální adresa získaná voláním \texttt{pci\_ioremap\_bar()}
483 Struktura \texttt{struct uio\_port} obsahuje položky:
485 \item[\texttt{const char *name}]~\\Textový popis daného regionu (viditelný z uživatelského prostoru).
486 \item[\texttt{unsigned long start}]~\\Fyzická adresa regionu získaná voláním \texttt{pci\_resource\_start()}.
487 \item[\texttt{unsigned long size}]~\\Délka regionu. Nejsnáze získaná voláním \texttt{pci\_resource\_len()}.
488 \item[\texttt{int porttype}]~\\Typ portu. Pro porty na architektuře IA-32 se použije \texttt{UIO\_PORT\_X86}
493 Příklad, jak taková jednoduchá inicializace struktury \texttt{struct uio\_info} včetně registrace může vypadat (bez ošetření chybných stavů):
495 1 | /* struct pci_dev *dev */
496 2 | struct uio_info *info;
497 3 | info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
499 5 | info->name = "mf624";
500 6 | info->version = "0.0.1";
502 8 | info->mem[0].name = "PCI chipset, ...";
503 9 | info->mem[0].addr = pci_resource_start(dev, 0);
504 10 | info->mem[0].size = pci_resource_len(dev, 0);
505 11 | info->mem[0].memtype = UIO_MEM_PHYS;
506 12 | info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
508 14 | info->port[0].name = "Board programming registers";
509 15 | info->port[0].porttype = UIO_PORT_X86;
510 16 | info->port[0].start = pci_resource_start(dev, 1);
511 17 | info->port[0].size = pci_resource_len(dev, 1);
513 19 | uio_register_device(&dev->dev, info);
514 20 | pci_set_drvdata(dev, info);
517 \ibox{\texttt{void pci\_set\_drvdata(struct pci\_dev *pdev, void *data)}}
519 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}
521 \subsection{Program v uživatelském prostoru}
522 Poté, co je jaderná část UIO ovladače úspěšně zkompilována a zavedena do systému, je rozhraní mezi tímto modulem a uživatelským prostorem tvořeno:
524 \item souborem \texttt{/dev/uio0\footnote{Pro názornost v textu uvádím konkrétní zařízení \texttt{uio0}. V případě, že systém obsahuje více aktivních UIO ovladačů, jsou postupně číslovány od 0 výše.}}.
525 \item složkou \texttt{/sys/class/uio/uio0}, která obsahuje informace o regionech, které jsou zpřístupněny skrze UIO modul v jádře.
528 \subsubsection{Obsah složky \texttt{/sys/class/uio/uio0}}
529 Tato složka obsahuje soubory převážně pouze pro čtení. Obsahuje podložku \texttt{maps}, ve které se nachází pro každý region PCI zařízení mapovaný do paměti (zpřístupněný jaderným ovladačem) složka obsahující soubory popisující tyto regiony (Soubor \texttt{addr} obsahuje fyzickou adresu regionu; \texttt{name} slovní pojmenování; \texttt{size} velikost regionu).
531 \subsubsection{Soubor \texttt{/dev/uio0}}
532 Tento soubor je využíván k samotnému přístupu k regionům karty. K souboru se přistupuje pomocí volání \texttt{mmap()}.
534 \ibox{\texttt{void *mmap(void *addr, size\_t length, int prot, int flags, int fd, off\_t offset);}}
536 Tato funkce slouží k \textit{namapování} souboru nebo zařízení do operační paměti. V případě, že funkci zavoláme na soubor, proběhne-li vše správně, návratová hodnota bude obsahovat ukazatel do paměti, kam je možné přistu\-povat k obsahu souboru pomocí ukazatelové aritmetiky -- stejně, jako by to byla paměť.
539 \item[Parametr \texttt{addr}] -- v případě, že není nulový -- určí, na jakou adresu by měla být paměť mapována.
540 \item[Parametr \texttt{length}] udává velikost mapované paměti v násobcích velikosti paměťové stránky.
541 \item[Parametr \texttt{prot}] obsahuje příznaky definující, zda bude mapovaná paměť pro čtení/zápis, apod.
542 \item[Parametr \texttt{flags}] pomocí příznaku určuje, zda se mají změny zapisovat pouze do \textit{lokální kopie} (příznak \texttt{MAP\_PRIVATE}) nebo zda mají být zapisovány do původního souboru/zařízení (příznak \texttt{MAP\_SHARED}).
543 \item[Parametr \texttt{fd}] obsahuje \textit{filedescriptor} na zařízení, které má být namapováno (v tomto případě \texttt{/dev/uio0}).
544 \item[Parametr \texttt{offset}] určuje, zda se zaný soubor/zařízení začne mapovat od posunuté adresy. V případě UIO ovladače je možné jako offset používat násobky velikosti paměťové stránky -- tento offset určí, který z regionů zpřístupněných jadernou částí ovladače má být namapován.
547 Příklad, jak takové volání může vypadat (bez ošetření chybných stavů):
549 1 | #define BAR2_offset (1 * sysconf(_SC_PAGESIZE))
550 2 | void* mf624_BAR2 = NULL;
551 3 | int device_fd = open("/dev/uio0", O_RDWR);
553 5 | mf624_BAR2 = mmap(0, 1 * sysconf(_SC_PAGESIZE),
554 6 | PROT_READ | PROT_WRITE, MAP_SHARED,
555 7 | device_fd, BAR2_offset);
558 S adresou vrácenou voláním \texttt{mmap()} však není možné vždy ihned pracovat. Může se stát, že mapovaný region PCI zařízení (reprezentovaný zařízením \texttt{/dev/uio0}, na které je \texttt{mmap()} volán) je menší než je velikost celé stránky, \texttt{mmap()} však vrací ukazatel zarovnaný na velikost stránky. Je tedy potřeba se v rámci této stránky posunout na tu část, která odpovídá požadovanému regionu.
560 Jak velký je potřeba udělat \textit{posun} pomůže zjistit soubor \texttt{/sys/class/uio/ /ui0/maps/map1/addr}\footnote{Pro názornost uvádím konkrétní cestu -- jedná se tedy o \textit{druhý} paměťový region zažízení \textit{uio0}.} -- ten obsahuje fyzickou adresu požadovaného regionu. Z té je možné následujícím trikem získat ukazatel, se kterým je již možné pracovat (nejnižší bity totiž budou zachovány z fyzické adresy):
562 mf624_BAR2 += (BAR2_phys_addr & (sysconf(_SC_PAGESIZE) - 1));
564 \-- Ukazatel vrácený voláním mmap()
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 \section{Comedi ovladač}
568 UIO ovladač dává uživateli možnost využít 100 \% všech funkcí zařízení -- daní za to je pomalejší odezva než v případě plnohodnotného jaderného ovladače.
570 Pro ovladače měřících karet existuje v Linuxu subsystém -- tzv. Comedi (Control and Measurement Device Interface).
572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576 %\chapter{Qemu a Humusoft MF624}\label{qemu}
577 %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.