您的位置:首页 > 运维架构 > Linux

在驱动模块初始化函数中实现设备节点的自动创建

2016-07-11 14:30 393 查看
在驱动模块初始化函数中实现设备节点的自动创建
 
我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
###################################################################################################################
需要使用的函数:class_create(…)、device_create(…)、device_destroy(......)、class_destroy(.......)
##################################################################################################################
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于/sys/class下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sys/class下寻找对应的类从而创建设备节点。
注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。
struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
在2.6.26.6内核版本中,struct class定义在头文件include/linux/device.h中:
/*

      * device classes

      */

    struct class {

      const char        *name;

      struct module     *owner;
  nbsp;struct kset         subsys;

      struct list_head         devices;

      struct list_head         interfaces;

      struct kset              class_dirs;

      struct semaphore sem;    /* locks children, devices, interfaces */

      struct class_attribute   *class_attrs;

      struct device_attribute      *dev_attrs;
  int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
  void (*class_release)(struct class *class);

      void (*dev_release)(struct device *dev);
  int (*suspend)(struct device *dev, pm_message_t state);

      int (*resume)(struct device *dev);
};
class_create(…)在/drivers/base/class.c中实现: 

     /**

    * class_create - create a struct class structure

    * @owner: pointer to the module that is to "own" this struct class

    * @name: pointer to a string for the name of this class.

    *

    * This is used to create a struct class pointer that can then be used

    * in calls to device_create().

    *

    * Note, the pointer created here is to be destroyed when finished by

    * making a call to class_destroy().
    */
   struct class *class_create(struct module *owner, const char *name)
   {
      struct class *cls;
      int retval;
      cls = kzalloc(sizeof(*cls), GFP_KERNEL);
      if (!cls) {
           retval = -ENOMEM;
           goto error;
      }
  cls->name = name;

      cls->owner = owner;

      cls->class_release = class_create_release;
  retval = class_register(cls);

      if (retval)

           goto error;
  return cls;
error:

      kfree(cls);

      return ERR_PTR(retval);

    }

    第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 

    在class.c中,还定义了class_destroy(…)函数,用于在模块卸载时删除类。注意:若在模块初始化函数中,调用class_create函数创建了一个类。那么,在模块卸载函数中必须调用class_destroy(…)函数删除这个类。
device_create(…)函数在/drivers/base/core.c中实现: 

    /**

     * device_create - creates a device and registers it with sysfs

     * @class: pointer to the struct class that this device should be registered to

     * @parent: pointer to the parent struct device of this new device, if any

     * @devt: the dev_t for the char device to be added

     * @fmt: string for the device's name

     *

     * This function can be used by char device classes. A struct device

     * will be created in sysfs, registered to the specified class.

     *

     * A "dev" file will be created, showing the dev_t for the device, if

     * the dev_t is not 0,0.

     * If a pointer to a parent struct device is passed in, the newly created

     * struct device will be a child of that device in sysfs.

     * The pointer to the struct device will be returned from the call.

     * Any further sysfs files that might be required can be created using this

     * pointer.

     *

     * Note: the struct class passed to this function must have previously

     * been created with a call to class_create().

     */
    struct device *device_create(struct class *class, struct device *parent,
                        dev_t devt, const char *fmt, ...)
    {
         va_list vargs;
         struct device *dev;
     va_start(vargs, fmt);

         dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs);

         va_end(vargs);

         return dev;

    }
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。注意:使用device_create创建设备节点之前,必须先使用class_create()创建类。
###################################################################################################################
总结:
在模块初始化函数的末尾,先调用class_creat(....)函数创建一个类,这个类在/sys/class目录下有与它对应的文件夹(我们可以打开文件夹看看里面都有什么)。接着调用device_create(.....)创建设备节点,创建的设备节点会出现在/dev目录下。
在模块卸载函数的末尾,先调用device_destroy(...)函数删除/dev目录下的设备节点。再调用class_destroy(....)函数删除/sys/class目录下的类。
如此,当insmod安装驱动模块时,会自动在/sys/class目录下生成我们定义的类,在/dev目录下生成我们定义的设备节点,不需要我们再用mknod命令去生成设备节点。当rmmod卸载驱动模块时,会自动删除/sys/class目录下我们定义的类,自动删除/dev目录下我们定义的设备节点。
###################################################################################################################
下面以一个简单字符设备驱动来展示如何使用这几个函数 

    #include <linux/module.h>

    #include <linux/kernel.h>

    #include <linux/init.h>

    #include <linux/fs.h>

    #include <linux/cdev.h>

    #include <linux/device.h>
