mirror of https://github.com/tLDP/LDP
received from David Lawyer
This commit is contained in:
parent
d8f721507f
commit
20ff5da0b9
|
@ -0,0 +1,389 @@
|
||||||
|
<!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&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.
|
||||||
|
|
||||||
|
<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, &new_termios );</verb></tscreen>
|
||||||
|
or:
|
||||||
|
<tscreen><verb>
|
||||||
|
tcgetattr( fd, &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, &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 */
|
||||||
|
}
|
||||||
|
</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, &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 */
|
||||||
|
}
|
||||||
|
</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 <termios.h>. 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, &(pid_t)pid );
|
||||||
|
int tcgetpgrp( fd, &(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>
|
Loading…
Reference in New Issue