1

I have two devices connected to each other via serial port. One operates as a master and the other as a slave.

The master device is an ARM based kit that is running linux.

The current situation is master sends command then poll the com port until the slave replies.

Now I don't wan't to use polling. I need the processor to do other tasks until slave replies.

I know that the solution is to use interrupt but can't find more details. I found some solution that uses signals. It reads the ttys in nonblocking mode then send io signal when data is ready.

So, what is the difference between interrupts and signals in serial communication? Is that right when using interrupts I should write a device driver or kernel module or so? Is there any other efficient solution in order not to use polling?

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Rehab11
  • 483
  • 2
  • 7
  • 16
  • 1
    *"I need the processor to do other tasks ..."* -- That's a given for any efficient programs and required in embedded systems. *"Is there any other efficient solution ..."* -- You start with using a *blocking* **read()**, probably in non-canonical mode. Let the OS do the scheduling work for you, and don't bother with async I/O and signals. Use threads if your program needs to. See http://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read – sawdust Sep 02 '16 at 19:19

1 Answers1

4

Is there any other efficient solution in order not to use polling?

Multi-threading approach

Some time ago I had the same task, and the best way I found to solve it is to use pthreads. Basically, I did next:

  • producer thread (main thread): wait for new data in serial port; once new data is available -- write it into circular buffer (produce data) and wake up the consumer thread via pthread_cond_signal()
  • consumer thread: wait for producer thread to wake me (using pthread_cond_wait() function); once woke up -- read new data from circular buffer (consume data) and process it properly
  • in my case, serial port was open in non-blocking mode, because I wanted to wait for new data with timeout, which provided in poll() function. If you don't need this -- you can open serial port in blocking mode and just use blocking read() call (as proposed by @sawdust in comment)

This way you don't waste CPU time waiting for new data to arrive, and in the same time you don't need to mess with signals (using signals has a lot of complications, so I decided to avoid it).

AIO API

The only other API to do asynchronous read is aio_read() from POSIX AIO interface. But as I understand, aio_read() based on signals. The only difference is:

  • SIGIO signal tells you that new data is available for reading, and you need to read it manually
  • aio_read() gives you read data (as read() does, only asynchronously).

I would not recommend you using it, as this API doesn't seem to be widespread and doesn't have any benefits over signal-driven approach.

So, what is the difference between interrupts and signals in serial communication?

  • Interrupt is low-level concept, so it's being dealt with only in kernel
  • Signal is OS concept, which is used for kernel to notify your user-space application when some event is occurred

In other words, when your serial port hardware generates interrupt, it's handled in kernel, and kernel generates SIGIO signal, notifying your user-space application of new event (e.g. new data is available for reading).

Is that right when using interrupts I should write a device driver or kernel module or so?

Generally yes, you can handle interrupts only in kernel. But in this case you don't need to. It was already done for you in kernel, more specifically, in line discipline code. It generates signal for you (when interrupt occurred), which you can use in your user-space application.

In case you want some details:

  • driver code flow: tty_flip_buffer_push() -> tty_schedule_flip() -> queue_work(...) -> flush_to_ldisc() -> receive_buf() -> tty_ldisc_receive_buf() -> .receive_buf2()
  • line discipline code flow: n_tty_receive_buf2() -> n_tty_receive_buf_common() -> __receive_buf()
  • __receive_buf() does:

    if (read_cnt(ldata)) {
        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
        wake_up_interruptible_poll(&tty->read_wait, POLLIN);
    }
    
  • which sends SIGIO signal, like this: kill_fasync() -> kill_fasync_rcu() -> send_sigio() -> send_sigio_to_task() -> do_send_sig_info() -> send_signal()

So you can rely on kernel to deliver you a message (via signal) when corresponding interrupt occurred in serial port driver.

See also

[1] Serial Programming HOWTO: Asynchronous Input Example

[2] The TTY demystified

[3] LDD3: Asynchronous Notification

Community
  • 1
  • 1
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
  • Do you have any programming example on how to use interrupts? – Rehab11 Sep 02 '16 at 14:05
  • Well, the kernel code is full of them. Take a look [here](http://lxr.free-electrons.com/ident?i=tty_flip_buffer_push) (see **Referenced in** section). Those are all places where kernel runs `tty_flip_buffer_push()`. For example, interrupt for OMAP-based platforms is handled [here](http://lxr.free-electrons.com/source/drivers/tty/serial/omap-serial.c#L563). But frankly, I don't understand what are you trying to achieve in kernel-side? All stuff that needs to be done in kernel is already implemented, so you can just rely on it in user-space, right? – Sam Protsenko Sep 02 '16 at 14:20