Thursday, May 29, 2008

Serial Programming Guide for POSIX Operating Systems

Serial Programming Guide for POSIX Operating Systems




Table of Contents



Introduction

Chapter 1, Basics of Serial Communications
  • What Are Serial Communications?
  • What Is RS-232?
    • Signal Definitions
  • Asynchronous Communications
    • What Are Full Duplex and Half Duplex?
    • Flow Control
    • What Is a Break?
  • Synchronous Communications
  • Accessing Serial Ports
    • Serial Port Files
    • Opening a Serial Port
    • Writing Data to the Port
    • Reading Data from the Port
    • Closing a Serial Port
Chapter 2, Configuring the Serial Port
  • The POSIX Terminal Interface
    • Control Options
    • Local Options
    • Input Options
    • Output Options
    • Control Characters
Chapter 3, MODEM Communications
  • What Is a MODEM?
  • Communicating With a MODEM
    • Standard MODEM Commands
    • Common MODEM Communication Problems
Chapter 4, Advanced Serial Programming
  • Serial Port IOCTLs
    • Getting the Control Signals
    • Setting the Control Signals
    • Getting the Number of Bytes Available
  • Selecting Input from a Serial Port
    • The SELECT System Call
    • Using the SELECT System Call
    • Using SELECT with the X Intrinsics Library
Appendix A, Pinouts
  • RS-232 Pinouts
  • RS-422 Pinouts
  • RS-574 (IBM PC/AT) Pinouts
  • SGI Pinouts
Appendix B, ASCII Control Codes
  • Control Codes

Introduction

The Serial Programming Guide for POSIX Operating Systems will teach you how to successfully, efficiently, and portably program the serial ports on your UNIX® workstation or PC. Each chapter provides programming examples that use the POSIX (Portable Standard for UNIX) terminal control functions and should work with very few modifications under IRIX®, HP-UX, SunOS®, Solaris®, Digital UNIX®, Linux®, and most other UNIX operating systems. The biggest difference between operating systems that you will find is the filenames used for serial port device and lock files.

This guide is organized into the following chapters and appendices:

  • Chapter 1, Basics of Serial Programming
  • Chapter 2, Configuring the Serial Port
  • Chapter 3, Talking to MODEMs
  • Chapter 4, Advanced Serial Programming
  • Appendix A, RS-232 Pinouts
  • Appendix B, ASCII Control Codes

Chapter 1, Basics of Serial Communications

This chapter introduces serial communications, RS-232 and other standards that are used on most computers as well as how to access a serial port from a C program.

What Are Serial Communications?

Computers transfer information (data) one or more bits at a time. Serial refers to the transfer of data one bit at a time. Serial communications include most network devices, keyboards, mice, MODEMs, and terminals.

When doing serial communications each word (i.e. byte or character) of data you send or receive is sent one bit at a time. Each bit is either on or off. The terms you'll hear sometimes are mark for the on state and space for the off state.

The speed of the serial data is most often expressed as bits-per-second ("bps") or baudot rate ("baud"). This just represents the number of ones and zeroes that can be sent in one second. Back at the dawn of the computer age, 300 baud was considered fast, but today computers can handle RS-232 speeds as high as 430,800 baud! When the baud rate exceeds 1,000, you'll usually see the rate shown in kilo baud, or kbps (e.g. 9.6k, 19.2k, etc). For rates above 1,000,000 that rate is shown in megabaud, or Mbps (e.g. 1.5Mbps).

When referring to serial devices or ports, they are either labeled as Data Communications Equipment ("DCE") or Data Terminal Equipment ("DTE"). The difference between these is simple - every signal pair, like transmit and receive, is swapped. When connecting two DTE or two DCE interfaces together, a serial null-MODEM cable or adapter is used that swaps the signal pairs.

What Is RS-232?

RS-232 is a standard electrical interface for serial communications defined by the Electronic Industries Association ("EIA"). RS-232 actually comes in 3 different flavors (A, B, and C) with each one defining a different voltage range for the on and off levels. The most commonly used variety is RS-232C, which defines a mark (on) bit as a voltage between -3V and -12V and a space (off) bit as a voltage between +3V and +12V. The RS-232C specification says these signals can go about 25 feet (8m) before they become unusable. You can usually send signals a bit farther than this as long as the baud is low enough.

Besides wires for incoming and outgoing data, there are others that provide timing, status, and handshaking:

Table 1 - RS-232 Pin Assignments
PinDescriptionPinDescription PinDescriptionPinDescriptionPin Description
1Earth Ground6DSR - Data Set Ready11Unassigned16Secondary RXD 21Signal Quality Detect
2TXD - Transmitted Data7GND - Logic Ground12Secondary DCD17 Receiver Clock22Ring Detect
3RXD - Received Data8DCD - Data Carrier Detect13Secondary CTS18 Unassigned23Data Rate Select
4RTS - Request To Send9 Reserved14Secondary TXD19Secondary RTS24Transmit Clock
5CTS - Clear To Send10Reserved 15Transmit Clock20DTR - Data Terminal Ready25Unassigned

Two standards for serial interfaces you may also see are RS-422 and RS-574. RS-422 uses lower voltages and differential signals to allow cable lengths up to about 1000ft (300m). RS-574 defines the 9-pin PC serial connector and voltages.

Signal Definitions

The RS-232 standard defines some 18 different signals for serial communications. Of these, only six are generally available in the UNIX environment.

GND - Logic Ground

Technically the logic ground is not a signal, but without it none of the other signals will operate. Basically, the logic ground acts as a reference voltage so that the electronics know which voltages are positive or negative.

TXD - Transmitted Data

The TXD signal carries data transmitted from your workstation to the computer or device on the other end (like a MODEM). A mark voltage is interpreted as a value of 1, while a space voltage is interpreted as a value of 0.

RXD - Received Data

The RXD signal carries data transmitted from the computer or device on the other end to your workstation. Like TXD, mark and space voltages are interpreted as 1 and 0, respectively.

DCD - Data Carrier Detect

The DCD signal is received from the computer or device on the other end of your serial cable. A space voltage on this signal line indicates that the computer or device is currently connected or on line. DCD is not always used or available.

DTR - Data Terminal Ready

The DTR signal is generated by your workstation and tells the computer or device on the other end that you are ready (a space voltage) or not-ready (a mark voltage). DTR is usually enabled automatically whenever you open the serial interface on the workstation.

CTS - Clear To Send

The CTS signal is received from the other end of the serial cable. A space voltage indicates that is alright to send more serial data from your workstation.

CTS is usually used to regulate the flow of serial data from your workstation to the other end.

RTS - Request To Send

The RTS signal is set to the space voltage by your workstation to indicate that more data is ready to be sent.

Like CTS, RTS helps to regulate the flow of data between your workstation and the computer or device on the other end of the serial cable. Most workstations leave this signal set to the space voltage all the time.

Asynchronous Communications

For the computer to understand the serial data coming into it, it needs some way to determine where one character ends and the next begins. This guide deals exclusively with asynchronous serial data.

In asynchronous mode the serial data line stays in the mark (1) state until a character is transmitted. A start bit preceeds each character and is followed immediately by each bit in the character, an optional parity bit, and one or more stop bits. The start bit is always a space (0) and tells the computer that new serial data is available. Data can be sent or received at any time, thus the name asynchronous.

Figure 1 - Asynchronous Data Transmission

The optional parity bit is a simple sum of the data bits indicating whether or not the data contains an even or odd number of 1 bits. With even parity, the parity bit is 0 if there is an even number of 1's in the character. With odd parity, the parity bit is 0 if there is an odd number of 1's in the data. You may also hear the terms space parity, mark parity, and no parity. Space parity means that the parity bit is always 0, while mark parity means the bit is always 1. No parity means that no parity bit is present or transmitted.

The remaining bits are called stop bits. There can be 1, 1.5, or 2 stop bits between characters and they always have a value of 1. Stop bits traditionally were used to give the computer time to process the previous character, but now only serve to synchronize the receiving computer to the incoming characters.

Asynchronous data formats are usually expressed as "8N1", "7E1", and so forth. These stand for "8 data bits, no parity, 1 stop bit" and "7 data bits, even parity, 1 stop bit" respectively.

What Are Full Duplex and Half Duplex?

Full duplex means that the computer can send and receive data simultaneously - there are two separate data channels (one coming in, one going out).

Half duplex means that the computer cannot send or receive data at the same time. Usually this means there is only a single data channel to talk over. This does not mean that any of the RS-232 signals are not used. Rather, it usually means that the communications link uses some standard other than RS-232 that does not support full duplex operation.

Flow Control

It is often necessary to regulate the flow of data when transferring data between two serial interfaces. This can be due to limitations in an intermediate serial communications link, one of the serial interfaces, or some storage media. Two methods are commonly used for asynchronous data.

The first method is often called "software" flow control and uses special characters to start (XON or DC1, 021 octal) or stop (XOFF or DC3, 023 octal) the flow of data. These characters are defined in the American Standard Code for Information Interchange ("ASCII"). While these codes are useful when transferring textual information, they cannot be used when transferring other types of information without special programming.

The second method is called "hardware" flow control and uses the RS-232 CTS and RTS signals instead of special characters. The receiver sets CTS to the space voltage when it is ready to receive more data and to the mark voltage when it is not ready. Likewise, the sender sets RTS to the space voltage when it is ready to send more data. Because hardware flow control uses a separate set of signals, it is much faster than software flow control which needs to send or receive multiple bits of information to do the same thing. CTS/RTS flow control is not supported by all hardware or operating systems.

What Is a Break?

Normally a receive or transmit data signal stays at the mark voltage until a new character is transferred. If the signal is dropped to the space voltage for a long period of time, usually 1/4 to 1/2 second, then a break condition is said to exist.

A break is sometimes used to reset a communications line or change the operating mode of communications hardware like a MODEM. Chapter 3, Talking to MODEMs covers these applications in more depth.

Synchronous Communications

Unlike asynchronous data, synchronous data appears as a constant stream of bits. To read the data on the line, the computer must provide or receive a common bit clock so that both the sender and receiver are synchronized.

Even with this synchronization, the computer must mark the beginning of the data somehow. The most common way of doing this is to use a data packet protocol like Serial Data Link Control ("SDLC") or High-Speed Data Link Control ("HDLC").

Each protocol defines certain bit sequences to represent the beginning and end of a data packet. Each also defines a bit sequence that is used when there is no data. These bit sequences allow the computer see the beginning of a data packet.

Because synchronous protocols do not use per-character synchronization bits they typically provide at least a 25% improvement in performance over asynchronous communications and are suitable for remote networking and configurations with more than two serial interfaces.

Despite the speed advantages of synchronous communications, most RS-232 hardware does not support it due to the extra hardware and software required.

Accessing Serial Ports

Like all devices, UNIX provides access to serial ports via device files. To access a serial port you simply open the corresponding device file.

Serial Port Files

Each serial port on a UNIX system has one or more device files (files in the /dev directory) associated with it:

Table 2 - Serial Port Device Files
SystemPort 1Port 2
IRIX®/dev/ttyf1/dev/ttyf2
HP-UX/dev/tty1p0/dev/tty2p0
Solaris®/SunOS®/dev/ttya/dev/ttyb
Linux®/dev/ttyS0/dev/ttyS1
Digital UNIX®/dev/tty01/dev/tty02

Opening a Serial Port

Since a serial port is a file, the open(2) function is used to access it. The one hitch with UNIX is that device files are usually not accessable by normal users. Workarounds include changing the access permissions to the file(s) in question, running your program as the super-user (root), or making your program set-userid so that it runs as the owner of the device file.

For now we'll assume that the file is accessable by all users. The code to open serial port 1 on an sgi® workstation running IRIX is:

    Listing 1 - Opening a serial port.

    #include 'stdio.h'  /* Standard input/output definitions */
    #include 'string.h' /* String function definitions */
    #include 'unistd.h' /* UNIX standard function definitions */
    #include 'fcntl.h' /* File control definitions */
    #include 'errno.h' /* Error number definitions */
    #include 'termios.h' /* POSIX terminal control definitions */

    /*
    * 'open_port()' - Open serial port 1.
    *
    * Returns the file descriptor on success or -1 on error.
    */

    int
    open_port(void)
    {
    int fd; /* File descriptor for the port */


    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
    {
    /*
    * Could not open the port.
    */

    perror("open_port: Unable to open /dev/ttyf1 - ");
    }
    else
    fcntl(fd, F_SETFL, 0);

    return (fd);
    }

Other systems would require the corresponding device file name, but otherwise the code is the same.

Open Options

You'll notice that when we opened the device file we used two other flags along with the read+write mode:

    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);

The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs like getty(1M/8) use this feature when starting the login process, but normally a user program does not want this behavior.

The O_NDELAY flag tells UNIX that this program doesn't care what state the DCD signal line is in - whether the other end of the port is up and running. If you do not specify this flag, your process will be put to sleep until the DCD signal line is the space voltage.

Writing Data to the Port

Writing data to the port is easy - just use the write(2) system call to send data it:

    n = write(fd, "ATZ\r", 4);
    if (n < 0)
    fputs("write() of 4 bytes failed!\n", stderr);

The write function returns the number of bytes sent or -1 if an error occurred. Usually the only error you'll run into is EIO when a MODEM or data link drops the Data Carrier Detect (DCD) line. This condition will persist until you close the port.

Reading Data from the Port

Reading data from a port is a little trickier. When you operate the port in raw data mode, each read(2) system call will return however many characters are actually available in the serial input buffers. If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs. The read function can be made to return immediately by doing the following:

    fcntl(fd, F_SETFL, FNDELAY);

The FNDELAY option causes the read function to return 0 if no characters are available on the port. To restore normal (blocking) behavior, call fcntl() without the FNDELAY option:

    fcntl(fd, F_SETFL, 0);

This is also used after opening a serial port with the O_NDELAY option.

Closing a Serial Port

To close the serial port, just use the close system call:

    close(fd);

Closing a serial port will also usually set the DTR signal low which causes most MODEMs to hang up.


Chapter 2, Configuring the Serial Port

This chapter discusses how to configure a serial port from C using the POSIX termios interface.

The POSIX Terminal Interface

Most systems support the POSIX terminal (serial) interface for changing parameters such as baud rate, character size, and so on. The first thing you need to do is include the file 'termios.h>'; this defines the terminal control structure as well as the POSIX control functions.

The two most important POSIX functions are tcgetattr(3) and tcsetattr(3). These get and set terminal attributes, respectively; you provide a pointer to a termios structure that contains all of the serial options available:

Table 3 - Termios Structure Members
MemberDescription
c_cflagControl options
c_lflagLine options
c_iflagInput options
c_oflagOutput options
c_ccControl characters
c_ispeedInput baud (new interface)
c_ospeedOutput baud (new interface)

Control Options

The c_cflag member controls the baud rate, number of data bits, parity, stop bits, and hardware flow control. There are constants for all of the supported configurations.
Table 4 - Constants for the c_cflag Member
ConstantDescription
CBAUDBit mask for baud rate
B00 baud (drop DTR)
B5050 baud
B7575 baud
B110110 baud
B134134.5 baud
B150150 baud
B200200 baud
B300300 baud
B600600 baud
B12001200 baud
B18001800 baud
B24002400 baud
B48004800 baud
B96009600 baud
B1920019200 baud
B3840038400 baud
B5760057,600 baud
B7680076,800 baud
B115200115,200 baud
EXTAExternal rate clock
EXTBExternal rate clock
CSIZEBit mask for data bits
CS55 data bits
CS66 data bits
CS77 data bits
CS88 data bits
CSTOPB2 stop bits (1 otherwise)
CREADEnable receiver
PARENBEnable parity bit
PARODDUse odd parity instead of even
HUPCLHangup (drop DTR) on last close
CLOCALLocal line - do not change "owner" of port
LOBLKBlock job control output
CNEW_RTSCTS
CRTSCTS
Enable hardware flow control (not supported on all platforms)

The c_cflag member contains two options that should always be enabled, CLOCAL and CREAD. These will ensure that your program does not become the 'owner' of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes.

The baud rate constants (CBAUD, B9600, etc.) are used for older interfaces that lack the c_ispeed and c_ospeed members. See the next section for information on the POSIX functions used to set the baud rate.

Never initialize the c_cflag (or any other flag) member directly; you should always use the bitwise AND, OR, and NOT operators to set or clear bits in the members. Different operating system versions (and even patches) can and do use the bits differently, so using the bitwise operators will prevent you from clobbering a bit flag that is needed in a newer serial driver.

Setting the Baud Rate

The baud rate is stored in different places depending on the operating system. Older interfaces store the baud rate in the c_cflag member using one of the baud rate constants in table 4, while newer implementations provide the c_ispeed and c_ospeed members that contain the actual baud rate value.

The cfsetospeed(3) and cfsetispeed(3) functions are provided to set the baud rate in the termios structure regardless of the underlying operating system interface. Typically you'd use the following code to set the baud rate:

    Listing 2 - Setting the baud rate.

    struct termios options;

    /*
    * Get the current options for the port...
    */

    tcgetattr(fd, &options);

    /*
    * Set the baud rates to 19200...
    */

    cfsetispeed(&options, B19200);
    cfsetospeed(&options, B19200);

    /*
    * Enable the receiver and set local mode...
    */

    options.c_cflag |= (CLOCAL | CREAD);

    /*
    * Set the new options for the port...
    */

    tcsetattr(fd, TCSANOW, &options);

The tcgetattr(3) function fills the termios structure you provide with the current serial port configuration. After we set the baud rates and enable local mode and serial data receipt, we select the new configuration using tcsetattr(3). The TCSANOW constant specifies that all changes should occur immediately without waiting for output data to finish sending or input data to finish receiving. There are other constants to wait for input and output to finish or to flush the input and output buffers.

Most systems do not support different input and output speeds, so be sure to set both to the same value for maximum portability.

Table 5 - Constants for tcsetattr
ConstantDescription
TCSANOWMake changes now without waiting for data to complete
TCSADRAINWait until everything has been transmitted
TCSAFLUSHFlush input and output buffers and make the change

Setting the Character Size

Unlike the baud rate, there is no convienience function to set the character size. Instead you must do a little bitmasking to set things up. The character size is specified in bits:

    options.c_cflag &= ~CSIZE; /* Mask the character size bits */
    options.c_cflag |= CS8; /* Select 8 data bits */

Setting Parity Checking

Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.

  • No parity (8N1):
  • options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
  • Even parity (7E1):
  • options.c_cflag |= PARENB
    options.c_cflag &= ~PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
  • Odd parity (7O1):
  • options.c_cflag |= PARENB
    options.c_cflag |= PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
  • Space parity is setup the same as no parity (7S1):
  • options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

Setting Hardware Flow Control

Some versions of UNIX support hardware flow control using the CTS (Clear To Send) and RTS (Request To Send) signal lines. If the CNEW_RTSCTS or CRTSCTS constants are defined on your system then hardware flow control is probably supported. Do the following to enable hardware flow control:

    options.c_cflag |= CNEW_RTSCTS;    /* Also called CRTSCTS */

Similarly, to disable hardware flow control:

    options.c_cflag &= ~CNEW_RTSCTS;

Local Options

The local modes member c_lflag controls how input characters are managed by the serial driver. In general you will configure the c_lflag member for canonical or raw input.

Table 6 - Constants for the c_lflag Member
ConstantDescription
ISIGEnable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
ICANONEnable canonical input (else raw)
XCASEMap uppercase \lowercase (obsolete)
ECHOEnable echoing of input characters
ECHOEEcho erase character as BS-SP-BS
ECHOKEcho NL after kill character
ECHONLEcho NL
NOFLSHDisable flushing of input buffers after interrupt or quit characters
IEXTENEnable extended functions
ECHOCTLEcho control characters as ^char and delete as ~?
ECHOPRTEcho erased character as character erased
ECHOKEBS-SP-BS entire line on line kill
FLUSHOOutput being flushed
PENDINRetype pending input at next read or input char
TOSTOPSend SIGTTOU for background output

Choosing Canonical Input

Canonical input is line-oriented. Input characters are put into a buffer which can be edited interactively by the user until a CR (carriage return) or LF (line feed) character is received.

When selecting this mode you normally select the ICANON, ECHO, and ECHOE options:

    options.c_lflag |= (ICANON | ECHO | ECHOE);

Choosing Raw Input

Raw input is unprocessed. Input characters are passed through exactly as they are received, when they are received. Generally you'll deselect the ICANON, ECHO, ECHOE, and ISIG options when using raw input:

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

A Note About Input Echo

Never enable input echo (ECHO, ECHOE) when sending commands to a MODEM or other computer that is echoing characters, as you will generate a feedback loop between the two serial interfaces!

Input Options

The input modes member c_iflag controls any input processing that is done to characters received on the port. Like the c_cflag field, the final value stored in c_iflag is the bitwise OR of the desired options.

Table 7 - Constants for the c_iflag Member
ConstantDescription
INPCKEnable parity check
IGNPARIgnore parity errors
PARMRKMark parity errors
ISTRIPStrip parity bits
IXONEnable software flow control (outgoing)
IXOFFEnable software flow control (incoming)
IXANYAllow any character to start flow again
IGNBRKIgnore break condition
BRKINTSend a SIGINT when a break condition is detected
INLCRMap NL to CR
IGNCRIgnore CR
ICRNLMap CR to NL
IUCLCMap uppercase to lowercase
IMAXBELEcho BEL on input line too long

Setting Input Parity Options

You should enable input parity checking when you have enabled parity in the c_cflag member (PARENB). The revelant constants for input parity checking are INPCK, IGNPAR, PARMRK , and ISTRIP. Generally you will select INPCK and ISTRIP to enable checking and stripping of the parity bit:

    options.c_iflag |= (INPCK | ISTRIP);

IGNPAR is a somewhat dangerous option that tells the serial driver to ignore parity errors and pass the incoming data through as if no errors had occurred. This can be useful for testing the quality of a communications link, but in general is not used for practical reasons.

PARMRK causes parity errors to be 'marked' in the input stream using special characters. If IGNPAR is enabled, a NUL character (000 octal) is sent to your program before every character with a parity error. Otherwise, a DEL (177 octal) and NUL character is sent along with the bad character.

Setting Software Flow Control

Software flow control is enabled using the IXON, IXOFF , and IXANY constants:

    options.c_iflag |= (IXON | IXOFF | IXANY);

To disable software flow control simply mask those bits:

    options.c_iflag &= ~(IXON | IXOFF | IXANY);

The XON (start data) and XOFF (stop data) characters are defined in the c_cc array described below.

Output Options

The c_oflag member contains output filtering options. Like the input modes, you can select processed or raw data output.

Table 8 - Constants for the c_oflag Member
ConstantDescription
OPOSTPostprocess output (not set = raw output)
OLCUCMap lowercase to uppercase
ONLCRMap NL to CR-NL
OCRNLMap CR to NL
NOCRNo CR output at column 0
ONLRETNL performs CR function
OFILLUse fill characters for delay
OFDELFill character is DEL
NLDLYMask for delay time needed between lines
NL0No delay for NLs
NL1Delay further output after newline for 100 milliseconds
CRDLYMask for delay time needed to return carriage to left column
CR0No delay for CRs
CR1Delay after CRs depending on current column position
CR2Delay 100 milliseconds after sending CRs
CR3Delay 150 milliseconds after sending CRs
TABDLYMask for delay time needed after TABs
TAB0No delay for TABs
TAB1Delay after TABs depending on current column position
TAB2Delay 100 milliseconds after sending TABs
TAB3Expand TAB characters to spaces
BSDLYMask for delay time needed after BSs
BS0No delay for BSs
BS1Delay 50 milliseconds after sending BSs
VTDLYMask for delay time needed after VTs
VT0No delay for VTs
VT1Delay 2 seconds after sending VTs
FFDLYMask for delay time needed after FFs
FF0No delay for FFs
FF1Delay 2 seconds after sending FFs

Choosing Processed Output

Processed output is selected by setting the OPOST option in the c_oflag member:

    options.c_oflag |= OPOST;

Of all the different options, you will only probably use the ONLCR option which maps newlines into CR-LF pairs. The rest of the output options are primarily historic and date back to the time when line printers and terminals could not keep up with the serial data stream!

Choosing Raw Output

Raw output is selected by resetting the OPOST option in the c_oflag member:

    options.c_oflag &= ~OPOST;

When the OPOST option is disabled, all other option bits in c_oflag are ignored.

Control Characters

The c_cc character array contains control character definitions as well as timeout parameters. Constants are defined for every element of this array.

Table 9 - Control Characters in the c_cc Member
ConstantDescriptionKey
VINTRInterruptCTRL-C
VQUITQuitCTRL-Z
VERASEEraseBackspace (BS)
VKILLKill-lineCTRL-U
VEOFEnd-of-fileCTRL-D
VEOLEnd-of-lineCarriage return (CR)
VEOL2Second end-of-lineLine feed (LF)
VMINMinimum number of characters to read
VTIMETime to wait for data (tenths of seconds)

Setting Software Flow Control Characters

The VSTART and VSTOP elements of the c_cc array contain the characters used for software flow control. Normally they should be set to DC1 (021 octal) and DC3 (023 octal) which represent the ASCII standard XON and XOFF characters.

Setting Read Timeouts

UNIX serial interface drivers provide the ability to specify character and packet timeouts. Two elements of the c_cc array are used for timeouts: VMIN and VTIME. Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl.

VMIN specifies the minimum number of characters to read. If it is set to 0, then the VTIME value specifies the time to wait for every character read. Note that this does not mean that a read call for N bytes will wait for N characters to come in. Rather, the timeout will apply to the first character and the read call will return the number of characters immediately available (up to the number you request).

If VMIN is non-zero, VTIME specifies the time to wait for the first character read. If a character is read within the time given, any read will block (wait) until all VMIN characters are read. That is, once the first character is read, the serial interface driver expects to receive an entire packet of characters (VMIN bytes total). If no character is read within the time allowed, then the call to read returns 0. This method allows you to tell the serial driver you need exactly N bytes and any read call will return 0 or N bytes. However, the timeout only applies to the first character read, so if for some reason the driver misses one character inside the N byte packet then the read call could block forever waiting for additional input characters.

VTIME specifies the amount of time to wait for incoming characters in tenths of seconds. If VTIME is set to 0 (the default), reads will block (wait) indefinitely unless the NDELAY option is set on the port with open or fcntl.


Chapter 3, MODEM Communications

This chapter covers the basics of dialup telephone Modulator/Demodulator (MODEM) communications. Examples are provided for MODEMs that use the defacto standard "AT" command set.

What Is a MODEM?

