Retrouvez cet article dans : Linux Magazine Hors série 23
Le but de cet article est la compré-hension du fonctionnement des ports série sous LINUX. L’article abordera également la configuration des ports série depuis le shell sh ainsi qu’en langage C.
Rappels sur le port série
Principe du port série
Le port série est une interface hardware permettant de transmettre des informations binaires de manière séquentielle (1 par 1). Par opposition, un port parallèle comme le port Centronics des imprimantes transmet les bits de manière simultanée (8 à la fois).
Le mode de transmission sur une ligne série (appelée aussi RS-232) est décrit dans le schéma ci-dessous :

Fig. 1
L’arrivée des données est indiquée par un bit de START. Les données sont transmises sous forme d’une suite de 5 à 8 bits contenant éventuellement un bit de PARITE utilisé pour la détection des erreurs de transmission. La fin de la transmission est indiquée par un bit de STOP. La fréquence de transmission des bits indique la vitesse de la ligne : 9600 bits/s, 19200 bits/s, etc.
Connectique et signaux
Concrètement, un port série est matérialisé par un connecteur contenant 25 points (DB25) ou 9 points (DB9). Ce dernier est le plus répandu sur les machines modernes lorsqu’elles ont encore la chance de disposer d’un tel port. Au besoin, on pourra acquérir une carte PCI/Série pour quelques euros. Un port série à bas débit n’a besoin que de trois lignes :

Ce type de connexion minimale sera utilisable pour connecter un équipement à basse vitesse, comme un terminal asynchrone (en croisant le TD et le RD). Malheureusement, ces signaux ne sont pas suffisants pour gérer correctement des périphériques plus évolués comme des MODEMS. On doit alors utiliser les signaux de contrôle suivant :

Dans la terminologie des lignes série, l’équipement de communication est appelé DCE (Data Communication Equipment) et le calculateur est appelé DTE (Data Terminal Equipment). Les signaux DTR et DCD sont les plus utilisés dans la pratique :
- Le signal DTR indique au MODEM que le calculateur est actif. La chute de ce signal provoquera la déconnexion du MODEM, ce qui est bien pratique si l’on veut éviter de rincer excessivement France Télécom ;-))
- Le signal DCD indique au calculateur que le MODEM est connecté au MODEM distant. La chute de ce signal (indiquant la déconnexion) sera détectable par le programme applicatif afin de déclencher la procédure adéquate.
En plus des signaux de contrôle indispensables, on peut également connecter les signaux RTS et CTS (hardware handshake, contrôle de flux hard). Le DTE peut affirmer RTS (Request To Send) pour demander au DCE s’il peut envoyer des données. En réponse, le DCE affirme CTS (Clear To Send) pour indiquer qu’il est prêt. Si le DCE n’est pas prêt, il fait tomber CTS, idem pour de DTE avec RTS.
Dans le cas d‘une prise 9 points cotés DTE (cas des PC en particulier), cela donne :

