<!--
     The FreeBSD Documentation Project
     The FreeBSD French Documentation Project

     $Id: article.sgml,v 1.5 2002-02-14 14:09:37 gioria Exp $
     $FreeBSD$
     Original revision: n.nn
-->   

<!DOCTYPE ARTICLE PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
<!ENTITY % man PUBLIC "-//FreeBSD//ENTITIES DocBook Manual Page Entities//EN"> %man;
<!ENTITY % urls PUBLIC "-//FreeBSD//ENTITIES Common Document URL Entities//FR"> %urls;
<!ENTITY % abstract PUBLIC "-//FreeBSD//ENTITIES DocBook Abstract Entities//FR"> %abstract;
<!ENTITY % artheader PUBLIC "-//FreeBSD//ENTITIES DocBook ArtHeader Entities//FR"> %artheader;
<!ENTITY % translators PUBLIC "-//FreeBSD//ENTITIES DocBook Translator Entities//FR"> %translators;

<!ENTITY % authors PUBLIC "-//FreeBSD//ENTITIES DocBook Author Entities//EN"> %authors
<!ENTITY % mailing-lists SYSTEM "../../share/sgml/mailing-lists.ent"> %mailing-lists;



<!--  French Build Only -->
<!--  <!ENTITY % authors SYSTEM "../../../en_US.ISO8859-1/books/handbook/authors.ent"> %authors; -->
<!--  <!ENTITY % mailing-lists PUBLIC "-//FreeBSD//ENTITIES DocBook Mailing List Entities//FR"> %mailing-lists; -->

 <!ENTITY rel.current CDATA "3.2">
]>


<article lang="fr">
  <articleinfo>
    <title>Le guide de l'auteur de pilotes de p&eacute;riph&eacute;riques pour FreeBSD</title>
    <authorgroup>
      <author>
        <firstname>Eric L.</firstname>
        <surname>Hernes</surname>
      </author>
    </authorgroup>
    &artheader.copyright;
    <abstract>
      <para><email>erich@rrnet.com</email></para>
      <para>29 Mai 1996</para>
      <para>Ce document d&eacute;crit comment ajouter un module de gestion de
p&eacute;riph&eacute;rique &agrave; FreeBSD.  Il <emphasis>n'est pas</emphasis> destin&eacute; pour &ecirc;tre un
cours d'instruction sur des modules de gestion de p&eacute;riph&eacute;rique
d'Unix en g&eacute;n&eacute;ral. Il est destin&eacute; pour les auteurs de module de
gestion de p&eacute;riph&eacute;rique, au courant du mod&egrave;le de module de gestion
de p&eacute;riph&eacute;rique d'Unix, pour travailler sur FreeBSD.
      </para>
        &abstract.license;
        &abstract.disclaimer;
        &trans.a.dntt;
    </abstract>
  </articleinfo>


<sect1>
<title>Sp&eacute;cificit&eacute; de FreeBSD2.x</title>

<para>Dû aux changements de FreeBSD avec le temps, ce guide est
seulement pr&eacute;cis en ce qui concerne FreeBSD 2.x.  Un guide de
rechange pour FreeBSD 3.x et au-del&agrave; est en train d'&ecirc;tre &eacute;crit. 
Contactez Jeroen Ruigrok <email>asmodai@wxs.nl</email> si
vous voulez l'aider &agrave; ce sujet.
</para>
</sect1>


<sect1>
<title>G&eacute;n&eacute;ralit&eacute;</title>