MODEMs are devices that modulate serial data into frequencies that can be transferred over an analog data link such as a telephone line or cable TV connection. A standard telephone MODEM converts serial data into tones that can be passed over the phone lines; because of the speed and complexity of the conversion these tones sound more like loud screeching if you listen to them.

Telephone MODEMs are available today that can transfer data across a telephone line at nearly 53,000 bits per second, or 53kbps. In addition, most MODEMs use data compression technology that can increase the bit rate to well over 100kbps on some types of data.

Communicating With a MODEM

The first step in communicating with a MODEM is to open and configure the port for raw input:

    Listing 3 - Configuring the port for raw input.

    int            fd;
    struct termios options;

    /* open the port */
    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
    fcntl(fd, F_SETFL, 0);

    /* get the current options */
    tcgetattr(fd, &options);

    /* set raw input, 1 second timeout */
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 10;

    /* set the options */
    tcsetattr(fd, TCSANOW, &options);

Next you need to establish communications with the MODEM. The best way to do this is by sending the "AT" command to the MODEM. This also allows smart MODEMs to detect the baud you are using. When the MODEM is connected correctly and powered on it will respond with the response "OK".

    Listing 4 - Initializing the MODEM.

    int                  /* O - 0 = MODEM ok, -1 = MODEM bad */
    init_modem(int fd) /* I - Serial port file */
    {
    char buffer[255]; /* Input buffer */
    char *bufptr; /* Current char in buffer */
    int nbytes; /* Number of bytes read */
    int tries; /* Number of tries so far */

    for (tries = 0; tries < 3; tries ++)
    {
    /* send an AT command followed by a CR */
    if (write(fd, "AT\r", 3) < 3)
    continue;

    /* read characters into our string buffer until we get a CR or NL */
    bufptr = buffer;
    while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
    {
    bufptr += nbytes;
    if (bufptr[-1] == '\n' || bufptr[-1] == '\r')
    break;
    }

    /* nul terminate the string and see if we got an OK response */
    *bufptr = '\0';

    if (strncmp(buffer, "OK", 2) == 0)
    return (0);
    }

    return (-1);
    }

Standard MODEM Commands

Most MODEMs support the "AT" command set, so called because each command starts with the "AT" characters. Each command is sent with the "AT" characters starting in the first column followed by the specific command and a carriage return (CR, 015 octal). After processing the command the MODEM will reply with one of several textual messages depending on the command.

ATD - Dial A Number

The ATD command dials the specified number. In addition to numbers and dashes you can specify tone ("T") or pulse ("P") dialing, pause for one second (","), and wait for a dialtone ("W"):

    ATDT 555-1212
    ATDT 18008008008W1234,1,1234
    ATD T555-1212WP1234

The MODEM will reply with one of the following messages:

    NO DIALTONE
    BUSY
    NO CARRIER
    CONNECT
    CONNECT baud

ATH - Hang Up

The ATH command causes the MODEM to hang up. Since the MODEM must be in "command" mode you probably won't use it during a normal phone call.

Most MODEMs will also hang up if DTR is dropped; you can do this by setting the baud to 0 for at least 1 second. Dropping DTR also returns the MODEM to command mode.

After a successful hang up the MODEM will reply with "NO CARRIER". If the MODEM is still connected the "CONNECT" or "CONNECT baud" message will be sent.

ATZ - Reset MODEM

The ATZ command resets the MODEM. The MODEM will reply with the string "OK".

Common MODEM Communication Problems

First and foremost, don't forget to disable input echoing. Input echoing will cause a feedback loop between the MODEM and computer.

Second, when sending MODEM commands you must terminate them with a carriage return (CR) and not a newline (NL). The C character constant for CR is "\r".

Finally, when dealing with a MODEM make sure you use a baud that the MODEM supports. While many MODEMs do auto-baud detection, some have limits (19.2kbps is common) that you must observe.


Chapter 4, Advanced Serial Programming

This chapter covers advanced serial programming techniques using the ioctl(2) and select(2) system calls.

Serial Port IOCTLs

In Chapter 2, Configuring the Serial Port we used the tcgetattr and tcsetattr functions to configure the serial port. Under UNIX these functions use the ioctl(2) system call to do their magic.

The ioctl system call takes three arguments:

    int ioctl(int fd, int request, ...);

The fd argument specifies the serial port file descriptor. The request argument is a constant defined in the header file and is typically one of the following:

Table 10 - IOCTL Requests for Serial Ports
RequestDescriptionPOSIX Function
TCGETSGets the current serial port settings. tcgetattr
TCSETSSets the serial port settings immediately.tcsetattr(fd, TCSANOW, &options)
TCSETSFSets the serial port settings after flushing the input and output buffers.tcsetattr(fd, TCSANOW, &options)
TCSETSWSets the serial port settings after allowing the input and output buffers to drain/empty. tcsetattr(fd, TCSANOW, &options)
TCSBRKSends a break for the given time. tcsendbreak, tcdrain
TCXONCControls software flow control. tcflow
TCFLSHFlushes the input and/or output queue. tcflush
TIOCMGETReturns the state of the "MODEM" bits. None
TIOCMSETSets the state of the "MODEM" bits. None
FIONREADReturns the number of bytes in the input buffer.None

Getting the Control Signals

The TIOCMGET ioctl gets the current "MODEM" status bits, which consist of all of the RS-232 signal lines except RXD and TXD:

Table 11 - Control Signal Constants
ConstantDescription
TIOCM_LEDSR (data set ready/line enable)
TIOCM_DTRDTR (data terminal ready)
TIOCM_RTSRTS (request to send)
TIOCM_STSecondary TXD (transmit)
TIOCM_SRSecondary RXD (receive)
TIOCM_CTSCTS (clear to send)
TIOCM_CARDCD (data carrier detect)
TIOCM_CDSynonym for TIOCM_CAR
TIOCM_RNGRNG (ring)
TIOCM_RISynonym for TIOCM_RNG
TIOCM_DSRDSR (data set ready)

To get the status bits, call ioctl with a pointer to an integer to hold the bits:

    Listing 5 - Getting the MODEM status bits.

    #include 
    #include

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);

Setting the Control Signals

The TIOCMSET ioctl sets the "MODEM" status bits defined above. To drop the DTR signal you can do:

    Listing 6 - Dropping DTR with the TIOCMSET ioctl.

    #include 'unistd.h'
    #include 'termios.h'

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);

    status &= ~TIOCM_DTR;

    ioctl(fd, TIOCMSET, status);

The bits that can be set depend on the operating system, driver, and modes in use. Consult your operating system documentation for more information.

Getting the Number of Bytes Available

The FIONREAD ioctl gets the number of bytes in the serial port input buffer. As with TIOCMGET you pass in a pointer to an integer to hold the number of bytes:

    Listing 7 - Getting the number of bytes in the input buffer.

    #include 'unistd.h'
    #include 'termios.h'

    int fd;
    int bytes;

    ioctl(fd, FIONREAD, &bytes);

This can be useful when polling a serial port for data, as your program can determine the number of bytes in the input buffer before attempting a read.

Selecting Input from a Serial Port

While simple applications can poll or wait on data coming from the serial port, most applications are not simple and need to handle input from multiple sources.

UNIX provides this capability through the select(2) system call. This system call allows your program to check for input, output, or error conditions on one or more file descriptors. The file descriptors can point to serial ports, regular files, other devices, pipes, or sockets. You can poll to check for pending input, wait for input indefinitely, or timeout after a specific amount of time, making the select system call extremely flexible.

Most GUI Toolkits provide an interface to select; we will discuss the X Intrinsics ("Xt") library later in this chapter.

The SELECT System Call

The select system call accepts 5 arguments:

    int select(int max_fd, fd_set *input, fd_set *output, fd_set *error,
    struct timeval *timeout);

The max_fd argument specifies the highest numbered file descriptor in the input, output, and error sets. The input, output, and error arguments specify sets of file descriptors for pending input, output, or error conditions; specify NULL to disable monitoring for the corresponding condition. These sets are initialized using three macros:

    FD_ZERO(fd_set);
    FD_SET(fd, fd_set);
    FD_CLR(fd, fd_set);

The FD_ZERO macro clears the set entirely. The FD_SET and FD_CLR macros add and remove a file descriptor from the set, respectively.

The timeout argument specifies a timeout value which consists of seconds (timeout.tv_sec) and microseconds (timeout.tv_usec ). To poll one or more file descriptors, set the seconds and microseconds to zero. To wait indefinitely specify NULL for the timeout pointer.

The select system call returns the number of file descriptors that have a pending condition, or -1 if there was an error.

Using the SELECT System Call

Suppose we are reading data from a serial port and a socket. We want to check for input from either file descriptor, but want to notify the user if no data is seen within 10 seconds. To do this we'll need to use the select system call:

    Listing 8 - Using SELECT to process input from more than one source.

    #include 'unistd.h'
    #include 'sys/types.h'
    #include 'sys/time.h'
    #include 'sys/select.h'

    int n;
    int socket;
    int fd;
    int max_fd;
    fd_set input;
    struct timeval timeout;

    /* Initialize the input set */
    FD_ZERO(input);
    FD_SET(fd, input);
    FD_SET(socket, input);

    max_fd = (socket > fd ? socket : fd) + 1;

    /* Initialize the timeout structure */
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    /* Do the select */
    n = select(max_fd, NULL, NULL, ;

    /* See if there was an error */
    if (n 0)
    perror("select failed");
    else if (n == 0)
    puts("TIMEOUT");
    else
    {
    /* We have input */
    if (FD_ISSET(fd, input))
    process_fd();
    if (FD_ISSET(socket, input))
    process_socket();
    }

You'll notice that we first check the return value of the select system call. Values of 0 and -1 yield the appropriate warning and error messages. Values greater than 0 mean that we have data pending on one or more file descriptors.

To determine which file descriptor(s) have pending input, we use the FD_ISSET macro to test the input set for each file descriptor. If the file descriptor flag is set then the condition exists (input pending in this case) and we need to do something.

Using SELECT with the X Intrinsics Library

The X Intrinsics library provides an interface to the select system call via the XtAppAddInput(3x) and XtAppRemoveInput(3x) functions:

    int XtAppAddInput(XtAppContext context, int fd, int mask,
    XtInputProc proc, XtPointer data);
    void XtAppRemoveInput(XtAppContext context, int input);

The select system call is used internally to implement timeouts, work procedures, and check for input from the X server. These functions can be used with any Xt-based toolkit including Xaw, Lesstif, and Motif.

The proc argument to XtAppAddInput specifies the function to call when the selected condition (e.g. input available) exists on the file descriptor. In the previous example you could specify the process_fd or process_socket functions.

Because Xt limits your access to the select system call, you'll need to implement timeouts through another mechanism, probably via XtAppAddTimeout(3x).


Appendix A, Pinouts

This appendix provides pinout information for many of the common serial ports you will find.

RS-232 Pinouts

RS-232 comes in three flavors (A, B, C) and uses a 25-pin D-Sub connector:

Figure 2 - RS-232 Connector

Table 12 - RS-232 Signals
PinDescriptionPinDescription
1Earth Ground14Secondary TXD
2TXD - Transmitted Data15 Transmit Clock
3RXD - Received Data16 Secondary RXD
4RTS - Request To Send17 Receiver Clock
5CTS - Clear To Send18 Unassigned
6DSR - Data Set Ready19 Secondary RTS
7GND - Logic Ground20DTR - Data Terminal Ready
8DCD - Data Carrier Detect21 Signal Quality Detect
9Reserved22Ring Detect
10Reserved23Data Rate Select
11Unassigned24Transmit Clock
12Secondary DCD25Unassigned
13Secondary CTS

RS-422 Pinouts

RS-422 also uses a 25-pin D-Sub connector, but with differential signals:

Figure 3 - RS-422 Connector

Table 13 - RS-422 Signals
PinDescriptionPinDescription
1Earth Ground14TXD+
2TXD- - Transmitted Data15 Transmit Clock-
3RXD- - Received Data16RXD+
4RTS- - Request To Send17 Receiver Clock-
5CTS- - Clear To Send18 Unassigned
6DSR - Data Set Ready19RTS+
7GND - Logic Ground20DTR- - Data Terminal Ready
8DCD- - Data Carrier Detect21 Signal Quality Detect
9Reserved22Unassigned
10Reserved23DTR+
11Unassigned24Transmit Clock+
12DCD+25Receiver Clock+
13CTS+

RS-574 (IBM PC/AT) Pinouts

The RS-574 interface is used exclusively by PC manufacturers and uses a 9-pin male D-Sub connector:

Figure 4 - RS-574 Connector

Table 14 - RS-574 (IBM PC/AT) Signals
PinDescriptionPinDescription
1DCD - Data Carrier Detect6 Data Set Ready
2RXD - Received Data7RTS - Request To Send
3TXD - Transmitted Data8CTS - Clear To Send
4DTR - Data Terminal Ready9 Ring Detect
5GND - Logic Ground

SGI Pinouts

Older SGI equipment uses a 9-pin female D-Sub connector. Unlike RS-574, the SGI pinouts nearly match those of RS-232:

Figure 5 - SGI 9-Pin Connector

Table 15 - SGI 9-Pin DSUB Signals
PinDescriptionPinDescription
1Earth Ground6DSR - Data Set Ready
2TXD - Transmitted Data7GND - Logic Ground
3RXD - Received Data8DCD - Data Carrier Detect
4RTS - Request To Send9DTR - Data Terminal Ready
5CTS - Clear To Send

The SGI Indigo, Indigo2, and Indy workstations use the Apple 8-pin MiniDIN connector for their serial ports:

Figure 6 - SGI 8-Pin Connector

Table 16 - SGI 8-Pin MiniDIN Signals
PinDescriptionPinDescription
1DTR - Data Terminal Ready5 RXD - Received Data
2CTS - Clear To Send6RTS - Request To Send
3TXD - Transmitted Data7DCD - Data Carrier Detect
4GND - Logic Ground8GND - Logic Ground

Appendix B, ASCII Control Codes

This chapter lists the ASCII control codes and their names.

Control Codes

The following ASCII characters are used for control purposes:
Table 17 - ASCII Control Codes
NameBinaryOctalDecimal Hexadecimal
NUL00000000000000
SOH00000001001101
STX00000010002202
ETX00000011003303
EOT00000100004404
ENQ00000101005505
ACK00000110006606
BEL00000111007707
BS00001000010808
HT00001001011909
NL00001010012100A
VT00001011013110B
NP, FF00001100014120C
CR00001101015130D
SO00001110016140E
SI00001111017150F
DLE000100000201610
XON, DC1000100010211711
DC2000100100221812
XOFF, DC3000100110231913
DC4000101000242014
NAK000101010252115
SYN000101100262216
ETB000101110272317
CAN000110000302418
EM000110010312519
SUB00011010032261A
ESC00011011033271B
FS00011100034281C
GS00011101035291D
RS00011110036301E
US00011111037311F
 Linux Serial Port Programming Mini-Howto


1. Introduction

This document describes how to program the RS-232 ports for serial
communications, under PC-Linux. It covers information about
the serial ports, RS232 connections, modem issues, and the C programming
logic.

2. Background

For our final year project, our group had to design a concept World
Wide Web browser. Our prototype was a hand-held device which plugged
into a PC's COM2 (25-pin) serial port. The user would issue commands
to the browser (eg Back, Open, etc) by sending character commands to
the PC. The browser software would detect it, and perform the required
operation.

The method provided in the 'Linux I/O port programming mini-HOWTO'
did not act reliably. Often, an incorrect value would be received.
The information within provided a 100% reliable, quasi-POSIX-compliant
approach to communication.

The program provided at the end of this 0 formed the basis of
the PC's communication program. This document is written from the
1 needed for the web browser project. It revolves around
C programming for Linux, for a 9600 baud device attached to COM2.

3. Acknowledgements

The information here comes mainly from these sources -

Heavily based on the 'Serial Programming Guide for POSIX Compliant
Operating Systems', at http://www.easysw.com/~mike/serial/, by
mike@easysw.com. If you need greater detail or more information, then
this is the place to visit. Most of the information in this 0
originated here.

The 'Linux I/O port programming mini-HOWTO', by Riku Saikkonen
(rjs@spider.compart.fi). This document provides a different approach
to Linux serial programming.

'The Linux Serial HOWTO', by Greg Hankins, greg.hankins@cc.gatech.edu
describes how to set up serial communications devices on a Linux box.
It describes many aspects of serial devices and 0.

My two wonderful project partners: Gerel Enrile and Joyce Gong.

4. Copyright

This document is copyright (C) 1997 by Antonino Iannella.
It is covered under the general Linux HOWTO copyright agreement.

It is intended for the general public. it may be reproduced and
distributed in whole or in part, using any electronic or physical
medium. However, this copyright notice must remain on all copies.

Please forward question, suggestions, corrections, and all tidbits
of information to the author at antonino@usa.net
(or nettuno@light.iinet.net.au).
You will be acknowledged in future HOWTO revisions.

5. RS-232 connections

RS-232 is a standard for serial communications. It comes in different
varieties. The most common is RS-232C which defines a 'mark' bit as a
voltage between -3V and -12V, and a 'space' bit as a voltage between
+3V and +12V. RS-574 is the standard for 9-pin PC connectors and
voltages.

RS-232 basically consists of wires for serial communications; sending
and receiving, timing, status, and handshaking.
You can use a null modem cable as your connector.
The following pins are what were used for our project. We connected
the device to the DB-25 pin COM2 port. Please note that the Transmit
line from the PC must connect to the Receive line of the device, and
vice versa. Also, please note that a parallel port is different to a
serial port!

PC's pins Device's pins
TxD Transmit Data 2 - 3 RxD Receive Data
RxD Receive Data 3 - 2 TxD Transmit Data
SG Signal Ground 7 - 7 SG Signal Ground


Refer to the Linux Serial HOWTO for more specialised connections, and
detailed RS-232 pins.

6. Serial 0 and RS-232 definitions

The way that data get transmitted in serial communications is, well,
serially. One data bit is sent at a time. Each bit is either on (or the
'mark' state), or off (or the 'space' state).

The serial data throughput is usually expressed in bits-per-second (bps)
or baudot (baud). Throughput is the number of data bits (2 or off) that
may be sent in a second. Your modem might be able to support 115200 baud.
The project's web browser device was designed to run at 9600 baud.

RS-232 provides 18 different signals. About 6 are available to UNIX for
programming.

GND - Logic ground

Very important. Acts as a reference voltage, so the devices know
the relative voltage of the data transmitted.

TxD - Transmitted data

Carries the data transmitted from your PC.
A 'mark' voltage is interpreted as 1,
while a 'space' voltage is interpreted as 0.

RxD - Received data

Carries the data transmitted to your PC from the other device.

DCD - Data carrier detect

Is sent from the other device to your PC. A 'space' voltage means
that the device is still connected, or 'on-line'. This signal is
not always used or available.

DTR - Data terminal ready

Is sent from your PC to tell the device that you are ready (space)
or not-ready (mark). DTR is usually enabled automatically whenever
you access the serial interface.

CTS - Clear to send

Is sent from the other device to your PC.
A 'space' voltage means that your PC may send some data.
It is usually used to regulate the flow of serial data from your
PC, but it is not currently supported by all UNIX flavours.

RTS - Request to send

Is set to the 'space' voltage by your PC when it requests to send
more data. It also used to regulate data flow. Many systems leave
it on 'space' voltage all the time.

7. Communication issues

This section covers other issues of serial communication which might be
relevant to your particular application.
Since we are programming for asynchronous communication, we need the
PCs/devices to know where each character starts and ends in the serial
data flow.

In asynchronous mode, the serial data line stays in the mark state until
a character is sent. A 'start bit' is sent before each character; it is
always 0 and tells the PC/device that a character will follow.
After the start bit, the character's bits are sent, then a 'parity' bit,
and one or more stop bits.

The parity bit is a checksum of the data bits, indicating the number of
1 bits it contained -

Even parity - parity bit is 1 if there is an even number of 1s
Odd parity - parity bit is 1 if there is an odd number of 1s
Space parity - parity bit is always 0
Mark parity - parity bit is 1
No parity - no parity bit is sent or present.

'Stop' bits come at the end of every character. There may be 1, 1.5, or 2
stop bits. They used to be used to give the computer time to process the
character, but now they are used to synchronise the computer to the
incoming characters.

Asynchronous data is usually described like '8N1' - 8 data bits, no parity
bits, 1 stop bit. Another common one is '7N1'.

Flow control is used to regulate the data flow between devices, if there
is some sort of limitation, such as a slow device. There is 'Software
Flow Control' using special characters, XON and XOFF, to regulate the flow.
This method is not useful for transmitting non-textual data.
'Hardware Flow Control' uses the RTS and CTS signals instead of special
characters. The receiver sets CTS to space when it is ready to receive,
and to mark when it's not. The sender uses RTS the same way. This method
is faster than Software Flow Control, since it uses a separate set of
signals instead of extra bits.

Since the receive or transmit signal is at mark voltage until a new character
is sent. A 'break' condition exists when the line is set to low for 1/4
to 1/2 a second. It is used to reset a communications line, or change
the operation mode of devices like modems.

8. Basic port programming

Hopefully, all of the above is reasonably clear to you, so you may proceed
to program with confidence!

In UNIX all system devices are treated as (special) files. All serial ports
are opened, read from, written to, and closed, just like a binary file.
In Linux, the PC serial ports are

COM1 - /dev/ttyS0
COM2 - /dev/ttyS1
COM3 - /dev/ttyS2
COM4 - /dev/ttyS3

Firstly, open the serial port as a file. However, UNIX does not allow
devices to be accessed by normal users. To solve this, either run the
program as the superuser, or change the permission on the device as root,
eg

chmod a+rw /dev/ttyS1 (lets everyone access COM2)

To open the file do the following. Notice the three flags used in the
open() function. O_RDWR means that we open the port for reading and
writing. O_NOCTTY specifies that the program won't be the controlling
entity for the port. Most user programs don't want this feature.
O_NDELAY means that your program ignores the DCD line. If it didn't,
the program will be put to sleep until DCD is set to 'space' voltage.

/*
* 'open_port()' - Open serial port 1 - COM2.
*
* Returns the file descriptor on success or -1 on error.
*/

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",
strerror(errno));
}

return (fd);
}

If you need to write data to the port, do something like

n = write(fd, "ATZ\r", 4);

if (n <> and access the termios structure using the
POSIX tcgetattr() and tcsetattr() functions.

The termios structure contains

c_cflag - Control options
c_lflag - Line options
c_iflag - Input options
c_oflag - Output options
c_cc - Control characters

See section 12 for a list of c_cflag control modes.
They are used to set the baud rate, parity and stop bits, and flow control.
Always enable CLOCAL and CREAD, so the program does not own the port, and so
the serial interface driver will read incoming bytes.

9.1. Accessing the termios structure and the baud rate

Use cfsetospeed() and cfsetispeed() to set the baud rate.
CRTSCTS might be called CNEW_RTSCTS on other systems.
The following uses a termios structure called 'options'.
For our project, the device transmitted at 9600 baud and transmitted nothing
special.

tcgetattr(mainfd, &options); /* Get the current options for the port */
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);
/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

The tcsetattr() function replaces the port's termios structure with the
settings you provided. The TCSANOW constant means that the changes should
occur immediately, without waiting for data transmission to complete.
Alternative choices are TCSADRAIN and TCSAFLUSH, which wait until buffers
are cleared. Refer to section 13.

9.2. Character size and parity

To set the character size, you must use bitwise logic.
The following code sets the character size to 8 data bits, and no parity.

options.c_cflag &= ~PARENB; /* Mask character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */

For other methods, refer to section 11.

9.3. Hardware flow control

To enable hardware flow control, use

options.c_cflag |= CRTSCTS; /* Enable hardware flow control */

To disable it,

options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */

9.4. Canonical and raw input

Canonical input means that all incoming characters are placed in a buffer
which may be edited by the user, until a carriage return or line feed (CR or
LF) are received. Typically, you would use

options.c_lflag |= ~(ICANON | ECHO | ECHOE);

Raw input is unprocessed, so they may be used as they are read.
Our device sent raw data.

options.c_lflag &= ~(ICANON | ECHO | ISIG);

Whether you use canonical or raw input, make sure you never enable input
echo when connected to a computer/device which is echoing characters to you.
Refer to section 14 for local mode constants.

9.5. POSIX input modes

Set the port's input modes for any input processing.
Set input parity when you have enabled parity in the c_cflag part.
Usually you'd use the following to enable parity checking, and strip the
parity bit off the data, before your program reads it.

options.c_iflag |= (INPCK | ISTRIP);

You might use IGNPAR, which ignores all parity errors. PARMRK marks parity
errors by inserting a DEL(255) and NUL character before the bad character.
If IGNPAR is enabled, only a NUL is inserted.

You may set software flow control using

options.c_iflag |= (IXON | IXOFF | IXANY);

Refer to section 15 for input mode constants.

9.6. POSIX output modes

To set port output modes, use the c_oflag member.
To select processed output, use the following. Of all the output modes, you
will probably only use ONCLR to convert newlines into CR and LFs.

options.c_oflag |= OPOST;

For raw output, use

options.c_oflag &= ~OPOST;

Refer to section 16 for output mode constants.

9.7. POSIX control modes

You may set the control characters using the c_cc part.
Set the software flow control characters in the VSTART and VSTOP
elements. The standard is DC1(17) and DC3(19) for XON and XOFF.
VMIN specifies the minimum number of 0 to read. If it is 0, then
VTIME specifies the time to wait for each character.
If VMIN is not 0, VTIME is the time to wait to read the first character.
If the first character is read, then any read() will be blocked until all
VMIN characters are read.
VTIME is specified in tenths of seconds. If it is 0, then read()s will
be permanently blocked unless NDELAY was previously specified with fcntl().

Refer to section 17 for control mode constants.

10. Sample program

This program is a skeleton COM2 reader, which was used for our project.
It did not need all of the information specified above for
configuring ports. The 20ms delay is used to indicate that data coming into
the port is buffered, and is available for the next read().

/* Better port reading program
v1.0
23-10-96

This test program uses quasi-POSIX compliant UNIX functions to
open the ABU port and read.

Uses termio functions to initialise the port to 9600 baud, at
8 data bits, no parity, no hardware flow control,
and features character buffering.
The 20ms delay after the port read indicates that characters are
buffered if a button is pressed many times.

This program was derived from instructions at
http://www.easysw.com/~mike/serial/
*/

#include stdio.h' /* Standard input/output definitions */
#include string.h /* String function definitions */
#include unistd.h /* UNIX standard function definitions */
#include fcntl.h /* File control definitions */
#include errno.h /* Error number definitions */
#include termios.h /* POSIX terminal control definitions */

