9.1. Konzeption

Die Grundlagen für einen guten Treiber werden bei der Konzeption gelegt. Auf der einen Seite wird im Detail entschieden, welche Möglichkeiten eine Applikation, aber auch das System hat, um auf ein Gerät respektive den Treiber zuzugreifen. Auf der anderen Seite wird spezifiziert, über welche Sequenzen und Kommandos die Zugriffe später erfolgen dürfen. Ziel der Konzeption: ein intuitiv zu nutzender Treiber.

Abbildung 9-1. Qualitätsaspekte beim Design

9.1.1. Keine halben Sachen ...

Um einen guten Treiber zu erhalten, sollte Ihr Treiber

  1. sämtliche Funktionen des Applikations-Interfaces unterstützen,

  2. die Zugriffsarten blockierend und nicht blockierend implementieren,

  3. sich vollständig in das System integrieren,

  4. sämtliche Funktionen der Hardware unterstützen und

  5. komplett dokumentiert sein.

Vollständige Abbildung des Applikations-Interfaces. Ein guter Treiber bildet das Applikations-Interface vollständig ab. Konkret bedeutet dies: Sämtliche Systemcalls sind zu unterstützen – nicht nur open close, read oder write, sondern insbesondere auch select, poll und lseek.

Systemintegration. Damit sich der Treiber vollständig in das System integriert, muss er

  • das Gerätemodell (Sys-Filesystem),

  • das Proc-Filesystem,

  • die Hotplug-Funktion (Export der Geräte-IDs) und

  • das Device-Filesystem

unterstützen.

Hardwareunterstützung. Dass ein Treiber die ihm zugeordnete Hardware vollständig unterstützt, versteht sich von selbst. Dabei ist zu beachten, dass ein Anwender die gleiche Hardware möglicherweise gleich mehrfach in seinen Rechner einbaut. Die Software muss also von vornherein mit mehreren physikalischen Einsteckkarten (Geräten) zurechtkommen.

Daher müssen insbesondere Datenstrukturen entsprechend redundant ausgelegt werden.

Vollständige Beschreibung des Treibers. Zu einem guten Treiber gehört auch eine vollständige Dokumentation. Die Dokumentation gliedert sich in den Teil der Metainformationen, die über das Programm modinfo abgerufen werden können und den offline-Part.

Der offline-Teil wiederum ist unterteilt in die technische Dokumentation und die User-Dokumentation. Die technische Dokumentation beschreibt die Konzeption des Treibers und die verwendeten Algorithmen. Die User-Dokumentation erläutert Kommandos und Parameter. Sie thematisiert ferner, wie der User die Funktionalität des Treibers nutzt und wie sich der Treiber sowohl im Gut- als auch im Fehlerfall (keine Hardware gefunden, notwendige Ressourcen nicht zugewiesen, Unterbrechung eines Systemcalls durch ein Signal) verhält.

9.1.2. Intuitive Nutzung durch Struktur

Ein Treiber muss sich sowohl dem Anwender »aufgeräumt« präsentieren als auch intern strukturiert aufgebaut sein.

Je nach Komplexität und Vielfalt der unterstützten Hardware ist über einen so genannten stacked driver, einen gestapelten Treiber nachzudenken, der aus mehreren Modulen besteht. Mehrere Low-Level-Module, von denen jeweils nur eins benötigt wird, sind für die unterschiedlichen Ankopplungsarten der Hardware (z.B. PCI oder USB) zuständig. Sie bieten ein einheitliches Interface für ein daraufgesetztes Modul, welches die eigentliche Funktionalität implementiert.

Die einzelnen Module werden zweifelsohne in jeweils einem eigenen Quellcode-File implementiert. Bei komplexen Treibern, die letztlich nur ein Modul ergeben, kann es aber ebenfalls Sinn machen, die Funktionalität auf unterschiedliche Quellcode-Dateien aufzuteilen und beim Generieren durch den Linker zu dem einen Modul zusammenbinden zu lassen.

