How to handle a serial port or modem?
The handling of serial devices under Unix is heavily influenced by the
traditional use of serial terminals. Historically, various combinations
of ioctls and other hacks were necessary to control the precise behaviour
of a serial device, but fortunately this is one of the areas that POSIX
made some efforts to standardise.
If you're using a system that doesn't understand <termios.h>
,
tcsetattr()
and related functions, then you'll have to go
elsewhere for information (or upgrade your system to something less
archaeological).
There are still significant differences between systems, however, mainly
in the area of device names, handling of hardware flow control, and
modem signalling. (Whenever possible, leave the device driver to do all
the handshaking work, and don't attempt to manipulate handshaking
signals directly.)
The basic steps for opening and initialising a serial device are:
-
open()
the device; this may require the use of certain flags:
O_NONBLOCK
-
Opening a dial-in or modem-controlled device will block until carrier is
present, unless this flag is used. A nonblocking open gives you the
opportunity to disable the modem controls (see CLOCAL below) if
necessary.
O_NOCTTY
-
On 4.4BSD-derived systems this is redundant, but on other systems it
controls whether the serial device can become a control terminal for the
session. In most cases you probably don't want to acquire a
control terminal, and should therefore specify this flag, but there are
exceptions.
-
Usetcgetattr()
to retrieve the current device modes. While one
will often ignore most or all of the initial settings thus obtained, it's
still a convenient way of initialising astruct termios
.
-
Set suitable values forc_iflag
,c_oflag
,c_cflag
,
c_lflag
, andc_cc
in the termios structure. (See below.)
-
Usecfsetispeed()
andcfsetospeed()
to set the desired
baud rate. Very few systems allow you to set differing input and output
speeds, so as a general rule you should set both to your desired speed.
-
Usetcsetattr()
to set the device modes.
-
You may wish, if you usedO_NONBLOCK
when opening the port, to
usefcntl()
to ensure thatO_NONBLOCK
is turned off again.
Systems seem to differ as to whether a nonblocking open on a tty will
affect subsequentread()
calls; better to be explicit.
Once you have opened and set up the port, you can then use read()
and write()
normally. Note that the behaviour of read()
will
be controlled by the flag settings you gave to tcsetattr()
.
tcflush()
, tcdrain()
, tcsendbreak()
and
tcflow()
are additional useful functions that you should be aware
of.
When you're done with the port, and want to close it, be aware of a very
nasty little hazard on some systems; if there's any pending output waiting
to be written to the device (e.g. if output flow is stopped by hardware
or software handshaking), your process can hang unkillably in the
close()
call until the output drains. Calling tcflush()
to
discard any pending output is probably a wise move.
(Blocked output on tty devices is by far the most common cause of
"unkillable" processes in my experience.)
Home | FAQ |