Question

I want to create the number of minor character drivers for my module. I want to see it at the /dev/ . However, I see only one driver. What is the problem in my code? What should be the right code?

#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h>  /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h>  /** character device **/
#include <linux/device.h>  /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>


/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");


static struct cdev basicCdev;
static struct class *basicDriverClass;

static int  basicMajorNumber = 0;

#define NUMBER_OF_MINOR_DEVICE (5)

/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/
static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);

static int basicOspen(struct inode *inode, struct file *file);   

/** File Operations function pointer table **/
/** There are plenty of file operations  **/

static struct file_operations fops = {
  .read = basicRead,
  .write = NULL,
  .open = basicOspen,
  .release = NULL
};

static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
     char msg[1024] = "Hello SJ_read\0";
     printk(KERN_ALERT "The Read operation called\r\n");
     copy_to_user( buffer, msg, sizeof(msg) );
     return sizeof(msg);
}

static int basicOspen(struct inode *inode, struct file *file)
{

   printk("Kernel.Basic Driver Opened now!!\r\n");

   return 0;
}

static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
         int err = -1;
         /** MKDEV call creates a device number i.e. combination of major and minor number **/
         int devno = MKDEV(basicMajorNumber, minor);
         /** Initiliaze character dev with fops **/
         cdev_init(dev, fops);
         /**owner and operations initialized **/
         dev->owner = THIS_MODULE;
         dev->ops = fops;
         /** add the character device to the system**/
         /** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
         err = cdev_add (dev, devno, 1);

         if (err)
         {
                 printk (KERN_NOTICE "Couldn't add cdev");
         }
 }

static int chrDriverInit(void)
{

    int result;
    dev_t dev; 

    printk("Welcome!! Device Init now..");

    /** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);  **/
    /** dev -> The dev_t variable type,which will get the major number that the kernel allocates.  **/
    /**The same name will appear in /proc/devices.  **/

    /** it is registering the character device **/   
    /** a major number will be dynamically allocated here **/
    /**  alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
    result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");

    if( result < 0 )
    {
      printk("Error in allocating device");
      return -1;    
    }

    /** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
    /**  creating class, and then device created removes the dependency of calling mknod  **/
    /** A good method - the mknod way is depreciated **/
    /** mknod way is -  mknod /dev/<driver_name> c <majorNumber> <minorNumber>


    /** add the driver to /sys/class/chardrv **/
    if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
    {
        unregister_chrdev_region(dev, 1);
        return -1;
    }

     /** add the driver to /dev/pSeudoDrv -- here **/
    if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
    {
        class_destroy(basicDriverClass);
        unregister_chrdev_region(dev, 1);
        return -1;
    }

     /** let's see what major number was assigned by the Kernel **/
     basicMajorNumber = MAJOR(dev);
     printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );

    /** Now setup the cdev **/
    setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);

    return 0;   

}


static void chrDriverExit(void)
{
    /** A reverse - destroy mechansim -- the way it was created **/
    printk("Releasing Simple Devs -- %s\r\n",  __FUNCTION__);
    /** delete the character driver added **/
    cdev_del(&basicCdev);
    /** destroy the device created **/
    device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
    /** destroy the class created **/
    class_destroy(basicDriverClass);
    /** unregister the chr dev **/
    unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);

}


module_init(chrDriverInit);
module_exit(chrDriverExit);

I get errors, if I modify the init function to the following.