/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);

if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",
strerror(errno));
}

return (fd);
}

void main()
{
int mainfd=0; /* File descriptor */
char chout;
struct termios options;

mainfd = open_port();

fcntl(mainfd, F_SETFL, FNDELAY); /* Configure port reading */
/* Get the current options for the port */
tcgetattr(mainfd, &options);
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);

/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */

/* Enable data to be processed as raw input */
options.c_lflag &= ~(ICANON | ECHO | ISIG);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

while (1)
{
read(mainfd, &chout, sizeof(chout)); /* Read character from ABU */

if (chout != 0)
printf("Got %c.\n", chout);

chout=0;
usleep(20000);
}
/* Close the serial port */
close(mainfd);
}

11. Character and parity settings

No parity (8N1):

options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

Even parity (7E1):

options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Odd parity (7O1):

options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Mark parity is simulated by using 2 stop bits (7M1):

options.c_cflag &= ~PARENB;
options.c_cflag |= CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Space parity is setup the same as no parity (7S1):

options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

12. POSIX control mode flags

The following table lists the possible control modes for c_cflag.

Constant Description
________________________

CBAUD Bit mask for baud rate
B0 0 baud (drop DTR)
B50 50 baud
B75 75 baud
B110 110 baud
B134 134.5 baud
B150 150 baud
B200 200 baud
B300 300 baud
B600 600 baud
B1200 1200 baud
B1800 1800 baud
B2400 2400 baud
B4800 4800 baud
B9600 9600 baud
B19200 19200 baud
B38400 38400 baud
EXTA External rate clock
EXTB External rate clock
CSIZE Bit mask for data bits
CS5 5 data bits
CS6 6 data bits
CS7 7 data bits
CS8 8 data bits
CSTOPB 2 stop bits (1 otherwise)
CREAD Enable receiver
PARENB Enable parity bit
PARODD Use odd parity instead of even
HUPCL Hangup (drop DTR) on last close
CLOCAL Local line - do not change 'owner' of port
LOBLK Block job control output
CRTSCTS Enable hardware flow control (not supported on all platforms)

13. POSIX tcsetattr Constants

Constant Description
______________________

TCSANOW Make changes now without waiting for data to complete
TCSADRAIN Wait until everything has been transmitted
TCSAFLUSH Flush input and output buffers and make the change

14. POSIX Local Mode Constants

Constant Description
______________________

ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
ICANON Enable canonical input (else raw)
XCASE Map uppercase \lowercase (obselete)
ECHO Enable echoing of input characters
ECHOE Echo erase character as BS-SP-BS
ECHOK Echo NL after kill character
ECHONL Echo NL
NOFLSH Disable flushing of input buffers after interrupt
or quit characters
IEXTEN Enable extended functions
ECHOCTL Echo control characters as ^char and delete as ~?
ECHOPRT Echo erased character as character erased
ECHOKE BS-SP-BS entire line on line kill
FLUSHO Output being flushed
PENDIN Retype pending input at next read or input char
TOSTOP Send SIGTTOU for background output

15. POSIX Input Mode Constants

Constant Description
______________________

INPCK Enable parity check
IGNPAR Ignore parity errors
PARMRK Mark parity errors
ISTRIP Strip parity bits
IXON Enable software flow control (outgoing)
IXOFF Enable software flow control (incoming)
IXANY Allow any character to start flow again
IGNBRK Ignore break condition
BRKINT Send a SIGINT when a break condition is detected
INLCR Map NL to CR
IGNCR Ignore CR
ICRNL Map CR to NL
IUCLC Map uppercase to lowercase
IMAXBEL Echo BEL on input line too long

16. POSIX Output Mode Constants

Constant Description
______________________

OPOST Postprocess output (not set = raw output)
OLCUC Map lowercase to uppercase
ONLCR Map NL to CR-NL
OCRNL Map CR to NL
NOCR No CR output at column 0
ONLRET NL performs CR function
OFILL Use fill characters for delay
OFDEL Fill character is DEL
NLDLY Mask for delay time needed between lines
NL0 No delay for NLs
NL1 Delay further output after newline for 100 milliseconds
CRDLY Mask for delay time needed to return carriage to left column
CR0 No delay for CRs
CR1 Delay after CRs depending on current column position
CR2 Delay 100 milliseconds after sending CRs
CR3 Delay 150 milliseconds after sending CRs
TABDLY Mask for delay time needed after TABs
TAB0 No delay for TABs
TAB1 Delay after TABs depending on current column position
TAB2 Delay 100 milliseconds after sending TABs
TAB3 Expand TAB characters to spaces
BSDLY Mask for delay time needed after BSs
BS0 No delay for BSs
BS1 Delay 50 milliseconds after sending BSs
VTDLY Mask for delay time needed after VTs
VT0 No delay for VTs
VT1 Delay 2 seconds after sending VTs
FFDLY Mask for delay time needed after FFs
FF0 No delay for FFs
FF1 Delay 2 seconds after sending FFs

17. POSIX Control Character Constants

Constant Description Key
______________________________________

VINTR Interrupt CTRL-C
VQUIT Quit CTRL-Z
VERASE Erase Backspace (BS)
VKILL Kill-line CTRL-U
VEOF End-of-file CTRL-D
VEOL End-of-line Carriage return (CR)
VEOL2 Second end-of-line Line feed (LF)
VMIN Minimum number of characters to read
VTIME Time to wait for data (tenths of seconds)

----------------- End of Linux Serial Programming Mini-Howto -----------------

Linux Serial Port Programming Mini-Howto

 Linux Serial Port Programming Mini-Howto


1. Introduction

This document describes how to program the RS-232 ports for serial
communications, under PC-Linux. It covers information about
the serial ports, RS232 connections, modem issues, and the C programming
logic.

2. Background

For our final year project, our group had to design a concept World
Wide Web browser. Our prototype was a hand-held device which plugged
into a PC's COM2 (25-pin) serial port. The user would issue commands
to the browser (eg Back, Open, etc) by sending character commands to
the PC. The browser software would detect it, and perform the required
operation.

The method provided in the 'Linux I/O port programming mini-HOWTO'
did not act reliably. Often, an incorrect value would be received.
The information within provided a 100% reliable, quasi-POSIX-compliant
approach to communication.

The program provided at the end of this 0 formed the basis of
the PC's communication program. This document is written from the
1 needed for the web browser project. It revolves around
C programming for Linux, for a 9600 baud device attached to COM2.

3. Acknowledgements

The information here comes mainly from these sources -

Heavily based on the 'Serial Programming Guide for POSIX Compliant
Operating Systems', at http://www.easysw.com/~mike/serial/, by
mike@easysw.com. If you need greater detail or more information, then
this is the place to visit. Most of the information in this 0
originated here.

The 'Linux I/O port programming mini-HOWTO', by Riku Saikkonen
(rjs@spider.compart.fi). This document provides a different approach
to Linux serial programming.

'The Linux Serial HOWTO', by Greg Hankins, greg.hankins@cc.gatech.edu
describes how to set up serial communications devices on a Linux box.
It describes many aspects of serial devices and 0.

My two wonderful project partners: Gerel Enrile and Joyce Gong.

4. Copyright

This document is copyright (C) 1997 by Antonino Iannella.
It is covered under the general Linux HOWTO copyright agreement.

It is intended for the general public. it may be reproduced and
distributed in whole or in part, using any electronic or physical
medium. However, this copyright notice must remain on all copies.

Please forward question, suggestions, corrections, and all tidbits
of information to the author at antonino@usa.net
(or nettuno@light.iinet.net.au).
You will be acknowledged in future HOWTO revisions.

5. RS-232 connections

RS-232 is a standard for serial communications. It comes in different
varieties. The most common is RS-232C which defines a 'mark' bit as a
voltage between -3V and -12V, and a 'space' bit as a voltage between
+3V and +12V. RS-574 is the standard for 9-pin PC connectors and
voltages.

RS-232 basically consists of wires for serial communications; sending
and receiving, timing, status, and handshaking.
You can use a null modem cable as your connector.
The following pins are what were used for our project. We connected
the device to the DB-25 pin COM2 port. Please note that the Transmit
line from the PC must connect to the Receive line of the device, and
vice versa. Also, please note that a parallel port is different to a
serial port!

PC's pins Device's pins
TxD Transmit Data 2 - 3 RxD Receive Data
RxD Receive Data 3 - 2 TxD Transmit Data
SG Signal Ground 7 - 7 SG Signal Ground


Refer to the Linux Serial HOWTO for more specialised connections, and
detailed RS-232 pins.

6. Serial 0 and RS-232 definitions

The way that data get transmitted in serial communications is, well,
serially. One data bit is sent at a time. Each bit is either on (or the
'mark' state), or off (or the 'space' state).

The serial data throughput is usually expressed in bits-per-second (bps)
or baudot (baud). Throughput is the number of data bits (2 or off) that
may be sent in a second. Your modem might be able to support 115200 baud.
The project's web browser device was designed to run at 9600 baud.

RS-232 provides 18 different signals. About 6 are available to UNIX for
programming.

GND - Logic ground

Very important. Acts as a reference voltage, so the devices know
the relative voltage of the data transmitted.

TxD - Transmitted data

Carries the data transmitted from your PC.
A 'mark' voltage is interpreted as 1,
while a 'space' voltage is interpreted as 0.

RxD - Received data

Carries the data transmitted to your PC from the other device.

DCD - Data carrier detect

Is sent from the other device to your PC. A 'space' voltage means
that the device is still connected, or 'on-line'. This signal is
not always used or available.

DTR - Data terminal ready

Is sent from your PC to tell the device that you are ready (space)
or not-ready (mark). DTR is usually enabled automatically whenever
you access the serial interface.

CTS - Clear to send

Is sent from the other device to your PC.
A 'space' voltage means that your PC may send some data.
It is usually used to regulate the flow of serial data from your
PC, but it is not currently supported by all UNIX flavours.

RTS - Request to send

Is set to the 'space' voltage by your PC when it requests to send
more data. It also used to regulate data flow. Many systems leave
it on 'space' voltage all the time.

7. Communication issues

This section covers other issues of serial communication which might be
relevant to your particular application.
Since we are programming for asynchronous communication, we need the
PCs/devices to know where each character starts and ends in the serial
data flow.

In asynchronous mode, the serial data line stays in the mark state until
a character is sent. A 'start bit' is sent before each character; it is
always 0 and tells the PC/device that a character will follow.
After the start bit, the character's bits are sent, then a 'parity' bit,
and one or more stop bits.

The parity bit is a checksum of the data bits, indicating the number of
1 bits it contained -

Even parity - parity bit is 1 if there is an even number of 1s
Odd parity - parity bit is 1 if there is an odd number of 1s
Space parity - parity bit is always 0
Mark parity - parity bit is 1
No parity - no parity bit is sent or present.

'Stop' bits come at the end of every character. There may be 1, 1.5, or 2
stop bits. They used to be used to give the computer time to process the
character, but now they are used to synchronise the computer to the
incoming characters.

Asynchronous data is usually described like '8N1' - 8 data bits, no parity
bits, 1 stop bit. Another common one is '7N1'.

Flow control is used to regulate the data flow between devices, if there
is some sort of limitation, such as a slow device. There is 'Software
Flow Control' using special characters, XON and XOFF, to regulate the flow.
This method is not useful for transmitting non-textual data.
'Hardware Flow Control' uses the RTS and CTS signals instead of special
characters. The receiver sets CTS to space when it is ready to receive,
and to mark when it's not. The sender uses RTS the same way. This method
is faster than Software Flow Control, since it uses a separate set of
signals instead of extra bits.

Since the receive or transmit signal is at mark voltage until a new character
is sent. A 'break' condition exists when the line is set to low for 1/4
to 1/2 a second. It is used to reset a communications line, or change
the operation mode of devices like modems.

8. Basic port programming

Hopefully, all of the above is reasonably clear to you, so you may proceed
to program with confidence!

In UNIX all system devices are treated as (special) files. All serial ports
are opened, read from, written to, and closed, just like a binary file.
In Linux, the PC serial ports are

COM1 - /dev/ttyS0
COM2 - /dev/ttyS1
COM3 - /dev/ttyS2
COM4 - /dev/ttyS3

Firstly, open the serial port as a file. However, UNIX does not allow
devices to be accessed by normal users. To solve this, either run the
program as the superuser, or change the permission on the device as root,
eg

chmod a+rw /dev/ttyS1 (lets everyone access COM2)

To open the file do the following. Notice the three flags used in the
open() function. O_RDWR means that we open the port for reading and
writing. O_NOCTTY specifies that the program won't be the controlling
entity for the port. Most user programs don't want this feature.
O_NDELAY means that your program ignores the DCD line. If it didn't,
the program will be put to sleep until DCD is set to 'space' voltage.

/*
* 'open_port()' - Open serial port 1 - COM2.
*
* Returns the file descriptor on success or -1 on error.
*/

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",
strerror(errno));
}

return (fd);
}

If you need to write data to the port, do something like

n = write(fd, "ATZ\r", 4);

if (n < 0)
puts("write() of 4 bytes failed!\n");

Reading from the port is more complicated. If you open the port in 'raw
data' mode (the norm), each read() returns the number of characters actually
available in the serial buffers. However, if no characters are available,
read() will block until it receives characters, an interval timer expires,
or an error occurs. Use the following to make read return immediately.
FNDELAY makes read() return 0 if no characters were read.

fcntl(mainfd, F_SETFL, FNDELAY); /* Configure port reading */

To close the serial port, simply use

close(fd);

9. Port configuration

This section discusses how to configure the serial port for your device.
You will need to set the terminal attributes related to the port.
To do this, include and access the termios structure using the
POSIX tcgetattr() and tcsetattr() functions.

The termios structure contains

c_cflag - Control options
c_lflag - Line options
c_iflag - Input options
c_oflag - Output options
c_cc - Control characters

See section 12 for a list of c_cflag control modes.
They are used to set the baud rate, parity and stop bits, and flow control.
Always enable CLOCAL and CREAD, so the program does not own the port, and so
the serial interface driver will read incoming bytes.

9.1. Accessing the termios structure and the baud rate

Use cfsetospeed() and cfsetispeed() to set the baud rate.
CRTSCTS might be called CNEW_RTSCTS on other systems.
The following uses a termios structure called 'options'.
For our project, the device transmitted at 9600 baud and transmitted nothing
special.

tcgetattr(mainfd, &options); /* Get the current options for the port */
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);
/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

The tcsetattr() function replaces the port's termios structure with the
settings you provided. The TCSANOW constant means that the changes should
occur immediately, without waiting for data transmission to complete.
Alternative choices are TCSADRAIN and TCSAFLUSH, which wait until buffers
are cleared. Refer to section 13.

9.2. Character size and parity

To set the character size, you must use bitwise logic.
The following code sets the character size to 8 data bits, and no parity.

options.c_cflag &= ~PARENB; /* Mask character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */

For other methods, refer to section 11.

9.3. Hardware flow control

To enable hardware flow control, use

options.c_cflag |= CRTSCTS; /* Enable hardware flow control */

To disable it,

options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */

9.4. Canonical and raw input

Canonical input means that all incoming characters are placed in a buffer
which may be edited by the user, until a carriage return or line feed (CR or
LF) are received. Typically, you would use

options.c_lflag |= ~(ICANON | ECHO | ECHOE);

Raw input is unprocessed, so they may be used as they are read.
Our device sent raw data.

options.c_lflag &= ~(ICANON | ECHO | ISIG);

Whether you use canonical or raw input, make sure you never enable input
echo when connected to a computer/device which is echoing characters to you.
Refer to section 14 for local mode constants.

9.5. POSIX input modes

Set the port's input modes for any input processing.
Set input parity when you have enabled parity in the c_cflag part.
Usually you'd use the following to enable parity checking, and strip the
parity bit off the data, before your program reads it.

options.c_iflag |= (INPCK | ISTRIP);

You might use IGNPAR, which ignores all parity errors. PARMRK marks parity
errors by inserting a DEL(255) and NUL character before the bad character.
If IGNPAR is enabled, only a NUL is inserted.

You may set software flow control using

options.c_iflag |= (IXON | IXOFF | IXANY);

Refer to section 15 for input mode constants.

9.6. POSIX output modes

To set port output modes, use the c_oflag member.
To select processed output, use the following. Of all the output modes, you
will probably only use ONCLR to convert newlines into CR and LFs.

options.c_oflag |= OPOST;

For raw output, use

options.c_oflag &= ~OPOST;

Refer to section 16 for output mode constants.

9.7. POSIX control modes

You may set the control characters using the c_cc part.
Set the software flow control characters in the VSTART and VSTOP
elements. The standard is DC1(17) and DC3(19) for XON and XOFF.
VMIN specifies the minimum number of 0 to read. If it is 0, then
VTIME specifies the time to wait for each character.
If VMIN is not 0, VTIME is the time to wait to read the first character.
If the first character is read, then any read() will be blocked until all
VMIN characters are read.
VTIME is specified in tenths of seconds. If it is 0, then read()s will
be permanently blocked unless NDELAY was previously specified with fcntl().

Refer to section 17 for control mode constants.

10. Sample program

This program is a skeleton COM2 reader, which was used for our project.
It did not need all of the information specified above for
configuring ports. The 20ms delay is used to indicate that data coming into
the port is buffered, and is available for the next read().

/* Better port reading program
v1.0
23-10-96

This test program uses quasi-POSIX compliant UNIX functions to
open the ABU port and read.

Uses termio functions to initialise the port to 9600 baud, at
8 data bits, no parity, no hardware flow control,
and features character buffering.
The 20ms delay after the port read indicates that characters are
buffered if a button is pressed many times.

This program was derived from instructions at
http://www.easysw.com/~mike/serial/
*/

#include /* Standard input/output definitions */
#include /* String function definitions */
#include /* UNIX standard function definitions */
#include /* File control definitions */
#include /* Error number definitions */
#include /* POSIX terminal control definitions */

/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);

if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",
strerror(errno));
}

return (fd);
}

void main()
{
int mainfd=0; /* File descriptor */
char chout;
struct termios options;

mainfd = open_port();

fcntl(mainfd, F_SETFL, FNDELAY); /* Configure port reading */
/* Get the current options for the port */
tcgetattr(mainfd, &options);
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);

/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */

/* Enable data to be processed as raw input */
options.c_lflag &= ~(ICANON | ECHO | ISIG);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

while (1)
{
read(mainfd, &chout, sizeof(chout)); /* Read character from ABU */

if (chout != 0)
printf("Got %c.\n", chout);

chout=0;
usleep(20000);
}
/* Close the serial port */
close(mainfd);
}

11. Character and parity settings

No parity (8N1):

options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

Even parity (7E1):

options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Odd parity (7O1):

options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Mark parity is simulated by using 2 stop bits (7M1):

options.c_cflag &= ~PARENB;
options.c_cflag |= CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

Space parity is setup the same as no parity (7S1):

options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

12. POSIX control mode flags

The following table lists the possible control modes for c_cflag.

Constant Description
________________________

CBAUD Bit mask for baud rate
B0 0 baud (drop DTR)
B50 50 baud
B75 75 baud
B110 110 baud
B134 134.5 baud
B150 150 baud
B200 200 baud
B300 300 baud
B600 600 baud
B1200 1200 baud
B1800 1800 baud
B2400 2400 baud
B4800 4800 baud
B9600 9600 baud
B19200 19200 baud
B38400 38400 baud
EXTA External rate clock
EXTB External rate clock
CSIZE Bit mask for data bits
CS5 5 data bits
CS6 6 data bits
CS7 7 data bits
CS8 8 data bits
CSTOPB 2 stop bits (1 otherwise)
CREAD Enable receiver
PARENB Enable parity bit
PARODD Use odd parity instead of even
HUPCL Hangup (drop DTR) on last close
CLOCAL Local line - do not change 'owner' of port
LOBLK Block job control output
CRTSCTS Enable hardware flow control (not supported on all platforms)

13. POSIX tcsetattr Constants

Constant Description
______________________

TCSANOW Make changes now without waiting for data to complete
TCSADRAIN Wait until everything has been transmitted
TCSAFLUSH Flush input and output buffers and make the change

14. POSIX Local Mode Constants

Constant Description
______________________

ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
ICANON Enable canonical input (else raw)
XCASE Map uppercase \lowercase (obselete)
ECHO Enable echoing of input characters
ECHOE Echo erase character as BS-SP-BS
ECHOK Echo NL after kill character
ECHONL Echo NL
NOFLSH Disable flushing of input buffers after interrupt
or quit characters
IEXTEN Enable extended functions
ECHOCTL Echo control characters as ^char and delete as ~?
ECHOPRT Echo erased character as character erased
ECHOKE BS-SP-BS entire line on line kill
FLUSHO Output being flushed
PENDIN Retype pending input at next read or input char
TOSTOP Send SIGTTOU for background output

15. POSIX Input Mode Constants

Constant Description
______________________

INPCK Enable parity check
IGNPAR Ignore parity errors
PARMRK Mark parity errors
ISTRIP Strip parity bits
IXON Enable software flow control (outgoing)
IXOFF Enable software flow control (incoming)
IXANY Allow any character to start flow again
IGNBRK Ignore break condition
BRKINT Send a SIGINT when a break condition is detected
INLCR Map NL to CR
IGNCR Ignore CR
ICRNL Map CR to NL
IUCLC Map uppercase to lowercase
IMAXBEL Echo BEL on input line too long

16. POSIX Output Mode Constants

Constant Description
______________________

OPOST Postprocess output (not set = raw output)
OLCUC Map lowercase to uppercase
ONLCR Map NL to CR-NL
OCRNL Map CR to NL
NOCR No CR output at column 0
ONLRET NL performs CR function
OFILL Use fill characters for delay
OFDEL Fill character is DEL
NLDLY Mask for delay time needed between lines
NL0 No delay for NLs
NL1 Delay further output after newline for 100 milliseconds
CRDLY Mask for delay time needed to return carriage to left column
CR0 No delay for CRs
CR1 Delay after CRs depending on current column position
CR2 Delay 100 milliseconds after sending CRs
CR3 Delay 150 milliseconds after sending CRs
TABDLY Mask for delay time needed after TABs
TAB0 No delay for TABs
TAB1 Delay after TABs depending on current column position
TAB2 Delay 100 milliseconds after sending TABs
TAB3 Expand TAB characters to spaces
BSDLY Mask for delay time needed after BSs
BS0 No delay for BSs
BS1 Delay 50 milliseconds after sending BSs
VTDLY Mask for delay time needed after VTs
VT0 No delay for VTs
VT1 Delay 2 seconds after sending VTs
FFDLY Mask for delay time needed after FFs
FF0 No delay for FFs
FF1 Delay 2 seconds after sending FFs

17. POSIX Control Character Constants

Constant Description Key
______________________________________

VINTR Interrupt CTRL-C
VQUIT Quit CTRL-Z
VERASE Erase Backspace (BS)
VKILL Kill-line CTRL-U
VEOF End-of-file CTRL-D
VEOL End-of-line Carriage return (CR)
VEOL2 Second end-of-line Line feed (LF)
VMIN Minimum number of characters to read
VTIME Time to wait for data (tenths of seconds)

----------------- End of Linux Serial Programming Mini-Howto -----------------

Tuesday, May 27, 2008

More MInicom Options

Minicom is a serial communication program to access a network or security device through its console port.
This tool is similar to Hyper Terminal, which is by default available on a Microsoft Windows system.

Let's install Minicom:

#apt-get install minicom
Check if you have active serial ports:

#dmesg | grep tty
[17179573.660000] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[17179573.660000] serial8250: ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A


The read and write permissions are required on the /dev/ttyS0 file. The dialout group, which is the default group owner of the file, has already these rights.
You can see it with the following command:

#ls -la /dev | grep ttyS0
crw-rw---- 1 root dialout 4, 64 2007-02-26 22:55 ttyS0

If the permissions are not set as above, you can configure them as follow:

#chown root /dev/ttyS0
#chgrp dialout /dev/ttyS0
#chmod 660 /dev/ttyS0
Now we are ready to add your Debian or Ubuntu user in the dialout group:

#adduser your_user dialout
To see which users are members of the dialout group, open the /etc/group file and look for the line beginning with dialout.

#cat /etc/group | grep dialout
dialout:x:20:cupsys,your_user

We can now start Minicom:

#minicom -s
minicom configuration Serial port setup minicom configuration change which setting
minicom configuration save setup as dfl minicom configuration exit minicom initializing modem
minicom welcome

To leave Minicom: Ctlr A -> Z -> X
The default config will be saved as /etc/minicom/minirc.dfl
The next time you want to use Minicom you just have to enter the following command:

#minicom
To save your Minicom settings on a specific file:

#minicom -s
minicom configuration save setup as minicom configuration save setup as
The config will be saved as /etc/minicom/minirc.config1
To start Minicom with settings configured on a specific file:

#minicom -s config1

Receiving

Receiving bytes by a serial port is similar to sending them only it's in the opposite direction. It's also interrupt driven. For the obsolete type of serial port with 1-byte buffers, when a byte is fully received from the external cable it goes into the 1-byte receive buffer. Then the port gives the CPU an interrupt to tell it to pick up that byte so that the serial port will have room for storing the next byte which is currently being received. For newer serial ports with 16-byte buffers, this interrupt (to fetch the bytes) may be sent after 14 bytes are in the receive buffer. The CPU then stops what it was doing, runs the interrupt service routine, and picks up 14 to 16 bytes from the port. For an interrupt sent when the 14th byte has been received, there could be 16 bytes to get if 2 more bytes have arrived since the interrupt. But if 3 more bytes should arrive (instead of 2), then the 16-byte buffer will overrun. It also may pick up less than 14 bytes by setting it that way or due to timeouts

FIFOs

To understand the differences between dumb and FIFO (First In, First Out queue discipline) first let's examine what happens when a UART has sent or received a byte. The UART itself can't do anything with the data passing thru it, it just receives and sends it. For the obsolete dumb UARTS, the CPU gets an interrupt from the serial device every time a byte has been sent or received. The CPU then moves the received byte out of the UART's buffer and into memory somewhere, or gives the UART another byte to send. The obsolete 8250 and 16450 UARTs only have a 1 byte buffer. That means, that every time 1 byte is sent or received, the CPU is interrupted. At low transfer rates, this is OK. But, at high transfer rates, the CPU gets so busy dealing with the UART, that is doesn't have time to adequately tend to other tasks. In some cases, the CPU does not get around to servicing the interrupt in time, and the byte is overwritten, because they are coming in so fast. This is called an "overrun" or "overflow".

