全部博文(343)
分类: LINUX
2005-07-14 09:31:14
Although it is rarely done today, one can connect a video terminal with a null modem cable to a serial port on a Linux box and get an interactive shell with very little effort. All that is required is that you add a line such as
dt:12345:respawn:/sbin/agetty -L ttyS0 9600 vt510
to /etc/inittab
, and then run telinit q
as root to get init
to re-read the /etc/inittab
file, and you will be greeted by a login prompt on the terminal screen.
The ease with which we can do this task that we almost never want to do is due to the long legacy of Unix that Linux inherited. When Unix was young, video terminals were the user interface of choice and became a very important concept in the design of the operating system. Today, although "glass teletypes" are becoming more and more rare, the terminal has remained at the heart of the operating system as a very powerful metaphor.
Although it is rarely done today, one can connect a video terminal with a null modem cable to a serial port on a Linux box and get an interactive shell with very little effort. All that is required is that you add a line such as
dt:12345:respawn:/sbin/agetty -L ttyS0 9600 vt510
to /etc/inittab
, and then run telinit q
as root to get init
to re-read the /etc/inittab
file, and you will be greeted by a login prompt on the terminal screen.
The ease with which we can do this task that we almost never want to do is due to the long legacy of Unix that Linux inherited. When Unix was young, video terminals were the user interface of choice and became a very important concept in the design of the operating system. Today, although "glass teletypes" are becoming more and more rare, the terminal has remained at the heart of the operating system as a very powerful metaphor.
Most computer users sophisticated (or perhaps old) enough to know what a character cell terminal is probably think of them as relics of the past before the GUI revolutionized the user interface. Nonetheless, the first thing almost all Unix users do immediately after logging in to their elaborate desktops (and watching the dazzling eye candy meant to distract them from the intolerably long wait until the desktop system is ready to do something useful) is to throw up a terminal window to get an interactive shell into which they can type commands. Many probably think of it as just an interactive shell and not a terminal window, but in fact in Unix you cannot have an interactive shell without a controlling terminal.
The controlling terminal is one of the properties listed by the ps
command, in the column headed TTY below:
$ ps PID TTY TIME CMD 26841 pts/1 00:00:00 bash 26897 pts/1 00:00:00 ps
pts/1
is actually an abbreviation for /dev/pts/1
, a character special file for the interactive shell's (PID 26841) controlling terminal:
$ ls -l /dev/pts/1 crw--w---- 1 user tty 136, 1 Feb 21 00:35 /dev/pts/1
This is a bit of a peculiar thing, because normally character special files correspond to hardware devices such as serial ports. However, there is no hardware corresponding to /dev/pts/1
; it is a "pseudo-terminal". When a terminal window is opened, a new pseudo-terminal will appear in /dev/pts
that will disappear again once the window is closed. Pseudo-terminals are discussed in detail below, but for now they can be thought of as virtual serial ports that allow the interactive shell and the operating system to use the familiar user interface paradigm of a user sitting in front of a character-cell terminal connected to the computer by a serial port, even in situations that bear little resemblance to the one described. For the purposes of this book, then, a "terminal" will be any device that connects to a computer through an asynchronous serial port (real or virtual) and communicates with it using the ASCII character set.
From the operating system's point of view, the terminal is the serial port and its associated device driver. Therefore, in this chapter the operation of the hardware and the implementation of the device driver are examined in some detail starting from the voltages on the wires and moving up to a discussion of the protocols for grouping serial bits into characters.
The serial part of "asynchronous serial communications" is easy to explain: it refers to the fact that the data are represented by voltages driven serially on one physical wire. This is as opposed to a parallel interface, which would transmit bits on more than one physical wire simultaneously. To say that the voltages are driven serially simply means that to transmit a character the transmitter must assert the voltages corresponding to each bit of the character sequentially in time. It is up to the receiver to reassemble them into the original character.
When a 1 bit is driven onto the wire the corresponding voltage is referred to as a "mark", and the voltage corresponding to a 0 bit is a "space". The RS-232C standard specifies the following voltages
Mark | Space | |
Transmit | -5V to -15V | +5V to +15V |
Receive | -3V to -25V | +3V to +25V |
by which it is meant, for example, that a standard-compliant line driver must drive a voltage between -5 and -15 volts to represent a mark, and a standard-compliant line receiver must recognize a voltage between -3 and -25 volts to represent a mark, etc.
If the transmitter and receiver were synchronized, then the receiver would know which bit is which simply by sampling the voltage on the wire at the times when bits are expected. This is, in fact, a fairly common way of doing serial communications, and synchronous serial ports are often used in telecommunications applications (for example, T1 lines). The important difference in asynchronous serial communications is that transmitter and receiver are only synchronized during a character, but the receiver can not determine when the transmitter will start the next character based on when the last one was sent.
When a transmitter is idle, it holds a mark on its output. That is, when a character is not being transmitted, the transmit pin is at a voltage of between -5 and -15 volts. Since the receiver is not synchronized with the transmitter, it does not know in advance when transmission of the next character will start. Therefore, asynchronous transmission of a character always begins with a "start bit", which the receiver uses as a reference point for timing the bits that follow. Since the idle channel is held at the voltage corresponding to a mark, the start bit is always a space so that it can be distinguished.
The bits that follow the start bit are the data bits. In order to reconstruct the character, the receiver must know the speed that the transmitter is sending data bits and the number of data bits per character (not always 8!). For example, if the receiver receives the start bit at time t, and the bit rate is r bits per second with n bits per character, then it can sample the voltage on the wire at t + i/r for i = 1, 2, 3 ... n to determine the corresponding bits of the character.
Since there are a number of ways that things could go wrong with such a simple scheme as the one just described, two additional measures are provided to make communications more robust: parity and stop bits.
Parity means an additional bit is generated before the first data bit of every character whose value is chosen so that the total number of ones transmitted always has the same parity, either even or odd. For example, the ASCII character 'A' is represented by the seven-bit binary number 1000001 which contains two ones. If even parity is chosen, then transmitted 'A' characters will be prefixed by a zero parity bit. If odd parity is chosen then transmitted 'A' characters will be prefixed by a one parity bit. Note that parity adds a one bit overhead to the communications channel which can be avoided by choosing to use no parity at all.
Stop bits are more subtle. As was described above, an idle channel is held in a marking condition and therefore the start bit is always a space. The stop bits, on the other hand, are always marks and therefore are indistiguishable from an idle channel. The standard allows for one, one and a half or two stop bits. What this amounts to is a choice of the minimum inter-character spacing, with one bit-time being the minimum and two bit-times the maximum. In the past, two stop bits were required by hardcopy terminals whose mechanisms would otherwise not be able to keep up with the data stream, but it is rare to use more than one stop bit today.
Setting | Possible values |
Data rate | many |
Data bits | 5, 6, 7, 8 |
Stop bits | 1, 1.5, 2 |
Parity | even, odd |
Things often go wrong on an asynchronous serial communications channel. However, it may be possible for the receiver to determine that an error has occurred in many situations. Three types of errors that are commonly detected are overrun, framing and parity.
An overrun means that the receiver's FIFO was full when a new character was received, forcing it to clobber existing data in order to store it. This is not a failure of the communications channel per se, but means that the computer isn't keeping up with the rate of data being received.
A framing error means that the receiver recognized a start bit, but did not find a valid stop bit when it was expected. This typically means that the intra-character timing was not synchronized between transmitter and receiver, possibly meaning that either the data rate or data bits settings do not match on transmitter and receiver.
A parity error simply means that the number of ones in a received character plus the parity bit did not match the setting specified.
There is an "out of band" signal that can be transmitted by a serial port, namely, a break. A break is sent by holding the transmit wire in a spacing condition for a duration of at least two character times plus three bit times (as specified by the CCITT "blue book"). In other words, the transmitter holds the voltage corresponding to a zero bit for a length of time that is definitely longer than the transmission time for a single character. The receiver, therefore, can recognize a break since it receives more than a character's worth of spaces with no stop bit (which are always marks, see ) when one would be expected.
Normally, recognizing breaks is the responsibility of the UART (see ), but if for some reason (such as a limited UART that does not implement this functionality) the UART fails to do so, reception of a break will manifest itself as a large number of framing errors (see ).
Breaks are used to signal an interrupt to the receiving process (see ).
The serial communications scheme described so far only requires three wires to connect the transmitter and receiver: at either end there must be one wire for transmitting, one wire for receiving and one to carry a ground reference for the transmit and receive voltages. In practice, serial communications are often done with just these three wires. However, the standard provides for a number of ancillary signals for supporting the most common serial communications application: serial communications mediated by modems over the public telephone network. These are known collectively as the modem control signals.
The situation envisioned by the standard is that of a terminal or a computer (called "Data Terminal Equipment" or DTE) connected to a modem (called "Data Communications Equipment" or DCE) by an asynchronous serial channel. The modem is connected to the telephone network, and should be able to originate and/or receive telephone calls to/from other modems. The modem control signals are listed in the table below, and typically external modems have LEDs that display the condition of most of these signals at any time.
Acronym | Full name | Comes From |
DTR | Data Terminal Ready | DTE |
DSR | Data Set Ready | DCE |
RTS | Request To Send | DTE |
CTS | Clear To Send | DCE |
DCD | Data Carrier Detect | DCE |
RI | Ring Indicator | DCE |
So the normal course of events when originating a call is
and when receiving a call
Like the transmit and received data signals, the modem control signals are held at -5 to -15 volts when idle, and +5 to +15 volts when asserted. These are the same voltages used on the data lines (TXD and RXD) to represent mark and space, respectively (see ). Therefore a binary zero and a control signal being asserted are both represented by the same voltage; in this sense the control signals can be said to use "complementary" or "negative" logic.
The inclusion of the modem control signals causes the number of wires to proliferate beyond the minimal three-wire configuration discussed above. In fact, most serial ports have either nine or 25 pins, with the signals assigned to pins as shown in the following table.
Signal Name | Acronym | 25-pin | 9-pin |
Protective ground | 1 | N/A | |
Transmitted data | TXD | 2 | 3 |
Received data | RXD | 3 | 2 |
Ready To Send | RTS | 4 | 7 |
Clear To Send | CTS | 5 | 8 |
Data Set Ready | DSR | 6 | 6 |
Signal ground | SG | 7 | 5 |
Data Carrier Detect | DCD | 8 | 1 |
Data Terminal Ready | DTR | 20 | 4 |
Ring Indicator | RI | 22 | 9 |
Note that the signal names are chosen from the perspective of a terminal (DTE); for example, a modem will transmit data to the terminal on RXD and receive data from the terminal on TXD.
Furthermore, if two DTEs are to be directly connected to each other without modems or telephones (such as the terminal and the Linux box in the introduction), the cable that connects them must interchange signals since, for example, both will want to transmit on pin 2 of a 25-pin connector and receive on pin 3. The cable must connect pin 2 of one connector to pin 3 of the other and vice versa; such a cable is called a "null modem" cable.
A null modem cable should also rearrange the modem control signals apropriately: RTS at one end should connect to CTS at the other, and DTR at one end should connect to DSR at the other. The DCD signal is usually connected to DSR at each end (and therefore to DTR at the opposite end).
TXD--/--TXD RXD--/--RXD RTS--/--RTS CTS--/--CTS DSR--+---DTR DCD--| DTR---+--DSR |--DCD
The hardware in a serial port that handles serial communications is called a UART, an acronym which stands for "Universal Asynchronous Receiver Transmitter". The UART is the device that the serial device driver drives. It is responsible for transmitting and receiving data as well as implementing all of the ancillary settings including bit rate, bits per character, parity, stop bits and modem control signals.
The UART does not directly interface with the pins on the serial port. It is usually designed to operate at much lower voltages (3.3 or 5) and is interfaced to the pins by line drivers and receivers (such as the DS1488 and DS1489) that convert between the RS-232C voltages and the UART voltage.
Important examples of UARTs include the Intel 8251, a very early UART that was used in Digital Equipment Corporation's VT100 terminals, and National Semiconductor's 16550A which is probably the most popular UART on the market today.
So far, the discussion has not touched at all on the software needed to support an asynchronous serial port. In this chapter, the bottom up approach continues with a detailed look at the operating system components that provide user level processes access to the hardware. Two characters play prominent roles in this part of the story: the serial device driver and the GNU C Library.
The serial device driver is a component of the operating system kernel (in Linux, it is frequently implemented as a loadable module), and implements the hardware-specific aspects of the software needed to use the serial port. For example, there are a variety of multiport serial cards available on the market, and no two will have the same register map unless they share a chipset. Therefore each will have a different device driver, all of which present the same interface to the operating system but perform different operations on the hardware.
A large part of any device driver is usually devoted to implementing "methods" that do the real work of system calls. System calls are the way the kernel provides services to user space programs in a safe and controlled way. When a system call is executed by a user space program, it invokes a "trap" causing the CPU to context switch into the privileged kernel mode and thereby gain access to registers, I/O ports and protected memory regions that are normally off-limits to user space programs. The kernel then passes (and possibly rearranges) the system call parameters to the device driver method that was registered for that call when the driver was loaded.
This operation is fundamentally different from the execution of a library function. Execution of a library function does not imply a context switch nor a change in CPU privilege; it is in almost every way the same a executing a normal call to a function. However, most of the interesting functions in the GNU C Library are implemented by executing system calls.
The Unix approach is to make an analogy between a hardware device such as a serial port and a file. A file is something that you can open, read, write and close with some expectation that the next time you open and read it you will find the last thing that was written there. Obviously this expectation cannot be extended to serial ports, but it still seems reasonable to read and write to them. Data read by a process from a serial port is what was received, and data written by a process to a serial port is transmitted.
Opening and closing a serial port is a bit more problematic. Since a serial port isn't really a file, it may seem a bit counterintuitive to open an entry in the filesystem in order to gain access to it. However, this is precisely what is done for hardware devices, except that the corresponding "special files" are conventionally found in the directory /dev
.
Special files are created by the mknod(1)
command and come in two flavors: block and character. Block devices can only be read from or written to one block (for example, 1024 bytes) at a time and are usually associated with disk drives, whereas character devices operate one character at a time. Since an asynchronous serial port always transmits or receives one character at a time, they are always character devices.
Special files also have major and minor numbers. The major number identifies to the operating system which device driver will service system calls on behalf of the special file, and the minor number identifies to the device driver one of the possibly several hardware devices that it manages on behalf of which it must act. For example, a multiport serial card would be managed by a single device driver but provides several serial ports. Each serial port would have a correpsonding special file with the same major number but a unique minor number. The minor number enables the device driver to distinguish which serial port has been opened, read from, written to, etc.
In fact, most serial ports are associated with two special files with different major but the same minor numbers. For example, both /dev/ttyS0
and /dev/cua0
are associated with the first UART serial port. Since device special files are a mechanism for extending the device/file analogy to the open(2)
and close(2)
system calls, different special files for the same device must correspond to different behaviors on one or both of these system calls. In fact, it is the open(2)
system call that behaves differently depending on which of the two special files is opened.
Recall that the DCD modem control signal (see ) is an indication to a DTE from a DCE that a dialed call has been completed and the opposite modem's carrier signal detected. If a program opens a serial port expecting to receive a call, it makes sense for the operating system to put that process to sleep until the modem asserts DCD since there is nothing for it to do before a call is established. On the other hand, if a program opens a serial port for purposes of placing a call, then it must have access to the modem before DCD is asserted in order to direct it to dial the number. This is precisely the difference between /dev/ttyS0
and /dev/cua0
: a program that opens /dev/ttyS0
will block in the system call until DCD is asserted whereas it will not block if it opens /dev/cua0
. For this reason, /dev/cua0
is often referred to as the "callout device."
Note that the blocking behavior of /dev/ttyS0
can be modified by setting the CLOCAL control mode flag, which causes the modem status lines to be ignored. This flag indicates that the terminal associated with the serial port is connected "locally", as opposed to remotely via a modem.
ioctl
and termios
Special files are the method used to extend the analogy between files and devices to the open(2)
and close(2)
system calls; however, it is not at all clear how to express operations such as changing the bit rate of a serial port or enabling hardware flow control in terms of operations that can also be done on a file. In fact, an addiontal system call, ioctl(2)
that exists for the purpose of providing a mechanism for implementing these sorts of ancillary "I/O Control" functions.
Because of the central role played by serial ports in user interaction, the serial device driver provides a much richer set of ioctl(2)
settings than might be expected for a device as simple as the UART. In fact, many of these settings control things like character substitution and line buffering that have nothing to do with the UART per se. Nonetheless, a subset of the serial ioctl(2)
s have a one-to-one correspondence with UART settings like bit rate, character size, parity, modem control signals, etc.
These days it is rare and considered bad form (for portability reasons) to use the ioctl(2)
system call on a serial port directly. Instead, the GNU C Library provides a set of mediating functions defined by the POSIX standard and declared in the header file
. Full details on these functions can be found in .
The
functions are passed a data structure called struct termios
that contains the following members
tcflag_t c_iflag; /* input modes */ tcflag_t c_oflag; /* output modes */ tcflag_t c_cflag; /* control modes */ tcflag_t c_lflag; /* local modes */ cc_t c_cc[NCCS]; /* control chars */
The first four members are bit masks in which programs can set and clear flags to specify certain behaviors. The UART settings can be directly mapped onto flags in the third bit mask, the control modes.
termios
Control ModesAs an example of how to use
functions to configure UART settings, let us see how the control modes settings are used to configure a serial port to operate at 9600 bps, with 8-bit characters, no parity and one stop bit. (This is a very common serial port setting, usually abbreviated to something like "9600 8n1".) Usually, 9600 bps is slow enough that flow control is not needed, either.
Because of the plethora of terminal settings available, the recommended method for changing them is to first read the current settings into a struct termios
and then set and clear bits to make the necessary changes. That way, the settings that we haven't touched will remain at reasonable default values (hopefully). The following code reads the current settings for the serial port /dev/ttyS0
into a struct termios
called tios
.
#include#include #include #include #include #include int main(int argc, char *argv[]) { struct termios tios; int fd; fd = open("/dev/ttyS0", O_RDWR); if(fd < 0) { perror("open failed: "); return 1; } if(tcgetattr(fd, &tios) < 0) { perror("tcgetattr failed: "); return 2; }
From here, we go about changing the settings one at a time until we arrive at "9600 8n1".
Unlike the other settings show below, the bit rate is not set by directly setting and clearing flags in tios.c_cflag
. Instead, a function cfsetspeed
is provided to diddle the bits for us.
cfsetspeed(&tios, B9600);
Note that we use the preprocessor symbol B9600
, not the number 9600.
There are four possible settings for the number of bits per character: 5, 6, 7 and 8 (see ). The GNU C Library defines five macros for accessing the corresponding bits in the control modes bitmask, namely CS5
, CS6
, CS7
, CS8
and CSIZE
. To set the number of bits per character you first clear all of the bits with the CSIZE
macro and then set the bits corresponding to the size you want. To set eight bits per character
tios.c_cflag &= ~CSIZE; tios.c_cflag |= CS8;
There are three possible settings for parity: even, odd and none (see ). The GNU C Library defines two macros for accessing the corresponding bits in the control modes bitmask, namely PARENB
and PARODD
. The former enables and disable parity, the latter chooses odd parity (if parity is enabled and PARODD
is cleared, then even parity is generated). Note that these flags affect both transmitter and receiver; for example, if both flags are set then odd parity will be generated by the transmitter and if a received character has an even number of bits the receiver will generate a parity error.
To configure for no parity
tios.c_cflag &= ~(PARENB | PARODD);
In the GNU C Library, there are two possible settings for the number of stop bits: one and two (see ). If the CSTOPB
flag is set, two stop bits are used, and only one is used if it is cleared. The following example shows how to configure a serial port for one stop bit.
tios.c_cflag &= ~CSTOPB;
The flow control signals are the RTS and CTS modem control signals that can be used to gate the flow of data in and out of a serial port (see ). Flow control can be enabled and disabled by setting and clearing the CRTSCTS
flag. The following example shows how to configure a serial port for no flow control.
tios.c_cflag &= ~CRTSCTS;
Finally, we can apply our changes using the tcsetattr
function
tcsetattr(fd, TCSANOW, &tios);
Note that this function returns 0, indicating success, if any of the changes could be carried out.
termios
Input/Output ModesThe control mode settings are the only ones that directly affect the UART hardware, but there are numerous other settings that control the way the serial device driver and kernel TTY layers will process I/O on the device. At the lowest software level, these settings control how parity errors and breaks are handled and character substitution.
If the hardware is configured to generate parity bits on transmitted characters and to check parity on received characters (i.e., the PARENB
flag is set in the control modes, see ), the software must still decide what to do when parity errors are reported on received characters. There are four choices for how to handle characters containing parity errors: pass it through exactly as received (ignore the error), drop it (ignore the character), mark it as containing an error, or replace it with something else. These behaviors are controlled by flags in the terminal input modes, c_iflag
, namely INPCK
, IGNPAR
and PARMRK
.
tios.c_iflag &= ~INPCK;The UART will still generate parity bits on transmitted characters as long as the
PARENB
bit in the control modes is set, but input characters with parity errors will be passed on as they were received by the device driver. tios.c_iflag |= (INPCK | IGNPAR); tios.c_iflag &= ~PARMRK;Any received characters containing parity errors will be silently dropped.
tios.c_iflag |= (INPCK | PARMRK); tios.c_iflag &= ~IGNPAR;This will cause a character containing a parity or framing error to be replaced by a three character sequence consiting of the erroneous charcter preceded by
377
and