How to handle a serial port or modem?

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.




  • Use tcgetattr() 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 a struct termios.



  • Set suitable values for c_iflag, c_oflag, c_cflag,
    c_lflag, and c_cc in the termios structure. (See below.)



  • Use cfsetispeed() and cfsetospeed() 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.



  • Use tcsetattr() to set the device modes.



  • You may wish, if you used O_NONBLOCK when opening the port, to
    use fcntl() to ensure that O_NONBLOCK is turned off again.
    Systems seem to differ as to whether a nonblocking open on a tty will
    affect subsequent read() 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