Skip to content

Char Devices

Linux character devices are a fundamental component of the kernel's device driver model, enabling communication with hardware that requires sequential data transfer. Unlike block devices (which handle random access to data blocks), character devices operate on a stream of bytes, making them ideal for devices like keyboards, serial ports, and sensors. Understanding their implementation is critical for kernel module development, as they form the basis for many peripheral interactions.


Device Numbers and Registration

Every character device is uniquely identified by a device number, a 32-bit value split into major and minor components. The major number identifies the driver responsible for the device, while the minor number distinguishes between multiple instances of the same driver (e.g., multiple serial ports).

Creating Device Numbers

Device numbers are created using the MKDEV() macro or make_dev_t() function:

dev_t dev_num = MKDEV(250, 0);  // Major 250, minor 0
The dev_t type is defined in <linux/kdev_t.h>, and the /proc/devices file lists all registered major numbers.

Registering Devices

To register a character device, use register_chrdev_region() or alloc_chrdev_region():

int ret = register_chrdev_region(dev_num, 1, "my_char_dev");
if (ret < 0) {
    printk(KERN_ERR "Failed to register device\n");
    return ret;
}
The latter dynamically assigns a major number, simplifying module development.


Character Device Drivers

A character driver implements the file_operations structure, defining callbacks for I/O operations:

struct file_operations fops = {
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
};
Key functions include: - open(): Initializes device-specific data (e.g., allocating buffers). - read()/write(): Handles data transfer, often using copy_to_user()/copy_from_user(). - release(): Cleans up resources when the device is unopened.

Synchronization and Locking

Drivers must protect against race conditions using mutexes or spinlocks. For example:

static DEFINE_MUTEX(my_mutex);

int my_open(struct inode *inode, struct file *file) {
    mutex_lock(&my_mutex);
    // Device-specific initialization
    mutex_unlock(&my_mutex);
    return 0;
}


Role in Linux System Architecture

Character devices bridge user-space applications and hardware, enabling direct interaction with peripherals. They are essential for: - Input devices (keyboards, mice) - Serial communication (RS-232 ports) - Custom hardware (e.g., sensors, GPIO interfaces)

Unlike block devices, character devices do not cache data, ensuring real-time responsiveness. They are also used in device drivers for virtual devices (e.g., /dev/zero or /dev/random).


Key takeaways

  • Character devices use major/minor numbers to map to drivers and instances.
  • Device registration requires register_chrdev_region() or alloc_chrdev_region().
  • The file_operations structure defines driver behavior for I/O operations.
  • Synchronization mechanisms like mutexes are critical for thread safety.
  • Character devices enable direct, stream-based communication with hardware and virtual devices.