Détection du hardware RS-232 par LINUX
Il existe de nombreux circuits permettant de gérer des ports RS-232. Les plus anciens se souviendront avec nostalgie de l’ACIA 6850, périphérique bien connu de l’antique processeur 6800 de chez MOTOROLA. Les PC d’aujourd’hui utilisent des circuits appelés UART comme le 16550A. Le noyau Linux contient naturellement des pilotes pour ce type de périphérique. La détection des ports série par le noyau est indiqué au démarrage de Linux par les lignes :
Serial: 8250/16550 driver $Revision: 1.90 $ 48 ports, IRQ sharing enabled ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A
Pour cela il faut avoir validé l’option suivante lors de la compilation du noyau :
Standard/generic serial support (CONFIG_SERIAL) [Y/m/n/?]
Si le système dispose d’une carte série additionnelle, on verra apparaître à la suite les messages :
tty02 at I/O 0x03e8 (irq = 4) is a 16550A
Dans cet exemple, on voit que la deuxième carte est bien détectée à une adresse différente (3e8), mais que le système lui a attribué la même interruption (IRQ) que le premier port série. Pour assurer le bon fonctionnement, il sera nécessaire de modifier le niveau d’interruption en accord avec la configuration de la carte. Pour cela, on utilisera la commande setserial appelée par exemple dans le script rc.local:
/bin/setserial /dev/cua2 irq 5
On voit que les ports série sont utilisables à travers les fichiers spéciaux /dev/ttyS0, /dev/ttyS1 et ainsi de suite.
Jusqu’au noyau 2.0, les utilisateurs de GNU/Linux avaient l’habitude de distinguer les devices série sortants (call-out) cuax des devices entrants (call-in) ttySx. Les actuelles distributions utilisant un noyau bien supérieur indiquent que les devices des ports série sont maintenant ttySx (plus de cuax) et que les applications doivent utiliser ces devices. Il existe cependant une différence notoire décrite dans le document Serial-HOWTO.
La seule différence est dans la manière dont le périphérique est ouvert. Le périphérique d’entrée /dev/ttySx est ouvert en mode bloquant, jusqu’à ce que CD soit positionné (i. e., quelqu’un se connecte). Cette différence peut provoquer des disfonctionnements de certaines applications si l’on utilise ttySx à la place de cuax alors que l’application n’a pas été prévue pour cela (en l’occurrence des blocages sur l’ouverture du device). Quelques infos complémentaires sont disponibles au paragraphe 4.2.
Pour tester la communication sur un port série, le mieux est d’utiliser un outil d’émulation de terminal comme kermit ou minicom. Ce dernier est livré en standard avec les distributions classiques.
Utilisation à travers le shell
La commande stty
Cette commande permet de visualiser et modifier les paramètres d’un terminal. Cette dénomination est à prendre au sens large et l’utilisation de stty ne se limite pas aux lignes série. Au niveau Linux, le terminal est défini comme un canal de communication associé à une discipline de ligne (line discipline) qui détermine le comportement de ce canal.
Au niveau de l’émulateur de terminal xterm, on peut utiliser simplement la commande en faisant :
pierre@mmxpf % stty -a speed 9600 baud; rows 24; columns 80; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
L’option -a permet d’afficher l’ensemble des paramètres du terminal. L’exemple ci-dessous nous indique quelles valeurs sont affectées aux caractères de contrôle de ce terminal (effacement, interruption, etc.). Il est bien entendu possible de modifier ces caractères de contrôle en utilisant stty, par exemple :
Affecte [CTRL-H] à l’effacement de caractère. De même, on peut modifier le paramétrage de la ligne : vitesse, nombre de bits de données, parité, traitement des signaux de contrôle. Dans notre exemple, la ligne n’utilise pas de parité (-parenb -parodd), utilise 8 bits de données (cs8) et ne traite pas de contrôle de flux hardware (-crtscts) ce qui n’est pas très étonnant pour un terminal attaché à un xterm ! La liste complète des paramètres gérés par stty est bien entendu disponible en faisant man stty.
Pour revenir au sujet qui nous intéresse aujourd’hui, on peut bien entendu utiliser stty sur un terminal série. Par la magie de la redirection des entrées/sorties sous Linux, on peut par exemple afficher les paramètres de la ligne utilisée par le modem en faisant :
stty -a < /dev/modem
Le device /dev/modem étant en général un lien symbolique sur un device série réel :
pierre@mmxpf % ls -l /dev/modem lrwxrwxrwx /dev/modem -> cua0
Le paramétrage d’une ligne série par un shell-script utilisant stty est de la même veine :
#!/bin/sh ( stty 115200 stty cs8 stty -parenb stty -parodd stty -clocal stty crtscts stty -echo # Suite du script ... ) < /dev/cua0 > /dev/cua0
Dans cet exemple, on effectue le paramétrage suivant :

La commande chat
Le mot chat signifie en anglais " bavarder, discuter ". La commande chat permet d’exécuter une séquence de type expect/send (j’envoie/j’attends) sur un terminal donné. La commande est en général associée au package du démon pppd. Elle sera donc installée par défaut (ou du moins disponible) sur la plupart des distributions courantes.
Une utilisation pratique de chat pourra être par exemple l’initialisation d’un périphérique connecté à une ligne série. Comme beaucoup de commandes sous Linux, chat utilise par défaut l’entrée et la sortie standard ce qui permet de tester le chat-script plus facilement.
Prenons l’exemple d’un script destiné à initialiser un modem, puis appeler un numéro de téléphone et attendre la connexion. On pourra facilement mettre au point un tel script directement dans un xterm en tapant la commande :
chat -v "" ATM0\&C1\&D2\&K3 OK ATDT3611 CONNECT
Ce qui signifie :

