Day 12 - Event-Driven GPIO Driver¶
Summary¶
Today I upgraded the GPIO IRQ driver from a simple IRQ demo into an event-driven Linux-style driver.
The driver now supports:
- blocking read
- non-blocking read
- poll-based event notification
What I Implemented¶
1. Wait queue support¶
Added a wait queue so user-space readers can sleep until an event occurs.
The queue is initialized in probe() and used by both read() and .poll().
2. Event state fields¶
Added simple event state fields:
These fields are used to bridge IRQ events to user-space reads.
3. IRQ wakeup path¶
Updated the IRQ handler so it:
- reads the button state
- stores the event
- wakes sleeping readers
Core event path:
4. Blocking read¶
Implemented blocking read() using:
Behavior:
- no event → sleep
- event occurs → wake and return data
5. Non-blocking read¶
Implemented O_NONBLOCK behavior.
Behavior:
- no event →
-EAGAIN - event available → return immediately
This matches the usual Linux file I/O model.
6. poll support¶
Implemented .poll() so user space can use:
select()poll()epoll()
The poll path uses the same wait queue as blocking read.
What I Learned¶
read() and poll() are different¶
read()fetches datapoll()waits for readiness
This separation is a core idea in Linux event-driven I/O.
Linux does not use user callback style driver APIs¶
Instead of:
Linux uses:
User space owns the event loop.
wait queue is the bridge¶
The wait queue is the mechanism that connects:
- kernel event source
- sleeping user task
- readiness notification
Test Results¶
Blocking read¶
Result:
- blocked as expected
- button event woke the read path
- returned one event line
Non-blocking read¶
Used a test program with O_NONBLOCK.
Result:
- returned
-EAGAINwhen no event was pending - returned event data when available
poll test¶
Used a test program based on poll().
Result:
poll()blocked correctly- IRQ woke the poll waiter
read()returned the expected event data
All expected behaviors were confirmed.
Current Limitations¶
1. Event overwrite¶
The current design uses a single pending flag.
This means multiple IRQs may overwrite each other before user space reads the event.
2. No debounce¶
Mechanical button bounce can generate multiple IRQs.
The driver currently reports raw button events.
3. Synchronization is still simple¶
The current version is suitable for learning, but it does not yet use stronger synchronization primitives such as:
- spinlock
- atomic variable
- ring buffer protection
Next Steps¶
Possible next improvements:
- add software debounce
- move heavier work out of IRQ context
- improve synchronization
- replace single event flag with an event queue
Conclusion¶
Day 12 was an important step toward a more realistic Linux driver.
The driver now demonstrates the standard Linux event-driven model:
This is much closer to real Linux driver behavior than a simple polling-only character device.