linux设备模型之Class

写在前面:

class_create 与 class_device_create 是在早期版本比如2.6.29上使用的,可以自动创建/dev下设备,register_chrdev很少使用了,这个使用后还需要手动在/dev下创建设备。

现在都使用下面的方法创建设备了。

参考:http://www.wowotech.net/device_model/class.html

    刚开始写字符设备驱动程序的时候,老师教我们自动创建设备节点,“要先创建类,在类下面创建设备,类名字不重要“。

        firstdrv_class = class_create(THIS_MODULE, "firstdrv");
        firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

上面firstdrv会在/sys/class下生成,另外如果device_create中如果将MKDEV(major, 0)修改为0,则不会再/dev下创建设备,只有使用MKDEV(major, 0)才会创建设备,只有这样我们才能通过/dev下的设备操作。

    于是乎,这两行代码被糊里糊涂的复制粘贴了好多次,差点成为一种习惯~        

    前面分析设备总线驱动模型的时候,我们知道,将一个设备调用 device_add 函数注册到内核中去的时候,如果指定了设备号,那么用户空间的 mdev 会根据 sysfs 文件系统中的设备信息去自动创建设备节点。我们看到前面第二行代码里有一个 device_create ,参数里还有设备号 ,八九不离十,里边也间接调用了device_add ,不信一会分析代码。

    类是一个设备的高层视图,它抽象出了低层的实现细节,大概意思就是抽象出了一个通用的接口吧。常见的类设备有 Input 、tty 、usb 、rtc 等等。

    class 就好比 bus ,我们在设备总线驱动模型中创建设备时,要指定它所属的 Bus ,那么在创建类设备的时候也需要指定它所从属的类,class 也离不开 Kobject ,因此如果你了解总线设备驱动模型,你就会发现,其实真的都是差不多的东西。

 

[cpp] view plain copy

  1. struct class {  
  2.     const char      *name;  
  3.     struct module       *owner;  
  4.     struct class_attribute      *class_attrs;  
  5.     struct device_attribute     *dev_attrs;  
  6.     struct kobject          *dev_kobj;  
  7.     int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);  
  8.     char *(*devnode)(struct device *dev, mode_t *mode);  
  9.     void (*class_release)(struct class *class);  
  10.     void (*dev_release)(struct device *dev);  
  11.     int (*suspend)(struct device *dev, pm_message_t state);  
  12.     int (*resume)(struct device *dev);  
  13.     const struct dev_pm_ops *pm;  
  14.     struct class_private *p;  
  15. };  

 

    name,class的名称,会在“/sys/class/”目录下体现。
    class_atrrs,该class的默认attribute,会在class注册到内核时,自动在“/sys/class/xxx_class”下创建对应的attribute文件。
    dev_attrs,该class下每个设备的attribute,会在设备注册到内核时,自动在该设备的sysfs目录下创建对应的attribute文件。
    dev_bin_attrs,类似dev_attrs,只不过是二进制类型attribute。
    dev_kobj,表示该class下的设备在/sys/dev/下的目录,现在一般有char和block两个,如果dev_kobj为NULL,则默认选择char。
    dev_uevent,当该class下有设备发生变化时,会调用class的uevent回调函数。
    class_release,用于release自身的回调函数。
    dev_release,用于release class内设备的回调函数。在device_release接口中,会依次检查Device、Device Type以及Device所在的class,是否注册release接口,如果有则调用相应的release接口release设备指针。

[cpp] view plain copy

  1. struct class_private {  
  2.     struct kset class_subsys;  
  3.     struct klist class_devices;  
  4.     struct list_head class_interfaces;  
  5.     struct kset class_dirs;  
  6.     struct mutex class_mutex;  
  7.     struct class *class;  
  8. };  

    struct class_interface是这样的一个结构:它允许class driver在class下有设备添加或移除的时候,调用预先设置好的回调函数(add_dev和remove_dev)。那调用它们做什么呢?想做什么都行(例如修改设备的名称),由具体的class driver实现。
    该结构的定义如下:

[cpp] view plain copy

  1. struct class_interface {  
  2.     struct list_head        node;  
  3.     struct class            *class;  
  4.   
  5.     int (*add_dev)          (struct device *, struct class_interface *);  
  6.     void (*remove_dev)      (struct device *, struct class_interface *);  
  7. };  

    下面,我们来看 Class 的注册过程,前面我们提到,class->name 会出现在/sys/class 目录下,那么这个目录是哪里来的,代码一看便知。

 

[cpp] view plain copy

  1. int __init classes_init(void)  
  2. {  
  3.     class_kset = kset_create_and_add("class", NULL, NULL);  
  4.     if (!class_kset)  
  5.         return -ENOMEM;  
  6.     return 0;  
  7. }  

    下面,我们来看一下一个Class 的注册过程

 

 

[cpp] view plain copy

  1. #define class_create(owner, name)       \  
  2. ({                      \  
  3.     static struct lock_class_key __key; \  
  4.     __class_create(owner, name, &__key);    \  
  5. })  

[cpp] view plain copy

  1. struct class *__class_create(struct module *owner, const char *name,  
  2.                  struct lock_class_key *key)  
  3. {  
  4.     struct class *cls;  
  5.     int retval;  
  6.   
  7.     cls = kzalloc(sizeof(*cls), GFP_KERNEL);  
  8.     if (!cls) {  
  9.         retval = -ENOMEM;  
  10.         goto error;  
  11.     }  
  12.   
  13.     cls->name = name;  
  14.     cls->owner = owner;  
  15.     cls->class_release = class_create_release;  
  16.   
  17.     retval = __class_register(cls, key);  
  18.     if (retval)  
  19.         goto error;  
  20.   
  21.     return cls;  
  22.   
  23. error:  
  24.     kfree(cls);  
  25.     return ERR_PTR(retval);  
  26. }  

    在 class_create 函数中,只是简单构造了一个class结构体,设置了名字以及所属的模块,然后调用 class_register 

 

 

[cpp] view plain copy

  1. int __class_register(struct class *cls, struct lock_class_key *key)  
  2. {  
  3.     struct class_private *cp;  
  4.     int error;  
  5.   
  6.     pr_debug("device class '%s': registering\n", cls->name);  
  7.   
  8.     cp = kzalloc(sizeof(*cp), GFP_KERNEL);  
  9.     if (!cp)  
  10.         return -ENOMEM;  
  11.     klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);  
  12.     INIT_LIST_HEAD(&cp->class_interfaces);  
  13.     kset_init(&cp->class_dirs);  
  14.     __mutex_init(&cp->class_mutex, "struct class mutex", key);  
  15.     error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);  
  16.     if (error) {  
  17.         kfree(cp);  
  18.         return error;  
  19.     }  
  20.   
  21.     /* set the default /sys/dev directory for devices of this class */  
  22.     if (!cls->dev_kobj)  
  23.         cls->dev_kobj = sysfs_dev_char_kobj;  
  24.   
  25. #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)  
  26.     /* let the block class directory show up in the root of sysfs */  
  27.     if (cls != &block_class)  
  28.         cp->class_subsys.kobj.kset = class_kset;  
  29. #else  
  30.     cp->class_subsys.kobj.kset = class_kset;  
  31. #endif  
  32.     cp->class_subsys.kobj.ktype = &class_ktype;  
  33.     cp->class = cls;  
  34.     cls->p = cp;  
  35.   
  36.     error = kset_register(&cp->class_subsys);  
  37.     if (error) {  
  38.         kfree(cp);  
  39.         return error;  
  40.     }  
  41.     error = add_class_attrs(class_get(cls));  
  42.     class_put(cls);  
  43.     return error;  
  44. }  

    代码第15行,将 cp->class_subsys.kobj 的 name 设置为cls->name

 

    代码第28行,将cp->class_subsys.kobj.kest 设置为class_kest

    代码第36行,将cp->class_subsys 注册进内核,没有设置 cp->class_subsys.kobj.parent ,内核会将cp->class_subsys.kobj.kset.kobj 设置成它的Parent ,这也就是为什么说 class->name  会出现在 /sys/class 目录下的原因。

 

    下面,来看向 class 注册 device 的过程

