The Linux Serial Programming HOWTO, Part 1 of 2

By Vernon C. Hoxie

v2.0 10 September 1999
This document describes how to program communications with devices over a serial port on a Linux box.

1. Copyright

The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Vernon Hoxie. Linux HOWTO documents may be reproduced and distributed in whole or in part, in any medium physical or electronic, as long as this copyright notice is retained on all copies. Commercial redistribution is allowed and encouraged; however, the author would like to be notified of any such distributions.

All translations, derivative works, or aggregate works incorporating this Linux HOWTO document must be covered under this copyright notice. That is, you may not produce a derivative work from this HOWTO and impose additional restrictions on its distribution.

This version is a complete rewrite of the previous Serial-Programming-HOWTO by Peter H. Baumann, mailto:Peter.Baumann@dlr.de

2. Introduction

This HOWTO will attempt to give hints about how to write a program which needs to access a serial port. Its principal focus will be on the Linux implementation and what the meaning of the various library functions available.

Someone asked about which of several sequences of operations was right. There is no absolute right way to accomplish an outcome. The options available are too numerous. If your sequences produces the desired results, then that is the right way for you. Another programmer may select another set of options and get the same results. His method is right for him.

Neither of these methods may operate properly with some other implementation of UNIX. It is strange that many of the concepts which were implemented in the SYSV version have been dumped. Because UNIX was developed by AT&T and much code has been generated on those concepts, the AT&T version should be the standard to which others should emulate.

Now the standard is POSIX.

It was once stated that the popularity of UNIX and C was that they were created by programmers for programmers. Not by scholars who insist on purity of style in deference to results and simplicity of use. Not by committees with people who have diverse personal or proprietary agenda. Now ANSI and POSIX have strayed from those original clear and simply concepts.

3. Opening

The various serial devices are opened just as any other file. Although, the fopen(3) command may be used, the plain open(2) is preferred. This call returns the file descriptor which is required for the various commands that configure the interface.

Open(2) has the format:

#include <fcntl.h>
int open( char *path, int flags, [int mode] ); 

In addition to the obvious O_RDWR, O_WRONLY and O_RDONLY, two additional flags are available. These are O_NONBLOCK and O_NOCTTY. Other flags listed in the open(2) manual page are not applicable to serial devices.

Normally, a serial device opens in "blocking" mode. This means that the open() will not return until the Carrier Detect line from the port is active, e.g. modem, is active. When opened with the O_NONBLOCK flag set, the open() will return immediately regardless of the status of the DCD line. The "blocking" mode also affects the read() call.

The fcntl(2) command can be used to change the O_NONBLOCK flag anytime after the device has been opened.

The device driver and the data passing through it are controlled according to settings in the struct termios. This structure is defined in "/usr/include/termios.h". In the Linux tree, further reference is made to "/usr/include/asm/termbits.h".

In blocking mode, a read(2) will block until data is available or a signal is received. It is still subject to state of the ICANON flag.

When the termios.c_lflag ICANON bit is set, input data is collected into strings until a NL, EOF or EOL character is received. You can define these in the termios.c_cc[] array. Also, ERASE and KILL characters will operate on the incoming data before it is delivered to the user.