FIFO UARTs help solve this problem. The 16550A (or 16550) FIFO chip comes with 16 byte FIFO buffers. This means that it can receive up to 14 bytes (or send 16 bytes) before it has to interrupt the CPU. Not only can it wait for more bytes, but the CPU then can transfer all (14 to 16) bytes at a time. This is a significant advantage over the obsolete UARTs, which only had 1 byte buffers. The CPU receives less interrupts, and is free to do other things. Data is rarely lost. Note that the interrupt threshold of FIFO buffers (trigger level) may be set at less than 14. 1, 4 and 8 are other possible choices. As of late 2000 there was no way the Linux user could set these directly (setserial can't do it). While many PC's only have a 16550 with 16-byte buffers, better UARTS have even larger buffers.

Note that the interrupt is issued slightly before the buffer gets full (at say a "trigger level" of 14 bytes for a 16-byte buffer). This allows room for a couple more bytes to be received before the interrupt service routine is able to actually fetch all these bytes. The trigger level may be set to various permitted values by kernel software. A trigger level of 1 will be almost like an obsolete UART (except that it still has room for 15 more bytes after it issues the interrupt).

Now consider the case where you're on the Internet. It's just sent you a short webpage of text. All of this came in thru the serial port. If you had a 16-byte buffer on the serial port which held back characters until it had 14 of them, some of the last several characters on the screen might be missing as the FIFO buffer waited to get the 14th character. But the 14th character doesn't arrive since you've been sent the entire page (over the phone line) and there are no more characters to send to you. It could be that these last characters are part of the HTML formatting, etc. and are not characters to display on the screen but you don't want to lose format either.

There is a "timeout" to prevent the above problem. The "timeout" works like this for the receive UART buffer: If characters arrive one after another, then an interrupt is issued only when say the 14th character reaches the buffer. But if a character arrives and the next character doesn't arrive soon thereafter, then an interrupt is issued anyway. This results in fetching all of the characters in the FIFO buffer, even if only a few (or only one) are present. There is also "timeout" for the transmit buffer as well


Why FIFO Buffers are Small

You may wonder why the FIFO buffers are not larger. After all, memory is cheap and it wouldn't cost much more to use buffers in the kilo-byte range. The reason is flow control. Flow control stops the flow of data (bytes) on serial line when necessary. If a stop signal is sent to serial port, then the stop request is handled by software (even if the flow control is "hardware"). The serial port hardware knows nothing about flow control.

If the serial port buffer contains 64 bytes ready to send when it receives a flow control signal to stop sending, it will send out the 64 bytes anyway in violation of the stop request. There is no stopping it since it doesn't know about flow control. If the buffer was large, then many more bytes would be sent in violation of flow control's request to stop.



Transmitting

Transmitting is sending bytes out of the serial port away from the computer. Once you understand transmitting, receiving is easy to understand since it's similar. The first explanation given here will be grossly oversimplified. Then more detail will be added in later explanations. When the computer wants to send a byte out the serial port (to the external cable) the CPU sends the byte on the bus inside the computer to the I/O (Input Output) address of the serial port. I/O is often written as just IO. The serial port takes the byte, and sends it out one bit at a time (a serial bit-stream) on the transmit pin of the serial cable connector. For what a bit (and byte) look like electrically see Voltage Waveshapes.

Here's a replay of the above in a little more detail (but still very incomplete). Most of the work at the serial port is done by the UART chip (or the like). To transmit a byte, the serial device driver program (running on the CPU) sends a byte to the serial port"s I/O address. This byte gets into a 1-byte "transmit shift register" in the serial port. From this shift register bits are taken from the byte one-by-one and sent out bit-by-bit on the serial line. Then when the last bit has been sent and the shift register needs another byte to send it could just ask the CPU to send it another byte. Thus would be simple but it would likely introduce delays since the CPU might not be able to get the byte immediately. After all, the CPU is usually doing other things besides just handling the serial port.

A way to eliminate such delays is to arrange things so that the CPU gets the byte before the shift register needs it and stores it in a serial port buffer (in hardware). Then when the shift register has sent out its byte and needs a new byte immediately, the serial port hardware just transfers the next byte from its own buffer to the shift register. No need to call the CPU to fetch a new byte.

The size of this serial port buffer was originally only one byte, but today it is usually 16 bytes (more in higher priced serial ports). Now there is still the problem of keeping this buffer sufficiently supplied with bytes so that when the shift register needs a byte to transmit it will always find one there (unless there are no more bytes to send). This is done by contacting the CPU using an interrupt.

First we'll explain the case of the old fashioned one-byte buffer, since 16-byte buffers work similarly (but are more complex). When the shift register grabs the byte out of the buffer and the buffer needs another byte, it sends an interrupt to the CPU by putting a voltage on a dedicated wire on the computer bus. Unless the CPU is doing something very important, the interrupt forces it to stop what it was doing and start running a program which will supply another byte to the port's buffer. The purpose of this buffer is to keep an extra byte (waiting to be sent) queued in hardware so that there will be no gaps in the transmission of bytes out the serial port cable.

Once the CPU gets the interrupt, it will know who sent the interrupt since there is a dedicated interrupt wire for each serial port (unless interrupts are shared). Then the CPU will start running the serial device driver which checks registers at I/0 addresses to find out what has happened. It finds out that the serial's transmit buffer is empty and waiting for another byte. So if there are more bytes to send, it sends the next byte to the serial port's I/0 address. This next byte should arrive when the previous byte is still in the transmit shift register and is still being transmitted bit-by-bit.

In review, when a byte has been fully transmitted out the transmit wire of the serial port and the shift register is now empty the following 3 things happen almost simultaneously:

  1. The next byte is moved from the transmit buffer into the transmit shift register
  2. The transmission of this new byte (bit-by-bit) begins
  3. Another interrupt is issued to tell the device driver to send yet another byte to the now empty transmit buffer

Thus we say that the serial port is interrupt driven. Each time the serial port issues an interrupt, the CPU sends it another byte. Once a byte has been sent to the transmit buffer by the CPU, then the CPU is free to pursue some other activity until it gets the next interrupt. The serial port transmits bits at a fixed rate which is selected by the user (or an application program). It's sometimes called the baud rate. The serial port also adds extra bits to each byte (start, stop and perhaps parity bits) so there are often 10 bits sent per byte. At a rate (also called speed) of 19,200 bits per second (bps), there are thus 1,920 bytes/sec (and also 1,920 interrupts/sec).

Doing all this is a lot of work for the CPU. This is true for many reasons. First, just sending one 8-bit byte at a time over a 32-bit data bus (or even 64-bit) is not a very efficient use of bus width. Also, there is a lot of overhead in handing each interrupt. When the interrupt is received, the device driver only knows that something caused an interrupt at the serial port but doesn't know that it's because a character has been sent. The device driver has to make various checks to find out what happened. The same interrupt could mean that a character was received, one of the control lines changed state, etc.

A major improvement has been the enlargement of the buffer size of the serial port from 1-byte to 16-bytes. This means that when the CPU gets an interrupt it gives the serial port up to 16 new bytes to transmit. This is fewer interrupts to service but data must still be transferred one byte at a time over a wide bus. The 16-byte buffer is actually a FIFO (First In First Out) queue and is often called a FIFO. See FIFOs for details about the FIFO along with a repeat of some of the above info.

Monday, May 26, 2008

Introduction to Serial Communications

Introduction to Serial Communications

All IBM PC and compatible computers are typically equipped with two serial ports and one parallel port. Although these two types of ports are used for communicating with external devices, they work in different ways.

A parallel port sends and receives data eight bits at a time over 8 separate wires. This allows data to be transferred very quickly; however, the cable required is more bulky because of the number of individual wires it must contain. Parallel ports are typically used to connect a PC to a printer and are rarely used for much else. A serial port sends and receives data one bit at a time over one wire. While it takes eight times as long to transfer each byte of data this way, only a few wires are required. In fact, two-way (full duplex) communications is possible with only three separate wires - one to send, one to receive, and a common signal ground wire.

--

Bi-Directional Communications

The serial port on your PC is a full-duplex device meaning that it can send and receive data at the same time. In order to be able to do this, it uses separate lines for transmitting and receiving data. Some types of serial devices support only one-way communications and therefore use only two wires in the cable - the transmit line and the signal ground.

--

Communicating by Bits

Once the start bit has been sent, the transmitter sends the actual data bits. There may either be 5, 6, 7, or 8 data bits, depending on the number you have selected. Both receiver and the transmitter must agree on the number of data bits, as well as the baud rate. Almost all devices transmit data using either 7 or 8 databits.

Notice that when only 7 data bits are employed, you cannot send ASCII values greater than 127. Likewise, using 5 bits limits the highest possible value to 31. After the data has been transmitted, a stop bit is sent. A stop bit has a value of 1 - or a mark state - and it can be detected correctly even if the previous data bit also had a value of 1. This is accomplished by the stop bit's duration. Stop bits can be 1, 1.5, or 2 bit periods in length.

--

The Parity Bit

Besides the synchronization provided by the use of start and stop bits, an additional bit called a parity bit may optionally be transmitted along with the data. A parity bit affords a small amount of error checking, to help detect data corruption that might occur during transmission. You can choose either even parity, odd parity, mark parity, space parity or none at all. When even or odd parity is being used, the number of marks (logical 1 bits) in each data byte are counted, and a single bit is transmitted following the data bits to indicate whether the number of 1 bits just sent is even or odd.

For example, when even parity is chosen, the parity bit is transmitted with a value of 0 if the number of preceding marks is an even number. For the binary value of 0110 0011 the parity bit would be 0. If even parity were in effect and the binary number 1101 0110 were sent, then the parity bit would be 1. Odd parity is just the opposite, and the parity bit is 0 when the number of mark bits in the preceding word is an odd number. Parity error checking is very rudimentary. While it will tell you if there is a single bit error in the character, it doesn't show which bit was received in error. Also, if an even number of bits are in error then the parity bit would not reflect any error at all.

Mark parity means that the parity bit is always set to the mark signal condition and likewise space parity always sends the parity bit in the space signal condition. Since these two parity options serve no useful purpose whatsoever, they are almost never used.

--

RS-232C

RS-232 stands for Recommend Standard number 232 and C is the latest revision of the standard. The serial ports on most computers use a subset of the RS-232C standard. The full RS-232C standard specifies a 25-pin "D" connector of which 22 pins are used. Most of these pins are not needed for normal PC communications, and indeed, most new PCs are equipped with male D type connectors having only 9 pins.

--

DCE and DTE Devices

Two terms you should be familiar with are DTE and DCE. DTE stands for Data Terminal Equipment, and DCE stands for Data Communications Equipment. These terms are used to indicate the pin-out for the connectors on a device and the direction of the signals on the pins. Your computer is a DTE device, while most other devices are usually DCE devices.

If you have trouble keeping the two straight then replace the term "DTE device" with "your PC" and the term "DCE device" with "remote device" in the following discussion.

The RS-232 standard states that DTE devices use a 25-pin male connector, and DCE devices use a 25-pin female connector. You can therefore connect a DTE device to a DCE using a straight pin-for-pin connection. However, to connect two like devices, you must instead use a null modem cable. Null modem cables cross the transmit and receive lines in the cable, and are discussed later in this chapter. The listing below shows the connections and signal directions for both 25 and 9-pin connectors.


25 Pin Connector on a DTE device (PC connection)

Male RS232 DB25

25 pin to 9 pin connector

Pin Number

Direction of signal:

1

Protective Ground

2

Transmitted Data (TD) Outgoing Data (from a DTE to a DCE)

3

Received Data (RD) Incoming Data (from a DCE to a DTE)

4

Request To Send (RTS) Outgoing flow control signal controlled by DTE

5

Clear To Send (CTS) Incoming flow control signal controlled by DCE

6

Data Set Ready (DSR) Incoming handshaking signal controlled by DCE

7

Signal Ground Common reference voltage

8

Carrier Detect (CD) Incoming signal from a modem

20

Data Terminal Ready (DTR) Outgoing handshaking signal controlled by DTE

22

Ring Indicator (RI) Incoming signal from a modem



9 Pin Connector on a DTE device (PC connection)

Male RS232 DB9

9 pin D connector

Pin Number

Direction of signal:

1

Carrier Detect (CD) (from DCE) Incoming signal from a modem

2

Received Data (RD) Incoming Data from a DCE

3

Transmitted Data (TD) Outgoing Data to a DCE

4

Data Terminal Ready (DTR) Outgoing handshaking signal

5

Signal Ground Common reference voltage

6

Data Set Ready (DSR) Incoming handshaking signal

7

Request To Send (RTS) Outgoing flow control signal

8

Clear To Send (CTS) Incoming flow control signal

9

Ring Indicator (RI) (from DCE) Incoming signal from a modem

The TD (transmit data) wire is the one through which data from a DTE device is transmitted to a DCE device. This name can be deceiving, because this wire is used by a DCE device to receive its data. The TD line is kept in a mark condition by the DTE device when it is idle. The RD (receive data) wire is the one on which data is received by a DTE device, and the DCE device keeps this line in a mark condition when idle.

RTS stands for Request To Send. This line and the CTS line are used when "hardware flow control" is enabled in both the DTE and DCE devices. The DTE device puts this line in a mark condition to tell the remote device that it is ready and able to receive data. If the DTE device is not able to receive data (typically because its receive buffer is almost full), it will put this line in the space condition as a signal to the DCE to stop sending data. When the DTE device is ready to receive more data (i.e. after data has been removed from its receive buffer), it will place this line back in the mark condition. The complement of the RTS wire is CTS, which stands for Clear To Send. The DCE device puts this line in a mark condition to tell the DTE device that it is ready to receive the data. Likewise, if the DCE device is unable to receive data, it will place this line in the space condition. Together, these two lines make up what is called RTS/CTS or "hardware" flow control. The Software Wedge supports this type of flow control, as well as Xon/XOff or "software" flow control. Software flow control uses special control characters transmitted from one device to another to tell the other device to stop or start sending data. With software flow control the RTS and CTS lines are not used.

DTR stands for Data Terminal Ready. Its intended function is very similar to the RTS line. DSR (Data Set Ready) is the companion to DTR in the same way that CTS is to RTS. Some serial devices use DTR and DSR as signals to simply confirm that a device is connected and is turned on. The Software Wedge sets DTR to the mark state when the serial port is opened and leaves it in that state until the port is closed. The DTR and DSR lines were originally designed to provide an alternate method of hardware handshaking. It would be pointless to use both RTS/CTS and DTR/DSR for flow control signals at the same time. Because of this, DTR and DSR are rarely used for flow control.

CD stands for Carrier Detect. Carrier Detect is used by a modem to signal that it has a made a connection with another modem, or has detected a carrier tone.

The last remaining line is RI or Ring Indicator. A modem toggles the state of this line when an incoming call rings your phone.

The Carrier Detect (CD) and the Ring Indicator (RI) lines are only available in connections to a modem. Because most modems transmit status information to a PC when either a carrier signal is detected (i.e. when a connection is made to another modem) or when the line is ringing, these two lines are rarely used.

--

9 to 25 Pin Adapters

The following table shows the connections inside a standard 9 pin to 25 pin adapter.

9-Pin Connector

25 Pin Connector

Pin 1 DCD

Pin 8 DCD

Pin 2 RD

Pin 3 RD

Pin 3 TD

Pin 2 TD

Pin 4 DTR

Pin 20 DTR

Pin 5 GND

Pin 7 GND

Pin 6 DSR

Pin 6 DSR

Pin 7 RTS

Pin 4 RTS

Pin 8 CTS

Pin 5 CTS

Pin 9 RI

Pin 22 RI


--

Baud vs. Bits per Second

The baud unit is named after Jean Maurice Emile Baudot, who was an officer in the French Telegraph Service. He is credited with devising the first uniform-length 5-bit code for characters of the alphabet in the late 19th century. What baud really refers to is modulation rate or the number of times per second that a line changes state. This is not always the same as bits per second (BPS). If you connect two serial devices together using direct cables then baud and BPS are in fact the same. Thus, if you are running at 19200 BPS, then the line is also changing states 19200 times per second. But when considering modems, this isn't the case.

Because modems transfer signals over a telephone line, the baud rate is actually limited to a maximum of 2400 baud. This is a physical restriction of the lines provided by the phone company. The increased data throughput achieved with 9600 or higher baud modems is accomplished by using sophisticated phase modulation, and data compression techniques.

--

Cables, Null Modems, and Gender Changers

In a perfect world, all serial ports on every computer would be DTE devices with 25-pin male "D" connectors. All other devices would be DCE devices with 25-pin female connectors. This would allow you to use a cable in which each pin on one end of the cable is connected to the same pin on the other end. Unfortunately, we don't live in a perfect world. Serial ports use both 9 and 25 pins, many devices can be configured as either DTE or DCE, and - as in the case of many data collection devices - may use completely non-standard or proprietary pin-outs. Because of this lack of standardization, special cables called null modem cables, gender changers and custom made cables are often required.

--

Cables Lengths

The RS-232C standard imposes a cable length limit of 50 feet. You can usually ignore this "standard", since a cable can be as long as 10000 feet at baud rates up to 19200 if you use a high quality, well shielded cable. The external environment has a large effect on lengths for unshielded cables. In electrically noisy environments, even very short cables can pick up stray signals. The following chart offers some reasonable guidelines for 24 gauge wire under typical conditions. You can greatly extend the cable length by using additional devices like optical isolators and signal boosters. Optical isolators use LEDs and Photo Diodes to isolate each line in a serial cable including the signal ground. Any electrical noise affects all lines in the optically isolated cable equally - including the signal ground line. This causes the voltages on the signal lines relative to the signal ground line to reflect the true voltage of the signal and thus canceling out the effect of any noise signals.

Baud Rate

Shielded Cable Length

Unshielded Cable Length

110

5000

1000

300

4000

1000

1200

3000

500

2400

2000

500

4800

500

250

9600

250

100

--

Gender Changers

A problem you may encounter is having two connectors of the same gender that must be connected. You can purchase gender changers at any computer or office supply store for under $5.

Note: The parallel port on a PC uses a 25 pin female connector which sometimes causes confusion because it looks just like a serial port except that it has the wrong gender. Both 9 and 25 pin serial ports on a PC will always have a male connector.

--

Null Modem Cables and Null Modem Adapters

If you connect two DTE devices (or two DCE devices) using a straight RS232 cable, then the transmit line on each device will be connected to the transmit line on the other device and the receive lines will likewise be connected to each other. A Null Modem cable or Null Modem adapter simply crosses the receive and transmit lines so that transmit on one end is connected to receive on the other end and vice versa. In addition to transmit and receive, DTR & DSR, as well as RTS & CTS are also crossed in a Null Modem connection.

Null Modem adapters are available at most computer and office supply stores for under $5.

--

Synchronous and Asynchronous Communications

There are two basic types of serial communications, synchronous and asynchronous. With synchronous communications, the two devices initially synchronize themselves to each other, and then continually send characters to stay in sync. Even when data is not really being sent, a constant flow of bits allows each device to know where the other is at any given time. That is, each character that is sent is either actual data or an idle character. Synchronous communications allows faster data transfer rates than asynchronous methods, because additional bits to mark the beginning and end of each data byte are not required. The serial ports on IBM-style PCs are asynchronous devices and therefore only support asynchronous serial communications.

Asynchronous means "no synchronization", and thus does not require sending and receiving idle characters. However, the beginning and end of each byte of data must be identified by start and stop bits. The start bit indicates when the data byte is about to begin and the stop bit signals when it ends. The requirement to send these additional two bits causes asynchronous communication to be slightly slower than synchronous however it has the advantage that the processor does not have to deal with the additional idle characters.

An asynchronous line that is idle is identified with a value of 1 (also called a mark state). By using this value to indicate that no data is currently being sent, the devices are able to distinguish between an idle state and a disconnected line. When a character is about to be transmitted, a start bit is sent. A start bit has a value of 0 (also called a space state). Thus, when the line switches from a value of 1 to a value of 0, the receiver is alerted that a data character is about to be sent.

Sunday, May 25, 2008

Accessing the Serial Port from C

Accessing the Serial Port from C

by Richard Lancaster

I get the feeling that people hate the serial port and don't want to touch it with a barge pole. Acorn didn't make it any more friendly by spreading the relevant information over three chapters of the PRM, nor did we get a C library to ease the pain. What follows is my own concise guide to accessing the serial port via kernel.h.

Introduction

There are several things that must be done before you can actually use the serial port:

  • Enabling the serial input buffer,
  • Set the transmission and reception baud rates,
  • Set the data format,
  • Set the handshaking type,

You can then either read or write to the serial input and output buffers (the serial port is buffered). Finally you can flush the buffers if necessary.

Enabling the serial input buffer

The serial input buffer is not by default turned on. It therefore will not accept data from the outside world, and so you will not be able to read in serial data. The buffer can be set to one of three states :

Serial input buffer states
0 Off (No data ever read from the serial cable into the buffer; computer functions as normal) [This is the default].
1 Keyboard input from serial port (The keyboard is disabled and keyboard input is taken from the serial port. Warning, if you don't have a serial keyboard or another computer connected then this will effectively lock you out of your computer until you perform a hard reset!).
2 Background (Data read into the serial input buffer and can be accessed by a program when necessary, computer functions as normal).

The state can be set using the following piece of code (where r is of type _kernel_swi_regs):

  r.r[0] = 2;
r.r[1] = level;
_kernel_swi(OS_Byte, &r, &r);

Where level is 0, 1 or 2 as above. If you want normal serial port operation then set it to 2. I have left out any error checking etc. for reasons of space.

Setting the baud rates

The transmission baud rate can be set with the following piece of code :

  r.r[0] = 6;
r.r[1] = rate;
_kernel_swi(OS_SerialOp, &r, &r);

Where rate is the baud rate code, which can be found from this table:

Unknown - FixMe
Code Baud Code Baud Code Baud Code Baud
0 9600 4 1200 8 19200 12 600
1 75 5 2400 9 50 13 1800
2 150 6 4800 10 110 14 3600
3 300 7 9600 11 134.5 15 7200

The reception rate can be set in the same way but with R0 set to 5 instead of 6.

I haven't got access to the rate codes for the faster baud rates on the RISC PC so perhaps someone would like to fill me in.

Setting the data format

The following piece of code allows you to set how many data bits you want, how many stop bits you want and whether you want parity etc. :

  r.r[0] = 1;
r.r[1] = format;
_kernel_swi(OS_SerialOp, &r, &r);

Where format is a 32 bit word that describes the data format you want to use. Bits 0 and 1 describe the number of data bits, bit 2 specifies the number of stop bits, bit 3 specifies whether you want parity bits added, bits 4 and 5 set the type of parity and, finally bits 6 to 31 must be set to 0, presumably to allow for future expansion. This table sets out the meaning of different bit values:

Unknown - FixMe
5 4 Parity type 3 Parity bits 2 Stop bits 1 0 Data bits
0 0 Odd parity 0 No 0 1 0 0 8
0 1 Even parity 1 Yes 1 21 0 1 7
1 0 Transmit 1; ignore received



1 0 6
1 1 Transmit 0; ignore received



1 1 5

Some example settings would be : format = 0x0; would give 8 data bits, 1 stop bit and no parity, format = 0xb; would give 5 data bits, 1 stop bit and odd parity.

Setting the handshaking type

The handshaking type can be set with the following code :

  r.r[0] = 0;
r.r[1] = XORMask;
r.r[2] = ANDMask;
_kernel_swi(OS_SerialOp, &r, &r);

This call sets a register that specifies the handshaking type. The call allows you to alter only the bits of the register you want to while leaving the others intact. You should therefore ensure that you don't alter any of the bits that you don't explicitly intend to. The SWI decides whether you want a bit set, unset or left alone by looking as the corresponding bits in the AND and XOR masks. The following table tells you what will happen to a bit depending on the state of that bit in the masks:

Unknown - FixMe
XORMask bit ANDmask bit Effect on register bit
0 0 Bit becomes 0
0 1 Bit is unchanged
1 0 Bit becomes 1
1 1 Bit is toggled

The three register bits that concern us are bits 0, 1 and 2 the rest can be left alone. The purpose of each bit is detailed below :

0 If set then XON/XOFF handshaking is employed.

1 If bit is unset then use DCD handshaking.

2 If bit is unset then use DSR handshaking.

Therefore if we wish to turn XON/XOFF handshaking on then we use: ANDMask = 0xfffffffe; XORMask = 0x00000001;

This is the most confusing of the serial port calls and you might like to read the description of OS_SerialOp 0 in the PRMs for more details.

Reading bytes from the input buffer

Bytes can be read from the input buffer using the following code :

  r.r[0] = 4;
r.r[1] = 300;
_kernel_swi(OS_SerialOp, &r, &r);

If there is a byte in the input buffer then it will be placed in R1. If there isn't any data in the buffer then R1 will be preserved. You can therefore test whether you have actually read any data out of the buffer by placing a number greater then 255 (i.e. larger than can be stored in a byte) into R1 before you call the SWI (in the example I have used 300). If after the SWI R1 is still the number you put in then the buffer was empty, otherwise R1 will hold the byte read from the buffer.

Writing bytes to the output buffer

This stumped me for a very long time. The reason is that the OS_SerialOp 3 command that sends bytes to the buffer sets the processor carry flag if the buffer was full and the byte wasn't sent. You might wonder what the problem is. Well, you obviously have to detect that the byte was not sent otherwise you will lose data. The problem is how do you read the carry flag from C? I almost resorted to writing an assembler routine but I then found the following function :

  _kernel_oserror *_kernel_swi_c(int no, _kernel_swi_regs *in,
_kernel_swi_regs *out, int *carry);

As well as calling the SWI as normal this sets the integer carry to the status of the carry flag on exit. The following piece of code will therefore transmit bytes :

  int notsent; /* read from carry flag */
r.r[0] = 3;
r.r[1] = (int) byte;
_kernel_swi_c(OS_SerialOp, &r, &r, &notsent);

Where on exit you can test notsent to see whether the byte was transmitted or not.

Detecting data in the input buffer

If you simply wish to see whether there is any data in the input buffer without extracting a byte then you can use the following piece of code :

  int bufferempty;
r.r[0] = 152;
r.r[1] = 1;
_kernel_swi_c(OS_Byte, &r, &r, &bufferempty);

Here bufferempty is set to 1 if the buffer is empty and 0 if it has data in.

Flushing the buffers

Finally you can flush the buffers using :

  r.r[0] = 21;
r.r[1] = buffer;
_kernel_swi(OS_Byte, &r, &r);

Where buffer is set to 1 for the input buffer and to 2 for the output buffer.

1 but 1 stop bit if you use an 8 bit word with parity and 1.5 stop bits if you use a 5 bit word without parity

Thursday, May 22, 2008

Find out linux serial ports connection

Serial Support

Simple run dmesg command
$ dmesg | grep tty
Output:

[   37.531286] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[ 37.531841] 00:0b: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[ 37.532138] 0000:04:00.3: ttyS1 at I/O 0x1020 (irq = 18) is a 16550A

setserial command

setserial is a program designed to set and/or report the configuration information associated with a serial port. This information includes what I/O port and IRQ a particular serial port is using, and whether or not the break key should be interpreted as the Secure Attention Key, and so on. Just type the following command:
$ setserial -g /dev/ttyS[0123]
Output:

/dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4
/dev/ttyS1, UART: 16550A, Port: 0x1020, IRQ: 18
/dev/ttyS2, UART: unknown, Port: 0x03e8, IRQ: 4
/dev/ttyS3, UART: unknown, Port: 0x02e8, IRQ: 3

seserial with -g option help to find out what physical serial ports your Linux box has.

Wednesday, May 21, 2008

Read Temperature Measurement by serial port

The program given at the end of this web page can be used to read the temperature data from the serial port connected to the 512 Point Temperature Measurement System developed by Prof. Peter H. Anderson and his students. It has been tested on a SUN LX running Solaris 7, but it should work on other Unix implementations as well. The program will make a measurement every 900 seconds assuming that one temperature sensor is connected to take measurements. The data is then written to a file in the following format:

Run  count  temperature    serial number
1 00 12.09 106F383B 0000007A Sun Dec 10 23:48:04 2000
1 00 12.02 106F383B 0000007A Mon Dec 11 00:03:04 2000
1 00 11.96 106F383B 0000007A Mon Dec 11 00:18:04 2000

*/
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "termios.h"
#include "stdio.h"
#include "time.h"

main()
{
time_t t;
struct tm *tt;
/* FILE to log the temperature */
FILE *logfile;
int fd,res,counter;
struct termios newtio;
char buf[255];

/* Open the serial port ttya.
O_NDELAY: Don't wait for DCD signal line when opening the port.
O_RDWR: Open for read and write */
fd = open("/dev/ttya", O_RDWR | O_NDELAY);

if (fd <>

bzero(&newtio, sizeof(newtio));

/* port settings: 9600, 8bit
CREAD enables port to read data
CLOCAL prevents that we become the owner of the port and react to
control characters and are subject to hang up signals etc. */
newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;

/* Set line options */
newtio.c_lflag = 0;

newtio.c_cc[VTIME] = 0; /* ignore timer */
newtio.c_cc[VMIN] = 0; /* no blocking read */

tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

counter = 0;
/* infinite loop to measure the temperature */
while (1==1)
{
int i;
char *buf2;
logfile = fopen("temp.log","a");

/* Send a character. This will initiate the temperature reading */
i = write(fd,"a",1);
if (i == -1)
{
printf("unable to write\n");
printf("exit ...\n");
exit(-1);
}

/* Wait a short time so the PIC can finish a reading */
sleep(2);

/* Read the data sent by the PIC */
res = read(fd,buf,255);

/* Print a header every 50 output lines */
if (counter % 50 == 0)
fprintf(logfile,"Run count temperature serial number\n");

/* Print the 'run' on which this reading was performed */
fprintf(logfile," %c ",buf[0]);

/* Hexadecimal number counting the readings */
fprintf(logfile," %c%c ",buf[1],buf[2]);

/* Now print the temperature */
/* Temperature starts at position 4 and ends with space character */
i = 4;
while (buf[i] != 32)
fprintf(logfile,"%c",buf[i++]);

/* Print rest of reading, which is the serial number */
fprintf(logfile," ");
for (;i

if (res!=0)
{
/* Get the current time */
t = time(NULL);
buf2 = ctime(&t);
fprintf(logfile," %s",buf2);
}
++counter;
fclose(logfile);
sleep(898);
}
}

Using serial ports in Linux,Read data from GPS device

Using serial ports in Linux is easy, but there are some tricks that are hard to know when to use. This tutorial is specifically about how to write a program that can read NMEA data (such as from a GPS device) and to parse the data that is returned. The following code was compiled successfully under Slackware 10 on a Mini-ITX board connected to a GPS device.
To Compile: g++ tut_linux_serial.cpp -g -o tut_linux_serial -O3
-g to allow gdb to work
-o tut_linux_serial is the output name
-O3 is the optimization level

using namespace std;
#define SERIALPORT "/dev/ttyS0" // port the device is plugged in to
#define BAUDRATE B38400 // baud rate the device spits out at
#define UPDATE_RATE 20 // update speed in Hz (probably set anywhere from 10-50

#include
#include // timers
#include // timers / serial
#include // serial
#include // serial, file
#include // serial, file

string serial_buffer; // Unprocessed data off the serial port
int fd_serial;

void timer_handler(int x); // used in automation
void serial_handler(int status); // interrupt function called on new data (position isn't guaranteed), pass to ReadSerial()


int main() {
struct termios tty; // will be used for new port settings
struct termios oldtty; // will be used to save old port settings

fd_serial = open(SERIALPORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd_serial < 0) {
printf("\nUnable to write to serial port (%s), are you root?\n", SERIALPORT);
_exit(1);
}
tcgetattr(fd_serial, &oldtty); // save current port settings
bzero(&tty, sizeof(tty)); // Initialize the port settings structure to all zeros
tty.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD | CRTSCTS; // 8N1
tty.c_iflag = IGNPAR;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_cc[VMIN] = 0; // 0 means use-vtime
tty.c_cc[VTIME] = 1; // time to wait until exiting read (tenths of a second)

tcflush(fd_serial, TCIFLUSH); // flush old data
tcsetattr(fd_serial, TCSANOW, &tty); // apply new settings
fcntl(fd_serial, F_SETOWN, getpid()); // enable our PID to receive serial interrupts
fcntl(fd_serial, F_SETFL, FASYNC);


struct sigaction saio; // set the serial interrupt handler
saio.sa_handler = serial_handler; // to this function
sigemptyset(&saio.sa_mask); // clear existing settings
saio.sa_flags = 0; // make sure sa_flags is cleared
saio.sa_restorer = NULL; // no restorer
sigaction(SIGIO, &saio, NULL); // apply new settings


// set up the main timer
struct itimerval timer1; // set up the timers
signal(SIGALRM, timer_handler); // call this function on rollover
timer1.it_interval.tv_sec = 0; // reset val
timer1.it_interval.tv_usec = (__suseconds_t)((1.0/(double)UPDATE_RATE)*1000000); // reset val (converts UPDATE_RATE from Hz into microseconds
timer1.it_value.tv_sec = timer1.it_interval.tv_sec; // initial val
timer1.it_value.tv_usec = timer1.it_interval.tv_usec; // initial val
setitimer(ITIMER_REAL, &timer1, NULL); // apply new settings


while (1) { } // press ctrl-c to exit the program

write(fd_serial, "t", 1); // where t is the test char to send

tcsetattr(fd_serial, TCSANOW, &oldtty); // restore the old port settings before quitting
}



void serial_handler (int status) {
// this function is called whenever there is new data to be read off the serial port.
// It has to execute quickly, so the data processing is *not* done here.
char temp_buffer[256*2]; // max chars to read at once, if you don't read the entire buffer then the function will get called again
int len = read(fd_serial, temp_buffer, sizeof(temp_buffer)); // do the actual read
temp_buffer[len] = 0; // null terminate the string **important**
serial_buffer += temp_buffer; // append what we read to the serial_buffer of unprocessed data
}




void timer_handler(int x) {
// this function gets called UPDATE_RATE times per second, it handles the data processing off the serial port
// ** note that serial_buffer gets populated from serial_handler
signal(SIGALRM, timer_handler); // clear the interrupt flag, so it will trigger again when we exit

int infinite_loop_preventer = 0; // sanity check to make sure this section won't run forever
if (serial_buffer.length() > 0) { // unprocessed data exists
int start_pos; // position of the first $ char (all packets must start with a $
int end_pos_n; // position of the first \n char following the first $
int end_pos_r; // position of the first \r char following the first $
int end_pos; // set to the lesser of end_pos_n or end_pos_r

do {
infinite_loop_preventer++;
// 1) find start char
// 2) find end char (\n or \r)
// 3) process that data
// loop while there's more data

start_pos = serial_buffer.find("$", 0);
end_pos_n = serial_buffer.find("\n", start_pos);
end_pos_r = serial_buffer.find("\r", start_pos);
end_pos = end_pos_n < end_pos_r ? end_pos_n : end_pos_r; // choose the lesser of the two ending points

if (start_pos != string::npos && end_pos != string::npos) {
string data = serial_buffer.substr(start_pos, end_pos-start_pos); // just the current packet
serial_buffer = serial_buffer.substr(end_pos+1); // remove parsed section
// cout << "Debug: ";
// cout << " start_pos=" <<>
// cout << " end_pos=" <<>
// cout << " data.len=" <<>
// cout << data=" << data << ">
// cout << serial_buffer=" << serial_buffer << ">
// cout <<>
cout << data << endl;
}
} while (start_pos != string::npos && end_pos != string::npos && infinite_loop_preventer < color="#008800"> // keep processing if buffer has end char in it

}
}

Sample Output:

GPGLL,3354.4970,N,11759.5354,W,025604,V,S*52
GPGLL,3354.4980,N,11759.5353,W,025605,V,S*55
GPGLL,3354.4990,N,11759.5355,W,025606,V,S*22

Talking to Device Files (writes and IOCTLs)

Device files are supposed to represent physical devices. Most physical devices are used for output as well as input, so there has to be some mechanism for device drivers in the kernel to get the output to send to the device from processes. This is done by opening the device file for output and writing to it, just like writing to a file. In the following example, this is implemented by device_write.

This is not always enough. Imagine you had a serial port connected to a modem (even if you have an internal modem, it is still implemented from the CPU's perspective as a serial port connected to a modem, so you don't have to tax your imagination too hard). The natural thing to do would be to use the device file to write things to the modem (either modem commands or data to be sent through the phone line) and read things from the modem (either responses for commands or the data received through the phone line). However, this leaves open the question of what to do when you need to talk to the serial port itself, for example to send the rate at which data is sent and received.

The answer in Unix is to use a special function called ioctl (short for Input Output ConTroL). Every device can have its own ioctl commands, which can be read ioctl's (to send information from a process to the kernel), write ioctl's (to return information to a process), [1] both or neither. The ioctl function is called with three parameters: the file descriptor of the appropriate device file, the ioctl number, and a parameter, which is of type long so you can use a cast to use it to pass anything. [2]

The ioctl number encodes the major device number, the type of the ioctl, the command, and the type of the parameter. This ioctl number is usually created by a macro call (_IO, _IOR, _IOW or _IOWR --- depending on the type) in a header file. This header file should then be included both by the programs which will use ioctl (so they can generate the appropriate ioctl's) and by the kernel module (so it can understand it). In the example below, the header file is chardev.h and the program which uses it is ioctl.c.

If you want to use ioctls in your own kernel modules, it is best to receive an official ioctl assignment, so if you accidentally get somebody else's ioctls, or if they get yours, you'll know something is wrong. For more information, consult the kernel source tree at Documentation/ioctl-number.txt.

Example 7-1. chardev.c

/*
* chardev.c - Create an input/output character device
*/

#include /* We're doing kernel work */
#include /* Specifically, a module */
#include
#include /* for get_user and put_user */

#include "chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80

/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
static int Device_Open = 0;

/*
* The message the device will give when asked
*/
static char Message[BUF_LEN];

/*
* How far did the process reading the message get?
* Useful if the message is larger than the size of the
* buffer we get to fill in device_read.
*/
static char *Message_Ptr;

/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_open(%p)\n", file);
#endif

/*
* We don't want to talk to two processes at the same time
*/
if (Device_Open)
return -EBUSY;

Device_Open++;
/*
* Initialize the message
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
return SUCCESS;
}

static int device_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
#endif

/*
* We're now ready for our next caller
*/
Device_Open--;

module_put(THIS_MODULE);
return SUCCESS;
}

/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
char __user * buffer, /* buffer to be
* filled with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
/*
* Number of bytes actually written to the buffer
*/
int bytes_read = 0;

#ifdef DEBUG
printk(KERN_INFO "device_read(%p,%p,%d)\n", file, buffer, length);
#endif

/*
* If we're at the end of the message, return 0
* (which signifies end of file)
*/
if (*Message_Ptr == 0)
return 0;

/*
* Actually put the data into the buffer
*/
while (length && *Message_Ptr) {

/*
* Because the buffer is in the user data segment,
* not the kernel data segment, assignment wouldn't
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment.
*/
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}

#ifdef DEBUG
printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length);
#endif

/*
* Read functions are supposed to return the number
* of bytes actually inserted into the buffer
*/
return bytes_read;
}

/*
* This function is called when somebody tries to
* write into our device file.
*/
static ssize_t
device_write(struct file *file,
const char __user * buffer, size_t length, loff_t * offset)
{
int i;

#ifdef DEBUG
printk(KERN_INFO "device_write(%p,%s,%d)", file, buffer, length);
#endif

for (i = 0; i < length && i < BUF_LEN; i++)
get_user(Message[i], buffer + i);

Message_Ptr = Message;

/*
* Again, return the number of input characters used
*/
return i;
}

/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
int device_ioctl(struct inode *inode, /* see include/linux/fs.h */
struct file *file, /* ditto */
unsigned int ioctl_num, /* number and param for ioctl */
unsigned long ioctl_param)
{
int i;
char *temp;
char ch;

/*
* Switch according to the ioctl called
*/
switch (ioctl_num) {
case IOCTL_SET_MSG:
/*
* Receive a pointer to a message (in user space) and set that
* to be the device's message. Get the parameter given to
* ioctl by the process.
*/
temp = (char *)ioctl_param;

/*
* Find the length of the message
*/
get_user(ch, temp);
for (i = 0; ch && i < BUF_LEN; i++, temp++)
get_user(ch, temp);

device_write(file, (char *)ioctl_param, i, 0);
break;

case IOCTL_GET_MSG:
/*
* Give the current message to the calling process -
* the parameter we got is a pointer, fill it.
*/
i = device_read(file, (char *)ioctl_param, 99, 0);

/*
* Put a zero at the end of the buffer, so it will be
* properly terminated
*/
put_user('\0', (char *)ioctl_param + i);
break;

case IOCTL_GET_NTH_BYTE:
/*
* This ioctl is both input (ioctl_param) and
* output (the return value of this function)
*/
return Message[ioctl_param];
break;
}

return SUCCESS;
}

/* Module Declarations */

/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations Fops = {
.read = device_read,
.write = device_write,
.ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* a.k.a. close */
};

/*
* Initialize the module - Register the character device
*/
int init_module()
{
int ret_val;
/*
* Register the character device (atleast try)
*/
ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);

/*
* Negative values signify an error
*/
if (ret_val < 0) {
printk(KERN_ALERT "%s failed with %d\n",
"Sorry, registering the character device ", ret_val);
return ret_val;
}

printk(KERN_INFO "%s The major device number is %d.\n",
"Registeration is a success", MAJOR_NUM);
printk(KERN_INFO "If you want to talk to the device driver,\n");
printk(KERN_INFO "you'll have to create a device file. \n");
printk(KERN_INFO "We suggest you use:\n");
printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
printk(KERN_INFO "The device file name is important, because\n");
printk(KERN_INFO "the ioctl program assumes that's the\n");
printk(KERN_INFO "file you'll use.\n");

return 0;
}

/*
* Cleanup - unregister the appropriate file from /proc
*/
void cleanup_module()
{
int ret;

/*
* Unregister the device
*/
ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

/*
* If there's an error, report it
*/
if (ret < 0)
printk(KERN_ALERT "Error: unregister_chrdev: %d\n", ret);
}

Example 7-2. chardev.h

/*
* chardev.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (in chardev.c) and the process calling ioctl (ioctl.c)
*/

#ifndef CHARDEV_H
#define CHARDEV_H

#include

/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAJOR_NUM 100

/*
* Set the message of the device driver
*/
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAJOR_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/

/*
* Get the message of the device driver
*/
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
/*
* This IOCTL is used for output, to get the message
* of the device driver. However, we still need the
* buffer to place the message in to be input,
* as it is allocated by the process.
*/

/*
* Get the n'th byte of the message
*/
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
/*
* The IOCTL is used for both input and output. It
* receives from the user a number, n, and returns
* Message[n].
*/

/*
* The name of the device file
*/
#define DEVICE_FILE_NAME "char_dev"

#endif

Example 7-3. ioctl.c

/*
* ioctl.c - the process to use ioctl's to control the kernel module
*
* Until now we could have used cat for input and output. But now
* we need to do ioctl's, which require writing our own process.
*/

/*
* device specifics, such as ioctl numbers and the
* major device file.
*/
#include "chardev.h"

#include /* open */
#include /* exit */
#include /* ioctl */

/*
* Functions for the ioctl calls
*/

ioctl_set_msg(int file_desc, char *message)
{
int ret_val;

ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

if (ret_val < 0) {
printf("ioctl_set_msg failed:%d\n", ret_val);
exit(-1);
}
}

ioctl_get_msg(int file_desc)
{
int ret_val;
char message[100];

/*
* Warning - this is dangerous because we don't tell
* the kernel how far it's allowed to write, so it
* might overflow the buffer. In a real production
* program, we would have used two ioctls - one to tell
* the kernel the buffer length and another to give
* it the buffer to fill
*/
ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

if (ret_val < 0) {
printf("ioctl_get_msg failed:%d\n", ret_val);
exit(-1);
}

printf("get_msg message:%s\n", message);
}

ioctl_get_nth_byte(int file_desc)
{
int i;
char c;

printf("get_nth_byte message:");

i = 0;
while (c != 0) {
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);

if (c < 0) {
printf
("ioctl_get_nth_byte failed at the %d'th byte:\n",
i);
exit(-1);
}

putchar(c);
}
putchar('\n');
}

/*
* Main - Call the ioctl functions
*/
main()
{
int file_desc, ret_val;
char *msg = "Message passed by ioctl\n";

file_desc = open(DEVICE_FILE_NAME, 0);
if (file_desc < 0) {
printf("Can't open device file: %s\n", DEVICE_FILE_NAME);
exit(-1);
}

ioctl_get_nth_byte(file_desc);
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc, msg);

close(file_desc);
}

