dev_t#
32 bit unsigned integer where first 20 used to store minor and rest 12 bit used to store major.
see:
include/linux/kdev_t.hinclude/linux/types.h
Char Devices#
There are 2 ways to create a char device node:
- Automatically, via
class_create()/device_create()(udev creates the/deventry).- Manually, via the
mknodcommand.
We’ll go through both.
Note: you must populate
struct file_operationsbefore binding it tostruct cdev(i.e. before callingcdev_init()). See example
Method 1 - class_create() / device_create() (udev)#
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h> /* IS_ERR(), PTR_ERR() */
static dev_t device_number;
struct cdev pcd_cdev;
struct class *class_pcd;
struct device *device_pcd;
struct file_operations pcd_fops;
/* Init function */
static int __init func_init(void)
{
int ret;
/* 1. Dynamically allocate a device number */
ret = alloc_chrdev_region(&device_number, 0, 1, "pcd_devices");
if (ret < 0) {
pr_err("Alloc chrdev failed\n");
goto out;
}
pr_info("Device number <major>:<minor> = %d:%d\n",
MAJOR(device_number), MINOR(device_number));
/* 2. Initialize the cdev structure with fops */
cdev_init(&pcd_cdev, &pcd_fops);
pcd_cdev.owner = THIS_MODULE;
/* 3. Register the device (cdev structure) with the VFS */
ret = cdev_add(&pcd_cdev, device_number, 1);
if (ret < 0) {
pr_err("Cdev add failed\n");
goto unreg_chrdev;
}
/* 4. Create a device class under /sys/class/ */
class_pcd = class_create(THIS_MODULE, "pcd_class");
if (IS_ERR(class_pcd)) {
pr_err("Class creation failed\n");
ret = PTR_ERR(class_pcd);
goto cdev_del;
}
/* 5. Populate sysfs with device information -> triggers udev to create /dev/pcd */
device_pcd = device_create(class_pcd, NULL, device_number, NULL, "pcd");
if (IS_ERR(device_pcd)) {
pr_err("Device create failed\n");
ret = PTR_ERR(device_pcd);
goto class_del;
}
pr_info("Module init was successful\n");
return 0;
class_del:
class_destroy(class_pcd);
cdev_del:
cdev_del(&pcd_cdev);
unreg_chrdev:
unregister_chrdev_region(device_number, 1);
out:
pr_info("Module insertion failed\n");
return ret;
}
/* Cleanup function */
static void __exit func_cleanup(void)
{
device_destroy(class_pcd, device_number);
class_destroy(class_pcd);
cdev_del(&pcd_cdev);
unregister_chrdev_region(device_number, 1);
pr_info("module unloaded\n");
}Method 2 - Manual node creation via mknod#
No use of class_create() / device_create() here, the driver only reserves the major/minor and registers the cdev. You create the /dev entry yourself afterward, using the major/minor printed in dmesg.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
static dev_t device_number;
struct cdev pcd_cdev;
struct file_operations pcd_fops;
/* Init function */
static int __init func_init(void)
{
int ret;
/* 1. Dynamically allocate a device number */
ret = alloc_chrdev_region(&device_number, 0, 1, "pcd_devices");
if (ret < 0) {
pr_err("Alloc chrdev failed\n");
goto out;
}
pr_info("Device number <major>:<minor> = %d:%d\n",
MAJOR(device_number), MINOR(device_number));
/* 2. Initialize the cdev structure with fops */
cdev_init(&pcd_cdev, &pcd_fops);
pcd_cdev.owner = THIS_MODULE;
/* 3. Register the device (cdev structure) with the VFS */
ret = cdev_add(&pcd_cdev, device_number, 1);
if (ret < 0) {
pr_err("Cdev add failed\n");
goto unreg_chrdev;
}
pr_info("Module init was successful\n");
return 0;
unreg_chrdev:
unregister_chrdev_region(device_number, 1);
out:
pr_info("Module insertion failed\n");
return ret;
}
/* Cleanup function */
static void __exit func_cleanup(void)
{
cdev_del(&pcd_cdev);
unregister_chrdev_region(device_number, 1);
pr_info("module unloaded\n");
}# Load the module, then check the major number it was assigned
insmod pcd.ko
cat /proc/devices | grep pcd_devices
dmesg | tail # also prints <major>:<minor> from pr_info above
# Manually create the device node (replace <major>/<minor> with the printed values)
sudo mknod /dev/pcd c <major> <minor>
sudo chmod 666 /dev/pcd # optional: open up permissions for testing
# Clean up
sudo rm /dev/pcd
rmmod pcdSee: embetronicx - Device File Creation for Character Drivers
Trade-off: mknod is quick for bring-up/testing but the node doesn’t survive a reboot and isn’t reproducible across machines (major number can shift). class_create()/device_create() lets udev manage the node automatically and is what real drivers use.