I am trying to receive serial data, but I can't figure out the timing.
When serial data is received, when does SIGIO fire - upon receiving the first byte of data, or when a specific character is received (CR, NL...)?
Set up serial data port:
/* Open serial port.
* O_RDONLY - Only receive data
* O_NOCTTY - Leave process control to other 'jobs' for better portability.
* O_NDELAY - Enable non-blocking read
*/
fd = open(PORT_PATHNAME, O_RDONLY | O_NOCTTY | O_NDELAY);
fd = open(PORT_PATHNAME, O_RDONLY | O_NOCTTY | O_NDELAY);
/* Set process ID, that will receive SIGIO signals for file desc. events */
fcntl (fd, F_SETOWN, getpid());
/* Enable generation of signals */
fcntl (fd, F_SETFL, O_ASYNC);
...
options.c_oflag &= ~OPOST;
/* Set terminal options using the file descriptor */
tcsetattr(fd, TCSANOW, &options);
struct termios options;
tcgetattr(fd, &options);
/* Set baudrate */
options.c_cflag = B115200;
Set up signal interrupt:
/* Add UART handler function */
saio.sa_handler = signal_handler_IO;
/* Non-zero used for calling sighandler on alternative stacks and so on */
saio.sa_flags = 0;
/* Not specified by POSIX -> not in use */
saio.sa_restorer = NULL;
/* Bind SIGIO (async I/O signals) to the defined structure */
int status = sigaction(SIGIO, &saio, NULL);
SIGIO
is fired to announce you that you can receive characters....(this meaning that if you make a read(2)
syscall, you will receive something) so it means that no signal will be fired if you try to read and no data is ready to be read. As you will read below, this doesn't mean that no characters have been received by your system, it depends on how the serial line is configured. Continue reading...
Your question is very interesting, because the signal is only sent when the driver decides you can read more data, and this can be:
when you hit the return key, if terminal is in canonical mode (cooked or line mode) Until you don't feed a line of data (or hit the ^D
char) you don't make the input buffer available to the application.
when you have received VMIN
charactes in raw mode (in case VMIN
setting is different than 0
)
when VTIME
has elapsed after receiving at least VMIN
characters (after setting VMIN
different than zero)
The reason of SIGIO
is not to have to wait for characters to arrive, so they can be treated in a signal handler. But there's no many use on that, as interrupts are asynchronous in nature, and you will end having to put those characters (after perhaps some possible asynchronous processing can be made) in some kind of buffer. There's a select(2)
or poll(2)
system call to allow you to select between a set of input channels and to be awaken as soon as some input is available.
So, in conclusion, then answer is: it depends.
It depends on how the driver is built, how is it configured, and what kind of device are you treating with. SIGIO
is definitely unconnected to the hardware interrupt that can end in such kind of interrupt to be delivered to the process, and how does it depend is something that can depend on many factors.
Think on this way: If you make a blocking read(2)
you'll be awaken at some point because data is available and you are able to receive some data. The same process that awakes you because some data is available will fire you a SIGIO
if you have configured to receive SIGIO
s. No magic here, that's the process.... if it is the driver that awakens you because your read(2)
is not waiting anymore, that same driver will send you the signal, but only if you have decided not to ignore it.
SIGIO should fire as soon as you get a byte of data.
This is a minimum example tested on Linux with driver ftdi_sio
:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#define tty "/dev/ttyUSB0"
#define baudrate B9600
void byteCallback(int status); // interrupt function
int main(){
int ttyDev = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (!ttyDev)
{
printf("couldn't open serial device");
return -1;
}
struct sigaction byteAction; // set the serial interrupt handler
struct termios oldtio, newtio;
//create signal handler
byteAction.sa_handler = byteCallback;
sigemptyset(&byteAction.sa_mask); //sa_mask = 0
byteAction.sa_flags = SA_RESTART;
sigaction(SIGIO, &byteAction, NULL);
//Allow process to detect SIGIO
fcntl(ttyDev, F_SETOWN, getpid());
fcntl(ttyDev, F_SETFL, FASYNC);
tcgetattr(ttyDev, &oldtio); //backup current settings
newtio.c_cflag = 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;
tcflush(ttyDev, TCIFLUSH);
tcsetattr(ttyDev, TCSANOW, &newtio);
while(1){
//Wait for SIGIO to fire
}
}
void byteCallback(int status){
printf("SIGIO fired!\r\n");
}
See also this question
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With