From 42cbeb39988df2958fb21020c06589cb2eb0e400 Mon Sep 17 00:00:00 2001
From: "Hans J. Koch" <hjk@linutronix.de>
Date: Mon, 22 Jun 2009 19:38:50 +0200
Subject: Updated English presentation and German handout in
 kernel-devel/uio-driver

---
 kernel-devel/uio-driver/handout_uio-driver_de.tex | 597 +++++++++++++++++++++-
 1 file changed, 591 insertions(+), 6 deletions(-)

(limited to 'kernel-devel/uio-driver/handout_uio-driver_de.tex')

diff --git a/kernel-devel/uio-driver/handout_uio-driver_de.tex b/kernel-devel/uio-driver/handout_uio-driver_de.tex
index 4c391b2..546c7fe 100644
--- a/kernel-devel/uio-driver/handout_uio-driver_de.tex
+++ b/kernel-devel/uio-driver/handout_uio-driver_de.tex
@@ -1,17 +1,602 @@
-\documentclass{article}
+\documentclass{lxarticle}
 \usepackage{german}
 \usepackage[utf8]{inputenc}
+\usepackage{lxheaders}
+\usepackage{lxextras}
 
 \begin{document}
 
-\section*{Titel}
+\section*{Linux-Treiber im Userspace}
 
-\subsection*{Abschnitt1}
+Das ab Linux 2.6.23 im stabilen Kernel verfügbare Userspace-I/O-Framework
+(UIO) ermöglicht, bestimmte Treiber fast vollständig in den Userspace zu
+verlagern. Dies vereinfacht die Entwicklung und Wartung von Treibern,
+außerdem ergeben sich Möglichkeiten, die besonders Industriebetrieben
+entgegen kommen.
 
-Text
+Eine im Automatisierungsbereich tätige Firma hat eine PCI-I/O-Karte
+entwickelt. Sie soll in einer unter Linux laufenden Maschinensteuerung
+eingesetzt werden. Ein Linux-Kerneltreiber muss her. Kein Problem, denkt
+man sich. Immerhin stehen sowohl der Kernel-Sourcecode als auch die nötigen
+Entwicklungstools frei zur Verfügung. So oder so ähnlich hat schon so manche
+Linux-Horror-Story begonnen.
 
-\subsection*{Abschnitt2}
+Trotz benutzerfreundlicher Kernel-Subsysteme und Unterstützung durch
+Mailinglisten bedeutet das Schreiben eines Kerneltreibers für Linux-Einsteiger
+eine nicht zu unterschätzende Einarbeitungszeit. Industrielle I/O-Hardware ist
+oft so speziell, dass die Subsysteme des Kernels dem Programmierer gerade einmal
+das Erkennen der PCI-Karte abnehmen können. Gleichzeitig verfügen solche Karten
+oft über sehr komplexe Funktionen, so dass der Treiber immer umfangreicher wird.
+Treibercode mit mehreren tausend Codezeilen ist keine Seltenheit.
 
