4

tl;dr

If I want my module do adhere to modern practices, should I create devices in /dev/ via mknod in a shell script or via class_create and device_create C functions directly in the module source code? What are the advantages of one approach over the other?

In detail

In Chapter 3 of Linux Device Drivers, Third Edition, page 45 the function register_chrdev_region is presented as the function to use for registering a character device if the device numbers are known.

However, since device numbers unused today could become used by the kernel tomorrow, the usage of this function is discouraged almost immediately, at page 46, in favor of alloc_chrdev_region, which allows a dynamic allocation of major numbers.

At this point, though, the authors make the point that one can't create device nodes in /dev/ in advance because to create them the major number has to be known, and it isn't known before alloc_chrdev_region returns.

The presented solution is a shell script, which:

  • loads the module with insmod,
    • which calls the module's init function,
      • which calls alloc_chrdev_region,
        • which results in a new device in /proc/devices,
  • uses awk to extract the major number from that device,
  • finally passes that major number to mknod to create device files in /dev/ (the minor number is encoded in the name of the device files).

However, The Linux Kernel Module Programming Guide makes use of functions class_create and device_create, I believe to accomplish the same task.

What makes me nervous about it is that, despite the book being still updated nowawdays, it has some aspect that makes it older than Linux Device Drivers, Third Edition.

For instance, it makes use of register_chrdev, even though it suggests preferring register_chrdev_region or alloc_chrdev_region, whereas Linux Device Drivers, Third Edition is more categorical in calling register_chrdev The Older Way, relegating its explanation to barely more than half of page 57.

On the other hand, The Linux Kernel Module Programming Guide is not the only source that I've found that uses class_create and device_create C functions, rather than mknod shell function, to create device files. Here's another one, blog post written in early 2018.

So how are things meant to be done today?

3
  • 1
    Devices (and symlinks to devices) are create via udev, have a look at e.g. this intro. Commented Nov 13, 2022 at 7:50
  • Have you looked at what existing modules are doing? Commented Jun 23, 2024 at 11:49
  • @larsks, since I'm interested in char drivers, I would expect to find something under drivers/char, but there, for instance in mem.c I find all of register_chrdev, class_register, and device_create used. Commented Jun 23, 2024 at 14:15

1 Answer 1

1
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #define DEVICE_NAME "example_char_device" #define CLASS_NAME "example_class" static int major_number; static struct class* example_class = NULL; static struct device* example_device = NULL; static struct cdev example_cdev; // Function prototypes static int dev_open(struct inode*, struct file*); static int dev_release(struct inode*, struct file*); static ssize_t dev_read(struct file*, char*, size_t, loff_t*); static ssize_t dev_write(struct file*, const char*, size_t, loff_t*); // File operations structure static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, }; static int __init example_init(void) { printk(KERN_INFO "Example: Initializing the Example LKM\n"); // Dynamically allocate a major number if (alloc_chrdev_region(&major_number, 0, 1, DEVICE_NAME) < 0) { printk(KERN_ALERT "Example failed to register a major number\n"); return major_number; } printk(KERN_INFO "Example: registered correctly with major number %d\n", MAJOR(major_number)); // Register the device class example_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(example_class)) { unregister_chrdev_region(major_number, 1); printk(KERN_ALERT "Failed to register device class\n"); return PTR_ERR(example_class); } printk(KERN_INFO "Example: device class registered correctly\n"); // Register the device driver example_device = device_create(example_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME); if (IS_ERR(example_device)) { class_destroy(example_class); unregister_chrdev_region(major_number, 1); printk(KERN_ALERT "Failed to create the device\n"); return PTR_ERR(example_device); } printk(KERN_INFO "Example: device class created correctly\n"); // Initialize the cdev structure and add it to the kernel cdev_init(&example_cdev, &fops); if (cdev_add(&example_cdev, MKDEV(major_number, 0), 1) < 0) { device_destroy(example_class, MKDEV(major_number, 0)); class_destroy(example_class); unregister_chrdev_region(major_number, 1); printk(KERN_ALERT "Failed to add cdev\n"); return -1; } return 0; } static void __exit example_exit(void) { cdev_del(&example_cdev); device_destroy(example_class, MKDEV(major_number, 0)); class_destroy(example_class); unregister_chrdev_region(major_number, 1); printk(KERN_INFO "Example: Goodbye from the LKM!\n"); } static int dev_open(struct inode* inodep, struct file* filep) { printk(KERN_INFO "Example: Device has been opened\n"); return 0; } static int dev_release(struct inode* inodep, struct file* filep) { printk(KERN_INFO "Example: Device successfully closed\n"); return 0; } static ssize_t dev_read(struct file* filep, char* buffer, size_t len, loff_t* offset) { printk(KERN_INFO "Example: Device read\n"); return 0; } static ssize_t dev_write(struct file* filep, const char* buffer, size_t len, loff_t* offset) { printk(KERN_INFO "Example: Device write\n"); return len; } module_init(example_init); module_exit(example_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple Linux char driver"); MODULE_VERSION("0.1"); 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.