Le Linux Serial Programming HOWTO par Peter H. Baumann, Peter.Baumann@dlr.de Adaptation francaise Etienne BERNARD eb@via.ecp.fr v0.3, 14 Juin 1997 Ce document decrit comment programmer sous Linux la communication avec des peripheriques sur port serie. 11.. IInnttrroodduuccttiioonn Voici le Linux Serial Programming HOWTO, qui explique comment programmer sous Linux la communication avec des peripheriques ou des ordinateurs via le port serie. Differentes techniques sont abordees : Entrees/Sorties canoniques (envoi ou reception ligne par ligne), asynchrones, ou l'attente de donnees depuis de multiples sources. Ce document ne decrit pas comment configurer les ports series, puisque c'est decrit par Greg Hankins dans le Serial-HOWTO. Je tiens a insister sur le fait que je ne suis pas un expert dans ce domaine, mais j'ai eu a realiser un projet utilisant la communication par le port serie. Les exemples de code source presentes dans ce document sont derives du programme miniterm, disponible dans le _L_i_n_u_x _p_r_o_g_r_a_m_m_e_r_'_s _g_u_i_d_e (ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers- guide/lpg-0.4.tar.gz et les miroirs, par exemple ftp://ftp.lip6.fr/pub/linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz) dans le repertoire contenant les exemples. Je me ferai un plaisir d'incorporer d'eventuels commentaires (voyez la section sur les commentaires). Tous les exemples ont ete testes avec un i386, utilisant un noyau Linux de version 2.0.29. 11..11.. CCooppyyrriigghhtt Le Linux Serial-Programming-HOWTO est copyright (c) 1997 Peter Baumann. Les HOWTO de Linux peuvent etre reproduits et distribues integralement ou seulement par partie, sur quelconque support physique ou electronique, aussi longtemps que ce message de copyright sera conserve dans toutes les copies. Une redistribution commerciale est autorisee, et encouragee; cependant, l'auteur _a_p_p_r_e_c_i_e_r_a_i_t d'etre prevenu en cas de distribution de ce type. Toutes les traductions ou travaux derives incorporant un document HOWTO Linux doit etre place sous ce copyright. C'est-a-dire que vous ne pouvez pas produire de travaux derives a partir d'un HOWTO et imposer des restrictions additionnelles sur sa distribution. Des exceptions a cette regle peuvent etre accordees sous certaines conditions ; contactez le coordinateur des HOWTO Linux a l'adresse donnee ci-dessous. En resume, nous desirons promouvoir la distribution de cette information par tous les moyens possibles. Neanmoins, nous desirons conserver le copyright sur les documents HOWTO, et nous _a_i_m_e_r_i_o_n_s etre informes de tout projet de redistribution des HOWTO. Pour toute question, veuillez contacter Greg Hankins, le coordinateur des HOWTO Linux, a gregh@sunsite.unc.edu par mail. 11..22.. NNoouuvveelllleess vveerrssiioonnss ddee ccee ddooccuummeenntt.. Les nouvelles version du Serial-Programming-HOWTO seront disponibles a ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO et les sites miroir, comme par exemple ftp://ftp.lip6.fr/pub/linux/docs/HOWTO/Serial-Programming-HOWTO. Il existe sous d'autres formats, comme PostScript ou DVI dans le sous repertoire other-formats. Le Serial-Programming-HOWTO est egalement disponible sur http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming- HOWTO.html, et sera poste dans comp.os.linux.answers tous les mois (NdT : la version francaise de ce document est egalement postee dans fr.comp.os.linux.annonce tous les mois). 11..33.. CCoommmmeennttaaiirreess Envoyez moi, s'il vous plait tout correction, question, commentaire, suggestion ou complement. Je desire ameliorer cet HOWTO ! Dites moi exactement ce que vous ne comprenez pas, ou ce qui pourrait etre plus clair. Vous pouvez me contacter a Peter.Baumann@dlr.de par courrier electronique. Veuillez inclure le numero de version de ce document pour tout courrier. Ce document est la version 0.3. 22.. DDeemmaarrrraaggee 22..11.. DDeebbuuggggaaggee Le meilleur moyen de debugguer votre code est d'installer une autre machine sous Linux et de connecter les deux ordinateurs par un cable null-modem. Utilisez miniterm (disponible dans le Linux programmers guide -- ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers- guide/lpg-0.4.tar.gz -- dans le repertoire des exemples) pour transmettre des caracteres a votre machine Linux. Miniterm est tres simple a compiler et transmettra toute entree clavier directement sur le port serie. Vous n'avez qu'a adapter la commande #define MODEMDEVICE "/dev/ttyS0" a vos besoins. Mettez ttyS0 pour COM1, ttyS1 for COM2, etc... Il est essentiel, pour les tests, que _t_o_u_s les caracteres soient transmis bruts (sans traitements) au travers de la ligne serie. Pour tester votre connexion, demarrez miniterm sur les deux ordinateurs et taper au clavier. Les caracteres ecrit sur un ordinateur devraient apparaitre sur l'autre ordinateur, et vice versa. ! ! ! L'entree clavier sera egalement recopiee sur l'ecran de l'ordinateur local. Pour fabriquer un cable null-modem, pour devez croiser les lignes TxD (_t_r_a_n_s_m_i_t) et RxD (_r_e_c_e_i_v_e). Pour une description du cable, referez vous a la section 7 du Serial-HOWTO. Il est egalement possible de faire cet essai avec uniquement un seul ordinateur, si vous disposez de deux ports serie. Vous pouvez lancez deux miniterms sur deux consoles virtuelles. Si vous liberez un port serie en deconnectant la souris, n'oubliez pas de rediriger /dev/mouse si ce fichier existe. Si vous utilisez une carte serie a ports multiples, soyez sur de la configurer correctement. La mienne n'etait pas correctement configuree, et tout fonctionnait correctement lorsque je testais sur mon ordinateur. Lorsque je l'ai connecte a un autre, le port a commence a perdre des caracteres. L'execution de deux programmes sur un seul ordinateur n'est pas totalement asynchrone. 22..22.. CCoonnffiigguurraattiioonn ddeess ppoorrttss Les peripheriques /dev/ttyS* sont destines a connecter les terminaux a votre machine Linux, et sont configures pour cet usage apres le demarrage. Vous devez vous en souvenir lorsque vous programmez la communication avec un peripherique autre. Par exemple, les ports sont configures pour afficher les caracteres envoyes vers lui-meme, ce qui normalement doit etre change pour la transmission de donnees. Tous les parametres peuvent etre facilement configure depuis un programme. La configuration est stockee dans une structure de type struct termios, qui est definie dans <asm/termbits.h> : #define NCCS 19 struct termios { tcflag_t c_iflag; /* Modes d'entree */ tcflag_t c_oflag; /* Modes de sortie */ tcflag_t c_cflag; /* Modes de controle */ tcflag_t c_lflag; /* Modes locaux */ cc_t c_line; /* Discipline de ligne */ cc_t c_cc[NCCS]; /* Caracteres de controle */ }; Ce fichier inclus egalement la definition des constantes. Tous les modes d'entree dans c_iflag prennent en charge le traitement de l'entree, ce qui signifie que les caracteres envoyes depuis le peripherique peuvent etre traites avant d'etre lu par read. De la meme facon, c_oflags se chargent du traitement en sortie. c_cflag contient les parametres du port, comme la vitesse, le nombre de bits par caractere, les bits d'arret, etc... Les modes locaux, stockes dans c_lflag determinent si les caracteres sont imprimes, si des signaux sont envoyes a votre programme, etc... Enfin, le tableau c_cc definit les caracteres de controle pour la fin de fichier, le caractere stop, etc... Les valeurs par defaut pour les caracteres de controle sont definies dans <asm/termios.h>. Les modes possibles sont decrits dans la page de manuel de termios(3) La structure termios contient l'element c_line. Cet element n'est ni mentionne dans la page de manuel de termios de Linux, ni dans celle de Solaris 2.5. Quelqu'un peut-il m'eclairer sur cet attribut ? Est-ce qu'il doit reellement etre inclus dans la structure termios ? 22..33.. FFaaccoonnss ddee lliirree ssuurr lleess ppeerriipphheerriiqquueess sseerriiee Voici trois facons de lire sur les peripheriques serie. Le moyen approprie doit etre choisi pour chaque application. Lorsque cela est possible, ne lisez pas les chaines caractere par caractere. Lorsque j'utilisais ce moyen, je perdais des caracteres, alors qu'un read sur la chaine complete ne donnait aucune erreur. 22..33..11.. EEnnttrreeee ccaannoonniiqquuee C'est le mode de fonctionnement normal pour les terminaux, mais peut egalement etre utilise pour communiquer avec d'autres peripheriques. Toutes les entrees sont traitees lignes par lignes, ce qui signifie qu'un read ne renverra qu'une ligne complete. Une ligne est terminee par defaut avec un caractere NL (ACII LF), une fin de fichier, ou un caractere de fin de ligne. Un CR (le caractere de fin de ligne par defaut de DOS et Windows) ne terminera pas une ligne, avec les parametres par defaut. L'entree canonique peut egalement prendre en charge le caractere erase, d'effacement de mot, et de reaffichage, la traduction de CR vers NL, etc... 22..33..22.. EEnnttrreeee nnoonn ccaannoonniiqquuee L'entree non canonique va prendre en charge un nombre fixe de caractere par lecture, et autorise l'utilisation d'un compteur de temps pour les caracteres. Ce mode doit etre utilise si votre application lira toujours un nombre fixe de caracteres, ou si le peripherique connecte envoit les caracteres par paquet. 22..33..33.. EEnnttrreeee aassyynncchhrroonnee Les deux modes ci-dessus peut etre utilise en mode synchrone ou asynchrone. Le mode synchrone est le mode par defaut, pour lequel un appel a read sera bloquant, jusqu'a ce que la lecture soit satisfaite. En mode asynchrone, un appel a read retournera immediatement et lancera un signal au programme appelant en fin de transfert. Ce signal peut etre recu par un gestionnaire de signal. 22..33..44.. AAtttteennttee dd''eennttrreeee ddeeppuuiiss ddee mmuullttiipplleess ssoouurrcceess Cela ne constitue pas un mode d'entree different, mais peut s'averer etre utile, si vous prenez en charge des peripheriques multiples. Dans mon application, je traitais l'entree depuis une socket TCP/IP et depuis une connexion serie sur un autre ordinateur quasiment en meme temps. L'exemple de programme donne plus loin attendra des caracteres en entree depuis deux sources. Si des donnees sur l'une des sources deviennent disponibles, elles seront traitees, et le programme attendra de nouvelles donnees. L'approche presentee plus loin semble plutot complexe, mais il est important que vous vous rappeliez que Linux est un systeme multi- tache. L'appel systeme select ne charge pas le processeur lorsqu'il attend des donnees, alors que le fait de faire une boucle jusqu'a ce que des caracteres deviennent disponibles ralentirait les autres processus. 33.. EExxeemmpplleess ddee pprrooggrraammmmeess Tous les exemples ont ete extraits de miniterm.c. Le tampon d'entree est limite a 255 caracteres, tout comme l'est la longueur maximale d'une ligne en mode canonique (<linux/limits.h> ou <posix1_lim.h>). Referez-vous aux commentaires dans le code source pour l'explication des differents modes d'entree. J'espere que le code est comprehensible. L'exemple sur l'entree canonique est la plus commentee, les autres exemples sont commentes uniquement lorsqu'ils different, afin de signaler les differences. Les descriptions ne sont pas completes, mais je vous encourage a modifier les exemples pour obtenir la solution la plus interessante pour votre application. N'oubliez pas de donner les droits corrects aux ports serie (par exemple, chmod a+rw /dev/ttyS1) ! 33..11.. TTrraaiitteemmeenntt ccaannoonniiqquuee #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> /* les valeurs pour la vitesse, baudrate, sont definies dans <asm/termbits.h>, qui est inclus dans <termios.h> */ #define BAUDRATE B38400 /* changez cette definition pour utiliser le port correct */ #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* code source conforme a POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; /* On ouvre le peripherique du modem en lecture/ecriture, et pas comme terminal de controle, puisque nous ne voulons pas etre termine si l'on recoit un caractere CTRL-C. */ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&oldtio); /* sauvegarde de la configuration courante */ bzero(newtio, sizeof(newtio)); /* on initialise la structure a zero */ /* BAUDRATE: Affecte la vitesse. vous pouvez egalement utiliser cfsetispeed et cfsetospeed. CRTSCTS : controle de flux materiel (uniquement utilise si le cable a les lignes necessaires. Voir la section 7 du Serial-HOWTO). CS8 : 8n1 (8 bits,sans parite, 1 bit d'arret) CLOCAL : connexion locale, pas de controle par le modem CREAD : permet la reception des caracteres */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; /* IGNPAR : ignore les octets ayant une erreur de parite. ICRNL : transforme CR en NL (sinon un CR de l'autre cote de la ligne ne terminera pas l'entree). sinon, utiliser l'entree sans traitement (device en mode raw). */ newtio.c_iflag = IGNPAR | ICRNL; /* Sortie sans traitement (raw). */ newtio.c_oflag = 0; /* ICANON : active l'entree en mode canonique desactive toute fonctionnalite d'echo, et n'envoit pas de signal au programme appelant. */ newtio.c_lflag = ICANON; /* initialise les caracteres de controle. les valeurs par defaut peuvent etre trouvees dans /usr/include/termios.h, et sont donnees dans les commentaires. Elles sont inutiles ici. */ newtio.c_cc[VINTR] = 0; /* Ctrl-c */ newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */ newtio.c_cc[VERASE] = 0; /* del */ newtio.c_cc[VKILL] = 0; /* @ */ newtio.c_cc[VEOF] = 4; /* Ctrl-d */ newtio.c_cc[VTIME] = 0; /* compteur inter-caractere non utilise */ newtio.c_cc[VMIN] = 1; /* read bloquant jusqu'a l'arrivee d'1 caractere */ newtio.c_cc[VSWTC] = 0; /* '\0' */ newtio.c_cc[VSTART] = 0; /* Ctrl-q */ newtio.c_cc[VSTOP] = 0; /* Ctrl-s */ newtio.c_cc[VSUSP] = 0; /* Ctrl-z */ newtio.c_cc[VEOL] = 0; /* '\0' */ newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */ newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */ newtio.c_cc[VWERASE] = 0; /* Ctrl-w */ newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */ newtio.c_cc[VEOL2] = 0; /* '\0' */ /* a present, on vide la ligne du modem, et on active la configuration pour le port */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); /* la configuration du terminal est faite, a present on traite les entrees Dans cet exemple, la reception d'un 'z' en debut de ligne mettra fin au programme. */ while (STOP==FALSE) { /* boucle jusqu'a condition de terminaison */ /* read bloque l'execution du programme jusqu'a ce qu'un caractere de fin de ligne soit lu, meme si plus de 255 caracteres sont saisis. Si le nombre de caracteres lus est inferieur au nombre de caracteres disponibles, des read suivant retourneront les caracteres restants. res sera positionne au nombre de caracteres effectivement lus */ res = read(fd,buf,255); buf[res]=0; /* on termine la ligne, pour pouvoir l'afficher */ printf(":%s:%d\n", buf, res); if (buf[0]=='z') STOP=TRUE; } /* restaure les anciens parametres du port */ tcsetattr(fd,TCSANOW,&oldtio); } 33..22.. EEnnttrreeee nnoonn ccaannoonniiqquuee Dans le mode non canonique, les caracteres lus ne sont pas assembles ligne par ligne, et ils ne subissent pas de traitement (erase, kill, delete, etc...). Deux parametres controlent ce mode : c_cc[VTIME] positionne le _t_i_m_e_r de caracteres, et c_cc[VMIN] indique le nombre minimum de caracteres a recevoir avant qu'une lecture soit satisfaite. Si MIN > 0 et TIME = 0, MIN indique le nombre de caracteres a recevoir avant que la lecture soit satisfaite. TIME est egal a zero, et le _t_i_m_e_r n'est pas utilise. Si MIN = 0 et TIME > 0, TIME est utilise comme une valeur de _t_i_m_e_o_u_t. Une lecture est satisfaite lorsqu'un caractere est recu, ou que la duree TIME est depassee (t = TIME * 0.1s). Si TIME est depasse, aucun caractere n'est retourne. Si MIN > 0 et TIME > 0, TIME est employe comme _t_i_m_e_r entre chaque caractere. La lecture sera satisfaite si MIN caracteres sont recus, ou que le _t_i_m_e_r entre deux caracteres depasse TIME. Le _t_i_m_e_r est reinitialise a chaque fois qu'un caractere est recu, et n'est active qu'apres la reception du premier caractere. Si MIN = 0 et TIME = 0, le retour est immediat. Le nombre de caracteres disponibles, ou bien le nombre de caracteres demande est retourne. Selon Antonino (voir le paragraphe sur les participations), vous pouvez utiliser un fcntl(fd, F_SETFL, FNDELAY); avant la lecture pour obtenir le meme resultat. Vous pouvez tester tous les modes decrit ci-dessus en modifiant newtio.c_cc[VTIME] et newtio.c_cc[VMIN]. #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 a POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&oldtio); /* sauvegarde de la configuration courante */ bzero(newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; /* positionne le mode de lecture (non canonique, sans echo, ...) */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; /* timer inter-caracteres non utilise */ newtio.c_cc[VMIN] = 5; /* read bloquant jusqu'a ce que 5 */ /* caracteres soient lus */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); while (STOP==FALSE) { /* boucle de lecture */ res = read(fd,buf,255); /* retourne apres lecture 5 caracteres */ buf[res]=0; /* pour pouvoir les imprimer... */ printf(":%s:%d\n", buf, res); if (buf[0]=='z') STOP=TRUE; } tcsetattr(fd,TCSANOW,&oldtio); } 33..33.. LLeeccttuurree aassyynncchhrroonnee #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 a POSIX */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; void signal_handler_IO (int status); /* le gestionnaire de signal */ int wait_flag=TRUE; /* TRUE tant que recu aucun signal */ main() { int fd,c, res; struct termios oldtio,newtio; struct sigaction saio; /* definition de l'action du signal */ char buf[255]; /* ouvre le port en mon non-bloquant (read retourne immediatement) */ fd = 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 = signal_handler_IO; saio.sa_mask = 0; saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO,&saio,NULL); /* 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,&oldtio); /* sauvegarde de la configuration courante */ /* positionne les nouvelles valeurs pour lecture canonique */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; newtio.c_lflag = ICANON; newtio.c_cc[VMIN]=1; newtio.c_cc[VTIME]=0; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); /* on boucle en attente de lecture. generalement, on realise des traitements a l'interieur de la boucle */ while (STOP==FALSE) { printf(".\n");usleep(100000); /* wait_flag = FALSE apres reception de SIGIO. Des donnees sont disponibles et peuvent etre lues */ if (wait_flag==FALSE) { res = read(fd,buf,255); buf[res]=0; printf(":%s:%d\n", buf, res); if (res==1) STOP=TRUE; /* on arrete la boucle si on lit une ligne seule */ wait_flag = TRUE; /* on attend de nouvelles donnees */ } } /* restaure les anciens parametres du port */ tcsetattr(fd,TCSANOW,&oldtio); } /*************************************************************************** * gestionnaire de signal. Positionne wait_flag a FALSE, pour indiquer a * * la boucle ci-dessus que des caracteres ont ete recus. * ***************************************************************************/ void signal_handler_IO (int status) { printf("reception du signal SIGIO.\n); wait_flag = FALSE; } 33..44.. MMuullttiipplleexxaaggee eenn lleeccttuurree Cette section est reduite au minimum, et n'est la que pour vous guider. Le code source d'exemple presente est donc reduit au strict minimum. Il ne fonctionnera pas seulement avec des ports serie, mais avec n'importe quel ensemble de descripteurs de fichiers. L'appel systeme select et les macros qui lui sont attachees utilisent un fd_set. C'est un tableau de bits, qui dispose d'un bit pour chaque descripteur de fichier valide. select accepte un fd_set ayant les bits positionnes pour les descripteurs de fichiers qui conviennent, et retourne un fd_set, dans lequel les bits des descripteurs de fichier ou une lecture, une ecriture ou une exception sont positionnes. Toutes les manipulations de fd_set sont faites avec les macros fournies. Reportez vous egalement a la page de manuel de select(2). #include <sys/time.h> #include <sys/types.h> #include <unistd.h> main() { int fd1, fd2; /* entrees 1 et 2 */ fd_set readfs; /* ensemble de descripteurs */ int maxfd; /* nombre max des descripteurs utilises */ int loop=1; /* boucle tant que TRUE */ /* open_input_source ouvre un peripherique, configure le port correctement, et retourne un descripteur de fichier. */ fd1 = open_input_source("/dev/ttyS1"); /* COM2 */ if (fd1<0) exit(0); fd2 = open_input_source("/dev/ttyS2"); /* COM3 */ if (fd2<0) exit(0); maxfd = MAX (fd1, fd2)+1; /* numero maximum du bit a tester */ /* boucle d'entree */ while (loop) { FD_SET(fd1, &readfs); /* test pour la source 1 */ FD_SET(fd2, &readfs); /* test pour la source 2 */ /* on bloque jusqu'a ce que des caracteres soient disponibles en lecture */ select(maxfd, &readfs, NULL, NULL, NULL); if (FD_ISSET(fd1)) /* caracteres sur 1 */ handle_input_from_source1(); if (FD_ISSET(fd2)) /* caracteres sur 2 */ handle_input_from_source2(); } } L'exemple ci-dessus bloque indefiniment, jusqu'a ce que des caracteres venant d'une des sources soient disponibles. Si vous avez besoin d'un _t_i_m_e_o_u_t, remplacez juste l'appel a select par : int res; struct timeval Timeout; /* fixe la valeur du timeout */ Timeout.tv_usec = 0; /* millisecondes */ Timeout.tv_sec = 1; /* secondes */ res = select(maxfd, &readfs, NULL, NULL, &Timeout); if (res==0) /* nombre de descripteurs de fichiers avec caracteres disponibles = 0, il y a eu timeout */ Cet exemple verra l'expiration du delai de _t_i_m_e_o_u_t apres une seconde. S'il y a _t_i_m_e_o_u_t, select retournera 0, mais faites attention, Timeout est decremente du temps reellement attendu par select. Si la valeur de _t_i_m_e_o_u_t est 0, select retournera immediatement. 44.. AAuuttrreess ssoouurrcceess dd''iinnffoorrmmaattiioonn +o Le Linux Serial-HOWTO decrit comment mettre en place les ports serie et contient des informations sur le materiel. +o Le Serial Programming Guide for POSIX Compliant Operating Systems <http://www.easysw.com/~mike/serial>, par Michael Sweet. +o La page de manuel de termios(3) decrit toutes les constantes utilisees pour la structure termios. 55.. CCoonnttrriibbuuttiioonnss Comme je l'ai dit dans l'introduction, je ne suis pas un expert dans le domaine, mais j'ai rencontre des problemes, et j'ai trouve les solutions avec l'aide d'autres personnes. Je tiens a remercier pour leur aide M. Strudthoff du European Transonic WindTunnel, Cologne, Michael Carter (mcarter@rocke.electro.swri.edu) et Peter Waltenberg (p.waltenberg@karaka.chch.cri.nz). Antonino Ianella (antonino@usa.net a ecrit le Serial-Port-Programming Mini HOWTO, au meme moment ou je preparais ce document. Greg Hankins m'a demande d'inclure le Mini-HOWTO d'Antonino dans ce document. La structure de ce document et le formattage SGML ont ete derives du Serial-HOWTO de Greg Hankins.