<para> <emphasis>Le noyau de FreeBSD est tr&egrave;s bien
document&eacute;, malheureusement il est enti&egrave;rement &eacute;crit en `C'.</emphasis>
</para>
</sect1>

<sect1>
<title>Types de pilotes de module de p&eacute;riph&eacute;riques.</title>

<sect2> 
<title>Caract&egrave;re</title>

<sect3>
<title>Structures de donn&eacute;es</title>

<para>Structure <citerefentry><refentrytitle>cdevsw</refentrytitle></citerefentry></para>
</sect3>

<sect3>  
<title>Points d'entr&eacute;e</title>

<sect4> 
<title><function>d_open()</function></title>
<para>
<function>d_open()</function> prend plusieurs arguments, la liste formelle ressemble &agrave;
quelque chose comme :  
</para>

<programlisting>
int
d_open(dev_t dev, int flag, int mode, struct proc *p) 
</programlisting>

<para><function>d_open()</function> est appel&eacute; &agrave; <emphasis>chaque</emphasis> ouverture du p&eacute;riph&eacute;rique.</para>

<para>L'argument <citerefentry><refentrytitle>dev</refentrytitle></citerefentry> contient le nombre majeur et mineur du
p&eacute;riph&eacute;rique ouvert. Ils sont disponibles par les macros
<citerefentry><refentrytitle><function>major()</function></refentrytitle></citerefentry> et <citerefentry><refentrytitle><function>minor()</function></refentrytitle></citerefentry>
</para>

<para>Les arguments <citerefentry><refentrytitle>flag</refentrytitle></citerefentry> et <citerefentry><refentrytitle>mode</refentrytitle></citerefentry> sont comme d&eacute;crits sur
la page de manuel de 
<ulink url="http://www.freebsd.org/cgi/man.cgi?open(2)">open</ulink>. 
Il est recommand&eacute; que vous examiniez
ces derniers pour vous assurer des droits d'acc&egrave;s dans &lt;sys/fcntl.h>
et faire ce qui est exig&eacute;.  Par exemple si <citerefentry><refentrytitle>flag </refentrytitle></citerefentry> est
(O_NONBLOCK | O_EXLOCK) l'ouverture &eacute;chouerait si il bloquait ou
si l'acc&egrave;s exclusif ne pouvait pas &ecirc;tre accord&eacute;.
</para>

<para>
L'argument <citerefentry><refentrytitle>p</refentrytitle></citerefentry> contient toutes les informations &agrave; propos du
processus actuel.
</para>
</sect4>  

<sect4>  
<title><function>d_close()</function></title>
<para> <function>d_close()</function> prend la m&ecirc;me liste d'argument que <function>d_open()</function>:  
</para>

<programlisting>
int
d_close(dev_t dev , int flag , int mode , struct proc *p)
</programlisting>

<para><function>d_close()</function> est seulement appel&eacute; &agrave; la derni&egrave;re fermeture de votre
p&eacute;riph&eacute;rique (par p&eacute;riph&eacute;rique mineur).  Par exemple dans le fragment
suivant de code, <function>d_open()</function> est appel&eacute; 3 fois, mais <function>d_close()</function>
seulement une fois.  
</para>

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

<para>Les arguments sont semblables &agrave; ceux d&eacute;crits ci-dessus pour
<function>d_open()</function>.
</para>
</sect4>

<sect4>  
<title><function>d_read()</function> et <function>d_write()</function></title>

<para><function>d_read()</function> et d_write prennent les listes suivantes d'argument:  
</para>

<programlisting>
int
d_read(dev_t dev, struct uio *uio, int flat)
int
d_write(dev_t dev, struct uio *uio, int flat)
</programlisting>

<para>
Les points d'entr&eacute;e de <function>d_read()</function> et de <function>d_write()</function> sont appel&eacute;s quand
<ulink url="http://www.freebsd.org/cgi/man.cgi?read(2)">read</ulink>  et 
<ulink url="http://www.freebsd.org/cgi/man.cgi?write(2)">write</ulink>
sont appel&eacute;s sur votre p&eacute;riph&eacute;rique depuis l'espace utilisateur.  Le transfert
des donn&eacute;es peut &ecirc;tre manipul&eacute; par la routine du noyau <function>uiomove()</function>.
</para>
</sect4>

<sect4>
<title><function>d_ioctl()</function></title>

<para> Sa liste d'argument est comme suit:  
</para>
<programlisting>
int
d_ioctl(dev_t dev, int cmd, caddr_t arg, int flag, struct proc *p)
</programlisting>

<para>
<function>d_ioctl()</function> est un fourre-tout pour les ex&eacute;cutions qui ne semblent
pas raisonnable dans un paradigme lecture/&eacute;criture.  Le plus
c&eacute;l&egrave;bre de tout les ioctl est probablement celui sur des p&eacute;riph&eacute;riques
tty, par le 
<ulink url="http://www.freebsd.org/cgi/man.cgi?stty(1)">stty</ulink>.

Le point d'entr&eacute;e d'ioctl est appel&eacute; depuis l'<function>ioctl()</function> de
<filename>sys/kern/sys_generic.c</filename></para>

<para>
Il y a quatre types diff&eacute;rents d'ioctl qui peuvent &ecirc;tre impl&eacute;ment&eacute;s.

&lt;sys/ioccom.h>  contient des macros pratiques de
pour d&eacute;finir ces ioctls.
</para>

<itemizedlist>
<listitem> 
<para><citerefentry><refentrytitle>_IO(g, n) </refentrytitle></citerefentry> pour les op&eacute;rations de type contr&ocirc;le.
</para>
</listitem>

<listitem> 
<para>
<citerefentry><refentrytitle>_IOR(g, n, t) </refentrytitle></citerefentry> pour des op&eacute;rations lisant des donn&eacute;es d'un
p&eacute;riph&eacute;rique.
</para>
</listitem>

<listitem> 
<para>
<citerefentry><refentrytitle>_IOW(g, n, t) </refentrytitle></citerefentry> pour les op&eacute;rations &eacute;crivant des donn&eacute;es
sur un p&eacute;riph&eacute;rique.
</para>
</listitem>

<listitem> 
<para>
<citerefentry><refentrytitle>_IOWR(g,n,t)</refentrytitle></citerefentry> pour les op&eacute;rations &eacute;crivant sur un p&eacute;riph&eacute;rique
puis lisent les donn&eacute;es.
</para>
</listitem>
</itemizedlist>


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

<sect4>
<title><function>d_stop()</function></title>
<para></para>
</sect4>  

<sect4>
<title><function>d_reset()</function></title>
<para></para>
</sect4>  

<sect4>
<title><function>d_devtotty()</function></title>
<para></para>
</sect4>  

<sect4>
<title><function>d_poll()</function> (3.0 et plus) ou <function>d_select()</function> (2.2)</title>

<para>la liste d'argument de <function>d_poll()</function> est comme suit :
</para>

<programlisting>
void
d_poll(dev_t dev, int events, struct proc *p)
</programlisting>

<para>  <function>d_poll()</function> est employ&eacute; pour d&eacute;couvrir si un p&eacute;riph&eacute;rique
est pr&ecirc;t pour les E/S. Par exemple, attendre que des donn&eacute;es du r&eacute;seau
soient dispnibles, ou que l'utilisateur presse une touche.
Cela correspond &agrave; un appel de <function>poll()</function> dans l'espace utilisateur.
</para>

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

<programlisting>
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 = &amp;my_softc[dev];

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

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

<para> <function>d_select()</function> est utilis&eacute; dans la version 2.2 et 
pr&eacute;c&eacute;dentes de FreeBSD.  Au lieu de 'events', il prend un simple 
entier 'rw', qui peut &ecirc;tre FREAD pour la lecture (comme dans
POLLIN ci-dessus), FWRITE pour l'&eacute;criture (comme dans POLLOUT ci-dessus),
et 0 pour 'exception' - lorsque quelque chose d'exceptionnel se produit,
comme une carte &eacute;tant ins&eacute;r&eacute;e ou retir&eacute;e pour le pilote de
pccard.
</para>
<para>Pour 'select', le fragment correspondant &agrave; la description
ci-dessus ressembleraient &agrave; ceci:  
</para>
<programlisting>
static int
mydevselect(dev_t dev, int rw, struct proc *p)
{
        int ret = 0;
        int s;
        struct my_softc *sc = &amp;my_softc[dev];

        s = <function>splhigh()</function>;
        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, &amp;sc->selp);
        splx(s);
        return(revents);
}
</programlisting>
</sect4>

<sect4> 
<title><function>d_mmap()</function></title> 
<para></para>
</sect4>  

<sect4>  
<title><function>d_strategy()</function></title> 

<para>
La liste d'argument de <function>d_strategy()</function> est comme suit :
</para>

<programlisting>
void
d_strategy(struct buf *bp)
</programlisting>

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

<sect3>  
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>

<sect2>
<title>Bloc</title>

<sect3>
<title>Structures de donn&eacute;es</title>
<para> Structure <citerefentry><refentrytitle>struct bdevsw</refentrytitle></citerefentry>
</para>

<para> Structure <citerefentry><refentrytitle>struct buf</refentrytitle></citerefentry>
</para>
</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>d_open()</function></title>
<para> D&eacute;crit dans la section p&eacute;riph&eacute;rique de caract&egrave;re.
</para>
</sect4>

<sect4>
<title><function>d_close()</function></title>
<para>D&eacute;crit dans la section p&eacute;riph&eacute;rique de caract&egrave;re.
</para>
</sect4>

<sect4>
<title><function>d_strategy()</function></title>
<para>D&eacute;crit dans la section p&eacute;riph&eacute;rique de caract&egrave;re.
</para>
</sect4>

<sect4>
<title><function>d_ioctl()</function></title>
<para>D&eacute;crit dans la section p&eacute;riph&eacute;rique de caract&egrave;re.
</para>
</sect4>

<sect4>
<title><function>d_dump()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>d_psize()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>  
 
<sect2>  
<title>R&eacute;seau</title>
<para>Structure <citerefentry><refentrytitle>struct ifnet</refentrytitle></citerefentry>
</para>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>if_init()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>if_output()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>if_start()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>if_done()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>if_ioctl()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>if_watchdog()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>  
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>

<sect2>  
<title>Protocole de communication</title>

<sect3> 
<title>Structures de donn&eacute;es</title>
<para>Structure <citerefentry><refentrytitle>struct linesw</refentrytitle></citerefentry>
</para>
</sect3> 

<sect3> 
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>l_open()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_close()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_read()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_write()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_ioctl()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_rint()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_start()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>l_modem()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>
</sect1>

<sect1>
<title>Bus Support&eacute;s</title>

<sect2>
<title>ISA -- Architecture Standard d'Industrie (<foreignphrase>Industry Standard
Architecture</foreignphrase></title> 

<sect3> 
<title>Structures de donn&eacute;es</title>

<sect4>
<title>Structure <citerefentry><refentrytitle>struct isa_device</refentrytitle></citerefentry></title>

<para>Cette structure est obligatoire, mais g&eacute;n&eacute;ralement elle est cr&eacute;&eacute;e par
<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink> &agrave; partir du fichier de configuration de noyau.
Elle est requise pour chaque p&eacute;riph&eacute;rique, c'est &agrave; dire que si vous avez
un pilote de p&eacute;riph&eacute;rique contr&ocirc;lant deux "serial boards", vous
aurez deux structures isa_device. Si vous construisez un p&eacute;riph&eacute;rique
comme un LKM, vous devrez cr&eacute;er votre propre structure isa_device afin
de refl&eacute;ter votre configuration (lignes 85 - 131 de pcaudio_lkm.c).
Il y a un &eacute;quivalence directe ebtre le fichier de configuration et la
structureisa_device.  La d&eacute;finition de 
<filename>/usr/src/sys/i386/isa/isa_device.h</filename>
est :  
</para>

<programlisting>
struct isa_device {
        int     id_id;          /* device id */
        struct  isa_driver *id_driver;
        int     id_iobase;      /* base i/o address */
        u_short id_irq;         /* interrupt request */
        short   id_drq;         /* DMA request */
        caddr_t id_maddr;       /* physical i/o memory address on bus (if any)*/
        int     id_msize;       /* size of i/o memory */
        inthand2_t *id_intr;    /* interrupt interface routine */
        int     id_unit;        /* unit number */
        int     id_flags;       /* flags */
        int     id_scsiid;      /* scsi id if needed */
        int     id_alive;       /* device is present */
#define RI_FAST         1               /* fast interrupt handler */
        u_int   id_ri_flags;    /* flags for <function>register_intr()</function> */
        int     id_reconfig;    /* hot eject device support (such as PCMCIA) */
        int     id_enabled;     /* is device enabled */
        int     id_conflicts;   /* we're allowed to conflict with things */
        struct isa_device *id_next; /* used in isa_devlist in <function>userconfig()</function> */
};
</programlisting>
</sect4>

<sect4>
<title>Structure <citerefentry><refentrytitle>struct isa_driver</refentrytitle></citerefentry></title>

<para>Cette structure est d&eacute;finie dans 
<filename>/usr/src/sys/i386/isa/isa_device.h</filename>,
est est requise pour chaque pilote de p&eacute;riph&eacute;rique. La d&eacute;finition
est :
</para>

<programlisting>
struct isa_driver { 
        int     (*probe) __P((struct isa_device *idp));
                                        /* test whether device is present */
        int     (*attach) __P((struct isa_device *idp));
                                        /* setup driver for a device */
        char    *name;                  /* device name */
        int     sensitive_hw;           /* true if other probes confuse us */
};
</programlisting>

<para>
C'est la structure employ&eacute;e par le code sondage/attachement
(<foreignphrase>probe/attach</foreignphrase>) pour
d&eacute;tecter et initialiser votre p&eacute;riph&eacute;rique. Le membre <citerefentry><refentrytitle>probe</refentrytitle></citerefentry> 
est un pointeur &agrave; votre fonction permettant de sonder les p&eacute;riph&eacute;riques.
Le membre <citerefentry><refentrytitle>attach</refentrytitle></citerefentry> est un pointeur vers votre fonction d'attache.
Le membre <citerefentry><refentrytitle>name</refentrytitle></citerefentry> est un pointeur de caract&egrave;re sur le nom de deux 
ou trois lettres de votre pilote.  
C'est le nom enregistr&eacute; pendant le processus de
sondage/attachement (et probablement aussi dans 
<ulink url="http://www.freebsd.org/cgi/man.cgi?lsdev(8)">lsdev</ulink>). 
Le membre <citerefentry><refentrytitle>sensitive_hw </refentrytitle></citerefentry> est un
indicateur qui aide le code de sondage &agrave; d&eacute;terminer l'ordre du sondage.
</para>

<para>
Un instantiation typique est:   
</para>

<programlisting>
struct  isa_driver      mcddriver = { mcd_probe, mcd_attach, "mcd" };
</programlisting>
</sect4>
</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>probe()</function></title>
<para><function>probe()</function> prend un pointeur sur une structure isa_device
comme argument et renvoie un int.  La valeur de retour est ``z&eacute;ro'' ou 
``non-z&eacute;ro '' quant &agrave; l'absence ou &agrave; la pr&eacute;sence de votre p&eacute;riph&eacute;rique.  
Ce point d'entr&eacute;e peut &ecirc;tre d&eacute;clar&eacute; comme 
<citerefentry><refentrytitle>static</refentrytitle></citerefentry> parce qu'il
est accessible par l'interm&eacute;diaire du membre 
<citerefentry><refentrytitle>probe</refentrytitle></citerefentry> de la structre
isa_driver. Cette fonction est destin&eacute;e &agrave;
d&eacute;tecter la pr&eacute;sence de votre p&eacute;riph&eacute;rique seulement et ne devrait
faire aucune configuration du p&eacute;riph&eacute;rique elle-m&ecirc;me.
</para>
</sect4>

<sect4>
<title><function>attach()</function></title>
<para>
<function>attach()</function> prend &eacute;galement un pointeur sur une structure
isa_device comme argument et
renvoie un int.  La valeur de retour est &eacute;galement ``z&eacute;ro'' ou
``non-z&eacute;ro'' indiquant si l'attache a r&eacute;ussie.  Cette fonction
est destin&eacute;e pour faire n'importe quelle initialisation sp&eacute;ciale du
p&eacute;riph&eacute;rique aussi bien que pour confirmer que le p&eacute;riph&eacute;rique est utilisable.
Il devrait aussi &ecirc;tre d&eacute;clar&eacute; <citerefentry><refentrytitle>static</refentrytitle></citerefentry> parce qu'il est accesible
par le membre <citerefentry><refentrytitle>attach</refentrytitle></citerefentry> de la structure <citerefentry><refentrytitle>isa_driver </refentrytitle></citerefentry>.
</para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>

<sect2>
<title>EISA -- Architecture Étendue de Standard industriel (<foreignphrase>Extended Industry Standard Architecture</foreignphrase>)</title>
<para></para>

<sect3>
<title>Structures de donn&eacute;es</title>

<para>Structure <citerefentry><refentrytitle>struct eisa_dev </refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct isa_driver</refentrytitle></citerefentry> </para>
</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>probe()</function></title>
<para>D&eacute;crit dans la section de p&eacute;riph&eacute;rique ISA.</para>
</sect4>

<sect4>
<title><function>attach()</function></title>
<para>D&eacute;crit dans la section de p&eacute;riph&eacute;rique ISA.</para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>

<sect2>
<title>PCI -- Bus d'interconnexion P&eacute;riph&eacute;rique (<foreignphrase>Peripheral Computer
Interconnect</foreignphrase>)</title>

<sect3>
<title>Structures de donn&eacute;es</title>

<para>  Structure <citerefentry><refentrytitle>struct pci_device</refentrytitle></citerefentry>
</para>

<itemizedlist>
<listitem>
<para>nom :  Le nom abr&eacute;g&eacute; du p&eacute;riph&eacute;rique.
</para>
</listitem>

<listitem>
<para> sonde:  Contr&ocirc;le si le pilote peut supporter un p&eacute;riph&eacute;rique avec
ce type.  L'&eacute;tiquette peut &ecirc;tre employ&eacute;e pour obtenir plus
d'information avec <function>pci_read_conf()</function>. Voir ci-dessous.  Elle renvoie
une cha&icirc;ne de caract&egrave;res avec le nom du p&eacute;riph&eacute;rique, ou un pointeur
NULL si le pilote ne peut pas supporter ce p&eacute;riph&eacute;rique.
</para>
</listitem>

<listitem>
<para> attache:  Assigne une structure de contr&ocirc;le et la pr&eacute;pare.  Cette
fonction peut utiliser les fonctions de mapping PCI. Voir
ci-dessous.  (identification de configuration) ou type.
</para>
</listitem>

<listitem>
<para> compte:  Un pointeur sur un compteur d'unit&eacute;.  Il est
employ&eacute; par le configurateur de PCI pour assigner des num&eacute;ros.
</para>
</listitem>
</itemizedlist>

</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>probe()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>attach()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>shutdown()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>

<sect2>
<title>SCSI -- <foreignphrase>Small Computer Systems Interface</foreignphrase></title>

<sect3>
<title>Structure de donn&eacute;es</title>

<para>Structure <citerefentry><refentrytitle>struct scsi_adapter</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct scsi_device</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct scsi_ctlr_config</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct scsi_device_config</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct scsi_link</refentrytitle></citerefentry> </para>
</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>attach()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>init()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>
<para></para>
</sect3>
</sect2>


<sect2>
<title>PCCARD (PCMCIA)</title>

<sect3>
<title>Structure de donn&eacute;es</title>

<para>Structure <citerefentry><refentrytitle>struct slot_cont</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct pccard_drv</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct pccard_dev</refentrytitle></citerefentry> </para>
<para>Structure <citerefentry><refentrytitle>struct slot</refentrytitle></citerefentry> </para>
</sect3>

<sect3>
<title>Points d'entr&eacute;e</title>

<sect4>
<title><function>handler()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>unload()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>suspend()</function></title>
<para></para>
</sect4>

<sect4>
<title><function>init()</function></title>
<para></para>
</sect4>
</sect3>

<sect3>
<title>Fichiers d'en-t&ecirc;te</title>

<para>&lt;pccard/slot.h>
</para>
</sect3>
</sect2>
</sect1>


<sect1>
<title>Incorporation dans le noyau</title>

<para>Dans FreeBSD, le support des bus d'ISA et EISA est sp&eacute;cifique &agrave;
i386.  Tandis que FreeBSD lui-m&ecirc;me est actuellement
disponible sur la plateforme i386, un certain effort a &eacute;t&eacute; fait pour
faire du code portable pour PCI, PCCARD, et SCSI.  Le code
sp&eacute;cifique &agrave; ISA et EISA r&eacute;side dans 
<filename>/usr/src/sys/i386/isa</filename> et 
<filename>/usr/src/sys/i386/eisa</filename> respectivement.  Le code ind&eacute;pendant de la
machine de PCI, de PCCARD, et de SCSI r&eacute;side dans
<filename>/usr/src/sys/{pci,pccard,scsi</filename>}. Le code sp&eacute;cifique i386 quand &agrave; lui
r&eacute;side dans <filename>/usr/src/sys/i386/{pci, pccard, scsi}</filename>.
</para>


<para>  Dans FreeBSD, un module de gestion de p&eacute;riph&eacute;rique peut
&ecirc;tre soit sous forme binaire soit sous forme de sources.  
Il n'y a aucun endroit ``officiel'' pour mettre les binaires des
pilotes de p&eacute;riph&eacute;riques.  les syst&egrave;mes BSD utilise quelque
chose comme sys/i386/OBJ.  Puisque la plupart des pilotes sont
distribu&eacute;s dans les sources, la discussion suivante se rapporte &agrave; un
source pilote de p&eacute;riph&eacute;rique.  
Des binaires de pilotes de p&eacute;riph&eacute;riques sont
parfois fournis par les constructeurs de mat&eacute;riel qui souhaitent
maintenir les sources de mani&egrave;re propri&eacute;taire.
</para>

<para>  Un pilote typique a son code source sous forme de fichier C,
comme dev.c.  Le pilote peut &eacute;galement inclure des
fichiers;  devreg.h contient typiquement des d&eacute;clarations publiques
de registre de p&eacute;riph&eacute;rique, des macros, et d'autres
d&eacute;clarations sp&eacute;cifique au pilote de p&eacute;riph&eacute;rique. 
Quelques pilotes appellent parfois ce fichier devvar.h.  
Quelques pilotes, tels que
le dgb (pour le Digiboard PC/Xe), exigent que du microcode soit charg&eacute;
sur la carte.  Pour le pilote de dgb le microcode est compil&eacute;
et report&eacute; dans un fichier d'en-t&ecirc;te par 
<ulink url="http://www.freebsd.org/cgi/man.cgi?file2c(1)">file2c</ulink>.
</para>

<para>  Si le pilote de p&eacute;riph&eacute;rique a des structures de donn&eacute;es et des
ioctl qui sont sp&eacute;cifiques au pilote de p&eacute;riph&eacute;rique ou
p&eacute;riph&eacute;rique, et
doivent &ecirc;tre accessibles de l'espace-utilisateur, elles devraient
&ecirc;tre mises dans un fichier d'en-t&ecirc;te s&eacute;par&eacute; qui r&eacute;sidera dans 
<filename>/usr/include/machine/</filename> (certaines de ces derniers r&eacute;sident dans 
<filename>/usr/include/sys/</filename>). Ceux-ci est typiquement nomm&eacute; quelque chose comme
ioctl_dev.h ou devio.h.
</para>

<para>  Si un pilote &eacute;crit depuis l'espace
d'utilisateur est identique &agrave; un p&eacute;riph&eacute;rique qui existe d&eacute;j&agrave;, il faut
prendre garde &agrave; utiliser les m&ecirc;mes
interfaces ioctl et structures de donn&eacute;es.  Par exemple, de l'espace
utilisateur, un lecteur de SCSI CDROM devrait &ecirc;tre identique &agrave; un
lecteur de cdrom IDE;  ou une ligne s&eacute;rie sur une carte
intelligente multiport (Digiboard, Cyclades...)  devrait &ecirc;tre identique
&agrave; un p&eacute;riph&eacute;rique sio.  Ces p&eacute;riph&eacute;riques ont une interface d&eacute;finie
relativement bonne et devraient &ecirc;tre utilis&eacute;es.
</para>

<para>  Il y a deux m&eacute;thodes pour lier un pilote dans le
noyau, statiquement et le mod&egrave;le LKM.  La premi&egrave;re m&eacute;thode
est assez standard &agrave; travers la famille *BSD.  L'autre
m&eacute;thode a &eacute;t&eacute; initialement d&eacute;velopp&eacute;e par Sun (je crois), et a
&eacute;t&eacute; mis en application dans BSD en utilisant le mod&egrave;le de Sun.
Je ne crois pas que l'impl&eacute;mentation actuelle utilise encore le moindre
code de Sun.
</para>

<sect2>
<title>Mod&egrave;le Standard</title>

<para>  Les &eacute;tapes exig&eacute;es pour ajouter votre pilote au
noyau standard de FreeBSD sont 
</para>

<itemizedlist>  
<listitem>
<para>Ajout &agrave; la liste des pilotes de p&eacute;riph&eacute;rique
</para>
</listitem>

<listitem>
<para>Ajout d'une entr&eacute;e au &lsqb;bc&rsqb;devsw
</para>
</listitem>

<listitem>
<para>Ajout d'une entr&eacute;e du pilote de p&eacute;riph&eacute;rique au fichier de
configuration du noyau
</para>
</listitem>

<listitem>
<para><ulink
url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink>,
compilation et installation du noyau
</para>
</listitem>

<listitem>
<para>cr&eacute;er les fichiers sp&eacute;ciaux requis
</para>
</listitem>

<listitem>
<para>red&eacute;marrage
</para>
</listitem>
</itemizedlist>  

<sect3>  
<title>Ajout &agrave; la liste des pilotes de p&eacute;riph&eacute;rique</title>

<para>Le mod&egrave;le standard pour ajouter un module de gestion de p&eacute;riph&eacute;rique
au noyau de Berkeley est d'ajouter votre pilote &agrave; la liste des
p&eacute;riph&eacute;riques connus.  Cette liste d&eacute;pend de l'architecture du CPU.
Si le p&eacute;riph&eacute;rique n'est pas sp&eacute;cifique i386
(PCCARD, PCI, SCSI), le fichier est dans 
<filename>/usr/src/sys/conf/files</filename>.
Si le p&eacute;riph&eacute;rique est sp&eacute;cifique i386, utilisez
<filename>/usr/src/sys/i386/conf/files.i386</filename>.  Une ligne typique ressemblerait
&agrave; :  
</para>

<programlisting>
i386/isa/joy.c                  optional        joy     device-driver
</programlisting>

<para>Le premier champ relatif est le chemin du module de pilote 
par rapport &agrave; <filename>/usr/src/sys</filename>.
Pour le cas d'un pilote binaire,  le chemin d'acc&egrave;s serait quelque
chose comme <filename>i386/OBJ/joy.o</filename>.
</para>

<para>Le deuxi&egrave;me champ indique &agrave; 
<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config(8)</ulink>  
que c'est un pilote facultatif.  Quelques
p&eacute;riph&eacute;riques sont obligatoires pour que le noyau puisse &ecirc;tre construit.
</para>

<para>
Le troisi&egrave;me champ est le nom du p&eacute;riph&eacute;rique.
</para>

<para>Le quatri&egrave;me champ indique &agrave; config que c'est un 
pilote de p&eacute;riph&eacute;rique (par opposition &agrave; juste facultatif).  Ceci
dit &agrave; config  de cr&eacute;er des entr&eacute;es pour le p&eacute;riph&eacute;rique dans dans
des structures de <filename>/usr/src/sys/compile/KERNEL/ioconf.c</filename>.
</para>

<para>Il est &eacute;galement possible de cr&eacute;er un fichier
<filename>/usr/src/sys/i386/conf/files.KERNEL</filename> dont le contenu ignorera le
fichier par d&eacute;faut files.i386, mais seulement pour le noyau ``KERNEL''.
</para>
</sect3>

<sect3>
<title>Faire de la place dans conf.c</title>

<para>Maintenant vous devez &eacute;diter <filename>/usr/src/sys/i386/i386/conf.c</filename>
pour faire une entr&eacute;e pour votre pilote.  Quelque part au d&eacute;but,
vous devez d&eacute;clarer vos points d'entr&eacute;e.  L'entr&eacute;e pour
le pilote du joystick est:  </para>

<programlisting>
#include "joy.h" 
#if NJOY > 0
d_open_t        joyopen;
d_close_t       joyclose;
d_rdwr_t        joyread;
d_ioctl_t       joyioctl;
#else
#define joyopen         nxopen
#define joyclose        nxclose
#define joyread         nxread
#define joyioctl        nxioctl
#endif
</programlisting>

<para>
Cela d&eacute;finit vos points d'entr&eacute;e, ou points d'entr&eacute;e nuls qui
renverront ENXIO quand appel&eacute; (clause #else).
</para>

<para>
Le fichier d'en-t&ecirc;te ``joy.h'' est automatiquement produit par 
<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink>  
quand l'arborescence de construction du noyau est
cr&eacute;&eacute;.  Cela se r&eacute;duit habituellement &agrave; une seule ligne comme :
</para>

<programlisting>
#define NJOY 1
</programlisting> 

<para>
ou 
</para>
<programlisting>
#define NJOY 0
</programlisting>

<para> ce qui d&eacute;finit le nombre de vos p&eacute;riph&eacute;riques dans votre noyau.
</para>

<para>
Vous devez additionnellement ajouter un slot au cdevsw&lsqb;&rsqb, ou
au bdevsw&lsqb;&rsqb, selon que ce soit un p&eacute;riph&eacute;rique caract&egrave;re,
p&eacute;riph&eacute;rique bloc, ou les deux si c'est un p&eacute;riph&eacute;rique bloc
avec une interface brute.  L'entr&eacute;e pour le pilote du joystick
est:
</para>

<programlisting>
/* open, close, read, write, ioctl, stop, reset, ttys, select, mmap, strat */
struct cdevsw   cdevsw[] =
{
 ...
        { joyopen,      joyclose,       joyread,        nowrite, /*51*/
          joyioctl,     nostop,         nullreset, nodevtotty,/*joystick */
          seltrue,      nommap,         NULL},
 ...
}
</programlisting>

<para>
 L'ordre est ce qui d&eacute;termine le nombre majeur de votre
p&eacute;riph&eacute;rique.  C'est pourquoi il y aura toujours une entr&eacute;e
pour votre pilote, que ce soit des points d'entr&eacute;e nuls, 
ou des points d'entr&eacute;e actuels.  Il est probablement int&eacute;ressant de 
noter que c'est
sensiblement diff&eacute;rent de SCO et d'autres d&eacute;riv&eacute;s du syst&egrave;me V, o&ugrave;
n'importe quel p&eacute;riph&eacute;rique (dans la th&eacute;orie) peut avoir n'importe 
quel nombre majeur.  C'est en grande partie un avantage sur FreeBSD,
sur la mani&egrave;re dont les fichiers sp&eacute;ciaux de p&eacute;riph&eacute;rique sont cr&eacute;&eacute;s.  
Nous reviendrons en d&eacute;tail sur ceci plus tard.  
</para> 
</sect3>

<sect3>
<title>Ajout de votre p&eacute;riph&eacute;rique dans le fichier de configuration.</title>

<para> Ceci ajoute simplement une ligne d&eacute;crivant votre p&eacute;riph&eacute;rique.  La
ligne de description du joystick est :  
<programlisting>
device          joy0    at isa? port "IO_GAME"
</programlisting>
Ceci indique que nous avons un
p&eacute;riph&eacute;rique appel&eacute; ``joy0'' sur le bus ISA en utilisant
le port E/S ``IO_GAME'' (IO_GAME est une macro d&eacute;finie dans
<filename>/usr/src/sys/i386/isa/isa.h</filename>).
</para>

<para>
Une entr&eacute;e l&eacute;g&egrave;rement plus compliqu&eacute;e est pour le pilote ``ix'' :

<programlisting>
device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768
vector ixintr
</programlisting>

Ceci indique que nous avons un p&eacute;riph&eacute;rique appel&eacute; 
`ix0 ' sur le bus ISA.  Il utilise le port E/S 0x300.  Son
interruption sera masqu&eacute; par d'autres p&eacute;riph&eacute;riques dans la classe 
r&eacute;seau.  Il utilise l'interruption 10.  Il utilise 32k de m&eacute;moire
partag&eacute;e &agrave; l'adresse physique 0xd0000.  Il le d&eacute;finit &eacute;galement
son pilote d'interruption comme &eacute;tant ``<function>ixintr()</function>''
</para>
</sect3>

