Async serial communication in non-canonical (raw) mode and generating SIGIO in linux/osx
Asked Answered
S

1

2

To start off, I'm having trouble getting my serial device to generate a SIGIO when data is ready to be read.

I am trying to write a simple serial interface to communicate to a micro using a usb to serial adapter. I'd like to keep this interface as similar to one you would find in a micro (interrupt driven, using callbacks) and so I decided to go the way of catching a SIGIO signal instead of using a separate thread or constant non-blocking serial reads. Here is my initialization code (most of it will look familiar from the various asynchronous serial tutorials):

int mcs = TIOCM_RTS;
ttyDev = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);

if (!ttyDev)
{
  printf("couldn't open serial device");
  return NULL;
}

//create signal handler
byteAction.sa_handler = byteCallback;
sigemptyset(&byteAction.sa_mask); //sa_mask = 0
byteAction.sa_flags = SA_RESTART;
sigaction(SIGPOLL, &byteAction, NULL);

//Allow process to detect SIGIO
fcntl(ttyDev, F_SETOWN, getpid());
fcntl(ttyDev, F_SETFL, FASYNC); //make async
fcntl(ttyDev, F_SETFL, FNDELAY);  //non blocking reads


tcgetattr(ttyDev, &oldtio); //backup current settings
newtio.c_cflag = getBaud(baudrate) | CS8 | CLOCAL | CREAD;
newtio.c_cflag &= ~CRTSCTS; //disable hw flow control
newtio.c_iflag &= ~(IXON | IXOFF | IXANY); //disable flow control
newtio.c_iflag |= IGNPAR; //ignore parity
newtio.c_oflag = 0;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw mode
newtio.c_cc[VMIN] = 1; 
newtio.c_cc[VTIME] = 0;
cfsetspeed(&newtio, getBaud(baudrate));
tcflush(ttyDev, TCIFLUSH);
tcsetattr(ttyDev, TCSANOW, &newtio);

//clear RTS
ioctl(ttyDev, TIOCMBIC, &mcs);

byteCallback is my signal handler. I already verified that it works by sending the process a signal manually. The micro I am communicating with does not support any sort of flow control, so that is disabled. I've verified that I can read and write from the device by using the standard read/write system calls.

The problem is that when data comes in, the signal handler doesn't get called. I suspect it is because SIGIO is only generated when a CR or end of line character is received. Since this is completely raw mode and no such character is being sent, no SIGIO is being generated. At the moment I have only tried this on OSX, but ideally the same code would work on linux as well.

Any help would be appreciated. I've searched quite a bit and people always end up suggesting to just use select() or another method. It's become almost a challenge and I'm not ready to give up just yet.

Thanks.

Slapup answered 13/4, 2012 at 2:4 Comment(1)
My answer should solve your problem, but note that using signals for asynchronous IO is a really bad idea. If you want to know how wrong it is, Emacs does this. :-) The main reason it's bad, all joking aside, is that under most circumstances you're extremely restricted in what functions you can call and what you can do from within a signal handler. Breaking the rules may appear to work, but it will have dangerous race conditions that will cause your program to deadlock or crash very rarely (and of course, being rare, the problem is difficult to reproduce or debug).Zehe
Z
3

Generation of SIGIO definitely has nothing to do with CR/NL. Your problem is that right after you set the FASYNC flag, you cleared it:

fcntl(ttyDev, F_SETFL, FASYNC); //make async
fcntl(ttyDev, F_SETFL, FNDELAY);  //non blocking reads AND CLEAR FASYNC FLAG

What you should be doing is:

fcntl(ttyDev, F_SETFL, O_ASYNC|O_NONBLOCK);

or preferably (to avoid clearing any other flags that may have already been set):

fcntl(ttyDev, F_SETFL, O_ASYNC|O_NONBLOCK|fcntl(ttyDev, F_GETFL));

Note that I also took the liberty of replacing the weird nonstandard flag names you were using with the corresponding standard names. :-)

Zehe answered 13/4, 2012 at 3:4 Comment(4)
Using SIGPOLL instead of SIGIO is a problem also.Chicane
Usually SIGPOLL and SIGIO are the same, but this is all nonportable junk anyway.. Of course even if they weren't the same, these signals' default action is to terminate the process, so OP would notice the signal being generated. :-)Zehe
Ah, I forgot to change the signal back to SIGIO. I was playing around with it to see what I was doing wrong.Slapup
The the person who responded: Thanks. This is probably exactly what is happening. I will try these changes tonight.Slapup

© 2022 - 2024 — McMap. All rights reserved.