mirror of https://github.com/tLDP/LDP
638 lines
23 KiB
Plaintext
638 lines
23 KiB
Plaintext
<!DOCTYPE LINUXDOC SYSTEM>
|
|
|
|
<ARTICLE>
|
|
<TITLE>The Linux Serial Programming HOWTO
|
|
<AUTHOR>by Peter H. Baumann, <TT><HTMLURL URL="mailto:Peter.Baumann@dlr.de"
|
|
NAME="Peter.Baumann@dlr.de"></TT>
|
|
<DATE>v1.0, 22 January 1998
|
|
<ABSTRACT>
|
|
This document describes how to program communications with devices
|
|
over a serial port on a Linux box.
|
|
</ABSTRACT>
|
|
<TOC>
|
|
|
|
<SECT>Introduction
|
|
<P>
|
|
This is the Linux Serial Programming HOWTO. All about how to program
|
|
communications with other devices / computers over a serial line under
|
|
Linux. Different techniques are explained: Canonical I/O (only
|
|
complete lines are transmitted/received), asyncronous I/O, and waiting
|
|
for input from multiple sources.
|
|
<P>
|
|
This document does not describe how to set up serial ports, because
|
|
this has been described by Greg Hankins in the Serial-HOWTO.
|
|
<P>
|
|
I have to emphasize that I am not an expert in this field, but have
|
|
had problems with a project that involved such communication. The code
|
|
examples presented here were derived from the miniterm code available
|
|
from the LDP programmers guide
|
|
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
|
|
and mirrors)
|
|
in the examples directory.
|
|
<P>
|
|
Since I wrote this document in June 1997, I have moved to WinNT to
|
|
satisfy customers need, so I have not built up more in depth
|
|
knowledge. If anybody has any comments, I will gladly incorporate them
|
|
into this document (see sect. Feedback). If someone would like to take
|
|
over and do a better job, please e-mail me.
|
|
<P>
|
|
All examples were tested using a i386 Linux Kernel 2.0.29.
|
|
|
|
<SECT1>Copyright
|
|
<P>
|
|
The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Peter
|
|
Baumann. Linux HOWTO documents may be reproduced and distributed in
|
|
whole or in part, in any medium physical or electronic, as long as
|
|
this copyright notice is retained on all copies. Commercial
|
|
redistribution is allowed and encouraged; however, the author would
|
|
<EM/like/ to be notified of any such distributions.
|
|
<P>
|
|
All translations, derivative works, or aggregate works incorporating
|
|
any Linux HOWTO documents must be covered under this copyright notice.
|
|
That is, you may not produce a derivative work from a HOWTO and impose
|
|
additional restrictions on its distribution. Exceptions to these rules
|
|
may be granted under certain conditions; please contact the Linux HOWTO
|
|
coordinator at the address given below.
|
|
<P>
|
|
In short, we wish to promote dissemination of this information through
|
|
as many channels as possible. However, we do wish to retain copyright
|
|
on the HOWTO documents, and would <EM/like/ to be notified of any
|
|
plans to redistribute the HOWTOs.
|
|
<P>
|
|
If you have questions, please contact Tim Bynum, the Linux HOWTO
|
|
coordinator, at <TT><HTMLURL URL="mailto:linux-howto@sunsite.unc.edu"
|
|
NAME="linux-howto@sunsite.unc.edu"></TT> via email.
|
|
|
|
<SECT1>New Versions Of This Document
|
|
<P>
|
|
New versions of the Serial-Programming-HOWTO will be available
|
|
at<NEWLINE> <TT><HTMLURL
|
|
URL="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"
|
|
NAME="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"></TT>
|
|
and mirror sites. There are other formats, such as PostScript and DVI
|
|
versions in the <TT/other-formats/ directory. The
|
|
Serial-Programming-HOWTO is also available at <TT><HTMLURL
|
|
URL="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html"
|
|
NAME="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html">
|
|
</TT>
|
|
and will be posted to <TT><HTMLURL URL="news:comp.os.linux.answers"
|
|
NAME="comp.os.linux.answers"></TT> monthly.
|
|
|
|
<SECT1>Feedback
|
|
<P>
|
|
Please send me any corrections, questions, comments, suggestions, or
|
|
additional material. I would like to improve this HOWTO! Tell me
|
|
exactly what you don't understand, or what could be clearer. You can
|
|
reach me at <TT><HTMLURL URL="mailto:Peter.Baumann@dlr.de"
|
|
NAME="Peter.Baumann@dlr.de"></TT> via email. Please include the
|
|
version number of the Serial-Programming-HOWTO when writing, this is
|
|
version 0.3.
|
|
|
|
|
|
<SECT>Getting started
|
|
<P>
|
|
<SECT1>Debugging
|
|
<P>
|
|
The best way to debug your code is to set up another Linux box, and
|
|
connect the two computers via a null-modem cable. Use miniterm
|
|
(available from the LDP programmers guide
|
|
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
|
|
in the examples directory) to transmit characters to your Linux
|
|
box. Miniterm can be compiled very easily and will transmit all
|
|
keyboard input raw over the serial port. Only the define statement
|
|
<TT>#define MODEMDEVICE "/dev/ttyS0"</TT> has to be checked. Set it to
|
|
<TT>ttyS0</TT> for COM1, <TT>ttyS1</TT> for COM2, etc.. It is
|
|
essential for testing, that <EM/all/ characters are transmitted raw
|
|
(without output processing) over the line. To test your connection,
|
|
start miniterm on both computers and just type away. The characters
|
|
input on one computer should appear on the other computer and vice
|
|
versa. The input will not be echoed to the attached screen.
|
|
<P>
|
|
To make a null-modem cable you have to cross the TxD (transmit) and
|
|
RxD (receive) lines. For a description of a cable see sect. 7 of the
|
|
Serial-HOWTO.
|
|
<P>
|
|
It is also possible to perform this testing with only one computer, if
|
|
you have two unused serial ports. You can then run two miniterms off
|
|
two virtual consoles. If you free a serial port by disconnecting the
|
|
mouse, remember to redirect <TT>/dev/mouse</TT> if it exists. If you
|
|
use a multiport serial card, be sure to configure it correctly. I had
|
|
mine configured wrong and everything worked fine as long as I was
|
|
testing only on my computer. When I connected to another computer, the port
|
|
started loosing characters. Executing two programs on one computer
|
|
just isn't fully asynchronous.
|
|
|
|
<SECT1>Port Settings
|
|
<P>
|
|
The devices <TT>/dev/ttyS*</TT> are intended to hook up terminals to
|
|
your Linux box, and are configured for this use after startup. This
|
|
has to be kept in mind when programming communication with a raw
|
|
device. E.g. the ports are configured to echo characters sent from the
|
|
device back to it, which normally has to be changed for data
|
|
transmission.
|
|
<P>
|
|
All parameters can be easily configured from within a program. The
|
|
configuration is stored in a structure <TT>struct termios</TT>, which
|
|
is defined in <TT><asm/termbits.h></TT>:
|
|
<TSCREEN><VERB>
|
|
#define NCCS 19
|
|
struct termios {
|
|
tcflag_t c_iflag; /* input mode flags */
|
|
tcflag_t c_oflag; /* output mode flags */
|
|
tcflag_t c_cflag; /* control mode flags */
|
|
tcflag_t c_lflag; /* local mode flags */
|
|
cc_t c_line; /* line discipline */
|
|
cc_t c_cc[NCCS]; /* control characters */
|
|
};
|
|
</VERB></TSCREEN>
|
|
|
|
This file also includes all flag definitions. The input mode flags in
|
|
<TT>c_iflag</TT> handle all input processing, which means that the
|
|
characters sent from the device can be processed before they are read
|
|
with <TT>read</TT>. Similarly <TT>c_oflag</TT> handles the output
|
|
processing. <TT>c_cflag</TT> contains the settings for the port, as
|
|
the baudrate, bits per character, stop bits, etc.. The local mode
|
|
flags stored in <TT>c_lflag</TT> determine if characters are echoed,
|
|
signals are sent to your program, etc.. Finally the array
|
|
<TT>c_cc</TT> defines the control characters for end of file, stop,
|
|
etc.. Default values for the control characters are defined in
|
|
<TT><asm/termios.h></TT>. The flags are described in the manual
|
|
page <TT>termios(3)</TT>. The structure <TT>termios</TT> contains the
|
|
<TT>c_line</TT> (line discipline) element, which is not used in POSIX compliant systems.
|
|
|
|
|
|
<SECT1>Input Concepts for Serial Devices
|
|
<P>
|
|
Here three different input concepts will be presented. The appropriate
|
|
concept has to be chosen for the intended application. Whenever
|
|
possible, do not loop reading single characters to get a complete
|
|
string. When I did this, I lost characters, whereas a <TT>read</TT> for
|
|
the whole string did not show any errors.
|
|
|
|
<SECT2>Canonical Input Processing
|
|
<P>
|
|
This is the normal processing mode for terminals, but can also be
|
|
useful for communicating with other dl input is processed in
|
|
units of lines, which means that a <TT>read</TT> will only return a
|
|
full line of input. A line is by default terminated by a <TT>NL</TT>
|
|
(ASCII <TT>LF</TT>), an end of file, or an end of line character. A
|
|
<TT>CR</TT> (the DOS/Windows default end-of-line) will not terminate a
|
|
line with the default settings.
|
|
<P>
|
|
Canonical input processing can also handle the erase, delete word, and
|
|
reprint characters, translate <TT>CR</TT> to <TT>NL</TT>, etc..
|
|
|
|
<SECT2>Non-Canonical Input Processing
|
|
<P>
|
|
Non-Canonical Input Processing will handle a fixed amount of
|
|
characters per read, and allows for a character timer. This mode
|
|
should be used if your application will always read a fixed number of
|
|
characters, or if the connected device sends bursts of characters.
|
|
<P>
|
|
|
|
<SECT2>Asynchronous Input
|
|
<P>
|
|
The two modes described above can be used in synchronous and asynchronous
|
|
mode. Synchronous is the default, where a <TT>read</TT> statement will
|
|
block, until the read is satisfied. In asynchronous mode the
|
|
<TT>read</TT> statement will return immediatly and send a signal to the
|
|
calling program upon completion. This signal can be received by a
|
|
signal handler.
|
|
|
|
<SECT2>Waiting for Input from Multiple Sources
|
|
<P>
|
|
This is not a different input mode, but might be useful, if you are
|
|
handling multiple devices. In my application I was handling input over
|
|
a TCP/IP socket and input over a serial connection from another computer
|
|
quasi-simultaneously. The program example given below will wait for
|
|
input from two different input sources. If input from one source
|
|
becomes available, it will be processed, and the program will then
|
|
wait for new input.
|
|
<P>
|
|
The approach presented below seems rather complex, but it is important
|
|
to keep in mind that Linux is a multi-processing operating system. The
|
|
<TT>select</TT> system call will not load the CPU while waiting for
|
|
input, whereas looping until input becomes available would slow down
|
|
other processes executing at the same time.
|
|
<P>
|
|
|
|
<SECT>Program Examples
|
|
<P>
|
|
All examples have been derived from <TT>miniterm.c</TT>. The type
|
|
ahead buffer is limited to 255 characters, just like the maximum
|
|
string length for canonical input processing
|
|
(<TT><linux/limits.h></TT> or <TT><posix1_lim.h></TT>).
|
|
<P>
|
|
See the comments in the code for explanation of the use of the
|
|
different input modes. I hope that the code is understandable. The
|
|
example for canonical input is commented best, the other examples are
|
|
commented only where they differ from the example for canonical input
|
|
to emphasize the differences.
|
|
<P>
|
|
The descriptions are not complete, but you are encouraged to
|
|
experiment with the examples to derive the best solution for your
|
|
application.
|
|
<P>
|
|
Don't forget to give the appropriate serial ports the right
|
|
permissions (e. g.: <TT>chmod a+rw /dev/ttyS1</TT>)!
|
|
<P>
|
|
|
|
<SECT1>Canonical Input Processing
|
|
<P>
|
|
<TSCREEN><VERB>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <stdio.h>
|
|
|
|
/* baudrate settings are defined in <asm/termbits.h>, which is
|
|
included by <termios.h> */
|
|
#define BAUDRATE B38400
|
|
/* change this definition for the correct port */
|
|
#define MODEMDEVICE "/dev/ttyS1"
|
|
#define _POSIX_SOURCE 1 /* POSIX compliant source */
|
|
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
|
|
volatile int STOP=FALSE;
|
|
|
|
main()
|
|
{
|
|
int fd,c, res;
|
|
struct termios oldtio,newtio;
|
|
char buf[255];
|
|
/*
|
|
Open modem device for reading and writing and not as controlling tty
|
|
because we don't want to get killed if linenoise sends CTRL-C.
|
|
*/
|
|
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
|
|
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
|
|
|
|
tcgetattr(fd,&ero;oldtio); /* save current serial port settings */
|
|
bzero(&ero;newtio, sizeof(newtio)); /* clear struct for new port settings */
|
|
|
|
/*
|
|
BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
|
|
CRTSCTS : output hardware flow control (only used if the cable has
|
|
all necessary lines. See sect. 7 of Serial-HOWTO)
|
|
CS8 : 8n1 (8bit,no parity,1 stopbit)
|
|
CLOCAL : local connection, no modem contol
|
|
CREAD : enable receiving characters
|
|
*/
|
|
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
|
|
|
|
/*
|
|
IGNPAR : ignore bytes with parity errors
|
|
ICRNL : map CR to NL (otherwise a CR input on the other computer
|
|
will not terminate input)
|
|
otherwise make device raw (no other input processing)
|
|
*/
|
|
newtio.c_iflag = IGNPAR | ICRNL;
|
|
|
|
/*
|
|
Raw output.
|
|
*/
|
|
newtio.c_oflag = 0;
|
|
|
|
/*
|
|
ICANON : enable canonical input
|
|
disable all echo functionality, and don't send signals to calling program
|
|
*/
|
|
newtio.c_lflag = ICANON;
|
|
|
|
/*
|
|
initialize all control characters
|
|
default values can be found in /usr/include/termios.h, and are given
|
|
in the comments, but we don't need them here
|
|
*/
|
|
newtio.c_cc[VINTR] = 0; /* Ctrl-c */
|
|
newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
|
|
newtio.c_cc[VERASE] = 0; /* del */
|
|
newtio.c_cc[VKILL] = 0; /* @ */
|
|
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
|
|
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
|
|
newtio.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
|
|
newtio.c_cc[VSWTC] = 0; /* '\0' */
|
|
newtio.c_cc[VSTART] = 0; /* Ctrl-q */
|
|
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
|
|
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
|
|
newtio.c_cc[VEOL] = 0; /* '\0' */
|
|
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
|
|
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
|
|
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
|
|
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
|
|
newtio.c_cc[VEOL2] = 0; /* '\0' */
|
|
|
|
/*
|
|
now clean the modem line and activate the settings for the port
|
|
*/
|
|
tcflush(fd, TCIFLUSH);
|
|
tcsetattr(fd,TCSANOW,&ero;newtio);
|
|
|
|
/*
|
|
terminal settings done, now handle input
|
|
In this example, inputting a 'z' at the beginning of a line will
|
|
exit the program.
|
|
*/
|
|
while (STOP==FALSE) { /* loop until we have a terminating condition */
|
|
/* read blocks program execution until a line terminating character is
|
|
input, even if more than 255 chars are input. If the number
|
|
of characters read is smaller than the number of chars available,
|
|
subsequent reads will return the remaining chars. res will be set
|
|
to the actual number of characters actually read */
|
|
res = read(fd,buf,255);
|
|
buf[res]=0; /* set end of string, so we can printf */
|
|
printf(":%s:%d\n", buf, res);
|
|
if (buf[0]=='z') STOP=TRUE;
|
|
}
|
|
/* restore the old port settings */
|
|
tcsetattr(fd,TCSANOW,&ero;oldtio);
|
|
}
|
|
|
|
|
|
</VERB></TSCREEN>
|
|
|
|
<SECT1>Non-Canonical Input Processing
|
|
<P>
|
|
In non-canonical input processing mode, input is not assembled into
|
|
lines and input processing (erase, kill, delete, etc.) does not
|
|
occur. Two parameters control the behavior of this mode:
|
|
<TT>c_cc[VTIME]</TT> sets the character timer, and <TT>c_cc[VMIN]</TT>
|
|
sets the minimum number of characters to receive before satisfying the
|
|
read.
|
|
<P>
|
|
If MIN > 0 and TIME = 0, MIN sets the number of characters to receive
|
|
before the read is satisfied. As TIME is zero, the timer is not used.
|
|
<P>
|
|
If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read
|
|
will be satisfied if a single character is read, or TIME is exceeded (t =
|
|
TIME *0.1 s). If TIME is exceeded, no character will be returned.
|
|
<P>
|
|
If MIN > 0 and TIME > 0, TIME serves as an inter-character
|
|
timer. The read will be satisfied if MIN characters are received, or
|
|
the time between two characters exceeds TIME. The timer is restarted
|
|
every time a character is received and only becomes active after the
|
|
first character has been received.
|
|
<P>
|
|
If MIN = 0 and TIME = 0, read will be satisfied immediately. The
|
|
number of characters currently available, or the number of characters
|
|
requested will be returned. According to Antonino (see contributions),
|
|
you could issue a <TT>fcntl(fd, F_SETFL, FNDELAY);</TT> before reading
|
|
to get the same result.
|
|
<P>
|
|
By modifying <TT>newtio.c_cc[VTIME]</TT> and
|
|
<TT>newtio.c_cc[VMIN]</TT> all modes described above can be tested.
|
|
|
|
<TSCREEN><VERB>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <stdio.h>
|
|
|
|
#define BAUDRATE B38400
|
|
#define MODEMDEVICE "/dev/ttyS1"
|
|
#define _POSIX_SOURCE 1 /* POSIX compliant source */
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
|
|
volatile int STOP=FALSE;
|
|
|
|
main()
|
|
{
|
|
int fd,c, res;
|
|
struct termios oldtio,newtio;
|
|
char buf[255];
|
|
|
|
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
|
|
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
|
|
|
|
tcgetattr(fd,&ero;oldtio); /* save current port settings */
|
|
|
|
bzero(&ero;newtio, sizeof(newtio));
|
|
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
|
|
newtio.c_iflag = IGNPAR;
|
|
newtio.c_oflag = 0;
|
|
|
|
/* set input mode (non-canonical, no echo,...) */
|
|
newtio.c_lflag = 0;
|
|
|
|
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
|
|
newtio.c_cc[VMIN] = 5; /* blocking read until 5 chars received */
|
|
|
|
tcflush(fd, TCIFLUSH);
|
|
tcsetattr(fd,TCSANOW,&ero;newtio);
|
|
|
|
|
|
while (STOP==FALSE) { /* loop for input */
|
|
res = read(fd,buf,255); /* returns after 5 chars have been input */
|
|
buf[res]=0; /* so we can printf... */
|
|
printf(":%s:%d\n", buf, res);
|
|
if (buf[0]=='z') STOP=TRUE;
|
|
}
|
|
tcsetattr(fd,TCSANOW,&ero;oldtio);
|
|
}
|
|
</VERB></TSCREEN>
|
|
|
|
<SECT1>Asynchronous Input
|
|
<P>
|
|
<TSCREEN><VERB>
|
|
#include <termios.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/types.h>
|
|
|
|
#define BAUDRATE B38400
|
|
#define MODEMDEVICE "/dev/ttyS1"
|
|
#define _POSIX_SOURCE 1 /* POSIX compliant source */
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
|
|
volatile int STOP=FALSE;
|
|
|
|
void signal_handler_IO (int status); /* definition of signal handler */
|
|
int wait_flag=TRUE; /* TRUE while no signal received */
|
|
|
|
main()
|
|
{
|
|
int fd,c, res;
|
|
struct termios oldtio,newtio;
|
|
struct sigaction saio; /* definition of signal action */
|
|
char buf[255];
|
|
|
|
/* open the device to be non-blocking (read will return immediatly) */
|
|
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
|
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
|
|
|
|
/* install the signal handler before making the device asynchronous */
|
|
saio.sa_handler = signal_handler_IO;
|
|
saio.sa_mask = 0;
|
|
saio.sa_flags = 0;
|
|
saio.sa_restorer = NULL;
|
|
sigaction(SIGIO,&ero;saio,NULL);
|
|
|
|
/* allow the process to receive SIGIO */
|
|
fcntl(fd, F_SETOWN, getpid());
|
|
/* Make the file descriptor asynchronous (the manual page says only
|
|
O_APPEND and O_NONBLOCK, will work with F_SETFL...) */
|
|
fcntl(fd, F_SETFL, FASYNC);
|
|
|
|
tcgetattr(fd,&ero;oldtio); /* save current port settings */
|
|
/* set new port settings for canonical input processing */
|
|
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
|
|
newtio.c_iflag = IGNPAR | ICRNL;
|
|
newtio.c_oflag = 0;
|
|
newtio.c_lflag = ICANON;
|
|
newtio.c_cc[VMIN]=1;
|
|
newtio.c_cc[VTIME]=0;
|
|
tcflush(fd, TCIFLUSH);
|
|
tcsetattr(fd,TCSANOW,&ero;newtio);
|
|
|
|
/* loop while waiting for input. normally we would do something
|
|
useful here */
|
|
while (STOP==FALSE) {
|
|
printf(".\n");usleep(100000);
|
|
/* after receiving SIGIO, wait_flag = FALSE, input is available
|
|
and can be read */
|
|
if (wait_flag==FALSE) {
|
|
res = read(fd,buf,255);
|
|
buf[res]=0;
|
|
printf(":%s:%d\n", buf, res);
|
|
if (res==1) STOP=TRUE; /* stop loop if only a CR was input */
|
|
wait_flag = TRUE; /* wait for new input */
|
|
}
|
|
}
|
|
/* restore old port settings */
|
|
tcsetattr(fd,TCSANOW,&ero;oldtio);
|
|
}
|
|
|
|
/***************************************************************************
|
|
* signal handler. sets wait_flag to FALSE, to indicate above loop that *
|
|
* characters have been received. *
|
|
***************************************************************************/
|
|
|
|
void signal_handler_IO (int status)
|
|
{
|
|
printf("received SIGIO signal.\n");
|
|
wait_flag = FALSE;
|
|
}
|
|
|
|
</VERB></TSCREEN>
|
|
|
|
<SECT1>Waiting for Input from Multiple Sources
|
|
<P>
|
|
This section is kept to a minimum. It is just intended to be a hint,
|
|
and therefore the example code is kept short. This will not only work
|
|
with serial ports, but with any set of file descriptors.
|
|
<P>
|
|
The select call and accompanying macros use a <TT>fd_set</TT>. This is a
|
|
bit array, which has a bit entry for every valid file descriptor
|
|
number. <TT>select</TT> will accept a <TT>fd_set</TT> with the bits set
|
|
for the relevant file descriptors and returns a <TT>fd_set</TT>, in which
|
|
the bits for the file descriptors are set where input, output, or an exception
|
|
occurred. All handling of <TT>fd_set</TT> is done with the provided
|
|
macros. See also the manual page <TT>select(2)</TT>.
|
|
|
|
<TSCREEN><VERB>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
main()
|
|
{
|
|
int fd1, fd2; /* input sources 1 and 2 */
|
|
fd_set readfs; /* file descriptor set */
|
|
int maxfd; /* maximum file desciptor used */
|
|
int loop=1; /* loop while TRUE */
|
|
|
|
/* open_input_source opens a device, sets the port correctly, and
|
|
returns a file descriptor */
|
|
fd1 = open_input_source("/dev/ttyS1"); /* COM2 */
|
|
if (fd1<0) exit(0);
|
|
fd2 = open_input_source("/dev/ttyS2"); /* COM3 */
|
|
if (fd2<0) exit(0);
|
|
maxfd = MAX (fd1, fd2)+1; /* maximum bit entry (fd) to test */
|
|
|
|
/* loop for input */
|
|
while (loop) {
|
|
FD_SET(fd1, &ero;readfs); /* set testing for source 1 */
|
|
FD_SET(fd2, &ero;readfs); /* set testing for source 2 */
|
|
/* block until input becomes available */
|
|
select(maxfd, &ero;readfs, NULL, NULL, NULL);
|
|
if (FD_ISSET(fd1)) /* input from source 1 available */
|
|
handle_input_from_source1();
|
|
if (FD_ISSET(fd2)) /* input from source 2 available */
|
|
handle_input_from_source2();
|
|
}
|
|
|
|
}
|
|
|
|
</VERB></TSCREEN>
|
|
<P>
|
|
The given example blocks indefinitely, until input from one of
|
|
the sources becomes available. If you need to timeout on input, just
|
|
replace the select call by:
|
|
<TSCREEN><VERB>
|
|
int res;
|
|
struct timeval Timeout;
|
|
|
|
/* set timeout value within input loop */
|
|
Timeout.tv_usec = 0; /* milliseconds */
|
|
Timeout.tv_sec = 1; /* seconds */
|
|
res = select(maxfd, &ero;readfs, NULL, NULL, &ero;Timeout);
|
|
if (res==0)
|
|
/* number of file descriptors with input = 0, timeout occurred. */
|
|
</VERB></TSCREEN>
|
|
|
|
This example will timeout after 1 second. If a timeout occurs, select
|
|
will return 0, but beware that <TT>Timeout</TT> is decremented by the
|
|
time actually waited for input by <TT>select</TT>. If the timeout
|
|
value is zero, select will return immediatly.
|
|
|
|
|
|
<SECT>Other Sources of Information
|
|
<P>
|
|
|
|
<ITEMIZE>
|
|
|
|
<ITEM>The Linux Serial-HOWTO describes how to set up serial ports and
|
|
contains hardware information.
|
|
|
|
<ITEM><URL URL="http://www.easysw.com/~mike/serial" NAME="Serial
|
|
Programming Guide for POSIX Compliant Operating Systems">, by Michael
|
|
Sweet. This link is obsolete and I could not find a new location for
|
|
it. Does somebody know where we can find it again? It was a well
|
|
prepared document!
|
|
|
|
<ITEM>The manual page <TT>termios(3)</TT> describes all flags for the
|
|
<TT>termios</TT> structure.
|
|
|
|
</ITEMIZE>
|
|
|
|
<SECT>Contributions
|
|
<P>
|
|
As mentioned in the introduction, I am no expert in this field, but
|
|
had problems myself, and found a solution with the help of others.
|
|
Thanks for the help from Mr. Strudthoff from the European Transonic
|
|
Windtunnel, Cologne, Michael Carter
|
|
(<TT>mcarter@rocke.electro.swri.edu</TT>, and Peter Waltenberg
|
|
(<TT>p.waltenberg@karaka.chch.cri.nz</TT>)
|
|
|
|
<P>
|
|
Antonino Ianella (<TT>antonino@usa.net</TT> wrote the
|
|
Serial-Port-Programming Mini HOWTO, at the same time I prepared this
|
|
document. Greg Hankins asked me to incorporate Antonino's Mini-HOWTO
|
|
into this document.
|
|
<P>
|
|
The structure of this document and SGML formatting was derived from
|
|
the Serial-HOWTO by Greg Hankins. Thanks also for various corrections
|
|
made by : Dave Pfaltzgraff (<TT>Dave_Pfaltzgraff@patapsco.com</TT>),
|
|
Sean Lincolne (<TT>slincol@tpgi.com.au</TT>), Michael Wiedmann
|
|
(<TT>mw@miwie.in-berlin.de</TT>), and Adrey Bonar (<TT>andy@tipas.lt</TT>).
|
|
|
|
</ARTICLE>
|