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.