Notes


Notice that here the roles of read and write are reversed again, so in ioctl's read is to send information to the kernel and write is to receive information from the kernel.


This isn't exact. You won't be able to pass a structure, for example, through an ioctl --- but you will be able to pass a pointer to the structure.

Tuesday, May 20, 2008

Serial Drivers

The usual view of a serial port

When discussing the software implementation of a serial port, the first thing that comes to mind is /dev/ttyS0, as this is the most known character of serial communication, at least on PC-class computers. Since /dev/ttyS0 is a file special file of type ``char'', a serial driver is often considered a conventional char driver, and phrases like ``char drivers are exemplified by serial ports'' come to mind. Unfortunately, the exemplification is basically wrong.

If you look in the real code, you'll see how the ``char driver'' idea only scratches the surface of what a serial driver is, and that's why a serial driver doesn't quite lend itself to be the prototypical example of char drivers.

Actually, the ``char driver'' abstraction doesn't correctly describe serial device drivers because there is not specific major number associated to each of them. Actually, you can have add-on serial ports that plug in your computer and are managed by specific kernel modules but do not get assigned a new major number.

Looking in /proc/devices you'll find that major 4 is associated to the ttyS driver, but that's a white lie: text-mode console devices belong to major 4 too, and, actually, Linux-2.0 used the more general ``ttyp'' name for major number 4.

What makes a serial port different from a more conventional char driver like the printer port or a tape drive is its being part of the tty abstraction. Since a serial communication channel can be used to plug an alphanumeric terminal, a serial device driver must be integrated in the terminal emulator layer, called the ``tty'' abstraction, from the name of ancient tele-type devices (still in wide use when Unix was being written)

Overview of tty management

Flexible and powerful tty handling is made up of several building blocks. You must consider that there exist a huge range of tty devices, from VGA and frame-buffer based text consoles up to serial communication channels and virtual terminals as exemplified by (but not limited to) the xterm application.

Figure 1 shows the various building blocks that are involved with operation of a serial driver. Most files live in drivers/char, if not, the directory specified is relative to the root directory of the kernel source tree. The figure shows how each building block is registered (registration is there in order to allow each block to be implemented as a kernel module).



Figure 1

The image is available as PostScript here


The file fs/devices.c exports the interface used by most system resources to register device drivers, each identified by a major number). This is how tty_register_driver gets hold of a major number if it needs it to support the new tty driver (an object that is introduced below). The function is defined by tty_io.c, which also defines the file operations that are used to act on tty devices. File operation are the driver-specific implementations of read, write and the other system calls that relate to file access.

What serial.c or other serial drivers do in order to run their own code is registering a struct tty_driver object. The ``driver'' declares the major number and the range of minor numbers it is going to manage, as well as a number of operations it supports. The operations are concerned with input and output of data as well as flow control and communication with higher layers. The implementation of these operations, together with interrupt handling and actual input/output of data is the scope of hardware drivers for serial ports.

The data flow between user space and the serial device driver, therefore, is mediated by the tty layer, that implements functionalities that are common to all tty-type devices. However, not all of tty management is defined be tty_io.c, most of the policy is define by the line discipline, a software module that disciplines how a physical tty I/O line is used. The default line discipline for Linux is called N_TTY, a name that will be explained later. If n_tty is active, input data reaches user space via the usual /dev/ interface and the standard terminal I/O handling (i.e, all the features defined by , that make terminal handling so powerful and so difficult).

The red line in figure 1 shows the logical flow of data, from the hardware channel up to the user-accessible device special file and backwards. What keeps it all together is the struct tty data item, that in itself includes a pointer to all three relevant objects: the file_operations structure that is used in communicating with user space, the tty_driver structure that hosts functions to control real hardware, and the tty_ldisc structure that lists all entry points to the current line discipline.

Why so complex?

Sure this kind of arrangement may look exceedingly complex. However, as usual, the extra complexity is meant to make things ultimately more flexible and powerful. Adding support for new serial hardware may be different from writing a conventional char driver, but this setup guarantees full tty emulation on all serial ports without any code replication or unneeded complexity in the low-level driver.

Another advantage of this kind of arrangement, possibly even more important than generalized tty support, is in the ability to change the line discipline associated to each tty device. Unlike typical device drivers, whose task is connecting an hardware device with user space, a serial driver has nothing to do with user space; data it receives from the hardware is passed over to the line discipline, and data it receives from software comes via a line discipline method.

A serial driver, therefore, is not concerned in any way with data transfer to/from user space. The task is left to the line discipline, together with all the hairy termios handling. This makes it possible for serial data to be steered to a different user-space access facility than its associated ttyS device special file.

PPP and slip

Whenever you dial your modem with ppp to connect to the Internet, or use the simpler slip communication protocol to connect the PC to your Linux palm-top, you are exploiting the complexity just shown. Both ppp and slip implement their own line discipline; when either of them is run, the tty device is switched to a different line discipline in order to detach /dev/ttyS0 from the serial port and keep all of serial communication within the kernel.