<sect3>
<title><ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink>
du noyau.</title>

<para>  Maintenant avec notre fichier de configuration en main,
nous pouvons cr&eacute;er un r&eacute;pertoire de compilation du noyau. Cela peut &ecirc;tre
fait en tapant :
<programlisting>
# config KERNEL
</programlisting>

o&ugrave; KERNEL est le nom de votre fichier de configuration.  
La configuration cr&eacute;e un arbre de compilation 
pour votre noyau dans <filename>/usr/src/sys/compile/KERNEL</filename>.  Elle cr&eacute;e le fichier
makefile, quelques fichiers C, et quelques fichiers H avec des
macros d&eacute;finissant le nombre de chaque p&eacute;riph&eacute;rique &agrave; inclure dans votre
votre noyau.
</para>

<para>
Maintenant vous pouvez aller dans le r&eacute;pertoire de compilation et
construire votre noyau. À chaque fois que vous lancerez config, votre
arbre de construction pr&eacute;c&eacute;dent sera retir&eacute;, &agrave; moins que vous ne lancez
config avec un -n. Si vous avez configur&eacute; et compil&eacute; un noyau GENERIC,
vous pouvez faire un ``make links'' afin d'&eacute;viter de compiler certains
fichiers &agrave; chaque it&eacute;ration. Typiquement, je lance :
<programlisting>  
# make depend links all
</programlisting>  
suivi d'un ``make install'' quand le noyau me convient.
</para>
</sect3>