[cpp] view plain copy

  1. struct device *device_create(struct class *classstruct device *parent,  
  2.                  dev_t devt, void *drvdata, const char *fmt, ...)  
  3. {  
  4.     va_list vargs;  
  5.     struct device *dev;  
  6.   
  7.     va_start(vargs, fmt);  
  8.     dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);  
  9.     va_end(vargs);  
  10.     return dev;  
  11. }  

[cpp] view plain copy

  1. struct device *device_create_vargs(struct class *classstruct device *parent,  
  2.                    dev_t devt, void *drvdata, const char *fmt,  
  3.                    va_list args)  
  4. {  
  5.     struct device *dev = NULL;  
  6.     int retval = -ENODEV;  
  7.   
  8.     if (class == NULL || IS_ERR(class))  
  9.         goto error;  
  10.   
  11.     dev = kzalloc(sizeof(*dev), GFP_KERNEL);  
  12.     if (!dev) {  
  13.         retval = -ENOMEM;  
  14.         goto error;  
  15.     }  
  16.   
  17.     dev->devt = devt;  
  18.     dev->class = class;  
  19.     dev->parent = parent;  
  20.     dev->release = device_create_release;  
  21.     dev_set_drvdata(dev, drvdata);  
  22.   
  23.     retval = kobject_set_name_vargs(&dev->kobj, fmt, args);  
  24.     if (retval)  
  25.         goto error;  
  26.   
  27.     retval = device_register(dev);  
  28.     if (retval)  
  29.         goto error;  
  30.   
  31.     return dev;  
  32.   
  33. error:  
  34.     put_device(dev);  
  35.     return ERR_PTR(retval);  
  36. }  

    上边代码也没有什么好分析的,与我们分析设备总线驱动模型时分析 device 时有一点不一样的就是这里设置的是 dev->class 而不是 dev->bus ,同时这里为 dev设置了设备号 devt ,因此,在sysfs中会创建 dev 属性文件,mdev 就会自动为我们创建设备节点了。

 

 

[cpp] view plain copy

  1. int device_register(struct device *dev)  
  2. {  
  3.     device_initialize(dev);  
  4.     return device_add(dev);  
  5. }  

