Livre de chevet du développeur FreeBSD
Précédent | Chapitre 16. Ecrire des pilotes de périphériques pour FreeBSD | Suivant

# 16.4. Les périphériques caractères

Un pilote de périphérique caractère est un pilote qui tranfère les données directement au processus utilisateur ou vers celui-ci. Il s'agit du plus commun des types de pilote de périphérique et il y en a plein d'exemples simples dans l'arbre des sources.

Cet exemple simple de pseudo-périphérique enregistre toutes les valeurs que vous lui avez écrites et peut vous les renvoyer quand vous les lui demandez.

    /*
     * un simple pseudo-périphérique `echo' KLD
     *
     * Murray Stokely
     */

    #define MIN(a,b) (((a) < (b)) ? (a) : (b))

    #include <sys/types.h>
    #include <sys/module.h>
    #include <sys/systm.h> /* uprintf */
    #include <sys/errno.h>
    #include <sys/param.h>  /* defines utilises dans kernel.h */
    #include <sys/kernel.h> /* types utilises dans me module d'initialisation */
    #include <sys/conf.h>   /* cdevsw struct */
    #include <sys/uio.h>    /* uio struct */
    #include <sys/malloc.h>

    #define BUFFERSIZE 256

    /* Prototypes des fonctions */
    d_open_t      echo_open;
    d_close_t     echo_close;
    d_read_t      echo_read;
    d_write_t     echo_write;

    /* Points d'entrée du périphérique Caractère */
    static struct cdevsw echo_cdevsw = {
      echo_open,
      echo_close,
      echo_read,
      echo_write,
      noioctl,
      nopoll,
      nommap,
      nostrategy,
      "echo",
      33,                   /* reserve pour lkms - /usr/src/sys/conf/majors */
      nodump,
      nopsize,
      D_TTY,
      -1
    };

    typedef struct s_echo {
      char msg[BUFFERSIZE];
      int len;
    } t_echo;

    /* variables */
    static dev_t sdev;
    static int len;
    static int count;
    static t_echo *echomsg;

    MALLOC_DECLARE(M_ECHOBUF);
    MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "cache pour le module echo");

    /*
     * Cette fonction est appele par les appels systeme kld[un]load(2) pour
     * determiner quelles actions doivent etre faites quand le
     * module est charge ou decharge
     */

    static int
    echo_loader(struct module *m, int what, void *arg)
    {
      int err = 0;

      switch (what) {
      case MOD_LOAD:                /* kldload */
        sdev = make_dev(&echo_cdevsw,
    		    0,
    		    UID_ROOT,
    		    GID_WHEEL,
    		    0600,
    		    "echo");
        /* aloocation de mémoire noyau pour l'utilisation de ce module */
        /*    malloc(256,M_ECHOBUF,M_WAITOK); */
        MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK);
        printf("Peripherique Echo charge.\n");
        break;
      case MOD_UNLOAD:
        destroy_dev(sdev);
        FREE(echomsg,M_ECHOBUF);
        printf("Peripherique Echo decharge.\n");
        break;
      default:
        err = EINVAL;
        break;
      }
      return(err);
    }

    int
    echo_open(dev_t dev, int oflags, int devtype, struct proc *p)
    {
      int err = 0;

      uprintf("Peripherique \"echo\" ouvert avec succes.\n");
      return(err);
    }

    int
    echo_close(dev_t dev, int fflag, int devtype, struct proc *p)
    {
      uprintf("Fermeture du peripherique \"echo.\"\n");
      return(0);
    }

    /*
     * La fonction read prend juste comme parametre
     * le cache qui a ete sauve par l'appel à echo_write()
     * et le retourne a l'utilisateur pour acces.
     * uio(9)
     */

    int
    echo_read(dev_t dev, struct uio *uio, int ioflag)
    {
      int err = 0;
      int amt;

      /* De quelle taille est cette operation read ?  Aussi grande que l'utilisateur le veut,
         ou aussi grande que les donnees restantes */
      amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0);
      if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) {
        uprintf("uiomove echoue!\n");
      }

      return err;
    }

    /*
     * echo_write prend un caractere en entree et le sauve
     * dans le cache pour une utilisation ulterieure.
     */

    int
    echo_write(dev_t dev, struct uio *uio, int ioflag)
    {
      int err = 0;

      /* Copie la chaine d'entree de la memoire de l'utilisateur a la memoire du noyau*/
      err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE));

      /* Maintenant nous avons besoin de terminer la chaine par NULL */
      *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0;
      /* Enregistre la taille */
      echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE);

      if (err != 0) {
        uprintf("Ecriture echouee: mauvaise adresse!\n");
      }

      count++;
      return(err);
    }

    DEV_MODULE(echo,echo_loader,NULL);

Pour installer ce pilote, vous devrez d'abord créer un fichier spécial dans votre système de fichiers avec une commande comme :

    	# mknod /dev/echo c 33 0

Avec ce pilote chargé, vous devriez maintenant êtr capable de taper quelque chose comme :

            # echo -n "Test Donnees" > /dev/echo
            # cat /dev/echo
    	Test Donnees

Périphériques réels dans le chapître suivant.

Informations additionnelles

- Dynamic Kernel Linker (KLD) Facility Programming Tutorial - Daemonnews October 2000

- How to Write Kernel Drivers with NEWBUS - Daemonnews July 2000

Précédent | Sommaire | Suivant
Accéder au pilote d'un périphérique | Niveau supérieur | Pilotes Réseau

Ce document, ainsi que d'autres peut être téléchargé sur ftp.FreeBSD.org/pub/FreeBSD/doc/.

Pour toutes questions à propos de FreeBSD, lisez la documentation avant de contacter <questions@FreeBSD.org>.
Pour les questions sur cette documentation, contactez <doc@FreeBSD.org>.