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 utilisent 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.
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
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''.
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 de plus ajouter un slot au cdevsw[], ou au bdevsw[], 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.
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 ixintrCeci 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()
''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 KERNELoù 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 allsuivi d'un ``make install'' quand le noyau me convient.
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 laisser 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.
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.
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 pouvez 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 connaît 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 */
Ce document, ainsi que d'autres peut être téléchargé sur ftp.FreeBSD.org/pub/FreeBSD/doc/.
Pour toutes questions à propos de FreeBSD, lisez la documentation avant de contacter <questions@FreeBSD.org>.
Pour les questions sur cette documentation, contactez <doc@FreeBSD.org>.