MODULE_LICENSE ("GPL");
int hello_major = 555;

    int hello_minor = 0;

    int number_of_devices = 1;
struct cdev cdev;

    dev_t dev = 0;
struct file_operations hello_fops = {

      .owner = THIS_MODULE

    };
static void char_reg_setup_cdev (void)

    {

       int error, devno = MKDEV (hello_major, hello_minor);

       cdev_init (&cdev, &hello_fops);

       cdev.owner = THIS_MODULE;

       cdev.ops = &hello_fops;

       error = cdev_add (&cdev, devno , 1);

       if (error)

           printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}
struct class *my_class;
static int __init hello_2_init (void)

    {

       int result;

       dev = MKDEV (hello_major, hello_minor);

       result = register_chrdev_region (dev, number_of_devices, "hello");

       if (result<0) {

           printk (KERN_WARNING "hello: can't get major number %d/n", hello_major);

           return result;

     }
 char_reg_setup_cdev ();
 /* create your own class under /sysfs */

     my_class = class_create(THIS_MODULE, "my_class");
     if(IS_ERR(my_class)) 
     {
          printk("Err: failed in creating class./n");
          return -1; 
      }
  /* register your own device in sysfs, and this will cause udev to create corresponding device node */

      device_create( my_class, NULL, MKDEV(hello_major, 0), "hello" "%d", 0 );
  printk (KERN_INFO "Registered character driver/n");

      return 0;

    }
static void __exit hello_2_exit (void)

    {

       dev_t devno = MKDEV (hello_major, hello_minor);

       cdev_del (&cdev);
   device_destroy(my_class, MKDEV(adc_major, 0));
        //delete device node under /dev
       class_destroy(my_class);                               //delete class created by us
   unregister_chrdev_region (devno, number_of_devices);
   printk (KERN_INFO "char driver cleaned up/n");

    }
module_init (hello_2_init);

module_exit (hello_2_exit);
这样,模块加载后,就能在/dev目录下找到hello0这个设备节点了。
 
例子2
 
drivers/i2c/i2c-dev.c
 
/*

 * module load/unload record keeping

 */
static int __init i2c_dev_init(void)

{

 int res;
 printk(KERN_INFO "i2c /dev entries driver/n");
 res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

 if (res)

  goto out;
 i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
 if (IS_ERR(i2c_dev_class)) {
  res = PTR_ERR(i2c_dev_class);
  goto out_unreg_chrdev;
 }
 res = i2c_add_driver(&i2cdev_driver);

 if (res)

  goto out_unreg_class;
 return 0;
out_unreg_class:

 class_destroy(i2c_dev_class);

out_unreg_chrdev:

 unregister_chrdev(I2C_MAJOR, "i2c");

out:

 printk(KERN_ERR "%s: Driver Initialisation failed/n", __FILE__);

 return res;

}
在 i2c_dev_init创建类i2c_dev_class
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
 
static int i2cdev_attach_adapter(struct i2c_adapter *adap)

{

 struct i2c_dev *i2c_dev;

 int res;
 i2c_dev = get_free_i2c_dev(adap);

 if (IS_ERR(i2c_dev))

  return PTR_ERR(i2c_dev);
 /* register this i2c device with the driver core */

 i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,

         MKDEV(I2C_MAJOR, adap->nr), NULL,

         "i2c-%d", adap->nr);

 if (IS_ERR(i2c_dev->dev)) {

  res = PTR_ERR(i2c_dev->dev);

  goto error;

 }

 res = device_create_file(i2c_dev->dev, &dev_attr_name);

 if (res)

  goto error_destroy;
 pr_debug("i2c-dev: adapter [%s] registered as minor %d/n",

   adap->name, adap->nr);

 return 0;

error_destroy:

 device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

error:

 return_i2c_dev(i2c_dev);

 return res;

}

在i2cdev_attach_adapter调用device_create(i2c_dev_class, &adap->dev,

         MKDEV(I2C_MAJOR, adap->nr), NULL,

         "i2c-%d", adap->nr);

这样在dev目录就产生i2c-0  或i2c-1节点
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zhenwenxian/archive/2010/03/28/5424434.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息