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().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alptugay
  • 1,676
  • 4
  • 22
  • 29

3 Answers3

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eugene
  • 5,977
  • 2
  • 29
  • 46
  • 2
    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. – Piotr May 28 '14 at 18:28
  • 1
    @Piotr: actually, I don't know if it exists. – Eugene May 29 '14 at 12:35
  • Looks good. Thanks for sharing. Question: how do I cleanup the registered device(s) and file(s) under /dev/my_dev_files ? – Nikita Vorontsov Jun 21 '14 at 13:22
  • 3
    @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. – Eugene Jun 22 '14 at 14:17
  • @Eugene - A great answer !! Thanks. I have one question - Isn't the device_create() should be called before the call to cdev_add() ? – Guy Avraham Oct 24 '17 at 05:33
  • 1
    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. – Eugene Oct 24 '17 at 09:14
  • @Eugene 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: http://www.makelinux.net/ldd3/chp-3-sect-4). Many thanks !! – Guy Avraham Oct 24 '17 at 12:24
  • @Eugene BTW - in the example here https://stackoverflow.com/a/41327360/1971003 the cdev_add() is also called after device_create(). – Guy Avraham Oct 24 '17 at 12:36
  • 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. – Eugene Oct 24 '17 at 14:09
  • 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. – Parth K Jan 30 '19 at 07:46
  • You can use an udev rule, for example, to set the desired permissions automatically. See https://unix.stackexchange.com/questions/111593/allow-non-root-user-to-read-write-dev-files – Eugene Jan 31 '19 at 10:43
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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Edwin Jose
  • 151
  • 1
  • 2
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");
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985