Le Linux Serial Programming HOWTO <author>par Peter H. Baumann, <TT><HTMLURL URL=3D"mailto:Peter.Bauman= n@dlr.de" NAME=3D"Peter.Baumann@dlr.de"></TT> &nl; Adaptation fran=E7aise Etienne BERNARD <TT><HTMLURL URL=3D"mailto:eb@via.ec= p.fr" NAME=3D"eb@via.ecp.fr"></TT> <date>v1.0, 22 janvier 1998 <abstract> Ce document d=E9crit comment programmer sous Linux la communication avec de= s p=E9riph=E9riques sur port s=E9rie. </abstract> <TOC> =20 <SECT>Introduction <P> Voici le Linux Serial Programming HOWTO, qui explique comment programmer so= us Linux la communication avec des p=E9riph=E9riques ou des ordinateurs via= le port s=E9rie. Diff=E9rentes techniques sont abord=E9es : Entr=E9es= /Sorties canoniques (envoi ou r=E9ception l= igne par ligne), asynchrones, ou l'attente de donn=E9es depuis de multiples= sources. <P> Ce document ne d=E9crit pas comment configurer les ports s=E9ries, puisque = c'est d=E9crit par Greg Hankins dans le Serial-HOWTO. <P> Je tiens =E0 insister sur le fait que je ne suis pas un expert dans ce doma= ine, mais j'ai eu =E0 r=E9aliser un projet utilisant la communication par l= e port s=E9rie. Les exemples de code source pr=E9sent=E9s dans ce document = sont d=E9riv=E9s du programme <tt/miniterm/,=20= disponible dans le <EM/Linux programmer's guide/ (<TT>ftp://sunsite.unc.edu= /pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT> et les miroirs, p= ar exemple <TT>ftp://ftp.lip6.fr/pub/linux/docs/LDP/programmers-guide/lpg-0= .4.tar.gz</TT>) dans le r=E9per= toire contenant les exemples. <P> Depuis la derni=E8re version de ce document, en juin 1997, j'ai d=FB instal= ler Windows NT pour satisfaire les besoins des client, et donc je n'ai pas = pu investiguer plus en avant sujet. Si quelqu'un a des commentaires =E0 me = faire, je me ferai un plaisir de le= s inclure dans ce document (voyez la section sur les commentaires). Si vous= d=E9sirez prendre en main l'=E9volution de ce document, et l'am=E9liorer, = envoyez moi un courrier =E9lectronique. <P> Tous les exemples ont =E9t=E9 test=E9s avec un i386, utilisant un noyau Lin= ux de version 2.0.29. <SECT1>Copyright <P> Le Linux Serial-Programming-HOWTO est copyright (c) 1997 Peter Baumann. Les= HOWTO de Linux peuvent =EAtre reproduits et distribu=E9s int=E9gralement o= u seulement par partie, sur quelconque support physique ou =E9lectronique, = aussi longtemps que ce message de cop= yright sera conserv=E9 dans toutes les copies. Une redistribution commercia= le est autoris=E9e, et encourag=E9e; cependant, l'auteur <EM/appr=E9cierait= / d'=EAtre pr=E9venu en cas de distribution de ce type. <P> Toutes les traductions ou travaux d=E9riv=E9s incorporant un document HOWTO= Linux doit =EAtre plac=E9 sous ce copyright. C'est-=E0-dire que vous ne po= uvez pas produire de travaux d=E9riv=E9s =E0 partir d'un HOWTO et imposer d= es restrictions additionnelles sur sa distrib= ution. Des exceptions =E0 cette r=E8gle peuvent =EAtre accord=E9es sous cer= taines conditions  ; contactez le coordinateur des HOWTO Linux =E0 l'a= dresse donn=E9e ci-dessous. <P> En r=E9sum=E9, nous d=E9sirons promouvoir la distribution de cette informat= ion par tous les moyens possibles. N=E9anmoins, nous d=E9sirons conserver l= e copyright sur les documents HOWTO, et nous <EM/aimerions/ =EAtre inform= =E9s de tout projet de redistribution des HOW= TO. <P> Pour toute question, veuillez contacter Greg Hankins, le coordinateur des H= OWTO Linux, =E0 <TT><HTMLURL URL=3D"mailto:gregh@sunsite.unc.edu" NAME=3D"g= regh@sunsite.unc.edu"></TT> par mail. <SECT1>Nouvelles versions de ce document. <P> Les nouvelles version du Serial-Programming-HOWTO seront disponibles =E0<NE= WLINE> <TT><HTMLURL URL=3D"ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Prog= ramming-HOWTO" NAME=3D"ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-P= rogramming-HOWTO"></TT> et les sites miroir, comme par exemple <TT><HTMLURL= URL=3D"ftp://ftp.lip6.fr/pub/linux= /docs/HOWTO/Serial-Programming-HOWTO" NAME=3D"ftp://ftp.lip6.fr/pub/linux/d= ocs/HOWTO/Serial-Programming-HOWTO"></TT>. Il existe sous d'autres formats,= comme PostScript ou DVI dans le sous r=E9pertoire <TT/other-formats/. Le S= erial-Programming-HOWTO est =E9gale= ment disponible sur <TT><HTMLURL URL=3D"http://sunsite.unc.edu/LDP/HOWTO/Se= rial-Programming-HOWTO.html" NAME=3D"http://sunsite.unc.edu/LDP/HOWTO/Seria= l-Programming-HOWTO.html"></TT>, et sera post=E9 dans <TT><HTMLURL URL=3D"n= ews:comp.os.linux.answers" NAME=3D"comp= .os.linux.answers"></TT> tous les mois (NdT : la version fran=E7aise d= e ce document est =E9galement post=E9e dans <TT><HTMLURL URL=3D"news:fr.com= p.os.linux.annonce" NAME=3D"fr.comp.os.linux.annonce"></TT> tous les mois). <SECT1>Commentaires <P> Envoyez moi, s'il vous pla=EEt toute correction, question, commentaire, sug= gestion ou compl=E9ment. Je d=E9sire am=E9liorer cet HOWTO ! Dites moi= exactement ce que vous ne comprenez pas, ou ce qui pourrait =EAtre plus cl= air. Vous pouvez me contacter =E0 <TT><HT= MLURL URL=3D"mailto:Peter.Baumann@dlr.de" NAME=3D"Peter.Baumann@dlr.de"></T= T> par courrier =E9lectronique. Veuillez inclure le num=E9ro de version de = ce document pour tout courrier. Ce document est la version 0.3. <SECT>D=E9marrage <P> <SECT1>D=E9buggage <P> Le meilleur moyen de d=E9bugguer votre code est d'installer une autre machi= ne sous Linux et de connecter les deux ordinateurs par un c=E2ble null-mode= m. Utilisez <TT/miniterm/ (disponible dans le Linux programmers guide -- <T= T>ftp://sunsite.unc.edu/pub/Linux= /docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT> -- dans le r=E9pertoire des= exemples) pour transmettre des caract=E8res =E0 votre machine Linux. <TT/M= initerm/ est tr=E8s simple =E0 compiler et transmettra toute entr=E9e clavi= er directement sur le port s=E9rie. Vous n'= avez qu'=E0 adapter la commande <TT>#define MODEMDEVICE "/dev/ttyS0"</TT> = =E0 vos besoins. Mettez <TT>ttyS0</TT> pour COM1, <TT>ttyS1</TT> for COM2, = etc... Il est essentiel, pour les tests, que <EM/tous/ les caract=E8res soi= ent transmis bruts (sans traitements= ) au travers de la ligne s=E9rie. Pour tester votre connexion, d=E9marrez <= TT/miniterm/ sur les deux ordinateurs et taper au clavier. Les caract=E8res= =E9crit sur un ordinateur devraient appara=EEtre sur l'autre ordinateur, e= t vice versa. L'entr=E9e clavier sera =E9ga= lement recopi=E9e sur l'=E9cran de l'ordinateur local. <P> Pour fabriquer un c=E2ble null-modem, pour devez croiser les lignes TxD (<E= M/transmit/) et RxD (<EM/receive/). Pour une description du c=E2ble, r=E9f= =E9rez vous =E0 la section 7 du Serial-HOWTO. <P> Il est =E9galement possible de faire cet essai avec uniquement un seul ordi= nateur, si vous disposez de deux ports s=E9rie. Vous pouvez lancez deux <TT= /miniterm/s sur deux consoles virtuelles. Si vous lib=E9rez un port s=E9rie= en d=E9connectant la souris, n'oubliez= pas de rediriger <TT>/dev/mouse</TT> si ce fichier existe. Si vous utilise= z une carte s=E9rie =E0 ports multiples, soyez s=FBr de la configurer corre= ctement. La mienne n'=E9tait pas correctement configur=E9e, et tout fonctio= nnait correctement lorsque je testais s= ur mon ordinateur. Lorsque je l'ai connect=E9 =E0 un autre, le port a comme= nc=E9 =E0 perdre des caract=E8res. L'ex=E9cution de deux programmes sur un = seul ordinateur n'est pas totalement asynchrone. <SECT1>Configuration des ports <P> Les p=E9riph=E9riques <TT>/dev/ttyS*</TT> sont destin=E9s =E0 connecter les= terminaux =E0 votre machine Linux, et sont configur=E9s pour cet usage apr= =E8s le d=E9marrage. Vous devez vous en souvenir lorsque vous programmez la= communication avec un p=E9riph=E9rique autre. Pa= r exemple, les ports sont configur=E9s pour afficher les caract=E8res envoy= =E9s vers lui-m=EAme, ce qui normalement doit =EAtre chang=E9 pour la trans= mission de donn=E9es. <P> Tous les param=E8tres peuvent =EAtre facilement configur=E9 depuis un progr= amme. La configuration est stock=E9e dans une structure de type <TT>struct = termios</TT>, qui est d=E9finie dans <TT><asm/termbits.h></TT> : <TSCREEN><VERB> #define NCCS 19 struct termios { tcflag_t c_iflag; /* Modes d'entr=E9e */ tcflag_t c_oflag; /* Modes de sortie */ tcflag_t c_cflag; /* Modes de contr=F4le */ tcflag_t c_lflag; /* Modes locaux */ cc_t c_line; /* Discipline de ligne */ cc_t c_cc[NCCS]; /* Caract=E8res de contr=F4le */ }; </VERB></TSCREEN> Ce fichier inclus =E9galement la d=E9finition des constantes. Tous les mode= s d'entr=E9e dans <TT>c_iflag</TT> prennent en charge le traitement de l'en= tr=E9e, ce qui signifie que les caract=E8res envoy=E9s depuis le p=E9riph= =E9rique peuvent =EAtre trait=E9s avant d'=EAtre lu p= ar <TT>read</TT>. De la m=EAme fa=E7on, <TT>c_oflags</TT> se chargent du tr= aitement en sortie. <TT>c_cflag</TT> contient les param=E8tres du port, com= me la vitesse, le nombre de bits par caract=E8re, les bits d'arr=EAt, etc..= . Les modes locaux, stock=E9s dans <TT>c_= lflag</TT> d=E9terminent si les caract=E8res sont imprim=E9s, si des signau= x sont envoy=E9s =E0 votre programme, etc... Enfin, le tableau <TT>c_cc</TT= > d=E9finit les caract=E8res de contr=F4le pour la fin de fichier, le carac= t=E8re stop, etc... Les valeurs par d=E9faut pour= les caract=E8res de contr=F4le sont d=E9finies dans <TT><asm/termios.h&= gt;</TT>. Les modes possibles sont d=E9crits dans la page de manuel de <TT>= termios(3)</TT>. La structure <TT>termios</TT> contient un champ <TT>c_line= </TT> (discipline de ligne), qui n'es= t pas utilis=E9 sur les syst=E8mes conformes =E0 POSIX. <SECT1>Fa=E7ons de lire sur les p=E9riph=E9riques s=E9rie <P> Voici trois fa=E7ons de lire sur les p=E9riph=E9riques s=E9rie. Le moyen ap= propri=E9 doit =EAtre choisi pour chaque application. Lorsque cela est poss= ible, ne lisez pas les cha=EEnes caract=E8re par caract=E8re. Lorsque j'uti= lisais ce moyen, je perdais des caract=E8res, alo= rs qu'un <TT>read</TT> sur la cha=EEne compl=E8te ne donnait aucune erreur. <SECT2>Entr=E9e canonique <P> C'est le mode de fonctionnement normal pour les terminaux, mais peut =E9gal= ement =EAtre utilis=E9 pour communiquer avec d'autres p=E9riph=E9riques. To= utes les entr=E9es sont trait=E9es lignes par lignes, ce qui signifie qu'un= <TT>read</TT> ne renverra qu'une ligne com= pl=E8te. Une ligne est termin=E9e par d=E9faut avec un caract=E8re <TT>NL</= TT> (ACII <TT>LF</TT>), une fin de fichier, ou un caract=E8re de fin de lig= ne. Un <TT>CR</TT> (le caract=E8re de fin de ligne par d=E9faut de DOS et W= indows) ne terminera pas une ligne, avec le= s param=E8tres par d=E9faut. <P> L'entr=E9e canonique peut =E9galement prendre en charge le caract=E8re eras= e, d'effacement de mot, et de r=E9affichage, la traduction de <TT>CR</TT> v= ers <TT>NL</TT>, etc... <SECT2>Entr=E9e non canonique <P> L'entr=E9e non canonique va prendre en charge un nombre fix=E9 de caract=E8= re par lecture, et autorise l'utilisation d'un compteur de temps pour les c= aract=E8res. Ce mode doit =EAtre utilis=E9 si votre application lira toujou= rs un nombre fix=E9 de caract=E8res, ou si le= p=E9riph=E9rique connect=E9 envoit les caract=E8res par paquet. <P> <SECT2>Entr=E9e asynchrone <P> Les deux modes ci-dessus peut =EAtre utilis=E9 en mode synchrone ou asynchr= one. Le mode synchrone est le mode par d=E9faut, pour lequel un appel =E0 <= TT>read</TT> sera bloquant, jusqu'=E0 ce que la lecture soit satisfaite. En= mode asynchrone, un appel =E0 <TT>read</= TT> retournera imm=E9diatement et lancera un signal au programme appelant e= n fin de transfert. Ce signal peut =EAtre re=E7u par un gestionnaire de sig= nal. <SECT2>Attente d'entr=E9e depuis de multiples sources <P> Cela ne constitue pas un mode d'entr=E9e diff=E9rent, mais peut s'av=E9rer = =EAtre utile, si vous prenez en charge des p=E9riph=E9riques multiples. Dan= s mon application, je traitais l'entr=E9e depuis une socket TCP/IP et depui= s une connexion s=E9rie sur un autre ordinate= ur quasiment en m=EAme temps. L'exemple de programme donn=E9 plus loin atte= ndra des caract=E8res en entr=E9e depuis deux sources. Si des donn=E9es sur= l'une des sources deviennent disponibles, elles seront trait=E9es, et le p= rogramme attendra de nouvelles donn=E9es. <P> L'approche pr=E9sent=E9e plus loin semble plut=F4t complexe, mais il est im= portant que vous vous rappeliez que Linux est un syst=E8me multi-t=E2che. L= 'appel syst=E8me <TT>select</TT> ne charge pas le processeur lorsqu'il atte= nd des donn=E9es, alors que le fait de fair= e une boucle jusqu'=E0 ce que des caract=E8res deviennent disponibles ralen= tirait les autres processus. <SECT>Exemples de programmes <P> Tous les exemples ont =E9t=E9 extraits de <TT>miniterm.c</TT>. Le tampon d'= entr=E9e est limit=E9 =E0 255 caract=E8res, tout comme l'est la longueur ma= ximale d'une ligne en mode canonique (<TT><linux/limits.h></TT> ou <T= T><posix1_lim.h></TT>). <P> R=E9f=E9rez-vous aux commentaires dans le code source pour l'explication de= s diff=E9rents modes d'entr=E9e. J'esp=E8re que le code est compr=E9hensibl= e. L'exemple sur l'entr=E9e canonique est la plus comment=E9e, les autres e= xemples sont comment=E9s uniquement lorsqu'ils=20= diff=E8rent, afin de signaler les diff=E9rences. <P> Les descriptions ne sont pas compl=E8tes, mais je vous encourage =E0 modifi= er les exemples pour obtenir la solution la plus int=E9ressante pour votre = application. <P> N'oubliez pas de donner les droits corrects aux ports s=E9rie (par exemple,= <TT>chmod a+rw /dev/ttyS1</TT>) ! <SECT1>Traitement canonique <P> <TSCREEN><VERB> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> /* les valeurs pour la vitesse, baudrate, sont d=E9finies dans <asm/termbit= s.h>, qui est inclus dans <termios.h> */ #define BAUDRATE B38400 =20 /* changez cette d=E9finition pour utiliser le port correct */ #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* code source conforme =E0 POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=3DFALSE;=20 main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; /*=20 On ouvre le p=E9riph=E9rique du modem en lecture/=E9criture, et pas comme terminal de contr=F4le, puisque nous ne voulons pas =EAtre termin=E9 si l= 'on re=E7oit un caract=E8re CTRL-C. */ fd =3D open(MODEMDEVICE, O_RDWR | O_NOCTTY );=20 if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&ero;oldtio); /* sauvegarde de la configuration courante */ bzero(&ero;newtio, sizeof(newtio)); /* on initialise la structure =E0 z=E9= ro */ /*=20 BAUDRATE: Affecte la vitesse. vous pouvez =E9galement utiliser cfsetispeed et cfsetospeed. CRTSCTS : contr=F4le de flux mat=E9riel (uniquement utilis=E9 si le c=E2b= le a les lignes n=E9cessaires. Voir la section 7 du Serial-HOWTO). CS8 : 8n1 (8 bits,sans parit=E9, 1 bit d'arr=EAt) CLOCAL : connexion locale, pas de contr=F4le par le modem CREAD : permet la r=E9ception des caract=E8res */ newtio.c_cflag =3D BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; =20 /* IGNPAR : ignore les octets ayant une erreur de parit=E9. ICRNL : transforme CR en NL (sinon un CR de l'autre c=F4t=E9 de la ligne ne terminera pas l'entr=E9e). sinon, utiliser l'entr=E9e sans traitement (device en mode raw). */ newtio.c_iflag =3D IGNPAR | ICRNL; =20 /* Sortie sans traitement (raw). */ newtio.c_oflag =3D 0; =20 /* ICANON : active l'entr=E9e en mode canonique d=E9sactive toute fonctionnalit=E9 d'echo, et n'envoit pas de signal au programme appelant. */ newtio.c_lflag =3D ICANON; =20 /*=20 initialise les caract=E8res de contr=F4le. les valeurs par d=E9faut peuvent =EAtre trouv=E9es dans /usr/include/termios.h, et sont donn=E9es dans les commentaires. Elles sont inutiles ici. */ newtio.c_cc[VINTR] =3D 0; /* Ctrl-c */=20 newtio.c_cc[VQUIT] =3D 0; /* Ctrl-\ */ newtio.c_cc[VERASE] =3D 0; /* del */ newtio.c_cc[VKILL] =3D 0; /* @ */ newtio.c_cc[VEOF] =3D 4; /* Ctrl-d */ newtio.c_cc[VTIME] =3D 0; /* compteur inter-caract=E8re non utilis= =E9 */ newtio.c_cc[VMIN] =3D 1; /* read bloquant jusqu'=E0 l'arriv=E9e d'= 1 caract=E8re */ newtio.c_cc[VSWTC] =3D 0; /* '\0' */ newtio.c_cc[VSTART] =3D 0; /* Ctrl-q */=20 newtio.c_cc[VSTOP] =3D 0; /* Ctrl-s */ newtio.c_cc[VSUSP] =3D 0; /* Ctrl-z */ newtio.c_cc[VEOL] =3D 0; /* '\0' */ newtio.c_cc[VREPRINT] =3D 0; /* Ctrl-r */ newtio.c_cc[VDISCARD] =3D 0; /* Ctrl-u */ newtio.c_cc[VWERASE] =3D 0; /* Ctrl-w */ newtio.c_cc[VLNEXT] =3D 0; /* Ctrl-v */ newtio.c_cc[VEOL2] =3D 0; /* '\0' */ /*=20 =E0 pr=E9sent, on vide la ligne du modem, et on active la configuration pour le port */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&ero;newtio); /* la configuration du terminal est faite, =E0 pr=E9sent on traite les entr=E9es Dans cet exemple, la r=E9ception d'un 'z' en d=E9but de ligne mettra fin au programme. */ while (STOP=3D=3DFALSE) { /* boucle jusqu'=E0 condition de terminaison= */ /* read bloque l'ex=E9cution du programme jusqu'=E0 ce qu'un caract=E8re de fin de ligne soit lu, m=EAme si plus de 255 caract=E8res sont saisis. Si le nombre de caract=E8res lus est inf=E9rieur au nombre de caract=E8= res disponibles, des read suivant retourneront les caract=E8res restants. res sera positionn=E9 au nombre de caract=E8res effectivement lus */ res =3D read(fd,buf,255);=20 buf[res]=3D0; /* on termine la ligne, pour pouvoir l'afficher */ printf(":%s:%d\n", buf, res); if (buf[0]=3D=3D'z') STOP=3DTRUE; } /* restaure les anciens param=E8tres du port */ tcsetattr(fd,TCSANOW,&ero;oldtio); } </VERB></TSCREEN> <SECT1>Entr=E9e non canonique <P> Dans le mode non canonique, les caract=E8res lus ne sont pas assembl=E9s li= gne par ligne, et ils ne subissent pas de traitement (erase, kill, delete, = etc...). Deux param=E8tres contr=F4lent ce mode : <TT>c_cc[VTIME]</TT>= positionne le <EM/timer/ de caract=E8r= es, et <TT>c_cc[VMIN]</TT> indique le nombre minimum de caract=E8res =E0 re= cevoir avant qu'une lecture soit satisfaite. <P> Si MIN > 0 et TIME =3D 0, MIN indique le nombre de caract=E8res =E0 rece= voir avant que la lecture soit satisfaite. TIME est =E9gal =E0 z=E9ro, et l= e <EM/timer/ n'est pas utilis=E9. <P> Si MIN =3D 0 et TIME > 0, TIME est utilis=E9 comme une valeur de <EM/ti= meout/. Une lecture est satisfaite lorsqu'un caract=E8re est re=E7u, ou que= la dur=E9e TIME est d=E9pass=E9e (t =3D TIME * 0.1s). Si TIME est d=E9pass= =E9, aucun caract=E8re n'est retourn=E9. <P> Si MIN > 0 et TIME > 0, TIME est employ=E9 comme <EM/timer/ entre cha= que caract=E8re. La lecture sera satisfaite si MIN caract=E8res sont re=E7u= s, ou que le <EM/timer/ entre deux caract=E8res d=E9passe TIME. Le <EM/time= r/ est r=E9initialis=E9 =E0 chaque fois qu'un c= aract=E8re est re=E7u, et n'est activ=E9 qu'apr=E8s la r=E9ception du premi= er caract=E8re. <P> Si MIN =3D 0 et TIME =3D 0, le retour est imm=E9diat. Le nombre de caract= =E8res disponibles, ou bien le nombre de caract=E8res demand=E9 est retourn= =E9. Selon Antonino (voir le paragraphe sur les participations), vous pouve= z utiliser un <TT>fcntl(fd, F_SETFL, FNDELAY)= ;</TT> avant la lecture pour obtenir le m=EAme r=E9sultat. <P> Vous pouvez tester tous les modes d=E9crit ci-dessus en modifiant <TT>newti= o.c_cc[VTIME]</TT> et <TT>newtio.c_cc[VMIN]</TT>. <TSCREEN><VERB> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> #define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* code source conforme =E0 POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=3DFALSE;=20 main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; fd =3D open(MODEMDEVICE, O_RDWR | O_NOCTTY );=20 if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&ero;oldtio); /* sauvegarde de la configuration courante */ bzero(&ero;newtio, sizeof(newtio)); newtio.c_cflag =3D BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag =3D IGNPAR; newtio.c_oflag =3D 0; /* positionne le mode de lecture (non canonique, sans echo, ...) */ newtio.c_lflag =3D 0; =20 newtio.c_cc[VTIME] =3D 0; /* timer inter-caract=E8res non utilis=E9 */ newtio.c_cc[VMIN] =3D 5; /* read bloquant jusqu'=E0 ce que 5 */ /* caract=E8res soient lus */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&ero;newtio); while (STOP=3D=3DFALSE) { /* boucle de lecture */ res =3D read(fd,buf,255); /* retourne apr=E8s lecture 5 caract=E8res */ buf[res]=3D0; /* pour pouvoir les imprimer... */ printf(":%s:%d\n", buf, res); if (buf[0]=3D=3D'z') STOP=3DTRUE; } tcsetattr(fd,TCSANOW,&ero;oldtio); } </VERB></TSCREEN> <SECT1>Lecture asynchrone <P> <TSCREEN><VERB> #include <termios.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/signal.h> #include <sys/types.h> #define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* code source conforme =E0 POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=3DFALSE;=20 void signal_handler_IO (int status); /* le gestionnaire de signal */ int wait_flag=3DTRUE; /* TRUE tant que re=E7u aucun signal */ main() { int fd,c, res; struct termios oldtio,newtio; struct sigaction saio; /* d=E9finition de l'action du signal */ char buf[255]; /* ouvre le port en mon non-bloquant (read retourne imm=E9diatement) */ fd =3D open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd <0) {perror(MODEMDEVICE); exit(-1); } /* installe le gestionnaire de signal avant de passer le port en mode asynchrone */ saio.sa_handler =3D signal_handler_IO; saio.sa_mask =3D 0; saio.sa_flags =3D 0; saio.sa_restorer =3D NULL; sigaction(SIGIO,&ero;saio,NULL); =20 /* permet au processus de recevoir un SIGIO */ fcntl(fd, F_SETOWN, getpid()); /* rend le descripteur de fichier asynchrone (la page de manuel indique que seuls O_APPEND et O_NONBLOCK fonctionnent avec F_SETFL...) */ fcntl(fd, F_SETFL, FASYNC); tcgetattr(fd,&ero;oldtio); /* sauvegarde de la configuration courante */ /* positionne les nouvelles valeurs pour lecture canonique */ newtio.c_cflag =3D BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag =3D IGNPAR | ICRNL; newtio.c_oflag =3D 0; newtio.c_lflag =3D ICANON; newtio.c_cc[VMIN]=3D1; newtio.c_cc[VTIME]=3D0; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&ero;newtio); =20 /* on boucle en attente de lecture. g=E9n=E9ralement, on r=E9alise des traitements =E0 l'int=E9rieur de la boucle */ while (STOP=3D=3DFALSE) { printf(".\n");usleep(100000); /* wait_flag =3D FALSE apr=E8s r=E9ception de SIGIO. Des donn=E9es sont disponibles et peuvent =EAtre lues */ if (wait_flag=3D=3DFALSE) {=20 res =3D read(fd,buf,255); buf[res]=3D0; printf(":%s:%d\n", buf, res); if (res=3D=3D1) STOP=3DTRUE; /* on arr=EAte la boucle si on lit une ligne seule */ wait_flag =3D TRUE; /* on attend de nouvelles donn=E9es */ } } /* restaure les anciens param=E8tres du port */ tcsetattr(fd,TCSANOW,&ero;oldtio); } /*************************************************************************** * gestionnaire de signal. Positionne wait_flag =E0 FALSE, pour indiquer =E0= * * la boucle ci-dessus que des caract=E8res ont =E9t=E9 re=E7us. = * ***************************************************************************/ void signal_handler_IO (int status) { printf("r=E9ception du signal SIGIO.\n); wait_flag =3D FALSE; } </VERB></TSCREEN> <SECT1>Multiplexage en lecture <P> Cette section est r=E9duite au minimum, et n'est l=E0 que pour vous guider.= Le code source d'exemple pr=E9sent=E9 est donc r=E9duit au strict minimum.= Il ne fonctionnera pas seulement avec des ports s=E9rie, mais avec n'impor= te quel ensemble de descripteurs de fichi= ers. <P> L'appel syst=E8me select et les macros qui lui sont attach=E9es utilisent u= n <TT>fd_set</TT>. C'est un tableau de bits, qui dispose d'un bit pour chaq= ue descripteur de fichier valide. <TT/select/ accepte un <TT/fd_set/ ayant = les bits positionn=E9s pour les des= cripteurs de fichiers qui conviennent, et retourne un <TT/fd_set/, dans leq= uel les bits des descripteurs de fichier o=F9 une lecture, une =E9criture o= u une exception sont positionn=E9s. Toutes les manipulations de <TT>fd_set<= /TT> sont faites avec les macros fo= urnies. Reportez vous =E9galement =E0 la page de manuel de <TT>select(2)</T= T>. <TSCREEN><VERB> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> main() { int fd1, fd2; /* entr=E9es 1 et 2 */ fd_set readfs; /* ensemble de descripteurs */ int maxfd; /* nombre max des descripteurs utilis=E9s */ int loop=3D1; /* boucle tant que TRUE */ /* open_input_source ouvre un p=E9riph=E9rique, configure le port correctement, et retourne un descripteur de fichier. */ fd1 =3D open_input_source("/dev/ttyS1"); /* COM2 */ if (fd1<0) exit(0); fd2 =3D open_input_source("/dev/ttyS2"); /* COM3 */ if (fd2<0) exit(0); maxfd =3D MAX (fd1, fd2)+1; /* num=E9ro maximum du bit =E0 tester */ /* boucle d'entr=E9e */ while (loop) { FD_SET(fd1, &ero;readfs); /* test pour la source 1 */ FD_SET(fd2, &ero;readfs); /* test pour la source 2 */ /* on bloque jusqu'=E0 ce que des caract=E8res soient disponibles en lecture */ select(maxfd, &ero;readfs, NULL, NULL, NULL); if (FD_ISSET(fd1)) /* caract=E8res sur 1 */ handle_input_from_source1(); if (FD_ISSET(fd2)) /* caract=E8res sur 2 */ handle_input_from_source2(); } } =20 </VERB></TSCREEN> <P> L'exemple ci-dessus bloque ind=E9finiment, jusqu'=E0 ce que des caract=E8re= s venant d'une des sources soient disponibles. Si vous avez besoin d'un <EM= /timeout/, remplacez juste l'appel =E0 select par : <TSCREEN><VERB> int res; struct timeval Timeout; /* fixe la valeur du timeout */ Timeout.tv_usec =3D 0; /* millisecondes */ Timeout.tv_sec =3D 1; /* secondes */ res =3D select(maxfd, &ero;readfs, NULL, NULL, &ero;Timeout); if (res=3D=3D0) /* nombre de descripteurs de fichiers avec caract=E8res disponibles =3D 0, il y a eu timeout */ </VERB></TSCREEN> Cet exemple verra l'expiration du delai de <EM/timeout/ apr=E8s une seconde= . S'il y a <EM/timeout/, select retournera 0, mais fa=EEtes attention, <TT>= Timeout</TT> est d=E9cr=E9ment=E9 du temps r=E9ellement attendu par <TT/sel= ect/. Si la valeur de <EM/timeout/ est 0,= select retournera imm=E9diatement. <SECT>Autres sources d'information <P> <ITEMIZE> <ITEM>Le Linux Serial-HOWTO d=E9crit comment mettre en place les = ports s=E9rie et contient des informations sur le mat=E9riel. <ITEM>Le <URL URL=3D"http://www.easysw.com/~mike/serial" NAME=3D"= Serial Programming Guide for POSIX Compliant Operating Systems">, par Micha= el Sweet. Ce lien est p=E9rim=E9 et je n'arrive pas =E0 trouver la nouvelle= adresse du document. Quelqu'un sait-il= o=F9 je peux le retrouver ? C'=E9tait un tr=E8s bon document ! <ITEM>La page de manuel de <TT>termios(3)</TT> d=E9crit toutes le= s constantes utilis=E9es pour la structure <TT>termios</TT>. </ITEMIZE> <SECT>Contributions <P> Comme je l'ai dit dans l'introduction, je ne suis pas un expert dans le dom= aine, mais j'ai rencontr=E9 des probl=E8mes, et j'ai trouv=E9 les solutions= avec l'aide d'autres personnes. Je tiens =E0 remercier pour leur aide M. S= trudthoff du European Transonic WindT= unnel, Cologne, Michael Carter (<TT>mcarter@rocke.electro.swri.edu</TT>) et= Peter Waltenberg (<TT>p.waltenberg@karaka.chch.cri.nz</TT>). <P> Antonino Ianella (<TT>antonino@usa.net</TT> a =E9crit le Serial-Port-Progra= mming Mini HOWTO, au m=EAme moment o=F9 je pr=E9parais ce document. Greg Ha= nkins m'a demand=E9 d'inclure le Mini-HOWTO d'Antonino dans ce document. <P> La structure de ce document et le formattage SGML ont =E9t=E9 d=E9riv=E9s d= u Serial-HOWTO de Greg Hankins. Merci =E9galement pour diverses corrections= faites par : Dave Pfaltzgraff (<TT>Dave_Pfaltzgraff@patapsco.com</TT>= ), Sean Lincolne (<TT>slincol@tpgi.com.= au</TT>), Michael Wiedmann (<TT>mw@miwie.in-berlin.de</TT>), et Adrey Bonar= (<TT>andy@tipas.lt</TT>). </ARTICLE>