L’option -v indique de tracer le dialogue en utilisant le syslog. En général, la trace sortira sur le fichier /var/log/message (suivant la configuration du syslog). En simulant la réponse du modem à la main (en tapant OK puis CONNECT dans le même terminal), on obtient la trace du dialogue dans le fichier de log :
Sep 26 21:31:01 mmxpf chat[463]: send (ATM0&C1&D2&K3^M) Sep 26 21:31:02 mmxpf chat[463]: expect (OK) Sep 26 21:31:03 mmxpf chat[463]: -- got it Sep 26 21:31:03 mmxpf chat[463]: send (ATDT3611^M) Sep 26 21:31:03 mmxpf chat[463]: expect (CONNECT) Sep 26 21:31:05 mmxpf chat[463]: -- got it
Lorsque le script est au point, il suffit de rediriger l’entrée et la sortie sur le device du modem :
chat -v "" ATM0\&C1\&D2\&K3 OK ATDT3611 CONNECT < /dev/cua0 > /dev/cua0
Forts de notre expérience de la commande stty, nous pourrons donc combiner l’initialisation de la ligne série avec le chat-script à envoyer au modem :
#!/bin/sh ( # Init ligne série stty 115200 stty cs7 stty parenb stty -clocal stty crtscts stty -echo # Init modem et appel chat -v "" ATM0\&C1\&D2\&K3 OK ATDT3611 CONNECT ) < /dev/cua0 > /dev/cua0
Le man chat vous donnera beaucoup plus d’informations concernant les possibilités de ce petit programme comme par exemple la définition du chat-script dans un fichier séparé.
Programmation des ports série en C
Interface POSIX (termios)
La programmation des ports série sous Linux utilise l’interface POSIX définie dans le fichier termios.h. Le but de ce paragraphe n’est pas de fournir une description complète de termios. Vous devrez pour cela vous référer à la page de manuel termios, aux divers documents consacrés au sujet. Le premier exemple ci-dessous permet de forcer le raccrochage (hangup) d’un modem en faisant chuter le signal DTR. Dans la définition de l’interface POSIX des terminaux, cela s’effectue simplement en positionnant la vitesse à 0 (valeur B0) :
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
main (int ac, char **av)
{
int fd;
struct termios tty, old;
/* Ouverture du device */
if ((fd = open (av[1], O_RDWR)) < 0) {
perror (av[1]);
exit (1);
}
/* Lecture des paramètres */
tcgetattr(fd, &tty);
tcgetattr(fd, &old);
/* On passe la vitesse à 0 ==> hangup */
cfsetospeed(&tty, B0);
cfsetispeed(&tty, B0);
/* On applique le nouveau paramètrage pendant 1s */
tcsetattr(fd, TCSANOW, &tty);
sleep(1);
/* On revient à l’ancien et on quitte */
tcsetattr(fd, TCSANOW, &old);
close (fd);
}
On pourra faire exactement la même chose en shell en faisant :
stty 0 < /dev/cua0 > /dev/cua0
Par défaut, une ligne série sous Linux est ouverte en mode canonique. Le mode canonique consiste à traiter les entrées d’un terminal comme une ligne terminée par un séparateur (le plus souvent LF : line-feed correspondant à la touche RETURN). Cela signifie que le programme applicatif ne pourra disposer de la saisie que lorsque le séparateur est reçu. Dans le mode canonique, un certain nombre de caractères de contrôles sont disponibles (effacement, etc. voir la commande stty).
Ce comportement est adapté au traitement d’un terminal réel avec un dialogue opérateur, mais il n’est pas utilisable dans le cas du dialogue avec un équipement de type modem par exemple. Le deuxième exemple permet donc de positionner et d’utiliser une ligne série en mode direct (raw), et donc d’inhiber le mode canonique :
/* Fixe un device en mode RAW */
void raw_mode (fd, old_term)
int fd;
struct termios *old_term;
{
struct termios term;
tcgetattr(fd, &term);
/* Sauve l’ancienne config dans le paramètre */
tcgetattr(fd, old_term);
/* mode RAW, pas de mode canonique, pas d’echo */
term.c_iflag = IGNBRK;
term.c_lflag = 0;
term.c_oflag = 0;
/* Controle de flux hardware RTS/CTS)
term.c_cflag |= (CREAD | CRTSCTS);
/* 1 caractère suffit */
term.c_cc[VMIN] = 1;
/* Donnée disponible immédiatement */
term.c_cc[VTIME] = 0;
/* Inhibe le controle de flux XON/XOFF */
term.c_iflag &= ~(IXON|IXOFF|IXANY);
/* 8 bits de données, pas de parité */
term.c_cflag &= ~(PARENB | CSIZE);
term.c_cflag |= CS8;
/* Gestion des signaux modem */
term.c_cflag &= ~CLOCAL;
tcsetattr(fd, TCSANOW, &term);
}
Autre type de configuration
Un descripteur de fichier sur un port série sera ouvert par une ligne du type
fd = open (device_name, O_RDWR)
Par défaut, cette ligne est ouverte en mode bloquant, ce qui peut parfois poser des problèmes dans le cas de l’utilisation du device /dev/ttySx (voir paragraphe 2). On peut alors ouvrir le device en mode non bloquant par l’appel :
fd = open (device_name, O_RDWR | O_NDELAY)
Conclusion
Malgré son grand âge, le port série est largement utilisé dans de nombreuses applications industrielles (acquisition de données, pilotage d’équipement). La maîtrise des techniques d’exploitation de cette interface est souvent indispensable pour la réalisation d’un système industriel complexe. En dépit de son apparente complexité, l’interface POSIX utilisée sous LINUX est d’une grande efficacité et laisse peu de place aux comportements imprévus.
Liens
- Le Serial-HOWTO sur : http://www.freenix.fr/unix/linux/HOWTO-vo/Serial-HOWTO.html
- Le Serial-Programming-HOWTO sur : http://www.freenix.fr/unix/linux/HOWTO-vo/Serial-Programming-HOWTO.html
- Le document Serial Programming Guide for POSIX Compliant Operating Systems sur : http://dns-gate.easysw.com/~mike/serial
- L’ouvrage Programmation Linux 2.0 (Card/Dumas/Mével) aux éditions EYROLLES
- L’utilitaire Kermit sur : http://www.columbia.edu/kermit/ck60.html
- L’utilitaire Minicom sur : http://www.pp.clinet.fi/~walker/minicom.html
Retrouvez cet article dans : Linux Magazine Hors série 23

