<!--
     The FreeBSD Documentation Project
     The FreeBSD French Documentation Project
         
     $Id: chapter.sgml,v 1.1 2002-02-14 14:25:02 gioria Exp $
     Original revision: 1.5
     $FreeBSD$

-->

<chapter id="driverbasics">
  <title>Ecrire des pilotes de p&#233;riph&#233;riques pour FreeBSD</title>

  <para>Ce chap&icirc;tre a &eacute;t&eacute; &eacute;crit par &.murray avec des s&eacute;lections
    depuis une vari&eacute;t&eacute; de codes source inclus dans la page de manuel d'intro(4) de Joerg
    Wunsch.</para>

  <sect1>
    <title>Introduction</title>
    <para>Ce chap&icirc;tre fournit une br&ecirc;ve introduction sur l'&eacute;criture 
      de pilotes de p&eacute;riph&eacute;riques pourFreeBSD.  
      Un p&eacute;riph&eacute;rique, dans ce contexte, est un terme utilis&eacute;
      le plus souvent pour tout ce qui est li&eacute; au mat&eacute;riel et qui d&eacute;pend
      du syst&egrave;me, comme les disques, imprimantes, ou un &eacute;cran avec son clavier.
      Un pilote de p&eacute;riph&eacute;rique est un composant logiciel du syst&egrave;me
      d'exploitation qui contr&ocirc;le un p&eacute;riph&eacute;rique sp&eacute;cifique.  Il y a aussi
      ce que l'on apelle les pseudo-p&eacute;riph&eacute;riques ("pseudo-devices") o&ugrave; un pilote
      de p&eacute;riph&eacute;rique &eacute;mule le comportement d'un p&eacute;riph&eacute;rique dans un logiciel sans
      mat&eacute;riel particulier sous-jacent. Les pilotes de p&eacute;riph&eacute;riques peuvent &ecirc;tre compil&eacute;s
      dans le yst&egrave;me statiquement ou charg&eacute; &agrave; la demande via l'&eacute;diteur de liens dynamique du
      noyau `kld'.</para>

    <para>La plupart des p&eacute;riph&eacute;riques dans un syst&egrave;me d'exploitation de type Unix
      sont accessibles au travers de fichiers sp&eacute;ciaux de p&eacute;riph&eacute;rique (device-nodes), appel&eacute;s parfois
      fichiers sp&eacute;ciaux. Ces fichiers sont habituellement stock&eacute;s dans le r&eacute;pertoire
      <filename>/dev</filename> de la hi&eacute;rarchie du syst&egrave;me de fichiers. Jusqu'&agrave; ce que
      devfs soit totalement int&eacute;gr&eacute; dans FreeBSD, chaque fichier sp&eacute;cial de p&eacute;riph&eacute;rique doit &ecirc;tre
      cr&eacute;&eacute; statiquement et ind&eacute;pendamment de l'existence du pilote de p&eacute;riph&eacute;rique associ&eacute;.
      La plupart des fichiers sp&eacute;ciaux de p&eacute;riph&eacute;rique du syst&egrave;me sont cr&eacute;&eacute;s en ex&eacute;cutant <command>MAKEDEV</command>.</para>

    <para>Les pilotes de p&eacute;riph&eacute;rique peuvent &ecirc;tre en gros s&eacute;par&eacute;s en deux cat&eacute;gories;
      les pilotes de p&eacute;riph&eacute;rique en mode caract&egrave;re et les pilotes de p&eacute;riph&eacute;riques r&eacute;seau.</para>

  </sect1>

  <sect1>
    <title>L'&#233;diteur de liens dynamiques du noyau - KLD</title>
    <!-- Traduction &agrave; v&eacute;rifier 
    Dynamic Kernel Linker Facility - KLD</title> -->

    <para>L'interface kld permet aux administrateurs syst&egrave;me d'ajouter
      et d'enlever dynamiquement une fonctionnalit&eacute; &agrave; un syst&egrave;me en marche.
      Cela permet aux d&eacute;veloppeurs de pilote de p&eacute;riph&eacute;rique de charger leurs nouveaux changements
      dans le noyau en fonctionnement sans red&eacute;marrer constamment pour tester ces derniers.
    </para>

    <para>L'interface kld est utilis&eacute; au travers des commandes d'administrateur suivantes :
    
    <itemizedlist>
      <listitem><simpara><command>kldload</command> - charge un nouveau module dans le noyau</simpara></listitem>
      <listitem><simpara><command>kldunload</command> - d&eacute;charge un module du noyau</simpara></listitem>
      <listitem><simpara><command>kldstat</command> - liste les modules charg&eacute;s dans le noyau</simpara></listitem>
    </itemizedlist>
    </para>

    <para>Structure squelettique d'un module de noyau</para>

<programlisting>/*
 * Squelette KLD 
 * Inspir&eacute; de l'article d'Andrew Reiter paru sur Daemonnews
 */

#include &lt;sys/types.h&gt;
#include &lt;sys/module.h&gt;
#include &lt;sys/systm.h&gt;  /* uprintf */ 
#include &lt;sys/errno.h&gt;
#include &lt;sys/param.h&gt;  /* defines utilise dans kernel.h */
#include &lt;sys/kernel.h&gt; /* types utilise dans le module d'initialisation */

/* 
 * charge le gestionnaire quit traite du chargement et d&eacute;chargement d'un KLD.
 */

static int
skel_loader(struct module *m, int what, void *arg)
{
  int err = 0;
  
  switch (what) {
  case MOD_LOAD:                /* kldload */
    <!-- D&eacute;sol&eacute;, les accents n'existent pas dans la console alors je ne les ai pas mis l&agrave; non plus
    -->
    uprintf("Skeleton KLD charge.\n");
    break;
  case MOD_UNLOAD:
    uprintf("Skeleton KLD decharge.\n");
    break;
  default:
    err = EINVAL;
    break;
  }
  return(err);
}

/* Declare ce module au reste du noyau */

DECLARE_MODULE(skeleton, skel_loader, SI_SUB_KLD, SI_ORDER_ANY);</programlisting>


    <sect2>
      <title>Makefile</title>

      <para>FreeBSD fournit un fichier d'inclusion "makefile" que vous pouvez utiliser pour
	compiler rapidement votre ajout au noyau.</para>

      <programlisting>SRCS=skeleton.c
KMOD=skeleton

.include &lt;bsd.kmod.mk&gt;</programlisting>

      <para>Lancer simplement la commande <command>make</command> avec ce fichier Makefile
        cr&eacute;era un fichier <filename>skeleton.ko</filename> qui peut
        &ecirc;tre charg&eacute; dans votre syst&egrave;me en tapant :
<screen> &prompt.root
	  kldload -v ./skeleton.ko
</screen>
      </para>
    </sect2>
  </sect1>

  <sect1>
    <title>Acc&#233;der au pilote d'un p&#233;riph&#233;rique</title>

    <para>Unix fournit un ensemble d'appels syt&egrave;me communs utilisable par 
      les applications de l'utilisateur. Les couches sup&eacute;rieures du noyau renvoient
      ces appels au pilote de p&eacute;riph&eacute;rique correspondant quand un utilisateur
      acc&egrave;de au fichier sp&eacute;cial de p&eacute;riph&eacute;rique.  Le script <command>/dev/MAKEDEV</command>
      cr&eacute;e la plupart des fichiers sp&eacute;ciaux de p&eacute;riph&eacute;rique pour votre syst&egrave;me mais si vous
      faites votre propre d&eacute;veloppement de pilote, il peut &ecirc;tre n&eacute;cessaire de cr&eacute;er
      vos propres fichiers sp&eacute;ciaux de p&eacute;riph&eacute;rique avec la commande <command>mknod</command>
    </para>

    <sect2>
      <title>Cr&#233;er des fichiers sp&#233;ciaux de p&#233;riph&#233;riques statiques</title>

      <para>La commande <command>mknod</command> n&eacute;cessite quatre
	arguments pou cr&eacute;er un fichier sp&eacute;cial de p&eacute;riph&eacute;rique.  Vous devez sp&eacute;cifier le nom
	de ce fichier sp&eacute;cial de p&eacute;riph&eacute;rique, le type de p&eacute;riph&eacute;rique, le num&eacute;ro majeur 
	et le num&eacute;ro mineur du p&eacute;riph&eacute;rique.</para>
    </sect2>

    <sect2>
      <title>Les fichiers sp&#233;ciaux de p&#233;riph&#233;rique dynamiques</title>

      <para>Le p&eacute;riph&eacute;rique syst&egrave;me de fichiers, ou devfs, fournit l'acc&egrave;s &agrave;
	l'espace des noms des p&eacute;riph&eacute;riques du noyau dans l'espace du syst&egrave;me de fichiers global.
	Ceci &eacute;limine les probl&egrave;mes de pilote sans fichier sp&eacute;cial statique, ou de fichier sp&eacute;cial sans pilote install&eacute;.
	Devfs est toujours un travail en cours mais il fonctionne d&eacute;j&agrave; assez bien.</para>
    </sect2>

  </sect1>

  <sect1>
    <title>Les p&#233;riph&#233;riques caract&#232;res</title>

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

    <para>Cet exemple simple de pseudo-p&eacute;riph&eacute;rique enregistre toutes les valeurs
      que vous lui avez &eacute;crites et peut vous les renvoyer quand vous les lui 
      demandez.</para>

<programlisting>/*
 * un simple pseudo-p&eacute;riph&eacute;rique `echo' KLD
 *
 * Murray Stokely
 */

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

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

