mirror of https://github.com/tLDP/LDP
docbook from linuxdoc
This commit is contained in:
parent
9522f8325c
commit
342a4e7682
|
@ -0,0 +1,838 @@
|
|||
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
|
||||
|
||||
<article>
|
||||
|
||||
<articleinfo>
|
||||
|
||||
<title>Serial Programming HOWTO</title>
|
||||
|
||||
<author>
|
||||
<firstname>Gary</firstname>
|
||||
<surname>Frerking</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>gary@frerking.org</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
|
||||
<author>
|
||||
<firstname>Peter</firstname>
|
||||
<surname>Baumann</surname>
|
||||
</author>
|
||||
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>1.01</revnumber>
|
||||
<date>2001-08-26</date>
|
||||
<authorinitials>glf</authorinitials>
|
||||
<revremark>
|
||||
New maintainer, converted to DocBook
|
||||
</revremark>
|
||||
</revision>
|
||||
|
||||
<revision>
|
||||
<revnumber>1.0</revnumber>
|
||||
<date>1998-01-22</date>
|
||||
<authorinitials>phb</authorinitials>
|
||||
<revremark>
|
||||
Initial document release
|
||||
</revremark>
|
||||
</revision>
|
||||
|
||||
</revhistory>
|
||||
|
||||
<abstract>
|
||||
|
||||
<para>
|
||||
This document describes how to program communications with devices
|
||||
over a serial port on a Linux box.
|
||||
</para>
|
||||
|
||||
</abstract>
|
||||
|
||||
</articleinfo>
|
||||
|
||||
<!-- Section1: intro -->
|
||||
|
||||
<sect1 id="intro">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This is the first update to the initial release of the Linux Serial
|
||||
Programming HOWTO. The primary purpose of this update is to change the
|
||||
author information and convert the document to DocBook format. In terms
|
||||
of technical content, very little if anything has changed at this time.
|
||||
Sweeping changes to the technical content aren't going to happen
|
||||
overnight, but I'll work on it as much as time allows.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you've been waiting in the wings for someone to take over this HOWTO,
|
||||
you've gotten your wish. Please send me any and all feedback you have,
|
||||
it'd be very much appreciated.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All examples were tested using a i386 Linux Kernel 2.0.29.
|
||||
</para>
|
||||
|
||||
<!-- Section2: copyright -->
|
||||
|
||||
<sect2 id="copyright">
|
||||
<title>Copyright Information</title>
|
||||
|
||||
<para>
|
||||
This document is copyrighted (c) 1997 Peter Baumann,
|
||||
(c) 2001 Gary Frerking and is distributed under the terms of
|
||||
the Linux Documentation Project (LDP) license, stated below.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Unless otherwise stated, Linux HOWTO documents are copyrighted
|
||||
by their respective authors. 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 like to be notified of any
|
||||
such distributions.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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 like to
|
||||
be notified of any plans to redistribute the HOWTOs.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you have any questions, please contact
|
||||
<email>linux-howto@metalab.unc.edu</email>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<!-- Section2: disclaimer -->
|
||||
|
||||
<sect2 id="disclaimer">
|
||||
<title>Disclaimer</title>
|
||||
|
||||
<para>
|
||||
No liability for the contents of this documents can be accepted.
|
||||
Use the concepts, examples and other content at your own risk.
|
||||
As this is a new edition of this document, there may be errors
|
||||
and inaccuracies, that may of course be damaging to your system.
|
||||
Proceed with caution, and although this is highly unlikely,
|
||||
the author(s) do not take any responsibility for that.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All copyrights are held by their by their respective owners, unless
|
||||
specifically noted otherwise. Use of a term in this document
|
||||
should not be regarded as affecting the validity of any trademark
|
||||
or service mark.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Naming of particular products or brands should not be seen
|
||||
as endorsements.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You are strongly recommended to take a backup of your system
|
||||
before major installation and backups at regular intervals.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<!-- Section2: newversions-->
|
||||
|
||||
<sect2 id="newversions">
|
||||
<title>New Versions</title>
|
||||
|
||||
<para>
|
||||
As previously mentioned, not much is new in terms of technical
|
||||
content yet.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<!-- Section2: credits -->
|
||||
|
||||
<sect2 id="credits">
|
||||
<title>Credits</title>
|
||||
|
||||
<para>
|
||||
The original author thanked Mr. Strudthoff, Michael Carter, Peter Waltenberg,
|
||||
Antonino Ianella, Greg Hankins, Dave Pfaltzgraff, Sean Lincolne, Michael Wiedmann,
|
||||
and Adrey Bonar.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<!-- Section2: feedback -->
|
||||
|
||||
<sect2 id="feedback">
|
||||
<title>Feedback</title>
|
||||
|
||||
<para>
|
||||
Feedback is most certainly welcome for this document. Without
|
||||
your submissions and input, this document wouldn't exist. Please
|
||||
send your additions, comments and criticisms to the following
|
||||
email address : <email>gary@frerking.org</email>.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Getting started</title>
|
||||
|
||||
<sect2>
|
||||
<title>Debugging</title>
|
||||
|
||||
<para>
|
||||
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
|
||||
(<literal remap="tt">ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</literal>
|
||||
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
|
||||
<literal remap="tt">#define MODEMDEVICE "/dev/ttyS0"</literal> has to be checked. Set it to
|
||||
<literal remap="tt">ttyS0</literal> for COM1, <literal remap="tt">ttyS1</literal> for COM2, etc.. It is
|
||||
essential for testing, that <Emphasis>all</Emphasis> 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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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 <literal remap="tt">/dev/mouse</literal> 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.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Port Settings</title>
|
||||
|
||||
<para>
|
||||
The devices <literal remap="tt">/dev/ttyS*</literal> 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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All parameters can be easily configured from within a program. The
|
||||
configuration is stored in a structure <literal remap="tt">struct termios</literal>, which
|
||||
is defined in <literal remap="tt"><asm/termbits.h></literal>:
|
||||
|
||||
<screen>
|
||||
#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 */
|
||||
};
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This file also includes all flag definitions. The input mode flags in
|
||||
<literal remap="tt">c_iflag</literal> handle all input processing, which means that the
|
||||
characters sent from the device can be processed before they are read
|
||||
with <literal remap="tt">read</literal>. Similarly <literal remap="tt">c_oflag</literal> handles the output
|
||||
processing. <literal remap="tt">c_cflag</literal> contains the settings for the port, as
|
||||
the baudrate, bits per character, stop bits, etc.. The local mode
|
||||
flags stored in <literal remap="tt">c_lflag</literal> determine if characters are echoed,
|
||||
signals are sent to your program, etc.. Finally the array
|
||||
<literal remap="tt">c_cc</literal> defines the control characters for end of file, stop,
|
||||
etc.. Default values for the control characters are defined in
|
||||
<literal remap="tt"><asm/termios.h></literal>. The flags are described in the manual
|
||||
page <literal remap="tt">termios(3)</literal>. The structure <literal remap="tt">termios</literal> contains the
|
||||
<literal remap="tt">c_line</literal> (line discipline) element, which is not used in POSIX compliant systems.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Input Concepts for Serial Devices</title>
|
||||
|
||||
<para>
|
||||
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 <literal remap="tt">read</literal> for
|
||||
the whole string did not show any errors.
|
||||
</para>
|
||||
|
||||
<sect3>
|
||||
<title>Canonical Input Processing</title>
|
||||
|
||||
<para>
|
||||
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 <literal remap="tt">read</literal> will only return a
|
||||
full line of input. A line is by default terminated by a <literal remap="tt">NL</literal>
|
||||
(ASCII <literal remap="tt">LF</literal>), an end of file, or an end of line character. A
|
||||
<literal remap="tt">CR</literal> (the DOS/Windows default end-of-line) will not terminate a
|
||||
line with the default settings.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Canonical input processing can also handle the erase, delete word, and
|
||||
reprint characters, translate <literal remap="tt">CR</literal> to <literal remap="tt">NL</literal>, etc..
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Non-Canonical Input Processing</title>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Asynchronous Input</title>
|
||||
|
||||
<para>
|
||||
The two modes described above can be used in synchronous and asynchronous
|
||||
mode. Synchronous is the default, where a <literal remap="tt">read</literal> statement will
|
||||
block, until the read is satisfied. In asynchronous mode the
|
||||
<literal remap="tt">read</literal> statement will return immediatly and send a signal to the
|
||||
calling program upon completion. This signal can be received by a
|
||||
signal handler.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Waiting for Input from Multiple Sources</title>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The approach presented below seems rather complex, but it is important
|
||||
to keep in mind that Linux is a multi-processing operating system. The
|
||||
<literal remap="tt">select</literal> 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.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Program Examples</title>
|
||||
|
||||
<para>
|
||||
All examples have been derived from <literal remap="tt">miniterm.c</literal>. The type
|
||||
ahead buffer is limited to 255 characters, just like the maximum
|
||||
string length for canonical input processing
|
||||
(<literal remap="tt"><linux/limits.h></literal> or <literal remap="tt"><posix1_lim.h></literal>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The descriptions are not complete, but you are encouraged to
|
||||
experiment with the examples to derive the best solution for your
|
||||
application.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Don't forget to give the appropriate serial ports the right
|
||||
permissions (e. g.: <literal remap="tt">chmod a+rw /dev/ttyS1</literal>)!
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Canonical Input Processing</title>
|
||||
|
||||
<para>
|
||||
|
||||
<screen>
|
||||
#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,&oldtio); /* save current serial port settings */
|
||||
bzero(&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,&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,&oldtio);
|
||||
}
|
||||
|
||||
</screen>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Non-Canonical Input Processing</title>
|
||||
|
||||
<para>
|
||||
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:
|
||||
<literal remap="tt">c_cc[VTIME]</literal> sets the character timer, and <literal remap="tt">c_cc[VMIN]</literal>
|
||||
sets the minimum number of characters to receive before satisfying the
|
||||
read.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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 <literal remap="tt">fcntl(fd, F_SETFL, FNDELAY);</literal> before reading
|
||||
to get the same result.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
By modifying <literal remap="tt">newtio.c_cc[VTIME]</literal> and
|
||||
<literal remap="tt">newtio.c_cc[VMIN]</literal> all modes described above can be tested.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
<screen>
|
||||
#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,&oldtio); /* save current port settings */
|
||||
|
||||
bzero(&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,&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,&oldtio);
|
||||
}
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Asynchronous Input</title>
|
||||
|
||||
<para>
|
||||
|
||||
<screen>
|
||||
#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,&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,&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,&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,&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;
|
||||
}
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Waiting for Input from Multiple Sources</title>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The select call and accompanying macros use a <literal remap="tt">fd_set</literal>. This is a
|
||||
bit array, which has a bit entry for every valid file descriptor
|
||||
number. <literal remap="tt">select</literal> will accept a <literal remap="tt">fd_set</literal> with the bits set
|
||||
for the relevant file descriptors and returns a <literal remap="tt">fd_set</literal>, in which
|
||||
the bits for the file descriptors are set where input, output, or an exception
|
||||
occurred. All handling of <literal remap="tt">fd_set</literal> is done with the provided
|
||||
macros. See also the manual page <literal remap="tt">select(2)</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
<screen>
|
||||
#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, &readfs); /* set testing for source 1 */
|
||||
FD_SET(fd2, &readfs); /* set testing for source 2 */
|
||||
/* block until input becomes available */
|
||||
select(maxfd, &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();
|
||||
}
|
||||
}
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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:
|
||||
|
||||
<screen>
|
||||
int res;
|
||||
struct timeval Timeout;
|
||||
|
||||
/* set timeout value within input loop */
|
||||
Timeout.tv_usec = 0; /* milliseconds */
|
||||
Timeout.tv_sec = 1; /* seconds */
|
||||
res = select(maxfd, &readfs, NULL, NULL, &Timeout);
|
||||
if (res==0)
|
||||
/* number of file descriptors with input = 0, timeout occurred. */
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This example will timeout after 1 second. If a timeout occurs, select
|
||||
will return 0, but beware that <literal remap="tt">Timeout</literal> is decremented by the
|
||||
time actually waited for input by <literal remap="tt">select</literal>. If the timeout
|
||||
value is zero, select will return immediatly.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Other Sources of Information</title>
|
||||
|
||||
<para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The Linux Serial-HOWTO describes how to set up serial ports and
|
||||
contains hardware information.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<ULink URL="http://www.easysw.com/~mike/serial">
|
||||
Serial Programming Guide for POSIX Compliant Operating Systems</ULink>,
|
||||
by Michael Sweet.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The manual page <literal remap="tt">termios(3)</literal> describes all flags for the
|
||||
<literal remap="tt">termios</literal> structure.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</article>
|
||||
|
Loading…
Reference in New Issue