从linux 内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。相比devfs,udev有很多优势,在此就不罗嗦了,提醒一 点,udev是应用层的东东,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,以作者所写的一个字符设备驱动为例,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。(不太明白什么是devfs,udev,对linux中的文件系统没有什么概念呢。)


大致用法如下:

struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);

class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);

这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件



class_create()

-------------------------------------------------

linux-2.6.22/include/linux/device.h

struct class *class_create(struct module *owner, const char *name)

    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.

在/sys/class/下创建类目录




class_device_create()

-------------------------------------------------

linux-2.6.22/include/linux/device.h

struct class_device *class_device_create(struct class        *cls,

                                         struct class_device *parent,

                                         dev_t               devt,

                                         struct device       *device,

                                         const char          *fmt, ...)


    class_device_create - creates a class device and registers it with sysfs

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

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

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

    @device: a pointer to a struct device that is assiociated with this class device.

    @fmt: string for the class device's name


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


 


我们在刚开始写Linux设备驱动程序的时候, 很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设 备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。


内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类, 内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。


注意,在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(…)函数,用于在模块卸载时删除类。


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,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。


下面以一个简单字符设备驱动来展示如何使用这几个函数 

    #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");  //创建一个名称为i2c-dev的class

     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节点



 


class_device_create(); 

class_device_destroy(); 


在2.6.27中变为: 

device_create() 

device_destroy()