The best way to debug your code is to set up another Linux box, and connect
the two computers via a null-modem cable. Use miniterm (available from the LDP
programmers guide
(ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz
in the examples directory) to transmit characters to your Linux box. Miniterm
can be compiled very easily and will transmit all keyboard input raw over the
serial port. Only the define statement #define MODEMDEVICE
"/dev/ttyS0"
has to be checked. Set it to ttyS0
for COM1,
ttyS1
for COM2, etc.. It is essential for testing, that
all characters are transmitted raw (without output processing) over the
line. To test your connection, start miniterm on both computers and just type
away. The characters input on one computer should appear on the other computer
and vice versa. The input will not be echoed to the attached screen.
To make a null-modem cable you have to cross the TxD (transmit) and RxD (receive) lines. For a description of a cable see sect. 7 of the Serial-HOWTO.
It is also possible to perform this testing with only one computer, if you
have two unused serial ports. You can then run two miniterms off two virtual
consoles. If you free a serial port by disconnecting the mouse, remember to
redirect /dev/mouse
if it exists. If you use a multiport serial
card, be sure to configure it correctly. I had mine configured wrong and
everything worked fine as long as I was testing only on my computer. When I
connected to another computer, the port started loosing characters. Executing
two programs on one computer just isn't fully asynchronous.
The devices /dev/ttyS*
are intended to hook up terminals to your
Linux box, and are configured for this use after startup. This has to be kept in
mind when programming communication with a raw device. E.g. the ports are
configured to echo characters sent from the device back to it, which normally
has to be changed for data transmission.
All parameters can be easily configured from within a program. The
configuration is stored in a structure struct termios
, which is
defined in <asm/termbits.h>
:
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
This file also includes all flag definitions. The input mode flags in
c_iflag
handle all input processing, which means that the
characters sent from the device can be processed before they are read with
read
. Similarly c_oflag
handles the output processing.
c_cflag
contains the settings for the port, as the baudrate, bits
per character, stop bits, etc.. The local mode flags stored in
c_lflag
determine if characters are echoed, signals are sent to
your program, etc.. Finally the array c_cc
defines the control
characters for end of file, stop, etc.. Default values for the control
characters are defined in <asm/termios.h>
. The flags are
described in the manual page termios(3)
. The structure
termios
contains the c_line
(line discipline) element,
which is not used in POSIX compliant systems.
Here three different input concepts will be presented. The appropriate
concept has to be chosen for the intended application. Whenever possible, do not
loop reading single characters to get a complete string. When I did this, I lost
characters, whereas a read
for the whole string did not show any
errors.
This is the normal processing mode for terminals, but can also be useful for
communicating with other dl input is processed in units of lines, which means
that a read
will only return a full line of input. A line is by
default terminated by a NL
(ASCII LF
), an end of file,
or an end of line character. A CR
(the DOS/Windows default
end-of-line) will not terminate a line with the default settings.
Canonical input processing can also handle the erase, delete word, and
reprint characters, translate CR
to NL
, etc..
Non-Canonical Input Processing will handle a fixed amount of characters per read, and allows for a character timer. This mode should be used if your application will always read a fixed number of characters, or if the connected device sends bursts of characters.
The two modes described above can be used in synchronous and asynchronous
mode. Synchronous is the default, where a read
statement will
block, until the read is satisfied. In asynchronous mode the read
statement will return immediatly and send a signal to the calling program upon
completion. This signal can be received by a signal handler.
This is not a different input mode, but might be useful, if you are handling multiple devices. In my application I was handling input over a TCP/IP socket and input over a serial connection from another computer quasi-simultaneously. The program example given below will wait for input from two different input sources. If input from one source becomes available, it will be processed, and the program will then wait for new input.
The approach presented below seems rather complex, but it is important to
keep in mind that Linux is a multi-processing operating system. The
select
system call will not load the CPU while waiting for input,
whereas looping until input becomes available would slow down other processes
executing at the same time.