<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éthode classique</H2> <P></P> <P>Les routines permettant l'accès aux ports d'E/S sont dé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><asm/io.h></B>~; Aucune autre bibliothèque (<EM>library</EM>, NDT) n'est requise.</P> <P>Du fait d'une limitation de <B>gcc</B> (au moins jusqu'à 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êche de compiler à la fois avec les options d'optimisation et de mise au point (<EM>-g</EM>). Cela signifie que si vous dé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éparé, 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 à votre programme la permission de le faire. Il suffit pour cela d'appeler la fonction <B>ioperm(2)</B> (déclarée dans <B>unistd.h</B> et définie dans le noyau) quelque part au début de votre application (avant tout accès à un port d'E/S). La syntaxe est <B>ioperm(from,num,turn_on)</B>, où <B>from</B> représente le premier numéro de port et <B>num</B> le nombre de ports consécutifs à rendre accessibles. Par exemple, <B>ioperm(0x300,5,1);</B> autoriserait l'accès aux ports 0x300 à 0x304 (5 ports au total). Le dernier argument est un booléen précisant si l'on désire donner (vrai (1)) ou retirer (faux (0)) l'accès au port. Pour autoriser plusieurs ports non consécutifs, on peut appeler <B>ioperm()</B> autant que nécessaire. Consultez la page de manuel de <B>ioperm(2)</B> pour avoir des précisions sur la syntaxe.</P> <P>Votre programme ne peut appeler <B>ioperm()</B> que s'il possède les privilèges de root~; pour cela, vous devez soit le lancer comme utilisateur root, soit le rendre suid root. Il devrait être possible (Je n'ai pas essayé~; SVP, envoyez-moi un message si vous l'avez fait) d'abandonner les privilèges de root une fois l'accès aux ports obtenu par <B>ioperm()</B>. Il n'est pas nécessaire d'appeler <B>ioperm(...,0)</B> à la fin du programme pour abandonner explicitement les droits, cette procédure étant automatique.</P> <P>Les privilèges accordé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ès qu'aux ports 0x000 à 0x3ff~; pour les ports supé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ès. SVP, envoyez-moi un message si vous avez des précisions à 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 écrire un octet, appelez <B>outb(value, port);</B> (attention à l'ordre des paramètres). Pour lire un mot sur les ports x et x+1 (mot formé par un octet de chaque port, comme l'instruction INW en assembleur), appelez <B>inw(x);</B>. Pour é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ême façon que celles précédemment évoquées, mais elles respectent, en plus, une courte attente (environ une microseconde) après l'accès au port; vous pouvez passer l'attente à quatre microsecondes en #définissant <B>REALLY_SLOW_IO</B> avant d'inclure <B>asm/io.h</B>. Ces macros créent cette temporisation en écrivant (à moins que vous ne #définissiez <B>SLOW_IO_BY_JUMPING</B>, moins précis certainement) dans le port 0x80, vous devez donc préalablement autoriser l'accès à ce port 0x80 avec <B>ioperm()</B> (les écriture vers le port 0x80 ne devraient pas affecter le fonctionnement du système par ailleurs). Pour des méthodes de temporisations plus souples, lisez plus loin.</P> <P>Les pages de manuels associées à 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èmes</H2> <P></P> <P></P> <H3>Je récolte des segmentation faults lorsque j'accède auxports~!</H3> <P></P> <P>Soit votre programme n'a pas les privilèges de root, soit l'appel à <B>ioperm()</B> a échoué pour quelqu'autre raison. Vérifiez la valeur de retour de <B>ioperm()</B>.</P> <P></P> <H3>Je ne trouve pas les définitions des fonctions in*(), out*(),gcc se plaint de références inconnues~!</H3> <P></P> <P>Vous n'avez pas compilé avec l'option d'optimisation (<EM>-O</EM>), et donc gcc n'a pas pu définir les macros dans <B>asm/io.h</B>. Ou alors vous n'avez pas #inclus <B><asm/io.h></B>.</P> <P></P> <H2>1.3 <A NAME="ss1.3"></A> Une autre méthode</H2> <P></P> <P>Une autre méthode consiste à ouvrir <B>/dev/port</B> (un périphérique caractère, major number 1, minor number 4) en lecture et/ou écriture (en utilisant les fonctions habituelles d'accès aux fichiers, <B>open()</B> etc. - les fonctions <B>f*()</B> de stdio utilisent des tampons internes, évitez-les). Puis positionnez-vous (<EM>seek</EM>, NDT) au niveau de l'octet approprié dans le fichier (position 0 dans le fichier = port 0, position 1 = port 1, etc.), lisez-y ou écrivez-y ensuite un octet ou un mot. Je n'ai pas vraiment essayé et je ne suis pas absolument certain que cela marche ainsi~; envoyez-moi un message si vous avez des détails.</P> <P>Bien évidemment, votre programme doit posséder les bons droits d'accès en lecture/écriture sur <B>/dev/port</B>. Cette méthode est probablement plus lente que la méthode traditionnelle évoqué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 écrire un pilote dans le noyau~ voyez le Linux Kernel Hacker's Guide (khg-x.yy) pour les dé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ères de <A HREF="IO-Port.html#toc1">ce chapitre</A>, <A HREF="IO-Port.html#toc">Table des matières</A> générale</P> <P> <A HREF="IO-Port.html">Début</A> du document, <A HREF="#0"> Début de ce chapitre</A></P> </BODY> </HTML>