2. Getting started
2.1. Debugging
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.
2.2. Port Settings
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.
2.3. Input Concepts for Serial Devices
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.
2.3.1. Canonical Input Processing
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..
2.3.2. Non-Canonical Input Processing
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.
2.3.3. Asynchronous Input
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.
2.3.4. Waiting for Input from Multiple Sources
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.