next up previous contents
Next: Gerätetreiber - Datenstrukturen und Up: Linux Gerätetreiber Previous: Makefile eines Gerätetreibers

Quellcode eines Gerätetreibers

  Der Quellcode eines Gerätetreiber muß auch bestimmte Bedingungen erfüllen. Wichtig ist hierbei zunächst, daß linux/module.h als erste Headerdatei eingebunden wird! Die Definition char kernel_version[] = UTS_RELEASE; ist, wie im vorigen Kapitel beschrieben, nur aus Kompatibilitätsgründen nötig. In den neuesten Kerneln ist dies schon in den Headerdateien enthalten. Hierzu ein Beispiel für einen Gerätetreiber, der das Einlesen des Joystick Ports ermöglicht:

/************************************************************************/
/*      This is a sample device driver for Linux                        */
/*      It implements a joystick device                                 */
/*      written by Marius Heuler                                        */
/************************************************************************/

#define BSP_NAME "joystick"

#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <linux/version.h>

#include <asm/io.h>
#include <asm/segment.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/sched.h>

/************************************************************************/
/*      Local Prototypes                                                */
/************************************************************************/

static void ReadJoystick(int *x0, int *y0, int *x1, int *y1, int *bt);

static int bsp_open(struct inode*, struct file*);
static void bsp_close(struct inode*, struct file*);
static int bsp_read(struct inode*, struct file*, char*, int);
static int bsp_write(struct inode*, struct file*, const char*, int);
static int bsp_ioctl(struct inode*, struct file*, uint, ulong);

int init_module(void);
void cleanup_module(void);

char kernel_version[] = UTS_RELEASE;    /* kernel version */

int major = BSP_MAJOR;  /* major device id */

static struct file_operations bsp_fops = {
        NULL,           /* lseek */
        bsp_read,       /* read */
        bsp_write,      /* write */
        NULL,           /* readdir */
        NULL,           /* select */
        bsp_ioctl,      /* ioctl */
        NULL,           /* mmap */
        bsp_open,       /* open */
        bsp_close,      /* close */
        NULL,           /* fsync */
        NULL,           /* fasync */
        NULL,           /* check media_change */
        NULL            /* revalidate */
};

/************************************************************************/
/*      This is called when loading the module                          */
/************************************************************************/

int init_module()
{
        int error;
      
        /* register the device to the kernel */
        if ((error = register_chrdev(major, BSP_NAME, &bsp_fops))) {
                printk(KERN_ALERT 
                  "Joystick device %d is already registered!\n", major);
                return error;
        }
        printk(KERN_DEBUG "Joystick driver loaded on device %d\n", major); 
        return(error);
}

/************************************************************************/
/*      This is called when unloading the module                        */
/************************************************************************/

void cleanup_module()
{
        printk(KERN_DEBUG "Joystick driver unloaded\n");
        unregister_chrdev(major, BSP_NAME);
}

/************************************************************************/
/*      This is the handler for read requests                           */
/************************************************************************/

static int bsp_read(struct inode *ip, struct file *fp, char* to, int len)
{
        int x0, y0, x1, y1, bt;
        int error;
        int minor = MINOR(ip->i_rdev);
        static char sbuf[100];

        /* Check for sanity */
        if ((error = verify_area(VERIFY_WRITE, to, len))) return error;
        if (minor != 0)
                return(-ENODEV);
                
        ReadJoystick(&x0, &y0, &x1, &y1, &bt);
        sprintf(sbuf, "x0:%+5d  y0:%+5d     x1:%+5d  y1:%+5d"
                      "     bt0:%d   bt1:%d   bt2:%d   bt3:%d\n",
                        x0, y0, x1, y1, 
                        (bt & 16) ? 0:1,
                        (bt & 32) ? 0:1,
                        (bt & 64) ? 0:1,
                        (bt & 128) ? 0:1);
        memcpy_tofs(to, sbuf, strlen(sbuf));
        return(strlen(sbuf));
}

/************************************************************************/
/*      This is the handler for write requests                          */
/************************************************************************/

static int bsp_write(struct inode *ip, struct file *fp, const char* from, int len)
{
        return -EINVAL;
}

/************************************************************************/
/*      IOctl handler of device                                         */
/************************************************************************/

static int bsp_ioctl(struct inode *ip, struct file *fp, uint cmd, ulong arg)
{
        return -EINVAL;
}

