383 lines
14 KiB
Text
383 lines
14 KiB
Text
<!--
|
|
The FreeBSD Documentation Project
|
|
The FreeBSD French Documentation Project
|
|
|
|
$FreeBSD$
|
|
$Id: chapter.sgml,v 1.2 2003-01-12 12:16:07 blackend Exp $
|
|
Original revision: 1.5
|
|
|
|
-->
|
|
|
|
<chapter id="driverbasics">
|
|
<title>Ecrire des pilotes de périphériques pour FreeBSD</title>
|
|
|
|
<para>Ce chapître a été écrit par &a.murray; avec des sélections
|
|
depuis une variété de codes source inclus dans la page de manuel d'&man.intro.4; de Joerg
|
|
Wunsch.</para>
|
|
|
|
<sect1>
|
|
<title>Introduction</title>
|
|
<para>Ce chapître fournit une brêve introduction sur l'écriture
|
|
de pilotes de périphériques pour FreeBSD.
|
|
Un périphérique, dans ce contexte, est un terme utilisé
|
|
le plus souvent pour tout ce qui est lié au matériel et qui dépend
|
|
du système, comme les disques, imprimantes, ou un écran avec son clavier.
|
|
Un pilote de périphérique est un composant logiciel du système
|
|
d'exploitation qui contrôle un périphérique spécifique. Il y a aussi
|
|
ce que l'on apelle les pseudo-périphériques (“pseudo-devices”) où un pilote
|
|
de périphérique émule le comportement d'un périphérique dans un logiciel sans
|
|
matériel particulier sous-jacent. Les pilotes de périphériques peuvent être compilés
|
|
dans le ystème statiquement ou chargé à la demande via l'éditeur de liens dynamique du
|
|
noyau “kld”.</para>
|
|
|
|
<para>La plupart des périphériques dans un système d'exploitation de type Unix
|
|
sont accessibles au travers de fichiers spéciaux de périphérique (device-nodes), appelés parfois
|
|
fichiers spéciaux. Ces fichiers sont habituellement stockés dans le répertoire
|
|
<filename>/dev</filename> de la hiérarchie du système de fichiers. Jusqu'à ce que
|
|
devfs soit totalement intégré dans FreeBSD, chaque fichier spécial de périphérique doit être
|
|
créé statiquement et indépendamment de l'existence du pilote de périphérique associé.
|
|
La plupart des fichiers spéciaux de périphérique du système sont créés en exécutant <command>MAKEDEV</command>.</para>
|
|
|
|
<para>Les pilotes de périphérique peuvent être en gros séparés en deux catégories;
|
|
les pilotes de périphérique en mode caractère et les pilotes de périphériques réseau.</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>L'éditeur de liens dynamiques du noyau - KLD</title>
|
|
<!-- Traduction à vérifier
|
|
Dynamic Kernel Linker Facility - KLD</title> -->
|
|
|
|
<para>L'interface kld permet aux administrateurs système d'ajouter
|
|
et d'enlever dynamiquement une fonctionnalité à un système en marche.
|
|
Cela permet aux développeurs de pilote de périphérique de charger leurs nouveaux changements
|
|
dans le noyau en fonctionnement sans redémarrer constamment pour tester ces derniers.
|
|
</para>
|
|
|
|
<para>L'interface kld est utilisé 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écharge un module du noyau</simpara></listitem>
|
|
<listitem><simpara><command>kldstat</command> - liste les modules chargés dans le noyau</simpara></listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
<para>Structure squelettique d'un module de noyau</para>
|
|
|
|
<programlisting>/*
|
|
* Squelette KLD
|
|
* Inspiré de l'article d'Andrew Reiter paru sur Daemonnews
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/module.h>
|
|
#include <sys/systm.h> /* uprintf */
|
|
#include <sys/errno.h>
|
|
#include <sys/param.h> /* defines utilise dans kernel.h */
|
|
#include <sys/kernel.h> /* types utilise dans le module d'initialisation */
|
|
|
|
/*
|
|
* charge le gestionnaire quit traite du chargement et dé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ésolé, les accents n'existent pas dans la console alors je ne les ai pas mis là 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 <bsd.kmod.mk></programlisting>
|
|
|
|
<para>Lancer simplement la commande <command>make</command> avec ce fichier Makefile
|
|
créera un fichier <filename>skeleton.ko</filename> qui peut
|
|
être chargé dans votre système en tapant :
|
|
<screen> &prompt.root
|
|
kldload -v ./skeleton.ko
|
|
</screen>
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Accéder au pilote d'un périphérique</title>
|
|
|
|
<para>Unix fournit un ensemble d'appels sytème communs utilisable par
|
|
les applications de l'utilisateur. Les couches supérieures du noyau renvoient
|
|
ces appels au pilote de périphérique correspondant quand un utilisateur
|
|
accède au fichier spécial de périphérique. Le script <command>/dev/MAKEDEV</command>
|
|
crée la plupart des fichiers spéciaux de périphérique pour votre système mais si vous
|
|
faites votre propre développement de pilote, il peut être nécessaire de créer
|
|
vos propres fichiers spéciaux de périphérique avec la commande <command>mknod</command>
|
|
</para>
|
|
|
|
<sect2>
|
|
<title>Créer des fichiers spéciaux de périphériques statiques</title>
|
|
|
|
<para>La commande <command>mknod</command> nécessite quatre
|
|
arguments pou créer un fichier spécial de périphérique. Vous devez spécifier le nom
|
|
de ce fichier spécial de périphérique, le type de périphérique, le numéro majeur
|
|
et le numéro mineur du périphérique.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Les fichiers spéciaux de périphérique dynamiques</title>
|
|
|
|
<para>Le périphérique système de fichiers, ou devfs, fournit l'accès à
|
|
l'espace des noms des périphériques du noyau dans l'espace du système de fichiers global.
|
|
Ceci élimine les problèmes de pilote sans fichier spécial statique, ou de fichier spécial sans pilote installé.
|
|
Devfs est toujours un travail en cours mais il fonctionne déjà assez bien.</para>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Les périphériques caractères</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<programlisting>/*
|
|
* 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(<literal>&</literal>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);</programlisting>
|
|
|
|
<para>Pour installer ce pilote, vous devrez d'abord créer un fichier spécial dans
|
|
votre système de fichiers avec une commande comme : </para>
|
|
|
|
<screen>
|
|
&prompt.root mknod /dev/echo c 33 0
|
|
</screen>
|
|
|
|
<para>Avec ce pilote chargé, vous devriez maintenant ê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ériphériques réels dans le chapî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éseau</title>
|
|
|
|
<para>Les pilotes pour périphérique réseau n'utilisent pas les fichiers spéciaux pour
|
|
pouvoir être acessibles. Leur sélection est basée sur d'autres décisions
|
|
faites à l'intérieur du noyau et plutôt que d'appeler open(), l'utilisation
|
|
d'un périphérique réseau se fait généralement en se servant de l'appel système
|
|
&man.socket.2;.</para>
|
|
|
|
<para>man ifnet(), périphérique "en boucle", drivers de Bill Paul,
|
|
etc..</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|