Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does SIGIO fire?

Tags:

c

linux

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);
like image 482
Alexander Avatar asked Jul 02 '19 06:07

Alexander


2 Answers

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 SIGIOs. 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.

like image 134
Luis Colorado Avatar answered Oct 18 '22 05:10

Luis Colorado


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

like image 1
Marcos G. Avatar answered Oct 18 '22 04:10

Marcos G.