3. Types de pilotes de module de périphériques.

3.1. Caractère

3.1.2. Points d'entrée

3.1.2.1. d_open()

d_open() prend plusieurs arguments, la liste formelle ressemble à quelque chose comme :

int
d_open(dev_t dev, int flag, int mode, struct proc *p)

d_open() est appelé à chaque ouverture du périphérique.

L'argument dev contient le nombre majeur et mineur du périphérique ouvert. Ils sont disponibles par les macros major() et minor()

Les arguments flag et mode sont comme décrits sur la page de manuel de open. Il est recommandé que vous examiniez ces derniers pour vous assurer des droits d'accès dans <sys/fcntl.h> et faire ce qui est exigé. Par exemple si flag est (O_NONBLOCK | O_EXLOCK) l'ouverture échouerait si il bloquait ou si l'accès exclusif ne pouvait pas être accordé.

L'argument p contient toutes les informations à propos du processus actuel.

3.1.2.2. d_close()

d_close() prend la même liste d'argument que d_open():

int
d_close(dev_t dev , int flag , int mode , struct proc *p)

d_close() est seulement appelé à la dernière fermeture de votre périphérique (par périphérique mineur). Par exemple dans le fragment suivant de code, d_open() est appelé 3 fois, mais d_close() seulement une fois.

 ...
    fd1=open("/dev/mydev", O_RDONLY);
    fd2=open("/dev/mydev", O_RDONLY);
    fd3=open("/dev/mydev", O_RDONLY);
 ...
   <useful stuff with fd1, fd2, fd3 here>
 ...
    close(fd1);
    close(fd2);
    close(fd3);
 ...

Les arguments sont semblables à ceux décrits ci-dessus pour d_open().

3.1.2.3. d_read() et d_write()

d_read() et d_write prennent les listes suivantes d'argument:

int
d_read(dev_t dev, struct uio *uio, int flat)
int
d_write(dev_t dev, struct uio *uio, int flat)

Les points d'entrée de d_read() et de d_write() sont appelés quand read et write sont appelés sur votre périphérique depuis l'espace utilisateur. Le transfert des données peut être manipulé par la routine du noyau uiomove().

3.1.2.4. d_ioctl()

Sa liste d'argument est comme suit:

int
d_ioctl(dev_t dev, int cmd, caddr_t arg, int flag, struct proc *p)

d_ioctl() est un fourre-tout pour les exécutions qui ne semblent pas raisonnable dans un paradigme lecture/écriture. Le plus célèbre de tout les ioctl est probablement celui sur des périphériques tty, par le stty. Le point d'entrée d'ioctl est appelé depuis l'ioctl() de sys/kern/sys_generic.c

Il y a quatre types différents d'ioctl qui peuvent être implémentés. <sys/ioccom.h> contient des macros pratiques de pour définir ces ioctls.

  • _IO(g, n) pour les opérations de type contrôle.

  • _IOR(g, n, t) pour des opérations lisant des données d'un périphérique.

  • _IOW(g, n, t) pour les opérations écrivant des données sur un périphérique.

  • _IOWR(g,n,t) pour les opérations écrivant sur un périphérique puis lisent les données.

Ici g se rapporte à un groupe /. C'est une valeur de 8 bits, en général indicative du périphérique ; par exemple, 't' est utilisé dans des ioctls de tty. n se rapporte au nombre de l'ioctl dans le groupe. Sur SCO, ce seul nombre dénote l'ioctl. t est le type de données qui sera passé au pilote de périphérique; ceci est alors remis à un opérateur sizeof() du noyau. L'appel système ioctl() fera soit un copyin() soit un copyout() ou les deux à votre pilote, puis vous renverra un pointeur à la structure de données dans l'argument arg de l'appel d'd_ioctl. Actuellement la taille de données est limitée à une page (4k sur l'i386).

3.1.2.8. d_poll() (3.0 et plus) ou d_select() (2.2)

la liste d'argument de d_poll() est comme suit :

void
d_poll(dev_t dev, int events, struct proc *p)

