Le Linux Serial Programming HOWTO
 par Peter H. Baumann, [email protected]

 Adaptation francaise Etienne BERNARD [email protected]

 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 [email protected] 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  [email protected]  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 ([email protected])  et  Peter  Waltenberg
 ([email protected]).

 Antonino  Ianella ([email protected] 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.