Linux Character Device Drivers
Deep Dive into Static Allocation, Kernel Restraints, and Device Registration
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.
1. Implementation File & Build Setup
static_alloc.cBelow 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);
Makefileobj-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
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.
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
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.
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
