LDP/LDP/inprogress/Serial-Programming-Hoxie-HO...

390 lines
15 KiB
Plaintext

<!DOCTYPE LINUXDOC SYSTEM>
<ARTICLE>
<TITLE>The Linux Serial Programming HOWTO, Part 1 of 2
<AUTHOR>By Vernon C. Hoxie
<DATE>v2.0 10 September 1999
<ABSTRACT>
This document describes how to program communications with devices
over a serial port on a Linux box.
</ABSTRACT>
<TOC>
<SECT>Copyright
<P>
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 <EM>
Serial-Programming-HOWTO </EM> by Peter H. Baumann, <URL
URL="mailto:Peter.Baumann@dlr.de">
<SECT>Introduction
<P>
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&amp;T and much code has been generated on those
concepts, the AT&amp;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.
<SECT>Opening
<P>
The various serial devices are opened just as any other file.
Although, the <it>fopen(3)</it> command may be used, the plain
<it>open(2)</it> is preferred. This call returns the file
descriptor which is required for the various commands that configure
the interface.
<it>Open(2)</it> has the format:
<tscreen><verb>
#include <fcntl.h>
int open( char *path, int flags, [int mode] ); </verb></tscreen>
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 <it>open(2)</it> manual page are not applicable to
serial devices.
Normally, a serial device opens in "blocking" mode. This
means that the <it>open()</it> 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 <it>open()</it> will return
immediately regardless of the status of the DCD line. The
"blocking" mode also affects the <it>read()</it> call.
The <it>fcntl(2)</it> 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 <it>struct termios</it>. 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 <it>read(2)</it> will block until data is available or a
signal is received. It is still subject to state of the ICANON flag.
When the <it>termios.c_lflag</it> 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 <it>termios.c_cc[]</it> 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
<it>c_cc[VMIN</it> and <it>c_cc[VTIME]</it> values in <it>termios.c_cc[]</it>.
Some programmers use the <it>select()</it> call to detect the
completion of a <it>read()</it>. This is not the best way of checking for
incoming data. <it>Select()</it> is part of the SOCKETS
scheme and too complex for most applications.
A full explanation of the fields of the <it>termios</it> structure is
contained in <it>termios(7)</it> of the Users Manual. A version is
included in Part 2 of this HOWTO document.
<SECT>Commands
<P>
Changes to the <it>struct termios</it> 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
<it>ioctl( fd, COMMAND, arg )</it> 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 <it>int</it>, sometimes it was a <it>pointer
to int</it> and other times it was a <it>pointer to struct
termios</it>. Then there were those times it was empty or NULL.
These variations are dependent upon the COMMAND.
As a alternative, the <it>tc*</it> series of functions were
concocted.
These are: <tscreen><verb>
int tcgetattr( int <it/filedes/, struct termios *termios_p );
int tcsetattr( int <it/filedes/, int <it/optional_actions/,
*const struct termios <it/termios_p/ ); </verb></tscreen>
instead of: <tscreen><verb>
int ioctl( int <it/filedes/, int <it/command/,
*const struct termios <it/termios_p/ ); </verb></tscreen>
where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF.
The TCSETS <it>command</it> is comparable to the TCSANOW
<it>optional_action</it> for the <it>tc*</it> version. These direct
the kernel to adopt the changes immediately. Other pairs are:
<tscreen><verb>
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.
</verb></tscreen>
Since the return code from either the <it>ioctl(2)</it> or the
<it>tcsetattr(2)</it> 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:
<tscreen><verb>
ioctl( fd, TCGETS, &amp;new_termios );</verb></tscreen>
or:
<tscreen><verb>
tcgetattr( fd, &amp;new_termios );</verb></tscreen>
A user function which makes changes to the <it>termios</it> structure should
define two <it>struct termios</it> 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:
<tscreen><verb>
struct termios my_termios;
struct termios new_termios;
tcgetattr( fd, &amp;my_termios );
my_termios.c_flag |= CRTSCTS;
tcsetattr( fd, TCSANOW, &amp;my_termios );
tcgetattr( fd, &amp;new_termios );
if ( memcmp( my_termios, new_termios,
sizeof( my_termios )) != 0 ) {
/* do some error handling */
}
</verb></tscreen>
<SECT>Changing Baud Rates
<P>
With Linux, the baud rate can be changed using a technique similar
to add/delete RTS/CTS.
<tscreen><verb>
struct termios my_termios;
struct termios new_termios;
tcgetattr( fd, &amp;my_termios );
my_termios.c_flag &= &tilde;CBAUD;
my_termios.c_flag |= B19200;
tcsetattr( fd, TCSANOW, &amp;my_termios );
tcgetattr( fd, &amp;new_termios );
if ( memcmp( my_termios, new_termios,
sizeof( my_termios )) != 0 ) {
/* do some error handling */
}
</verb></tscreen>
POSIX adds another method. They define:
<tscreen><verb>
speed_t cfgetispeed( const struct termios *termios_p );
speed_t cfgetospeed( const struct termios *termios_p );
</verb></tscreen>
library calls to extract the current input or output speed from the
struct termios pointed to with <it>*termio_p</it>. This is a variable
defined in the calling process. In practice, the data contained in
this termios, should be obtained by the <it>tcgetattr()</it> call or
an <it>ioctl()</it> call using the TCGETS command.
The companion library calls are:
<tscreen><verb>
int cfsetispeed( struct termios *termios_p, speed_t speed );
int cfsetospeed( struct termios *termios_p, speed_t speed );
</verb></tscreen>
which are used to change the value of the baud rate in the locally
defined <it>*termios_p</it>. Following either of these calls, either
a call to <it>tcsetattr()</it> or <it>ioctl()</it> with one of TCSETS,
TCSETSW or TCSETSF as the command to transmit the change to the
kernel.
The <it>cf*</it> commands are preferred for portability. Some weird Unices
use a considerably different format of <it>termios</it>.
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 &lt;termios.h&gt;. In reality, they are in
/usr/include/asm/termbits.h.
<SECT>Additional Control Calls
<P>
<SECT1>Sending a "break".
<P> <tscreen><verb>
int ioctl( fd, TCSBRK, int arg );
int tcsendbreak( fd, int arg );</verb></tscreen>
Send a <bf>break</bf>: Here the action differs between the
conventional <it>ioctl()</it> call and the POSIX call. For the
conventional call, an <it>arg</it> of '0' sets the break control line
of the UART for 0.25 seconds. For the POSIX command, the break line
is set for <it>arg</it> times 0.1 seconds.
<SECT1>Hardware flow control.
<P> <tscreen><verb>
int ioctl( fd, TCXONC, int action );
int tcflow( fd, int action ); </verb></tscreen>
The <it>action</it> flags are:
<itemize>
<item> TCOOFF 0 suspend output
<item> TCOON 1 restart output
<item> TCIOFF 2 transmit STOP character to suspend input
<item> TCION 3 transmit START character to restart input
</itemize>
<SECT1>Flushing I/O buffers.
<P> <tscreen><verb>
int ioctl( fd, TCFLSH, queue_selector );
int tcflush( fd, queue_selector ); </verb></tscreen>
The <it>queue_selector</it> flags are:
<itemize>
<item> TCIFLUSH 0 flush any data not yet read from the input buffer
<item> TCOFLUSH 1 flush any data written to the output buffer but not
yet transmitted
<item> TCIOFLUSH 2 flush both buffers
</itemize>
<SECT>Modem control
<P>
The hardware modem control lines can be monitored or modified by
the <it>ioctl(2)</it> system call. A set of comparable <it>tc*</it> calls
apparently do not exist. The form of this call is:<newline>
<tscreen><verb>
int ioctl( fd, COMMAND, (int *)flags ); </verb></tscreen>
The COMMANDS and their action are:
<itemize>
<item> TIOCMBIS turn on control lines depending upon which bits
are set in <it>flags</it>.
<item> TIOCMBIC turn off control lines depending upon which bits
are unset in <it>flags</it>.
<item> TIOCMGET the appropriate bits are set in <it>flags</it> according
to the current status
<item> TIOCMSET the state of the UART is changed according to which
bits are set/unset in 'flags'
</itemize>
The bit pattern of <it>flags</it> refer to the following control lines:
<itemize>
<item> TIOCM_LE Line enable
<item> TIOCM_DTR Data Terminal Ready
<item> TIOCM_RTS Request to send
<item> TIOCM_ST Secondary transmit
<item> TIOCM_SR Secondary receive
<item> TIOCM_CTS Clear to send
<item> TIOCM_CAR Carrier detect
<item> TIOCM_RNG Ring
<item> TIOCM_DSR Data set ready
</itemize>
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 <it>ioctl()</it> 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 <URL
URL="ftp://scicom.alphacd.com/pub/linux/serial_suite">
<SECT>Process Groups
<P>
<SECT1>Sessions
<P>
<SECT1>Process Groups
<P>
Any newly created process inherits the Process Group of its creator.
The Process Group leader has the same PID as PGID.
<SECT1>Controlling Terminal
<P>
There are a series of <it>ioctl(2)</it> and <it>tc*(2)</it> calls
which can be used to monitor or to change the process group to which
the device is attached.
<SECT2>Get the foreground group process id.
<P>
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.
<tscreen><verb>
int ioctl( fd, TIOCGPGRP, (pid_t *)pid );
int tcgetpgrp( fd, (pid_t *)pid ); </verb></tscreen>
<SECT2>Set the foreground process group id of a terminal.
<P>
The <it>fd</it> must be the controlling terminal and be associated
with the session of the calling process.
<tscreen><verb>
int ioctl( fd, TIOCSPGRP, (pid_t *)pid );
int tcsetpgrp( fd, (pid_t *)pid ); </verb></tscreen>
<SECT2>Get process group id.
<P>
<tscreen><verb>
int ioctl( fd, TIOCGPGRP, &amp;(pid_t)pid );
int tcgetpgrp( fd, &amp;(pid_t)pid ); </verb></tscreen>
<SECT>Lockfiles
<P>
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 <URL
URL="ftp://scicom.alphacdc.com/pub/linux"> for an example of how these
lock files should be utilized.
<SECT>Additional Information
<P>
Check out my "serial_suite.tgz" for more information about
programming the serial ports at <URL
URL="mailto:vern@zebra.alphacdc.com">. There some examples and some
blurbs about setting up modems and comments about some general
considerations.
<SECT>Feedback
<P>
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 <URL URL="mailto:vern@zebra.alphacdc.com"> via email.
Please include the version number of the Serial-Programming-HOWTO when
writing.
</ARTICLE>