Projekte.PCI-Tutorial-TGKernel (Struktur)


Ein einfacher Kerneltreiber mit read/write-Funktion

Die Beispielprogramme sind [xilinx_pci.c] [read-mem.c] [Makefile] [write-mem.c] . Die Datei xilinx_pci.c enthält den Gerätetreiber, die Dateien write-mem.c und read-mem.c sind die Testprogramme für den Treiber mit der gleichen Funktionalität wie die Userspace-Testprogramme.

Das Kernelmodul für dieses Projekt verwendet als Grundlage das Kernelmodul des Projekts "Board-Test". Die Funktionen init_module() und cleanup_module, die beim Laden des Moduls bzw. beim Entfernen aus dem System ausgeführt werden, sind unverändert übernommen. Auch die Kernelfunktionen xilinx_pci_open und xilinx_pci_release, die die Dateifunktionen open () / fopen() bzw. close () / fclose() implementieren, sind gleich geblieben.

Der Zugriff auf den Speicher der PCI-Karte erfolgt in diesem Beispiel über die Dateifunktionen write() / fwrite() bzw. read() / fread() der Standardbibliothek und ihre höheren Abstraktionen wie z.B. fprintf(), fscanf(), fputs(), fgets(), ... Dafür muss das Kernelmodul die entsprechenden Kernelfunktionen-Gegenstücke implementieren. Auch die fseek()-Dateifunktion kann dann vom Anwendungsprogramm benutzt werden. Der Kernel hält dafür eine Default-Routine bereit, allerdings mit der Einschränkung, dass der Dateizeiger nicht vom Ende der Datei (des Speichers) aus platziert werden kann (Argument SEEK_END).

Die Methoden read und write führen eine ähnliche Aufgabe durch, sie kopieren Daten aus dem und in den Applikationscode. Die Prototypen sind daher sehr ähnlich:

    ssize_t xilinx_pci_read (struct file *filp, char *buf, 
                             size_t count, loff_t *f_pos);

    ssize_t xilinx_pci_write (struct file *filp, const char *buf, 
                              size_t count, loff_t *f_pos);
Bei beiden Methoden ist filp der file-Zeiger (nicht zu verwechseln mit dem Datentyp FILE) und count die Größe der angeforderten Datenübertragung. Das Argument buff zeigt auf den Puffer im Anwendungsprogramm, der die zu schreibenden Daten enthält, oder auf den leeren Puffer, in den die gelesenen Daten hineingeschrieben werden sollen. offp ist schließlich ein Zeiger auf ein Objekt mit "langem Offset", das die Dateiposition angibt, auf die der Benutzer zugreifen will.
Was den Datentransfer angeht, werden die schon eingeführten Funktionen copy_to/from_user() verwendet, um Daten zwischen Kernelspace und Userspace hin- und herzukopieren. Nach der Kopieraktion muss der Dateizeiger offp entsprechend angepasst werden.

Rückgabewert der read-Funktion

Die möglichen Rückgabewerte von read() sind genau festgelegt:
  • Wenn der Wert gleich dem Argument count ist, dann ist die angeforderte Zahl von Bytes übertragen worden.
  • Wenn der Wert positiv, aber kleiner als count ist, ist nur ein Teil der Daten übertragen worden. Das kann aus einer Reihe von Gründen passieren, die vom jeweiligen Gerät abhängen. Meistens wird das Programm noch einmal versuchen, die Daten zu lesen. Die Funktion fread() der Standardbibliothek beispielsweise ruft den Systemaufruf noch einmal auf, um die angeforderte Datenübertragung noch einmal durchzuführen.
  • Wenn der Wert 0 ist, wird das als Erreichen des Dateiendes interpretiert.
  • Ein negativer Wert weist auf einen Fehler hin. Der Wert gibt an, was für ein Fehler das war; die Werte stehen in <linux/errno.h>. Diese Fehler sehen aus wie -EINTR (unterbrochener Systemaufruf) oder -EFAULT (fehlerhafte Adresse).
Die Funktion xilinx_pci_read() in diesem Beispieltreiber hält sich an diese Vorgaben. Vor dem copy_to_user() wird geprüft, ob der Transfer über den zulässigen Speicherbereich hinausgehen würde und die Anzahl der zu kopierenden Bytes angepasst. Falls der Dateizeiger schon zuvor am Dateiende (Ende des Speicherbereichs) angekommen war, wird nichts kopiert und eine 0 zurückgegeben.

Rückgabewert der write-Funktion

Die möglichen Rückgabewerte von write() sind ebenfalls genau festgelegt:
  • Wenn der Wert gleich count ist, ist die angeforderte Anzahl von Bytes übertragen worden.
  • Wenn der Wert positiv, aber kleiner als count ist, dann wurde nur ein Teil der Daten übertragen. Das Anwendungsprogramm wird wahrscheinlich versuchen, auch den Rest der Daten zu schreiben.
  • Wenn der Wert 0 ist, ist nichts geschrieben worden. Dieses Ergebnis ist kein Fehler, weswegen auch kein Fehlercode zurückgegeben wird. Auch hier wird die Standardbibliothek write() erneut aufrufen (Blockierendes Schreiben).
  • Ein negativer Wert weist auf einen aufgetretenen Fehler hin. Wie bei read() sind die möglichen Fehlerwerte in <linux/errno.h>definiert.
Insbesondere mit dem zweiten und dritten Fall, wenn also der Schreibtransfer nicht vollständig durchgeführt werden kann, muss der Treiberprogrammierer individuell umgehen. In diesem Beispiel treten diese Situationen ein, wenn das Ende des Speicherbereichs der PCI-Karte erreicht wurde bzw. beim Transfer erreicht wird. Dann macht auch eine Wiederholung des Schreibversuchs durch die Dateifunktionen keinen Sinn. Die Funktion xilinx_pci_write() führt daher in diesem Beispieltreiber das copy_from_user() nur dann aus, wenn der Datenblock vollständig in den Speicher der PCI-Karte (ab der aktuellen Dateizeiger-Position) passt. Anderenfalls gibt die Funktion einen Fehlercode zurück.

Bemerkungen

  • Auch dieser Treiber ist wieder nur daruf ausgelegt, nur eine PCI-Karte zu verwalten.
  • Es sind keine weiteren Zugriffsbeschränkungen auf das Device einbaut. Mehre Programme können die Gerätedatei gleichzeitig öffnen und Daten transferieren. Mögliche Beschränkungsmechanismen werden in der angegebenen Literatur erläutert und sind relativ leicht zu implementieren.

Beispielprogramme

Die Beispielprogramme write-mem.c und read-mem.c haben die gleiche Funktionalität wie die gleichnamigen reinen Userspace-Programme. Sie öffnen die Gerätedatei /dev/xilinx_pci und lesen oder schreiben mit den Dateifunktionen der Standardbibliothek im Kartenspeicher.

Beispiel

    cat write-mem.c | ./write-mem
schreibt den Inhalt der Datei write-mem.c in den Speicher des PCI-Targets.
   ./read-mem.c
liest den PCI-Speicher. Es wird der zuvor hineingeschriebene Inhalt ausgegeben.


Autor: gkemnitz, Letzte Änderung: 14.04.2011 15:09:59


 TU Clausthal 2020  Impressum