From 20ff5da0b98ad9957d964addd64217522954f1b8 Mon Sep 17 00:00:00 2001 From: "Martin A. Brown" Date: Wed, 9 Mar 2016 07:51:30 -0800 Subject: [PATCH] received from David Lawyer --- .../Serial-Programming-Hoxie-HOWTO-1.sgml | 389 ++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 LDP/inprogress/Serial-Programming-Hoxie-HOWTO-1.sgml diff --git a/LDP/inprogress/Serial-Programming-Hoxie-HOWTO-1.sgml b/LDP/inprogress/Serial-Programming-Hoxie-HOWTO-1.sgml new file mode 100644 index 00000000..babba6b0 --- /dev/null +++ b/LDP/inprogress/Serial-Programming-Hoxie-HOWTO-1.sgml @@ -0,0 +1,389 @@ + + +
+The Linux Serial Programming HOWTO, Part 1 of 2 +<AUTHOR>By Vernon C. Hoxie +<DATE>v2.0 10 September 1999 +<ABSTRACT> +This document describes how to program communications with devices +over a serial port on a Linux box. +</ABSTRACT> +<TOC> + +<SECT>Copyright +<P> +The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Vernon +Hoxie. 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. + +All translations, derivative works, or aggregate works incorporating +this Linux HOWTO document must be covered under this copyright notice. +That is, you may not produce a derivative work from this HOWTO and +impose additional restrictions on its distribution. + +This version is a complete rewrite of the previous <EM> +Serial-Programming-HOWTO </EM> by Peter H. Baumann, <URL +URL="mailto:Peter.Baumann@dlr.de"> + +<SECT>Introduction +<P> +This HOWTO will attempt to give hints about how to write a program +which needs to access a serial port. Its principal focus will be on +the Linux implementation and what the meaning of the various +library functions available. + +Someone asked about which of several sequences of operations was +right. There is no absolute right way to accomplish an outcome. The +options available are too numerous. If your sequences produces the +desired results, then that is the right way for you. Another +programmer may select another set of options and get the same results. +His method is right for him. + +Neither of these methods may operate properly with some other +implementation of UNIX. It is strange that many of the concepts which +were implemented in the SYSV version have been dumped. Because UNIX +was developed by AT&T and much code has been generated on those +concepts, the AT&T version should be the standard to which others +should emulate. + +Now the standard is POSIX. + +It was once stated that the popularity of UNIX and C was that they +were created by programmers for programmers. Not by scholars who +insist on purity of style in deference to results and simplicity of +use. Not by committees with people who have diverse personal or +proprietary agenda. Now ANSI and POSIX have strayed from those original +clear and simply concepts. + +<SECT>Opening +<P> +The various serial devices are opened just as any other file. +Although, the <it>fopen(3)</it> command may be used, the plain +<it>open(2)</it> is preferred. This call returns the file +descriptor which is required for the various commands that configure +the interface. + +<it>Open(2)</it> has the format: +<tscreen><verb> +#include <fcntl.h> +int open( char *path, int flags, [int mode] ); </verb></tscreen> + +In addition to the obvious O_RDWR, O_WRONLY and O_RDONLY, two +additional flags are available. These are O_NONBLOCK and O_NOCTTY. +Other flags listed in the <it>open(2)</it> manual page are not applicable to +serial devices. + +Normally, a serial device opens in "blocking" mode. This +means that the <it>open()</it> will not return until the Carrier +Detect line from the port is active, e.g. modem, is active. When +opened with the O_NONBLOCK flag set, the <it>open()</it> will return +immediately regardless of the status of the DCD line. The +"blocking" mode also affects the <it>read()</it> call. + +The <it>fcntl(2)</it> command can be used to change the +O_NONBLOCK flag anytime after the device has been opened. + +The device driver and the data passing through it are controlled +according to settings in the <it>struct termios</it>. This structure is +defined in "/usr/include/termios.h". In the Linux +tree, further reference is made to +"/usr/include/asm/termbits.h". + +In blocking mode, a <it>read(2)</it> will block until data is available or a +signal is received. It is still subject to state of the ICANON flag. + +When the <it>termios.c_lflag</it> ICANON bit is set, input data is +collected into strings until a NL, EOF or EOL character is received. +You can define these in the <it>termios.c_cc[]</it> array. Also, +ERASE and KILL characters will operate on the incoming data before it +is delivered to the user. + +In non-canonical mode, incoming data is quanitified by use of the +<it>c_cc[VMIN</it> and <it>c_cc[VTIME]</it> values in <it>termios.c_cc[]</it>. + +Some programmers use the <it>select()</it> call to detect the +completion of a <it>read()</it>. This is not the best way of checking for +incoming data. <it>Select()</it> is part of the SOCKETS +scheme and too complex for most applications. + +A full explanation of the fields of the <it>termios</it> structure is +contained in <it>termios(7)</it> of the Users Manual. A version is +included in Part 2 of this HOWTO document. + +<SECT>Commands +<P> +Changes to the <it>struct termios</it> are made by retrieving the current +settings, making the desired changes and transmitting the modified +structure back to the kernel. + +The historic means of communicating with the kernel was by use of the +<it>ioctl( fd, COMMAND, arg )</it> system call. Then the purists in the +computer industry decided that this was not genetically consistent. +Their argument was that the argument changed its stripes. Sometimes +it was an <it>int</it>, sometimes it was a <it>pointer +to int</it> and other times it was a <it>pointer to struct +termios</it>. Then there were those times it was empty or NULL. +These variations are dependent upon the COMMAND. + +As a alternative, the <it>tc*</it> series of functions were +concocted. + +These are: <tscreen><verb> +int tcgetattr( int <it/filedes/, struct termios *termios_p ); +int tcsetattr( int <it/filedes/, int <it/optional_actions/, + *const struct termios <it/termios_p/ ); </verb></tscreen> +instead of: <tscreen><verb> +int ioctl( int <it/filedes/, int <it/command/, + *const struct termios <it/termios_p/ ); </verb></tscreen> +where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF. + +The TCSETS <it>command</it> is comparable to the TCSANOW +<it>optional_action</it> for the <it>tc*</it> version. These direct +the kernel to adopt the changes immediately. Other pairs are: +<tscreen><verb> + command optional_action Meaning + TCSETSW TCSADRAIN Change after all output has drained. + TCSETSF TCSAFLUSH Change after all output has drained + then discard any input characters + not read. +</verb></tscreen> + +Since the return code from either the <it>ioctl(2)</it> or the +<it>tcsetattr(2)</it> commands only indicate that the command was +processed by the kernel. These do not indicate whether or not the +changes were actually accomplished. Either of these commands should +be followed by a call to: + +<tscreen><verb> +ioctl( fd, TCGETS, &new_termios );</verb></tscreen> +or: +<tscreen><verb> +tcgetattr( fd, &new_termios );</verb></tscreen> + +A user function which makes changes to the <it>termios</it> structure should +define two <it>struct termios</it> variables. One of these variables should +contain the desired configuration. The other should contain a copy of +the kernels version. Then after the desired configuration has been +sent to the kernel, another call should be made to retrieve the +kernels version. Then the two compared. + +Here is an example of how to add RTS/CTS flow control: +<tscreen><verb> +struct termios my_termios; +struct termios new_termios; + +tcgetattr( fd, &my_termios ); +my_termios.c_flag |= CRTSCTS; +tcsetattr( fd, TCSANOW, &my_termios ); +tcgetattr( fd, &new_termios ); +if ( memcmp( my_termios, new_termios, + sizeof( my_termios )) != 0 ) { + /* do some error handling */ +} +</verb></tscreen> + +<SECT>Changing Baud Rates +<P> +With Linux, the baud rate can be changed using a technique similar +to add/delete RTS/CTS. +<tscreen><verb> +struct termios my_termios; +struct termios new_termios; + +tcgetattr( fd, &my_termios ); +my_termios.c_flag &= ˜CBAUD; +my_termios.c_flag |= B19200; +tcsetattr( fd, TCSANOW, &my_termios ); +tcgetattr( fd, &new_termios ); +if ( memcmp( my_termios, new_termios, + sizeof( my_termios )) != 0 ) { + /* do some error handling */ +} +</verb></tscreen> + +POSIX adds another method. They define: +<tscreen><verb> +speed_t cfgetispeed( const struct termios *termios_p ); +speed_t cfgetospeed( const struct termios *termios_p ); +</verb></tscreen> + +library calls to extract the current input or output speed from the +struct termios pointed to with <it>*termio_p</it>. This is a variable +defined in the calling process. In practice, the data contained in +this termios, should be obtained by the <it>tcgetattr()</it> call or +an <it>ioctl()</it> call using the TCGETS command. + +The companion library calls are: +<tscreen><verb> +int cfsetispeed( struct termios *termios_p, speed_t speed ); +int cfsetospeed( struct termios *termios_p, speed_t speed ); +</verb></tscreen> +which are used to change the value of the baud rate in the locally +defined <it>*termios_p</it>. Following either of these calls, either +a call to <it>tcsetattr()</it> or <it>ioctl()</it> with one of TCSETS, +TCSETSW or TCSETSF as the command to transmit the change to the +kernel. + +The <it>cf*</it> commands are preferred for portability. Some weird Unices +use a considerably different format of <it>termios</it>. + +Most implementations of Linux use only the input speed for both input +and output. These functions are defined in the application program by +reference to <termios.h>. In reality, they are in +/usr/include/asm/termbits.h. + +<SECT>Additional Control Calls +<P> +<SECT1>Sending a "break". +<P> <tscreen><verb> +int ioctl( fd, TCSBRK, int arg ); +int tcsendbreak( fd, int arg );</verb></tscreen> + +Send a <bf>break</bf>: Here the action differs between the +conventional <it>ioctl()</it> call and the POSIX call. For the +conventional call, an <it>arg</it> of '0' sets the break control line +of the UART for 0.25 seconds. For the POSIX command, the break line +is set for <it>arg</it> times 0.1 seconds. + +<SECT1>Hardware flow control. +<P> <tscreen><verb> +int ioctl( fd, TCXONC, int action ); +int tcflow( fd, int action ); </verb></tscreen> + +The <it>action</it> flags are: +<itemize> +<item> TCOOFF 0 suspend output +<item> TCOON 1 restart output +<item> TCIOFF 2 transmit STOP character to suspend input +<item> TCION 3 transmit START character to restart input +</itemize> + +<SECT1>Flushing I/O buffers. +<P> <tscreen><verb> +int ioctl( fd, TCFLSH, queue_selector ); +int tcflush( fd, queue_selector ); </verb></tscreen> + +The <it>queue_selector</it> flags are: +<itemize> +<item> TCIFLUSH 0 flush any data not yet read from the input buffer +<item> TCOFLUSH 1 flush any data written to the output buffer but not + yet transmitted +<item> TCIOFLUSH 2 flush both buffers +</itemize> + +<SECT>Modem control +<P> +The hardware modem control lines can be monitored or modified by +the <it>ioctl(2)</it> system call. A set of comparable <it>tc*</it> calls +apparently do not exist. The form of this call is:<newline> +<tscreen><verb> +int ioctl( fd, COMMAND, (int *)flags ); </verb></tscreen> + +The COMMANDS and their action are: +<itemize> +<item> TIOCMBIS turn on control lines depending upon which bits + are set in <it>flags</it>. +<item> TIOCMBIC turn off control lines depending upon which bits + are unset in <it>flags</it>. +<item> TIOCMGET the appropriate bits are set in <it>flags</it> according + to the current status +<item> TIOCMSET the state of the UART is changed according to which + bits are set/unset in 'flags' +</itemize> +The bit pattern of <it>flags</it> refer to the following control lines: +<itemize> +<item> TIOCM_LE Line enable +<item> TIOCM_DTR Data Terminal Ready +<item> TIOCM_RTS Request to send +<item> TIOCM_ST Secondary transmit +<item> TIOCM_SR Secondary receive +<item> TIOCM_CTS Clear to send +<item> TIOCM_CAR Carrier detect +<item> TIOCM_RNG Ring +<item> TIOCM_DSR Data set ready +</itemize> + +It should be noted that some of these bits are controlled by the +modem and the UART cannot change them but their status can be +sensed by TIOCMGET. Also, most Personal Computers do not provide +hardware for secondary transmit and receive. + +There are also a pair of <it>ioctl()</it> to monitor these lines. They +are undocumented as far as I have learned. The commands are +TIOCMIWAIT and TCIOGICOUNT. They also differ between versions of +the Linux kernel. + +See the lines.c file in my "serial_suite" for an example of how these +can be used see <URL +URL="ftp://scicom.alphacd.com/pub/linux/serial_suite"> + +<SECT>Process Groups +<P> +<SECT1>Sessions +<P> +<SECT1>Process Groups +<P> +Any newly created process inherits the Process Group of its creator. +The Process Group leader has the same PID as PGID. + +<SECT1>Controlling Terminal +<P> +There are a series of <it>ioctl(2)</it> and <it>tc*(2)</it> calls +which can be used to monitor or to change the process group to which +the device is attached. + +<SECT2>Get the foreground group process id. +<P> +If there is no foreground group, a number not representing an existing +process group is returned. On error, a -1 is returned and errno is +set. +<tscreen><verb> + int ioctl( fd, TIOCGPGRP, (pid_t *)pid ); + int tcgetpgrp( fd, (pid_t *)pid ); </verb></tscreen> + +<SECT2>Set the foreground process group id of a terminal. +<P> +The <it>fd</it> must be the controlling terminal and be associated +with the session of the calling process. +<tscreen><verb> +int ioctl( fd, TIOCSPGRP, (pid_t *)pid ); +int tcsetpgrp( fd, (pid_t *)pid ); </verb></tscreen> + +<SECT2>Get process group id. +<P> +<tscreen><verb> +int ioctl( fd, TIOCGPGRP, &(pid_t)pid ); +int tcgetpgrp( fd, &(pid_t)pid ); </verb></tscreen> + +<SECT>Lockfiles +<P> +Any process which accesses a serial device should first check for +the existence of lock file for the desired device. If such a lock +lock file exists, this means that the device may be in use by another +process. + +Check my "libdevlocks-x.x.tgz" at <URL +URL="ftp://scicom.alphacdc.com/pub/linux"> for an example of how these +lock files should be utilized. + +<SECT>Additional Information +<P> +Check out my "serial_suite.tgz" for more information about +programming the serial ports at <URL +URL="mailto:vern@zebra.alphacdc.com">. There some examples and some +blurbs about setting up modems and comments about some general +considerations. + +<SECT>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 <URL URL="mailto:vern@zebra.alphacdc.com"> via email. +Please include the version number of the Serial-Programming-HOWTO when +writing. + +</ARTICLE>