问题
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.
回答1:
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. :-)
来源:https://stackoverflow.com/questions/10134369/async-serial-communication-in-non-canonical-raw-mode-and-generating-sigio-in-l