<html><head><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>2. Utilisation des ports d'entrées / sorties en langage C</title><link href="style.css" rel="stylesheet" type="text/css"><meta content="DocBook XSL Stylesheets V1.69.1" name="generator"><link rel="start" href="index.html" title=" Petit guide de programmation des ports d'entrées / sorties sous Linux "><link rel="up" href="index.html" title=" Petit guide de programmation des ports d'entrées / sorties sous Linux "><link rel="prev" href="ar01s01.html" title="1. Introduction"><link rel="next" href="ar01s03.html" title="3. Interruptions (IRQ) et accès DMA"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table summary="Navigation header" width="100%"><tr><th align="center" colspan="3">2. Utilisation des ports d'entrées / sorties en langage C</th></tr><tr><td align="left" width="20%"><a accesskey="p" href="ar01s01.html">Précédent</a> </td><th align="center" width="60%"> </th><td align="right" width="20%"> <a accesskey="n" href="ar01s03.html">Suivant</a></td></tr></table><hr></div><div class="sect1" lang="fr"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="N100A0"></a>2. Utilisation des ports d'entrées / sorties en langage C</h2></div></div><div></div></div><div class="sect2" lang="fr"><div class="titlepage"><div><div><h3 class="title"><a name="N100A3"></a>2.1. La méthode normale</h3></div></div><div></div></div><p> Les routines pour accéder aux ports d'entrées / sorties sont situées dans le fichier d'en-tête <code class="filename">/usr/include/asm/io.h</code> (ou <code class="filename">linux/include/asm-i386/io.h</code> dans les sources du noyau Linux). Ces routines sont des macros, il suffit donc de déclarer <code class="literal">#include <asm/io.h></code>; dans votre code source sans avoir besoin de bibliothèques additionnelles. </p><p> À cause d'une limitation de gcc (présente dans toutes les versions que je connais, egcs y compris) vous <span class="emphasis"><em>devez</em></span> compiler le code source qui fait appel à ces routines avec le drapeau d'optimisation (<span><strong class="command">gcc -O1</strong></span> ou plus), ou alternativement en déclarant <code class="literal">#define extern static</code> avant la ligne <code class="literal">#include <asm/io.h></code> (n'oubliez pas de rajouter ensuite <code class="literal">#undef extern</code>). </p><p> Pour le débogage, vous pouvez compiler avec les drapeaux suivants (tout du moins avec les versions les plus récentes de gcc) : <span><strong class="command">gcc -g -O</strong></span>. 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é. </p><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N100D1"></a>2.1.1. Les permissions</h4></div></div><div></div></div><p> 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 <code class="function">ioperm()</code> (déclarée dans <code class="filename">unistd.h</code> 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 : <code class="function">ioperm(<em class="replaceable"><code>premier_port</code></em>, , <em class="replaceable"><code>nombre</code></em>, , <em class="replaceable"><code>activer</code></em>)</code>, où <em class="replaceable"><code>premier_port</code></em> est le numéro du premier port auquel on souhaite avoir accès et <em class="replaceable"><code>nombre</code></em> le nombre de ports consécutifs auxquels on veut avoir la permission d'accéder. Par exemple, <code class="function">ioperm(0x300, 5, 1)</code> donnerait accès aux ports <code class="literal">0x300</code> jusqu'à <code class="literal">0x304</code> (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 à <code class="function">ioperm()</code>. Reportez vous à la page de manuel <span class="citerefentry"><span class="refentrytitle">ioperm</span>(2)</span> pour plus de détails sur la syntaxe. </p><p> L'appel à <code class="function">ioperm()</code> 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 à <code class="function">ioperm()</code>. Il n'est pas impératif d'abandonner de façon explicite les privilèges d'accès aux ports en utilisant <code class="function">ioperm( ... , 0 )</code> à la fin de votre programme, ceci est fait automatiquement lorsque le processus se termine. </p><p> L'utilisation de <code class="function">setuid()</code> par un utilisateur non privilégié ne supprime pas l'accès accordé aux ports par <code class="function">ioperm()</code>. En revanche, lors d'un <code class="function">fork()</code>, le processus fils n'hérite pas des permissions de son père (qui lui les garde). </p><p> La fonction <code class="function">ioperm()</code> permet de contrôler l'accès aux ports de <code class="literal">0x000</code> à <code class="literal">0x3ff</code> uniquement. Pour les ports supérieurs, vous devez utiliser <code class="function">iopl()</code> qui ouvre un accès à tous les ports d'un coup. Pour donner à votre programme l'accès à <span class="emphasis"><em>tous</em></span> 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 <code class="literal">3</code> (<code class="function">iopl(3)</code>). Reportez-vous à la page de manuel <span class="citerefentry"><span class="refentrytitle">iopl</span>(2)</span> pour plus de détails. </p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N10147"></a>2.1.2. L'accès aux ports</h4></div></div><div></div></div><p> Pour lire un octet (8 bits) sur un port, un appel à <code class="function">inb(<em class="replaceable"><code>port</code></em>)</code> retourne la valeur de l'octet lu. Pour l'écriture d'un octet, il suffit d'appeler la fonction <code class="function">outb(<em class="replaceable"><code>valeur</code></em>, , <em class="replaceable"><code>port</code></em>)</code> (attention à l'ordre des paramètres). La lecture d'un mot (16 bits) sur les ports <code class="literal"><em class="replaceable"><code>x</code></em></code> et <code class="literal"><em class="replaceable"><code>x</code></em>+1</code> (un octet sur chaque port pour constituer un mot grâce à l'instruction assembleur <code class="function">inw</code>), faites appel à <code class="function">inw(<em class="replaceable"><code>x</code></em>)</code>. Enfin, pour l'écriture d'un mot sur les deux ports, utilisez <code class="function">outw(<em class="replaceable"><code>value</code></em>, , <em class="replaceable"><code>x</code></em>)</code>. Si vous n'êtes pas certain quant à la fonction à utiliser (octet ou mot), il est sage de se cantonner à l'appel de <code class="function">inb()</code> et <code class="function">outb()</code>. 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. </p><p> Les macros <code class="function">inb_p()</code>, <code class="function">outb_p()</code>, <code class="function">inw_p()</code> et <code class="function">outw_p()</code> 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 <code class="literal">#define REALLY_SLOW_IO</code> avant de déclarer <code class="literal">#include <asm/io.h></code>. Ces macros utilisent normalement une écriture sur le port <code class="literal">0x80</code> pour leur temps de pause (sauf en déclarant un <code class="literal">#define SLOW_IO_BY_JUMPING</code>, qui est en revanche moins précis). Vous devez donc au préalable autoriser l'accès au port <code class="literal">0x80</code> avec <code class="function">ioperm()</code> (l'écriture sur le port <code class="literal">0x80</code> ne devrait avoir aucun effet indésirable sur votre système). </p><p> Si vous êtes à la recherche de méthodes plus souples d'utilisation, lisez la suite … </p><p> Des pages de manuel pour <span class="citerefentry"><span class="refentrytitle">ioperm</span>(2)</span>, <span class="citerefentry"><span class="refentrytitle">iopl</span>(2)</span> et les macros décrites ci-dessus sont disponibles dans les collections assez récentes des pages de manuel Linux. </p></div></div><div class="sect2" lang="fr"><div class="titlepage"><div><div><h3 class="title"><a name="N101C3"></a>2.2. Une méthode alternative : <code class="filename">/dev/port</code></h3></div></div><div></div></div><p> Un autre moyen d'accéder aux ports d'entrées / sorties est d'ouvrir en lecture ou en écriture le périphérique <code class="filename">/dev/port</code> (un périphérique en mode caractère, numéro majeur <code class="literal">1</code>, mineur <code class="literal">4</code>) au moyen de la fonction <code class="function">open()</code>. Notons que les fonctions en <code class="function">f*()</code> 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 <code class="function">lseek()</code> (l'octet <code class="literal">0</code> du fichier équivaut au port <code class="literal">0x00</code>, l'octet <code class="literal">1</code> au port <code class="literal">0x01</code>, et cætera) et d'en lire (<code class="function">read()</code>) ou écrire (<code class="function">write()</code>) un octet ou un mot. </p><p> Il est évident que l'application doit avoir la permission d'accéder au périphérique <code class="filename">/dev/port</code> 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 à <code class="function">ioperm()</code>. 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 à <code class="filename">/dev/port</code> (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 <code class="filename">/dev/port</code> pour accéder directement aux disques durs, cartes réseaux, et cætera). </p><p> Il n'est pas possible d'utiliser les fonctions <span class="citerefentry"><span class="refentrytitle">select</span>(2)</span> ou <span class="citerefentry"><span class="refentrytitle">poll</span>(2)</span> pour lire <code class="filename">/dev/port</code> 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. </p></div></div><div class="navfooter"><hr><table summary="Navigation footer" width="100%"><tr><td align="left" width="40%"><a accesskey="p" href="ar01s01.html">Précédent</a> </td><td align="center" width="20%"> </td><td align="right" width="40%"> <a accesskey="n" href="ar01s03.html">Suivant</a></td></tr><tr><td valign="top" align="left" width="40%">1. Introduction </td><td align="center" width="20%"><a accesskey="h" href="index.html">Sommaire</a></td><td valign="top" align="right" width="40%"> 3. Interruptions (IRQ) et accès DMA</td></tr></table></div></body></html>