-Text
+Doch auch wenn diese Hürde genommen, der Treiber erfolgreich debugged und
+getestet wurde, gibt es ein noch größeres Problem: Die Wartung des Treibers.
+Denn ein so großer Treiber verwendet zweifellos eine große Zahl interner
+Kernel-Funktionen und -Makros. Dieses Kernel-interne API ist stets in Bewegung.
+Wer einen Treiber für 2.6.30 geschrieben hat, hat keinerlei Garantie, dass der
+unter 2.6.31 noch fehlerfrei kompiliert. Wer seinen Code mehrere Kernel-Versionen
+lang liegen lässt und dann upgraden will, hat höchst wahrscheinlich ein paar
+unerfreuliche Tage vor sich.
+
+Das zuletzt genannte Problem löst sich in Luft auf, wenn es gelingt, die
+Kernel-Entwickler, an der Spitze Linus Torvalds, dazu zu bewegen, den Code in
+den stabilen Mainline-Kernel aufzunehmen. Denn dann sind die Entwickler, die
+etwas am internen API ändern, verpflichtet, für die Anpassung aller betroffenen
+Module zu sorgen. Für unsere Automatisierungsfirma würde das bedeuten, dass die
+Linux-Community ihnen gratis diese Wartungsarbeit abnehmen würde. Sie und ihre
+Kunden könnten immer die neueste Kernelversion verwenden und würden ihren Treiber
+kompilierfähig vorfinden.
+
+Leider ist dies aus mehreren Gründen oft nicht machbar. Erstens werden die
+Kernel-Entwickler die Aufnahme eines großen Treibers, der nicht von allgemeinem
+Interesse ist, schlicht verweigern. Der Umfang des Kernelcodes würde sonst ins
+Unermessliche wachsen. Zweitens geht der Aufnahme von Code in den Mainline-Kernel
+gewöhnlich eine langwierige Review-Prozedur voraus. Dabei wird nicht nur
+Coding-Style begutachtet, es kann sich durchaus auch um Kritik an grundlegenden
+Vorgehensweisen handeln. Erst wenn alle Kritiker zufrieden gestellt sind, wird
+der Code in den Kernel aufgenommen. Darüber können Wochen vergehen. Die wenigsten
+Industriebetriebe genehmigen ihren Programmierern diesen Zusatzaufwand, da der
+Treiber in ihrer Maschine ja schon läuft.
+
+Drittens haben manche Firmen Probleme mit der GPL. Code, der im Kernel
+einkompiliert wird, muss dieser Lizenz genügen, das heißt, der Sourcecode muss
+öffentlich zugänglich sein. Firmen haben in der Vergangenheit zahlreiche
+(meist illegale) Tricks angewandt, um dieser Bedingung der GPL zu entgehen und
+ihren Kernelcode unter Verschluss zu halten.
+
+\subsection*{UIO löst Probleme}
+
+Die geschilderte Problematik führte bereits vor längerer Zeit zu Überlegungen,
+zumindest Teile eines Treibers in den Userspace zu verlagern. I/O-Hardware
+besteht in der Regel aus einem Speicherbereich, in dem sich RAM oder Register
+befinden. Linux ist von Haus aus in der Lage, einen solchen Speicherbereich per
+\cmd{mmap()} in den Userspace zu mappen. Damit kann man bereits alle Register
+der Hardware erreichen. Dies ist aber erst die halbe Miete: In der Regel
+erzeugen I/O-Karten Interrupts, und diese lassen sich nur im Kernel bearbeiten.
+Ausserdem muss man in einem allgemein verwendbaren System der Tatsache
+Rechnung tragen, dass eventuell mehrere (gleiche oder verschiedene) I/O-Karten
+im System vorhanden sein können.
+
+Das Userspace-I/O-Framework (UIO) löst diese Probleme einfach und elegant.
+Das für den Treiber immer noch benötigte Kernelmodul beschränkt sich auf einen
+minimalen Interrupt-Handler sowie das Anmelden des Treibers beim
+UIO-Framework. Letzteres erzeugt ein Device-File unter /dev sowie einige
+dokumentierte Standardfiles im sysfs unter /sys/class/uio. Ausserdem stellt das
+UIO-Framework bereits die für den Zugriff auf das Device-File benötigten
+Funktionen wie \cmd{open()}, \cmd{read()} und \cmd{mmap()} zur Verfügung. Im
+kundenspezifischen Kerneltreiber wird lediglich eine Struktur vom Typ
+\cmd{struct uio\_info} mit einigen Informationen gefüllt und damit die Funktion
+\cmd{uio\_register\_device()} aufgerufen. Den Rest erledigt das Framework.
+Durch dieses Design kann das hardwarespezifische Kernelmodul sehr klein
+ausfallen: das seit Kernel 2.6.23 enthaltene Modul für eine PCI-Feldbuskarte
+(Hilscher CIF) besteht aus gerade einmal 156 Zeilen, inklusive Leerzeilen und
+Kommentaren. Durch die sehr überschaubare Codegröße ist ein solches Modul schnell
+geschrieben, übersichtlich, wenig fehleranfällig, leicht wartbar und kann
+wesentlich leichter in den Mainline-Kernel integriert werden. Der Review-Prozess
+ist viel einfacher, da auch Kernelentwickler, die nicht über die passende
+Hardware verfügen, das Modul bezüglich Fehlerfreiheit beurteilen können.
+
+Nach dem Laden des Kernelmoduls kann man vom Userspace aus das Device-File
+(z.B. \cmd{/dev/uio0}) öffnen und sich per \cmd{mmap()}-Aufruf Zugriff auf
+den Speicher der Hardware verschaffen. Man wird zunächst durch geeignetes
+Beschreiben von Registern die Hardware initialisieren, so dass sie die
+gewünschte Funktion ausführt und Interrupts generiert. Auf Interrupts wartet
+man, indem man mit einem blockierenden \cmd{read()}-Aufruf einen 32-bit-Wert
+aus dem Device-File liest. Der Aufruf kehrt zurück, sobald ein Interrupt
+eintrifft. Der gelesene 32-bit-Wert gibt die Gesamtzahl der bisher
+aufgetretenen Interrupts an. Der Wert sollte idealerweise um eins größer
+sein als beim letzten Mal. Ist er noch größer, so hat man Interrupts verpasst.
+
+Man beachte, dass der Interrupt-Handler im Kernelmodul den Interrupt bereits
+quittiert haben muss. Im Fall eines pegelgetriggerten Interrupts, wie er bei
+PCI-Karten üblich ist, würde sich der Rechner sonst sang- und klanglos
+aufhängen. Bei Rückkehr des blockierenden \cmd{read()}-Aufrufs kann man sich
+also darauf beschränken, Daten zu lesen und zu schreiben, Statusregister zu
+lesen, neue Parameter zu setzen oder ähnliche Aufgaben, die beim Auftreten
+eines Interrupts anfallen. Falls die entsprechende Routine damit bis zum
+Eintreffen des nächsten Interrupts nicht fertig wird, so ist das zumindest
+softwaretechnisch kein Problem: Anhand des gelesenen Werts beim nächsten
+\cmd{read()} erkennen Sie, wie viele Interrupts Sie verpasst haben.
+
+\subsection*{GPL oder nicht GPL}
+
+Der Linux-Kernel einschließlich aller enthaltenen Module ist unter der GPL
+lizensiert. Programme im Linux-Userspace unterliegen solchen Einschränkungen
+nicht. Das gilt auch für UIO-Treiber. Mit UIO existiert daher erstmals eine
+Kernel-Schnittstelle, die es erlaubt, einen interruptfähigen Treiber zu
+schreiben, der größtenteils nicht der GPL unterliegt (das kleine Kernelmodul
+muss natürlich immer noch GPL-lizensiert werden). Diese Tatsache hat für
+einigen Wirbel in der Linux-Szene gesorgt. Das UIO-Framework wurde von
+Mitarbeitern der Firma Linutronix entwickelt und an Greg Kroah-Hartman,
+den Maintainer des Linux driver core, weiter gereicht. Kroah-Hartman
+versuchte am 13.12.2006 erstmals, UIO im Mainline-Kernel unterzubringen.
+Linus Torvalds lehnte die Aufnahme damals ab, hauptsächlich aufgrund von
+Missverständnissen bezüglich eines beigefügten Demo-Treibers. In der Folge
+entstand eine wochenlange Diskussion, die schnell vom eigentlichen Thema
+abwich und sich hauptsächlich mit der Lizensierungs-Problematik beschäftigte.
+
+Dabei lag es in erster Linie gar nicht in der Absicht der UIO-Entwickler,
+proprietäre Treiber zu ermöglichen. Dies ist eigentlich ein Nebeneffekt, der
+sich aus der Tatsache ergibt, dass man für Userspace-Software unter Linux seit
+eh und je die Lizenz frei wählen darf. Dennoch mag dieser Punkt in einigen
+Firmen für Erleichterung sorgen. Wer etwa bestimmtes Know-How in einem FPGA
+untergebracht hat, der möchte nicht unter dem Zwang stehen, den Code des
+zugehörigen Treibers öffentlich zu machen. Manchmal kann dies sogar durch den
+Endkunden vertraglich untersagt sein, zum Beispiel bei Code, der für die
+Einhaltung gesetzlich vorgeschriebener Werte sorgt, bei militärischen oder
+sonst sicherheitsrelevanten Anwendungen.
+
+Bisher waren solche Firmen gezwungen, Codeänderungen durch technische Klimmzüge
+zu verhindern, bewusst gegen die GPL zu verstoßen oder ganz auf Linux zu
+verzichten. Durch UIO wird an dieser Stelle ein gangbarer und legaler Ausweg
+geschaffen.
+
+\subsection*{Konventionelle Treiber...}
+
+Bisherige Gerätetreiber sind vollständig im Kernel integriert. Die
+Schnittstelle zum Userspace besteht typischerweise aus einer Gerätedatei, auf
+die mit den üblichen Funktionen wie \cmd{open()}, \cmd{close()}, \cmd{read()},
+\cmd{write()} und \cmd{ioctl()} zugegriffen werden kann.
+
+\begin{figure}[h]
+\centering
+\includegraphics[width=0.8\textwidth]{images/konventioneller-treiber_de.png}
+\caption{Aufbau eines konventionellen Treibers}
+\label{img:konvtreiber}
+\end{figure}
+
+
+
+Alle diese Funktionen
+müssen vollständig im Treiber implementiert werden und führen je nach Gerät
+mehr oder weniger komplexe Operationen mit der Hardware aus. Bei industriellen
+I/O-Karten liegt meist ein Schwerpunkt auf der \cmd{ioctl()}-Funktion. Die Ein-
+und Ausgabe-Funktionaltät der Karte ist oft so komplex, dass man sie nicht auf
+einfache \cmd{read()}- und \cmd{write()}-Operationen abbilden kann. Ausserdem
+haben solche Karten in der Regel zahlreiche Parameter und Optionen, die auf
+einen bestimmten Wert gesetzt oder ein- und ausgeschaltet werden wollen.
+Man findet daher häufig Treiber für derartige Industrie-Karten, die 50 oder
+mehr ioctl-Unterfunktionen implementieren.
+
+\begin{figure}[h]
+\centering
+\includegraphics[width=0.8\textwidth]{images/ioctl-vs-uio_de.png}
+\caption{Vergleich von \cmd{ioctl()}-basierten Sytemen mit UIO}
+\label{img:ioctl}
+\end{figure}
+
+Dies führt oft dazu, dass letzlich mehr Datenaustausch mit der Karte per
+\cmd{ioctl()} abgewickelt wird als über die eigentlich dafür vorgesehenen
+\cmd{read()}- und \cmd{write()}-Funktionen. Auch aus Performance-Gründen ist
+diese Vorgehensweise alles Andere als optimal. Wenn beispielsweise
+\cmd{ioctl()} dazu verwendet wird, einen 32-Bit-Wert in ein Register der
+Hardware zu schreiben, dann packt das Anwenderprogramm im Userspace zunächst
+den Wert auf den Stack und ruft über die libc \cmd{ioctl()} auf. Per syscall
+landet der Aufruf im Kernel. Dort sucht das Virtual File System (VFS) den
+Pointer auf die für das jeweilige Gerät zuständige \cmd{ioctl()}-Funktion und
+ruft diese auf. Da die übergebenen Parameter im User-Speicher liegen, muss sie
+die \cmd{ioctl()}-Implementierung zunächst mit Hilfe der Kernel-Funktion
+\cmd{copy\_from\_user()} in den Kernel-Speicher holen. Jetzt endlich kann der
+übergebene Wert in das Hardware-Register geschrieben werden. Falls der Aufruf
+auch Werte zurückgeben soll, müssen diese entsprechend erst mit
+\cmd{copy\_to\_user()} in den übergebenen Puffer geschrieben werden.
+
+Letztlich handelt es sich bei einem derartigen Konzept um einen Missbrauch von
+\cmd{ioctl()}. Die Funktion war ursprünglich nur dazu gedacht, gelegentlich
+einen Parameter einer (Geräte-)Datei zu ändern. Für häufigen, schnellen
+Datenaustausch ist sie denkbar ungeeignet und vor allem auch unnötig, wie wir
+weiter unten sehen werden. Die Kernel-Entwickler stehen daher einer
+\cmd{ioctl()}-basierten Treiberimplementierung äußerst kritisch gegenüber.
+Ein Treiber für eine exotische Karte, der 50 neue \cmd{ioctl()}-Funktionen
+einführen will, wird wahrscheinlich schon aus diesem Grund nicht in den
+Mainline-Kernel aufgenommen.
+
+Ein weiteres Problem ergibt sich aus der Größe des Treibercodes. Ein paar
+tausend (nicht-triviale) Zeilen sind schnell beieinander. Obwohl auch für die
+Kernel-Entwicklung zahlreiche mächtige Hilfsmittel zur Fehlersuche existieren,
+erfordert das Debugging doch erheblich mehr an Spezialwissen als im Userspace.
+In einem Kerneltreiber können einfache Fehler den Rechner so lahm legen, dass
+nur noch der Griff zum Resettaster Rettung bringt. In einem Industriebetrieb
+verfügen häufig auch die ansonsten sehr qualifizierten Programmierer nur über
+wenig Erfahrung mit Linux-Kernelprogrammierung. Mit den üblicherweise recht
+eingeschränkten Ressourcen an Zeit und Personal entstehen dann Treiber, die
+ebenso fehlerträchtig wie unübersichtlich sind und keinerlei Chance auf
+Aufnahme in den offiziellen Mainline-Kernel haben. Dadurch haben die
+Programmierer dann während der gesamten Laufzeit des Produkts zusätzlich das
+Problem, diesen wackligen Treiber out-of-tree warten zu müssen. 
+
+\subsection*{...und UIO-Treiber}
+
+Sieht man sich Gerätetreiber etwas genauer an, so stellt man fest, dass es
+stets wiederkehrende Aufgabenstellungen gibt. Zum einen hat die Hardware
+einen oder mehrere Speicherbereiche, auf die man zugreifen möchte. Zum
+anderen erzeugt die Hardware Interrupts, auf die reagiert werden muss. Das
+UIO-Framework enthält den Code für diese Standardaufgaben und vermeidet so,
+dass Programmierer ständig ein nicht ganz einfaches Rad neu erfinden müssen.
+
+\begin{figure}[h]
+\centering
+\includegraphics[width=0.8\textwidth]{images/uio-treiber_de.png}
+\caption{Aufbau eines konventionellen Treibers}
+\label{img:konvtreiber}
+\end{figure}
+
+Um Speicherbereiche verwenden zu können, muss man dem UIO-Framework lediglich
+deren Startadresse und Länge angeben. UIO stellt dann bereits eine
+\cmd{mmap()}-Funktion zur Verfügung, die es erlaubt, die Speicherbereiche
+in den Userspace zu mappen. Im Falle einer PCI-Karte erhalten Sie die
+Informationen über Startadresse und Länge bereits vom PCI-Subsystem.
+
+Zur Behandlung von Interrupts muss lediglich ein minimaler Interrupt-Handler
+geschrieben werden. Um Interrupt-Sharing zu ermöglichen, sollte der Handler
+erkennen, ob es die eigene Karte war, die den Interrupt ausgelöst hat. Falls
+nein, gibt er einfach IRQ\_NONE zurück. Falls ja, wird er den Interrupt bei
+der Hardware quittieren und dafür sorgen, dass die Interrupt-Leitung wieder
+ihren inaktiven Zustand annimmt. Auch hier muss man lediglich die Nummer des
+Interrupts und einen Pointer auf den Handler angeben, den Rest erledigt das
+Framework.
+
+Zu den weiteren Dienstleistungen des UIO-Frameworks gehört das Anlegen und
+Behandeln von Dateien im sysfs. Unter \cmd{/sys/class/uio} finden sich
+Informationen zu allen angemeldeten UIO-Geräten. Dazu gehören der Name des
+Geräts, Version seines Kernelmoduls, Adressen und Größen seiner
+Speicherbereiche, Anzahl aufgetretener Interrupts und Ähnliches. Diese
+(dokumentierten) sysfs-Dateien ermöglichen es dem Userspace-Teil des Treibers,
+gezielt nach seinem Gerät zu suchen und beispielsweise die Größen der
+Speicherbereiche korrekt zu erfragen, anstatt sich auf fest kodierte Werte
+zu verlassen.
+
+Insgesamt enthält das UIO-Framework etwa 900 Zeilen nicht-trivialen Code, die
+man für eine ähliche Funktionalität sonst selbst schreiben müsste. Dagegen
+kommt das zu erstellende gerätespezifische Modul auf etwa 150 Zeilen Code,
+der zudem recht einfach ist und leicht von schon existierenden Treibern
+abgeleitet werden kann. Dadurch haben auch weniger erfahrene Programmierer
+die Chance, ein sauberes, übersichtliches und stabiles Modul für ihre Hardware
+zu produzieren. Es bleibt auch jederzeit die Möglichkeit, sich dieses Modul
+von einem erfahrenen Dienstleister erstellen zu lassen. Auf diese Weise ist es
+möglich, eigene Treiber zu entwickeln, ohne eine einzige Zeile Kernel-Code
+schreiben zu müssen.
+
+Der ganze Rest, also die komplette eigentliche Funktionalität des Treibers,
+wird im Userspace erledigt. Der Treiber kann als Shared Library, als
+Bestandteil eines Anwenderprogramms oder in bester Unix-Manier als Dämon
+realisiert werden. Die Programmierer des Treibers benötigen nicht mehr
+Kenntnisse und Tools, als sie zur Programmierung ihrer Applikation ohnehin
+benötigen.
+
+Im Beispiel oben haben wir gesehen, welchen Aufwand es bedeutet, einen
+einzelnen Wert per \cmd{ioctl()} in ein Hardware-Register zu befördern. Im
+Falle von UIO ist diese Operation eine einfache Zuweisung an die betreffende
+Stelle des gemappten Speichers. Wenn \cmd{a} der Zeiger auf den Speicher ist
+und an die relative Adresse 1000 der Wert 5 geschrieben werden soll, so
+schreibt man ohne Umschweife \cmd{a[1000]=5}. Jegliches Hin- und Herkopieren
+entfällt, so dass sich bei schnellen Anwendungen ein echter Performance-Vorteil
+gegenüber einem Kerneltreiber mit \cmd{ioctl()}-Konzept ergibt. Es ist also
+keinesfalls so, dass man für den Userspace-Komfort Geschwindigkeits-Nachteile
+in Kauf nehmen müsste. 
+
+Was sich ebenfalls verbessert, ist das Handling von Updates. Bei einem reinen
+Kernel-Treiber ist ein Update des Kernels oder zumindest der Module nötig, was
+in der Regel root-Rechte des Anwenders erfordert und bei Fehlern zu einem
+völlig unbrauchbaren System führt. Bei einem UIO-Treiber unterscheidet sich
+ein Update des Treibers durch nichts von einem Update der Anwender-Applikation.
+
+\subsection*{Wartungsvertrag}
+
+Ein Treiber will gewartet sein. Bei industriellen Geräten findet man oft die
+Situation, dass ein Gerät über einen längeren Zeitraum mit der selben
+Kernelversion ausgeliefert wird. Möchte man später, etwa nach 6 Monaten oder
+einem Jahr, beispielsweise im Zuge eines allgemeinen Software-Updates auf einen
+aktuellen Kernel wechseln, so sind oft deutliche Änderungen am Kerneltreiber
+nötig. 
+
+Konventionelle Treiber beinhalten zwangsläufig viele Aufrufe von internen
+Kernelfunktionen. Im Gegensatz zur stabilen syscall-Schnittstelle kann sich
+dieses interne Kernel-API jederzeit ändern. Sie müssen damit rechnen, dass
+Funktionen, die ihr Treiber verwendet, plötzlich nicht mehr existieren oder
+andere Parameter haben. Entsprechend hoch ist der Wartungsaufwand für einen
+solchen Treiber, wenn er nicht im Mainline-Kernel enthalten ist.
+
+UIO macht ihnen auch hier das Leben leichter. Das Framework übernimmt die
+üble Kleinarbeit, die etwa beim Umgang mit sysfs, Interrupt-Handling oder
+Memory-Mapping anfällt. Es schirmt sie sozusagen von den wilden Seiten des
+Kernels ab, indem es ihnen erlaubt, mit etwa einem halben Dutzend einfacher
+Funktionen auszukommen. Angesichts der geringen Codegröße eines
+UIO-Kernelmoduls sind selbst bei Änderungen an diesen Funktionen die
+Wartungsarbeiten sehr überschaubar und schnell ausgeführt.
+
+Betrachtet man als Beispiel den Treiber für die Hilscher CIF-Karte
+\cmd{(drivers/uio/uio\_cif.c)}, dann fällt auf, dass für die Einbindung in das
+UIO-Framework lediglich zwei Funktionen (\cmd{uio\_register\_device} und
+\cmd{uio\_unregister\_device}) sowie eine Datenstruktur
+(\cmd{struct uio\_info}) benötigt werden. Ansonsten werden lediglich 7
+Funktionen des PCI-Subsystems sowie 6 sonstige Funktionen wie \cmd{kzalloc()}
+benutzt. Alle diese Funktionen sind einfach und gängig. Das sie sich ändern,
+ist möglich, aber nicht sehr wahrscheinlich.
+
+Dennoch sollten sie den Kernelteil ihres Treibers möglichst nicht ausserhalb
+des offiziellen Mainline-Kernels pflegen wollen. Schreiben sie sauberen Code
+entsprechend dem Kernel-Coding-Style und zögern sie nicht, auf der
+Linux-Kernel-Mailingliste um die Aufnahme desselben zu bitten. Die Mühe lohnt
+sich: Zukünftige Kernel haben die Unterstützung für ihr Produkt schon
+eingebaut, Versionskonflikte oder das mühsame Kernel-Patchen entfällt. 
+
+\subsection*{Los geht's...}
+
+Wie sieht nun ein UIO-Treiber konkret aus? Betrachten wir dazu einen Treiber
+für eine PCI-Karte. Die folgenden Beispiele sind Auszüge aus dem bereits
+erwähnten Treiber für die Hilscher CIF Feldbuskarte. Er befindet sich seit
+2.6.23-rc1 im Mainline-Kernel.
+
+Der Treiber unterscheidet sich zunächst nur wenig von einem normalen
+PCI-Kartentreiber. Anhand einer Tabelle mit PCI-ID-Nummern wird dem
+PCI-Subsystem mitgeteilt, für welche Karten sich der Treiber zuständig fühlt.
+Wird beim Booten eine solche Karte gefunden, so lädt das PCI-Subsystem den
+Treiber und ruft dessen probe-Funktion auf. Diese muss Karte und
+Treiber-Variablen initialisieren und das Ergebnis dieser Bemühungen
+zurückmelden. Konnten Treiber und Karte erfolgreich initialisiert werden, so
+haben sie dem System ein betriebsbereites PCI-Device hinzugefügt.
+
+Bei einem UIO-Treiber besteht ihre Hauptarbeit darin, eine Struktur vom Typ
+\cmd{struct uio\_info} zu erzeugen und auszufüllen. Beginnen wir mit Name und
+Version ihres Treibers. Dies sind beliebige strings, die später über
+sysfs-Dateien abgefragt werden können:
+
+\begin{lstlisting}
+/* Struktur anlegen */
+struct uio_info *info;
+info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+/* Name und Version setzen */
+info->name = "Meine UIO Karte";
+info->version = "0.0.1";
+\end{lstlisting}
+
+Als Nächstes kann der von der Karte verwendete Interrupt eingetragen werden.
+Freundlicherweise übergibt der Kernel der probe-Funktion einen Pointer
+(hier \emph{dev} genannt) auf eine Struktur vom Typ \cmd{struct pci\_dev}, in der
+derartige Angaben schon vom PCI-Subsystem eingetragen wurden. Unsere Aufgabe
+ist also leicht zu erfüllen:
+
+\begin{lstlisting}
+info->irq = dev->irq;
+\end{lstlisting}
+
+Ausserdem kann man dem UIO-Framework mitteilen, dass man einen
+"Fast"-Interrupthandler möchte und für Interrupt-Sharing vorbereitet ist:
+
+\begin{lstlisting}
+info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
+\end{lstlisting}
+
+Die Adresse des (noch zu erstellenden) Interrupt-Handlers darf natürlich
+nicht fehlen:
+
+\begin{lstlisting}
+info->handler = mein_irq_handler;
+\end{lstlisting}
+
+Jetzt wäre noch der Speicher der PCI-Karte anzugeben. Nehmen wir an, die Karte
+habe nur einen solchen Bereich. Wir erfahren Adresse und Größe des Bereichs
+wieder mit Hilfe des Pointers \emph{dev}. Zusätzlich geben wir an, dass es sich um
+physikalischen Speicher handelt:
+
+\begin{lstlisting}
+info->mem[0].addr = pci_resource_start(dev, 0);
+info->mem[0].size = pci_resource_len(dev, 0);
+info->mem[0].memtype = UIO_MEM_PHYS;
+\end{lstlisting}
+
+Die hier eingetragene physikalische Adresse kann nicht direkt für Zugriffe
+verwendet werden. Sie wird vom UIO-Framework beim \cmd{mmap()}-Aufruf in den
+Userspace gemappt. Allerdings wird man sicherlich auch in der Interrupt-Routine
+Zugriff auf den Speicher benötigen. Dazu mappen wir die Adresse in einen vom
+Kernel verwendbaren Bereich:
+
+\begin{lstlisting}
+info->mem[0].internal_addr = ioremap(pci_resource_start(dev, 0),
+                                     pci_resource_len(dev, 0) );
+\end{lstlisting}
+
+Damit haben wir unserer Pflicht Genüge getan und können unser Device beim
+UIO-Framework registrieren:
+
+\begin{lstlisting}
+uio_register_device(&dev->dev, info);
+\end{lstlisting}
+
+Das war's. Ein neues UIO-Device hat das Licht der Welt erblickt. Es gibt jetzt
+eine Gerätedatei \cmd{/dev/uio0} sowie verschiedene Dateien im Verzeichnis
+\cmd{/sys/class/uio/uio0/}, die die in \cmd{struct uio\_info} enthaltenen Daten
+wiedergeben. Natürlich wird man in der Realität dem obigen Beispielcode noch
+Fehlerüberprüfungen hinzufügen, dies wurde hier der Übersichtlichkeit halber
+weggelassen.
+
+Damit das Ganze funktioniert, muss natürlich noch der oben angegebene
+Interrupt-Handler implementiert werden. Sein Gerüst sieht so aus:
+
+\begin{lstlisting}
+static irqreturn_t mein_irq_handler(int irq, struct uio_info *dev_info)
+{
+  void *ptr = dev_info->mem[0].internal_addr;
+
+  if (Interrupt_kam_nicht_von_meiner_Karte)
+    return IRQ_NONE;
+\end{lstlisting}
+
+  Hier sind Zugriffe mit Hilfe von ioread/iowrite möglich:
+
+\begin{lstlisting}
+  ein_byte = ioread8(ptr + MEIN_REGISTER_OFFSET);
+  iowrite8(ein_byte | INTERRUPT_AUS, ptr + NOCH_EIN_OFFSET);
+
+  Hier muss sichergestellt sein, dass die Interruptleitung
+  wieder inaktiv ist!
+
+  return IRQ_HANDLED;
+}
+\end{lstlisting}
+
+Jetzt ist der Kernel-Teil des Treibers komplett. Beispiele für vollständige
+Treiber finden sich im Verzeichnis \cmd{drivers/uio/} in den Kernel-Quellen
+oder auf dem Server des Open Source Automation Development Lab (www.osadl.org).
+Ausführliche Dokumentation findet sich auch in den Kernel-Quellen in
+\cmd{Documentation/DocBook/uio-howto.tmpl}; mit \cmd{make htmldocs} erstellt
+man daraus eine lesbare Form. 
+
+\subsection*{Testen mit lsuio}
+
+Hat man einen UIO-Kerneltreiber geladen, so führt der erste Weg ins Verzeichnis
+\cmd{/sys/class/uio/uio0}, wo man überprüft, ob alle Dateien die erwarteten
+Werte enthalten. Dies kann auf Dauer recht lästig sein, besonders, wenn man
+mehrere UIO-Devices gleichzeitig testen will. Hier hilft ein kleines Tool
+namens \cmd{lsuio}, das alle diese Werte übersichtlich auflistet.
+
+Der Sourcecode dieses Tools findet sich auf dem Server des OSADL. Nach dem
+Auspacken des Archivs erstellt das übliche
+
+\begin{lstlisting}
+./configure && make && make install
+\end{lstlisting}
+
+das Tool. Dabei wird auch eine manpage installiert, die die möglichen Optionen
+des Programms erläutert. Zum Beispiel sorgt die Option -m dafür, dass
+\cmd{lsuio} testweise überprüft, ob der Speicher der Karte problemlos in den
+Userspace gemappt werden kann.
+
+Der recht übersichtliche Sourcecode von \cmd{lsuio} (er besteht nur aus zwei
+C-Dateien) kann im Übrigen als Beispiel für eigene Anwendungen dienen. Der
+Userspace-Teil des Treibers sollte nämlich nie eine bestimmte Version des
+Kerneltreibers voraussetzen. Ebenso sollte er nicht als selbstverständlich
+voraussetzen, dass das eigene Device immer als \cmd{/dev/uio0} erscheint.
+Durch Überprüfen der Dateien \cmd{name} und \cmd{version} in den Verzeichnissen
+unterhalb von \cmd{/sys/class/uio} kann man diese Informationen auf sichere
+Weise gewinnen. 
+
+\subsection*{Weiter geht's im Userspace}
+
+Der Userspace-Teil des Treibers wird zunächst über das sysfs den Namen des
+zuständigen Device-Files ermitteln. Im Folgenden wird der Einfachkeit halber
+\cmd{/dev/uio0} angenommen. Ausserdem wird er überprüfen, ob die Version des
+Kerneltreibers den Erwartungen entspricht. Wenn Alles im grünen Bereich ist,
+wird \cmd{/dev/uio0} geöffnet:
+
+\begin{lstlisting}
+int fd = open("/dev/uio0", O_RDWR);
+\end{lstlisting}
+
+Jetzt müssen wir uns Zugriff auf den Speicher der Karte verschaffen. Nehmen
+wir an, die Karte verfügt über einen 4kByte großen Bereich, auf den wir
+zugreifen wollen:
+
+\begin{lstlisting}
+void* map_addr = mmap( NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
+\end{lstlisting}
+
+Jetzt enthält \cmd{map\_addr} einen Zeiger, mit dem direkt auf den Speicher
+der Karte zugegriffen werden kann. Der Treiber wird jetzt die Register der
+Karte mit sinnvollen Werten beschreiben. Zuletzt wird er dafür sorgen, dass
+die Karte Interrupts generiert.
+
+Auf das Eintreffen von Interrupts wartet der Treiber, indem er in einer
+Schleife einen blockierenden \cmd{read()}-Aufruf für das Device-File ausführt.
+Dabei liest er einen vorzeichenbehafteten 32-bit-Wert. Da \cmd{read()}
+standardmäßig blockiert, wenn bei \cmd{open()} kein \cmd{O\_NONBLOCK}
+angegeben wurde, sieht das einfach so aus: 
+
+\begin{lstlisting}
+s32 irq_count;
+
+while (read(fd, &irq_count, 4)==4) {
+
+/* Interrupt-Aktionen */
+
+}
+\end{lstlisting}
+
+Natürlich sollte die Schleife noch eine Abbruchbedingung haben, damit das
+Programm irgendwann einmal beendet werden kann. Der in \cmd{irq\_count}
+gelieferte Wert ist die Gesamtzahl der aufgetretenen Interrupts. Meist wird
+man sich diesen Wert merken wollen, damit man beim nächsten Interrupt
+überprüfen kann, ob der Wert genau um eins größer geworden ist.
+
+Will man gleichzeitig auf mehrere Filedeskriptoren warten, z.B. auf
+Interrupts des UIO-Device und auf das Eintreffen von Bytes in einem
+Netzwerk-Socket, so kann man auch \cmd{select()} verwenden.
+
+Insgesamt ist der Userspace-Teil des Treibers sehr Entwickler-freundlich.
+Unter der Annahme, dass man die Karte nicht kaputt programmieren kann,
+steht Experimenten kaum etwas im Weg. Sie können jedenfalls keine
+schlimmeren Abstürze verursachen als beim Programmieren jeder anderen
+Anwender-Applikation.
+
+\subsection*{UIO oder nicht UIO}
+
+Sollen jetzt in Zukunft alle Treiber mittels UIO realisiert werden? Sicher
+nicht. UIO eignet sich sehr gut für Hardware, die Interrupts erzeugt, deren
+Register durch Memory-Mapping in den Userspace gebracht werden können, und
+die vor allem nur wenig mit anderen Subsystemen des Kernels zu tun haben.
+Letzteres ist leicht einzusehen, da die Interaktion mit anderen Subsystemen
+nur innerhalb des Kernel erfolgen kann. Wollte man etwa den Treiber für
+einen SATA-Controller mit UIO realisieren, so müßte dieser ja ein gültiges
+Block-Device auf die Beine stellen. Hätte man den dazu nötigen Code, der
+eng mit anderen Kernel-Teilen zusammenarbeiten würde, fertiggestellt, so
+würde man bemerken, dass für den Userspace nicht mehr viel übrig bleibt.
+
+Auch Hardware, die keine Interrupts erzeugt, ist kein idealer Kandidat für
+UIO, da die saubere Handhabung von Interrupts die eigentliche Besonderheit
+des Frameworks darstellt. Das reine Mapping von Speicher in den Userspace
+kann man auch ohne UIO haben. Allerdings bietet eine Implementierung mit Hilfe
+von UIO immer noch den Vorteil, eine standardisierte und dokumentierte
+sysfs-Schnittstelle gratis dazu zu bekommen.
+
+Eine weitere Einschränkung besteht darin, dass UIO in seiner aktuellen Version
+kein DMA unterstützt. Die Entwickler planen, dies in einer zukünftigen Version
+nachzureichen, falls sich Anwendungen dafür finden. Ein naheliegendes Beispiel
+sind natürlich Treiber für Grafikkarten, allerdings hat sich hier durch die
+weite Verbreitung von X.org bereits ein anderer Standard etabliert, der im
+Übrigen teilweise ähnliche Ansätze wie UIO aufweist. Aber auch I/O-Karten,
+beispielsweise schnelle A/D-Wandler, könnten DMA benötigen. Die Zukunft wird
+zeigen, ob an dieser Stelle Nachfrage besteht.
+
+\subsection*{Fazit}
+
+Durch das seit Kernel-Version 2.6.23 in Linux verfügbare
+Userspace I/O-Framework wird es besonders für Industriebetriebe erheblich
+einfacher, Treiber für ihre I/O-Karten zu schreiben und zu warten. Der
+Kernel-Teil des Treibers beschränkt sich auf ein absolutes Minimum, und das
+UIO-Framework entkoppelt ihn wirkungsvoll von der Vielzahl an internen
+Kernelfunktionen, die herkömmliche Treiber verwenden müssen. Die Entwicklung der
+eigentlichen Treiber-Funktionalität erfolgt bequem im Userspace, unter Verwendung
+der für die Applikationsentwicklung gewohnten Tools und Bibliotheken.
+
+Hat man sich einmal für UIO entschieden, so sollte man unbedingt anstreben, den
+Kernelteil des Treibers im Mainline-Kernel unterzubringen. Auf diese Weise
+erspart man sich und seinen Kunden das ständige Patchen des Kernels und die
+Pflege eines out-of-tree Moduls. 
+
+Verhilft man seinem Kernel zusätzlich zu Echtzeit-Fähigkeiten, indem man den 
+Realtime-Preemption-Patch einspielt, so erhält man ein einfach zu
+programmierendes und vollständig aus Open Source-Software bestehendes System
+für die Automatisierungstechnik.
 
 \end{document}
-- 
cgit v1.2.3

