diff --git a/fr_FR.ISO8859-1/articles/ddwg/Makefile b/fr_FR.ISO8859-1/articles/ddwg/Makefile new file mode 100644 index 0000000000..68714b471c --- /dev/null +++ b/fr_FR.ISO8859-1/articles/ddwg/Makefile @@ -0,0 +1,20 @@ +# +# The FreeBSD Documentation Project +# The FreeBSD French Documentation Project +# +# $Id: Makefile,v 1.1 2000-06-01 19:14:23 gioria Exp $ +# Original revision: 1.4 +# + +DOC?= article + +FORMATS?= html + +INSTALL_COMPRESSED?=gz +INSTALL_ONLY_COMPRESSED?= + +SRCS= article.sgml + +DOC_PREFIX?= ${.CURDIR}/../../.. + +.include "${DOC_PREFIX}/share/mk/doc.project.mk" diff --git a/fr_FR.ISO8859-1/articles/ddwg/article.sgml b/fr_FR.ISO8859-1/articles/ddwg/article.sgml new file mode 100644 index 0000000000..012e35654a --- /dev/null +++ b/fr_FR.ISO8859-1/articles/ddwg/article.sgml @@ -0,0 +1,1861 @@ + + + %man; + %urls; + %abstract; + %artheader; + %translators; + + %authors; + %mailing-lists; + +]> + + +
+ + Le guide de l'auteur de pilotes de périphériques pour FreeBSD + + + Eric L. + Hernes + + + &artheader.copyright; + + erich@rrnet.com + 29 Mai 1996 + Ce document décrit comment ajouter un module de gestion de +périphérique à FreeBSD. Il n'est pas destiné pour être un +cours d'instruction sur des modules de gestion de périphérique +d'Unix en général. Il est destiné pour les auteurs de module de +gestion de périphérique, au courant du modèle de module de gestion +de périphérique d'Unix, pour travailler sur FreeBSD. + + &abstract.license; + &abstract.disclaimer; + &trans.a.dntt; + + + + + +Spécificité de FreeBSD2.x + +Dû aux changements de FreeBSD avec le temps, ce guide est +seulement précis en ce qui concerne FreeBSD 2.x. Un guide de +rechange pour FreeBSD 3.x et au-delà est en train d'être écrit. +Contactez Jeroen Ruigrok asmodai@wxs.nl si +vous voulez l'aider à ce sujet. + + + + + +Généralité + + Le noyau de FreeBSD est très bien +documenté, malheureusement il est entièrement écrit en `C'. + + + + +Types de pilotes de module de périphériques. + + +Caractère + + +Structures de données + +Structure cdevsw + + + +Points d'entrée + + +<function>d_open()</function> + +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. + + + + +<function>d_close()</function> + 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(). + + + + +<function>d_read()</function> et <function>d_write()</function> + +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(). + + + + +<function>d_ioctl()</function> + + 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). + + + + +<function>d_stop()</function> + + + + +<function>d_reset()</function> + + + + +<function>d_devtotty()</function> + + + + +<function>d_poll()</function> (3.0 et plus) ou <function>d_select()</function> (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 dispnibles, 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); +} + + + + +<function>d_mmap()</function> + + + + +<function>d_strategy()</function> + + +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(). + + + + + +Fichiers d'en-tête + + + + + +Bloc + + +Structures de données + Structure struct bdevsw + + + Structure struct buf + + + + +Points d'entrée + + +<function>d_open()</function> + Décrit dans la section périphérique de caractère. + + + + +<function>d_close()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_strategy()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_ioctl()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_dump()</function> + + + + +<function>d_psize()</function> + + + + + +Fichiers d'en-tête + + + + + +Réseau +Structure struct ifnet + + + +Points d'entrée + + +<function>if_init()</function> + + + + +<function>if_output()</function> + + + + +<function>if_start()</function> + + + + +<function>if_done()</function> + + + + +<function>if_ioctl()</function> + + + + +<function>if_watchdog()</function> + + + + + +Fichiers d'en-tête + + + + + +Protocole de communication + + +Structures de données +Structure struct linesw + + + + +Points d'entrée + + +<function>l_open()</function> + + + + +<function>l_close()</function> + + + + +<function>l_read()</function> + + + + +<function>l_write()</function> + + + + +<function>l_ioctl()</function> + + + + +<function>l_rint()</function> + + + + +<function>l_start()</function> + + + + +<function>l_modem()</function> + + + + + +Fichiers d'en-tête + + + + + + +Bus Supportés + + +ISA -- Architecture Standard d'Industrie (<foreignphrase>Industry Standard +Architecture</foreignphrase> + + +Structures de données + + +Structure <citerefentry><refentrytitle>struct isa_device</refentrytitle></citerefentry> + +Cette structure est obligatoire, mais généralement elle est créée par +config à partir du fichier de configuration de noyau. +Elle est requise pour chaque périphérique, c'est à dire que si vous avez +un pilote de périphérique contrôlant deux "serial boards", vous +aurez deux structures isa_device. Si vous construisez un périphérique +comme un LKM, vous devrez créer votre propre structure isa_device afin +de refléter votre configuration (lignes 85 - 131 de pcaudio_lkm.c). +Il y a un équivalence directe ebtre le fichier de configuration et la +structureisa_device. La définition de +/usr/src/sys/i386/isa/isa_device.h +est : + + + +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 register_intr() */ + 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 userconfig() */ +}; + + + + +Structure <citerefentry><refentrytitle>struct isa_driver</refentrytitle></citerefentry> + +Cette structure est définie dans +/usr/src/sys/i386/isa/isa_device.h, +est est requise pour chaque pilote de périphérique. La définition +est : + + + +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 */ +}; + + + +C'est la structure employée par le code sondage/attachement +(probe/attach) pour +détecter et initialiser votre périphérique. Le membre probe +est un pointeur à votre fonction permettant de sonder les périphériques. +Le membre attach est un pointeur vers votre fonction d'attache. +Le membre name est un pointeur de caractère sur le nom de deux +ou trois lettres de votre pilote. +C'est le nom enregistré pendant le processus de +sondage/attachement (et probablement aussi dans +lsdev). +Le membre sensitive_hw est un +indicateur qui aide le code de sondage à déterminer l'ordre du sondage. + + + +Un instantiation typique est: + + + +struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; + + + + + +Points d'entrée + + +<function>probe()</function> +probe() prend un pointeur sur une structure isa_device +comme argument et renvoie un int. La valeur de retour est ``zéro'' ou +``non-zéro '' quant à l'absence ou à la présence de votre périphérique. +Ce point d'entrée peut être déclaré comme +static parce qu'il +est accessible par l'intermédiaire du membre +probe de la structre +isa_driver. Cette fonction est destinée à +détecter la présence de votre périphérique seulement et ne devrait +faire aucune configuration du périphérique elle-même. + + + + +<function>attach()</function> + +attach() prend également un pointeur sur une structure +isa_device comme argument et +renvoie un int. La valeur de retour est également ``zéro'' ou +``non-zéro'' indiquant si l'attache a réussie. Cette fonction +est destinée pour faire n'importe quelle initialisation spéciale du +périphérique aussi bien que pour confirmer que le périphérique est utilisable. +Il devrait aussi être déclaré static parce qu'il est accesible +par le membre attach de la structure isa_driver . + + + + + +Fichiers d'en-tête + + + + + +EISA -- Architecture Étendue de Standard industriel (<foreignphrase>Extended Industry Standard Architecture</foreignphrase>) + + + +Structures de données + +Structure struct eisa_dev +Structure struct isa_driver + + + +Points d'entrée + + +<function>probe()</function> +Décrit dans la section de périphérique ISA. + + + +<function>attach()</function> +Décrit dans la section de périphérique ISA. + + + + +Fichiers d'en-tête + + + + + +PCI -- Bus d'interconnexion Périphérique (<foreignphrase>Peripheral Computer +Interconnect</foreignphrase>) + + +Structures de données + + Structure struct pci_device + + + + +nom : Le nom abrégé du périphérique. + + + + + sonde: Contrôle si le pilote peut supporter un périphérique avec +ce type. L'étiquette peut être employée pour obtenir plus +d'information avec pci_read_conf(). Voir ci-dessous. Elle renvoie +une chaîne de caractères avec le nom du périphérique, ou un pointeur +NULL si le pilote ne peut pas supporter ce périphérique. + + + + + attache: Assigne une structure de contrôle et la prépare. Cette +fonction peut utiliser les fonctions de mapping PCI. Voir +ci-dessous. (identification de configuration) ou type. + + + + + compte: Un pointeur sur un compteur d'unité. Il est +employé par le configurateur de PCI pour assigner des numéros. + + + + + + + +Points d'entrée + + +<function>probe()</function> + + + + +<function>attach()</function> + + + + +<function>shutdown()</function> + + + + + +Fichiers d'en-tête + + + + + +SCSI -- <foreignphrase>Small Computer Systems Interface</foreignphrase> + + +Structure de données + +Structure struct scsi_adapter +Structure struct scsi_device +Structure struct scsi_ctlr_config +Structure struct scsi_device_config +Structure struct scsi_link + + + +Points d'entrée + + +<function>attach()</function> + + + + +<function>init()</function> + + + + + +Fichiers d'en-tête + + + + + + +PCCARD (PCMCIA) + + +Structure de données + +Structure struct slot_cont +Structure struct pccard_drv +Structure struct pccard_dev +Structure struct slot + + + +Points d'entrée + + +<function>handler()</function> + + + + +<function>unload()</function> + + + + +<function>suspend()</function> + + + + +<function>init()</function> + + + + + +Fichiers d'en-tête + +<pccard/slot.h> + + + + + + + +Incorporation dans le noyau + +Dans FreeBSD, le support des bus d'ISA et EISA est spécifique à +i386. Tandis que FreeBSD lui-même est actuellement +disponible sur la plateforme i386, un certain effort a été fait pour +faire du code portable pour PCI, PCCARD, et SCSI. Le code +spécifique à ISA et EISA réside dans +/usr/src/sys/i386/isa et +/usr/src/sys/i386/eisa respectivement. Le code indépendant de la +machine de PCI, de PCCARD, et de SCSI réside dans +/usr/src/sys/{pci,pccard,scsi}. Le code spécifique i386 quand à lui +réside dans /usr/src/sys/i386/{pci, pccard, scsi}. + + + + Dans FreeBSD, un module de gestion de périphérique peut +ê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ériphériques. les systèmes BSD utilise quelque +chose comme sys/i386/OBJ. Puisque la plupart des pilotes sont +distribués dans les sources, la discussion suivante se rapporte à un +source pilote de périphérique. +Des binaires de pilotes de périphériques sont +parfois fournis par les constructeurs de matériel qui souhaitent +maintenir les sources de manière propriétaire. + + + Un pilote typique a son code source sous forme de fichier C, +comme dev.c. Le pilote peut également inclure des +fichiers; devreg.h contient typiquement des déclarations publiques +de registre de périphérique, des macros, et d'autres +déclarations spécifique au pilote de périphé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é +sur la carte. Pour le pilote de dgb le microcode est compilé +et reporté dans un fichier d'en-tête par +file2c. + + + Si le pilote de périphérique a des structures de données et des +ioctl qui sont spécifiques au pilote de périphérique ou +périphérique, et +doivent être accessibles de l'espace-utilisateur, elles devraient +être mises dans un fichier d'en-tête séparé qui résidera dans +/usr/include/machine/ (certaines de ces derniers résident dans +/usr/include/sys/). Ceux-ci est typiquement nommé quelque chose comme +ioctl_dev.h ou devio.h. + + + Si un pilote écrit depuis l'espace +d'utilisateur est identique à un périphérique qui existe déjà, il faut +prendre garde à utiliser les mêmes +interfaces ioctl et structures de données. Par exemple, de l'espace +utilisateur, un lecteur de SCSI CDROM devrait être identique à un +lecteur de cdrom IDE; ou une ligne série sur une carte +intelligente multiport (Digiboard, Cyclades...) devrait être identique +à un périphérique sio. Ces périphériques ont une interface définie +relativement bonne et devraient être utilisées. + + + Il y a deux méthodes pour lier un pilote dans le +noyau, statiquement et le modèle LKM. La première méthode +est assez standard à travers la famille *BSD. L'autre +méthode a été initialement développée par Sun (je crois), et a +été mis en application dans BSD en utilisant le modèle de Sun. +Je ne crois pas que l'implémentation actuelle utilise encore le moindre +code de Sun. + + + +Modèle Standard + + Les étapes exigées pour ajouter votre pilote au +noyau standard de FreeBSD sont + + + + +Ajout à la liste des pilotes de périphérique + + + + +Ajout d'une entrée au [bc]devsw + + + + +Ajout d'une entrée du pilote de périphérique au fichier de +configuration du noyau + + + + +config, +compilation et installation du noyau + + + + +créer les fichiers spéciaux requis + + + + +redémarrage + + + + + +Ajout à la liste des pilotes de périphérique + +Le modèle standard pour ajouter un module de gestion de périphérique +au noyau de Berkeley est d'ajouter votre pilote à la liste des +périphériques connus. Cette liste dépend de l'architecture du CPU. +Si le périphérique n'est pas spécifique i386 +(PCCARD, PCI, SCSI), le fichier est dans +/usr/src/sys/conf/files. +Si le périphérique est spécifique i386, utilisez +/usr/src/sys/i386/conf/files.i386. Une ligne typique ressemblerait +à : + + + +i386/isa/joy.c optional joy device-driver + + +Le premier champ relatif est le chemin du module de pilote +par rapport à /usr/src/sys. +Pour le cas d'un pilote binaire, le chemin d'accès serait quelque +chose comme i386/OBJ/joy.o. + + +Le deuxième champ indique à +config(8) +que c'est un pilote facultatif. Quelques +périphériques sont obligatoires pour que le noyau puisse être construit. + + + +Le troisième champ est le nom du périphérique. + + +Le quatrième champ indique à config que c'est un +pilote de périphérique (par opposition à juste facultatif). Ceci +dit à config de créer des entrées pour le périphérique dans dans +des structures de /usr/src/sys/compile/KERNEL/ioconf.c. + + +Il est également possible de créer un fichier +/usr/src/sys/i386/conf/files.KERNEL dont le contenu ignorera le +fichier par défaut files.i386, mais seulement pour le noyau ``KERNEL''. + + + + +Faire de la place dans conf.c + +Maintenant vous devez éditer /usr/src/sys/i386/i386/conf.c +pour faire une entrée pour votre pilote. Quelque part au début, +vous devez déclarer vos points d'entrée. L'entrée pour +le pilote du joystick est: + + +#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 + + + +Cela définit vos points d'entrée, ou points d'entrée nuls qui +renverront ENXIO quand appelé (clause #else). + + + +Le fichier d'en-tête ``joy.h'' est automatiquement produit par +config +quand l'arborescence de construction du noyau est +créé. Cela se réduit habituellement à une seule ligne comme : + + + +#define NJOY 1 + + + +ou + + +#define NJOY 0 + + + ce qui définit le nombre de vos périphériques dans votre noyau. + + + +Vous devez additionnellement ajouter un slot au cdevsw[&rsqb, ou +au bdevsw[&rsqb, selon que ce soit un périphérique caractère, +périphérique bloc, ou les deux si c'est un périphérique bloc +avec une interface brute. L'entrée pour le pilote du joystick +est: + + + +/* 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}, + ... +} + + + + L'ordre est ce qui détermine le nombre majeur de votre +périphérique. C'est pourquoi il y aura toujours une entrée +pour votre pilote, que ce soit des points d'entrée nuls, +ou des points d'entrée actuels. Il est probablement intéressant de +noter que c'est +sensiblement différent de SCO et d'autres dérivés du système V, où +n'importe quel périphérique (dans la théorie) peut avoir n'importe +quel nombre majeur. C'est en grande partie un avantage sur FreeBSD, +sur la manière dont les fichiers spéciaux de périphérique sont créés. +Nous reviendrons en détail sur ceci plus tard. + + + + +Ajout de votre périphérique dans le fichier de configuration. + + Ceci ajoute simplement une ligne décrivant votre périphérique. La +ligne de description du joystick est : + +device joy0 at isa? port "IO_GAME" + +Ceci indique que nous avons un +périphérique appelé ``joy0'' sur le bus ISA en utilisant +le port E/S ``IO_GAME'' (IO_GAME est une macro définie dans +/usr/src/sys/i386/isa/isa.h). + + + +Une entrée légèrement plus compliquée est pour le pilote ``ix'' : + + +device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768 +vector ixintr + + +Ceci indique que nous avons un périphérique appelé +`ix0 ' sur le bus ISA. Il utilise le port E/S 0x300. Son +interruption sera masqué par d'autres périphériques dans la classe +réseau. Il utilise l'interruption 10. Il utilise 32k de mémoire +partagée à l'adresse physique 0xd0000. Il le définit également +son pilote d'interruption comme étant ``ixintr()'' + + + + +<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink> +du noyau. + + Maintenant avec notre fichier de configuration en main, +nous pouvons créer un répertoire de compilation du noyau. Cela peut être +fait en tapant : + +# config KERNEL + + +où KERNEL est le nom de votre fichier de configuration. +La configuration crée un arbre de compilation +pour votre noyau dans /usr/src/sys/compile/KERNEL. Elle crée le fichier +makefile, quelques fichiers C, et quelques fichiers H avec des +macros définissant le nombre de chaque périphérique à inclure dans votre +votre noyau. + + + +Maintenant vous pouvez aller dans le répertoire de compilation et +construire votre noyau. À chaque fois que vous lancerez config, votre +arbre de construction précédent sera retiré, à moins que vous ne lancez +config avec un -n. Si vous avez configuré et compilé un noyau GENERIC, +vous pouvez faire un ``make links'' afin d'éviter de compiler certains +fichiers à chaque itération. Typiquement, je lance : + +# make depend links all + +suivi d'un ``make install'' quand le noyau me convient. + + + + +Créer les fichiers spéciaux de périphériques + + Sur FreeBSD, vous avez la responsabilité de faire vos propres fichiers +spéciaux +de périphérique. Le +nombre majeur de votre périphérique est déterminé par le nombre de +slots dans le commutateur de périphérique. Le nombre mineur est +dépendant du pilote, naturellement. Vous pouvez +soit exécuter mknod depuis la ligne de commande, soit liasser faire le +travail à /dev/MAKEDEV.local, ou même +/dev/MAKEDEV. +Je crée parfois un script MAKEDEV.dev qui peut être soit lancé +de manière autonome soit collé dans /dev/MAKEDEV.local. + + + + +Redémarrage + C'est la partie facile. Il y a un +certain nombre de méthodes pour faire ceci, reboot, fastboot, +shutdown - r, couper le courant, etc. Au démarrage, vous +devriez voir votre XXprobe() appelé, et si tout marche, +votre attach() aussi. + + + + + +Module du noyau à chargement dynamique (LKM) + +Il n'y a vraiment aucune procédure définie pour écrire un pilote de +LKM. Ce qui suit est ma propre conception après expérimentation +avec l'interface de périphérique LKM et en regardant le modèle standard +de module de gestion de périphérique, c'est une manière d'ajouter une +interface LKM à 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écifiques LKM devraient faire partie du pilote +lui-même, compilé de manière conditionnelle par la macro LKM +(c.-à-d. #ifdef LKM). + + + +Cette section se concentrera sur la manière d'écrire la partie spécifique +LKM du pilote. Nous supposerons que nous avons écrit un +pilote qui atterrira dans le modèle standard de +gestion de périphérique, que nous voudrions maintenant mettre en +application comme étant LKM. Nous utiliserons le pilote de pcaudio +comme pilote d'exemple, et développerons une entrée LKM. La +source et le fichier makefile pour le LKM pcaudio , ``pcaudio_lkm.c'' +et ``Makefile'', devraient être dans placé /usr/src/lkm/pcaudio. +Ce qui suit est le code commenté de pcaudio_lkm.c. + + + +Lignes 17 - 26 + + + +Ceci inclut le fichier ``pca.h'' et fait une compilation conditionnelle +du reste de LKM suivant que vous avez défini ou non le pilote de +périphérique pcaudio. +Cela imite le comportement de config. Dans un pilote de +périphérique standard, +config +produit le fichier pca.h depuis le nombre de périphériques pca +le fichier de config. + + + 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 + + + +Lignes 27 - 37 + + + +Les fichiers d'en-tête requis depuis divers répertoire d'inclusion. + + + + 27 #include <sys/param.h> + 28 #include <sys/systm.h> + 29 #include <sys/exec.h> + 30 #include <sys/conf.h> + 31 #include <sys/sysent.h> + 32 #include <sys/lkm.h> + 33 #include <sys/errno.h> + 34 #include <i386/isa/isa_device.h> + 35 #include <i386/isa/isa.h> + 36 + 37 + + + +Lignes 38 - 51 + + +déclarent vos points d'entrée comme externs . + + + + 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 + + + +Lignes 52 - 70 + + + +Cela crée la table d'entrée de commutateur de périphérique pour +votre pilote. Cette table est en gros entièrement mise dans le +système de commutation de périphériques à l'emplacement indiqué par +votre nombre majeur. Dans le modèle standard, c'est dans +/usr/src/sys/i386/i386/conf.c. +NOTE: vous ne pouvez pas sélectionner +un nombre majeur de périphérique plus grand que ce qui existe dans +conf.c, par exemple +il y a 67 slots pour des périphériques caractère, vous ne popuvez pas +utiliser un périphérique (caractère) de numéro majeur 67 ou +plus, sans avoir d'abord réservé de l'espace dans conf.c. + + + + 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 + + + +Lignes 71 - 131 + + +cette section est analogue à la déclaration de fichier de configuration +de votre périphérique. Les membres de la structure isa_device sont +remplis grace à ce qu'il connait de votre périphérique, +port E/S, segment partagé de mémoire, etc... +Nous n'aurons probablement jamais un besoin de deux périphériques +pcaudio dans le noyau, mais cet exemple montre comment +périphériques multiples peuvent être supportés. + + + + 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 &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 &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 + + + +Lignes 132 - 139 + + + +Ceci appelle la macro MOD_DEV du préprocesseur C, qui +installe un module de gestion de périphérique de LKM, par +opposition à un système de fichiers LKM, ou un appel système de LKM. + + + + 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, &pcacdevsw); + 139 + + + +Lignes 140 - 168 + + + +c'est la fonction qui sera appelée lorsque le pilote sera +chargé. Cette fonction essaye de fonctionner comme +/sys/i386/isa/isa.c qui fait les appels de probe/attach pour un +pilote au moment du redémarrage. La plus grande astuce ici est qu'il +met en correspondance l'adresse physique du segment partagé de mémoire, qui est +indiqué dans la structure isa_device à une adresse virtuelle du noyau. +Normalement, l'adresse physique est mise dans le fichier de configuration +qui construit la structure isa_device dans +/usr/src/sys/compile/KERNEL/ioconf.c. La séquence probe/attach de +/usr/src/sys/isa/isa.c traduit l'adresse physique en une virtuelle de +sorte que dans les sous-programmes de probe/attach vous puissiez +faire des choses comme + + +(int *)id->id_maddr = something; + + et se réfère juste au segment partagé de mémoire +par l'intermédiaire de pointeurs. + + + + 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 pcaload(){ + 148 int i; + 149 uprintf("PC Audio Driver Loaded\n"); + 150 for (i=0; i<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 + + +Lignes 169 - 179 + + +c'est la fonction appelée quand votre pilote n'est pas +chargé; il affiche juste un message à cet effet. + + + + 169 /* + 170 * this function is called + 171 * when the module is unloaded + 172 */ + 173 + 174 int + 175 pcaunload(){ + 176 uprintf("PC Audio Driver Unloaded\n"); + 177 return 0; + 178 } + 179 + + +Lignes 180 - 190 + + +c'est le point d'entrée qui est indiqué sur la ligne de commande +de modload. Par convention il est nommé <dev>_mod. C'est +ainsi qu'il est dé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. + +Note : Il y a eu tellement de révisions entre la version 2.0 et +2.1. Il peut ou ne peut ne pas être possible d'écrire +un module qui est portable pour chacune des trois versions. + + + + + 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 */ + + + + +Idiosyncrasies du type périphérique + + +Caractère + + + + +Bloc + + + + +Réseau + + + + +Line discipline + + + + + +Idiosyncrasies du type bus + + +ISA + + + + +EISA + + + + +PCI + + + + +SCSI + + + + +PCCARD + + + + + + + +Support du noyau + + +Structures de données + + +Structure <citerefentry><refentrytitle>struct kern_devconf</refentrytitle></citerefentry> + +Cette structure contient quelques informations sur l'état du +périphérique et de son pilote. Elle est définie dans +/usr/src/sys/sys/devconf.h comme ci-dessous : + + + +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 */ +}; + + + + +Structure <citerefentry><refentrytitle>struct proc</refentrytitle></citerefentry> + + Cette structure contient toutes les informations sur un processus. +Elle est dans définie /usr/src/sys/sys/proc.h: + + + +/* + * 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 */ +}; + + + + +Structure <citerefentry><refentrytitle>struct buf</refentrytitle></citerefentry> +La structure struct buf est employée pour s'interfacer +avec le cache de la mémoire tampon. Elle est dans +définie /usr/src/sys/sys/buf.h : + + + +/* + * 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; +}; + + + + + +Structure <citerefentry><refentrytitle>struct uio</refentrytitle></citerefentry> + +Cette structure est utilisée pour déplacer des données entre le noyau et +les espaces utilisateur par les appels système de read() et de write(). +Il est dans défini /usr/src/sys/sys/uio.h : + + + +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; +}; + + + + + + +Fonctions +plein + + + +Références. + + FreeBSD Kernel Sources http://www.freebsd.org + + + NetBSD Kernel Sources http://www.netbsd.org + + + Writing Device Drivers: Tutorial and Reference; +Tim Burke, Mark A. Parenti, Al, Wojtas; +Digital Press, ISBN 1-55558-141-2. + + + Writing A Unix Device Driver; +Janet I. Egan, Thomas J. Teixeira; +John Wiley & Sons, ISBN 0-471-62859-X. + + + Writing Device Drivers for SCO Unix; +Peter Kettle; + + + +
diff --git a/fr_FR.ISO_8859-1/articles/ddwg/Makefile b/fr_FR.ISO_8859-1/articles/ddwg/Makefile new file mode 100644 index 0000000000..68714b471c --- /dev/null +++ b/fr_FR.ISO_8859-1/articles/ddwg/Makefile @@ -0,0 +1,20 @@ +# +# The FreeBSD Documentation Project +# The FreeBSD French Documentation Project +# +# $Id: Makefile,v 1.1 2000-06-01 19:14:23 gioria Exp $ +# Original revision: 1.4 +# + +DOC?= article + +FORMATS?= html + +INSTALL_COMPRESSED?=gz +INSTALL_ONLY_COMPRESSED?= + +SRCS= article.sgml + +DOC_PREFIX?= ${.CURDIR}/../../.. + +.include "${DOC_PREFIX}/share/mk/doc.project.mk" diff --git a/fr_FR.ISO_8859-1/articles/ddwg/article.sgml b/fr_FR.ISO_8859-1/articles/ddwg/article.sgml new file mode 100644 index 0000000000..012e35654a --- /dev/null +++ b/fr_FR.ISO_8859-1/articles/ddwg/article.sgml @@ -0,0 +1,1861 @@ + + + %man; + %urls; + %abstract; + %artheader; + %translators; + + %authors; + %mailing-lists; + +]> + + +
+ + Le guide de l'auteur de pilotes de périphériques pour FreeBSD + + + Eric L. + Hernes + + + &artheader.copyright; + + erich@rrnet.com + 29 Mai 1996 + Ce document décrit comment ajouter un module de gestion de +périphérique à FreeBSD. Il n'est pas destiné pour être un +cours d'instruction sur des modules de gestion de périphérique +d'Unix en général. Il est destiné pour les auteurs de module de +gestion de périphérique, au courant du modèle de module de gestion +de périphérique d'Unix, pour travailler sur FreeBSD. + + &abstract.license; + &abstract.disclaimer; + &trans.a.dntt; + + + + + +Spécificité de FreeBSD2.x + +Dû aux changements de FreeBSD avec le temps, ce guide est +seulement précis en ce qui concerne FreeBSD 2.x. Un guide de +rechange pour FreeBSD 3.x et au-delà est en train d'être écrit. +Contactez Jeroen Ruigrok asmodai@wxs.nl si +vous voulez l'aider à ce sujet. + + + + + +Généralité + + Le noyau de FreeBSD est très bien +documenté, malheureusement il est entièrement écrit en `C'. + + + + +Types de pilotes de module de périphériques. + + +Caractère + + +Structures de données + +Structure cdevsw + + + +Points d'entrée + + +<function>d_open()</function> + +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. + + + + +<function>d_close()</function> + 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(). + + + + +<function>d_read()</function> et <function>d_write()</function> + +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(). + + + + +<function>d_ioctl()</function> + + 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). + + + + +<function>d_stop()</function> + + + + +<function>d_reset()</function> + + + + +<function>d_devtotty()</function> + + + + +<function>d_poll()</function> (3.0 et plus) ou <function>d_select()</function> (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 dispnibles, 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); +} + + + + +<function>d_mmap()</function> + + + + +<function>d_strategy()</function> + + +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(). + + + + + +Fichiers d'en-tête + + + + + +Bloc + + +Structures de données + Structure struct bdevsw + + + Structure struct buf + + + + +Points d'entrée + + +<function>d_open()</function> + Décrit dans la section périphérique de caractère. + + + + +<function>d_close()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_strategy()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_ioctl()</function> +Décrit dans la section périphérique de caractère. + + + + +<function>d_dump()</function> + + + + +<function>d_psize()</function> + + + + + +Fichiers d'en-tête + + + + + +Réseau +Structure struct ifnet + + + +Points d'entrée + + +<function>if_init()</function> + + + + +<function>if_output()</function> + + + + +<function>if_start()</function> + + + + +<function>if_done()</function> + + + + +<function>if_ioctl()</function> + + + + +<function>if_watchdog()</function> + + + + + +Fichiers d'en-tête + + + + + +Protocole de communication + + +Structures de données +Structure struct linesw + + + + +Points d'entrée + + +<function>l_open()</function> + + + + +<function>l_close()</function> + + + + +<function>l_read()</function> + + + + +<function>l_write()</function> + + + + +<function>l_ioctl()</function> + + + + +<function>l_rint()</function> + + + + +<function>l_start()</function> + + + + +<function>l_modem()</function> + + + + + +Fichiers d'en-tête + + + + + + +Bus Supportés + + +ISA -- Architecture Standard d'Industrie (<foreignphrase>Industry Standard +Architecture</foreignphrase> + + +Structures de données + + +Structure <citerefentry><refentrytitle>struct isa_device</refentrytitle></citerefentry> + +Cette structure est obligatoire, mais généralement elle est créée par +config à partir du fichier de configuration de noyau. +Elle est requise pour chaque périphérique, c'est à dire que si vous avez +un pilote de périphérique contrôlant deux "serial boards", vous +aurez deux structures isa_device. Si vous construisez un périphérique +comme un LKM, vous devrez créer votre propre structure isa_device afin +de refléter votre configuration (lignes 85 - 131 de pcaudio_lkm.c). +Il y a un équivalence directe ebtre le fichier de configuration et la +structureisa_device. La définition de +/usr/src/sys/i386/isa/isa_device.h +est : + + + +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 register_intr() */ + 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 userconfig() */ +}; + + + + +Structure <citerefentry><refentrytitle>struct isa_driver</refentrytitle></citerefentry> + +Cette structure est définie dans +/usr/src/sys/i386/isa/isa_device.h, +est est requise pour chaque pilote de périphérique. La définition +est : + + + +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 */ +}; + + + +C'est la structure employée par le code sondage/attachement +(probe/attach) pour +détecter et initialiser votre périphérique. Le membre probe +est un pointeur à votre fonction permettant de sonder les périphériques. +Le membre attach est un pointeur vers votre fonction d'attache. +Le membre name est un pointeur de caractère sur le nom de deux +ou trois lettres de votre pilote. +C'est le nom enregistré pendant le processus de +sondage/attachement (et probablement aussi dans +lsdev). +Le membre sensitive_hw est un +indicateur qui aide le code de sondage à déterminer l'ordre du sondage. + + + +Un instantiation typique est: + + + +struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; + + + + + +Points d'entrée + + +<function>probe()</function> +probe() prend un pointeur sur une structure isa_device +comme argument et renvoie un int. La valeur de retour est ``zéro'' ou +``non-zéro '' quant à l'absence ou à la présence de votre périphérique. +Ce point d'entrée peut être déclaré comme +static parce qu'il +est accessible par l'intermédiaire du membre +probe de la structre +isa_driver. Cette fonction est destinée à +détecter la présence de votre périphérique seulement et ne devrait +faire aucune configuration du périphérique elle-même. + + + + +<function>attach()</function> + +attach() prend également un pointeur sur une structure +isa_device comme argument et +renvoie un int. La valeur de retour est également ``zéro'' ou +``non-zéro'' indiquant si l'attache a réussie. Cette fonction +est destinée pour faire n'importe quelle initialisation spéciale du +périphérique aussi bien que pour confirmer que le périphérique est utilisable. +Il devrait aussi être déclaré static parce qu'il est accesible +par le membre attach de la structure isa_driver . + + + + + +Fichiers d'en-tête + + + + + +EISA -- Architecture Étendue de Standard industriel (<foreignphrase>Extended Industry Standard Architecture</foreignphrase>) + + + +Structures de données + +Structure struct eisa_dev +Structure struct isa_driver + + + +Points d'entrée + + +<function>probe()</function> +Décrit dans la section de périphérique ISA. + + + +<function>attach()</function> +Décrit dans la section de périphérique ISA. + + + + +Fichiers d'en-tête + + + + + +PCI -- Bus d'interconnexion Périphérique (<foreignphrase>Peripheral Computer +Interconnect</foreignphrase>) + + +Structures de données + + Structure struct pci_device + + + + +nom : Le nom abrégé du périphérique. + + + + + sonde: Contrôle si le pilote peut supporter un périphérique avec +ce type. L'étiquette peut être employée pour obtenir plus +d'information avec pci_read_conf(). Voir ci-dessous. Elle renvoie +une chaîne de caractères avec le nom du périphérique, ou un pointeur +NULL si le pilote ne peut pas supporter ce périphérique. + + + + + attache: Assigne une structure de contrôle et la prépare. Cette +fonction peut utiliser les fonctions de mapping PCI. Voir +ci-dessous. (identification de configuration) ou type. + + + + + compte: Un pointeur sur un compteur d'unité. Il est +employé par le configurateur de PCI pour assigner des numéros. + + + + + + + +Points d'entrée + + +<function>probe()</function> + + + + +<function>attach()</function> + + + + +<function>shutdown()</function> + + + + + +Fichiers d'en-tête + + + + + +SCSI -- <foreignphrase>Small Computer Systems Interface</foreignphrase> + + +Structure de données + +Structure struct scsi_adapter +Structure struct scsi_device +Structure struct scsi_ctlr_config +Structure struct scsi_device_config +Structure struct scsi_link + + + +Points d'entrée + + +<function>attach()</function> + + + + +<function>init()</function> + + + + + +Fichiers d'en-tête + + + + + + +PCCARD (PCMCIA) + + +Structure de données + +Structure struct slot_cont +Structure struct pccard_drv +Structure struct pccard_dev +Structure struct slot + + + +Points d'entrée + + +<function>handler()</function> + + + + +<function>unload()</function> + + + + +<function>suspend()</function> + + + + +<function>init()</function> + + + + + +Fichiers d'en-tête + +<pccard/slot.h> + + + + + + + +Incorporation dans le noyau + +Dans FreeBSD, le support des bus d'ISA et EISA est spécifique à +i386. Tandis que FreeBSD lui-même est actuellement +disponible sur la plateforme i386, un certain effort a été fait pour +faire du code portable pour PCI, PCCARD, et SCSI. Le code +spécifique à ISA et EISA réside dans +/usr/src/sys/i386/isa et +/usr/src/sys/i386/eisa respectivement. Le code indépendant de la +machine de PCI, de PCCARD, et de SCSI réside dans +/usr/src/sys/{pci,pccard,scsi}. Le code spécifique i386 quand à lui +réside dans /usr/src/sys/i386/{pci, pccard, scsi}. + + + + Dans FreeBSD, un module de gestion de périphérique peut +ê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ériphériques. les systèmes BSD utilise quelque +chose comme sys/i386/OBJ. Puisque la plupart des pilotes sont +distribués dans les sources, la discussion suivante se rapporte à un +source pilote de périphérique. +Des binaires de pilotes de périphériques sont +parfois fournis par les constructeurs de matériel qui souhaitent +maintenir les sources de manière propriétaire. + + + Un pilote typique a son code source sous forme de fichier C, +comme dev.c. Le pilote peut également inclure des +fichiers; devreg.h contient typiquement des déclarations publiques +de registre de périphérique, des macros, et d'autres +déclarations spécifique au pilote de périphé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é +sur la carte. Pour le pilote de dgb le microcode est compilé +et reporté dans un fichier d'en-tête par +file2c. + + + Si le pilote de périphérique a des structures de données et des +ioctl qui sont spécifiques au pilote de périphérique ou +périphérique, et +doivent être accessibles de l'espace-utilisateur, elles devraient +être mises dans un fichier d'en-tête séparé qui résidera dans +/usr/include/machine/ (certaines de ces derniers résident dans +/usr/include/sys/). Ceux-ci est typiquement nommé quelque chose comme +ioctl_dev.h ou devio.h. + + + Si un pilote écrit depuis l'espace +d'utilisateur est identique à un périphérique qui existe déjà, il faut +prendre garde à utiliser les mêmes +interfaces ioctl et structures de données. Par exemple, de l'espace +utilisateur, un lecteur de SCSI CDROM devrait être identique à un +lecteur de cdrom IDE; ou une ligne série sur une carte +intelligente multiport (Digiboard, Cyclades...) devrait être identique +à un périphérique sio. Ces périphériques ont une interface définie +relativement bonne et devraient être utilisées. + + + Il y a deux méthodes pour lier un pilote dans le +noyau, statiquement et le modèle LKM. La première méthode +est assez standard à travers la famille *BSD. L'autre +méthode a été initialement développée par Sun (je crois), et a +été mis en application dans BSD en utilisant le modèle de Sun. +Je ne crois pas que l'implémentation actuelle utilise encore le moindre +code de Sun. + + + +Modèle Standard + + Les étapes exigées pour ajouter votre pilote au +noyau standard de FreeBSD sont + + + + +Ajout à la liste des pilotes de périphérique + + + + +Ajout d'une entrée au [bc]devsw + + + + +Ajout d'une entrée du pilote de périphérique au fichier de +configuration du noyau + + + + +config, +compilation et installation du noyau + + + + +créer les fichiers spéciaux requis + + + + +redémarrage + + + + + +Ajout à la liste des pilotes de périphérique + +Le modèle standard pour ajouter un module de gestion de périphérique +au noyau de Berkeley est d'ajouter votre pilote à la liste des +périphériques connus. Cette liste dépend de l'architecture du CPU. +Si le périphérique n'est pas spécifique i386 +(PCCARD, PCI, SCSI), le fichier est dans +/usr/src/sys/conf/files. +Si le périphérique est spécifique i386, utilisez +/usr/src/sys/i386/conf/files.i386. Une ligne typique ressemblerait +à : + + + +i386/isa/joy.c optional joy device-driver + + +Le premier champ relatif est le chemin du module de pilote +par rapport à /usr/src/sys. +Pour le cas d'un pilote binaire, le chemin d'accès serait quelque +chose comme i386/OBJ/joy.o. + + +Le deuxième champ indique à +config(8) +que c'est un pilote facultatif. Quelques +périphériques sont obligatoires pour que le noyau puisse être construit. + + + +Le troisième champ est le nom du périphérique. + + +Le quatrième champ indique à config que c'est un +pilote de périphérique (par opposition à juste facultatif). Ceci +dit à config de créer des entrées pour le périphérique dans dans +des structures de /usr/src/sys/compile/KERNEL/ioconf.c. + + +Il est également possible de créer un fichier +/usr/src/sys/i386/conf/files.KERNEL dont le contenu ignorera le +fichier par défaut files.i386, mais seulement pour le noyau ``KERNEL''. + + + + +Faire de la place dans conf.c + +Maintenant vous devez éditer /usr/src/sys/i386/i386/conf.c +pour faire une entrée pour votre pilote. Quelque part au début, +vous devez déclarer vos points d'entrée. L'entrée pour +le pilote du joystick est: + + +#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 + + + +Cela définit vos points d'entrée, ou points d'entrée nuls qui +renverront ENXIO quand appelé (clause #else). + + + +Le fichier d'en-tête ``joy.h'' est automatiquement produit par +config +quand l'arborescence de construction du noyau est +créé. Cela se réduit habituellement à une seule ligne comme : + + + +#define NJOY 1 + + + +ou + + +#define NJOY 0 + + + ce qui définit le nombre de vos périphériques dans votre noyau. + + + +Vous devez additionnellement ajouter un slot au cdevsw[&rsqb, ou +au bdevsw[&rsqb, selon que ce soit un périphérique caractère, +périphérique bloc, ou les deux si c'est un périphérique bloc +avec une interface brute. L'entrée pour le pilote du joystick +est: + + + +/* 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}, + ... +} + + + + L'ordre est ce qui détermine le nombre majeur de votre +périphérique. C'est pourquoi il y aura toujours une entrée +pour votre pilote, que ce soit des points d'entrée nuls, +ou des points d'entrée actuels. Il est probablement intéressant de +noter que c'est +sensiblement différent de SCO et d'autres dérivés du système V, où +n'importe quel périphérique (dans la théorie) peut avoir n'importe +quel nombre majeur. C'est en grande partie un avantage sur FreeBSD, +sur la manière dont les fichiers spéciaux de périphérique sont créés. +Nous reviendrons en détail sur ceci plus tard. + + + + +Ajout de votre périphérique dans le fichier de configuration. + + Ceci ajoute simplement une ligne décrivant votre périphérique. La +ligne de description du joystick est : + +device joy0 at isa? port "IO_GAME" + +Ceci indique que nous avons un +périphérique appelé ``joy0'' sur le bus ISA en utilisant +le port E/S ``IO_GAME'' (IO_GAME est une macro définie dans +/usr/src/sys/i386/isa/isa.h). + + + +Une entrée légèrement plus compliquée est pour le pilote ``ix'' : + + +device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768 +vector ixintr + + +Ceci indique que nous avons un périphérique appelé +`ix0 ' sur le bus ISA. Il utilise le port E/S 0x300. Son +interruption sera masqué par d'autres périphériques dans la classe +réseau. Il utilise l'interruption 10. Il utilise 32k de mémoire +partagée à l'adresse physique 0xd0000. Il le définit également +son pilote d'interruption comme étant ``ixintr()'' + + + + +<ulink url="http://www.freebsd.org/cgi/man.cgi?config(8)">config</ulink> +du noyau. + + Maintenant avec notre fichier de configuration en main, +nous pouvons créer un répertoire de compilation du noyau. Cela peut être +fait en tapant : + +# config KERNEL + + +où KERNEL est le nom de votre fichier de configuration. +La configuration crée un arbre de compilation +pour votre noyau dans /usr/src/sys/compile/KERNEL. Elle crée le fichier +makefile, quelques fichiers C, et quelques fichiers H avec des +macros définissant le nombre de chaque périphérique à inclure dans votre +votre noyau. + + + +Maintenant vous pouvez aller dans le répertoire de compilation et +construire votre noyau. À chaque fois que vous lancerez config, votre +arbre de construction précédent sera retiré, à moins que vous ne lancez +config avec un -n. Si vous avez configuré et compilé un noyau GENERIC, +vous pouvez faire un ``make links'' afin d'éviter de compiler certains +fichiers à chaque itération. Typiquement, je lance : + +# make depend links all + +suivi d'un ``make install'' quand le noyau me convient. + + + + +Créer les fichiers spéciaux de périphériques + + Sur FreeBSD, vous avez la responsabilité de faire vos propres fichiers +spéciaux +de périphérique. Le +nombre majeur de votre périphérique est déterminé par le nombre de +slots dans le commutateur de périphérique. Le nombre mineur est +dépendant du pilote, naturellement. Vous pouvez +soit exécuter mknod depuis la ligne de commande, soit liasser faire le +travail à /dev/MAKEDEV.local, ou même +/dev/MAKEDEV. +Je crée parfois un script MAKEDEV.dev qui peut être soit lancé +de manière autonome soit collé dans /dev/MAKEDEV.local. + + + + +Redémarrage + C'est la partie facile. Il y a un +certain nombre de méthodes pour faire ceci, reboot, fastboot, +shutdown - r, couper le courant, etc. Au démarrage, vous +devriez voir votre XXprobe() appelé, et si tout marche, +votre attach() aussi. + + + + + +Module du noyau à chargement dynamique (LKM) + +Il n'y a vraiment aucune procédure définie pour écrire un pilote de +LKM. Ce qui suit est ma propre conception après expérimentation +avec l'interface de périphérique LKM et en regardant le modèle standard +de module de gestion de périphérique, c'est une manière d'ajouter une +interface LKM à 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écifiques LKM devraient faire partie du pilote +lui-même, compilé de manière conditionnelle par la macro LKM +(c.-à-d. #ifdef LKM). + + + +Cette section se concentrera sur la manière d'écrire la partie spécifique +LKM du pilote. Nous supposerons que nous avons écrit un +pilote qui atterrira dans le modèle standard de +gestion de périphérique, que nous voudrions maintenant mettre en +application comme étant LKM. Nous utiliserons le pilote de pcaudio +comme pilote d'exemple, et développerons une entrée LKM. La +source et le fichier makefile pour le LKM pcaudio , ``pcaudio_lkm.c'' +et ``Makefile'', devraient être dans placé /usr/src/lkm/pcaudio. +Ce qui suit est le code commenté de pcaudio_lkm.c. + + + +Lignes 17 - 26 + + + +Ceci inclut le fichier ``pca.h'' et fait une compilation conditionnelle +du reste de LKM suivant que vous avez défini ou non le pilote de +périphérique pcaudio. +Cela imite le comportement de config. Dans un pilote de +périphérique standard, +config +produit le fichier pca.h depuis le nombre de périphériques pca +le fichier de config. + + + 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 + + + +Lignes 27 - 37 + + + +Les fichiers d'en-tête requis depuis divers répertoire d'inclusion. + + + + 27 #include <sys/param.h> + 28 #include <sys/systm.h> + 29 #include <sys/exec.h> + 30 #include <sys/conf.h> + 31 #include <sys/sysent.h> + 32 #include <sys/lkm.h> + 33 #include <sys/errno.h> + 34 #include <i386/isa/isa_device.h> + 35 #include <i386/isa/isa.h> + 36 + 37 + + + +Lignes 38 - 51 + + +déclarent vos points d'entrée comme externs . + + + + 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 + + + +Lignes 52 - 70 + + + +Cela crée la table d'entrée de commutateur de périphérique pour +votre pilote. Cette table est en gros entièrement mise dans le +système de commutation de périphériques à l'emplacement indiqué par +votre nombre majeur. Dans le modèle standard, c'est dans +/usr/src/sys/i386/i386/conf.c. +NOTE: vous ne pouvez pas sélectionner +un nombre majeur de périphérique plus grand que ce qui existe dans +conf.c, par exemple +il y a 67 slots pour des périphériques caractère, vous ne popuvez pas +utiliser un périphérique (caractère) de numéro majeur 67 ou +plus, sans avoir d'abord réservé de l'espace dans conf.c. + + + + 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 + + + +Lignes 71 - 131 + + +cette section est analogue à la déclaration de fichier de configuration +de votre périphérique. Les membres de la structure isa_device sont +remplis grace à ce qu'il connait de votre périphérique, +port E/S, segment partagé de mémoire, etc... +Nous n'aurons probablement jamais un besoin de deux périphériques +pcaudio dans le noyau, mais cet exemple montre comment +périphériques multiples peuvent être supportés. + + + + 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 &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 &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 + + + +Lignes 132 - 139 + + + +Ceci appelle la macro MOD_DEV du préprocesseur C, qui +installe un module de gestion de périphérique de LKM, par +opposition à un système de fichiers LKM, ou un appel système de LKM. + + + + 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, &pcacdevsw); + 139 + + + +Lignes 140 - 168 + + + +c'est la fonction qui sera appelée lorsque le pilote sera +chargé. Cette fonction essaye de fonctionner comme +/sys/i386/isa/isa.c qui fait les appels de probe/attach pour un +pilote au moment du redémarrage. La plus grande astuce ici est qu'il +met en correspondance l'adresse physique du segment partagé de mémoire, qui est +indiqué dans la structure isa_device à une adresse virtuelle du noyau. +Normalement, l'adresse physique est mise dans le fichier de configuration +qui construit la structure isa_device dans +/usr/src/sys/compile/KERNEL/ioconf.c. La séquence probe/attach de +/usr/src/sys/isa/isa.c traduit l'adresse physique en une virtuelle de +sorte que dans les sous-programmes de probe/attach vous puissiez +faire des choses comme + + +(int *)id->id_maddr = something; + + et se réfère juste au segment partagé de mémoire +par l'intermédiaire de pointeurs. + + + + 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 pcaload(){ + 148 int i; + 149 uprintf("PC Audio Driver Loaded\n"); + 150 for (i=0; i<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 + + +Lignes 169 - 179 + + +c'est la fonction appelée quand votre pilote n'est pas +chargé; il affiche juste un message à cet effet. + + + + 169 /* + 170 * this function is called + 171 * when the module is unloaded + 172 */ + 173 + 174 int + 175 pcaunload(){ + 176 uprintf("PC Audio Driver Unloaded\n"); + 177 return 0; + 178 } + 179 + + +Lignes 180 - 190 + + +c'est le point d'entrée qui est indiqué sur la ligne de commande +de modload. Par convention il est nommé <dev>_mod. C'est +ainsi qu'il est dé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. + +Note : Il y a eu tellement de révisions entre la version 2.0 et +2.1. Il peut ou ne peut ne pas être possible d'écrire +un module qui est portable pour chacune des trois versions. + + + + + 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 */ + + + + +Idiosyncrasies du type périphérique + + +Caractère + + + + +Bloc + + + + +Réseau + + + + +Line discipline + + + + + +Idiosyncrasies du type bus + + +ISA + + + + +EISA + + + + +PCI + + + + +SCSI + + + + +PCCARD + + + + + + + +Support du noyau + + +Structures de données + + +Structure <citerefentry><refentrytitle>struct kern_devconf</refentrytitle></citerefentry> + +Cette structure contient quelques informations sur l'état du +périphérique et de son pilote. Elle est définie dans +/usr/src/sys/sys/devconf.h comme ci-dessous : + + + +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 */ +}; + + + + +Structure <citerefentry><refentrytitle>struct proc</refentrytitle></citerefentry> + + Cette structure contient toutes les informations sur un processus. +Elle est dans définie /usr/src/sys/sys/proc.h: + + + +/* + * 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 */ +}; + + + + +Structure <citerefentry><refentrytitle>struct buf</refentrytitle></citerefentry> +La structure struct buf est employée pour s'interfacer +avec le cache de la mémoire tampon. Elle est dans +définie /usr/src/sys/sys/buf.h : + + + +/* + * 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; +}; + + + + + +Structure <citerefentry><refentrytitle>struct uio</refentrytitle></citerefentry> + +Cette structure est utilisée pour déplacer des données entre le noyau et +les espaces utilisateur par les appels système de read() et de write(). +Il est dans défini /usr/src/sys/sys/uio.h : + + + +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; +}; + + + + + + +Fonctions +plein + + + +Références. + + FreeBSD Kernel Sources http://www.freebsd.org + + + NetBSD Kernel Sources http://www.netbsd.org + + + Writing Device Drivers: Tutorial and Reference; +Tim Burke, Mark A. Parenti, Al, Wojtas; +Digital Press, ISBN 1-55558-141-2. + + + Writing A Unix Device Driver; +Janet I. Egan, Thomas J. Teixeira; +John Wiley & Sons, ISBN 0-471-62859-X. + + + Writing Device Drivers for SCO Unix; +Peter Kettle; + + + +