/************************************************************************/
/*      Open handler of module                                          */
/************************************************************************/

static int bsp_open(struct inode *ip, struct file *fp)
{
        int minor = MINOR(ip->i_rdev);

        if (minor != 0)               
                return(-ENODEV);
        MOD_INC_USE_COUNT;
        return 0;
}

/************************************************************************/
/*      Close handler of module                                         */
/************************************************************************/

static void bsp_close(struct inode *ip, struct file *fp)
{
        int minor = MINOR(ip->i_rdev);

        if (minor != 0)
                return;
        MOD_DEC_USE_COUNT;
}

/************************************************************************/
/*      Read data from joystick port                                    */
/************************************************************************/

static void ReadJoystick(int *x0, int *y0, int *x1, int *y1, int *bt)
{
        int t, w;

        t = jiffies;
        *x0 = 0;
        *y0 = 0;
        *x1 = 0;
        *y1 = 0;
        outb(0, 0x201);
        w = inb(0x202);
        *bt = (w & (16+32+64+128));

        while (t == jiffies) {
                w = inb(0x201);
                if (w & 1) (*x0)++;
                if (w & 2) (*y0)++;
                if (w & 4) (*x1)++;
                if (w & 8) (*y1)++;
        }
}

Beim Laden des Moduls wird die Funktion init_module() vom Kernel aufgerufen, die die Initialisierung und Einbindung in den Kernel vornimmt. Im Erfolgsfall muß sie 0 zurückliefern. Die Funktion cleanup_module wird beim Entfernen des Treiber aufgerufen. Sie muß alle vom Treiber benötigten Ressourcen wieder freigeben.

Gerätetreiber benötigen eine Gerätekennung (Major Device ID), die eindeutig sein muß. In der Datei devices.txt im Linux-Kernel werden die verschiedenen belegten IDs aufgelistet. Für einen neuen Treiber muß eine freie ID verwendet werden!

Um nun ein Zeichengerät zu registrieren, wird die Funktion register_chrdev(major, name, &file_ops) aufgerufen. Normalerweise wird dies beim Laden des Moduls in init_module() durchgeführt. Bei den Parametern gibt major die obige ID an. Mit name wird der Name des Geräts festgelegt, u.a. für das Modutils-Paket. Unter /proc/devices werden alle geladenen Gerätetreiber mit Namen und Gerätekennung aufgelistet.

Die Struktur file_ops gibt die Fähigkeiten des Geräts an. Sie ist so aufgebaut:

struct file_operations {
        int (*lseek) (struct inode *, struct file *, off_t, int);
        int (*read) (struct inode *, struct file *, char *, int);
        int (*write) (struct inode *, struct file *, const char *int);
        int (*readdir) (struct inode *, struct file *, void *,
                        filldir_t);
        int (*select) (struct inode *, struct file *, int,
                       select_table *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, 
                      unsigned long);        
        int (*mmap) (struct inode *, struct file*, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        void (*release) (struct inode *, struct file *);
        int (*fsync) (struct inode *, struct file *);
        int (*fasync) (struct inode *, struct file *, int);
        int (*check_media_change) (dev_t dev);
        int (*revalidate) (dev_t dev);
};
Die Felder enthalten jeweils Zeiger auf C-Funktionen mit den entsprechenden Parametern. Falls eine Funktion nicht unterstützt wird, gibt man NULL an. Die wichtigsten Funktionen werden nun kurz beschrieben: Nachdem die Funktionalität der einzelnen Funktionen implementiert wurde, ist die Schnittstelle zu Programmen fertig.

Bei der Erstellung eines Gerätetreibers müssen aber unbedingt folgende Dinge beachtet werden:

Um den Gerätetreiber zu benutzen muß nur noch mittels insmod joystick der Treiber geladen werden. Nun kann ein Programm mittels eines Device, z.B. /dev/joystick, welches die Gerätekennung (Major Device ID) des Treibers hat, auf den Gerätetreiber zugreifen. Ein Device kann mittels des mknod Befehls oder der mknod() C-Funktion erzeugt werden.


next up previous contents
Next: Gerätetreiber - Datenstrukturen und Up: Linux Gerätetreiber Previous: Makefile eines Gerätetreibers

Marius Heuler
Tue Jan 7 12:11:50 MET 1997