Day 13 - GPIO IRQ (Debounce + Bottom Half)¶
🎯 Learning Goals¶
- Understand IRQ top half vs bottom half design
- Implement software debounce using workqueue
- Learn event-driven driver architecture
- Introduce ring buffer for event queue
- Integrate wait queue + poll/select
🔥 IRQ Design (Top Half vs Bottom Half)¶
Top Half (ISR)¶
Characteristics:
- Runs in interrupt context
- Must be fast
- Cannot sleep
- Should NOT:
- call blocking APIs
- perform heavy logic
Example:
static irqreturn_t mygpio_btn_isr(int irq, void *data)
{
struct mygpio_dev *mydev = data;
mod_delayed_work(system_wq,
&mydev->btn_dwork,
msecs_to_jiffies(mydev->debounce_ms));
return IRQ_HANDLED;
}
👉 ISR only schedules work → best practice
Bottom Half (Workqueue)¶
Runs in process context:
- Can sleep
- Can use mutex
- Can call gpiod_get_value_cansleep()
Example:
static void mygpio_btn_dwork_handler(struct work_struct *work)
{
...
value = gpiod_get_value_cansleep(...);
}
⏱ Software Debounce¶
Why debounce?
- Mechanical button produces noise (bounce)
- Multiple IRQs triggered for one press
Solution:
- Delay sampling after IRQ
- Only report stable state
Flow:
🔄 Event Queue (Ring Buffer)¶
Structure:
Full condition:
Empty condition:
Advantages:
- Supports burst events
- Avoids losing data
- Decouples producer/consumer
🧠 Synchronization Strategy¶
Critical section (protected)¶
- enqueue event
- dequeue event
Use:
Lockless checks¶
Example:
👉 Allowed to race (by design)
🚫 Why NOT use volatile?¶
Key concept:
volatile does NOT provide thread safety
Kernel approach:
- Use mutex / spinlock
- Use memory barrier
- Use atomic operations if needed
💤 Wait Queue (Blocking I/O)¶
Used for blocking read:
Behavior:
- Sleep until condition true
- Woken by:
🔔 poll / select Support¶
Driver implements:
static __poll_t mygpio_poll(...)
{
poll_wait(file, &queue, wait);
if (data_available)
return POLLIN;
}
🧩 Full Data Flow¶
[ IRQ ]
↓
[ ISR (top half) ]
↓
[ delayed work (debounce) ]
↓
[ event queue (ring buffer) ]
↓
[ wake_up_interruptible ]
↓
[ user: poll/select ]
↓
[ user: read() ]
⚠️ Common Pitfalls¶
1. Doing too much in ISR ❌¶
- Reading GPIO
- Logging
- Sleeping
2. Missing wake_up ❌¶
→ read() will block forever
3. Using volatile ❌¶
→ does NOT fix race condition
4. Holding lock too long ❌¶
→ impacts latency
🚀 Summary¶
- ISR should be minimal
- Workqueue handles logic
- Ring buffer decouples events
- Wait queue enables blocking read
- poll enables event-driven user space