Pierre Ficheux (pficheux@com1.fr)
Septembre 1999
Le mode de transmission sur un ligne série (appelée aussi RS-232) est décrit dans le schéma ci-dessous:
L'arrivée des données est indiqué 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 indiquent la vitesse de la ligne: 9600 bits/s, 19200 bits/s, etc...
1 | FGND (Frame Ground) | Masse mécanique |
2 | TD (Transmit Data) | Transmission des données |
3 | RD (Receive Data) | Réception des données |
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 controle suivant:
20 | DTR (Data Terminal Ready) | Présence du DTE |
6 | DSR (Data Set Ready) | Présence du DCE |
8 | DCD (Data Carrier Detect) | Connexion DCE établie |
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:
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 si 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.
On en déduit donc le cablage utilisé pour une liaison calculateur/MODEM (interconnexion DTE/DCE complète) dans le cas d'une connexion entre 2 prises 25 points (DB 25). La colonne de gauche indique de DTE, celle de droite le DCE. Les flèches indiquent la direction des signaux.
1 | FGND | <-> | FGND | 1 |
2 | TD | --> | TD | 2 |
3 | RD | <-- | RD | 3 |
4 | RTS | --> | RTS | 4 |
5 | CTS | <-- | CTS | 5 |
6 | DSR | <-- | DSR | 6 |
7 | GND | <-> | GND | 7 |
8 | DCD | <-- | DCD | 8 |
20 | DTR | --> | DTR | 20 |
Dans le cas d'une prise 9 points coté DTE (cas des PC en particulier) cela donne :
3 | TD | --> | TD | 2 |
2 | RD | <-- | RD | 3 |
7 | RTS | --> | RTS | 4 |
8 | CTS | <-- | CTS | 5 |
6 | DSR | <-- | DSR | 6 |
5 | GND | <-> | GND | 7 |
1 | DCD | <-- | DCD | 8 |
4 | DTR | --> | DTR | 20 |
Le noyau LINUX contient naturellement des pilotes pour ce type de périphérique. La détection des ports séries par le noyau est indiqué au démarrage de LINUX par les lignes:
Serial driver version 4.13 with no serial options enabled tty00 at 0x03f8 (irq = 4) is a 16550A tty01 at 0x02f8 (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 apparaitre à la suite les message:
tty02 at 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 5On voit que les ports séries sont utilisables à travers les fichiers spéciaux /dev/ttyS0 (ou /dev/cua0), /dev/ttyS1 (ou /dev/cua1) et ainsi de suite.
Jusqu'au noyau 2.0, les utilisateurs de LINUX avaient l'habitude de distinguer les devices séries sortants (call-out) cuax des devices entrants (call-in) ttySx Les nouvelles distributions de LINUX utilisant le noyau 2.2 indique que les devices des ports séries sont maintenant ttySx (plus de cuax) et que les applications doivent utiliser ce 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 positionne (ie, 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'occurence 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. Voici un exemple de dialogue avec un modem avec kermit:
root@mmxpf # kermit C-Kermit 6.0.192, 6 Sep 96, for Linux Copyright (C) 1985, 1996, Trustees of Columbia University in the City of New York. Default file-transfer mode is TEXT Type ? or HELP for help. [/root] C-Kermit>set line /dev/cua0 [/root] C-Kermit>set speed 115200 /dev/cua0, 115200 bps [/root] C-Kermit>connect Connecting to /dev/cua0, speed 115200. The escape character is Ctrl-\ (ASCII 28, FS) Type the escape character followed by C to get back, or followed by ? to see other options. at OK (Back at mmxpf) [/root] C-Kermit>q root@mmxpf #
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 =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 controle de ce terminal (effacement, interruption, etc...). Il est bien entendu possible de modifier ces caractères de controle en utilisant stty, par exemple:; 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
stty erase "^H"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 controle. Dans notre exemple la ligne n'utilise pas de parité (-parenb -parodd), utilise 8 bits de données (cs8) et ne traite pas de controle 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 1 root uucp 4 Oct 10 1997 /dev/modem -> cua0
Le paramètrage d'une ligne série par un shell-script utilisant man 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:
115200 | vitesse à 115200 bps |
cs8 | 8 bits de données | -parenb | pas de parité paire |
-parodd | ni impaire d'ailleurs... |
-clocal | controle de flux non local traitement des signaux modem (DCD, DTR) |
crtscts | activation du controle de flux hardware |
-echo | pas d'écho local des caractères |
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 CONNECTce qui signifie:
"" | on n'attend aucune chaîne au départ |
ATM0\&C1\&D2\&K3 | on envoit un chaîne d'init... |
OK | et on attend OK |
ATDT3611 | on compose le numéro... |
CONNECT | et on attend CONNECT |
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 on pourra 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é.
Le but de ce paragraphe n'est pas de fournir une description complète de termios, vous devrez pour cela vous référer au man termios, aux divers documents consacrés au sujet ou au chapitre consacré aux terminaux POSIX dans l'ouvrage Linux 2.0 co-écrit par mon jeune camarade Eric Dumas (dumas@freenix.org), exilé au pays des hamburgers et des nymphettes siliconées (voir bibliographie).
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#include #include #include 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 controles sont disponible (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 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); }
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)