Introduction
This article summarizes how to use the common Linux device-registration functions register_chrdev_region(), register_chrdev(), and alloc_chrdev_region().
Header file declarations
Functions for allocating, registering, and unregistering device numbers are declared in fs.h, as follows:
extern int register_chrdev_region(dev_t, unsigned, const char *); // Static allocation and registration of device number
extern int alloc_chrdev_region(dev_t *, unsigned, const char *); // Dynamically allocate and register a device number
extern int register_chrdev(unsigned int, const char *, struct file_operations *); // If first arg is 0 then dynamic, nonzero means static
extern int unregister_chrdev(unsigned int, const char *); // Unregister device number
extern void unregister_chrdev_region(dev_t, unsigned); // Unregister device number
Registering device numbers
Static registration
To use register_chrdev_region() you first define a dev_t variable to represent the device number:
dev_t dev_num;
To register a device you need a major device number.
Compose a device number from major and minor:
dev_num = MKDEV(major, minor); // major is the major device number, minor is the minor number
Register the device number:
register_chrdev_region(dev_num, 2, "dev_name");
The first argument is the device number, the second is the number of devices to register, and the third is the device name.
Dynamic registration
If the device number is not known in advance, request one dynamically:
alloc_chrdev_region(&dev_num, minor, 2, "dev_name");
The first argument returns the allocated device number, the second is the first minor number, the third is the count, and the fourth is the device name.
Retrieve the major number from a device number:
dev_major = MAJOR(dev_num);
Adding a character device to the kernel
struct cdev devno;
cdev_init(&devno, &file_ops); // Initialize the cdev
devno.owner = THIS_MODULE; // .owner indicates who owns this driver
devno.ops = &mem_fops;
If the major number is known, add the device with:
cdev_add(&devno, dev_num, count);
If the device number was allocated dynamically, use:
cdev_add(&devno, MKDEV(mem_major, 0), count);
The first argument to cdev_add() is the device, the second is the device number, and the third is the number of minor devices to register. mem_major is the major number saved when allocated dynamically.
Unregistering devices
To unregister a device number, call:
unregister_chrdev_region(dev_t, unsigned);
The first argument must match the device number used when registering. If the device number was allocated dynamically, save it for later. The second argument is the count of minor devices.
Differences with register_chrdev()
Signature:
register_chrdev(unsigned int, const char *, struct file_operations *);
If the first argument is 0, the kernel dynamically allocates a major number for the driver. If nonzero, the driver requests a specific major number. The second argument is the device name and the third is the file_operations pointer. When dynamically allocated, the function returns the assigned major number.
Corresponding unregister function:
unregister_chrdev(unsigned int, const char *);
The first argument is the major number and must match the value used during registration. If the major number was allocated dynamically, save it for later. The second argument is the device name.
In summary, register_chrdev_region() differs from register_chrdev() by including the explicit step of registering a cdev with the kernel. register_chrdev_region() is effectively a more explicit version of register_chrdev().
ALLPCB