<sect3>
<title>Cr&eacute;er les fichiers sp&eacute;ciaux de p&eacute;riph&eacute;riques</title>

<para> Sur FreeBSD, vous avez la responsabilit&eacute; de faire vos propres fichiers
sp&eacute;ciaux 
de p&eacute;riph&eacute;rique.  Le
nombre majeur de votre p&eacute;riph&eacute;rique est d&eacute;termin&eacute; par le nombre de
slots dans le commutateur de p&eacute;riph&eacute;rique.  Le nombre mineur est
d&eacute;pendant du  pilote, naturellement.  Vous pouvez 
soit ex&eacute;cuter mknod depuis la ligne de commande, soit liasser faire le
travail &agrave; <filename>/dev/MAKEDEV.local</filename>, ou m&ecirc;me 
<filename>/dev/MAKEDEV</filename>.
Je cr&eacute;e parfois un script MAKEDEV.dev qui peut &ecirc;tre soit lanc&eacute; 
de mani&egrave;re autonome soit coll&eacute; dans <filename>/dev/MAKEDEV.local</filename>.
</para>
</sect3>

<sect3>
<title>Red&eacute;marrage</title>  
<para>  C'est la partie facile.  Il y a un
certain nombre de m&eacute;thodes pour faire ceci, reboot, fastboot,
shutdown - r, couper le courant, etc.  Au d&eacute;marrage, vous
devriez voir votre XX<function>probe()</function> appel&eacute;, et si tout marche,
votre <function>attach()</function> aussi.
</para>
</sect3>
</sect2>