Figure 2 shows the conceptual layout in the slip case. I chose not to use ppp in the example to avoid extra complexity or incorrect simplification. In version 2.4 of the Linux kernel the ppp software implementation is split in several files (once again, it's more structured to be more powerful and avoid code replication across similar devices). The two protocols behave otherwise in the same manner.



Figure 2

The image is available as PostScript here


The role of the slip driver, as shown, is registering both a network device (depicted as slip0 and a line discipline for tty devices (called N_SLIP). When the tty device is switched to the new line discipline, TCP/IP communication can begin. The new line discipline sets up data transfer between serial hardware and the network layer; when it is active, nothing can be read from or written to the associated /dev/ttyS device. As soon as the device (/dev/ttyS0 or equivalent) is closed, the default line discipline is restored. Actually, that's the main reason why neither slattach nor pppd can exit after setting up the network channel.

The individual data structures

There are three main data structures involved in tty management (and thus, serial communication):

  • struct tty_struct: this is the data structure that stays at the core of tty management. It includes both of the following structures. An instance of tty_struct is created any time a new tty device is opened, and exists until it is last closed. Note that actual code (in tty_io.c) is complicated by the need to preserve termios settings across close and open, at least for some of the ttys (like serial ports).
  • struct tty_driver: this is the low level hardware handling. At open time, the function get_tty_driver retrieves the driver for the current tty an places it into the driver field of tty_struct, where it is further accessed.
     struct tty_driver {

    /* the driver states which range of devices it supports */
    short major; /* major device number */
    short minor_start; /* start of minor device number*/
    short num; /* number of devices */

    /* and has its own operations */
    int (*open)();
    void (*close)();
    int (*write)();
    int (*ioctl)(); /* device-specific control */

    /* return information on buffer state */
    int (*write_room)(); /* how much can be written */
    int (*chars_in_buffer)(); /* how much is there to read */

    /* flow control, input and output */
    void (*throttle)();
    void (*unthrottle)();
    void (*stop)();
    void (*start)();

    /* and callbacks for /proc/tty/driver/ */
    int (*read_proc)();
    int (*write_proc)();
    };
  • struct tty_ldisc: the structure is referenced by the ldisc field of tty_struct. At open time the field is initialized to reference n_tty, and user programs can change the current line discipline via ioctl, as explained in a while.
     struct tty_ldisc {

    /* routines called from above */
    int (*open)();
    void (*close)();
    ssize_t (*read)();
    ssize_t (*write)();
    int (*ioctl)();

    /* routines called from below */
    void (*receive_buf)();
    int (*receive_room)();
    void (*write_wakeup)();
    };

The structures are declared in three different files: tty_struct is a complex structure defined in include/linux/tty.h, a header generally devoted to tty issues. Actually, it is not as interesting to look at as the other two, because user modules rarely need to directly interact with it.

include/linux/tty_driver.h and include/linux/tty_ldisc.h are devoted exclusively to the definition of the relevant data structures. The files carry a prominent comment block that explains the exact role of most of the fields. Unlike tty_struct, both tty_driver and tty_ldisc are actively used by authors of device driver modules.

Typically, a kernel module that supports a new kind of hardware transmission will implement a tty_driver structure, while a module that uses generic serial hardware for a new purpose will implement a line discipline. For example, if you have a special keyboard that transmits data via a standard RS-232 serial port, you'll need a line discipline that gathers data packets and send them to either the input mechanism (see drivers/input/input.c and include/linux/input.h) or to the generic keyboard driver (using handle_scancode(), exported by drivers/char/keyboard.c).

Listing 1 shows the most important operations declared by the tty_driver data structure, while listing 2 depicts those exported by the line discipline. Note that those listings are by no means complete, if you look for authoritative information, you should read the relevant header files.

Data flow in reading and writing



Figure 3

The image is available as PostScript here


Figure 3 visualizes how data flows from user space down to hardware interfaces and backwards. It refers to the specific case of a standard PC serial port with the default line discipline attached. The logical stacking of line disciplines (near to user space) and tty drivers (near to the hardware itself) should be apparent.

While writing data is straightforward, the reading process may need some explanation. Reading is more complex than writing because there's no direct causal relationship between hardware (that pushes up data when it arrives) and user space (that requests data when it needs it). The solution is, as you expected, use of buffering: data received by hardware remains on hold in a kernel buffer until a user space program requests it; whenever a user program asks for data and the buffer is empty, the program is put to sleep, and is awaken only when the buffer is filled with at least partially. Note that a write buffer exists as well, however, the write implementation is much more straightforward because each step towards hardware level is directly driven by the step above it. There are no uncontrolled delays in data transmission, and the buffer is only needed to decouple hardware transmission from program flow. The figure does not show it for simplicity.

When tty devices are concerned, the read buffer lives within the tty data structure; while this makes struct tty_struct considerably bigger, there is no point in keeping the buffer elsewhere: each tty can't transfer data without a buffer, and tty devices are dynamically allocated so no memory is wasted in buffers for unused devices.

Actually, tty-related buffering is organized in two levels: kernel developers chose to provide both a ``conventional'' buffer, where data is waiting to be eaten by the line discipline (i.e., in the default case, being transferred to user space), and a ``flip'' buffer, used by hardware routines to store incoming data as quickly as possible, without the need to synchronize for concurrent access: flip buffers are exclusive ownership of the hardware device, which eventually calls tty_flip_buffer_push to deliver data to the tty buffer, where the line discipline pulls it from.

It's interesting to note that the flip buffer is laid out as two physical buffers that are alternatively written to. This allows more reliable operation, as the interrupt handler will always have a whole buffer available for writing. The function flush_to_ldisc, called by the low-level driver and part of the tty layer (i.e., tty_io.c), arranges for the flip buffer to be flipped, before the interrupt handler returns. This layout, by the way, is why the flip buffer is called so.

How to use a custom line discipline

Kernel code, as stated, can register a new line discipline with the tty subsystem, and this is also available to modularized code. You could, therefore, write your own line discipline and register it. Each line discipline is identified by a number, and a symbolic name for it is available, as common with C programming. Assigned numbers are given a name by include/asm/termios.h.

The default tty line discipline is identified by a number of N_TTY, PPP uses N_PPP and so on. Unfortunately, no line discipline numbers have currently been reserved for ``local use'', so you can only experiment with the numbers that are not used on your system. Actually, no official driver currently used N_MOUSE, so this is a good bet for your custom line discipline.

In order to activate the N_MOUSE line discipline, the user space program must use this code:

    #include 

int i = N_MOUSE;
ioctl(fd, TIOCSETD, &i);

The role of register_serial

If you noted that drivers/char/serial.c exports a function called register_serial, you may wonder what's its role in the tty architecture just outlined. As a matter of facts, the facility is only an hook offered by the ``standard'' serial tty driver in order to easy run-time addition of standard serial ports. The ``serial'' being registered is not a whole software module, but rather, only a definition or parameters to use for the new serial port. The parameters are described by struct serial_struct, which in turn is defined by include/linux/serial.h; they are used by the conventional serial driver, exploiting the de-facto standardization that exists on PC serial ports. You can't use the function to register a driver for serial hardware of a different kind than a 16450 or compatible UART. The list of supported hardware for the PC platform is found in the array uart_config, in serial.c. Other platforms offer different implementations for register_serial.

Monday, May 19, 2008

Serial Programming Guide for POSIX Operating Systems

Introduction

Chapter 1, Basics of Serial Communications
  • What Are Serial Communications?
  • What Is RS-232?
    • Signal Definitions
  • Asynchronous Communications
    • What Are Full Duplex and Half Duplex?
    • Flow Control
    • What Is a Break?
  • Synchronous Communications
  • Accessing Serial Ports
    • Serial Port Files
    • Opening a Serial Port
    • Writing Data to the Port
    • Reading Data from the Port
    • Closing a Serial Port
Chapter 2, Configuring the Serial Port
  • The POSIX Terminal Interface
    • Control Options
    • Local Options
    • Input Options
    • Output Options
    • Control Characters
Chapter 3, MODEM Communications
  • What Is a MODEM?
  • Communicating With a MODEM
    • Standard MODEM Commands
    • Common MODEM Communication Problems
Chapter 4, Advanced Serial Programming
  • Serial Port IOCTLs
    • Getting the Control Signals
    • Setting the Control Signals
    • Getting the Number of Bytes Available
  • Selecting Input from a Serial Port
    • The SELECT System Call
    • Using the SELECT System Call
    • Using SELECT with the X Intrinsics Library
Appendix A, Pinouts
  • RS-232 Pinouts
  • RS-422 Pinouts
  • RS-574 (IBM PC/AT) Pinouts
  • SGI Pinouts
Appendix B, ASCII Control Codes
  • Control Codes

Introduction

The Serial Programming Guide for POSIX Operating Systems will teach you how to successfully, efficiently, and portably program the serial ports on your UNIX® workstation or PC. Each chapter provides programming examples that use the POSIX (Portable Standard for UNIX) terminal control functions and should work with very few modifications under IRIX®, HP-UX, SunOS®, Solaris®, Digital UNIX®, Linux®, and most other UNIX operating systems. The biggest difference between operating systems that you will find is the filenames used for serial port device and lock files.

This guide is organized into the following chapters and appendices:

  • Chapter 1, Basics of Serial Programming
  • Chapter 2, Configuring the Serial Port
  • Chapter 3, Talking to MODEMs
  • Chapter 4, Advanced Serial Programming
  • Appendix A, RS-232 Pinouts
  • Appendix B, ASCII Control Codes

Chapter 1, Basics of Serial Communications

This chapter introduces serial communications, RS-232 and other standards that are used on most computers as well as how to access a serial port from a C program.

What Are Serial Communications?

Computers transfer information (data) one or more bits at a time. Serial refers to the transfer of data one bit at a time. Serial communications include most network devices, keyboards, mice, MODEMs, and terminals.

When doing serial communications each word (i.e. byte or character) of data you send or receive is sent one bit at a time. Each bit is either on or off. The terms you'll hear sometimes are mark for the on state and space for the off state.

The speed of the serial data is most often expressed as bits-per-second ("bps") or baudot rate ("baud"). This just represents the number of ones and zeroes that can be sent in one second. Back at the dawn of the computer age, 300 baud was considered fast, but today computers can handle RS-232 speeds as high as 430,800 baud! When the baud rate exceeds 1,000, you'll usually see the rate shown in kilo baud, or kbps (e.g. 9.6k, 19.2k, etc). For rates above 1,000,000 that rate is shown in megabaud, or Mbps (e.g. 1.5Mbps).

When referring to serial devices or ports, they are either labeled as Data Communications Equipment ("DCE") or Data Terminal Equipment ("DTE"). The difference between these is simple - every signal pair, like transmit and receive, is swapped. When connecting two DTE or two DCE interfaces together, a serial null-MODEM cable or adapter is used that swaps the signal pairs.

What Is RS-232?

RS-232 is a standard electrical interface for serial communications defined by the Electronic Industries Association ("EIA"). RS-232 actually comes in 3 different flavors (A, B, and C) with each one defining a different voltage range for the on and off levels. The most commonly used variety is RS-232C, which defines a mark (on) bit as a voltage between -3V and -12V and a space (off) bit as a voltage between +3V and +12V. The RS-232C specification says these signals can go about 25 feet (8m) before they become unusable. You can usually send signals a bit farther than this as long as the baud is low enough.

Besides wires for incoming and outgoing data, there are others that provide timing, status, and handshaking:

Table 1 - RS-232 Pin Assignments
PinDescriptionPinDescription PinDescriptionPinDescriptionPin Description
1Earth Ground6DSR - Data Set Ready11Unassigned16Secondary RXD 21Signal Quality Detect
2TXD - Transmitted Data7GND - Logic Ground12Secondary DCD17 Receiver Clock22Ring Detect
3RXD - Received Data8DCD - Data Carrier Detect13Secondary CTS18 Unassigned23Data Rate Select
4RTS - Request To Send9 Reserved14Secondary TXD19Secondary RTS24Transmit Clock
5CTS - Clear To Send10Reserved 15Transmit Clock20DTR - Data Terminal Ready25Unassigned

Two standards for serial interfaces you may also see are RS-422 and RS-574. RS-422 uses lower voltages and differential signals to allow cable lengths up to about 1000ft (300m). RS-574 defines the 9-pin PC serial connector and voltages.

Signal Definitions

The RS-232 standard defines some 18 different signals for serial communications. Of these, only six are generally available in the UNIX environment.

GND - Logic Ground

Technically the logic ground is not a signal, but without it none of the other signals will operate. Basically, the logic ground acts as a reference voltage so that the electronics know which voltages are positive or negative.

TXD - Transmitted Data

The TXD signal carries data transmitted from your workstation to the computer or device on the other end (like a MODEM). A mark voltage is interpreted as a value of 1, while a space voltage is interpreted as a value of 0.

RXD - Received Data

The RXD signal carries data transmitted from the computer or device on the other end to your workstation. Like TXD, mark and space voltages are interpreted as 1 and 0, respectively.

DCD - Data Carrier Detect

The DCD signal is received from the computer or device on the other end of your serial cable. A space voltage on this signal line indicates that the computer or device is currently connected or on line. DCD is not always used or available.

DTR - Data Terminal Ready

The DTR signal is generated by your workstation and tells the computer or device on the other end that you are ready (a space voltage) or not-ready (a mark voltage). DTR is usually enabled automatically whenever you open the serial interface on the workstation.

CTS - Clear To Send

The CTS signal is received from the other end of the serial cable. A space voltage indicates that is alright to send more serial data from your workstation.

CTS is usually used to regulate the flow of serial data from your workstation to the other end.

RTS - Request To Send

The RTS signal is set to the space voltage by your workstation to indicate that more data is ready to be sent.

Like CTS, RTS helps to regulate the flow of data between your workstation and the computer or device on the other end of the serial cable. Most workstations leave this signal set to the space voltage all the time.

Asynchronous Communications

For the computer to understand the serial data coming into it, it needs some way to determine where one character ends and the next begins. This guide deals exclusively with asynchronous serial data.

In asynchronous mode the serial data line stays in the mark (1) state until a character is transmitted. A start bit preceeds each character and is followed immediately by each bit in the character, an optional parity bit, and one or more stop bits. The start bit is always a space (0) and tells the computer that new serial data is available. Data can be sent or received at any time, thus the name asynchronous.

Figure 1 - Asynchronous Data Transmission

The optional parity bit is a simple sum of the data bits indicating whether or not the data contains an even or odd number of 1 bits. With even parity, the parity bit is 0 if there is an even number of 1's in the character. With odd parity, the parity bit is 0 if there is an odd number of 1's in the data. You may also hear the terms space parity, mark parity, and no parity. Space parity means that the parity bit is always 0, while mark parity means the bit is always 1. No parity means that no parity bit is present or transmitted.

The remaining bits are called stop bits. There can be 1, 1.5, or 2 stop bits between characters and they always have a value of 1. Stop bits traditionally were used to give the computer time to process the previous character, but now only serve to synchronize the receiving computer to the incoming characters.

Asynchronous data formats are usually expressed as "8N1", "7E1", and so forth. These stand for "8 data bits, no parity, 1 stop bit" and "7 data bits, even parity, 1 stop bit" respectively.

What Are Full Duplex and Half Duplex?

Full duplex means that the computer can send and receive data simultaneously - there are two separate data channels (one coming in, one going out).

Half duplex means that the computer cannot send or receive data at the same time. Usually this means there is only a single data channel to talk over. This does not mean that any of the RS-232 signals are not used. Rather, it usually means that the communications link uses some standard other than RS-232 that does not support full duplex operation.

Flow Control

It is often necessary to regulate the flow of data when transferring data between two serial interfaces. This can be due to limitations in an intermediate serial communications link, one of the serial interfaces, or some storage media. Two methods are commonly used for asynchronous data.

The first method is often called "software" flow control and uses special characters to start (XON or DC1, 021 octal) or stop (XOFF or DC3, 023 octal) the flow of data. These characters are defined in the American Standard Code for Information Interchange ("ASCII"). While these codes are useful when transferring textual information, they cannot be used when transferring other types of information without special programming.

The second method is called "hardware" flow control and uses the RS-232 CTS and RTS signals instead of special characters. The receiver sets CTS to the space voltage when it is ready to receive more data and to the mark voltage when it is not ready. Likewise, the sender sets RTS to the space voltage when it is ready to send more data. Because hardware flow control uses a separate set of signals, it is much faster than software flow control which needs to send or receive multiple bits of information to do the same thing. CTS/RTS flow control is not supported by all hardware or operating systems.

What Is a Break?

Normally a receive or transmit data signal stays at the mark voltage until a new character is transferred. If the signal is dropped to the space voltage for a long period of time, usually 1/4 to 1/2 second, then a break condition is said to exist.

A break is sometimes used to reset a communications line or change the operating mode of communications hardware like a MODEM. Chapter 3, Talking to MODEMs covers these applications in more depth.

Synchronous Communications

Unlike asynchronous data, synchronous data appears as a constant stream of bits. To read the data on the line, the computer must provide or receive a common bit clock so that both the sender and receiver are synchronized.

Even with this synchronization, the computer must mark the beginning of the data somehow. The most common way of doing this is to use a data packet protocol like Serial Data Link Control ("SDLC") or High-Speed Data Link Control ("HDLC").

Each protocol defines certain bit sequences to represent the beginning and end of a data packet. Each also defines a bit sequence that is used when there is no data. These bit sequences allow the computer see the beginning of a data packet.

Because synchronous protocols do not use per-character synchronization bits they typically provide at least a 25% improvement in performance over asynchronous communications and are suitable for remote networking and configurations with more than two serial interfaces.

Despite the speed advantages of synchronous communications, most RS-232 hardware does not support it due to the extra hardware and software required.

Accessing Serial Ports

Like all devices, UNIX provides access to serial ports via device files. To access a serial port you simply open the corresponding device file.

Serial Port Files

Each serial port on a UNIX system has one or more device files (files in the /dev directory) associated with it:

Table 2 - Serial Port Device Files
SystemPort 1Port 2
IRIX®/dev/ttyf1/dev/ttyf2
HP-UX/dev/tty1p0/dev/tty2p0
Solaris®/SunOS®/dev/ttya/dev/ttyb
Linux®/dev/ttyS0/dev/ttyS1
Digital UNIX®/dev/tty01/dev/tty02

Opening a Serial Port

Since a serial port is a file, the open(2) function is used to access it. The one hitch with UNIX is that device files are usually not accessable by normal users. Workarounds include changing the access permissions to the file(s) in question, running your program as the super-user (root), or making your program set-userid so that it runs as the owner of the device file.

For now we'll assume that the file is accessable by all users. The code to open serial port 1 on an sgi® workstation running IRIX is:

    Listing 1 - Opening a serial port.

    #include    /* Standard input/output definitions */
    #include /* String function definitions */
    #include /* UNIX standard function definitions */
    #include /* File control definitions */
    #include /* Error number definitions */
    #include /* POSIX terminal control definitions */

    /*
    * 'open_port()' - Open serial port 1.
    *
    * Returns the file descriptor on success or -1 on error.
    */

    int
    open_port(void)
    {
    int fd; /* File descriptor for the port */


    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
    {
    /*
    * Could not open the port.
    */

    perror("open_port: Unable to open /dev/ttyf1 - ");
    }
    else
    fcntl(fd, F_SETFL, 0);

    return (fd);
    }

Other systems would require the corresponding device file name, but otherwise the code is the same.

Open Options

You'll notice that when we opened the device file we used two other flags along with the read+write mode:

    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);

The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs like getty(1M/8) use this feature when starting the login process, but normally a user program does not want this behavior.

The O_NDELAY flag tells UNIX that this program doesn't care what state the DCD signal line is in - whether the other end of the port is up and running. If you do not specify this flag, your process will be put to sleep until the DCD signal line is the space voltage.

Writing Data to the Port

Writing data to the port is easy - just use the write(2) system call to send data it:

    n = write(fd, "ATZ\r", 4);
    if (n < 0)
    fputs("write() of 4 bytes failed!\n", stderr);

The write function returns the number of bytes sent or -1 if an error occurred. Usually the only error you'll run into is EIO when a MODEM or data link drops the Data Carrier Detect (DCD) line. This condition will persist until you close the port.

Reading Data from the Port

Reading data from a port is a little trickier. When you operate the port in raw data mode, each read(2)read function can be made to return immediately by doing the following: system call will return however many characters are actually available in the serial input buffers. If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs. The

    fcntl(fd, F_SETFL, FNDELAY);

The FNDELAY option causes the read function to return 0 if no characters are available on the port. To restore normal (blocking) behavior, call fcntl() without the FNDELAY option:

    fcntl(fd, F_SETFL, 0);

This is also used after opening a serial port with the O_NDELAY option.

Closing a Serial Port

To close the serial port, just use the close system call:

    close(fd);

Closing a serial port will also usually set the DTR signal low which causes most MODEMs to hang up.


Chapter 2, Configuring the Serial Port

This chapter discusses how to configure a serial port from C using the POSIX termios interface.

The POSIX Terminal Interface

Most systems support the POSIX terminal (serial) interface for changing parameters such as baud rate, character size, and so on. The first thing you need to do is include the file ; this defines the terminal control structure as well as the POSIX control functions.

The two most important POSIX functions are tcgetattr(3) and tcsetattr(3). These get and set terminal attributes, respectively; you provide a pointer to a termios structure that contains all of the serial options available:

Table 3 - Termios Structure Members
MemberDescription
c_cflagControl options
c_lflagLine options
c_iflagInput options
c_oflagOutput options
c_ccControl characters
c_ispeedInput baud (new interface)
c_ospeedOutput baud (new interface)

Control Options

The c_cflag member controls the baud rate, number of data bits, parity, stop bits, and hardware flow control. There are constants for all of the supported configurations.
Table 4 - Constants for the c_cflag Member
ConstantDescription
CBAUDBit mask for baud rate
B00 baud (drop DTR)
B5050 baud
B7575 baud
B110110 baud
B134134.5 baud
B150150 baud
B200200 baud
B300300 baud
B600600 baud
B12001200 baud
B18001800 baud
B24002400 baud
B48004800 baud
B96009600 baud
B1920019200 baud
B3840038400 baud
B5760057,600 baud
B7680076,800 baud
B115200115,200 baud
EXTAExternal rate clock
EXTBExternal rate clock
CSIZEBit mask for data bits
CS55 data bits
CS66 data bits
CS77 data bits
CS88 data bits
CSTOPB2 stop bits (1 otherwise)
CREADEnable receiver
PARENBEnable parity bit
PARODDUse odd parity instead of even
HUPCLHangup (drop DTR) on last close
CLOCALLocal line - do not change "owner" of port
LOBLKBlock job control output
CNEW_RTSCTS
CRTSCTS
Enable hardware flow control (not supported on all platforms)

The c_cflag member contains two options that should always be enabled, CLOCAL and CREAD. These will ensure that your program does not become the 'owner' of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes.

The baud rate constants (CBAUD, B9600, etc.) are used for older interfaces that lack the c_ispeedc_ospeed members. See the next section for information on the POSIX functions used to set the baud rate. and

Never initialize the c_cflag (or any other flag) member directly; you should always use the bitwise AND, OR, and NOT operators to set or clear bits in the members. Different operating system versions (and even patches) can and do use the bits differently, so using the bitwise operators will prevent you from clobbering a bit flag that is needed in a newer serial driver.

Setting the Baud Rate

The baud rate is stored in different places depending on the operating system. Older interfaces store the baud rate in the c_cflag member using one of the baud rate constants in table 4, while newer implementations provide the c_ispeed and c_ospeed members that contain the actual baud rate value.

The cfsetospeed(3) and cfsetispeed(3) functions are provided to set the baud rate in the termios structure regardless of the underlying operating system interface. Typically you'd use the following code to set the baud rate:

    Listing 2 - Setting the baud rate.

    struct termios options;

    /*
    * Get the current options for the port...
    */

    tcgetattr(fd, &options);

    /*
    * Set the baud rates to 19200...
    */

    cfsetispeed(&options, B19200);
    cfsetospeed(&options, B19200);

    /*
    * Enable the receiver and set local mode...
    */

    options.c_cflag |= (CLOCAL | CREAD);

    /*
    * Set the new options for the port...
    */

    tcsetattr(fd, TCSANOW, &options);

The tcgetattr(3) function fills the termios structure you provide with the current serial port configuration. After we set the baud rates and enable local mode and serial data receipt, we select the new configuration using tcsetattr(3). The TCSANOW constant specifies that all changes should occur immediately without waiting for output data to finish sending or input data to finish receiving. There are other constants to wait for input and output to finish or to flush the input and output buffers.

Most systems do not support different input and output speeds, so be sure to set both to the same value for maximum portability.

Table 5 - Constants for tcsetattr
ConstantDescription
TCSANOWMake changes now without waiting for data to complete
TCSADRAINWait until everything has been transmitted
TCSAFLUSHFlush input and output buffers and make the change

Setting the Character Size

Unlike the baud rate, there is no convienience function to set the character size. Instead you must do a little bitmasking to set things up. The character size is specified in bits:

    options.c_cflag &= ~CSIZE; /* Mask the character size bits */
    options.c_cflag |= CS8; /* Select 8 data bits */

Setting Parity Checking

Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.

  • No parity (8N1):
  • options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
  • Even parity (7E1):
  • options.c_cflag |= PARENB
    options.c_cflag &= ~PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
  • Odd parity (7O1):
  • options.c_cflag |= PARENB
    options.c_cflag |= PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
  • Space parity is setup the same as no parity (7S1):
  • options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

Setting Hardware Flow Control

Some versions of UNIX support hardware flow control using the CTS (Clear To Send) and RTS (Request To Send) signal lines. If the CNEW_RTSCTS or CRTSCTS constants are defined on your system then hardware flow control is probably supported. Do the following to enable hardware flow control:

    options.c_cflag |= CNEW_RTSCTS;    /* Also called CRTSCTS */

Similarly, to disable hardware flow control:

    options.c_cflag &= ~CNEW_RTSCTS;

Local Options

The local modes member c_lflag controls how input characters are managed by the serial driver. In general you will configure the c_lflag member for canonical or raw input.

Table 6 - Constants for the c_lflag Member
ConstantDescription
ISIGEnable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
ICANONEnable canonical input (else raw)
XCASEMap uppercase \lowercase (obsolete)
ECHOEnable echoing of input characters
ECHOEEcho erase character as BS-SP-BS
ECHOKEcho NL after kill character
ECHONLEcho NL
NOFLSHDisable flushing of input buffers after interrupt or quit characters
IEXTENEnable extended functions
ECHOCTLEcho control characters as ^char and delete as ~?
ECHOPRTEcho erased character as character erased
ECHOKEBS-SP-BS entire line on line kill
FLUSHOOutput being flushed
PENDINRetype pending input at next read or input char
TOSTOPSend SIGTTOU for background output

Choosing Canonical Input

Canonical input is line-oriented. Input characters are put into a buffer which can be edited interactively by the user until a CR (carriage return) or LF (line feed) character is received.

When selecting this mode you normally select the ICANON, ECHO, and ECHOE options:

    options.c_lflag |= (ICANON | ECHO | ECHOE);

Choosing Raw Input

Raw input is unprocessed. Input characters are passed through exactly as they are received, when they are received. Generally you'll deselect the ICANON, ECHO, ECHOE, and ISIG options when using raw input:

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

A Note About Input Echo

Never enable input echo (ECHO, ECHOE) when sending commands to a MODEM or other computer that is echoing characters, as you will generate a feedback loop between the two serial interfaces!

Input Options

The input modes member c_iflag controls any input processing that is done to characters received on the port. Like the c_cflag field, the final value stored in c_iflag is the bitwise OR of the desired options.

Table 7 - Constants for the c_iflag Member
ConstantDescription
INPCKEnable parity check
IGNPARIgnore parity errors
PARMRKMark parity errors
ISTRIPStrip parity bits
IXONEnable software flow control (outgoing)
IXOFFEnable software flow control (incoming)
IXANYAllow any character to start flow again
IGNBRKIgnore break condition
BRKINTSend a SIGINT when a break condition is detected
INLCRMap NL to CR
IGNCRIgnore CR
ICRNLMap CR to NL
IUCLCMap uppercase to lowercase
IMAXBELEcho BEL on input line too long

Setting Input Parity Options

You should enable input parity checking when you have enabled parity in the c_cflag member (PARENB). The revelant constants for input parity checking are INPCK, IGNPAR, PARMRK , and ISTRIP. Generally you will select INPCK and ISTRIP to enable checking and stripping of the parity bit:

    options.c_iflag |= (INPCK | ISTRIP);

IGNPAR is a somewhat dangerous option that tells the serial driver to ignore parity errors and pass the incoming data through as if no errors had occurred. This can be useful for testing the quality of a communications link, but in general is not used for practical reasons.

PARMRK causes parity errors to be 'marked' in the input stream using special characters. If IGNPAR is enabled, a NUL character (000 octal) is sent to your program before every character with a parity error. Otherwise, a DEL (177 octal) and NUL character is sent along with the bad character.

Setting Software Flow Control

Software flow control is enabled using the IXON, IXOFF , and IXANY constants:

    options.c_iflag |= (IXON | IXOFF | IXANY);

To disable software flow control simply mask those bits:

    options.c_iflag &= ~(IXON | IXOFF | IXANY);

The XON (start data) and XOFF (stop data) characters are defined in the c_cc array described below.

Output Options

The c_oflag member contains output filtering options. Like the input modes, you can select processed or raw data output.

Table 8 - Constants for the c_oflag Member
ConstantDescription
OPOSTPostprocess output (not set = raw output)
OLCUCMap lowercase to uppercase
ONLCRMap NL to CR-NL
OCRNLMap CR to NL
NOCRNo CR output at column 0
ONLRETNL performs CR function
OFILLUse fill characters for delay
OFDELFill character is DEL
NLDLYMask for delay time needed between lines
NL0No delay for NLs
NL1Delay further output after newline for 100 milliseconds
CRDLYMask for delay time needed to return carriage to left column
CR0No delay for CRs
CR1Delay after CRs depending on current column position
CR2Delay 100 milliseconds after sending CRs
CR3Delay 150 milliseconds after sending CRs
TABDLYMask for delay time needed after TABs
TAB0No delay for TABs
TAB1Delay after TABs depending on current column position
TAB2Delay 100 milliseconds after sending TABs
TAB3Expand TAB characters to spaces
BSDLYMask for delay time needed after BSs
BS0No delay for BSs
BS1Delay 50 milliseconds after sending BSs
VTDLYMask for delay time needed after VTs
VT0No delay for VTs
VT1Delay 2 seconds after sending VTs
FFDLYMask for delay time needed after FFs
FF0No delay for FFs
FF1Delay 2 seconds after sending FFs

Choosing Processed Output

Processed output is selected by setting the OPOST option in the c_oflag member:

    options.c_oflag |= OPOST;

Of all the different options, you will only probably use the ONLCR option which maps newlines into CR-LF pairs. The rest of the output options are primarily historic and date back to the time when line printers and terminals could not keep up with the serial data stream!

Choosing Raw Output

Raw output is selected by resetting the OPOST option in the c_oflag member:

    options.c_oflag &= ~OPOST;

When the OPOST option is disabled, all other option bits in c_oflag are ignored.

Control Characters

The c_cc character array contains control character definitions as well as timeout parameters. Constants are defined for every element of this array.

Table 9 - Control Characters in the c_cc Member
ConstantDescriptionKey
VINTRInterruptCTRL-C
VQUITQuitCTRL-Z
VERASEEraseBackspace (BS)
VKILLKill-lineCTRL-U
VEOFEnd-of-fileCTRL-D
VEOLEnd-of-lineCarriage return (CR)
VEOL2Second end-of-lineLine feed (LF)
VMINMinimum number of characters to read
VTIMETime to wait for data (tenths of seconds)

Setting Software Flow Control Characters

The VSTART and VSTOP elements of the c_cc array contain the characters used for software flow control. Normally they should be set to DC1 (021 octal) and DC3 (023 octal) which represent the ASCII standard XON and XOFF characters.

Setting Read Timeouts

UNIX serial interface drivers provide the ability to specify character and packet timeouts. Two elements of the c_cc array are used for timeouts: VMIN and VTIME. Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl.

VMIN specifies the minimum number of characters to read. If it is set to 0, then the VTIME value specifies the time to wait for every character read. Note that this does not mean that a read call for N bytes will wait for N characters to come in. Rather, the timeout will apply to the first character and the read call will return the number of characters immediately available (up to the number you request).

If VMIN is non-zero, VTIME specifies the time to wait for the first character read. If a character is read within the time given, any read will block (wait) until all VMIN characters are read. That is, once the first character is read, the serial interface driver expects to receive an entire packet of characters (VMIN bytes total). If no character is read within the time allowed, then the call to read returns 0. This method allows you to tell the serial driver you need exactly N bytes and any read call will return 0 or N bytes. However, the timeout only applies to the first character read, so if for some reason the driver misses one character inside the N byte packet then the read call could block forever waiting for additional input characters.

VTIME specifies the amount of time to wait for incoming characters in tenths of seconds. If VTIME is set to 0 (the default), reads will block (wait) indefinitely unless the NDELAY option is set on the port with open or fcntl.


Chapter 3, MODEM Communications

This chapter covers the basics of dialup telephone Modulator/Demodulator (MODEM) communications. Examples are provided for MODEMs that use the defacto standard "AT" command set.

What Is a MODEM?

MODEMs are devices that modulate serial data into frequencies that can be transferred over an analog data link such as a telephone line or cable TV connection. A standard telephone MODEM converts serial data into tones that can be passed over the phone lines; because of the speed and complexity of the conversion these tones sound more like loud screeching if you listen to them.

Telephone MODEMs are available today that can transfer data across a telephone line at nearly 53,000 bits per second, or 53kbps. In addition, most MODEMs use data compression technology that can increase the bit rate to well over 100kbps on some types of data.

Communicating With a MODEM

The first step in communicating with a MODEM is to open and configure the port for raw input:

    Listing 3 - Configuring the port for raw input.

    int            fd;
    struct termios options;

    /* open the port */
    fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
    fcntl(fd, F_SETFL, 0);

    /* get the current options */
    tcgetattr(fd, &options);

    /* set raw input, 1 second timeout */
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 10;

    /* set the options */
    tcsetattr(fd, TCSANOW, &options);

Next you need to establish communications with the MODEM. The best way to do this is by sending the "AT" command to the MODEM. This also allows smart MODEMs to detect the baud you are using. When the MODEM is connected correctly and powered on it will respond with the response "OK".

    Listing 4 - Initializing the MODEM.

    int                  /* O - 0 = MODEM ok, -1 = MODEM bad */
    init_modem(int fd) /* I - Serial port file */
    {
    char buffer[255]; /* Input buffer */
    char *bufptr; /* Current char in buffer */
    int nbytes; /* Number of bytes read */
    int tries; /* Number of tries so far */

    for (tries = 0; tries < 3; tries ++)
    {
    /* send an AT command followed by a CR */
    if (write(fd, "AT\r", 3) < 3)
    continue;

    /* read characters into our string buffer until we get a CR or NL */
    bufptr = buffer;
    while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
    {
    bufptr += nbytes;
    if (bufptr[-1] == '\n' || bufptr[-1] == '\r')
    break;
    }

    /* nul terminate the string and see if we got an OK response */
    *bufptr = '\0';

    if (strncmp(buffer, "OK", 2) == 0)
    return (0);
    }

    return (-1);
    }

Standard MODEM Commands

Most MODEMs support the "AT" command set, so called because each command starts with the "AT" characters. Each command is sent with the "AT" characters starting in the first column followed by the specific command and a carriage return (CR, 015 octal). After processing the command the MODEM will reply with one of several textual messages depending on the command.

ATD - Dial A Number

The ATD command dials the specified number. In addition to numbers and dashes you can specify tone ("T") or pulse ("P") dialing, pause for one second (","), and wait for a dialtone ("W"):

    ATDT 555-1212
    ATDT 18008008008W1234,1,1234
    ATD T555-1212WP1234

The MODEM will reply with one of the following messages:

    NO DIALTONE
    BUSY
    NO CARRIER
    CONNECT
    CONNECT baud

ATH - Hang Up

The ATH command causes the MODEM to hang up. Since the MODEM must be in "command" mode you probably won't use it during a normal phone call.

Most MODEMs will also hang up if DTR is dropped; you can do this by setting the baud to 0 for at least 1 second. Dropping DTR also returns the MODEM to command mode.

After a successful hang up the MODEM will reply with "NO CARRIER". If the MODEM is still connected the "CONNECT" or "CONNECT baud" message will be sent.

ATZ - Reset MODEM

The ATZ command resets the MODEM. The MODEM will reply with the string "OK".

Common MODEM Communication Problems

First and foremost, don't forget to disable input echoing. Input echoing will cause a feedback loop between the MODEM and computer.

Second, when sending MODEM commands you must terminate them with a carriage return (CR) and not a newline (NL). The C character constant for CR is "\r".

Finally, when dealing with a MODEM make sure you use a baud that the MODEM supports. While many MODEMs do auto-baud detection, some have limits (19.2kbps is common) that you must observe.


Chapter 4, Advanced Serial Programming

This chapter covers advanced serial programming techniques using the ioctl(2) and select(2) system calls.

Serial Port IOCTLs

In Chapter 2, Configuring the Serial Port we used the tcgetattr and tcsetattr functions to configure the serial port. Under UNIX these functions use the ioctl(2) system call to do their magic.

The ioctl system call takes three arguments:

    int ioctl(int fd, int request, ...);

The fd argument specifies the serial port file descriptor. The request argument is a constant defined in the header file and is typically one of the following:

Table 10 - IOCTL Requests for Serial Ports
RequestDescriptionPOSIX Function
TCGETSGets the current serial port settings. tcgetattr
TCSETSSets the serial port settings immediately.tcsetattr(fd, TCSANOW, &options)
TCSETSFSets the serial port settings after flushing the input and output buffers.tcsetattr(fd, TCSANOW, &options)
TCSETSWSets the serial port settings after allowing the input and output buffers to drain/empty. tcsetattr(fd, TCSANOW, &options)
TCSBRKSends a break for the given time. tcsendbreak, tcdrain
TCXONCControls software flow control. tcflow
TCFLSHFlushes the input and/or output queue. tcflush
TIOCMGETReturns the state of the "MODEM" bits. None
TIOCMSETSets the state of the "MODEM" bits. None
FIONREADReturns the number of bytes in the input buffer.None

Getting the Control Signals

The TIOCMGET ioctl gets the current "MODEM" status bits, which consist of all of the RS-232 signal lines except RXD and TXD:

Table 11 - Control Signal Constants
ConstantDescription
TIOCM_LEDSR (data set ready/line enable)
TIOCM_DTRDTR (data terminal ready)
TIOCM_RTSRTS (request to send)
TIOCM_STSecondary TXD (transmit)
TIOCM_SRSecondary RXD (receive)
TIOCM_CTSCTS (clear to send)
TIOCM_CARDCD (data carrier detect)
TIOCM_CDSynonym for TIOCM_CAR
TIOCM_RNGRNG (ring)
TIOCM_RISynonym for TIOCM_RNG
TIOCM_DSRDSR (data set ready)

To get the status bits, call ioctl with a pointer to an integer to hold the bits:

    Listing 5 - Getting the MODEM status bits.

    #include 
    #include

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);

