Vous êtes sur la page 1sur 14

Petit guide de programmation des ports dentres / sorties sous Linux

Adaptation franaise du Linux I/O port programming mini-HOWTO Riku Saikkonen


<Riku POINT Saikkonen CHEZ hut POINT fi> Adaptation franaise: Jean-Franois Prvost Relecture de la version franaise: Guillaume Lelarge, Jean-Philippe Gurard Version : 3.0.fr.1.1 24 avril 2006 Historique des versions Version 3.0.fr.1.1 2006-04-24 JPG

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.

1.1. Droits dutilisation


Copyright 1995-2000 Riku Saikkonen. Copyright 2002-2006 Jean-Franois Prvost, Guillaume Lelarge et Jean-Philippe Gurard pour la version franaise. Sauf mention du contraire, les Guides pratiques Linux sont la proprit de leurs auteurs respectifs. Les Guides pratiques Linux peuvent tre copis ou diffuss, en partie ou en intgralit, sous tout format quil soit physique ou lectronique, tant que ce texte dinformation sur les droits dutilisation est conserv dans toutes les copies. Les diffusions commerciales de ce document sont permises et encourages ; cependant, lauteur souhaiterait tre tenu au courant de telles diffusions. Toute traduction, uvre drive ou compilation incluant un Guide pratique Linux doit tre diffuse en respectant les conditions de ce texte dinformation sur les droits dutilisation. Autrement dit, vous navez pas le droit de crer des uvres drives dun Guide pratique et dimposer des restrictions supplmentaires sa diffusion. Des exceptions ces rgles peuvent tre accordes dans certains cas ; contactez (en anglais) le coordinateur des Guides pratiques Linux ladresse ci-dessous. En rsum, nous souhaitons encourager la dissmination de ces informations au travers le plus grand nombre de circuits possibles. Cependant, nous souhaitons conserver nos droits dauteurs sur ces Guides pratiques et nous souhaitons tre inform de tout projet de diffusion des Guides pratiques. Pour toute question, envoyez un courrier lectronique, en anglais, Tim Bynum, coordinateur des Guides pratiques Linux, ladresse <tjbynum CHEZ metalab POINT unc POINT edu>. Unless otherwise stated, Linux HOWTO documents are copyrighted by their respective authors. Linux HOWTO documents may be reproduced and distributed in whole or in part, in any medium physical or electronic, as long as this copyright notice is retained on all copies. Commercial redistribution is allowed and encouraged; however, the author would like to be notified of any such distributions.

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.

1.2. Commentaires et corrections


Si vous avez des remarques ou des corrections, nhsitez pas mcrire en anglais ladresse <Riku POINT Saikkonen CHEZ hut POINT fi> Nhsitez pas faire parvenir vos commentaires et suggestions concernant ladaptation franaise de ce document au projet Traduc.org ladresse : <commentaires CHEZ traduc POINT org>.

2. Utilisation des ports dentres / sorties en langage C


2.1. La mthode normale
Les routines pour accder aux ports dentres / sorties sont situes dans le fichier den-tte /usr/include/asm/io.h (ou linux/include/asm-i386/io.h dans les sources du noyau Linux). Ces routines sont des macros, il suffit donc de dclarer #include <asm/io.h>; dans votre code source sans avoir besoin de bibliothques additionnelles. cause dune limitation de gcc (prsente dans toutes les versions que je connais, egcs y compris) vous devez compiler le code source qui fait appel ces routines avec le drapeau doptimisation (gcc -O1 ou plus), ou alternativement en dclarant #define extern static avant la ligne #include <asm/io.h> (noubliez pas de rajouter ensuite #undef extern). Pour le dbogage, vous pouvez compiler avec les drapeaux suivants (tout du moins avec les versions les plus rcentes de gcc) : gcc -g -O. Il faut savoir que loptimisation engendre un comportement parfois bizarre de la part du dbogueur. Si cela vous pose un rel problme, vous pouvez toujours utiliser les routines daccs aux ports dentres / sorties dans un fichier source spar, et ne compiler que ce fichier avec le drapeau doptimisation activ.

2.1.1. Les permissions


