I'm programming a driver for a serial communication device that works the following way:
1 - I call a read function from user space.
2 - The system enters in a busy wait until the first character arrives on the FIFO.
3 - A character arrives and the system reads it.
4 - Now, the system shall wait (in a non-busy waiting state) until the next character arrives, thus a waiting queue is being used.
5 - When a character arrives, the hardware raises an interruption. The handler of this interruption then wakes the reading process, which continues.
Initially, I had the following schema for the code:
read_function(int number) {
busy_wait(!fifo_empty)
for(i = 0, i < number) {
char_read = global_buffer;
wait_event_interruptible(queue,!fifo_empty);
}
irq_handler() {
wake_up(queue);
global_buffer = read_fifo();
return IRQ_HANDLED;
}
However, this had the following problem: After I read the fifo on the handler, the condition "fifo_empty" becomes true. This means that even though I woke up the process in a moment where it was false, before I exit the handler the condition will return back to true and then the reading process won't continue.
I then decided to remove the buffer, and put the read_fifo() call on the read function. However, this causes some problems, namely:
a) The flag that raises the interrupt doesn't go down before I do read_fifo. I'm seeing the interruption being called numerous times on the kernel messages, and the way the function behaves in terms of what is coming first is pretty random.
b) If a character arrives when the read function has not been called, the kernel crashes because the interrupt enters a loop.
c) For some reason, even after having typed number
characters, the system won't leave the loop.
I would then like to know whether should I put the fifo_read call on the handler or the read. I strongly believe (based on other answers on this site) that it should be put on the handler. But then how can I wake the process? Obviously using some global variables would do the trick, but I am wondering if there is any better way to do it.