Einteilung in logische Geräte. Um dem Anwender einen »aufgeräumten« Treiber zu präsentieren, der zudem intuitiv genutzt werden kann, werden semantisch unterscheidbare Funktionalitäten auf jeweils eigene, logische Geräte abgebildet. So könnte beispielsweise für die Konfiguration eines Gerätes respektive eines Treibers für den eigentlichen Datenaustausch und für die Statusinformationen ein eigenes, logisches Gerät angelegt werden. Mit den logischen Geräten entfällt die Notwendigkeit eines komplizierten Sets von IO-Controls.

Die intuitive Nutzung eines solchen Interfaces möge das folgende Beispiel verdeutlichen: Zum Betrieb eines Boards muss eine Firmware auf dieses geladen werden. Hierzu ist ein eigenes logisches Gerät, das Board-Device, konzipiert worden. Das Laden der Firmware wird mit einer normalen Schreiboperation durchgeführt:

    cat Firmware > /dev/BoardDevice

Da die Möglichkeit, Funktionen auf logische Geräte zu verteilen, nur für zeichenorientierte Treiber besteht (bei den blockorientierten Treibern ist die Bedeutung der Minor-Nummer festgelegt), kann anstelle der logischen Geräte auch das Gerätemodell verwendet werden. Im Fall des gerade beschriebenen Firmware-Downloads bietet sich zweifelsohne das dortige Firmware-Interface [Sainz2003] an.

Systemprogramme statt Spezialsoftware. Der Clou bei der Verwendung von logischen Geräten oder des Gerätemodells ist die Kommunikation mit dem Treiber über eine Schnittstelle, bei der Konfiguration und Kommandos im Klartext (ASCII-basiert) übergeben werden. Somit kann der Treiber mit einfachen Systemprogrammen, mit cat und echo, verwaltet werden, was Spezialsoftware überflüssig macht.

9.1.3. Sicher muss es sein

Sicherheitsgerichtetes Verhalten. Ein Treiber ist grundsätzlich so zu entwerfen, dass im Fehlerfall sowohl innerhalb der Applikations-Software, erst recht aber innerhalb des angesteuerten Gerätes ein eindeutiger, sicherer Zustand eingenommen wird. Dass die Parameter, die eine Applikation dem Treiber möglicherweise übergibt, auf ihre Gültigkeit (z.B. Wertebereich) überprüft werden, versteht sich von selbst. Noch wichtiger aber ist die Implementierung der Funktion driver_close. Stürzt nämlich eine Applikation ab, ruft der Kernel diese Funktion auf, die daraufhin das Gerät (und damit einen möglicherweise über das Gerät angesteuerten technischen Prozess) in den sicheren Zustand überführt.

Bei »kritischen« Geräten macht es durchaus Sinn, eine Watchdog-Funktion zu implementieren, die sicherstellt, dass das Gerät ordnungsgemäß angesteuert wird und sich eine zugehörige Applikation nicht in einer Endlosschleife befindet.

Spezifische Zugriffsrechte. Im Rahmen der driver_open-Funktion hat ein Treiber die Möglichkeit, eigene Zugriffsrechte zu implementieren. Hier kann beispielsweise festgelegt werden, wie viele Rechenprozesse zu einem Zeitpunkt gleichzeitig auf das Gerät zugreifen dürfen. Nutzen Sie – soweit sinnvoll – diese Möglichkeit.

9.1.4. Funktional muss es sein

Ein Treiber sollte neben der Standard-Funktionalität auch Komfort-Funktionalität erhalten. So sollten beispielsweise, wenn irgendwie möglich, die Hardware- und Konfigurationsparameter vom Treiber automatisch detektiert und eingestellt werden; eine lästige Parameterangabe beim Laden des Treibers entfällt damit. Sind doch Parameter notwendig, sollten diese zum einen mit Hilfe der Makros für die Modul-Metainformation erläutert werden, zum anderen aber auch nach dem Laden des Treibers über das Sys-Filesystem einstellbar sein.

Hinweise zur automatischen Hardware-Erkennung finden sich im Kapitel Hardware erkennen.


Lizenz