Setting the Control Signals

The TIOCMSET ioctl sets the "MODEM" status bits defined above. To drop the DTR signal you can do:

    Listing 6 - Dropping DTR with the TIOCMSET ioctl.

    #include 
    #include

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);

    status &= ~TIOCM_DTR;

    ioctl(fd, TIOCMSET, status);

The bits that can be set depend on the operating system, driver, and modes in use. Consult your operating system documentation for more information.

Getting the Number of Bytes Available

The FIONREAD ioctl gets the number of bytes in the serial port input buffer. As with TIOCMGET you pass in a pointer to an integer to hold the number of bytes:

    Listing 7 - Getting the number of bytes in the input buffer.

    #include 
    #include

    int fd;
    int bytes;

    ioctl(fd, FIONREAD, &bytes);

This can be useful when polling a serial port for data, as your program can determine the number of bytes in the input buffer before attempting a read.

Selecting Input from a Serial Port

While simple applications can poll or wait on data coming from the serial port, most applications are not simple and need to handle input from multiple sources.

UNIX provides this capability through the select(2) system call. This system call allows your program to check for input, output, or error conditions on one or more file descriptors. The file descriptors can point to serial ports, regular files, other devices, pipes, or sockets. You can poll to check for pending input, wait for input indefinitely, or timeout after a specific amount of time, making the select system call extremely flexible.

Most GUI Toolkits provide an interface to select; we will discuss the X Intrinsics ("Xt") library later in this chapter.

The SELECT System Call

The select system call accepts 5 arguments:

    int select(int max_fd, fd_set *input, fd_set *output, fd_set *error,
    struct timeval *timeout);

The max_fd argument specifies the highest numbered file descriptor in the input, output, and errorinput, output, and error arguments specify sets of file descriptors for pending input, output, or error conditions; specify NULL to disable monitoring for the corresponding condition. These sets are initialized using three macros: sets. The

    FD_ZERO(fd_set);
    FD_SET(fd, fd_set);
    FD_CLR(fd, fd_set);

The FD_ZERO macro clears the set entirely. The FD_SET and FD_CLR macros add and remove a file descriptor from the set, respectively.

The timeout argument specifies a timeout value which consists of seconds (timeout.tv_sec) and microseconds (timeout.tv_usec ). To poll one or more file descriptors, set the seconds and microseconds to zero. To wait indefinitely specify NULL for the timeout pointer.

The select system call returns the number of file descriptors that have a pending condition, or -1 if there was an error.

Using the SELECT System Call

Suppose we are reading data from a serial port and a socket. We want to check for input from either file descriptor, but want to notify the user if no data is seen within 10 seconds. To do this we'll need to use the select system call:

    Listing 8 - Using SELECT to process input from more than one source.

    #include 
    #include
    #include
    #include

    int n;
    int socket;
    int fd;
    int max_fd;
    fd_set input;
    struct timeval timeout;

    /* Initialize the input set */
    FD_ZERO(input);
    FD_SET(fd, input);
    FD_SET(socket, input);

    max_fd = (socket > fd ? socket : fd) + 1;

    /* Initialize the timeout structure */
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    /* Do the select */
    n = select(max_fd, NULL, NULL, ;

    /* See if there was an error */
    if (n 0)
    perror("select failed");
    else if (n == 0)
    puts("TIMEOUT");
    else
    {
    /* We have input */
    if (FD_ISSET(fd, input))
    process_fd();
    if (FD_ISSET(socket, input))
    process_socket();
    }

You'll notice that we first check the return value of the select system call. Values of 0 and -1 yield the appropriate warning and error messages. Values greater than 0 mean that we have data pending on one or more file descriptors.

To determine which file descriptor(s) have pending input, we use the FD_ISSET macro to test the input set for each file descriptor. If the file descriptor flag is set then the condition exists (input pending in this case) and we need to do something.

Using SELECT with the X Intrinsics Library

The X Intrinsics library provides an interface to the select system call via the XtAppAddInput(3x) and XtAppRemoveInput(3x) functions:

    int XtAppAddInput(XtAppContext context, int fd, int mask,
    XtInputProc proc, XtPointer data);
    void XtAppRemoveInput(XtAppContext context, int input);

The select system call is used internally to implement timeouts, work procedures, and check for input from the X server. These functions can be used with any Xt-based toolkit including Xaw, Lesstif, and Motif.

The proc argument to XtAppAddInput specifies the function to call when the selected condition (e.g. input available) exists on the file descriptor. In the previous example you could specify the process_fd or process_socket functions.

Because Xt limits your access to the select system call, you'll need to implement timeouts through another mechanism, probably via XtAppAddTimeout(3x).


Appendix A, Pinouts

This appendix provides pinout information for many of the common serial ports you will find.

RS-232 Pinouts

RS-232 comes in three flavors (A, B, C) and uses a 25-pin D-Sub connector:

Figure 2 - RS-232 Connector

Table 12 - RS-232 Signals
PinDescriptionPinDescription
1Earth Ground14Secondary TXD
2TXD - Transmitted Data15 Transmit Clock
3RXD - Received Data16 Secondary RXD
4RTS - Request To Send17 Receiver Clock
5CTS - Clear To Send18 Unassigned
6DSR - Data Set Ready19 Secondary RTS
7GND - Logic Ground20DTR - Data Terminal Ready
8DCD - Data Carrier Detect21 Signal Quality Detect
9Reserved22Ring Detect
10Reserved23Data Rate Select
11Unassigned24Transmit Clock
12Secondary DCD25Unassigned
13Secondary CTS

RS-422 Pinouts

RS-422 also uses a 25-pin D-Sub connector, but with differential signals:

Figure 3 - RS-422 Connector

Table 13 - RS-422 Signals
PinDescriptionPinDescription
1Earth Ground14TXD+
2TXD- - Transmitted Data15 Transmit Clock-
3RXD- - Received Data16RXD+
4RTS- - Request To Send17 Receiver Clock-
5CTS- - Clear To Send18 Unassigned
6DSR - Data Set Ready19RTS+
7GND - Logic Ground20DTR- - Data Terminal Ready
8DCD- - Data Carrier Detect21 Signal Quality Detect
9Reserved22Unassigned
10Reserved23DTR+
11Unassigned24Transmit Clock+
12DCD+25Receiver Clock+
13CTS+

RS-574 (IBM PC/AT) Pinouts

The RS-574 interface is used exclusively by PC manufacturers and uses a 9-pin male D-Sub connector:

Figure 4 - RS-574 Connector

Table 14 - RS-574 (IBM PC/AT) Signals
PinDescriptionPinDescription
1DCD - Data Carrier Detect6 Data Set Ready
2RXD - Received Data7RTS - Request To Send
3TXD - Transmitted Data8CTS - Clear To Send
4DTR - Data Terminal Ready9 Ring Detect
5GND - Logic Ground

SGI Pinouts

Older SGI equipment uses a 9-pin female D-Sub connector. Unlike RS-574, the SGI pinouts nearly match those of RS-232:

Figure 5 - SGI 9-Pin Connector

Table 15 - SGI 9-Pin DSUB Signals
PinDescriptionPinDescription
1Earth Ground6DSR - Data Set Ready
2TXD - Transmitted Data7GND - Logic Ground
3RXD - Received Data8DCD - Data Carrier Detect
4RTS - Request To Send9DTR - Data Terminal Ready
5CTS - Clear To Send

The SGI Indigo, Indigo2, and Indy workstations use the Apple 8-pin MiniDIN connector for their serial ports:

Figure 6 - SGI 8-Pin Connector

Table 16 - SGI 8-Pin MiniDIN Signals
PinDescriptionPinDescription
1DTR - Data Terminal Ready5 RXD - Received Data
2CTS - Clear To Send6RTS - Request To Send
3TXD - Transmitted Data7DCD - Data Carrier Detect
4GND - Logic Ground8GND - Logic Ground

Appendix B, ASCII Control Codes

This chapter lists the ASCII control codes and their names.

Control Codes

The following ASCII characters are used for control purposes:
Table 17 - ASCII Control Codes
NameBinaryOctalDecimal Hexadecimal
NUL00000000000000
SOH00000001001101
STX00000010002202
ETX00000011003303
EOT00000100004404
ENQ00000101005505
ACK00000110006606
BEL00000111007707
BS00001000010808
HT00001001011909
NL00001010012100A
VT00001011013110B
NP, FF00001100014120C
CR00001101015130D
SO00001110016140E
SI00001111017150F
DLE000100000201610
XON, DC1000100010211711
DC2000100100221812
XOFF, DC3000100110231913
DC4000101000242014
NAK000101010252115
SYN000101100262216
ETB000101110272317
CAN000110000302418
EM000110010312519
SUB00011010032261A
ESC00011011033271B
FS00011100034281C
GS00011101035291D
RS00011110036301E
US00011111037311F

Read and write on the serial port ( c program in linux)

/* In this program basic function are: open port, read from the serial port and display in console
pgm name: open_port1.c
This pgm will work only in linux os
*/
#include /* Standard input/output definitions */
#include /* String function definitions */
#include /* UNIX standard function definitions */
#include /* File control definitions */
#include /* Error number definitions */
#include /* POSIX terminal control definitions */

/*
* * 'open_port()' - Open serial port 1.
* *
* * Returns the file descriptor on success or -1 on error.
* */

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);

if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS0 - %s\n",
strerror(errno));
}

return (fd);
}

main()
{
int mainfd=0; /* File descriptor */
char chout;
struct termios options;

mainfd = open_port();

fcntl(mainfd, F_SETFL, FNDELAY); /* Configure port reading */
/* Get the current options for the port */
tcgetattr(mainfd, &options);
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);

/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */
/* Enable data to be processed as raw input */
options.c_lflag &= ~(ICANON | ECHO | ISIG);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

while (1)
{
read(mainfd, &chout, sizeof(chout)); /* Read character from ABU */

if (chout != 0)
printf("Got %c.\n", chout);

chout=0;
usleep(20000);
}
/* Close the serial port */
close(mainfd);
}


// execute the commands
gcc open_port1.c -o open
./open

// program for writing data to the serial port
// pgm name: write_port.c



#include /* Standard input/output definitions */
#include /* String function definitions */
#include /* UNIX standard function definitions */
#include /* File control definitions */
#include /* Error number definitions */
#include /* POSIX terminal control definitions */

/*
* * 'open_port()' - Open serial port 1.
* *
* * Returns the file descriptor on success or -1 on error.
* */

int open_port(void)
{
int fd; /* File descriptor for the port */

fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);

if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",
strerror(errno));
}

return (fd);
}

main()
{
int mainfd=0; /* File descriptor */
char chout;
struct termios options;

mainfd = open_port();

fcntl(mainfd, F_SETFL, FNDELAY); /* Configure port reading */
/* Get the current options for the port */
tcgetattr(mainfd, &options);
cfsetispeed(&options, B9600); /* Set the baud rates to 9600 */
cfsetospeed(&options, B9600);

/* Enable the receiver and set local mode */
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Mask the character size to 8 bits, no parity */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; /* Select 8 data bits */
options.c_cflag &= ~CRTSCTS; /* Disable hardware flow control */
/* Enable data to be processed as raw input */
options.c_lflag &= ~(ICANON | ECHO | ISIG);

/* Set the new options for the port */
tcsetattr(mainfd, TCSANOW, &options);

write(mainfd, "manoj\r", 6 ) ; /* Close the serial port */
close(mainfd);
}


gcc write_port.c -o write
./write

Note: Run both the program in the different machine
2) Use serial port cable
3) use the command minicom -s
4) set the serial port setup
5) For any problem u can mail me manojlu03@yahoo.com

Using Linux to Send Commands to ADR Interfaces

This page will demonstrate how to send and receive data from the ADR112 or any other ADR interface, using Linux gcc programmes. It shows the commands used to configure the serial port, send data out through the serial port, and receive data through the serial port.

Additional help is available from the Linux HowTo documents found at a variety of locations around the web (hint: use a search engine). The man pages of your Linux system are invaluable as well. I make no effort to explain all of the parameters used in the various API calls. Read The Famous Manual.

This page is divided into several sections. The first section presents the source code with brief usage details. The second section shows pictorial examples and screen shots. Some troubleshooting hints are given in the second section. The third section describes some details (skip it if the first two sections provide what you need.)

Linux Version

I tested this program using Redhat 7.3, however I believe this code will operate correctly under all recent Linux kernels (your mileage may vary). The code was compiled with version 2.96-110 of the gcc compiler. Any recent vintage of the gcc compiler should be fine.

Source Code

There are four files needed to compile the Linux example.

  • adrport.h
  • adrport.c
  • adrserial.c
  • Makefile

adrport.h

// adrport.h
// Copyright MMI, MMII by Sisusypro Incorporated

int OpenAdrPort (char* sPortNumber);
int WriteAdrPort(char* psOutput);
int ReadAdrPort(char* psResponse, int iMax);
void CloseAdrPort();

adrport.c

// adrport.c - Serial Port Handler
// Copyright MMI, MMII by Sisusypro Incorporated

// Permission is hereby granted to freely copy,
// modify, utilize and distribute this example in
// whatever manner you desire without restriction.

#include
#include
#include
#include
#include
#include
#include
#include
#include "adrport.h"
static int fd = 0;

