How can I create a device node from the init_module code of a Linux kernel module?
Asked Answered
C

3

51

I am writing a module for the Linux kernel, and I want to create some device nodes in the init() function:

int init_module(void)
{
    Major = register_chrdev(0, DEVICE_NAME, &fops);

    // Now I want to create device nodes
    // with the returned major number
}

I also want the kernel to assign a minor number for my first node, and then I will assign the other nodes' minor numbers by myself.

How can I do this in the code? I don’t want to create devices from the shell using mknod().

Cigarillo answered 11/5, 2011 at 21:3 Comment(0)
A
80

To have more control over the device numbers and the device creation, you could do the following steps (instead of register_chrdev()):

  1. Call alloc_chrdev_region() to get a major number and a range of minor numbers to work with.
  2. Create a device class for your devices with class_create().
  3. For each device, call cdev_init() and cdev_add() to add the character device to the system.
  4. For each device, call device_create(). As a result, among other things, Udev will create device nodes for your devices. There isn’t any need for mknod() or the like. device_create() also allows you to control the names of the devices.

There are probably many examples of this on the Internet, and one of them is here.

Adige answered 12/5, 2011 at 4:52 Comment(11)
Sorry for digging this up from the past, but is there an equivalent method of doing this when the license is not GPL? class_create cannot be used with non-GPL licenses.Ilex
@Piotr: actually, I don't know if it exists.Adige
Looks good. Thanks for sharing. Question: how do I cleanup the registered device(s) and file(s) under /dev/my_dev_files ?Keene
@Nikita Vorontsov, the following cleanup operations are executed: device_destroy (it also takes care of deleting the device node), cdev_del unregisters the device from the kernel. After each device has been deleted, class_destroy is called to delete the class and then - unregister_chrdev_region. What is done when creating the devices is undone in reverse order, as usual.Adige
@Adige - A great answer !! Thanks. I have one question - Isn't the device_create() should be called before the call to cdev_add() ?Hangup
I think device_create() should be called after cdev_add(). cdev_add() prepares the in kernel structures to maintain the device and device_create() makes the device available to the user space among other things. And as soon as you make your device available to the user space, that device should be ready to handle requests from there.Adige
@Adige Thanks for your reply. I'm following the Linux Device Drivers, 3rd Edition by O'Reilly - and up to this stage they don't mention the device_create method, but they do introduce the cdev_add and they mention the same as thing you mention only for the cdev_add method. I guess that later on when they do call the device_create in their samples they will call them both in the order you mentioned. Thanks for enlightening that point out (see the end of section 3.4 to see my point: makelinux.net/ldd3/chp-3-sect-4). Many thanks !!Hangup
@Adige BTW - in the example here https://mcmap.net/q/354793/-where-do-char-device-appear-after-cdev_add-registers-successfully-with-major-on-117 the cdev_add() is also called after device_create().Hangup
LDD3 is great, but yes, it is a bit outdated. Previously, one would create device "files" with mknod. device_create() become available later. As for other examples where cdev_add() is called after device_create() - well, I do not know if it is a right thing to do and prefer to play safer. One can try to build such example module and try to load it while repeatedly trying to access the devices it creates at the same time. If the kernel has no protection w.r.t. such cases, it may crash. Or may be, it detects "uninitialized" devices somehow - I just do not know.Adige
Sorry to dig it up but I have a question in this. After the device file etc is created, I would like to read, write etc to it. For this I have to change the file perms using chmod every time I load the module. Is there an automated way to do this.Tades
You can use an udev rule, for example, to set the desired permissions automatically. See unix.stackexchange.com/questions/111593/…Adige
M
15
static int __init ofcd_init(void) /* Constructor */
{
    printk(KERN_INFO "Welcome!");
    if (alloc_chrdev_region(&first, 0, 1, "char_dev") < 0)    //$cat /proc/devices
    {
        return -1;
    }
    if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
    {
        unregister_chrdev_region(first, 1);
        return -1;
    }
    if (device_create(cl, NULL, first, NULL, "mynull") == NULL)    //$ls /dev/
    {
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return -1;
    }
    cdev_init(&c_dev, &fops);
    if (cdev_add(&c_dev, first, 1) == -1)
    {
        device_destroy(cl, first);
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return -1;
    }
    return 0;
}
Mcneill answered 3/9, 2013 at 14:24 Comment(3)
Perhaps cdev_add() should be called before device_create(). That seems to be the more common order.Gallicism
Code might be clear if you would use goto like kernel uses.Rouvin
Your cleanup is missing cdev_del. See: https://mcmap.net/q/345521/-how-can-i-create-a-device-node-from-the-init_module-code-of-a-linux-kernel-moduleCoronel
C
9

Minimal runnable example

Minimized from other answers. GitHub upstream with test setup.

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h> /* register_chrdev, unregister_chrdev */
#include <linux/module.h>
#include <linux/seq_file.h> /* seq_read, seq_lseek, single_release */

#define NAME "lkmc_character_device_create"

static int major = -1;
static struct cdev mycdev;
static struct class *myclass = NULL;

static int show(struct seq_file *m, void *v)
{
    seq_printf(m, "abcd");
    return 0;
}

static int open(struct inode *inode, struct file *file)
{
    return single_open(file, show, NULL);
}

static const struct file_operations fops = {
    .llseek = seq_lseek,
    .open = open,
    .owner = THIS_MODULE,
    .read = seq_read,
    .release = single_release,
};

static void cleanup(int device_created)
{
    if (device_created) {
        device_destroy(myclass, major);
        cdev_del(&mycdev);
    }
    if (myclass)
        class_destroy(myclass);
    if (major != -1)
        unregister_chrdev_region(major, 1);
}

static int myinit(void)
{
    int device_created = 0;

    /* cat /proc/devices */
    if (alloc_chrdev_region(&major, 0, 1, NAME "_proc") < 0)
        goto error;
    /* ls /sys/class */
    if ((myclass = class_create(THIS_MODULE, NAME "_sys")) == NULL)
        goto error;
    /* ls /dev/ */
    if (device_create(myclass, NULL, major, NULL, NAME "_dev") == NULL)
        goto error;
    device_created = 1;
    cdev_init(&mycdev, &fops);
    if (cdev_add(&mycdev, major, 1) == -1)
        goto error;
    return 0;
error:
    cleanup(device_created);
    return -1;
}

static void myexit(void)
{
    cleanup(1);
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
Coronel answered 6/8, 2017 at 12:3 Comment(1)
Santilli - +1 for the great example.Hangup

© 2022 - 2024 — McMap. All rights reserved.