Re: Keyboard/Serial port monitoring using select()

From: Floyd L. Davidson (floyd_at_barrow.com)
Date: 07/14/04


Date: Wed, 14 Jul 2004 08:50:53 -0800

Kim Adil <ksadil@bigpond.net.au> wrote:
>Floyd L. Davidson wrote:
>
>> Kim Adil <ksadil@bigpond.net.au> wrote:
>
>>
>> Well... hmmm, just like I showed!
>>
>> options.c_iflag = IGNBRK | IGNPAR;
>> options.c_cflag = CREAD | CRTSCTS | CS8;
>> options.c_lflag = 0;
>> options.c_oflag = 0;
>>.
>
>Doh! OK, no my terminal raw mode monitoring works a treat, but my new serial
>port select does not fire. I have a modem connected to ttyS0 and when I
>call it from my mobile, I know thet it broadcasts "RING" but my new code
>does not detect any activity on ttyS0. Any ideas? I suspect it is in the
>options. I wan to set them difinitively, as per your advice. My new code:
>
>#include <sys/types.h>
>#include <sys/stat.h>
>#include <sys/select.h>

There is no need to include sys/select.h, because both sys/types.h
and sys/time.h include it for you.

>#include <sys/time.h>
>#include <fcntl.h>
>#include <errno.h>
>#include <stdio.h>
>#include <termios.h>
>#include <unistd.h>
>#include <linux/serial.h>
>
> int fd;
> char ch;
> int max_fd;
> int status;
> fd_set input;
> struct termios options;
> struct termios toptionsold;
> struct termios toptions;
> struct timeval timeout;
>
>int setterm(void)
> {
> tcgetattr(0,&toptions);
> tcgetattr(0,&toptionsold);
> toptions.c_lflag =0;

Heh heh, when you diddle the flags for stdin, you probably *don't*
want to reset everything! Instead, this is where the examples you
were using previously are correct. Just &= ~(ICANON) to selectively
leave everything the way it was with the exception of the one set of
bits you want to clear. You might, or might not, want to also clear
ISIG along with ICANON.

> tcsetattr(0,TCSANOW,&toptions);
>
> }
>
>int setser(void)
> {
>
> if((fd=open("/dev/ttyS0", O_RDWR|O_NDELAY))!=-1)
> {
> printf("fd=%d\n",fd);
> options.c_iflag = IGNBRK|IGNPAR; //ignore parity errors
> options.c_cflag = CREAD | CRTSCTS |CS8;
> options.c_lflag = ICANON;
> options.c_line = 0;
> cfsetospeed(&options,B38400);
> cfsetispeed(&options,B38400);
> options.c_cc[VTIME]=5;
> options.c_cc[VMIN]= 1;
> tcflush(fd,TCIFLUSH);
> status=tcsetattr(fd,TCSANOW,&options);
> if(status==-1)
> {
> printf("cannot set serial port options\n");
> exit(0);
> }
> }
> max_fd = (0 > fd ? 0 : fd) + 1;
> }
>
>int checkselect(void)
> {
> int retval;
>
> FD_ZERO(&input);
> /* Watch stdin (fd 0) to see when it has input. */
> FD_SET(0, &input);
> /* Watch serial port (fd) to see when it has input. */
> FD_SET(fd, &input);
>
> timeout.tv_sec = 0;
> timeout.tv_usec = 500;
>
> retval = select(max_fd, &options, NULL, NULL, NULL);

This is *not* the way to call select(), and if your compiler
didn't complain then you aren't invoking it with a high enough
level of warnings! See below for the warning about line 69.
Plus, you've set the timeout values, but didn't provide that
to select().

The sensible way to do this is with a Makefile, because you
don't want to type in all the options that gcc requires every
time. Here is what happened when I plugged this into my
standard "test" Makefile by dropping your code into foo.c and
running make. I've folded the long lines to make them fit.
What you are interested in is the huge list of options,

     gcc -O6 -march=i686 -mcpu=i686 -Wall -W -Wcast-align \
         -Wcast-qual -Wmissing-prototypes -Wshadow \
         -Wnested-externs -Wstrict-prototypes \
         -Waggregate-return -finline-functions -c foo.c

Here are the warnings gcc provided:

  foo.c:23: warning: no previous prototype for `setterm'
  foo.c: In function `setterm':
  foo.c:29: warning: control reaches end of non-void function
  foo.c: At top level:
  foo.c:32: warning: no previous prototype for `setser'
  foo.c: In function `setser':
  foo.c:54: warning: control reaches end of non-void function
  foo.c: At top level:
  foo.c:57: warning: no previous prototype for `checkselect'
  foo.c: In function `checkselect':
  foo.c:69: warning: passing arg 2 of `select' from incompatible pointer type
  foo.c:77: warning: empty body in an if-statement
  foo.c: At top level:
  foo.c:81: warning: return-type defaults to `int'
  foo.c:81: warning: function declaration isn't a prototype
  foo.c: In function `main':
  foo.c:101: warning: zero-length format string
  foo.c:104: warning: zero-length format string
  foo.c:110: warning: control reaches end of non-void function
  gcc foo.o -o foo

The minimum level of warnings you would ever want to invoke gcc
with are "gcc -O -Wall -W". But obviously the others are very
helpful too.

You also really do not want to use global variables for
everything. This should use local variables, but in this case I
can't see any reason for putting this wrapper around select()
and having it in a separate function.

> if (retval == -1 )
> return -1;
> if (retval == 0)
> return 0;
> if ( FD_ISSET(0, &input))
> return 1 ;
> if ( FD_ISSET(fd, &input));
> return 3 ;

The fundamental problem with this method is that select() may
return with a true status for *all* of the descriptors tested!
You can only indicate one, and will miss the other. Instead,
put select() inside a loop, and if it returns indicating that
any descriptor is valid, check each of them in turn and read
each that is ready.

Isn't there a nice example in the man page for select()? Copy
it!

> }
>
>main(){

int main(void)

Also you should have a return statement at the end of the main
function.

> /* Initialize the input set */
>
> setterm();
> setser();
>
> /* Initialize the timeout structure */
>
> do
> {
> switch (status = checkselect()) {
> case 1: //got stdin
> read(0,&ch,1);
> printf("Stdin got selected with %c\n",ch);
> break;
> case 3: //got port
> read(fd,&ch,1);
> printf("Modem got selected %c\n",ch);
> break;
> case 0:
> printf("");
> break;
> default:
> printf("");
> }
> }
> while ( ch != 'q' );

Hmmm... one more problem. You are reading only 1 character from
either descriptor. But there might be one or hundreds of
characters waiting.

> tcsetattr(0,TCSANOW,&toptionsold);

            return 0;

>}

-- 
FloydL. Davidson           <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska)                         floyd@barrow.com


Relevant Pages

  • Re: gcc bug? Openoffice port impossibel to compile on 4.8
    ... remove this silly "bitten by the Linux bug" and the red-herring of gcc ... struct bar {int a; int b;} dapper; ... The *warning* emitted by gcc when enough analysis is done (e.g. ...
    (freebsd-hackers)
  • xemacs installation problems
    ... checking for gcc... ... checking whether we are using GNU C... ... checking size of int... ... configure: warning: No OffiX without generic Drag'n'Drop support ...
    (comp.os.linux.misc)
  • Re: How to check if same partition
    ... GCC will bitch when you get this wrong. ... function(int b, int c, int something) ... The gcc version I am using is clever enough not to give this warning ... I covered all cases for argc. ...
    (comp.os.linux.development.apps)
  • Help needed in solving C-errors in Linux (gcc)
    ... i'm trying to make I got the following messages from gcc. ... main.c:22: warning: comparison is always false due to limited range of data type ... int conv_inch2feet; ... int convert(int unit, const char *,const char *,const char *); ...
    (comp.os.linux.development.apps)
  • Help needed in solving C-errors in Linux (gcc)
    ... i'm trying to make I got the following messages from gcc. ... main.c:22: warning: comparison is always false due to limited range of data type ... int conv_inch2feet; ... int convert(int unit, const char *,const char *,const char *); ...
    (alt.os.linux)

Loading