Avant daccder aux ports, vous devez donner votre programme la permission de le faire. Pour cela, il vous faut faire appel la fonction ioperm() (dclare dans unistd.h et dfinie dans le noyau) quelque part au dbut de votre programme (avant tout accs aux ports dentres / sorties). La syntaxe est la suivante : ioperm(premier_port, , nombre, , activer), o premier_port est le numro du premier port auquel on souhaite avoir accs et nombre le nombre de ports conscutifs auxquels on veut avoir la permission daccder. Par exemple, ioperm(0x300, 5, 1) donnerait accs aux ports 0x300 jusqu 0x304 (au total 5 ports). Le dernier argument est une valeur boolenne spcifiant si on autorise laccs aux ports (vrai [1]) ou si on le restreint (faux [0]). Pour

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.

2.1.2. Laccs aux ports


Pour lire un octet (8 bits) sur un port, un appel inb(port) retourne la valeur de loctet lu. Pour lcriture dun octet, il suffit dappeler la fonction outb(valeur, , port) (attention lordre des paramtres). La lecture dun mot (16 bits) sur les ports x et x+1 (un octet sur chaque port pour constituer un mot grce linstruction assembleur inw), faites appel inw(x). Enfin, pour lcriture dun mot sur les deux ports, utilisez outw(value, , x). Si vous ntes pas certain quant la fonction utiliser (octet ou mot), il est sage de se cantonner lappel de inb() et outb(). La plupart des priphriques sont conus pour des accs sur un octet. Notez que toutes les instructions daccs aux ports ncessitent un temps dexcution dau minimum une microseconde. Les macros inb_p(), outb_p(), inw_p() et outw_p() fonctionnent de manire identique celles voques prcdemment lexception du fait quelles effectuent un court temps de pause additionnel aprs laccs au port (environ une microseconde). Vous avez la possibilit dallonger ce temps de pause quatre microsecondes avec la directive #define REALLY_SLOW_IO avant de dclarer #include <asm/io.h>. Ces macros utilisent normalement une criture sur le port 0x80 pour leur temps de pause (sauf en dclarant un #define SLOW_IO_BY_JUMPING, qui est en revanche moins prcis). Vous devez donc au pralable autoriser laccs au port 0x80 avec ioperm() (lcriture sur le port 0x80 ne devrait avoir aucun effet indsirable sur votre systme). Si vous tes la recherche de mthodes plus souples dutilisation, lisez la suite Des pages de manuel pour ioperm(2), iopl(2) et les macros dcrites ci-dessus sont disponibles dans les collections assez rcentes des pages de manuel Linux.

2.2. Une mthode alternative : /dev/port


Un autre moyen daccder aux ports dentres / sorties est douvrir en lecture ou en criture le priphrique /dev/port (un priphrique en mode caractre, numro majeur 1, mineur 4) au moyen de la fonction open(). Notons que les fonctions en f*() de la bibliothque stdio font appel des tampons mmoires internes, il vaut donc mieux les viter. Il suffit ensuite, comme dans le cas dun fichier, de se

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.

3. Interruptions (IRQ) et accs DMA


Vous ne pouvez tout simplement pas utiliser directement les interruptions ou laccs DMA depuis un processus en mode utilisateur. Pour cela, il vous faut dvelopper un pilote pour le noyau. Reportez-vous au Linux Kernel Hackers Guide pour plus de dtails et au code source du noyau pour des exemples. Vous avez cependant la possibilit de dsactiver les interruptions depuis une application en mode utilisateur, mais cela peut savrer dangereux (mme les pilotes du noyau ne le font que pour des priodes de temps trs brves). Aprs appel iopl(3), vous pouvez dsactiver les interruptions en utilisant asm("cli"); et les ractiver avec asm("sti");.

4. Temporisation de haute prcision


4.1. Temporisations
Avant toutes choses, il est important de prciser limpossibilit de garantir un contrle prcis des temps dexcution de processus en mode utilisateur du fait de la nature multitche du noyau Linux. Votre processus peut tre mis en sommeil nimporte quel moment pour une dure allant de 10 millisecondes quelques secondes (sur un systme dont la charge est trs importante). Malgr tout, pour la plupart des applications utilisant les ports dentres / sorties, cela nest pas trs important. Si vous voulez minimiser cet inconvnient, vous pouvez donner votre processus une priorit plus haute (reportez-vous la page de manuel de nice(2)) ou faire appel lordonnancement temps-rel (voir ci-aprs). Si vous souhaitez obtenir une prcision de temporisation plus leve que celle quoffre les processus en mode utilisateur usuels, sachez quil existe des possibilits de support temps-rel en mode utilisateur. Les noyaux Linux de la srie 2.x permettent de travailler en quasi temps-rel. Pour les dtails, reportez-vous la page de manuel de sched_setscheduler(2). Il existe galement des versions spciales du noyau offrant un vrai ordonnancement temps-rel.

