Sophie

Sophie

distrib > Mandriva > 9.1 > ppc > by-pkgid > d74cd56fd5c222851f198ac5005d694d > files > 99

howto-text-fr-9.0-1mdk.noarch.rpm

  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.