<sect2>  
<title>Module du noyau &agrave; chargement dynamique (LKM)</title>

<para>Il n'y a vraiment aucune proc&eacute;dure d&eacute;finie pour &eacute;crire un pilote de
LKM.  Ce qui suit est ma propre conception apr&egrave;s exp&eacute;rimentation
avec l'interface de p&eacute;riph&eacute;rique LKM et en regardant le mod&egrave;le standard
de module de gestion de p&eacute;riph&eacute;rique, c'est une mani&egrave;re d'ajouter une
interface LKM &agrave; un pilote existant sans toucher aux sources (ou binaire)
initiaux de pilote .  On recommande cependant,
que si vous projetez de distribuer les sources de votre pilote, 
que les parties sp&eacute;cifiques  LKM devraient faire partie du pilote
lui-m&ecirc;me, compil&eacute; de mani&egrave;re conditionnelle par la macro LKM
(c.-&agrave;-d. #ifdef LKM).
</para>

<para>
Cette section se concentrera sur la mani&egrave;re d'&eacute;crire la partie sp&eacute;cifique
LKM du pilote.  Nous supposerons que nous avons &eacute;crit un
pilote qui atterrira dans le mod&egrave;le standard de
gestion de p&eacute;riph&eacute;rique, que nous voudrions maintenant mettre en
application comme &eacute;tant LKM.  Nous utiliserons le pilote de pcaudio
comme pilote d'exemple, et d&eacute;velopperons une entr&eacute;e LKM.  La
source et le fichier makefile pour le LKM pcaudio , ``pcaudio_lkm.c''
et ``Makefile'', devraient &ecirc;tre dans plac&eacute; <filename>/usr/src/lkm/pcaudio</filename>.
Ce qui suit est le code comment&eacute; de pcaudio_lkm.c.
</para>

<para>
Lignes 17 - 26
</para>

<para>
Ceci inclut le fichier ``pca.h'' et fait une compilation conditionnelle 
du reste de LKM suivant que vous avez d&eacute;fini ou non le pilote de 
p&eacute;riph&eacute;rique pcaudio.
Cela imite le comportement de config. Dans un pilote de
p&eacute;riph&eacute;rique standard,
<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink>
produit le fichier pca.h depuis le nombre de p&eacute;riph&eacute;riques pca
le fichier de config.  </para>

<programlisting>
    17  /*
    18   * figure out how many devices we have..
    19   */
    20
    21  #include "pca.h"
    22
    23  /*
    24   * if we have at least one ...
    25   */
    26  #if NPCA > 0
</programlisting>

<para>
Lignes 27 - 37
</para>

<para>
Les fichiers d'en-t&ecirc;te requis depuis divers r&eacute;pertoire d'inclusion.
</para>

<programlisting>
    27  #include &lt;sys/param.h>
    28  #include &lt;sys/systm.h>
    29  #include &lt;sys/exec.h>
    30  #include &lt;sys/conf.h>
    31  #include &lt;sys/sysent.h>
    32  #include &lt;sys/lkm.h>
    33  #include &lt;sys/errno.h>
    34  #include &lt;i386/isa/isa_device.h>
    35  #include &lt;i386/isa/isa.h>       
    36    
    37
</programlisting>

<para>
Lignes 38 -  51
</para>

<para>d&eacute;clarent vos points d'entr&eacute;e comme externs .
</para>

<programlisting>
    38  /*
    39   * declare your entry points as externs
    40   */
    41
    42  extern int pcaprobe(struct isa_device *);
    43  extern int pcaattach(struct isa_device *);
    44  extern int pcaopen(dev_t, int, int, struct proc *);
    45  extern int pcaclose(dev_t, int, int, struct proc *);
    46  extern int pcawrite(dev_t, struct uio *, int);
    47  extern int pcaioctl(dev_t, int, caddr_t);
    48  extern int pcaselect(dev_t, int, struct proc *);
    49  extern void pcaintr(struct clockframe *);
    50  extern struct isa_driver pcadriver;
    51
</programlisting>

<para>
Lignes 52 - 70
</para>

<para>
Cela cr&eacute;e la table d'entr&eacute;e de commutateur de p&eacute;riph&eacute;rique pour
votre pilote. Cette table est en gros enti&egrave;rement mise dans le
syst&egrave;me de commutation de p&eacute;riph&eacute;riques &agrave; l'emplacement indiqu&eacute; par
votre nombre majeur.  Dans le mod&egrave;le standard, c'est dans
<filename>/usr/src/sys/i386/i386/conf.c</filename>.
NOTE:  vous ne pouvez pas s&eacute;lectionner
un nombre majeur de p&eacute;riph&eacute;rique plus grand que ce qui existe dans
conf.c, par exemple
il y a 67 slots pour des p&eacute;riph&eacute;riques caract&egrave;re, vous ne popuvez pas
utiliser un p&eacute;riph&eacute;rique (caract&egrave;re) de num&eacute;ro majeur 67  ou
plus, sans avoir d'abord r&eacute;serv&eacute; de l'espace dans conf.c.
</para>

<programlisting>
    52  /*
    53   * build your device switch entry table
    54   */
    55
    56  static struct cdevsw pcacdevsw = {
    57    (d_open_t *)      pcaopen, /* open */
    58    (d_close_t *)     pcaclose, /* close */
    59    (d_rdwr_t *)      enodev, /* read */
    60    (d_rdwr_t *)      pcawrite, /* write */
    61    (d_ioctl_t *)     pcaioctl, /* ioctl */
    62    (d_stop_t *)      enodev, /* stop?? */
    63    (d_reset_t *)     enodev, /* reset */
    64    (d_ttycv_t *)     enodev, /* ttys */
    65    (d_select_t *)    pcaselect, /* select */
    66    (d_mmap_t *)      enodev, /* mmap */
    67    (d_strategy_t *)  enodev /* strategy */
    68  };
    69
    70
</programlisting>

<para>
Lignes 71 - 131
</para>
<para>
cette section est analogue &agrave; la d&eacute;claration de fichier de configuration
de votre p&eacute;riph&eacute;rique.  Les membres de la structure isa_device sont
remplis grace &agrave; ce qu'il connait de votre p&eacute;riph&eacute;rique,
port E/S, segment partag&eacute; de m&eacute;moire, etc...
Nous n'aurons probablement jamais un besoin de deux p&eacute;riph&eacute;riques 
pcaudio dans le noyau, mais cet exemple montre comment
p&eacute;riph&eacute;riques multiples peuvent &ecirc;tre support&eacute;s.  
</para>

<programlisting>
    71  /*
    72   * this lkm arbitrarily supports two
    73   * instantiations of the pc-audio device.
    74   *
    75   * this is for illustration purposes
    76   * only, it doesn't make much sense
    77   * to have two of these beasts...
    78   */
    79
    80
    81  /*
    82   * these have a direct correlation to the
    83   * config file entries...
    84   */
    85  struct isa_device pcadev[NPCA] = {
    86    {
    87      11,         /* device id */
    88      &amp;pcadriver,  /* driver pointer */
    89      IO_TIMER1,         /* base io address */
    90      -1,      /* interrupt */
    91      -1,         /* dma channel */
    92      (caddr_t)-1,    /* physical io memory */
    93      0,     /* size of io memory */
    94      pcaintr ,       /* interrupt interface */
    95      0,          /* unit number */
    96      0,     /* flags */
    97      0,          /* scsi id */
    98      0,          /* is alive */
    99      0,          /* flags for register_intr */
   100      0,          /* hot eject device support */
   101      1           /* is device enabled */
   102    },
   103  #if NPCA >1
   104    {
   105
   106   /*
   107    * these are all zeros, because it doesn't make
   108    * much sense to be here
   109    * but it may make sense for your device
   110    */
   111
   112      0,         /* device id */
   113      &amp;pcadriver,  /* driver pointer */
   114      0,         /* base io address */
   115      -1,      /* interrupt */
   116      -1,         /* dma channel */
   117      -1,    /* physical io memory */
   118      0,     /* size of io memory */
   119      NULL,       /* interrupt interface */
   120      1,          /* unit number */
   121      0,     /* flags */
   122      0,          /* scsi id */
   123      0,          /* is alive */
   124      0,          /* flags for register_intr */
   125      0,          /* hot eject device support */
   126      1           /* is device enabled */
   127    },
   128  #endif
   129
   130  };
   131
</programlisting>

<para>
Lignes 132 - 139
</para>

<para>
Ceci appelle la macro MOD_DEV du pr&eacute;processeur C, qui 
installe un module de gestion de p&eacute;riph&eacute;rique de LKM, par
opposition &agrave; un syst&egrave;me de fichiers LKM, ou un appel syst&egrave;me de LKM.
</para>

<programlisting>
   132  /*
   133   * this macro maps to a function which
   134   * sets the LKM up for a driver
   135   * as opposed to a filesystem, system call, or misc
   136   * LKM.
   137   */
   138  MOD_DEV("pcaudio_mod", LM_DT_CHAR, 24, &amp;pcacdevsw);
   139
</programlisting>

<para>
Lignes 140 - 168
</para>

<para>
c'est la fonction qui sera appel&eacute;e lorsque le pilote sera
charg&eacute;.  Cette fonction essaye de fonctionner comme
<filename>/sys/i386/isa/isa.c</filename> qui fait les appels de probe/attach pour un
pilote au moment du red&eacute;marrage. La plus grande astuce ici est qu'il
met en correspondance l'adresse physique du segment partag&eacute; de m&eacute;moire, qui est
indiqu&eacute; dans la structure isa_device &agrave; une adresse virtuelle du noyau.
Normalement, l'adresse physique est mise dans le fichier de configuration
qui construit la structure isa_device dans
<filename>/usr/src/sys/compile/KERNEL/ioconf.c</filename>.  La s&eacute;quence probe/attach de
<filename>/usr/src/sys/isa/isa.c</filename> traduit l'adresse physique en une virtuelle de
sorte que dans les sous-programmes de probe/attach vous puissiez
faire des choses comme 
</para>
<programlisting>
(int *)id->id_maddr = something;
</programlisting>
<para> et se r&eacute;f&egrave;re juste au segment partag&eacute; de m&eacute;moire
par l'interm&eacute;diaire de pointeurs.  
</para> 

<programlisting>
   140  /*
   141   * this function is called when the module is
   142   * loaded; it tries to mimic the behavior
   143   * of the standard probe/attach stuff from
   144   * isa.c
   145   */
   146  int
   147  <function>pcaload()</function>{
   148    int i;
   149    uprintf("PC Audio Driver Loaded\n");
   150    for (i=0; i&lt;NPCA; i++){
   151      /*
   152       * this maps the shared memory address
   153       * from physical to virtual, to be
   154       * consistent with the way
   155       * /usr/src/sys/i386/isa.c handles it.
   156       */
   157      pcadev[i].id_maddr -=0xa0000;
   158      pcadev[i].id_maddr += atdevbase;
   159      if ((*pcadriver.probe)(pcadev+i)) {
   160        (*(pcadriver.attach))(pcadev+i);
   161      } else {
   162        uprintf("PC Audio Probe Failed\n");
   163        return(1);
   164      }
   165    }
   166      return 0;
   167  }
   168
</programlisting>

<para>Lignes 169 - 179
</para>

<para>c'est la fonction appel&eacute;e quand votre pilote n'est pas
charg&eacute;; il affiche juste un message &agrave; cet effet.  
</para>

<programlisting>
   169  /*
   170   * this function is called
   171   * when the module is unloaded
   172   */
   173
   174  int
   175  <function>pcaunload()</function>{
   176    uprintf("PC Audio Driver Unloaded\n");
   177    return 0;
   178  }
   179
</programlisting>

<para>Lignes 180 - 190
</para>

<para>c'est le point d'entr&eacute;e qui est indiqu&eacute; sur la ligne de commande
de modload.  Par convention il est nomm&eacute; &lt;dev>_mod.  C'est
ainsi qu'il est d&eacute;fini dans bsd.lkm.mk, le makefile qui
construit le LKM.  Si vous nommez votre module suivant cette
convention, vous pouvez faire ``make load'' et ``make unload''
de /usr/src/lkm/pcaudio.</para>  

<para>Note : Il y a eu <emphasis>tellement</emphasis> de r&eacute;visions entre la version 2.0 et
2.1.  Il peut ou ne peut ne pas &ecirc;tre possible d'&eacute;crire
un module qui est portable pour chacune des trois versions.
</para>


<programlisting>
   180  /*
   181   * this is the entry point specified
   182   * on the modload command line
   183   */
   184
   185  int
   186  pcaudio_mod(struct lkm_table *lkmtp, int cmd, int ver)
   187  {
   188          DISPATCH(lkmtp, cmd, ver, pcaload, pcaunload, nosys);
   189  }
   190
   191  #endif /* NICP > 0 */
</programlisting>
</sect2>

<sect2>
<title>Idiosyncrasies du type p&eacute;riph&eacute;rique</title>

<sect3>
<title>Caract&egrave;re</title>
<para></para>
</sect3>

<sect3>
<title>Bloc</title>
<para></para>
</sect3>

<sect3>
<title>R&eacute;seau</title>
<para></para>
</sect3>

<sect3>
<title>Line discipline</title>
<para></para>
</sect3>
</sect2>

<sect2>
<title>Idiosyncrasies du type bus</title>

<sect3>
<title>ISA</title>
<para></para>
</sect3>

<sect3>
<title>EISA</title> 
<para></para>
</sect3>

<sect3>
<title>PCI</title> 
<para></para>
</sect3>

<sect3>
<title>SCSI</title>
<para></para>
</sect3>

<sect3>
<title>PCCARD</title>
<para></para>
</sect3>
</sect2>
</sect1>


<sect1>
<title>Support du noyau</title>

<sect2>
<title>Structures de donn&eacute;es</title>

<sect3>  
<title>Structure <citerefentry><refentrytitle>struct kern_devconf</refentrytitle></citerefentry></title> 

<para>Cette structure contient quelques informations sur l'&eacute;tat du
p&eacute;riph&eacute;rique et de son pilote.  Elle est d&eacute;finie dans 
<filename>/usr/src/sys/sys/devconf.h</filename> comme ci-dessous :  
</para>

<programlisting>
struct devconf {
        char dc_name[MAXDEVNAME];       /* name */
        char dc_descr[MAXDEVDESCR];     /* description */
        int dc_unit;                    /* unit number */
        int dc_number;                  /* unique id */
        char dc_pname[MAXDEVNAME];      /* name of the parent device */
        int dc_punit;                   /* unit number of the parent */
        int dc_pnumber;                 /* unique id of the parent */
        struct machdep_devconf dc_md;   /* machine-dependent stuff */
        enum dc_state dc_state;         /* state of the device (see above) */
        enum dc_class dc_class;         /* type of device (see above) */
        size_t dc_datalen;              /* length of data */
        char dc_data[1];                /* variable-length data */
};
</programlisting>
</sect3>

<sect3>
<title>Structure <citerefentry><refentrytitle>struct proc</refentrytitle></citerefentry></title>

<para> Cette structure contient toutes les informations sur un processus.
Elle est dans d&eacute;finie <filename>/usr/src/sys/sys/proc.h</filename>:  
</para>

<programlisting>
/*
 * Description of a process.
 *
 * This structure contains the information needed to manage a thread of
 * control, known in UN*X as a process; it has references to
substructures
 * containing descriptions of things that the process uses, but may
share
 * with related processes.  The process structure and the substructures
 * are always addressable except for those marked "(PROC ONLY)" below,
 * which might be addressable only on a processor on which the process
 * is running.
 */
struct  proc {
        struct  proc *p_forw;           /* Doubly-linked run/sleep queue. */
        struct  proc *p_back;
        struct  proc *p_next;           /* Linked list of active procs */
        struct  proc **p_prev;          /*    and zombies. */

        /* substructures: */
        struct  pcred *p_cred;          /* Process owner's identity. */
        struct  filedesc *p_fd;         /* Ptr to open files structure.  */
        struct  pstats *p_stats;        /* Accounting/statistics (PROC ONLY). */
        struct  plimit *p_limit;        /* Process limits. */
        struct  vmspace *p_vmspace;     /* Address space. */
        struct  sigacts *p_sigacts;     /* Signal actions, state (PROC ONLY). */

#define p_ucred         p_cred->pc_ucred
#define p_rlimit        p_limit->pl_rlimit

        int     p_flag;                 /* P_* flags. */
        char    p_stat;                 /* S* process status. */
        char    p_pad1[3];

        pid_t   p_pid;                  /* Process identifier. */
        struct  proc *p_hash;    /* Hashed based on p_pid for kill+exit+... */
        struct  proc *p_pgrpnxt; /* Pointer to next process in process group. */
        struct  proc *p_pptr;    /* Pointer to process structure of parent. */
        struct  proc *p_osptr;   /* Pointer to older sibling processes.  */

/* The following fields are all zeroed upon creation in fork. */
#define p_startzero     p_ysptr
        struct  proc *p_ysptr;   /* Pointer to younger siblings. */
        struct  proc *p_cptr;    /* Pointer to youngest living child. */
        pid_t   p_oppid;         /* Save parent pid during ptrace. XXX */
        int     p_dupfd;         /* Sideways return value from fdopen.  XXX */

        /* scheduling */
        u_int   p_estcpu;        /* Time averaged value of p_cpticks. */
        int     p_cpticks;       /* Ticks of cpu time. */
        fixpt_t p_pctcpu;        /* %cpu for this process during p_swtime */
        void    *p_wchan;        /* Sleep address. */
        char    *p_wmesg;        /* Reason for sleep. */
        u_int   p_swtime;        /* Time swapped in or out. */
        u_int   p_slptime;       /* Time since last blocked. */

        struct  itimerval p_realtimer;  /* Alarm timer. */
        struct  timeval p_rtime;        /* Real time. */
        u_quad_t p_uticks;              /* Statclock hits in user mode.  */
        u_quad_t p_sticks;              /* Statclock hits in system mode. */
        u_quad_t p_iticks;              /* Statclock hits processing intr. */

        int     p_traceflag;            /* Kernel trace points. */
        struct  vnode *p_tracep;        /* Trace to vnode. */

        int     p_siglist;              /* Signals arrived but not delivered. */

        struct  vnode *p_textvp;        /* Vnode of executable. */

        char    p_lock;                 /* Process lock (prevent swap) count. */
        char    p_pad2[3];              /* alignment */

/* End area that is zeroed on creation. */
#define p_endzero       p_startcopy

/* The following fields are all copied upon creation in fork. */
#define p_startcopy     p_sigmask

        sigset_t p_sigmask;     /* Current signal mask. */
        sigset_t p_sigignore;   /* Signals being ignored. */
        sigset_t p_sigcatch;    /* Signals being caught by user. */

        u_char  p_priority;     /* Process priority. */
        u_char  p_usrpri;       /* User-priority based on p_cpu and p_nice. */
        char    p_nice;         /* Process "nice" value. */
        char    p_comm[MAXCOMLEN+1];

        struct  pgrp *p_pgrp;   /* Pointer to process group. */

        struct  sysentvec *p_sysent; /* System call dispatch information. */

        struct  rtprio p_rtprio;        /* Realtime priority. */
/* End area that is copied on creation. */
#define p_endcopy       p_addr
        struct  user *p_addr;   /* Kernel virtual addr of u-area (PROC ONLY). */
        struct  mdproc p_md;    /* Any machine-dependent fields. */

        u_short p_xstat;        /* Exit status for wait; also stop signal. */
        u_short p_acflag;       /* Accounting flags. */
        struct  rusage *p_ru;   /* Exit information. XXX */
};
</programlisting>
</sect3>

<sect3>
<title>Structure  <citerefentry><refentrytitle>struct buf</refentrytitle></citerefentry></title> 
<para>La structure <citerefentry><refentrytitle>struct buf</refentrytitle></citerefentry> est employ&eacute;e pour s'interfacer
avec le cache de la m&eacute;moire tampon.  Elle est dans
d&eacute;finie <filename>/usr/src/sys/sys/buf.h</filename> :
</para>

<programlisting>
/*
 * The buffer header describes an I/O operation in the kernel.
 */
struct buf {
        LIST_ENTRY(buf) b_hash;         /* Hash chain. */
        LIST_ENTRY(buf) b_vnbufs;       /* Buffer's associated vnode. */
        TAILQ_ENTRY(buf) b_freelist;    /* Free list position if not active. */
        struct  buf *b_actf, **b_actb;  /* Device driver queue when active. */
        struct  proc *b_proc;           /* Associated proc; NULL if kernel. */
        volatile long   b_flags;        /* B_* flags. */
        int     b_qindex;               /* buffer queue index */
        int     b_error;                /* Errno value. */
        long    b_bufsize;              /* Allocated buffer size. */
        long    b_bcount;               /* Valid bytes in buffer. */
        long    b_resid;                /* Remaining I/O. */
        dev_t   b_dev;                  /* Device associated with buffer. */
        struct {
                caddr_t b_addr;         /* Memory, superblocks, indirect etc. */
        } b_un;
        void    *b_saveaddr;            /* Original b_addr for physio.  */
        daddr_t b_lblkno;               /* Logical block number. */
        daddr_t b_blkno;                /* Underlying physical block number. */
                                        /* Function to call upon completion. */
        void    (*b_iodone) __P((struct buf *));
                                        /* For nested b_iodone's. */
        struct  iodone_chain *b_iodone_chain;
        struct  vnode *b_vp;            /* Device vnode. */
        int     b_pfcent;               /* Center page when swapping cluster. */
        int     b_dirtyoff;             /* Offset in buffer of dirty region. */
        int     b_dirtyend;             /* Offset of end of dirty region. */
        struct  ucred *b_rcred;         /* Read credentials reference.  */
        struct  ucred *b_wcred;         /* Write credentials reference.  */
        int     b_validoff;             /* Offset in buffer of valid region. */
        int     b_validend;             /* Offset of end of valid region. */
        daddr_t b_pblkno;               /* physical block number */
        caddr_t b_savekva;              /* saved kva for transfer while bouncing */
        void    *b_driver1;             /* for private use by the driver */
        void    *b_driver2;             /* for private use by the driver */
        void    *b_spc;
        struct  vm_page *b_pages[(MAXPHYS + PAGE_SIZE - 1)/PAGE_SIZE];
        int             b_npages;
};
</programlisting>
</sect3>


<sect3>
<title>Structure <citerefentry><refentrytitle>struct uio</refentrytitle></citerefentry></title>

<para>Cette structure est utilis&eacute;e pour d&eacute;placer des donn&eacute;es entre le noyau et
les espaces utilisateur par les appels syst&egrave;me de <function>read()</function> et de <function>write()</function>.
Il est dans d&eacute;fini <filename>/usr/src/sys/sys/uio.h</filename> :  
</para>

<programlisting>
struct uio {
        struct  iovec *uio_iov;
        int     uio_iovcnt;
        off_t   uio_offset;
        int     uio_resid;
        enum    uio_seg uio_segflg;
        enum    uio_rw uio_rw;
        struct  proc *uio_procp;
};
</programlisting>
</sect3>
</sect2>


<sect2>  
<title>Fonctions</title>
<para>plein</para>
</sect2>

<sect2>  
<title>R&eacute;f&eacute;rences.</title>

<para> FreeBSD Kernel Sources http://www.freebsd.org
</para>

<para> NetBSD Kernel Sources http://www.netbsd.org
</para>

<para>  Writing Device Drivers: Tutorial and Reference;
Tim Burke, Mark A. Parenti, Al, Wojtas; 
Digital Press, ISBN 1-55558-141-2.
</para>

<para> Writing A Unix Device Driver;
Janet I. Egan, Thomas J. Teixeira; 
John Wiley &amp; Sons, ISBN 0-471-62859-X.
</para>

<para>  Writing Device Drivers for SCO Unix;
Peter Kettle;
</para>
</sect2>  
</sect1>
</article>