#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&eacute;e du p&eacute;riph&eacute;rique Caract&egrave;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(<literal>&</literal>echo_cdevsw,
		    0,
		    UID_ROOT,
		    GID_WHEEL,
		    0600,
		    "echo");
    /* aloocation de m&eacute;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 &agrave; 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);</programlisting>

    <para>Pour installer ce pilote, vous devrez d'abord cr&eacute;er un fichier sp&eacute;cial dans
      votre syst&egrave;me de fichiers avec une commande comme : </para>

<screen>
	&prompt.root mknod /dev/echo c 33 0
</screen>

    <para>Avec ce pilote charg&eacute;, vous devriez maintenant &ecirc;tr capable de taper
      quelque chose comme :</para>

<screen>
        &prompt.root echo -n "Test Donnees" > /dev/echo
        &prompt.root cat /dev/echo
	Test Donnees
</screen>

    <para>P&eacute;riph&eacute;riques r&eacute;els dans le chap&icirc;tre suivant.</para>

    <para>Informations additionnelles
    <itemizedlist>
      <listitem><simpara><ulink
	url="http://www.daemonnews.org/200010/blueprints.html">Dynamic
	Kernel Linker (KLD) Facility Programming Tutorial</ulink> -
	<ulink url="http://www.daemonnews.org">Daemonnews</ulink> October 2000</simpara></listitem>
      <listitem><simpara><ulink
	url="http://www.daemonnews.org/200007/newbus-intro.html">How
	to Write Kernel Drivers with NEWBUS</ulink> - <ulink
	url="http://www.daemonnews.org">Daemonnews</ulink> July
	2000</simpara></listitem>
    </itemizedlist>
    </para>
  </sect1>

  <sect1>
    <title>Pilotes R&#233;seau</title>

    <para>Les pilotes pour p&eacute;riph&eacute;rique r&eacute;seau n'utilisent pas les fichiers sp&eacute;ciaux pour
      pouvoir &ecirc;tre acessibles.  Leur s&eacute;lection est bas&eacute;e sur d'autres d&eacute;cisions
      faites &agrave; l'int&eacute;rieur du noyau et plut&ocirc;t que d'appeler open(), l'utilisation
      d'un p&eacute;riph&eacute;rique r&eacute;seau se fait g&eacute;n&eacute;ralement en se servant de l'appel syst&egrave;me
      socket(2).</para> 

    <para>man ifnet(), p&eacute;riph&eacute;rique "en boucle", drivers de Bill Paul,
      etc..</para>

  </sect1>

</chapter>
