Skip to content

Day12 - Event-Driven GPIO Driver with poll()

Goal

Upgrade the Day11 GPIO IRQ driver so that it supports:

  • blocking read
  • non-blocking read
  • poll/select/epoll compatible event notification

Step 1 - Add Event Fields

Add the following fields into the device private structure:

wait_queue_head_t read_queue;
int event_pending;
int event_btn_state;

Purpose

  • read_queue: used for sleep/wakeup
  • event_pending: indicates whether a new event is waiting
  • event_btn_state: stores the button state for the pending event

Step 2 - Initialize in probe()

Initialize the new fields in the probe function:

init_waitqueue_head(&mydev->read_queue);
mydev->event_pending = 0;
mydev->event_btn_state = 0;

Step 3 - Update IRQ Handler

When an IRQ occurs:

  • read the button state
  • store it as pending event data
  • wake up sleeping readers

Example logic:

mydev->btn_state = value;
mydev->event_btn_state = value;
mydev->event_pending = 1;
wake_up_interruptible(&mydev->read_queue);

Step 4 - Implement Blocking Read

In blocking mode, read() should sleep until an event becomes available.

Core logic:

ret = wait_event_interruptible(mydev->read_queue,
                               mydev->event_pending != 0);
if (ret)
    return ret;

After wakeup:

  • copy event data
  • clear event_pending

Example:

btn = mydev->event_btn_state;
mydev->event_pending = 0;

Step 5 - Add Non-Blocking Read

If the file is opened with O_NONBLOCK, do not sleep.

Example:

if ((file->f_flags & O_NONBLOCK) && !mydev->event_pending)
    return -EAGAIN;

This matches standard Linux non-blocking I/O behavior.


Step 6 - Add poll Support

Include the required header:

#include <linux/poll.h>

Add a poll callback:

static __poll_t mygpio_poll(struct file *file, poll_table *wait)
{
    struct mygpio_dev *mydev = file->private_data;
    __poll_t mask = 0;

    poll_wait(file, &mydev->read_queue, wait);

    if (mydev->event_pending)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

Step 7 - Register poll in file_operations

static const struct file_operations mygpio_fops = {
    .owner = THIS_MODULE,
    .open = mygpio_open,
    .release = mygpio_release,
    .read = mygpio_read,
    .write = mygpio_write,
    .poll = mygpio_poll,
};

Step 8 - Build and Load

make
sudo insmod mygpio-irq.ko
dmesg | tail

Step 9 - Test Blocking Read

Run:

cat /dev/mygpio

Expected behavior:

  • the command blocks
  • pressing or releasing the button wakes the reader
  • one event line is returned

Step 10 - Test Non-Blocking Read

Use a user-space program that opens the device with O_NONBLOCK.

Expected behavior:

  • no event available → read() returns -EAGAIN
  • event available → read() returns data immediately

Step 11 - Test poll()

Example user-space flow:

poll(&pfd, 1, -1);
read(fd, buf, sizeof(buf));

Expected behavior:

  • poll() blocks until an event occurs
  • button IRQ wakes poll()
  • read() consumes the event data

Step 12 - Optional Timeout Test

Example:

ret = poll(&pfd, 1, 5000);

Expected behavior:

  • event occurs within 5 seconds → poll() returns ready
  • no event within 5 seconds → timeout

Result

At the end of this lab, the driver supports:

  • blocking I/O
  • non-blocking I/O
  • event-driven I/O

This is the core Linux model for asynchronous user-space notification.