// opens the serial port
// return code:
// > 0 = fd for the port
// -1 = open failed
int OpenAdrPort(char* sPortNumber)
{
char sPortName[64];
printf("in OpenAdrPort port#=%s\n", sPortNumber);
sprintf(sPortName, "/dev/ttyS%s", sPortNumber);
printf("sPortName=%s\n", sPortName);

// make sure port is closed
CloseAdrPort(fd);

fd = open(sPortName, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < cflag="%08x\n" oflag="%08x\n" iflag="%08x\n" lflag="%08x\n" line="%02x\n" c_cflag =" B9600" cflag="%08x\n" oflag="%08x\n" iflag="%08x\n" lflag="%08x\n" line="%02x\n">= 0 = number of characters written
// -1 = write failed
int WriteAdrPort(char* psOutput)
{
int iOut;
if (fd < iout =" write(fd,">= 0 = number of characters read
// -1 = read failed
int ReadAdrPort(char* psResponse, int iMax)
{
int iIn;
printf("in ReadAdrPort iMax=%d\n", iMax);
if (fd < iin =" read(fd," errno ="="" read="" d="" n="" return="" end="" readadrport="" closes="" serial="" void="" you="" may="" want="" to="" restore="" the="" saved="" port="" attributes="" if="" fd=""> 0)
{
close(fd);
} // end if
} // end CloseAdrPort

adrserial.c

// adrserial.c - Serial Port Test Example                
// Copyright MMII by Sisusypro Incorporated

// WARNING: Example only. Lacks error checking!
#include
#include
#include
#include
#include "adrport.h"

// this is the mainline thingee
int main(int argc, char *argv[])
{
char sCmd[254];
char sResult[254];
if (argc <> 2)
{
printf("adrserial needs 1 parameter for the serial port\n");
printf(" ie. use 'adrserial 0' to connect to /dev/ttyS0\n");
return 0;
} // end if
printf("Type q to quit.\n\n");
if (OpenAdrPort(argv[1]) < ispot =" strlen(sCmd);"> after the command
sCmd[iSpot+1] = 0x00; // terminate the string properly
if (WriteAdrPort(sCmd) <> 0)
{
printf("****Response is %s\n", sResult);
} // end if
} // end while

CloseAdrPort();

} // end main

Makefile

CFLAGS=-g -Wall
adrserial: adrserial.c adrport.c
gcc -g -c -Wall adrserial.c -o adrserial.o
gcc -g -c -Wall adrport.c -o adrport.o
gcc adrport.o adrserial.o -o adrserial


Ontrak will send you a tarred and gzipped version of the above files (see bottom of this page), but it includes nothing beyond what is listed above.

Building the Linux Example

  • Copy the four source files into a single directory. (or expand LinuxEg.tar.gz)
  • To compile type "make" at the shell prompt.

Running the adrserial Example

Permissions

First off, you have to grant permission to access the serial port. On my system I logged in with root authority and used chmod to grant all users read/write access to the serial port.

          chmod o+rw /dev/ttyS0

The adrserial programme has a single parameter: the numeric value for the selected serial port. On my system the ADR card is connected to the first serial port which is known as /dev/ttyS0 in Linux. (please adjust the following instructions to match your system.)

  • Type "./adrserial 0" at the shell prompt to run the example.
    Some debugging information will appear followed by "?:" (a prompt for commands)
  • Type in a command for the ADR card and press enter.
    The command is written to the port and any response is displayed.
  • Type "q" to shut down the adrserial programme.

Pictorials

Here is the ADR112 card that I used to test this programme.

There are several things to notice in the picture.

  • The LEDs are wired to PortA of the ADR112 card. There are 8 LEDs, one for each of PA0 through PA7.
  • Current limiting resistors protect the LEDs from burnout.
  • The ADR card requires a special serial cable. Thus I wrote "ADR" on the DB9 connector so I can easily identify it (versus loopback and straight-thru cables.)
  • The loose red wire is attached to the +5V supply. It allows me to pull an I/O line high during testing.

Here is a screen-shot of the adrserial programme starting up.

It commences with the command

        ./adrserial 0

followed by a number of lines of debug information. The ?: is a prompt to enter a command for the ADR112.

Here is a screen-shot of 2 ADR commands.

The first command, cpa00000000, sets PortA into output mode.
The second command, ma2, turns on the PA1 I/O line of the ADR card.

After these two commands the LED connected to PA1 is lit up.

The rpa command instructs the ADR card to report the status of all 8 lines on PortA.

The above screen-shot shows that PA1 is high (on) and all other PortA lines are low (off).

Prior to the next command I used the red wire to feed +5 volts to PA7. This caused the LED on PA7 to light up.

Notice how both the LEDs on PA7 and PA1 are lit up. PA1 was turned on by the earlier ma2 command and PA7 is lit up by the current flowing in the red jumper wire.

An rpa command was sent to the ADR card eliciting the following response.
This shows the ADR card reporting that line PA1 and line PA7 are high (on). All other lines on PortA are low (off).

Trouble Shooting (things that can go wrong)

  • Specifying the wrong port number. In Linux the ports are numbered from 0 unlike Windows where port numbering begins at 1.
  • Using the right port number but the wrong connector. Ensure that the cable is connected to the right DB9 connector on the back of the PC.
  • Using the wrong cable. ADR cards use a special cable. Normal serial cables will not work. (after I clearly labelled my ADR cables this problem disappeared... hint, hint)
  • Using malformed ADR commands.The ADR card will silently fail to honour malformed commands. Thus a cpa command must be followed by exactly 8 digits plus a carriage return. If a digit is left out then the command is ignored. Ditto for the ma command if the decimal value is larger than 255.
  • 1 means input, 0 means output.If the port is configured incorrectly then unexpected results occur. Microchip suggests a memory aid: 1 is like an I for Input and 0 is like an O for Output.
  • Error Codes.The programme bails on any unexpected condition. Fortunately it displays an error code for each failure. Check your local man pages for the failing API to determine the cause of failure. Of course your application will have to deal with problems in a way appropriate to your environment. Ideally a production programme should never bail but should gracefully handle errors.
  • Compiler Warning. The gcc compiler emits a warning "the gets function is dangerous and should not be used." This warning is expected and OK for a tutorial sample. Your application will have its own mechanism for invoking commands.
  • Read Error: EAGAIN. For the sake of simplicity the adrserial demo does not try to differentiate between commands that evoke a response from the ADR card and those that do not. The demo simply tries to read a response after every write. If no response is forthcoming then the EAGAIN errno is generated by the library routines. It is best if your application avoids reading after writing a command to which there is no response.

Additional Details

The remainder of this page contains some additional details that you may skip at your discretion. Some of this is directed at Linux newbies.

Support

Do not call Ontrak and for help about this example. It is provided as a courtesy to show the operation of an ADR card in the Linux environment. It is not supported.

Screen-Shots

All the screen-shots show an xterm session running on a Gnome desktop. The caption bar shows the userid, jhomppi, and the machine name, guardian. (This machine normally operates as an Internet firewall.... hence guardian is its name).

The shell prompt is the userid followed by the directory in which the example resides:
[jhomppi:~/LinuxEg]$

The current directory (ie dot) is not in the path. Thus it must be specified in the command to start the programme:

    ./adrserial 0

The programme can be run from a normal tty console session (ie. command line). You do not need to start an xterm. You do not need to start the x-window environment.

Interpreting Error Codes

When you have a numeric error code you will find that all of the man pages refer to #defined constants (eg EAGAIN). The /usr/include header files help you translate an error number into its constant.

On our development system (Redhat Linux 7.3) the file /usr/include/asm/errno.h contains a long list of defined error constants. Looking up the EAGAIN constant shows a value of 11.

Searching For Error Codes. You can use the "find" and "grep" commands to locate all the headers referring to a given defined constant. For example, the header files that refer to the EAGAIN constant can be listed with the following command:

    find /usr/include -exec grep -l 'EAGAIN' {} \;

Sunday, May 18, 2008

Minicom command

Minicom commands

minicom -s : To set the teminal, port and baud rate

Install minicom

Use apt-get under Debian / Ubuntu Linux, enter:
$ sudo apt-get install minicom
If you are using Red hat Linux (RHEL) / CentOS / Fedora Linux, enter:
# yum install minicom

How do I use minicom?

First, make sure Linux has detected serial ports. Use setserial command to set and/or report the configuration information associated with a serial port.

Setup minicom

The -s option use to setup minicom. Type the following command at shell prompt:
$ minicom -s
Configure the first time when you run minicom
(Fig. 01: minicom in configuration mode)
Some terminals such as the Linux console support color with the standard ANSI escape sequences. Type the following command start minicom with colours:
$ minicom -s -c on
Minicom in configuration mode with colour console
(Fig: 02: minicom in configuration mode with color console)
When minicom starts, it first searches the MINICOM environment variable for command-line arguments, which can be over-ridden on the command line. Thus, if you have done:
$ export MINICOM="-m -c on"
Start minicom
$ minicom
minicom will assume that your terminal has a Meta or key and that color is supported. You can add MINICOM variable to your shell startup script such as ~/.bash_profile.

minicom keyboard short cut keys

Use the following keys:

  1. UP arrow-up or k
  2. DOWN arrow-down or j
  3. LEFT arrow-left or h
  4. RIGHT arrow-right or l
  5. CHOOSE (select menu) Enter
  6. CANCEL ESCape

Configure serial port

You need to configure serial port. Use up and down arrows to select menus. Press down and select Serial port setup:
Minicom serial port configuration
(Fig. 03: Configure serial port with minicom)

  • Press A to setup serial device name such as /dev/ttyS1
  • Press E to setup Bps/Par/Bits
  • Press [ESC] to exit
  • Save setup as DFL
  • Exit

More on shortcut keys

To activate help menu press [CTRL+A] followed by [Z] for help on special keys:
minicom command summary
(Fig: 04: minicom command help summary)

minicom in action

You need to connect your serial device such as router or modem using modem cable. Once connected power on device and type minicom command without -s option:
$ minicom -c on
Minicom in action - connected to one of my dump device
(Fig: 05: minicom connected to one of my embedded Linux device via null modem cable [ click to enlarge image])
The soekris embedded Linux / BSD board with AMD 266 Mhz CPU + 256M RAM. This device connected to my computer using DB9 null modem cable. During the development you need to use minicom to install Linux kernel, format filesystem and configure device.

Introduction to RS-232


RS232 is the most known serial port used in transmitting the data in communication and interface. Even though serial port is harder to program than the parallel port, this is the most effective method in which the data transmission requires less wires that yields to the less cost. The RS232 is the communication line which enables the data transmission by only using three wire links. The three links provides ‘transmit’, ‘receive’ and common ground


The ‘transmit’ and ‘receive’ line on this connecter send and receive data between the computers. As the name indicates, the data is transmitted serially. The two pins are TXD & RXD. There are other lines on this port as RTS, CTS, DSR, DTR, and RTS, RI. The ‘1’ and ‘0’ are the data which defines a voltage level of 3V to 25V and -3V to -25V respectively.

The electrical characteristics of the serial port as per the EIA (Electronics Industry Association) RS232C Standard specifies a maximum baud rate of 20,000bps, which is slow compared to today’s standard speed. For this reason, we have chosen the new RS-232D Standard, which was recently released.

The RS-232D has existed in two types. i.e., D-TYPE 25 pin connector and D-TYPE 9 pin connector, which are male connectors on the back of the PC. You need a female connector on your communication from Host to Guest computer. The pin outs of both D-9 & D-25 are show below.

D-Type-9 pin no.

D-Type-25 pin no.

Pin outs

Function

3

2

RD

Receive Data (Serial data input)

2

3

TD

Transmit Data (Serial data output)

7

4

RTS

Request to send (acknowledge to modem that UART is ready to exchange data

8

5

CTS

Clear to send (i.e.; modem is ready to exchange data)

6

6

DSR

Data ready state (UART establishes a link)

5

7

SG

Signal ground

1

8

DCD

Data Carrier detect (This line is active when modem detects a carrier

4

20

DTR

Data Terminal Ready.

9

22

RI

Ring Indicator (Becomes active when modem detects ringing signal from PSTN

About DTE & DCE:

Devices, which use serial cables for their communication, are split into two categories. These are DCE (Data Communications Equipment) and DTE (Data Terminal Equipment.) Data Communications Equipments are devices such as your modem, TA adapter, plotter etc while Data Terminal Equipment is your Computer or Terminal. A typical Data Terminal Device is a computer and a typical Data Communications Device is a Modem. Often people will talk about DTE to DCE or DCE to DCE speeds. DTE to DCE is the speed between your modem and computer, sometimes referred to as your terminal speed. This should run at faster speeds than the DCE to DCE speed. DCE to DCE is the link between modems, sometimes called the line speed.

Most people today will have 28.8K or 33.6K modems. Therefore, we should expect the DCE to DCE speed to be either 28.8K or 33.6K. Considering the high speed of the modem we should expect the DTE to DCE speed to be about 115,200 BPS. (Maximum Speed of the 16550a UART) . The communications program, which we use, has settings for DCE to DTE speeds. However, the speed is 9.6 KBPS, 144 KBPS etc and the modem speed.

If we were transferring that text file at 28.8K (DCE- DCE), then when the modem compresses it you are actually transferring 115.2 KBPS between computers and thus have a DCE- DTE speed of 115.2 KBPS. Thus, this is why the DCE- DTE should be much higher than the modem's connection speed. Therefore, if our DTE to DCE speed is several times faster than our DCE to DCE speed the PC can send data to your modem at 115,200 BPS.

What is NULL MODEM?

Null modem is used to connect two DTE's together. This is used to transfer files between the computers using protocols like Zmodem protocol, xmodem protocol, etc

Null modem coneection

Figure: Above shows the connections of the Null modem using RS-232D connecter

Above-mentioned figure shows the wiring of the null modem. The main feature indicated here is that the to make the computer to chat with the modem rather than another computer. The guest & host computer connected through the TD, RD, and SG pins. Any data that is transmitted through TD line from the Host to Guest is received on RD line. The Guest computer must have the same setup as the Host. The signal ground (SG) line of the both must be shorted so that grounds are common to each computer.

The Data Terminal Ready (DTR) is looped back to Data Set Ready and Carrier Detect on both computers. When the Data Terminal Ready is asserted active, then the Data Set Ready and Carrier Detect immediately become active. At this point, the computer thinks the Virtual Modem to which it is connected is ready and has detected the carrier of the other modem.

All left to worry about now is the Request to Send and Clear To Send. As both computers communicate together at the same speed, flow control is not needed thus these two lines are also linked together on each computer. When the computer wishes to send data, it asserts the Request to Send high and as it is hooked together with the Clear to Send, It immediately gets a reply that it is ok to send and does so.

The Ring indicator line is only used to tell the computer that there is a ringing signal on the phone line. As we do not have, a modem connected to the phone line this is left disconnected

To know about the RS232 ports available in your computer, Right click on "My Computer", Goto 'Properties', Select tab 'Device Manager', go to Ports( COM & LPT ), In that you will find 'Communication Port(Com1)' etc. If you right click on that and go to properties, you will get device status. Make sure that you have enabled the port( Use this port is selected).

How to program the Serial Port using C/C++?

There are two popular methods of sending data to or from the serial port in Turbo C. One is using outportb(PORT_ID, DATA) or outport(PORT_ID,DATA) defined in “dos.h”. Another method is using bioscom() function defined in “bios.h”.

Using outportb() :

The function outportb () sends a data byte to the port ‘PORT_ID’. The function outport() sends a data word. These functions can be used for any port including serial port, parallel ports. Similarly to receive data these are used.

  • inport reads a word from a hardware port

  • inportb reads a byte from a hardware port

  • outport outputs a word to a hardware port

  • outportb outputs a byte to a hardware port

Declaration:

  • int inport(int portid);

  • unsigned char inportb(int portid);

  • void outport(int portid, int value);

  • void outportb(int portid, unsigned char value);

Remarks:

  • inport works just like the 80x86 instruction IN. It reads the low byte of a word from portid, the high byte from portid + 2.

  • inportb is a macro that reads a byte

  • outport works just like the 80x86 instruction OUT. It writes the low byte of value to portid, the high byte to portid + 1.

  • outportb is a macro that writes value Argument

portid:

  • Inport- port that inport and inportb read from;

  • Outport- port that outport and outportb write to

value:

  • Word that outport writes to portid;

  • Byte- that outportb writes to portid.

If you call inportb or outportb when dos.h has been included, they are treated as macros that expand to inline code.

If you don't include dos.h, or if you do include dos.h and #undef the macro(s), you get the function(s) of the same name.

Return Value:

# inport and inportb return the value read

# outport and outportb do not return

For more details of these functions read article from beondlogic.com

Using bioscom:

The macro bioscom () and function _bios_serialcom() are used in this method in the serial communication using RS-232 connecter. First we have to set the port with the settings depending on our need and availability. In this method, same function is used to make the settings using control word, to send data to the port and check the status of the port. These actions are distinguished using the first parameter of the function. Along with that we are sending data and the port to be used to communicate.

Here are the deatails of the Turbo C Functions for communication ports.

Declaration:

bioscom(int cmd, char abyte, int port)
_bios_serialcom(int cmd ,int port, char abyte)

bioscom() and _bios_serialcom() uses the bios interrupt 0x14 to perform various communicate the serial communication over the I/O ports given in port.

cmd: The I/O operation to be performed.

cmd (boiscom)

cmd(_bios_serialcom)

Action

0

_COM_INIT

Initialise the parameters to the port

1

_COM_SEND

Send the character to the port

2

_COM_RECEIVE

Receive character from the port

3

_COM_STATUS

Returns rhe current status of the communication port

portid: port to which data is to be sent or from which data is to be read.

0: COM1
1: COM2
2: COM3

abyte:

When cmd =2 or 3 (_COM_SEND or _COM_RECEIVE) parameter abyte is ignored.

When cmd = 0 (_COM_INIT), abyte is an OR combination of the following bits (One from each group):

value of abyte

Meaning

Bioscom

_bios_serialcom

0x02

0x03

_COM_CHR7

_COM_CHR8

7 data bits

8 data bits

0x00

0x04

_COM_STOP1

_COM_STOP2

1 stop bit

2 stop bits

0x00

0x08

0X10

_COM_NOPARITY

_COM_ODDPARITY

_COM_EVENPARITY

No parity

Odd parity

Even parity

0x00

0x20

0x40

0x60

0x80

0xA0

0xC0

0xE0

_COM_110

_COM_150

_COM_300

_COM_600

_COM_1200

_COM_2400

_COM_4800

_COM_9600

110 baud

150 baud

300 baud

600 baud

1200 baud

2400 baud

4800 baud

9600 baud

For example, if

abyte = 0x8B = (0x80 | 0x08 | 0x00 | 0x03) = (_COM_1200 | _COM_ODDPARITY | _COM_STOP1 | _COM_CHR8)

the communications port is set to
1200 baud (0x80 = _COM_1200)
Odd parity (0x08 = _COM_ODDPARITY)
1 stop bit (0x00 = _COM_STOP1)
8 data bits (0x03 = _COM_CHR8)

To initialise the port with above settings we have to write,

bioscom(0, 0x8B, 0);

To send a data to COM1, the format of the function will be bioscom(1, data, 0). Similarly bioscom(1, 0, 0 ) will read a data byte from the port.

The following example illustrate how to serial port programs. When a data is available in the port, it inputs the data and displays onto the screen and if a key is pressed the ASCII value will be sent to the port.

#include
#include
#define COM1 0
#define DATA_READY 0x100
#define SETTINGS ( 0x80 | 0x02 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
bioscom(0, SETTINGS, COM1); /*initialize the port*/
cprintf("Data sent to you: ");
while (1)
{
status = bioscom(3, 0, COM1); /*wait until get a data*/
if (status & DATA_READY)
if ((out = bioscom(2, 0, COM1) & 0x7F) != 0) /*input a data*/
putch(out);
if (kbhit())
{
if ((in = getch()) == 27) /* ASCII of Esc*/
break;
bioscom(1, in, COM1); /*output a data*/
}
}
return 0;
}


When you compile and run the above program in both the computers, The characters typed in one computer should appear on the other computer screen and vice versa. Initially, we set the port to desired settings as defined in macro settings. Then we waited in an idle loop until a key is pressed or a data is available on the port. If any key is pressed, then kbhit() function returns non zero value. So will go to getch function where we are finding out which key is pressed. Then we are sending it to the com port. Similarly, if any data is available on the port, we are receiving it from the port and displaying it on the screen.

To check the port, If you have a single computer, you can use loop-back connection as follows. This is most commonly used method for developing communication programs. Here, data is transmitted to that port itself. Loop-back plug connection is as follows.

Loop Back Plug Connection
Fig 2. Loop-back plug connection

If you run the above program with the connection as in this diagram, the character entered in the keyboard should be displayed on the screen. This method is helpful in writing serial port program with single computer. Also you can make changes in the port id if your computer has 2 rs232ports. You can connect the com1 port to com2 of the same computer and change the port id in the program. The data sent to the port com1 should come to port com2. then also whatever you type in the keyboard should appear on the screen.

The program given below is an example source code for serial communication programmers. It is a PC to PC communication using RS232. Download the code, unzip and run to chat in dos mode between two computers. Use the program to get more idea about serial port programming.


Usb in a NutShell

USB Protocols

Unlike RS-232 and similar serial interfaces where the format of data being sent is not defined, USB is made up of several layers of protocols. While this sounds complicated, don’t give up now. Once you understand what is going on, you really only have to worry about the higher level layers. In fact most USB controller I.C.s will take care of the lower layer, thus making it almost invisible to the end designer.

Each USB transaction consists of a

    • Token Packet (Header defining what it expects to follow), an
    • Optional Data Packet, (Containing the payload) and a
    • Status Packet (Used to acknowledge transactions and to provide a means of error correction)

As we have already discussed, USB is a host centric bus. The host initiates all transactions. The first packet, also called a token is generated by the host to describe what is to follow and whether the data transaction will be a read or write and what the device’s address and designated endpoint is. The next packet is generally a data packet carrying the payload and is followed by an handshaking packet, reporting if the data or token was received successfully, or if the endpoint is stalled or not available to accept data.

Common USB Packet Fields

Data on the USBus is transmitted LSBit first. USB packets consist of the following fields,

    • Sync
    • All packets must start with a sync field. The sync field is 8 bits long at low and full speed or 32 bits long for high speed and is used to synchronise the clock of the receiver with that of the transmitter. The last two bits indicate where the PID fields starts.

    • PID
    • PID stands for Packet ID. This field is used to identify the type of packet that is being sent. The following table shows the possible values.

      Group

      PID Value

      Packet Identifier

      Token

      0001

      OUT Token

      1001

      IN Token

      0101

      SOF Token

      1101

      SETUP Token

      Data

      0011

      DATA0

      1011

      DATA1

      0111

      DATA2

      1111

      MDATA

      Handshake

      0010

      ACK Handshake

      1010

      NAK Handshake

      1110

      STALL Handshake

      0110

      NYET (No Response Yet)

      Special

      1100

      PREamble

      1100

      ERR

      1000

      Split

      0100

      Ping

      There are 4 bits to the PID, however to insure it is received correctly, the 4 bits are complemented and repeated, making an 8 bit PID in total. The resulting format is shown below.

      PID0

      PID1

      PID2

      PID3

      nPID0

      nPID1

      nPID2

      nPID3

    • ADDR
    • The address field specifies which device the packet is designated for. Being 7 bits in length allows for 127 devices to be supported. Address 0 is not valid, as any device which is not yet assigned an address must respond to packets sent to address zero.

    • ENDP
    • The endpoint field is made up of 4 bits, allowing 16 possible endpoints. Low speed devices, however can only have 2 additional endpoints on top of the default pipe. (4 endpoints max)

    • CRC
    • Cyclic Redundancy Checks are performed on the data within the packet payload. All token packets have a 5 bit CRC while data packets have a 16 bit CRC.

    • EOP
    • End of packet. Signalled by a Single Ended Zero (SE0) for approximately 2 bit times followed by a J for 1 bit time.

USB Packet Types

USB has four different packet types. Token packets indicate the type of transaction to follow, data packets contain the payload, handshake packets are used for acknowledging data or reporting errors and start of frame packets indicate the start of a new frame.

    • Token Packets
    • There are three types of token packets,

      • In - Informs the USB device that the host wishes to read information.
      • Out - Informs the USB device that the host wishes to send information.
      • Setup - Used to begin control transfers.

      Token Packets must conform to the following format,

      Sync

      PID

      ADDR

      ENDP

      CRC5

      EOP

    • Data Packets
    • There are two types of data packets each capable of transmitting up to 1024 bytes of data.

      • Data0
      • Data1

      High Speed mode defines another two data PIDs, DATA2 and MDATA.

      Data packets have the following format,

      Sync

      PID

      Data

      CRC16

      EOP

      • Maximum data payload size for low-speed devices is 8 bytes.
      • Maximum data payload size for full-speed devices is 1023 bytes.
      • Maximum data payload size for high-speed devices is 1024 bytes.
      • Data must be sent in multiples of bytes.
    • Handshake Packets
    • There are three type of handshake packets which consist simply of the PID

      • ACK - Acknowledgment that the packet has been successfully received.
      • NAK - Reports that the device temporary cannot send or received data. Also used during interrupt transactions to inform the host there is no data to send.
      • STALL - The device finds its in a state that it requires intervention from the host.

      Handshake Packets have the following format,

      Sync

      PID

      EOP

    • Start of Frame Packets
    • The SOF packet consisting of an 11-bit frame number is sent by the host every 1ms ± 500ns on a full speed bus or every 125 µs ± 0.0625 µs on a high speed bus.

      Sync

      PID

      Frame Number

      CRC5

      EOP

USB Functions

When we think of a USB device, we think of a USB peripheral, but a USB device could mean a USB transceiver device used at the host or peripheral, a USB Hub or Host Controller IC device, or a USB peripheral device. The standard therefore makes references to USB functions which can be seen as USB devices which provide a capability or function such as a Printer, Zip Drive, Scanner, Modem or other peripheral.

So by now we should know the sort of things which make up a USB packet. No? You're forgotten how many bits make up a PID field already? Well don't be too alarmed. Fortunately most USB functions handle the low level USB protocols up to the transaction layer (which we will cover next chapter) in silicon. The reason why we cover this information is most USB function controllers will report errors such as PID Encoding Error. Without briefly covering this, one could ask what is a PID Encoding Error? If you suggested that the last four bits of the PID didn't match the inverse of the first four bits then you would be right.

Most functions will have a series of buffers, typically 8 bytes long. Each buffer will belong to an endpoint - EP0 IN, EP0 OUT etc. Say for example, the host sends a device descriptor request. The function hardware will read the setup packet and determine from the address field whether the packet is for itself, and if so will copy the payload of the following data packet to the appropriate endpoint buffer dictated by the value in the endpoint field of the setup token. It will then send a handshake packet to acknowledge the reception of the byte and generate an internal interrupt within the semiconductor/micro-controller for the appropriate endpoint signifying it has received a packet. This is typically all done in hardware.

The software now gets an interrupt, and should read the contents of the endpoint buffer and parse the device descriptor request.

Endpoints

Endpoints can be described as sources or sinks of data. As the bus is host centric, endpoints occur at the end of the communications channel at the USB function. At the software layer, your device driver may send a packet to your devices EP1 for example. As the data is flowing out from the host, it will end up in the EP1 OUT buffer. Your firmware will then at its leisure read this data. If it wants to return data, the function cannot simply write to the bus as the bus is controlled by the host. Therefore it writes data to EP1 IN which sits in the buffer until such time when the host sends a IN packet to that endpoint requesting the data. Endpoints can also be seen as the interface between the hardware of the function device and the firmware running on the function device.

All devices must support endpoint zero. This is the endpoint which receives all of the devices control and status requests during enumeration and throughout the duration while the device is operational on the bus.

Pipes

While the device sends and receives data on a series of endpoints, the client software transfers data through pipes. A pipe is a logical connection between the host and endpoint(s). Pipes will also have a set of parameters associated with them such as how much bandwidth is allocated to it, what transfer type (Control, Bulk, Iso or Interrupt) it uses, a direction of data flow and maximum packet/buffer sizes. For example the default pipe is a bi-directional pipe made up of endpoint zero in and endpoint zero out with a control transfer type.

USB defines two types of pipes

  • Stream Pipes have no defined USB format, that is you can send any type of data down a stream pipe and can retrieve the data out the other end. Data flows sequentially and has a pre-defined direction, either in or out. Stream pipes will support bulk, isochronous and interrupt transfer types. Stream pipes can either be controlled by the host or device.


  • Message Pipes have a defined USB format. They are host controlled, which are initiated by a request sent from the host. Data is then transferred in the desired direction, dictated by the request. Therefore message pipes allow data to flow in both directions but will only support control transfers.

Friday, May 16, 2008

LINUX ALLOCATED DEVICES



**** DEVICE DRIVERS AUTHORS PLEASE READ THIS ****

THE DEVICE REGISTRY IS OFFICIALLY FROZEN FOR LINUS TORVALDS' KERNEL
TREE. At Linus' request, no more allocations will be made official
for Linus' kernel tree; the 3 June 2001 version of this list is the
official final version of this registry. At Alan Cox' request,
however, the registry will continue to be maintained for the -ac
series of kernels, and registrations will be accepted.

To have a major number allocated, or a minor number in situations
where that applies (e.g. busmice), please contact me with the
appropriate device information. Also, if you have additional
information regarding any of the devices listed below, or if I have
made a mistake, I would greatly appreciate a note.

I do, however, make a few requests about the nature of your report.
This is necessary for me to be able to keep this list up to date and
correct in a timely manner. First of all, *please* send it to the
correct address... . I receive hundreds of email
messages a day, so mail sent to other addresses may very well get lost
in the avalanche. Please put in a descriptive subject, so I can find
your mail again should I need to. Too many people send me email
saying just "device number request" in the subject.

Second, please include a description of the device *in the same format
as this list*. The reason for this is that it is the only way I have
found to ensure I have all the requisite information to publish your
device and avoid conflicts.

Third, please don't assume that the distributed version of the list is
up to date. Due to the number of registrations I have to maintain it
in "batch mode", so there is likely additional registrations that
haven't been listed yet.

Finally, sometimes I have to play "namespace police." Please don't be
offended. I often get submissions for /dev names that would be bound
to cause conflicts down the road. I am trying to avoid getting in a
situation where we would have to suffer an incompatible forward
change. Therefore, please consult with me *before* you make your
device names and numbers in any way public, at least to the point
where it would be at