4.1.1. Avec sleep() et usleep()


Commenons tout dabord par les appels de temporisation les plus simples. Pour des temporisation de plusieurs secondes, le meilleur choix est probablement la fonction sleep(). Pour des dures au minimum de lordre de dizaines de millisecondes (10 millisecondes semblent tre la dure minimum), usleep() devrait savrer suffisant. Ces fonctions librent laccs au microprocesseur pour dautres processus, vitant ainsi le gaspillage du temps machine. Les pages de manuel de sleep(3) et usleep(3) vous donneront plus de prcisions. Pour des temporisations de moins de 50 millisecondes (en fonction de la cadence du microprocesseur, de la machine ainsi que de la charge du systme), redonner le processeur aux autres processus prend normment de temps. En effet lordonnanceur des tches du noyau Linux (en tout cas pour les microprocesseurs de la famille x86) prend gnralement au moins 10 30 millisecondes avant de rendre le contrle au processus. De ce fait, dans les temporisations de courte dure, usleep(3) effectue en ralit une pause plus longue que celle spcifie en paramtre, prenant au moins 10 millisecondes supplmentaires.

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.

4.1.3. Temporisations grce aux ports dentre / sortie


Les accs aux ports dentre / sortie sont un autre moyen dobtenir des temporisations. Lcriture ou la lecture dun octet sur le port 0x80 (voir ci-dessus la procdure suivre) devrait avoir pour consquence un retard dune microseconde, indpendamment du type et de la cadence du microprocesseur. Vous pouvez donc procder de la sorte afin dobtenir un retard de quelques microsecondes. Lcriture sur ce port ne devrait pas avoir deffets secondaires sur une machine classique, pour preuve certains pilotes du noyau font appel cette mthode. Cest galement de cette manire que {in|out}[bw]_p() effectue une pause (voir asm/io.h). Plus prcisment, une opration de lecture ou dcriture sur la plupart des ports dans lintervalle 0x000-0x3ff prend 1 microseconde. Par exemple, si vous utilisez directement le port parallle, il suffit dutiliser des inb() additionnels sur ce port pour obtenir une temporisation.

4.1.4. Temporisations en assembleur


Si vous connaissez le type et la frquence du processeur de la machine sur laquelle votre programme va sexcuter, vous pouvez coder en dur les temporisations en faisant appel certaines instructions assembleur. Rappelez-vous cependant qu tout moment votre processus peut-tre mis en attente par lordonnanceur et, de ce fait, les temporisations peuvent savrer plus longues que souhaites.

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.

4.1.5. rdtsc pour Pentium


Avec les microprocesseurs Pentium, vous avez la possibilit de connatre le nombre de cycles dhorloge couls depuis le dernier redmarrage avec le code C suivant (qui fait appel linstruction appele RDTSC) :
extern __inline__ unsigned long long int rdtsc() { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; }

Vous pouvez scruter cette valeur dans une boucle dattente afin dobtenir un retard correspondant au nombre de cycles dhorloge que vous souhaitez.

4.2. Mesure du temps


Pour des dures de la prcision dune seconde, il est certainement plus simple dutiliser la fonction time(). Pour obtenir plus de prcision, gettimeofday() a une prcision denviron une microseconde (mais rappelez-vous de lordonnancement dj voqu prcdemment). Pour les Pentium, le fragment de code rdtsc ci-dessus est prcis au cycle dhorloge prs. Si vous souhaitez que votre processus reoive un signal aprs un certain laps de temps, utilisez setitimer() ou alarm(). Voyez les pages de manuel de ces fonctions pour plus de dtails.

5. Dautres langages de programmation