[cpp] view plain copy

  1. int device_add(struct device *dev)  
  2. {  
  3.     struct device *parent = NULL;  
  4.     struct class_interface *class_intf;  
  5.     int error = -EINVAL;  
  6.   
  7.     dev = get_device(dev);  
  8.     if (!dev)  
  9.         goto done;  
  10.   
  11.     if (!dev->p) {  
  12.         error = device_private_init(dev);  
  13.         if (error)  
  14.             goto done;  
  15.     }  
  16.   
  17.     /* 
  18.      * for statically allocated devices, which should all be converted 
  19.      * some day, we need to initialize the name. We prevent reading back 
  20.      * the name, and force the use of dev_name() 
  21.      */  
  22.     if (dev->init_name) {  
  23.         dev_set_name(dev, "%s", dev->init_name);  
  24.         dev->init_name = NULL;  
  25.     }  
  26.   
  27.     if (!dev_name(dev))  
  28.         goto name_error;  
  29.   
  30.     pr_debug("device: '%s': %s\n", dev_name(dev), __func__);  
  31.   
  32.     parent = get_device(dev->parent);  
  33.     setup_parent(dev, parent);  
  34.   
  35.     /* use parent numa_node */  
  36.     if (parent)  
  37.         set_dev_node(dev, dev_to_node(parent));  
  38.   
  39.     /* first, register with generic layer. */  
  40.     /* we require the name to be set before, and pass NULL */  
  41.     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);  
  42.     if (error)  
  43.         goto Error;  
  44.   
  45.     /* notify platform of device entry */  
  46.     if (platform_notify)  
  47.         platform_notify(dev);  
  48.   
  49.     error = device_create_file(dev, &uevent_attr);  
  50.     if (error)  
  51.         goto attrError;  
  52.   
  53.     if (MAJOR(dev->devt)) {  
  54.         error = device_create_file(dev, &devt_attr);  
  55.         if (error)  
  56.             goto ueventattrError;  
  57.   
  58.         error = device_create_sys_dev_entry(dev);  
  59.         if (error)  
  60.             goto devtattrError;  
  61.   
  62.         devtmpfs_create_node(dev);  
  63.     }  
  64.   
  65.     error = device_add_class_symlinks(dev);  
  66.     if (error)  
  67.         goto SymlinkError;  
  68.     error = device_add_attrs(dev);  
  69.     if (error)  
  70.         goto AttrsError;  
  71.     error = bus_add_device(dev);  
  72.     if (error)  
  73.         goto BusError;  
  74.     error = dpm_sysfs_add(dev);  
  75.     if (error)  
  76.         goto DPMError;  
  77.     device_pm_add(dev);  
  78.   
  79.     /* Notify clients of device addition.  This call must come 
  80.      * after dpm_sysf_add() and before kobject_uevent(). 
  81.      */  
  82.     if (dev->bus)  
  83.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,  
  84.                          BUS_NOTIFY_ADD_DEVICE, dev);  
  85.   
  86.     kobject_uevent(&dev->kobj, KOBJ_ADD);  
  87.     bus_probe_device(dev);  
  88.     if (parent)  
  89.         klist_add_tail(&dev->p->knode_parent,  
  90.                    &parent->p->klist_children);  
  91.   
  92.     if (dev->class) {  
  93.         mutex_lock(&dev->class->p->class_mutex);  
  94.         /* tie the class to the device */  
  95.         klist_add_tail(&dev->knode_class,  
  96.                    &dev->class->p->class_devices);  
  97.   
  98.         /* notify any interfaces that the device is here */  
  99.         list_for_each_entry(class_intf,  
  100.                     &dev->class->p->class_interfaces, node)  
  101.             if (class_intf->add_dev)  
  102.                 class_intf->add_dev(dev, class_intf);  
  103.         mutex_unlock(&dev->class->p->class_mutex);  
  104.     }  
  105. done:  
  106.     put_device(dev);  
  107.     return error;  
  108.  DPMError:  
  109.     bus_remove_device(dev);  
  110.  BusError:  
  111.     device_remove_attrs(dev);  
  112.  AttrsError:  
  113.     device_remove_class_symlinks(dev);  
  114.  SymlinkError:  
  115.     if (MAJOR(dev->devt))  
  116.         device_remove_sys_dev_entry(dev);  
  117.  devtattrError:  
  118.     if (MAJOR(dev->devt))  
  119.         device_remove_file(dev, &devt_attr);  
  120.  ueventattrError:  
  121.     device_remove_file(dev, &uevent_attr);  
  122.  attrError:  
  123.     kobject_uevent(&dev->kobj, KOBJ_REMOVE);  
  124.     kobject_del(&dev->kobj);  
  125.  Error:  
  126.     cleanup_device_parent(dev);  
  127.     if (parent)  
  128.         put_device(parent);  
  129. name_error:  
  130.     kfree(dev->p);  
  131.     dev->p = NULL;  
  132.     goto done;  
  133. }  

    代码第41行,将 dev->kobj 注册进内核,会在/sys/devices 目录下创建目录。

 

    代码第53-63行,是创建属性文件dev 的过程,也就是这一步,让mdev能够自动为我们创建设备节点。

    代码第65行,创建 /sys/class 到 /sys/device/xx/dev->name的符号链接,这个跟设备总线驱动模型中创建/sys/bus 到 /sys/device/xx/dev->name的符号链接是一样一样的。

    代码第92-103行,将 dev 加入 class的设备链表,并调用 class_interfaces 链表中的每一个 class_intf 结构,调用里面的 add_dev 函数。

    分析到这,class 好像并没有干什么实质性的事情。后面到input、tty、rtc在具体分析吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值