Arc I · Linux Driver Lab
Chapter 01

Message Queue Driver

The first question: how does data get from the kernel to userspace safely?

character driver ring buffer wait queue mutex

Why this driver?

Camera pipelines are producer-consumer systems. A sensor produces frames; an application consumes them. Before I could understand GStreamer or V4L2, I needed to understand the most fundamental version of this: how does a kernel-space producer hand data to a userspace consumer reliably?

This driver is that question in code — a 16-slot ring buffer exposed through a character device.

How it works

Writers push 128-byte messages into the buffer via write(). Readers call read() and block if the buffer is empty. The kernel wait queue handles the sleeping and waking.

ParameterValue
Device node/dev/sanath_queue
Buffer16 slots × 128 bytes
Syncmutex
Full-buffer policyDrop oldest (overwrite)

The ring buffer

typedef struct kernel_logger {
    uint8_t kernel_buffer[ROW_SIZE][MEM_SIZE];
    uint8_t read_indexer;
    uint8_t write_indexer;
    struct mutex etx_mutex;
    uint8_t count;
} kernel_logger_t;

Drop-oldest on full

if (kernel_logger.count < ROW_SIZE)
    kernel_logger.count++;
else
    kernel_logger.read_indexer =
        (kernel_logger.read_indexer + 1) % ROW_SIZE;
This is identical to how camera pipelines handle a slow consumer — keep the newest frame, discard the oldest. Freshness over completeness.

Blocking read

while (kernel_logger.count == 0) {
    mutex_unlock(&kernel_logger.etx_mutex);
    if (wait_event_interruptible_exclusive(etx_wait_queue,
                                           kernel_logger.count > 0))
        return -ERESTARTSYS;
    mutex_lock(&kernel_logger.etx_mutex);
}

The exclusive flag means only one waiter wakes per write event. Without it every reader wakes up, races to grab the mutex, and all but one find nothing to read — the thundering herd problem.

Key concepts

ConceptWhat I learned
alloc_chrdev_regionDynamic major number — the kernel assigns it, you don't hardcode it
cdev_init / cdev_addBinds your file_operations struct to the character device
class_create / device_createCreates /dev/sanath_queue automatically via udev
copy_to/from_userThe kernel can't trust user pointers — these functions handle faults safely
mutex vs spinlockRead/write run in process context so they can sleep — mutex is correct here