<?xml version="1.0" encoding="iso-8859-15"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
  "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
<!ENTITY howto         "http://www.traduc.org/docs/HOWTO/lecture/">
<!ENTITY mini-howto    "http://www.traduc.org/docs/HOWTO/mini/lecture/">
]>

<article lang="fr">
  
  <articleinfo>
    
    <title>
    
        Petit guide de programmation des ports 
        d'entrées / sorties sous Linux
    
    </title>
    
    <subtitle>
    
        Adaptation française du <foreignphrase lang="en">Linux I/O port 
        programming mini-HOWTO</foreignphrase>
    
    </subtitle>

    <author>
        <firstname>Riku</firstname>
        <surname>Saikkonen</surname>
        <email>Riku POINT Saikkonen CHEZ hut POINT fi</email>
    </author>
    
    <othercredit role="traduction" class="translator">
        <firstname>Jean-François</firstname>
        <surname>Prévost</surname>
        <contrib>Adaptation française</contrib>
        <email>prevost CHEZ 2cse TIRET group POINT com</email>
    </othercredit>
    
    <othercredit role="relecture">
        <firstname>Guillaume</firstname>
        <surname>Lelarge</surname>
        <contrib>Relecture de la version française</contrib>
        <email>gleu CHEZ wanadoo POINT fr</email>
    </othercredit>
    
    <othercredit role="relecture">
        <firstname>Jean-Philippe</firstname>
        <surname>Guérard</surname>
        <contrib>Relecture de la version française</contrib>
        <email>fevrier CHEZ tigreraye POINT org</email>
    </othercredit>
    
    <abstract>
        <para>
        Ce document présente les différentes façons de programmer des 
        entrées / sorties pour les architectures Intel x86 ainsi 
        que de les différentes méthodes permettant l'utilisation de 
        temporisations très courtes pour les applications Linux tournant 
        en mode utilisateur.
        </para>
    </abstract>

    <pubdate>24 avril 2006</pubdate>
    <releaseinfo>Version&nbsp;: 3.0.fr.1.1</releaseinfo>

  <!-- Historique des révisions -->

  <revhistory>

    <revision>
      <revnumber>3.0.fr.1.1</revnumber>
      <date>2006-04-24</date>
      <authorinitials>JPG</authorinitials>
      <revremark>
          
          Conversion en XML. Mise à jour de l'en-tête du document 
          pour respecter les conventions actuelles du projet Traduc.org.
          
      </revremark>
    </revision>

    <revision>
      <revnumber>3.0.fr.1.0</revnumber>
      <date>2003-02-04</date>
      <authorinitials>JFP, GL, JPG</authorinitials>
      <revremark>Première traduction française.</revremark>
    </revision>

    <revision>
      <revnumber>3.0</revnumber>
      <date>2000-12-13</date>
      <authorinitials>RS</authorinitials>
    
    </revision>

  </revhistory>

  </articleinfo>
  
  <sect1>
    <title>Introduction</title>
    
    <para>
    
      Ce document traite de la façon de programmer des entrées / sorties 
      matérielles sur une architecture Intel x86 ainsi que de 
      l'utilisation de temporisations très courtes pour des applications 
      s'exécutant en mode utilisateur sous Linux. Ce document est un 
      descendant du très court Petit guide des ports d'entrées-sorties 
      (<foreignphrase lang="en">IO-Port mini-HOWTO</foreignphrase>) du 
      même auteur.
      
    </para>

    <sect2>
    <title>Droits d'utilisation</title>
    
    <para>
        Copyright &copy; 1995-2000 Riku Saikkonen.
    </para>

    <para>
        Copyright &copy; 2002-2006 Jean-François Prévost, Guillaume 
        Lelarge et Jean-Philippe Guérard pour la version française.
    </para>
    
    <para>

        Sauf mention du contraire, les Guides pratiques Linux sont la 
        propriété de leurs auteurs respectifs. Les Guides pratiques 
        Linux peuvent être copiés ou diffusés, en partie ou en 
        intégralité, sous tout format qu'il soit physique ou 
        électronique, tant que ce texte d'information sur les droits 
        d'utilisation est conservé dans toutes les copies. Les 
        diffusions commerciales de ce document sont permises et 
        encouragées&nbsp;; cependant, l'auteur souhaiterait être tenu au 
        courant de telles diffusions.
        
    </para>

    <para>
    
        Toute traduction, œuvre dérivée ou compilation incluant un Guide 
        pratique Linux doit être diffusée en respectant les conditions 
        de ce texte d'information sur les droits d'utilisation. 
        Autrement dit, vous n'avez pas le droit de créer des œuvres 
        dérivées d'un Guide pratique et d'imposer des restrictions 
        supplémentaires à sa diffusion. Des exceptions à ces règles 
        peuvent être accordées dans certains cas&nbsp;; contactez (en 
        anglais) le coordinateur des Guides pratiques Linux à l'adresse 
        ci-dessous.
    
    </para>

    <para>

        En résumé, nous souhaitons encourager la dissémination de ces 
        informations au travers le plus grand nombre de circuits 
        possibles. Cependant, nous souhaitons conserver nos droits 
        d'auteurs sur ces Guides pratiques et nous souhaitons être 
        informé de tout projet de diffusion des Guides pratiques.

    </para>

    <para>
    
        Pour toute question, envoyez un courrier électronique, en 
        anglais, à Tim Bynum, coordinateur des Guides pratiques Linux, à 
        l'adresse
        
        <email>tjbynum CHEZ metalab POINT unc POINT edu</email>.
    
    </para>
    
    <para><foreignphrase lang="en">

        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.

    </foreignphrase></para>
    
    <para><foreignphrase lang="en">

        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.

    </foreignphrase></para>
    
    <para><foreignphrase lang="en">

        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.

    </foreignphrase></para>
    
    <para><foreignphrase lang="en">

        If you have questions, please contact Tim Bynum, the Linux HOWTO 
        coordinator, at
        
        <email>tjbynum AT metalab DOT unc DOT edu</email>
        
        via email.

    </foreignphrase></para>
    
    </sect2>
    <sect2>
    <title>Commentaires et corrections</title>

    <para>

      Si vous avez des remarques ou des corrections, n'hésitez pas à m'écrire
      en anglais à l'adresse 
      
      <email>Riku POINT Saikkonen CHEZ hut POINT fi</email> 
      
      &hellip;

    </para>
    
    <para>
    
    N'hésitez pas à faire parvenir vos commentaires et suggestions
    concernant l'adaptation française de ce document au projet
    <ulink url="http://traduc.org">Traduc.org</ulink> à
    l'adresse&nbsp;:
    
    <email>commentaires CHEZ traduc POINT org</email>.

    </para>
  </sect2>
    
  </sect1>
  
  <sect1>
    <title>Utilisation des ports d'entrées / sorties en langage C</title>
    
    <sect2>
      <title>La méthode normale</title>
      
      <para>
        Les routines pour accéder aux ports d'entrées / sorties sont 
        situées dans le fichier d'en-tête 
        <filename class="headerfile">/usr/include/asm/io.h</filename> (ou 
        <filename class="headerfile">linux/include/asm-i386/io.h</filename> dans les 
        sources du noyau Linux). Ces routines sont des macros, il suffit
        donc de déclarer <literal>&num;include 
        &lt;asm/io.h&gt;</literal>; dans votre code source sans avoir 
        besoin de bibliothèques additionnelles.
      </para>
      
      <para>
        À cause d'une limitation de gcc (présente dans toutes les 
        versions que je connais, egcs y compris) vous 
        <emphasis>devez</emphasis> compiler le code source qui fait 
        appel à ces routines avec le drapeau d'optimisation 
        (<command>gcc -O1</command> ou plus), ou alternativement en 
        déclarant <literal>&num;define extern static</literal> avant la 
        ligne <literal>&num;include &lt;asm/io.h&gt;</literal> 
        (n'oubliez pas de rajouter ensuite <literal>&num;undef 
        extern</literal>).
      </para>
      
      <para>
        Pour le débogage, vous pouvez compiler avec les drapeaux 
        suivants (tout du moins avec les versions les plus récentes de 
        gcc)&nbsp;: <command>gcc -g -O</command>. Il faut savoir que 
        l'optimisation engendre un comportement parfois bizarre de la 
        part du débogueur. Si cela vous pose un réel problème, vous 
        pouvez toujours utiliser les routines d'accès aux ports 
        d'entrées / sorties dans un fichier source séparé, et ne 
        compiler que ce fichier avec le drapeau d'optimisation activé.
      </para>
      
      <sect3>
        <title>Les permissions</title>
        
        <para>
          Avant d'accéder aux ports, vous devez donner à votre 
          programme la permission de le faire. Pour cela, il vous faut 
          faire appel à la fonction <function>ioperm()</function> 
          (déclarée dans <filename class="headerfile">unistd.h</filename> et définie dans le 
          noyau) quelque part au début de votre programme (avant tout 
          accès aux ports d'entrées / sorties). La syntaxe est la 
          suivante&nbsp;: 
          <function>ioperm(<replaceable>premier&lowbar;port</replaceable>, 
          <replaceable>nombre</replaceable>, 
          <replaceable>activer</replaceable>)</function>, où
          <replaceable>premier&lowbar;port</replaceable> est le numéro 
          du premier port auquel on souhaite avoir accès et 
          <replaceable>nombre</replaceable> le nombre de ports 
          consécutifs auxquels on veut avoir la permission d'accéder.
          Par exemple, <function>ioperm(0x300, 5, 1)</function> donnerait 
          accès aux ports <literal>0x300</literal> jusqu'à 
          <literal>0x304</literal> (au total 5 ports). Le 
          dernier argument est une valeur booléenne spécifiant si on 
          autorise l'accès aux ports (vrai [1]) ou si on le 
          restreint (faux [0]). Pour activer l'accès à plusieurs 
          ports non consécutifs, vous pouvez faire plusieurs appels à 
          <function>ioperm()</function>. Reportez vous à la page de manuel 
          <citerefentry>
            <refentrytitle>ioperm</refentrytitle>
            <manvolnum>2</manvolnum>
          </citerefentry> pour plus de détails sur la syntaxe.
        </para>
        
        <para>
          L'appel à <function>ioperm()</function> dans votre 
          programme nécessite les privilèges de
          super utilisateur (root). Il faut donc que votre programme 
          soit exécuté en tant qu'utilisateur root, ou qu'il soit rendu 
          setuid root. Vous pouvez abandonner les privilèges 
          d'utilisateur root après  l'appel à 
          <function>ioperm()</function>. Il n'est pas impératif
          d'abandonner de façon explicite  les privilèges d'accès aux 
          ports en utilisant <function>ioperm( ... , 0 )</function> 
          à la fin de votre programme, ceci est fait automatiquement 
          lorsque le processus se termine.
        </para>
        
        <para>
          L'utilisation de <function>setuid()</function> par un 
          utilisateur non privilégié ne supprime pas l'accès accordé aux 
          ports par <function>ioperm()</function>. En revanche, lors d'un 
          <function>fork()</function>, le processus fils n'hérite pas des 
          permissions de son père (qui lui les garde).
        </para>
        
        <para>
          La fonction <function>ioperm()</function> permet de contrôler 
          l'accès aux ports de <literal>0x000</literal> à 
          <literal>0x3ff</literal> uniquement. Pour les ports 
          supérieurs, vous devez utiliser <function>iopl()</function> 
          qui ouvre un accès à tous les ports d'un coup. Pour donner à 
          votre programme l'accès à <emphasis>tous</emphasis> les ports 
          d'entrées / sorties (soyez certains de ce que vous faites 
          car l'accès à des ports inappropriés peut avoir des 
          conséquences désastreuses pour votre système), il suffit de 
          passer à la fonction un argument de valeur 
          <literal>3</literal> (<function>iopl(3)</function>). 
          Reportez-vous à la page de manuel 
          <citerefentry>
            <refentrytitle>iopl</refentrytitle>
            <manvolnum>2</manvolnum>
          </citerefentry>
          pour plus de détails.
        </para>
        
      </sect3>
      
      <sect3>
        <title>L'accès aux ports</title>
        
        <para>
          Pour lire un octet (8 bits) sur un port, un appel à
          <function>inb(<replaceable>port</replaceable>)</function> 
          retourne la valeur de l'octet lu. Pour l'écriture d'un octet, 
          il suffit d'appeler la fonction 
          <function>outb(<replaceable>valeur</replaceable>, 
          <replaceable>port</replaceable>)</function> (attention à 
          l'ordre des paramètres).
          La lecture d'un mot (16 bits) sur les ports 
          <literal><replaceable>x</replaceable></literal> et 
          <literal><replaceable>x</replaceable>+1</literal> (un octet 
          sur chaque port pour constituer un mot grâce à l'instruction 
          assembleur <function>inw</function>), faites appel à 
          <function>inw(<replaceable>x</replaceable>)</function>. Enfin, 
          pour l'écriture d'un mot sur les deux ports, utilisez 
          <function>outw(<replaceable>value</replaceable>, 
          <replaceable>x</replaceable>)</function>. 
          Si vous n'êtes pas certain quant à la fonction à utiliser 
          (octet ou mot), il est sage de se cantonner à l'appel de 
          <function>inb()</function> et <function>outb()</function>. La 
          plupart des périphériques sont conçus pour des accès sur un 
          octet. Notez que toutes les instructions d'accès aux ports 
          nécessitent un temps d'exécution d'au minimum une 
          microseconde.
        </para>
        
        <para>
          Les macros <function>inb&lowbar;p()</function>, 
          <function>outb&lowbar;p()</function>, 
          <function>inw&lowbar;p()</function> et 
          <function>outw&lowbar;p()</function> fonctionnent  de manière 
          identique à celles évoquées précédemment à l'exception du fait 
          qu'elles effectuent un court temps de pause additionnel après 
          l'accès au port (environ une microseconde). Vous avez la 
          possibilité d'allonger ce temps de pause à quatre 
          microsecondes avec la directive <literal>&num;define 
          REALLY&lowbar;SLOW&lowbar;IO</literal> avant de déclarer 
          <literal>&num;include &lt;asm/io.h&gt;</literal>. Ces macros 
          utilisent normalement une écriture sur le port 
          <literal>0x80</literal> pour leur temps de pause (sauf en 
          déclarant un <literal>&num;define 
          SLOW&lowbar;IO&lowbar;BY&lowbar;JUMPING</literal>, qui est en 
          revanche moins précis). Vous devez donc au préalable autoriser 
          l'accès au port <literal>0x80</literal> avec 
          <function>ioperm()</function> (l'écriture sur le port 
          <literal>0x80</literal> ne devrait avoir aucun effet 
          indésirable sur votre système).
        </para>
        
        <para>
          Si vous êtes à la recherche de méthodes plus souples 
          d'utilisation, lisez la suite&nbsp;&hellip;
        </para>
        
        <para>
          Des pages de manuel pour
          <citerefentry>
            <refentrytitle>ioperm</refentrytitle>
            <manvolnum>2</manvolnum>
          </citerefentry>,
          <citerefentry>
            <refentrytitle>iopl</refentrytitle>
            <manvolnum>2</manvolnum>
          </citerefentry> et les macros décrites ci-dessus sont 
          disponibles dans les collections assez récentes des pages de 
          manuel Linux.
        </para>
        
      </sect3>
      
    </sect2>
    
    <sect2>
      <title>Une méthode alternative&nbsp;: <filename 
             class="devicefile">/dev/port</filename></title>
      
      <para>
        Un autre moyen d'accéder aux ports d'entrées / sorties est 
        d'ouvrir en lecture ou en écriture le périphérique <filename 
        class="devicefile">/dev/port</filename> (un périphérique en mode 
        caractère, numéro majeur <literal>1</literal>, mineur 
        <literal>4</literal>) au moyen de la fonction 
        <function>open()</function>. Notons que les fonctions en 
        <function>f*()</function> de la bibliothèque stdio font appel à 
        des tampons mémoires internes, il vaut donc mieux les éviter. Il 
        suffit ensuite, comme dans le cas d'un fichier, de se 
        positionner sur l'octet approprié au moyen de la fonction 
        <function>lseek()</function> (l'octet <literal>0</literal> du 
        fichier équivaut au port <literal>0x00</literal>, l'octet 
        <literal>1</literal> au port <literal>0x01</literal>, et cætera) 
        et d'en lire (<function>read()</function>) ou écrire 
        (<function>write()</function>) un octet ou un mot.
      </para>
      
      <para>
        Il est évident que l'application doit avoir la permission 
        d'accéder au périphérique <filename 
        class="devicefile">/dev/port</filename> pour que cette méthode 
        fonctionne. Cette façon de faire reste certainement plus lente 
        que la première, mais elle ne nécessite ni optimisation lors de 
        la compilation ni appel à <function>ioperm()</function>. L'accès 
        aux privilèges de super-utilisateur n'est pas impératif non 
        plus, si vous donnez les permissions adéquates à un utilisateur 
        ou un groupe pour accéder à <filename 
        class="devicefile">/dev/port</filename> (cela reste tout de même 
        une très mauvaise idée du point de vue de la sécurité du 
        système, puisqu'il devient possible de porter atteinte au 
        système, peut-être même d'obtenir le statut de root en utilisant
        <filename class="devicefile">/dev/port</filename> pour accéder 
        directement aux disques durs, cartes réseaux, et cætera).
      </para>
      
      <para>
        Il n'est pas possible d'utiliser les fonctions
        <citerefentry>
          <refentrytitle>select</refentrytitle>
          <manvolnum>2</manvolnum>
        </citerefentry> ou
        <citerefentry>
          <refentrytitle>poll</refentrytitle>
          <manvolnum>2</manvolnum>
        </citerefentry>
        pour lire <filename class="devicefile">/dev/port</filename> puisque l'électronique du système n'a pas la
        possibilité d'avertir le microprocesseur qu'une valeur a changé sur
        un port d'entrée.
      </para>
      
    </sect2>
    
  </sect1>
  
  <sect1>
    <title>Interruptions (IRQ) et accès DMA</title>
    
    <para>
      Vous ne pouvez tout simplement pas utiliser directement les
      interruptions ou l'accès DMA depuis un processus en mode 
      utilisateur. Pour cela, il vous faut développer un pilote pour le 
      noyau. Reportez-vous au <ulink
      url="http://www.tldp.org/LDP/khg/HyperNews/get/khg.html">Linux
      Kernel Hacker's Guide</ulink> pour plus de détails et au code 
      source du noyau pour des exemples.
    </para>
    
    <para>
      Vous avez cependant la possibilité de désactiver les
      interruptions depuis une application en mode utilisateur, mais
      cela peut s'avérer dangereux (même les pilotes du noyau ne le
      font que pour des périodes de temps très brèves). Après appel
      à <function>iopl(3)</function>, vous pouvez désactiver les
      interruptions en utilisant
      <function>asm("cli");</function> et les réactiver avec
      <function>asm("sti");</function>.
    </para>
    
  </sect1>
  
  <sect1>
    <title>Temporisation de haute précision</title>
    
    <sect2>
      <title>Temporisations</title>
      
      <para>
        Avant toutes choses, il est important de préciser
        l'impossibilité de garantir un contrôle précis des temps
        d'exécution de processus en mode utilisateur du fait de la
        nature multitâche du noyau Linux. Votre processus peut être
        mis en sommeil à n'importe quel moment pour une durée allant de
        10&nbsp;millisecondes à quelques secondes (sur un système
        dont la charge est très importante). Malgré tout, pour la 
        plupart des applications utilisant les ports d'entrées / 
        sorties, cela n'est pas très important. Si vous voulez minimiser 
        cet inconvénient, vous pouvez donner à votre processus une 
        priorité plus haute (reportez-vous à la page de manuel de 
        <citerefentry>
          <refentrytitle>nice</refentrytitle>
          <manvolnum>2</manvolnum>
        </citerefentry>) ou faire appel à l'ordonnancement temps-réel 
        (voir ci-après).
      </para>
      
      <para>
        Si vous souhaitez obtenir une précision de temporisation plus
        élevée que celle qu'offre les processus en mode utilisateur 
        usuels, sachez qu'il existe des possibilités de support 
        &laquo;&nbsp;temps-réel&nbsp;&raquo; en mode utilisateur. Les 
        noyaux Linux de la série 2.x permettent de travailler en quasi 
        temps-réel. Pour les détails, reportez-vous à la page de manuel 
        de
        <citerefentry>
          <refentrytitle>sched&lowbar;setscheduler</refentrytitle>                       
          <manvolnum>2</manvolnum>
        </citerefentry>.
        Il existe également des versions spéciales du noyau offrant un 
        vrai ordonnancement temps-réel.
        <!-- Lien mort !
        voyez l'adresse <ulink url="http://luz.cs.nmt.edu/~rtlinux/"></ulink>
        pour plus d'informations à ce sujet. -->
      </para>
      
      <sect3>
        <title>Avec <function>sleep()</function> et
              <function>usleep()</function>
        </title>
        
        <para>
          Commençons tout d'abord par les appels de temporisation les
          plus simples. Pour des temporisation de plusieurs secondes,
          le meilleur choix est probablement la fonction 
          <function>sleep()</function>. Pour des durées au minimum 
          de l'ordre de dizaines de millisecondes (10&nbsp;millisecondes
          semblent être la durée minimum), <function>usleep()</function>
          devrait s'avérer suffisant. Ces fonctions libèrent l'accès au
          microprocesseur pour d'autres processus, évitant ainsi le
          gaspillage du temps machine. Les pages de manuel de
          <citerefentry>                                                          
            <refentrytitle>sleep</refentrytitle>              
            <manvolnum>3</manvolnum>
          </citerefentry> et
          <citerefentry>                                                          
            <refentrytitle>usleep</refentrytitle>              
            <manvolnum>3</manvolnum>
          </citerefentry> vous donneront plus de précisions.
        </para>

        <para>
          Pour des temporisations de moins de 50 millisecondes (en 
          fonction de la cadence du microprocesseur, de la machine ainsi 
          que de la charge du système), redonner le processeur aux 
          autres processus prend énormément de temps. En effet 
          l'ordonnanceur des tâches du noyau Linux (en tout cas pour les 
          microprocesseurs de la famille x86) prend généralement au 
          moins 10 à 30 millisecondes avant de rendre le contrôle
          au processus. De ce fait, dans les temporisations de courte 
          durée,
          <citerefentry>
            <refentrytitle>usleep</refentrytitle>
            <manvolnum>3</manvolnum>
          </citerefentry> effectue en réalité une pause plus longue que 
          celle spécifiée en paramètre, prenant au moins 
          10&nbsp;millisecondes supplémentaires.
        </para>
        
      </sect3>
      
      <sect3>
        <title><function>nanosleep()</function></title>
        
        <para>
          Dans les noyaux Linux de la série 2.0.x est apparu l'appel
          système <function>nanosleep()</function> (voir la page de 
          manuel de
          <citerefentry>                                                          
            <refentrytitle>nanosleep</refentrytitle>              
            <manvolnum>2</manvolnum>
          </citerefentry>)
          permettant d'endormir ou de retarder un processus pendant
          un laps de temps très court (quelques microsecondes ou plus).
        </para>
        
        <para>
          Pour des attentes &les; 2 millisecondes, si (et seulement si) 
          votre processus fonctionne en ordonnancement quasi temps-réel 
          (au moyen de 
          <function>sched&lowbar;setscheduler()</function>), 
          <function>nanosleep()</function> fait appel à une boucle 
          d'attente&nbsp;; si tel n'est pas le cas, le processus 
          s'endort simplement tout comme avec 
          <function>usleep()</function>.
        </para>
        
        <para>
          La boucle d'attente utilise <function>udelay()</function> (une 
          fonction interne au noyau utilisée par beaucoup de pilotes), 
          la durée de celle-ci étant calculée en fonction du nombre de
          <literal>BogoMips</literal>. La vitesse de ce type de boucle 
          d'attente est une des grandeurs que les
          <literal>BogoMips</literal> permettent de mesurer de façon 
          précise. Voyez <filename 
          class="headerfile">/usr/include/asm/delay.h</filename> pour 
          plus de détails quant à son fonctionnement.
        </para>
        
      </sect3>
      
      <sect3>
        <title>Temporisations grâce aux ports d'entrée / 
               sortie</title>

        <para>
          Les accès aux ports d'entrée / sortie sont un autre moyen 
          d'obtenir des temporisations. L'écriture ou la lecture d'un 
          octet sur le port <literal>0x80</literal> (voir ci-dessus la 
          procédure à suivre) devrait avoir pour conséquence un 
          retard d'une microseconde, indépendamment du type et de la 
          cadence du microprocesseur. Vous pouvez donc procéder de la 
          sorte afin d'obtenir un retard de quelques microsecondes. 
          L'écriture sur ce port ne devrait pas avoir d'effets 
          secondaires sur une machine classique, pour preuve certains 
          pilotes du noyau font appel à cette méthode. C'est également 
          de cette manière que 
          <function>&lcub;in&verbar;out&rcub;[bw]&lowbar;p()</function> 
          effectue une pause (voir <filename 
          class="headerfile">asm/io.h</filename>).
        </para>
        
        <para>
          Plus précisément, une opération de lecture ou d'écriture sur 
          la plupart des ports dans l'intervalle 
          <literal>0x000</literal>-<literal>0x3ff</literal> prend 
          1&nbsp;microseconde. Par exemple, si vous utilisez directement 
          le port parallèle, il suffit d'utiliser des 
          <function>inb()</function> additionnels sur ce port pour 
          obtenir une temporisation.
        </para>
        
      </sect3>
      
      <sect3>
        <title>Temporisations en assembleur</title>
        
        <para>
          Si vous connaissez le type et la fréquence du processeur de la 
          machine sur laquelle votre programme va s'exécuter, 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 
          l'ordonnanceur et, de ce fait, les temporisations peuvent 
          s'avérer plus longues que souhaitées.
        </para>
        
        <para>
          Pour les données du tableau ci-dessous, la fréquence interne 
          du microprocesseur détermine le nombre de cycles d'horloge 
          consommés. Par exemple, pour un microprocesseur à 50&nbsp;Mhz 
          (un 486DX-50), un cycle d'horloge dure 1/50000000 de seconde 
          (soit 200&nbsp;nanosecondes).
        </para>

        <informaltable pgwide="1">
         <tgroup cols="3" align="center">
            <thead>
              <row>
                <entry>Instruction</entry>
                <entry>cycles d'horloge i386</entry>
                <entry>cycles d'horloge i486</entry>
              </row>
           </thead>
           <tbody>
              <row>
                <entry><literal>xchg %bx,%bx</literal></entry>
                <entry>3</entry>
                <entry>3</entry>
              </row>
              <row>
                <entry><literal>nop</literal></entry>
                <entry>3</entry>
                <entry>1</entry>
              </row>
              <row>
                <entry><literal>or %ax,%ax</literal></entry>
                <entry>2</entry>
                <entry>1</entry>
              </row>
              <row>
                <entry><literal>mov %ax,%ax</literal></entry>
                <entry>2</entry>
                <entry>1</entry>
              </row>
              <row>
                <entry><literal>add %ax,0</literal></entry>
                <entry>2</entry>
                <entry>1</entry>
              </row>
           </tbody>
          </tgroup>
        </informaltable>
        
        <para>
          Les cycles d'horloges du Pentium devraient être les mêmes 
          que ceux du 486, à l'exception du Pentium Pro / II, dont 
          l'instruction <literal>add &percnt;ax, 0</literal> peut ne 
          consommer qu'un demi cycle d'horloge. Cette instruction 
          peut-être parfois être combinée avec une autre (cependant, du 
          fait de l'algorithme d'exécution hors de séquence 
          (<foreignphrase>out-of-order</foreignphrase>), il 
          n'est pas nécessaire qu'il s'agisse d'une instruction 
          consécutive dans le flot).
        </para>
        
        <para>
          Les instructions <literal>nop</literal> et 
          <literal>xchg</literal> du tableau ne devraient pas avoir 
          d'effets secondaires. Les autres, en revanche, peuvent 
          modifier le registre d'état, mais cela reste sans gravité 
          puisque gcc devrait le détecter. <literal>xchg 
          &percnt;bx,&percnt;bx</literal> reste un bon compromis comme 
          instruction de temporisation.
        </para>
        
        <para>
          Pour utiliser ces instructions, placez un appel
          <function>asm("<replaceable>instruction</replaceable>")</function> 
          dans votre programme. La syntaxe d'appel de ces instructions 
          est telle qu'énumérée dans le tableau ci-dessus. Si vous 
          préférez grouper plusieurs instructions dans le même appel à 
          <function>asm</function>, il vous suffit de les séparer par 
          des points-virgules. Par exemple 
          <function>asm("nop&nbsp;; nop&nbsp;; nop&nbsp;; 
          nop")</function> exécute quatre fois l'instruction 
          <literal>nop</literal>, effectuant une temporisation de quatre 
          cycles d'horloge sur un i486 ou un Pentium (ou douze cycles 
          sur un i386).
        </para>
        
        <para>
          Les instructions <function>asm()</function> sont directement intégrées au code par gcc, évitant
          ainsi la perte de temps que pourrait engendrer un appel de fonction classique.
        </para>
        
        <para>
          Les temporisations de moins d'un cycle d'horloge sont impossibles sur les
          architectures x86 d'Intel.
        </para>
        
      </sect3>
      
      <sect3>
        <title><literal>rdtsc</literal> pour Pentium</title>
        
        <para>
          Avec les microprocesseurs Pentium, vous avez la possibilité de 
          connaître le nombre de cycles d'horloge écoulés depuis le 
          dernier redémarrage avec le code C suivant (qui fait appel à 
          l'instruction appelée RDTSC)&nbsp;:
        </para>
        
          <programlisting>
            extern __inline__ unsigned long long int rdtsc()
            {
            unsigned long long int x;
            __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
            return x;
            }
          </programlisting>
          
        <para>
          Vous pouvez scruter cette valeur dans une boucle d'attente 
          afin d'obtenir un retard correspondant au nombre de cycles 
          d'horloge que vous souhaitez.
        </para>
        
      </sect3>
      
    </sect2>
    
    <sect2>
      <title>Mesure du temps</title>
      
      <para>
        Pour des durées de la précision d'une seconde, il est 
        certainement plus simple d'utiliser la fonction 
        <function>time()</function>. Pour obtenir plus de précision, 
        <function>gettimeofday()</function> a une précision d'environ 
        une microseconde (mais rappelez-vous de l'ordonnancement déjà 
        évoqué précédemment). Pour les Pentium, le fragment de code 
        <literal>rdtsc</literal> ci-dessus est précis au cycle 
        d'horloge près.
      </para>
      
      <para>
        Si vous souhaitez que votre processus reçoive un signal après un 
        certain laps de temps, utilisez <function>setitimer()</function> 
        ou <function>alarm()</function>. Voyez les pages de manuel de 
        ces fonctions pour plus de détails.
      </para>
      
    </sect2>
    
  </sect1>
  
  <sect1>
    <title>D'autres langages de programmation</title>
    
    <para>
      Les descriptions ci-dessus se concentrent principalement sur le 
      langage C. Cependant, ces exemples devraient être directement 
      applicables au C++ et à l'objective C. En assembleur, il vous 
      suffit de faire appel à <function>ioperm()</function> ou 
      <function>iopl()</function>, comme en C, et d'utiliser ensuite
      directement les ports d'entrée / sortie.
    </para>
    
    <para>
      Avec d'autres langages, à moins de pouvoir insérer du code 
      assembleur, du code C ou utiliser les appels systèmes, il est 
      probablement plus simple d'écrire un programme C très simple 
      offrant des fonctions prenant en charge les temporisations et 
      les accès aux ports d'entrées / sorties souhaités, de le 
      compiler et de le lier avec le reste de votre application. Ou 
      vous pouvez utiliser <filename 
      class="devicefile">/dev/port</filename> comme décrit plus haut.
    </para>
    
  </sect1>
  
  <sect1>
    <title>Quelques ports utiles</title>
    
    <para>
      Voici quelques informations utiles pour la programmation des ports 
      les plus utilisés. Ces ports peuvent être directement interfacés 
      avec une électronique logique de type TTL (ou CMOS).
    </para>
    
    <para>
      Si vous avez l'intention d'utiliser ces ports ou des ports 
      classiques en vue d'applications pour lesquels ils ont été conçus 
      (par exemple pour contrôler une imprimante ou un modem ordinaire), 
      vous auriez intérêt à utiliser les pilotes déjà existants (qui 
      sont généralement inclus dans le noyau) plutôt que de programmer 
      directement les ports comme décrit dans ce document. Cette section 
      s'adresse principalement aux personnes désireuses de connecter aux 
      ports d'entrée / sortie standards de leur PC des écrans à 
      cristaux liquides, des moteurs pas-à-pas ou d'autres montages 
      électroniques faits maison.
    </para>
    
    <para>
      En revanche, si votre but est de piloter un périphérique grand
      public, tel qu'un scanner, disponible sur le marché depuis
      quelques temps déjà, essayez plutôt de savoir si un pilote a
      déjà été développé. Le <ulink
      url="&howto;Hardware-HOWTO.html">Hardware-HOWTO</ulink> est un
      bon point de départ pour mener cette investigation.
    </para>
    
    <para>
      <ulink url="http://www.hut.fi/Misc/Electronics/"></ulink> est
      également une excellente source d'information sur la connexion
      de périphériques à un ordinateur et sur l'électronique en
      générale.
    </para>
    
    <sect2>
      <title>Le port parallèle</title>
      
      <para>
        L'adresse de base du port parallèle (appelée
        <replaceable>BASE</replaceable> ci-dessous) est 
        <literal>0x3bc</literal> pour <filename 
        class="devicefile">/dev/lp0</filename>, <literal>0x378</literal>
        pour <filename class="devicefile">/dev/lp1</filename> et
        <literal>0x278</literal> pour
        <filename class="devicefile">/dev/lp2</filename>.
        Si vous souhaitez piloter un matériel fonctionnant comme une
        imprimante classique, voyez le <ulink
        url="&howto;Printing-HOWTO.html">Printing-HOWTO</ulink>.
      </para>
      
      <para>
        En plus du mode standard d'écriture-seule décrit ci-dessous, il 
        existe un mode &laquo;&nbsp;étendu&nbsp;&raquo; bidirectionnel 
        sur la plupart des ports parallèles. Pour des informations à ce 
        sujet ainsi que sur les récents modes ECP / EPP (et le 
        standard IEEE 1284 en général), voici deux adresses&nbsp;:  
        <ulink url="http://www.fapo.com/"></ulink> et
        <!-- http://www.senet.com.au/~cpeacock/parallel.htm n'existe
             plus. Il semble remplacé par : http://www.beyondlogic.org/ 
             -->
        <ulink url="http://www.beyondlogic.org/"></ulink>. 
        Rappelez-vous cependant que puisque vous ne pouvez pas utiliser 
        les requêtes d'interruptions (IRQ) ou l'accès DMA dans un 
        programme en mode utilisateur, vous serez certainement obligé 
        d'écrire un pilote pour le noyau afin d'exploiter les modes 
        ECP / EPP. Il me semble que quelqu'un est déjà en train de 
        développer un tel pilote, mais je n'en sais pas plus à ce sujet.
      </para>
      
      <para>
        Le port <literal><replaceable>BASE</replaceable>+0</literal> 
        (port de données) contrôle les signaux de données du port 
        (respectivement D0 à D7 pour les bits 0 à 7; états&nbsp;: 0 = 
        bas (0V), 1 = haut (5V)). Une écriture sur ce port positionne 
        l'état des broches correspondantes. La lecture retourne la 
        dernière valeur écrite en mode standard ou étendu, sinon les 
        données présentes sur les broches connectées à un autre 
        périphérique en mode de lecture étendue.
      </para>
      
        <para>
        Le port <literal><replaceable>BASE</replaceable>+1</literal>
        (port d'état) est en lecture seule et retourne l'état des
        signaux d'entrée suivants&nbsp;:
        </para>

        <itemizedlist>
        <listitem><para>
              Bits 0 et 1 sont réservés.
        </para></listitem>
          
        <listitem><para>
              Bit 2 état d'IRQ (ne correspond pas à une broche, je ne sais pas comment il fonctionne)
        </para></listitem>
          
        <listitem><para>
              Bit 3 ERROR (1 = état haut)
        </para></listitem>

        <listitem><para>
              Bit 4 SLCT (1 = état haut)
        </para></listitem>

        <listitem><para>
              Bit 5 PE (1 = état haut)
        </para></listitem>

        <listitem><para>
              Bit 6 ACK (1 = état haut)
        </para></listitem>
        
        <listitem><para>
              Bit 7 -BUSY (0 = état haut)
        </para></listitem>

        </itemizedlist>
      
      <para>
        Le port <literal><replaceable>BASE</replaceable>+2</literal> (port de contrôle) est en écriture seule (une lecture sur
        celui-ci retourne la dernière valeur écrite) et contrôle les signaux d'état
        suivants&nbsp;:
        </para>
        
        <itemizedlist>

          <listitem><para>
              Bit 0 -STROBE (0 = état haut)
          </para></listitem>
          
          <listitem><para>
              Bit 1 -AUTO&lowbar;FD&lowbar;XT (0 = état haut)
          </para></listitem>
          
          <listitem><para>
              Bit 2 INIT (1 = état haut)
          </para></listitem>

          <listitem><para>
              Bit 3 -SLCT&lowbar;IN (0 = état haut)
          </para></listitem>

          <listitem><para>
              Bit 4 active la requête d'interruption (IRQ) du port 
              parallèle (qui survient lors d'une transition 
              bas-vers-haut de la broche ACK) lorsqu'il est positionné à 
              1.
          </para></listitem>

          <listitem><para>
              Bit 5 contrôle 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).
          </para></listitem>
          
          <listitem><para>
              Bits 6 et 7 sont réservés.
          </para></listitem>
          
        </itemizedlist>
      
        <para>
        Brochage (d'un connecteur femelle sub-D 25 sur le port) 
        (e&nbsp;= entrée, s&nbsp;= sortie)&nbsp;:
        </para>

<screen>
 1(e/s) -STROBE,   2(e/s) D0,          3(e/s) D1,          4(e/s) D2, 
 5(e/s) D3,        6(e/s) D4,          7(e/s) D5,          8(e/s) D6, 
 9(e/s) D7,       10(e) ACK,          11(e) -BUSY,        12(e) PE, 
13(e) SLCT,       14(s) -AUTO_FD_XT,  15(e) ERROR,        16(s) INIT,     
17(s) -SLCT_IN,   18-25 masse
</screen>
      
      <para>
        Les spécifications données par IBM indiquent que les broches 1, 
        14, 16 et 17 (les sorties de contrôle) sont de type collecteur 
        ouvert ramené à 5,0&nbsp;V au moyen d'une résistance de 
        4,7&nbsp;kohm (intensité de fuite de 20&nbsp;mA, de source 
        0,55&nbsp;mA, état haut de sortie 5,0&nbsp;V moins tension de 
        <foreignphrase>pull-up</foreignphrase>). Le reste des broches 
        ont une intensité de fuite de 24&nbsp;mA, de source de 
        15&nbsp;mA et leur voltage à l'état haut est au minimum de 
        2,4&nbsp;V. L'état bas pour toutes les broches est au maximum de 
        0,5&nbsp;V. Les ports parallèles d'autres types que celui d'IBM 
        peuvent s'écarter de ce standard. Pour plus d'informations à ce 
        sujet, voyez l'adresse&nbsp;: <ulink 
        url="http://www.hut.fi/Misc/Electronics/circuits/lptpower.html"></ulink>.
      </para>
      
      <warning><para>
        Faites très attention aux masses&nbsp;! J'ai déjà grillé à 
        plusieurs reprises des ports parallèles en les connectant alors 
        que l'ordinateur était sous tension. Utiliser un port parallèle 
        non intégré à la carte mère peut s'avérer une bonne solution 
        pour éviter de trop grands désagréments. Vous pouvez obtenir un 
        second port parallèle pour votre machine au moyen d'une carte 
        multi-entrées / sorties à petit prix. Il vous suffit de 
        désactiver les ports dont vous n'avez pas besoin, puis de 
        configurer le port parallèle de la carte sur une adresse libre. 
        Vous n'avez pas besoin de vous préoccuper de l'IRQ du port 
        parallèle si vous n'y faites pas appel.
      </para></warning>
      
    </sect2>
    
    <sect2>
      <title>Le port de manette de jeu</title>
      
      <para>
        Le port de manette de jeu est accessible aux adresses 
        <literal>0x200</literal>-<literal>0x207</literal>. Si vous 
        souhaitez contrôler une manette de jeu ordinaire, vous serez 
        probablement mieux servi en utilisant les pilotes distribués 
        avec le noyau.
      </para>
      
      <para>
        Brochage (pour un connecteur sub-D 15 femelle)&nbsp;:
      </para>
      
        <itemizedlist>

          <listitem>
            <para>
              1,8,9,15&nbsp;: +5&nbsp;V (Alimentation)
            </para>
          </listitem>

          <listitem>
            <para>
              4,5,12&nbsp;: masse
            </para>
          </listitem>

          <listitem>
            <para>
              2,7,10,14&nbsp;: entrées numériques, respectivement BA1, 
              BA2, BB1 et BB2
            </para>
          </listitem>

          <listitem>
            <para>
              3,6,11,13&nbsp;: entrées 
              &laquo;&nbsp;analogiques&nbsp;&raquo;, respectivement AX, 
              AY, BX et BY
            </para>
          </listitem>
          
        </itemizedlist>
      
      <para>
        Les broches fournissant une tension de +5&nbsp;V semblent 
        souvent être connectées directement sur l'alimentation de la 
        carte mère, ce qui peut leur permettre d'obtenir pas mal de 
        puissance, en fonction de la carte mère, du bloc d'alimentation 
        et du port de manette de jeu.
      </para>
      
      <para>
        Les entrées numériques sont utilisées pour les boutons des deux 
        manettes de jeu (manette A et manette B, avec deux boutons 
        chacune) que vous pouvez connecter au port. Ces entrées 
        devraient être de niveau TTL classique, ainsi vous pouvez lire 
        directement leurs valeurs sur le port d'état (voir plus bas). 
        Une véritable manette de jeu retourne un état bas (0&nbsp;V) 
        lorsque le bouton est pressé et un état haut dans l'autre cas 
        (une tension de 5&nbsp;V des broches de d'alimentation au 
        travers d'une résistance de 1&nbsp;kohm).
      </para>
      
      <!-- http://www.gel.ulaval.ca/~richar01/osa/notes/notes09.pdf -->
      <!-- http://www.promelec.ru/pdf/ne558.pdf -->
      <!-- http://www.hardwarebook.net/connector/userinput/gameportpc.html -->
      <para>
        Les pseudo entrées analogiques mesurent en réalité la 
        résistance. Le port de manette de jeu comporte un quadruple 
        monostable (une puce de type NE558) connecté aux quatre 
        entrées. Pour chaque entrée, il y a une résistance de 
        2,2&nbsp;kohm entre la broche d'entrée et la sortie du 
        monostable, et un condensateur de 0,01&nbsp;&micro;F entre la 
        sortie du monostable et la masse. Une véritable de manette de 
        jeu a un potentiomètre pour chaque axe (X et Y), connecté entre 
        le +5&nbsp;V et la broche d'entrée appropriée (AX ou AY pour la 
        manette A, ou BX ou BY pour la manette B).
      </para>
      
      <para>
        Lorsqu'il est activé, le monostable initialise ses 
        lignes de sortie à un état haut (5&nbsp;V) et attend que chaque 
        condensateur de temporisation atteigne une tension de 3,3&nbsp;V 
        avant de mettre la sortie correspondante à un état bas. La durée 
        de l'état haut de la sortie du temporisateur est proportionnelle 
        à la résistance du potentiomètre de la manette de jeu (en clair, 
        la position du manche de la manette de jeu de l'axe approprié) 
        comme expliqué ci-dessous&nbsp;:
      </para>
      <blockquote><para>
      R = (t - 24.2) / 0.011
      </para></blockquote>
      <para>
        où <literal>R</literal> est la résistance en ohm, du 
        potentiomètre et <literal>t</literal> la durée de l'état haut de 
        la sortie, en microsecondes.
      </para>
      
      <para>
        Pour effectuer une lecture sur les entrées analogiques, vous 
        devez tout d'abord activer le monostable (au moyen d'une 
        écriture sur le port, voir plus bas), puis scruter l'état des 
        quatre axes au moyen de lectures répétées jusqu'à la transition 
        à un état bas, permettant ainsi de mesurer la durée de l'état 
        haut. Cette scrutation est très gourmande en temps machine. De 
        plus, sur un système multitâche non temps-réel tel que Linux (en 
        mode utilisateur normal) le résultat n'est pas très précis, du 
        fait de l'impossibilité de scruter le port constamment (à moins 
        d'utiliser un pilote noyau et de désactiver la gestion des 
        interruptions pendant la scrutation, mais sachez que vous 
        consommerez encore plus de temps machine). Si vous savez à 
        l'avance que le signal va mettre un certain temps (plusieurs 
        dizaines de millisecondes) avant de basculer, vous pouvez faire 
        appel à <function>usleep()</function> avant de procéder à la 
        scrutation afin de laisser un peu de temps machine aux autres 
        processus.
      </para>
      
      <para>
        Le seul port d'entrées / sorties auquel vous avez besoin 
        d'accéder est le port <literal>0x201</literal> (les autres ports 
        se comportent de façon identique ou ne réagissent pas). Toute 
        écriture sur ce port, peu importe la valeur envoyée, active le 
        temporisateur. Une lecture retourne l'état des signaux 
        d'entrée&nbsp;:
      </para>   

        <itemizedlist>
        
          <listitem><para>
              Bit 0&nbsp;: AX (état de la sortie du temporisateur, 
              1&nbsp;= état haut)
          </para></listitem>
            
          <listitem><para>
              Bit 1&nbsp;: AY (état de la sortie du temporisateur,
              1&nbsp;= état haut)
          </para></listitem>
          
          <listitem><para>
              Bit 2&nbsp;: BX (état de la sortie du temporisateur,
              1&nbsp;= état haut)
          </para></listitem>

          <listitem><para>
              Bit 3&nbsp;: BY (état de la sortie du temporisateur,
              1&nbsp;= état haut)
          </para></listitem>
          
          <listitem><para>
              Bit 4&nbsp;: BA1 (entrée numérique, 1 = état haut)
          </para></listitem>

          <listitem><para>
              Bit 5&nbsp;: BA2 (entrée numérique, 1 = état haut)
          </para></listitem>

          <listitem><para>
              Bit 6&nbsp;: BB1 (entrée numérique, 1 = état haut)
          </para></listitem>

          <listitem><para>
              Bit 7&nbsp;: BB2 (entrée numérique, 1 = état haut)
          </para></listitem>
          
        </itemizedlist>
      
    </sect2>
    
    <sect2>
      <title>Le port série</title>
      
      <para>
        Si le périphérique avec lequel vous communiquez est à peu près 
        compatible avec le standard RS-232, vous devriez être en mesure 
        d'utiliser pour cela le port série. Le pilote série 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 très polyvalent et l'usage 
        de débits non-standards ne devrait pas poser de problèmes 
        particuliers.
      </para>
      
      <para>
        Voyez la page de manuel de
        <citerefentry>
          <refentrytitle>termios</refentrytitle>
          <manvolnum>3</manvolnum>
        </citerefentry>, le code source du pilote 
        (<filename>linux/drivers/char/serial.c</filename>) ainsi que la 
        page <ulink url="http://www.easysw.com/~mike/serial/"></ulink> 
        pour plus d'informations sur la programmation des ports séries 
        des systèmes Unix.
      </para>
      
    </sect2>
    
  </sect1>
  
  <sect1>
    <title>Conseils</title>
    
    <para>
      Si vous souhaitez obtenir une bonne acquisition analogique, vous 
      pouvez connecter un CAN ou un CNA au port parallèle 
      (conseil&nbsp;: pour l'alimentation, utilisez soit le port de 
      manette de jeu, soit un connecteur d'alimentation de disque 
      que vous relierez à l'extérieur de votre boîtier, à moins que vous 
      n'utilisiez un périphérique peu consommateur d'énergie, et que 
      vous puissiez utiliser le port parallèle lui-même pour 
      l'alimenter, sinon utilisez tout simplement une source 
      d'alimentation externe). Vous pouvez également investir dans 
      l'achat d'une carte d'acquisition numérique / analogique 
      ou analogique / numérique (la plupart des cartes 
      d'acquisition les plus anciennes et les plus lentes s'appuient sur 
      l'usage de ports de communication externes). Sinon, si vous pouvez 
      vous satisfaire de seulement un ou deux canaux d'acquisition, de 
      résultats peu précis et sans doute d'un mauvais niveau du zéro, 
      l'utilisation d'une carte son bas de gamme supportée par le noyau 
      Linux devrait répondre à votre attente (et vous permettre de faire 
      des acquisitions rapides).
    </para>
    
    <para>
      Avec des périphériques analogiques de précision, une mauvaise mise 
      à la masse peut générer de mauvaises mesures lors de lectures ou 
      d'écritures analogiques. Si vous rencontrez ce type de 
      désagrément, vous pouvez isoler électriquement le périphérique 
      de votre ordinateur au moyen d'optocoupleurs (sur 
      <emphasis>toutes</emphasis> les lignes entre le périphérique et 
      l'ordinateur). Vous pouvez essayer de tirer l'alimentation 
      électrique des optocoupleurs de votre ordinateur (les signaux 
      de broches non utilisées du port peuvent peut-être vous fournir 
      une puissance suffisante) afin d'obtenir une meilleure isolation.
    </para>
    
    <para>
      Si vous êtes à la recherche d'un logiciel de conception de circuits
      imprimés sous Linux, sachez qu'il existe une application libre pour
      X11 appelée <productname>Pcb</productname> qui devrait vous rendre 
      de grands services, tout du moins si vous n'avez pas de projets 
      trop compliqués. Ce logiciel est inclus dans la plupart des 
      distributions Linux et est disponible à l'adresse suivante&nbsp;: 
      <ulink url="ftp://sunsite.unc.edu/pub/Linux/apps/circuits/"></ulink>
      (nom de l'archive <filename>pcb-*</filename>).
    </para>
    
  </sect1>
  
  <sect1>
    <title>Problèmes et solutions</title>
    
    <para>
      <variablelist>
        
        <varlistentry>
          <term>Q1.</term>
          <listitem>
            <para>
              J'obtiens une erreur de segmentation lorsque j'essaye 
              d'accéder aux ports.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>R1.</term>
          <listitem>
            <para>
              Soit votre programme n'a pas les privilèges de super 
              utilisateur, soit l'appel à <function>ioperm()</function> 
              n'a pas réussi pour une raison ou une autre. Vérifiez la
              valeur de retour de <function>ioperm()</function>. 
              Vérifiez également que vous accédez bien aux ports que 
              vous avez activés préalablement avec 
              <function>ioperm()</function> (voir Q3). Si vous faites 
              appel aux macros de temporisation 
              (<function>inb&lowbar;p()</function>, 
              <function>outb&lowbar;p()</function>, et cætera), pensez 
              aussi à utiliser <function>ioperm()</function> pour 
              obtenir l'accès au port <literal>0x80</literal>.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Q2.</term>
          <listitem>
            <para>
              Je n'arrive à trouver nulle part la définition de 
              <function>in*()</function>, <function>out*()</function>, 
              de plus gcc se plaint de références non-définies.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>R2.</term>
          <listitem>
            <para>
              Vous n'avez pas lancé la compilation avec les drapeaux 
              d'optimisation (<literal>-O</literal>), en 
              conséquence gcc n'a pas pu trouver les macros définies 
              dans <filename class="headerfile">asm/io.h</filename>. 
              Ou alors, vous n'avez pas inclus du tout la ligne 
              <literal>&num;include &lt;asm/io.h&gt;</literal> dans 
              votre code.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Q3.</term>
          <listitem>
            <para>
              <function>out*()</function> ne fait rien ou ne retourne 
              que des valeurs bizarres.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>R3.</term>
          <listitem>
            <para>
              Vérifiez l'ordre des paramètres, ceux-ci devraient être 
              comme ce qui suit&nbsp;: 
              <function>outb(<replaceable>valeur</replaceable>, 
              <replaceable>port</replaceable>)</function> et non pas 
              <function>outportb(<replaceable>port</replaceable>, 
              <replaceable>valeur</replaceable>)</function> comme en 
              MS-DOS.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Q4.</term>
          <listitem>
            <para>
              Je souhaite contrôler un périphérique standard, tel 
              qu'un périphérique RS-232, une imprimante parallèle, une 
              manette de jeu,&nbsp;&hellip;
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>R4.</term>
          <listitem>
            <para>
              Vous auriez plutôt intérêt à utiliser les pilotes déjà 
              existants (dans le noyau Linux ou un serveur X ou 
              ailleurs) pour ce faire. Ces pilotes sont généralement 
              assez polyvalents. Ainsi ils arrivent même en général à 
              faire fonctionner les périphériques sortant légèrement des 
              standards. Voyez la note d'information ci-dessus sur les 
              ports standards pour de la documentation à leur sujet.
            </para>
          </listitem>
        </varlistentry>
      </variablelist>
    </para>
    
  </sect1>
  
  <sect1>
    <title>Code d'exemple</title>
    
    <para>
      Voici un exemple de programme simple permettant l'accès aux ports 
      d'entrées / sorties&nbsp;:
    </para>
    
<programlisting>
/*
* exemple.c&nbsp;: un exemple très simple d'accès aux ports d'E/S
*
* Ce programme ne fait rien d'utile, juste une écriture sur le port,
* une pause, puis une lecture sur le même port.
* À compiler avec &laquo; gcc -O2 -o exemple exemple.c &raquo;et à 
* exécuter en tant que root avec &laquo; ./exemple &raquo;.
*/

#include &#60;stdio.h&#62;
#include &#60;unistd.h&#62;
#include &#60;asm/io.h&#62;

#define BASEPORT 0x378 /* lp1 */

int main()
{
/* Obtention de l'accès aux ports */
if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);}

/* Initialisation de tous les signaux de données (D0-D7) à l'état bas (0) */
outb(0, BASEPORT);

/* Dormons pendant un moment (100 ms) */
usleep(100000);

/* Lecture sur le port d'état (BASE+1) et affichage du résultat */
printf("status&nbsp;: %d\n", inb(BASEPORT + 1));

/* Nous n'avons plus besoin de l'accès aux ports */
if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);}

exit(0);
}

/* fin d'exemple.c */
</programlisting>
      
  </sect1>
  
  <sect1>
    <title>Remerciements</title>
    
    <para>
      Beaucoup trop de personnes ont contribué à l'écriture de ce 
      document pour que je puisse en faire la liste ici, mais merci à 
      tous. Je n'ai pas pu répondre à toutes les contributions reçues, 
      je m'en excuse, mais encore merci pour votre aide.
    </para>
    
  </sect1>

  <sect1 id="adaptation-francaise" xreflabel="adaptation française">
  <title>Adaptation française</title>
  <sect2>
  <title>Traduction</title>
    <para>
        
        La traduction française de ce document a été réalisée par
        Jean-François Prévost

        <email>prevost CHEZ 2cse TIRET group POINT com</email>.
          
   </para>

  </sect2>
  <sect2>
  <title>Relecture</title>
  
    <para>
    
        La relecture de ce document a été réalisée par Guillaume Lelarge 
        
        <email>gleu CHEZ wanadoo POINT fr</email>
        
        et Jean-Philippe Guérard 
        
        <email>fevrier CHEZ tigreraye POINT org</email>.
        
    </para>
  </sect2>
</sect1>

</article>

Site hébergé sur un Cloud Public IKOULA Ikoula