\documentclass[a4paper, 12pt]{report} 
\usepackage[hscale=0.75, vscale=0.75]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[czech]{babel}
\usepackage{indentfirst}
\usepackage{graphicx}
\usepackage{multicol}
\usepackage{amssymb,amsmath}
\usepackage{url}

\newcommand{\ibox}[1]{ \begin{center}\begin{small} \fbox{\parbox{14cm}{ #1 }} \end{small}\end{center}}

%opening
\title{\textsf{\begin{huge}Prostředí pro výuku vývoje PCI ovladačů do OS GNU/Linux\end{huge}\\
Fakulta elektrotechnická ČVUT}
\date{}}
\author{Rostislav Lisový (lisovy@gmail.com)}

\begin{document}

\maketitle
\newpage

\tableofcontents
\newpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{abstract}
Cílem této práce je vysvětlit základní principy komunikace ovladače operačního systému s hardwarem. Pro názornost jsou v práci popsány postupy implementace UIO a Comedi ovladače pro měřící kartu Humusoft MF624 pro operační systém GNU/Linux.

Součástí této práce je i implementace základních funkcí (DAC, ADC, DIO) karty Humusoft MF624 do emulátoru Qemu, aby si případný zájemce mohl popsané postupy vyzkoušet, aniž by vlastnil potřebný hardware.

Od čtenáře se očekává základní znalost používání operačního systému GNU/Linux, dobrá znalost programovacího jazyka C a základní znalost anglického jazyka.
\end{abstract}
\newpage

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Hardware}
\section{Komunikace s hardwarem}
Jak je možné ovládat hardware pomocí programu (software) je nejsnazší ukázat na příkladu jednočipového počítače.

\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: 
\begin{description}
\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.
\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.
\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).
\end{description}


%\begin{figure}[h!]
%	\begin{center}
%	\includegraphics[width=80mm]{img/msp430g2x01.png}
%	\caption{Architektura velmi jednoduchého mikroprocesoru MSP430G2x01 firmy Texas Instruments.}
%	\label{microcontroller}
%	\end{center}
%\end{figure}

}

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č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}\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}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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!]
	\begin{center}
	\begin{minipage}[b]{0.4\linewidth}
		\includegraphics[width=50mm]{img/pci_schema2.png}
	\end{minipage}
	\begin{minipage}[b]{0.4\linewidth}
		\includegraphics[width=50mm]{img/pci2.jpg}
	\end{minipage}
	\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ů}
	\label{pci}
	\end{center}
\end{figure}

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.

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.


\begin{figure}[h!]
	\begin{center}
	\includegraphics[width=100mm]{img/pci-config-space2.pdf}
	\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čů)}
	\label{sa1}
	\end{center}
\end{figure}

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í 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}

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.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Humusoft MF624}
\begin{figure}[h!]
	\begin{center}
	\includegraphics[width=150mm]{img/mf624.jpg}
	\caption{Měřící karta Humusoft MF624}
	\label{sa1}
	\end{center}
\end{figure}

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 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ž 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čů
\end{itemize}

\subsection{Komunikace s kartou}\label{hum_komunikace}
Komunikace s kartou probíhá celkem přímočarým způsobem:
\begin{itemize}
\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
\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}} manuál ke kartě.

Na straně 11 je k vidění první důležitá tabulka (zde tab. \ref{tab_bar}):

\begin{table}[h!]
	\begin{center}
	\begin{tabular}{|p{2cm}|p{6cm}|c|c|}
	\hline \textbf{Region} & \textbf{Function} & \textbf{Size (bytes)} & \textbf{Width (bytes)} \\ 
	\hline BADR0 (memory mapped) & PCI chipset, interrupts, status bits, special functions & 32 & 32 \\ 
	\hline BADR1 (memory mapped) & A/D, D/A, digital I/O & 128 & 16/32 \\ 
	\hline BADR2 (memory mapped) & Counter/timer chip & 128 & 32 \\ 
	\hline 
	\end{tabular} 
	\caption{Paměťové regiony, které PCI karta využívá}
	\label{tab_bar}
	\end{center}
\end{table}

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}.

\begin{table}[h!]
	\begin{center}
	\begin{tabular}{|c|c|c|}
	\hline \textbf{Address} & \textbf{Read} & \textbf{Write} \\ 
	\hline BADR1 + 0x00 & \textbf{ADDATA} -- A/D data & ADCTRL -- A/D control \\ 
	\hline BADR1 + 0x02 & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x04 & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x06 & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x08 & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x0A & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x0C & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x0E & \textbf{ADDATA} -- A/D data mirror &  \\ 
	\hline BADR1 + 0x10 & \textbf{DIN} -- Digital input & \textbf{DOUT} -- Digital output \\ 
	\hline BADR1 + 0x20 & \textbf{ADSTART} -- A/D SW trigger & \textbf{DA0} -- D/A 0 data \\ 
	\hline BADR1 + 0x22 &  & \textbf{DA1} -- D/A 1 data \\ 
	\hline BADR1 + 0x24 &  & \textbf{DA2} -- D/A 2 data \\ 
	\hline BADR1 + 0x26 &  & \textbf{DA3} -- D/A 3 data \\ 
	\hline BADR1 + 0x28 &  & \textbf{DA4} -- D/A 4 data \\ 
	\hline BADR1 + 0x2A &  & \textbf{DA5} -- D/A 5 data \\ 
	\hline BADR1 + 0x2C &  & \textbf{DA6} -- D/A 6 data \\ 
	\hline BADR1 + 0x2E &  & \textbf{DA7} -- D/A 7 data \\ 
	\hline 
	\end{tabular} 
	\caption{Registry karty obsažené v regionu BAR1}
	\label{tab_bar1}
	\end{center}