Les descriptions ci-dessus se concentrent principalement sur le langage C. Cependant, ces exemples devraient tre directement applicables au C++ et lobjective C. En assembleur, il vous suffit de faire appel ioperm() ou iopl(), comme en C, et dutiliser ensuite directement les ports dentre / sortie. Avec dautres langages, moins de pouvoir insrer du code assembleur, du code C ou utiliser les appels systmes, il est probablement plus simple dcrire un programme C trs simple offrant des fonctions prenant en charge les temporisations et les accs aux ports dentres / sorties souhaits, de le compiler et de le lier avec le reste de votre application. Ou vous pouvez utiliser /dev/port comme dcrit plus haut.

6. Quelques ports utiles


Voici quelques informations utiles pour la programmation des ports les plus utiliss. Ces ports peuvent tre directement interfacs avec une lectronique logique de type TTL (ou CMOS). Si vous avez lintention dutiliser ces ports ou des ports classiques en vue dapplications pour lesquels ils ont t conus (par exemple pour contrler une imprimante ou un modem ordinaire), vous auriez intrt utiliser les pilotes dj existants (qui sont gnralement inclus dans le noyau) plutt que de programmer directement les ports comme dcrit dans ce document. Cette section sadresse principalement aux personnes dsireuses de connecter aux ports dentre / sortie standards de leur PC des crans cristaux liquides, des moteurs pas--pas ou dautres montages lectroniques faits maison. En revanche, si votre but est de piloter un priphrique grand public, tel quun scanner, disponible sur le march depuis quelques temps dj, essayez plutt de savoir si un pilote a dj t dvelopp. Le Hardware-HOWTO est un bon point de dpart pour mener cette investigation. http://www.hut.fi/Misc/Electronics/ est galement une excellente source dinformation sur la connexion de priphriques un ordinateur et sur llectronique en gnrale.

6.1. Le port parallle


Ladresse de base du port parallle (appele BASE ci-dessous) est 0x3bc pour /dev/lp0, 0x378 pour /dev/lp1 et 0x278 pour /dev/lp2. Si vous souhaitez piloter un matriel fonctionnant comme une imprimante classique, voyez le Printing-HOWTO.

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

3(e/s) D1, 7(e/s) D5, 11(e) -BUSY, 15(e) ERROR,

4(e/s) D2, 8(e/s) D6, 12(e) PE, 16(s) INIT,

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.

6.2. Le port de manette de jeu


Le port de manette de jeu est accessible aux adresses 0x200-0x207. Si vous souhaitez contrler une manette de jeu ordinaire, vous serez probablement mieux servi en utilisant les pilotes distribus avec le noyau. Brochage (pour un connecteur sub-D 15 femelle) : 1,8,9,15 : +5 V (Alimentation) 4,5,12 : masse 2,7,10,14 : entres numriques, respectivement BA1, BA2, BB1 et BB2 3,6,11,13 : entres analogiques , respectivement AX, AY, BX et BY Les broches fournissant une tension de +5 V semblent souvent tre connectes directement sur lalimentation de la carte mre, ce qui peut leur permettre dobtenir pas mal de puissance, en fonction de la carte mre, du bloc dalimentation et du port de manette de jeu. Les entres numriques sont utilises pour les boutons des deux manettes de jeu (manette A et manette B, avec deux boutons chacune) que vous pouvez connecter au port. Ces entres devraient tre de niveau TTL classique, ainsi vous pouvez lire directement leurs valeurs sur le port dtat (voir plus bas). Une vritable manette de jeu retourne un tat bas (0 V) lorsque le bouton est press et un tat haut dans lautre cas (une tension de 5 V des broches de dalimentation au travers dune rsistance de 1 kohm).

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

6.3. Le port srie


Si le priphrique avec lequel vous communiquez est peu prs compatible avec le standard RS-232, vous devriez tre en mesure dutiliser pour cela le port srie. Le pilote srie du noyau Linux devrait suffire pour la plupart des applications (vous ne devriez pas avoir programmer le port directement, de plus vous devriez probablement crire un pilote pour le noyau si vous souhaitiez le faire), il est en effet trs polyvalent et lusage de dbits non-standards ne devrait pas poser de problmes particuliers. Voyez la page de manuel de termios(3), le code source du pilote (linux/drivers/char/serial.c) ainsi que la page http://www.easysw.com/~mike/serial/ pour plus dinformations sur la programmation des ports sries des systmes Unix.

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. Adaptation franaise


11.1. Traduction
La traduction franaise de ce document a t ralise par Jean-Franois Prvost <prevost CHEZ 2cse TIRET group POINT com>.

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

Vous aimerez peut-être aussi