In non-canonical mode, incoming data is quanitified by use of the c_cc[VMIN and c_cc[VTIME] values in termios.c_cc[].

Some programmers use the select() call to detect the completion of a read(). This is not the best way of checking for incoming data. Select() is part of the SOCKETS scheme and too complex for most applications.

A full explanation of the fields of the termios structure is contained in termios(7) of the Users Manual. A version is included in Part 2 of this HOWTO document.

4. Commands

Changes to the struct termios are made by retrieving the current settings, making the desired changes and transmitting the modified structure back to the kernel.

The historic means of communicating with the kernel was by use of the ioctl( fd, COMMAND, arg ) system call. Then the purists in the computer industry decided that this was not genetically consistent. Their argument was that the argument changed its stripes. Sometimes it was an int, sometimes it was a pointer to int and other times it was a pointer to struct termios. Then there were those times it was empty or NULL. These variations are dependent upon the COMMAND.

As a alternative, the tc* series of functions were concocted.

These are:

int tcgetattr( int <it/filedes/, struct termios *termios_p );
int tcsetattr( int <it/filedes/, int <it/optional_actions/,
               *const struct termios <it/termios_p/ ); 
instead of:
int ioctl( int <it/filedes/, int <it/command/,
           *const struct termios <it/termios_p/ ); 
where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF.

The TCSETS command is comparable to the TCSANOW optional_action for the tc* version. These direct the kernel to adopt the changes immediately. Other pairs are:

  command   optional_action        Meaning 
  TCSETSW     TCSADRAIN         Change after all output has drained.
  TCSETSF     TCSAFLUSH         Change after all output has drained
                              then discard any input characters
                              not read. 

Since the return code from either the ioctl(2) or the tcsetattr(2) commands only indicate that the command was processed by the kernel. These do not indicate whether or not the changes were actually accomplished. Either of these commands should be followed by a call to:

ioctl( fd, TCGETS, &new_termios );
or:
tcgetattr( fd, &new_termios );

A user function which makes changes to the termios structure should define two struct termios variables. One of these variables should contain the desired configuration. The other should contain a copy of the kernels version. Then after the desired configuration has been sent to the kernel, another call should be made to retrieve the kernels version. Then the two compared.

Here is an example of how to add RTS/CTS flow control:

struct termios my_termios;
struct termios new_termios;

tcgetattr( fd, &my_termios );
my_termios.c_flag |= CRTSCTS;
tcsetattr( fd, TCSANOW, &my_termios );
tcgetattr( fd, &new_termios );
if ( memcmp( my_termios, new_termios,
     sizeof( my_termios )) != 0 ) {
    /* do some error handling */
}

5. Changing Baud Rates

With Linux, the baud rate can be changed using a technique similar to add/delete RTS/CTS.

struct termios my_termios;
struct termios new_termios;

tcgetattr( fd, &my_termios );
my_termios.c_flag &= ~CBAUD;
my_termios.c_flag |= B19200;
tcsetattr( fd, TCSANOW, &my_termios );
tcgetattr( fd, &new_termios );
if ( memcmp( my_termios, new_termios,
     sizeof( my_termios )) != 0 ) {
    /* do some error handling */
}

POSIX adds another method. They define:

speed_t cfgetispeed( const struct termios *termios_p );
speed_t cfgetospeed( const struct termios *termios_p );

library calls to extract the current input or output speed from the struct termios pointed to with *termio_p. This is a variable defined in the calling process. In practice, the data contained in this termios, should be obtained by the tcgetattr() call or an ioctl() call using the TCGETS command.

The companion library calls are:

int cfsetispeed( struct termios *termios_p, speed_t speed );
int cfsetospeed( struct termios *termios_p, speed_t speed );
which are used to change the value of the baud rate in the locally defined *termios_p. Following either of these calls, either a call to tcsetattr() or ioctl() with one of TCSETS, TCSETSW or TCSETSF as the command to transmit the change to the kernel.

The cf* commands are preferred for portability. Some weird Unices use a considerably different format of termios.

Most implementations of Linux use only the input speed for both input and output. These functions are defined in the application program by reference to <termios.h>. In reality, they are in /usr/include/asm/termbits.h.

6. Additional Control Calls

6.1 Sending a "break".

int ioctl( fd, TCSBRK, int arg );
int tcsendbreak( fd, int arg );

Send a break: Here the action differs between the conventional ioctl() call and the POSIX call. For the conventional call, an arg of '0' sets the break control line of the UART for 0.25 seconds. For the POSIX command, the break line is set for arg times 0.1 seconds.

6.2 Hardware flow control.

int ioctl( fd, TCXONC, int action );
int tcflow( fd, int action ); 

The action flags are:

6.3 Flushing I/O buffers.

int ioctl( fd, TCFLSH, queue_selector );
int tcflush( fd, queue_selector ); 

The queue_selector flags are:

7. Modem control

The hardware modem control lines can be monitored or modified by the ioctl(2) system call. A set of comparable tc* calls apparently do not exist. The form of this call is:

int ioctl( fd, COMMAND, (int *)flags ); 

The COMMANDS and their action are:

The bit pattern of flags refer to the following control lines:

It should be noted that some of these bits are controlled by the modem and the UART cannot change them but their status can be sensed by TIOCMGET. Also, most Personal Computers do not provide hardware for secondary transmit and receive.

There are also a pair of ioctl() to monitor these lines. They are undocumented as far as I have learned. The commands are TIOCMIWAIT and TCIOGICOUNT. They also differ between versions of the Linux kernel.

See the lines.c file in my "serial_suite" for an example of how these can be used see ftp://scicom.alphacd.com/pub/linux/serial_suite

8. Process Groups

8.1 Sessions

8.2 Process Groups

Any newly created process inherits the Process Group of its creator. The Process Group leader has the same PID as PGID.

8.3 Controlling Terminal

There are a series of ioctl(2) and tc*(2) calls which can be used to monitor or to change the process group to which the device is attached.

Get the foreground group process id.

If there is no foreground group, a number not representing an existing process group is returned. On error, a -1 is returned and errno is set.

     int ioctl( fd, TIOCGPGRP, (pid_t *)pid );
     int tcgetpgrp( fd, (pid_t *)pid ); 

Set the foreground process group id of a terminal.

The fd must be the controlling terminal and be associated with the session of the calling process.

int ioctl( fd, TIOCSPGRP, (pid_t *)pid );
int tcsetpgrp( fd, (pid_t *)pid ); 

Get process group id.

int ioctl( fd, TIOCGPGRP, &(pid_t)pid );
int tcgetpgrp( fd, &(pid_t)pid ); 

9. Lockfiles

Any process which accesses a serial device should first check for the existence of lock file for the desired device. If such a lock lock file exists, this means that the device may be in use by another process.

Check my "libdevlocks-x.x.tgz" at ftp://scicom.alphacdc.com/pub/linux for an example of how these lock files should be utilized.

10. Additional Information

Check out my "serial_suite.tgz" for more information about programming the serial ports at mailto:vern@zebra.alphacdc.com. There some examples and some blurbs about setting up modems and comments about some general considerations.

11. Feedback

Please send me any corrections, questions, comments, suggestions, or additional material. I would like to improve this HOWTO! Tell me exactly what you don't understand, or what could be clearer. You can reach me at mailto:vern@zebra.alphacdc.com via email. Please include the version number of the Serial-Programming-HOWTO when writing.