Linux Character Device Drivers

 

Linux Character Device Drivers

Deep Dive into Static Allocation, Kernel Restraints, and Device Registration

12 Bits Major Number Width
20 Bits Minor Number Width
511 Max Kernel Major Limit

Understanding Device Numbers

In Linux, devices are represented as files under the /dev directory. The kernel identifies these device files using standard identifiers: a Major Number (which designates the driver handling the device class) and a Minor Number (used by the driver to distinguish individual physical or virtual instances). This guide explores static allocations, collisions, and hard structural ceilings within modern Linux kernels.

Core Architecture Concepts Covered:
register_chrdev_region()
/proc/devices
Static vs Dynamic Allocation
CHRDEV_MAJOR_MAX
Module Parameters
Bit Overflows

1. Implementation File & Build Setup

Driver Implementation: static_alloc.c

Below is the foundational character driver code implementing static identifier registration using kernel module parameters:

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/kdev_t.h>

#include <linux/fs.h>

 

int major_number = 120;

int minor_number = 0;

char *device_name = “chardev”;

int count = 1;

dev_t devnum;

 

// Allow configuration via insmod command-line arguments

module_param(major_number, int, 0);

module_param(minor_number, int, 0);

module_param(count, int, 0);

module_param(device_name, charp, 0);

 

MODULE_LICENSE(“GPL”);

 

static int __init hello_init(void)

{

devnum = MKDEV(major_number, minor_number);

printk(KERN_INFO “Major Number : %d\n”, MAJOR(devnum));

printk(KERN_INFO “Minor number : %d\n”, MINOR(devnum));

printk(KERN_INFO “count : %d\n”, count);

printk(KERN_INFO “Device Name : %s \n”, device_name);

 

if (!register_chrdev_region(devnum, count, device_name)) {

printk(KERN_INFO “device num registered \n”);

return 0;

} else {

printk(KERN_ALERT “device number reg failed \n”);

return -EBUSY;

}

}

 

static void __exit hello_exit(void)

{

unregister_chrdev_region(devnum, count);

printk(KERN_INFO “Driver unregistered cleanly\n”);

}

 

module_init(hello_init);

module_exit(hello_exit);

Compilation Setup: Makefile

obj-m += static_alloc.o

KDIR = /lib/modules/$(shell uname -r)/build

PWD = $(shell pwd)

 

all:

make -C $(KDIR) M=$(PWD) modules

 

clean:

make -C $(KDIR) M=$(PWD) clean

2. Runtime Analysis & Constraint Testing

Scenario A: Attempting to Use an Existing Major Number

Question: What happens if I attempt to map a static major number that is already in use by another driver in the system (e.g., Major 10 for misc)? Can we force a re-use?

Result: Failure. The operation explicitly fails because major registration fields must be distinct to safely prevent kernel collisions.

$ sudo insmod ./static_alloc.ko major_number=10

[ dmesg Output ]

[ 8601.044048] Major Number : 10

[ 8601.044050] Minor number : 0

[ 8601.044050] count : 1

[ 8601.044051] Device Name : chardev

[ 8601.044052] device number reg failed

Key Takeaway: This clear error risk is why real-world production scripts avoid hardcoded options, favoring dynamic major number allocation (via alloc_chrdev_region) instead.

Scenario B: Reusing an Existing Driver Name

Question: Can we register a device using an identical text label string (e.g., "usb") as an existing driver, provided we choose an open, distinct major index?

Result: Success. The kernel indexes device registry tables by number rather than name. The naming property is conversational and allowed to overlap across varying major keys.

$ sudo insmod ./static_alloc.ko major_number=311 device_name=usb

[ dmesg Output ]

[11785.653156] Major Number : 311

[11785.653162] Minor number : 0

[11785.653164] count : 1

[11785.653165] Device Name : usb

[11785.653168] device num registered

Checking cat /proc/devices will verify a new row entry alongside the old reference: 311 usb.

3. Structural Ceiling Thresholds

Minor Number Structural Capacity

The standard modern dev_t allocation reserves a solid 20-bit block explicitly for tracking individual instances.

  • Mathematical Max Capacity: $2^{20} = 1,048,576$ unique sub-devices per driver allocation block.
  • Consecutive Allocations: You can safely allocate blocks spanning consecutive ranges (e.g., requesting a count of 5 elements starting from index 0).
  • Warning: Attempting to specify an allocation offset boundary beyond the $2^{20}$ bit limit triggers instant memory truncation or data overflow failure.
Major Number Boundaries: The Artificial Ceiling

Architectural layout guidelines allocate a 12-bit block to manage primary driver indices ($2^{12} = 4096$ values mapping from 0 to 4095). However, trying to blindly claim the highest indices fails:

$ sudo insmod ./static_alloc.ko major_number=4095

[ dmesg Output ]

[13087.802651] Major Number : 4095

[13087.802656] Minor number : 0

[13087.802657] count : 1

[13087.802658] Device Name : chardev

[13087.802660] CHRDEV “chardev” major requested (4095) is greater than the maximum (511)

[13087.802664] device number reg failed

Why did this fail?

While the architectural layout theoretically supports 4,096 indices, the internal Linux subsystem imposes deliberate structural ceilings inside its header configurations to optimize registry performance:

/* Located within the core kernel header include/linux/fs.h */

#define CHRDEV_MAJOR_MAX 512

Consequently, the usable numeric range for assigned drivers stops directly at 511. Allocations matching

Leave a Reply

Your email address will not be published. Required fields are marked *