Académique Documents
Professionnel Documents
Culture Documents
Conversion en XML. Mise jour de len-tte du document pour respecter les conventions actuelles du projet Traduc.org. Version 3.0.fr.1.0 Premire traduction franaise. Version 3.0 Rsum Ce document prsente les diffrentes faons de programmer des entres / sorties pour les architectures Intel x86 ainsi que de les diffrentes mthodes permettant lutilisation de temporisations trs courtes pour les applications Linux tournant en mode utilisateur. Table des matires 1. Introduction [p 2] 1.1. Droits dutilisation [p 2] 1.2. Commentaires et corrections [p 3] 2. Utilisation des ports dentres / sorties en langage C [p 3] 2.1. La mthode normale [p 3] 2.2. Une mthode alternative : /dev/port [p 4] 3. Interruptions (IRQ) et accs DMA [p 5] 4. Temporisation de haute prcision [p 5] 4.1. Temporisations [p 5] 4.2. Mesure du temps [p 8] 5. Dautres langages de programmation [p 8] 2000-12-13 RS 2003-02-04 JFP, GL, JPG
6. Quelques ports utiles [p 8] 6.1. Le port parallle [p 8] 6.2. Le port de manette de jeu [p 10] 6.3. Le port srie [p 12] 7. Conseils [p 12] 8. Problmes et solutions [p 12] 9. Code dexemple [p 13] 10. Remerciements [p 14] 11. Adaptation franaise [p 14] 11.1. Traduction [p 14] 11.2. Relecture [p 14]
1. Introduction
Ce document traite de la faon de programmer des entres / sorties matrielles sur une architecture Intel x86 ainsi que de lutilisation de temporisations trs courtes pour des applications sexcutant en mode utilisateur sous Linux. Ce document est un descendant du trs court Petit guide des ports dentres-sorties (IO-Port mini-HOWTO) du mme auteur.
All translations, derivative works, or aggregate works incorporating any Linux HOWTO documents must be covered under this copyright notice. That is, you may not produce a derivative work from a HOWTO and impose additional restrictions on its distribution. Exceptions to these rules may be granted under certain conditions; please contact the Linux HOWTO coordinator at the address given below. In short, we wish to promote dissemination of this information through as many channels as possible. However, we do wish to retain copyright on the HOWTO documents, and would like to be notified of any plans to redistribute the HOWTOs. If you have questions, please contact Tim Bynum, the Linux HOWTO coordinator, at <tjbynum AT metalab DOT unc DOT edu> via email.
activer laccs plusieurs ports non conscutifs, vous pouvez faire plusieurs appels ioperm(). Reportez vous la page de manuel ioperm(2) pour plus de dtails sur la syntaxe. Lappel ioperm() dans votre programme ncessite les privilges de super utilisateur (root). Il faut donc que votre programme soit excut en tant quutilisateur root, ou quil soit rendu setuid root. Vous pouvez abandonner les privilges dutilisateur root aprs lappel ioperm(). Il nest pas impratif dabandonner de faon explicite les privilges daccs aux ports en utilisant ioperm( ... , 0 ) la fin de votre programme, ceci est fait automatiquement lorsque le processus se termine. Lutilisation de setuid() par un utilisateur non privilgi ne supprime pas laccs accord aux ports par ioperm(). En revanche, lors dun fork(), le processus fils nhrite pas des permissions de son pre (qui lui les garde). La fonction ioperm() permet de contrler laccs aux ports de 0x000 0x3ff uniquement. Pour les ports suprieurs, vous devez utiliser iopl() qui ouvre un accs tous les ports dun coup. Pour donner votre programme laccs tous les ports dentres / sorties (soyez certains de ce que vous faites car laccs des ports inappropris peut avoir des consquences dsastreuses pour votre systme), il suffit de passer la fonction un argument de valeur 3 (iopl(3)). Reportez-vous la page de manuel iopl(2) pour plus de dtails.
positionner sur loctet appropri au moyen de la fonction lseek() (loctet 0 du fichier quivaut au port 0x00, loctet 1 au port 0x01, et ctera) et den lire (read()) ou crire (write()) un octet ou un mot. Il est vident que lapplication doit avoir la permission daccder au priphrique /dev/port pour que cette mthode fonctionne. Cette faon de faire reste certainement plus lente que la premire, mais elle ne ncessite ni optimisation lors de la compilation ni appel ioperm(). Laccs aux privilges de super-utilisateur nest pas impratif non plus, si vous donnez les permissions adquates un utilisateur ou un groupe pour accder /dev/port (cela reste tout de mme une trs mauvaise ide du point de vue de la scurit du systme, puisquil devient possible de porter atteinte au systme, peut-tre mme dobtenir le statut de root en utilisant /dev/port pour accder directement aux disques durs, cartes rseaux, et ctera). Il nest pas possible dutiliser les fonctions select(2) ou poll(2) pour lire /dev/port puisque llectronique du systme na pas la possibilit davertir le microprocesseur quune valeur a chang sur un port dentre.
4.1.2. nanosleep()
Dans les noyaux Linux de la srie 2.0.x est apparu lappel systme nanosleep() (voir la page de manuel de nanosleep(2)) permettant dendormir ou de retarder un processus pendant un laps de temps trs court (quelques microsecondes ou plus). Pour des attentes 2 millisecondes, si (et seulement si) votre processus fonctionne en ordonnancement quasi temps-rel (au moyen de sched_setscheduler()), nanosleep() fait appel une boucle dattente ; si tel nest pas le cas, le processus sendort simplement tout comme avec usleep(). La boucle dattente utilise udelay() (une fonction interne au noyau utilise par beaucoup de pilotes), la dure de celle-ci tant calcule en fonction du nombre de BogoMips. La vitesse de ce type de boucle dattente est une des grandeurs que les BogoMips permettent de mesurer de faon prcise. Voyez /usr/include/asm/delay.h pour plus de dtails quant son fonctionnement.
Pour les donnes du tableau ci-dessous, la frquence interne du microprocesseur dtermine le nombre de cycles dhorloge consomms. Par exemple, pour un microprocesseur 50 Mhz (un 486DX-50), un cycle dhorloge dure 1/50000000 de seconde (soit 200 nanosecondes). Instruction xchg %bx,%bx nop or %ax,%ax mov %ax,%ax add %ax,0 cycles dhorloge i386 3 3 2 2 2 cycles dhorloge i486 3 1 1 1 1
Les cycles dhorloges du Pentium devraient tre les mmes que ceux du 486, lexception du Pentium Pro / II, dont linstruction add %ax, 0 peut ne consommer quun demi cycle dhorloge. Cette instruction peut-tre parfois tre combine avec une autre (cependant, du fait de lalgorithme dexcution hors de squence (out-of-order), il nest pas ncessaire quil sagisse dune instruction conscutive dans le flot). Les instructions nop et xchg du tableau ne devraient pas avoir deffets secondaires. Les autres, en revanche, peuvent modifier le registre dtat, mais cela reste sans gravit puisque gcc devrait le dtecter. xchg %bx,%bx reste un bon compromis comme instruction de temporisation. Pour utiliser ces instructions, placez un appel asm("instruction") dans votre programme. La syntaxe dappel de ces instructions est telle qunumre dans le tableau ci-dessus. Si vous prfrez grouper plusieurs instructions dans le mme appel asm, il vous suffit de les sparer par des points-virgules. Par exemple asm("nop ; nop ; nop ; nop") excute quatre fois linstruction nop, effectuant une temporisation de quatre cycles dhorloge sur un i486 ou un Pentium (ou douze cycles sur un i386). Les instructions asm() sont directement intgres au code par gcc, vitant ainsi la perte de temps que pourrait engendrer un appel de fonction classique. Les temporisations de moins dun cycle dhorloge sont impossibles sur les architectures x86 dIntel.
Vous pouvez scruter cette valeur dans une boucle dattente afin dobtenir un retard correspondant au nombre de cycles dhorloge que vous souhaitez.
En plus du mode standard dcriture-seule dcrit ci-dessous, il existe un mode tendu bidirectionnel sur la plupart des ports parallles. Pour des informations ce sujet ainsi que sur les rcents modes ECP / EPP (et le standard IEEE 1284 en gnral), voici deux adresses : http://www.fapo.com/ et http://www.beyondlogic.org/. Rappelez-vous cependant que puisque vous ne pouvez pas utiliser les requtes dinterruptions (IRQ) ou laccs DMA dans un programme en mode utilisateur, vous serez certainement oblig dcrire un pilote pour le noyau afin dexploiter les modes ECP / EPP. Il me semble que quelquun est dj en train de dvelopper un tel pilote, mais je nen sais pas plus ce sujet. Le port BASE+0 (port de donnes) contrle les signaux de donnes du port (respectivement D0 D7 pour les bits 0 7; tats : 0 = bas (0V), 1 = haut (5V)). Une criture sur ce port positionne ltat des broches correspondantes. La lecture retourne la dernire valeur crite en mode standard ou tendu, sinon les donnes prsentes sur les broches connectes un autre priphrique en mode de lecture tendue. Le port BASE+1 (port dtat) est en lecture seule et retourne ltat des signaux dentre suivants : Bits 0 et 1 sont rservs. Bit 2 tat dIRQ (ne correspond pas une broche, je ne sais pas comment il fonctionne) Bit 3 ERROR (1 = tat haut) Bit 4 SLCT (1 = tat haut) Bit 5 PE (1 = tat haut) Bit 6 ACK (1 = tat haut) Bit 7 -BUSY (0 = tat haut) Le port BASE+2 (port de contrle) est en criture seule (une lecture sur celui-ci retourne la dernire valeur crite) et contrle les signaux dtat suivants : Bit 0 -STROBE (0 = tat haut) Bit 1 -AUTO_FD_XT (0 = tat haut) Bit 2 INIT (1 = tat haut) Bit 3 -SLCT_IN (0 = tat haut) Bit 4 active la requte dinterruption (IRQ) du port parallle (qui survient lors dune transition bas-vers-haut de la broche ACK) lorsquil est positionn 1. Bit 5 contrle la direction du mode tendu (0 = criture, 1 = lecture) et est en criture seule (une lecture sur ce bit ne retournera pas de valeur significative). Bits 6 et 7 sont rservs. Brochage (dun connecteur femelle sub-D 25 sur le port) (e = entre, s = sortie) :
1(e/s) -STROBE, 5(e/s) D3, 9(e/s) D7, 13(e) SLCT, 17(s) -SLCT_IN,
2(e/s) D0, 6(e/s) D4, 10(e) ACK, 14(s) -AUTO_FD_XT, 18-25 masse
Les spcifications donnes par IBM indiquent que les broches 1, 14, 16 et 17 (les sorties de contrle) sont de type collecteur ouvert ramen 5,0 V au moyen dune rsistance de 4,7 kohm (intensit de fuite de 20 mA, de source 0,55 mA, tat haut de sortie 5,0 V moins tension de pull-up). Le reste des broches ont une intensit de fuite de 24 mA, de source de 15 mA et leur voltage ltat haut est au minimum de 2,4 V. Ltat bas pour toutes les broches est au maximum de 0,5 V. Les ports parallles dautres types que celui dIBM peuvent scarter de ce standard. Pour plus dinformations ce sujet, voyez ladresse : http://www.hut.fi/Misc/Electronics/circuits/lptpower.html. Avertissement Faites trs attention aux masses ! Jai dj grill plusieurs reprises des ports parallles en les connectant alors que lordinateur tait sous tension. Utiliser un port parallle non intgr la carte mre peut savrer une bonne solution pour viter de trop grands dsagrments. Vous pouvez obtenir un second port parallle pour votre machine au moyen dune carte multi-entres / sorties petit prix. Il vous suffit de dsactiver les ports dont vous navez pas besoin, puis de configurer le port parallle de la carte sur une adresse libre. Vous navez pas besoin de vous proccuper de lIRQ du port parallle si vous ny faites pas appel.
10
Les pseudo entres analogiques mesurent en ralit la rsistance. Le port de manette de jeu comporte un quadruple monostable (une puce de type NE558) connect aux quatre entres. Pour chaque entre, il y a une rsistance de 2,2 kohm entre la broche dentre et la sortie du monostable, et un condensateur de 0,01 F entre la sortie du monostable et la masse. Une vritable de manette de jeu a un potentiomtre pour chaque axe (X et Y), connect entre le +5 V et la broche dentre approprie (AX ou AY pour la manette A, ou BX ou BY pour la manette B). Lorsquil est activ, le monostable initialise ses lignes de sortie un tat haut (5 V) et attend que chaque condensateur de temporisation atteigne une tension de 3,3 V avant de mettre la sortie correspondante un tat bas. La dure de ltat haut de la sortie du temporisateur est proportionnelle la rsistance du potentiomtre de la manette de jeu (en clair, la position du manche de la manette de jeu de laxe appropri) comme expliqu ci-dessous : R = (t - 24.2) / 0.011 o R est la rsistance en ohm, du potentiomtre et t la dure de ltat haut de la sortie, en microsecondes. Pour effectuer une lecture sur les entres analogiques, vous devez tout dabord activer le monostable (au moyen dune criture sur le port, voir plus bas), puis scruter ltat des quatre axes au moyen de lectures rptes jusqu la transition un tat bas, permettant ainsi de mesurer la dure de ltat haut. Cette scrutation est trs gourmande en temps machine. De plus, sur un systme multitche non temps-rel tel que Linux (en mode utilisateur normal) le rsultat nest pas trs prcis, du fait de limpossibilit de scruter le port constamment ( moins dutiliser un pilote noyau et de dsactiver la gestion des interruptions pendant la scrutation, mais sachez que vous consommerez encore plus de temps machine). Si vous savez lavance que le signal va mettre un certain temps (plusieurs dizaines de millisecondes) avant de basculer, vous pouvez faire appel usleep() avant de procder la scrutation afin de laisser un peu de temps machine aux autres processus. Le seul port dentres / sorties auquel vous avez besoin daccder est le port 0x201 (les autres ports se comportent de faon identique ou ne ragissent pas). Toute criture sur ce port, peu importe la valeur envoye, active le temporisateur. Une lecture retourne ltat des signaux dentre : Bit 0 : AX (tat de la sortie du temporisateur, 1 = tat haut) Bit 1 : AY (tat de la sortie du temporisateur, 1 = tat haut) Bit 2 : BX (tat de la sortie du temporisateur, 1 = tat haut) Bit 3 : BY (tat de la sortie du temporisateur, 1 = tat haut) Bit 4 : BA1 (entre numrique, 1 = tat haut) Bit 5 : BA2 (entre numrique, 1 = tat haut) Bit 6 : BB1 (entre numrique, 1 = tat haut) Bit 7 : BB2 (entre numrique, 1 = tat haut)
11
7. Conseils
Si vous souhaitez obtenir une bonne acquisition analogique, vous pouvez connecter un CAN ou un CNA au port parallle (conseil : pour lalimentation, utilisez soit le port de manette de jeu, soit un connecteur dalimentation de disque que vous relierez lextrieur de votre botier, moins que vous nutilisiez un priphrique peu consommateur dnergie, et que vous puissiez utiliser le port parallle lui-mme pour lalimenter, sinon utilisez tout simplement une source dalimentation externe). Vous pouvez galement investir dans lachat dune carte dacquisition numrique / analogique ou analogique / numrique (la plupart des cartes dacquisition les plus anciennes et les plus lentes sappuient sur lusage de ports de communication externes). Sinon, si vous pouvez vous satisfaire de seulement un ou deux canaux dacquisition, de rsultats peu prcis et sans doute dun mauvais niveau du zro, lutilisation dune carte son bas de gamme supporte par le noyau Linux devrait rpondre votre attente (et vous permettre de faire des acquisitions rapides). Avec des priphriques analogiques de prcision, une mauvaise mise la masse peut gnrer de mauvaises mesures lors de lectures ou dcritures analogiques. Si vous rencontrez ce type de dsagrment, vous pouvez isoler lectriquement le priphrique de votre ordinateur au moyen doptocoupleurs (sur toutes les lignes entre le priphrique et lordinateur). Vous pouvez essayer de tirer lalimentation lectrique des optocoupleurs de votre ordinateur (les signaux de broches non utilises du port peuvent peut-tre vous fournir une puissance suffisante) afin dobtenir une meilleure isolation. Si vous tes la recherche dun logiciel de conception de circuits imprims sous Linux, sachez quil existe une application libre pour X11 appele Pcb qui devrait vous rendre de grands services, tout du moins si vous navez pas de projets trop compliqus. Ce logiciel est inclus dans la plupart des distributions Linux et est disponible ladresse suivante : ftp://sunsite.unc.edu/pub/Linux/apps/circuits/ (nom de larchive pcb-*).
8. Problmes et solutions
Q1. Jobtiens une erreur de segmentation lorsque jessaye daccder aux ports. R1. Soit votre programme na pas les privilges de super utilisateur, soit lappel ioperm() na pas russi pour une raison ou une autre. Vrifiez la valeur de retour de ioperm(). Vrifiez galement que vous accdez bien aux ports que vous avez activs pralablement avec ioperm()
12
(voir Q3). Si vous faites appel aux macros de temporisation (inb_p(), outb_p(), et ctera), pensez aussi utiliser ioperm() pour obtenir laccs au port 0x80. Q2. Je narrive trouver nulle part la dfinition de in*(), out*(), de plus gcc se plaint de rfrences non-dfinies. R2. Vous navez pas lanc la compilation avec les drapeaux doptimisation (-O), en consquence gcc na pas pu trouver les macros dfinies dans asm/io.h. Ou alors, vous navez pas inclus du tout la ligne #include <asm/io.h> dans votre code. Q3. out*() ne fait rien ou ne retourne que des valeurs bizarres. R3. Vrifiez lordre des paramtres, ceux-ci devraient tre comme ce qui suit : outb(valeur, , port) et non pas outportb(port, , valeur) comme en MS-DOS. Q4. Je souhaite contrler un priphrique standard, tel quun priphrique RS-232, une imprimante parallle, une manette de jeu, R4. Vous auriez plutt intrt utiliser les pilotes dj existants (dans le noyau Linux ou un serveur X ou ailleurs) pour ce faire. Ces pilotes sont gnralement assez polyvalents. Ainsi ils arrivent mme en gnral faire fonctionner les priphriques sortant lgrement des standards. Voyez la note dinformation ci-dessus sur les ports standards pour de la documentation leur sujet.
9. Code dexemple
Voici un exemple de programme simple permettant laccs aux ports dentres / sorties :
/* * exemple.c : un exemple trs simple daccs aux ports dE/S * * Ce programme ne fait rien dutile, juste une criture sur le port, * une pause, puis une lecture sur le mme port. * compiler avec gcc -O2 -o exemple exemple.c et * excuter en tant que root avec ./exemple . */ #include <stdio.h> #include <unistd.h> #include <asm/io.h> #define BASEPORT 0x378 /* lp1 */ int main()
13
{ /* Obtention de laccs aux ports */ if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);} /* Initialisation de tous les signaux de donnes (D0-D7) ltat bas (0) */ outb(0, BASEPORT); /* Dormons pendant un moment (100 ms) */ usleep(100000); /* Lecture sur le port dtat (BASE+1) et affichage du rsultat */ printf("status : %d\n", inb(BASEPORT + 1)); /* Nous navons plus besoin de laccs aux ports */ if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);} exit(0); } /* fin dexemple.c */
10. Remerciements
Beaucoup trop de personnes ont contribu lcriture de ce document pour que je puisse en faire la liste ici, mais merci tous. Je nai pas pu rpondre toutes les contributions reues, je men excuse, mais encore merci pour votre aide.
11.2. Relecture
La relecture de ce document a t ralise par Guillaume Lelarge <gleu CHEZ wanadoo POINT fr> et Jean-Philippe Gurard <fevrier CHEZ tigreraye POINT org>.
14