7.4. Treiber parametrieren

Zur Einstellung der Hardware-Adressen oder zum Ein- oder Ausschalten von spezifischen Funktionalitäten können dem Treiber beim Laden Parameter übergeben werden. Der Kernelentwickler bestimmt, welche Parameter das sind und von welchem Typ diese sind.

Für die Parameter stehen dem Kernelentwickler sowohl vordefinierte, als auch selbst zu definierende Datentypen zur Verfügung. Mit Letzteren wird eine spezifische Typ- und Bereichsprüfung möglich.

Die vordefinierten Datentypen sind in Tabelle Unterstützte Datentypen der Treiberparameter dargestellt. Um einen Parameter mit einem dieser Standarddatentypen zu definieren, sind drei Schritte erforderlich:

  1. Mit Hilfe des Makros MODULE_PARM_DESC wird eine Beschreibung des Parameters erstellt.

  2. Die Variable selbst, die den Parameterwert später aufnimmt, wird definiert.

  3. Mit Hilfe des Makros module_param oder module_param_named wird der Parameter dem Kernel bekannt gegeben.

Tabelle 7-2. Unterstützte Datentypen der Treiberparameter

BezeichnungBeschreibung
short Vorzeichenbehafteter Short-Wert.
ushort Short-Wert ohne Vorzeichen.
int Vorzeichenbehafteter Integer-Wert.
uint Integer-Wert ohne Vorzeichen.
long Vorzeichenbehafteter Long-Wert.
ulong Long-Wert ohne Vorzeichen.
charp String (Zeiger auf einen String).
bool Boolscher Wert, mögliche Werte 'y', 'Y', 'n', 'N', '0', '1'.
invbool Inverser boolscher Wert; Werte wie bei bool.

Das Makro module_param hat drei Parameter. Zuerst kommt der Parametername. Dieser ist identisch mit dem Namen der Variablen, in der der Parameter schließlich abgelegt wird. Als zweiter Parameter ist der Datentyp an der Reihe. Drittens sind schließlich die Zugriffsrechte anzugeben. Auch wenn noch nicht implementiert, sollen die Parameter über das Gerätemodell im Sys-Filesystem als Dateien gelesen und auch geschrieben werden können. Sind Zugriffsrechte (ungleich »Null«) angegeben, werden solche Dateien erzeugt.

Das Makro module_param_named unterscheidet sich von module_param dadurch, dass der Parametername (»name«) nicht identisch sein muss mit dem Namen der Variablen (»value«).

Für die Verarbeitung von Feldern gibt es außerdem die Makros module_param_string und module_param_array. Das erste Makro ist für den Fall eines Zeichenfeldes (Stringbuffer) gedacht. Der beim Laden des Treibers übergebene Parameter wird durch den Kernel in den angegebenen Buffer kopiert, dessen Adresse als zweiter Parameter (»string«) übergeben worden ist.

Das Makro module_param_array hat vier Parameter. Der erste Parameter spezifiziert wieder den Namen, der zweite den Datentyp der Feldelemente, der dritte Parameter die maximale Anzahl der Elemente und der vierte schließlich die Zugriffsrechte für die Datei im Sys-Filesytem. Die maximale Anzahl Elemente wird über eine eigene Variable übergeben.

static int intarray[4];
static int intarraycount=4;
module_param_array( intarray, int, intarraycount, 0 );

Der Inhalt der Variablen wird entsprechend der Anzahl beim Laden übergebener Parameter angeglichen. Sind beispielsweise 4 Elemente im Array vorhanden, beim Aufruf werden aber nur zwei angegeben, enthält die Variable bei Aufruf der Modulinitialisierungsfunktion den Wert »2«. Allerdings ist anhand des Wertes nicht erkennbar, ob alle Felder oder kein Feld belegt wurde. Denn auch wenn kein Feldparameter beim Laden angegeben wurde, ist die Variable mit dem Initialwert belegt (im Beispiel also mit »4«).