\end{table}

Asi uprostřed tabulky je řádek, ve kterém je napsáno \textit{DIN -- Digital input, DOUT -- Digital output} -- ten 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). 

Abychom přesně věděli, jak jsou data v registrech reprezentována, podíváme se (v oficiálním manuálu) na stranu 16, kde jsou tyto dva registry podrobně popsány (zde tabulka \ref{tab_din} a \ref{tab_dout}). První sloupec nám 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, tedy vidíme, ž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.

\begin{table}[h!]
	\begin{center}
	\begin{tabular}{|c|c|c|}
	\hline \textbf{Bit} & \textbf{Description} & \textbf{Default} \\ 
	\hline 7:0 & \textbf{Digital input 7:0.} Reads digital input port. & 1 \\ 
	\hline 15:8 & Reserved & N/A \\ 
	\hline 
	\end{tabular} 
	\caption{DIN -- Digital Input Register Format}
	\label{tab_din}
	\end{center}
\end{table}

\begin{table}[h!]
	\begin{center}
	\begin{tabular}{|c|c|c|}
	\hline \textbf{Bit} & \textbf{Description} & \textbf{Default} \\ 
	\hline 7:0 & \textbf{Digital output 7:0.} Writes to digital output port. & 0 \\ 
	\hline 15:8 & Reserved & N/A \\ 
	\hline 
	\end{tabular} 
	\caption{DOUT -- Digital Output Register Format}
	\label{tab_dout}
	\end{center}
\end{table}


\subsection{A/D převodníky}
TODO

Přerušení?
\subsection{D/A převodníky}
TODO



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Implementace ovladačů}
\section{Operační systém GNU/Linux}
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.

\subsection{Principy psaní jaderných modulů}
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.

Jak se takový jaderný modul může vypadat, je nejlepší si ukázat na příkladu:
\begin{verbatim}
 1 |  #include <linux/init.h>
 2 |  #include <linux/module.h>
 3 | 
 4 |  static int hello_init(void)
 5 |  {
 6 |      printk("Hello, world!\n");
 7 |      return 0;
 8 |  }
 9 | 
10 |  static void hello_exit(void)
11 |  {
12 |      printk("Goodbye, cruel world!\n");
13 |  }
14 | 
15 |  module_init(hello_init);
16 |  module_exit(hello_exit);
17 |  
18 |  MODULE_LICENSE("Dual BSD/GPL");
\end{verbatim}

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).
\begin{description}
\item[Řádky 1 a 2] obsahují vložení hlavičkových souborů -- obsahují prototypy námi volaných funkcí a jsou nutné pro tvorbu jaderného modulu.
\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()}.

Pro jednoduchost můžeme s funkcí \texttt{printk()} pracovat jako s pro nás 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$
\item[řádku 15] -- ten obsahuje makro \texttt{module\_init()}, kterému řekneme právě to, která funkce se má po načtení spustit.
\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$
\item[řádcích 10--13.] Tato funkce nemá na starost nic jiného než výpis krátkého textu do logu jádra.
\end{description}

\subsection{Kompilace modulu}
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
3 |
4 |  all:
5 |      make -C /lib/modules/$(KERNEL_VER)/build M=$(PWD) modules
6 |  clean:
7 |      make -C /lib/modules/$(KERNEL_VER)/build M=$(PWD) clean
\end{verbatim}
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\_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}

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:
\begin{verbatim}
$ make
make -C /lib/modules/`uname -r`/build M=/tmp/kernel_module_example modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.35-28-generic'
  CC [M]  /tmp/kernel_module_example/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/hello.mod.o
  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 se nachází kromě různých souborů, které vzniky při překladu, i potřebný \texttt{hello.ko}

\begin{verbatim}
$ ls
hello.c  hello.ko  hello.mod.c  hello.mod.o  hello.o  
Makefile  modules.order  Module.symvers
\end{verbatim}

\subsection{Zavedení modulu}
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 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 text vypsaný zavedeným modulem.

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 

$ dmesg | tail -1
[ 9612.256929] Goodbye, cruel world!
\end{verbatim}
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.
\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);}}

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()}.

\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()}.

K ukazateli, který vrátí volání \texttt{pci\_ioremap\_bar()} je již možné pomocí speciálních funkcí přistupovat.

\ibox{FIXME povídání o out-of-order execution a cache a proč musíme používat speciální funkce}

\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 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}
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}\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}