d_poll() est employé pour découvrir si un périphérique est prêt pour les E/S. Par exemple, attendre que des données du réseau soient disponibles, ou que l'utilisateur presse une touche. Cela correspond à un appel de poll() dans l'espace utilisateur.

L'appel à d_poll() devrait vérifier les événements indiqués dans le masque d'évènement. Si aucun des événements demandés n'est en activité, mais qu'elles pourraient devenir actif plus tard, il devrait enregistrer ceci pour les actions futures du noyau. d_poll() fait ceci en appelant selrecord() avec une structure selinfo pour ce périphérique. La somme de toutes ces activités ressemblent à quelque chose comme ceci:

static struct my_softc {
        struct queue rx_queue;  /* As example only - not required */
        struct queue tx_queue;  /* As example only - not required */
        struct selinfo selp;    /* Required */
} my_softc[NMYDEV];

...

static int
mydevpoll(dev_t dev, int events, struct proc *p)
{
        int revents = 0;        /* Events we found */
        int s;
        struct my_softc *sc = &my_softc[dev];

        /* We can only check for IN and OUT */
        if ((events & (POLLIN|POLLOUT)) == 0)
                return(POLLNVAL);

        s = splhigh();
        /* Writes are if the transmit queue can take them */
        if ((events & POLLOUT) &&
            !IF_QFULL(sc->tx_queue))
                revents |= POLLOUT;
        /* ... while reads are OK if we have any data */
        if ((events & POLLIN) &&
            !IF_QEMPTY(sc->rx_queue))
                revents |= POLLIN;
        if (revents == 0)
                selrecord(p, &sc->selp);
        splx(s);
        return revents;
}

d_select() est utilisé dans la version 2.2 et précédentes de FreeBSD. Au lieu de 'events', il prend un simple entier 'rw', qui peut être FREAD pour la lecture (comme dans POLLIN ci-dessus), FWRITE pour l'écriture (comme dans POLLOUT ci-dessus), et 0 pour 'exception' - lorsque quelque chose d'exceptionnel se produit, comme une carte étant insérée ou retirée pour le pilote de pccard.

Pour 'select', le fragment correspondant à la description ci-dessus ressembleraient à ceci:

static int
mydevselect(dev_t dev, int rw, struct proc *p)
{
        int ret = 0;
        int s;
        struct my_softc *sc = &my_softc[dev];

        s = splhigh();
        switch (rw) {
        case FWRITE:
                /* Writes are if the transmit queue can take them */
                if (!IF_QFULL(sc->tx_queue))
                        ret = 1;
                break;
        case FREAD:
                /* ... while reads are OK if we have any data */
                if (!IF_QEMPTY(sc->rx_queue))
                        ret = 1;
                break;
        case 0:
                /* This driver never get any exceptions */
                break;
        }
        if(ret == 0)
                selrecord(p, &sc->selp);
        splx(s);
        return(revents);
}

3.1.2.10. d_strategy()

La liste d'argument de d_strategy() est comme suit :

void
d_strategy(struct buf *bp)

d_strategy() est utilisé pour les périphériques utilisant des E/S de type disperser-regrouper (scatter-gather). C'est ce qu'il y a de plus courant dans un périphérique de bloc. C'est sensiblement différent du modèle de système V, où seulement le pilote de bloc fait une E/S de type disperser-regrouper. Sous BSD, les périphériques de caractère sont parfois sommé d'exécuter une E/S de type disperser-regrouper par l'intermédiaire des appels systèmes readv() et writev().

3.2. Bloc

3.2.2. Points d'entrée

3.2.2.1. d_open()

Décrit dans la section périphérique de caractère.

3.2.2.2. d_close()

Décrit dans la section périphérique de caractère.

3.2.2.3. d_strategy()

Décrit dans la section périphérique de caractère.

3.2.2.4. d_ioctl()

Décrit dans la section périphérique de caractère.

3.3. Réseau

Structure struct ifnet

3.4. Protocole de communication

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>.