Sophie

Sophie

distrib > Mandriva > 9.0 > i586 > by-pkgid > 0d5cd12c82d627a82c59047e1ba7b8a9 > files > 1774

howto-html-fr-9.0-0.2mdk.noarch.rpm

<HTML>
<HEAD>
<TITLE>Utilisation des ports d'E/S dans les programmes C</TITLE>
</HEAD>
<BODY>
<H1>1. <A NAME="s1"></A>Utilisation des ports d'E/S dans les programmes C</H1>
<P>
<A HREF="IO-Port.html#toc1">Contenu de cette section</A></P>

<P></P>
<H2>1.1 <A NAME="ss1.1"></A> M&eacute;thode classique</H2>

<P></P>
<P>Les routines permettant l'acc&egrave;s aux ports d'E/S sont d&eacute;finies 
dans <B>/usr/include/asm/io.h</B> (ou <B>linux/include/asm-i386/io.h</B> 
dans les sources du noyau). Ce sont des macros "inline", il suffit donc 
de #inclure <B>&lt;asm/io.h&gt;</B>~; Aucune autre biblioth&egrave;que
(<EM>library</EM>, NDT) n'est requise.</P>
<P>Du fait d'une limitation de <B>gcc</B> (au moins jusqu'&agrave; la version
2.7.0
comprise), vous <B>devez</B> compiler tout code source utilisant ces
routines avec les options d'optimisation (i.e. <EM>gcc -O</EM>). Une autre
limitation de <B>gcc</B> emp&ecirc;che de compiler &agrave; la fois avec
les options
d'optimisation et de mise au point (<EM>-g</EM>). Cela signifie que si vous
d&eacute;sirez utiliser <B>gdb</B> sur un programme manipulant les ports
d'E/S,
il est judicieux de mettre les routines utilisant les ports d'E/S dans un
fichier source s&eacute;par&eacute;, puis, lors de la mise au point, de
compiler ce
fichier source avec l'option d'optimisation, le reste avec l'option de
mise au point.</P>
<P>Avant d'utiliser un port, il faut donner &agrave; votre programme la
permission de
le faire. Il suffit pour cela d'appeler la fonction <B>ioperm(2)</B>
(d&eacute;clar&eacute;e dans <B>unistd.h</B> et d&eacute;finie dans le noyau)
quelque part au d&eacute;but de votre application (avant tout acc&egrave;s
&agrave; 
un port d'E/S). La syntaxe est <B>ioperm(from,num,turn_on)</B>, o&ugrave; 
<B>from</B> repr&eacute;sente le premier num&eacute;ro de port et
<B>num</B> 
le nombre de ports cons&eacute;cutifs &agrave; rendre accessibles. Par exemple, 
<B>ioperm(0x300,5,1);</B> autoriserait l'acc&egrave;s aux ports 0x300
&agrave; 
0x304 (5 ports au total). Le dernier argument est un bool&eacute;en
pr&eacute;cisant 
si l'on d&eacute;sire donner (vrai (1)) ou retirer (faux (0)) l'acc&egrave;s 
au port. Pour autoriser plusieurs ports non cons&eacute;cutifs, on peut
appeler <B>ioperm()</B> autant que n&eacute;cessaire. Consultez la page de
manuel
de <B>ioperm(2)</B> pour avoir des pr&eacute;cisions sur la syntaxe.</P>
<P>Votre programme ne peut appeler <B>ioperm()</B> que s'il poss&egrave;de les
privil&egrave;ges de root~; pour cela, vous devez soit le lancer comme
utilisateur root, soit le rendre suid root. Il devrait &ecirc;tre possible (Je
n'ai pas essay&eacute;~; SVP, envoyez-moi un message si vous l'avez fait)
d'abandonner les privil&egrave;ges de root une fois l'acc&egrave;s aux ports
obtenu 
par <B>ioperm()</B>. Il n'est pas n&eacute;cessaire d'appeler
<B>ioperm(...,0)</B>
&agrave; la fin du programme pour abandonner explicitement les droits, cette
proc&eacute;dure &eacute;tant automatique.</P>
<P>Les privil&egrave;ges accord&eacute;s par <B>ioperm()</B> demeurent lors d'un
<B>fork()</B>, <B>exec()</B> ou <B>setuid()</B> en un utilisateur
autre que root.</P>
<P><B>ioperm()</B> ne permet l'acc&egrave;s qu'aux ports 0x000 &agrave;
0x3ff~; 
pour les ports sup&eacute;rieurs, il faut utiliser <B>iopl(2)</B> (qui
donne des
droits sur tous les ports d'un coup)~; je ne l'ai jamais fait, regardez
le manuel pour en savoir plus. Je suppose que l'argument <B>level</B> doit
valoir 3 pour autoriser l'acc&egrave;s. SVP, envoyez-moi un message si vous avez
des pr&eacute;cisions &agrave; ce sujet.</P>
<P>Maintenant, l'utilisation proprement dite... Pour lire un octet sur un port,
appelez <B>inb(port);</B> qui retourne l'octet correspondant. Pour
&eacute;crire
un octet, appelez <B>outb(value, port);</B> (attention &agrave; l'ordre des
param&egrave;tres). Pour lire un mot sur les ports x et x+1 (mot
form&eacute; par un 
octet de chaque port, comme l'instruction INW en assembleur), appelez
<B>inw(x);</B>. Pour &eacute;crire un mot vers deux ports,
<B>outw(value,x);</B>.</P>
<P>Les macros <B>inb_p()</B>, <B>outb_p()</B>, <B>inw_p()</B> et <B>outw_p()</B>
fonctionnent de la m&ecirc;me fa&ccedil;on que celles
pr&eacute;c&eacute;demment 
&eacute;voqu&eacute;es, mais elles respectent, en plus, une courte attente
(environ 
une microseconde) apr&egrave;s l'acc&egrave;s au port; vous pouvez passer
l'attente
&agrave; quatre microsecondes en #d&eacute;finissant <B>REALLY_SLOW_IO</B>
avant 
d'inclure <B>asm/io.h</B>. Ces macros cr&eacute;ent cette temporisation en 
&eacute;crivant (&agrave; moins que vous ne #d&eacute;finissiez
<B>SLOW_IO_BY_JUMPING</B>, 
moins pr&eacute;cis certainement) dans le port 0x80, vous devez donc
pr&eacute;alablement 
autoriser l'acc&egrave;s &agrave; ce port 0x80 avec <B>ioperm()</B> (les
&eacute;criture vers 
le port 0x80 ne devraient pas affecter le fonctionnement du syst&egrave;me
par ailleurs). 
Pour des m&eacute;thodes de temporisations plus souples, lisez plus loin.</P>
<P>Les pages de manuels associ&eacute;es &agrave; ces macros paraitront dans
une version
future des pages de manuels de Linux.</P>
<P></P>
<P></P>

<H2>1.2 <A NAME="ss1.2"></A> Probl&egrave;mes</H2>

<P></P>
<P></P>
<H3>Je r&eacute;colte des segmentation faults lorsque j'acc&egrave;de auxports~!</H3>

<P></P>
<P>Soit votre programme n'a pas les privil&egrave;ges de root, soit l'appel
&agrave;
<B>ioperm()</B> a &eacute;chou&eacute; pour quelqu'autre raison.
V&eacute;rifiez la valeur de
retour de <B>ioperm()</B>.</P>
<P></P>
<H3>Je ne trouve pas les d&eacute;finitions des fonctions in*(), out*(),gcc se plaint de r&eacute;f&eacute;rences inconnues~!</H3>

<P></P>
<P>Vous n'avez pas compil&eacute; avec l'option d'optimisation (<EM>-O</EM>), et
donc gcc
n'a pas pu d&eacute;finir les macros dans <B>asm/io.h</B>. Ou alors vous
n'avez pas
#inclus <B>&lt;asm/io.h&gt;</B>.</P>
<P></P>

<H2>1.3 <A NAME="ss1.3"></A> Une autre m&eacute;thode</H2>

<P></P>
<P>Une autre m&eacute;thode consiste &agrave; ouvrir <B>/dev/port</B> 
(un p&eacute;riph&eacute;rique caract&egrave;re, major number 1, minor
number 4) en 
lecture et/ou &eacute;criture (en utilisant les fonctions habituelles
d'acc&egrave;s 
aux fichiers, <B>open()</B> etc. - les fonctions <B>f*()</B> de stdio
utilisent 
des tampons internes, &eacute;vitez-les). Puis positionnez-vous
(<EM>seek</EM>, NDT) 
au niveau de l'octet appropri&eacute; dans le fichier (position 0 dans le
fichier = 
port 0, position 1 = port 1, etc.), lisez-y ou &eacute;crivez-y ensuite un
octet ou 
un mot. Je n'ai pas vraiment essay&eacute; et je ne suis pas absolument
certain que 
cela marche ainsi~; envoyez-moi un message si vous avez des d&eacute;tails.</P>
<P>Bien &eacute;videmment, votre programme doit poss&eacute;der les bons droits
d'acc&egrave;s 
en lecture/&eacute;criture sur <B>/dev/port</B>. Cette m&eacute;thode est
probablement plus
lente que la m&eacute;thode traditionnelle &eacute;voqu&eacute;e auparavant.</P>
<P></P>

<H2>1.4 <A NAME="ss1.4"></A> Interruptions (IRQs) et DMA</H2>

<P></P>
<P>Pour autant que je sache, il n'est pas possible d'utiliser les IRQs ou DMA
directement dans un programme en mode utilisateur. Vous devez &eacute;crire un
pilote dans le noyau~ voyez le Linux Kernel Hacker's Guide (khg-x.yy) pour
les d&eacute;tails et les sources du noyau pour des exemples.</P>
<P></P>
<P></P>

<HR>
<P>
Chapitre <A HREF="IO-Port-2.html">suivant</A><P>
Table des mati&egrave;res de <A HREF="IO-Port.html#toc1">ce chapitre</A>,
 <A HREF="IO-Port.html#toc">Table des mati&egrave;res</A> g&eacute;n&eacute;rale</P>
<P>
<A HREF="IO-Port.html">D&eacute;but</A> du document,
 <A HREF="#0"> D&eacute;but de ce chapitre</A></P>
</BODY>
</HTML>