Strings schließlich können nach folgendem Vorbild implementiert werden:

static char string[10];
module_param_string( optionname, string, sizeof(string), 0 );

Selbstdefinierte Datentypen geben dem Entwickler die Möglichkeit, den Wertebereich der übergebenen Daten zu überprüfen. Hierzu muss der Entwickler zwei Funktionen und unter Umständen noch ein Makro zur Verfügung stellen. Die erste der beiden Funktionen hat die Aufgabe, den übergebenen Zeichen-String zu interpretieren und in den korrespondierenden Datentyp zu wandeln. Dabei muss die Funktion feststellen, ob das übergebene Datum dem Datentyp entspricht oder nicht. Soll für einen Parameter beispielsweise nur der Wert »1«, »2« oder »3« erlaubt sein, dann muss diese Funktion zum einen den übergebenen ASCII-Wert überprüfen und zum anderen sicherstellen, dass der Anwender keinen Wert kleiner als »1« und keinen Wert größer als »3« eingegeben hat. Der zugehörige Datentyp wird hier »onetwothree« genannt. Eine Implementierung der Konvertierung und Datentyp-Überprüfung findet sich in Selbstdefinierte Datentypen bei der Parameterübergabe.

Beispiel 7-14. Selbstdefinierte Datentypen bei der Parameterübergabe

int param_set_onetwothree( const char *pbuf, struct kernel_param *kp )
{
    if( pbuf[1] == '\0' ) { // nur einstellige Zahlen sind gültig
        if( pbuf[0] == '1' ) {
            *(int *)kp->arg = 1;
            return 0;
        }
        if( pbuf[0] == '2' ) {
            *(int *)kp->arg = 2;
            return 0;
        }
        if( pbuf[0] == '3' ) {
            *(int *)kp->arg = 3;
            return 0;
        }
    }
    return -EINVAL;
}

Die zweite Funktion ist schließlich für die Rückkonvertierung des Parameters in einen ASCII-Wert zuständig. Der Kernel übergibt dazu die Adresse auf einen Puffer, der die Größe einer Speicherseite hat:

int param_get_onetwothree( char *pbuf, struct kernel_param *kp )
{
    return sprintf((char *)pbuf, "%d", *(int *)kp->arg );
}

Neben den beiden Funktionen ist als Drittes ein Makro nötig, welches eine Typprüfung vornimmt. Die Typprüfung selbst muss durch den Compiler erfolgen. Es ist auch möglich, dieses Makro leer zu lassen und auf die Typprüfung durch den Compiler zu verzichten:

#define param_check_onetwothree( name, p )

Sind diese drei Komponenten erstellt, kann der Parameter mit dem Makro module_param definiert werden:

static int para=1;
module_param( para, onetwothree, 0666 );

Der Treiber kann jetzt – zusammen mit dem Parameter »para« – nur geladen werden, wenn der zugehörige Wert im programmierten Wertebereich liegt. Allerdings kann der Treiber ebenfalls geladen werden, falls kein Parameter angegeben ist.

# insmod paramself.ko para=2
#

Ein Laden des Treibers ist nicht möglich, falls der Wert außerhalb des zugehörigen Wertebereiches liegt:

# insmod paramself.ko para=0
Error inserting 'paramself.ko': -1 Invalid parameters
#

Die Übergabe der Parameter beim Laden des Treibers unterscheidet sich für Modul-Treiber und Built-in-Treiber. Beim Modul-Treiber besteht die Parameter-Definition aus dem Parameternamen und einem Gleichheitszeichen, gefolgt vom Parameterwert.

(root)# insmod treiber.ko ioport=0x3fe

Bei einem Built-in-Treiber reicht die Angabe des Parameternamens nicht aus. Damit der Parameter dem richtigen Treiber zugeordnet werden kann, muss vorweg noch der Treibername gesetzt werden. Treibername und Parametername werden durch einen Punkt voneinander getrennt (treiber.ioport=0x3fe).


Lizenz