static int chrDriverInit(void)
    {

        int result;
        dev_t dev; 
        dev_t dev2; 

        printk("Welcome!! Device Init now..");

        /** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);  **/
        /** dev -> The dev_t variable type,which will get the major number that the kernel allocates.  **/
        /**The same name will appear in /proc/devices.  **/

        /** it is registering the character device **/   
        /** a major number will be dynamically allocated here **/
        /**  alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
        result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");

        if( result < 0 )
        {
          printk("Error in allocating device");
          return -1;    
        }

        /** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
        /**  creating class, and then device created removes the dependency of calling mknod  **/
        /** A good method - the mknod way is depreciated **/
        /** mknod way is -  mknod /dev/<driver_name> c <majorNumber> <minorNumber>


        /** add the driver to /sys/class/chardrv **/
        if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
        {
            unregister_chrdev_region(dev, 1);
            return -1;
        }

         /** add the driver to /dev/pSeudoDrv -- here **/
        if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
        {
            class_destroy(basicDriverClass);
            unregister_chrdev_region(dev, 1);
            return -1;
        }

         /** let's see what major number was assigned by the Kernel **/
         basicMajorNumber = MAJOR(dev);
         printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );

         dev2 = MKDEV(basicMajorNumber,2);

        #if 1

         if (device_create(basicDriverClass, NULL, dev2, NULL, "pSeudoDrv2") == NULL) //$ls /dev/
        {
            class_destroy(basicDriverClass);
            unregister_chrdev_region(dev, 1);
            return -1;
        }

        #endif


        /** Now setup the cdev **/
        setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);







        return 0;   

    }

Here is the crash -

[19554.180535] CPU: 1 PID: 16636 Comm: insmod Tainted: GF       W  O 3.13.5-101.fc19.x86_64 #1
[19554.180537] Hardware name: Dell Inc. Studio 1558/0G939P, BIOS A07 06/24/2010
[19554.180539]  0000000000000009 ffff8800667d7ad8 ffffffff81680664 ffff8800667d7b20
[19554.180543]  ffff8800667d7b10 ffffffff8106d35d ffff880130f9e810 00000000ffffffef
[19554.180547]  0000000000000000 ffff880133432378 0000000000000000 ffff8800667d7b70
[19554.180551] Call Trace:
[19554.180555]  [<ffffffff81680664>] dump_stack+0x45/0x56
[19554.180558]  [<ffffffff8106d35d>] warn_slowpath_common+0x7d/0xa0
[19554.180562]  [<ffffffff8106d3cc>] warn_slowpath_fmt+0x4c/0x50
[19554.180566]  [<ffffffff81315be4>] kobject_add_internal+0x214/0x320
[19554.180570]  [<ffffffff81316115>] kobject_add+0x65/0xb0
[19554.180574]  [<ffffffff81315550>] ? kobject_put+0x30/0x60
[19554.180578]  [<ffffffff814138b5>] device_add+0x125/0x640
[19554.180582]  [<ffffffff81413fe0>] device_create_groups_vargs+0xe0/0x110
[19554.180586]  [<ffffffffa078d0e0>] ? basicRead+0xc0/0xc0 [character_driver]
[19554.180590]  [<ffffffff81414071>] device_create+0x41/0x50
[19554.180595]  [<ffffffffa078d1d3>] chrDriverInit+0xf3/0x160 [character_driver]
[19554.180598]  [<ffffffff8100214a>] do_one_initcall+0xfa/0x1b0
[19554.180602]  [<ffffffff81054803>] ? set_memory_nx+0x43/0x50
[19554.180609]  [<ffffffff810e060d>] load_module+0x1e1d/0x25b0
[19554.180613]  [<ffffffff810dc320>] ? store_uevent+0x40/0x40
[19554.180617]  [<ffffffff810e0f16>] SyS_finit_module+0x86/0xb0
[19554.180622]  [<ffffffff8168f629>] system_call_fastpath+0x16/0x1b
[19554.180624] ---[ end trace dd8ae4ccda100ad8 ]---
Was it helpful?

Solution

Your problem is that you only call device_create() for the first device. alloc_chrdev_region() only allocate major/minor numbers but it does not actually create the device structures in the kernel. The dev_t returned by alloc_chrdev_region() represents the first of your five major/minor numbers. You need to use a for loop, increment the minor number and then call device_create() on all of them.

Don't forget to destroy all of them in the exit function.

OTHER TIPS

The kernel does not create device nodes automatically.

Something in userspace must do it, using mknod(2) or the mknod utility.

This can be done manually or when a file system image is created, by a script on boot, or by a daemon such as udev.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top