设备类class理解

http://blog.chinaunix.net/uid-20729583-id-1884552.html

设备类struct class
一个类是一个设备的高级视图,它抽象出低级的实现细节。例如,驱动可以见到一个SCSI磁盘或者一个ATA磁盘,在类的级别,他们都是磁盘,类允许用户空间基于它们作什么来使用设备,而不是它们如何被连接或者它们如何工作。

设备类表示一类设备,所有的class对象都属于class_subsys子系统
struct class
{
    const char *name;//类名称
    struct module *owner;//对应模块
    struct subsystem subsys;//对应的subsystem;
    struct list_head children;//class_device链表
    struct list_head  interfaces;//class_interface链表
    struct semaphore  sem;//children和interfaces链表锁
    struct class_attribute *class_attrs;//类属性
    int (*uevent)(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);//事件
    void (*release)(struct class_device *dev);//释放类设备的方法
    void (*class_release)(struct class *class);    //释放类的方法
}
一、struct list_head结构体
  struct list_head {
          struct list_head *next, *prev;  //此处进行了结构体list_head的定义系统使用这个链表的形式进行系统文件资源的管理
  };
二、semaphore 结构体
 struct semaphore {
          atomic_t count;       // 右边是atomic 的定义 typedef struct { volatile int counter; } atomic_t;   (这里的volatile int 指的是counter变量可以被其他的程序更改,但是不能被你自己更改)此处使用了atomic_t类型
//是为了是引用计数不被自己更改,增强安全性,由于这个计数变量只能由其他程序更改,而其他程序只能是系统中的管理类设备的程序,所以增强了安全性
          wait_queue_head_t wait;    //即等待队列头,(在linux驱动中,可以通过等待队列来实现阻塞进程的呼唤)右边是wait_queue_head_t的定义   typedef struct __wait_queue_head wait_queue_head_t;
   //下面是__wait_queue_head 的定义
  /*
      struct __wait_queue_head {
          spinlock_t  lock;
下面是spinlock_t的定义
 /*
#include 
  19
  20typedef struct {
  21        raw_spinlock_t raw_lock;
  22#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
  23        unsigned int break_lock;
  24#endif
  25#ifdef CONFIG_DEBUG_SPINLOCK
  26        unsigned int magic, owner_cpu;
  27        void *owner;
  28#endif
  29#ifdef CONFIG_DEBUG_LOCK_ALLOC
  30        struct lockdep_map dep_map;
  31#endif
  32} spinlock_t;
*/
          struct list_head task_list;
  };
  */
  };

struct semaphore {
          atomic_t count;
          int sleepers;
          wait_queue_head_t wait;
  };
三、类属性class_attribute
下面是结构体class_attribute的定义
struct class_attribute {
 210        struct attribute        attr;
/*
28struct attribute {
  29        const char              * name;    //类属性的名称
  30        struct module           * owner;   //类属性所述模块
  31        mode_t                  mode;     //保护位,如果是只读,就要设置成 S_IRUGO,读写就是 S_IWUSR。
  32};
*/
 211        ssize_t (*show)(struct class *, char * buf);  //具体实现在用户空间/sys下面的读操作
  /*

*/
 212        ssize_t (*store)(struct class *, const char * buf, size_t count);     //存储用户通过buffer传入的属性值
 213};
对于这个属性结构体,可以使用函数来进行新属性的添加
int device_create_file(struct device *,struct device_attribute *);
void device_remove_file(struct device *,struct device_attribute *);
四、用户事件 uevent(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);(即user event)
下面介绍一个与此十分相似的相关函数
static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,int num_envp, char *buffer, int buffer_size);
           
五、void (*release)(struct class_device *dev)
六、class_release
static void class_release(struct kobject * kobj)
  51{
  52        struct class *class = to_class(kobj);//此处的to_class()为一个宏定义,这个宏定义用来指向设备类,定义如右:#define to_class(obj) container_of(obj, struct class, subsys.kobj)
  53
  54        pr_debug("class '%s': release.\n", class->name);
  55
  56        if (class->class_release)
  57                class->class_release(class);
  58        else
  59                pr_debug("class '%s' does not have a release() function, "   "be careful\n", class->name); //  这是一个宏定义:#define pr_debug(fmt,arg...)      printk(KERN_DEBUG fmt,##arg)
/*上面的这个宏是一个可变参数宏,详细的解释如下:
在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:

 include/linux/kernel.h
110: #define pr_debug(fmt,arg...) \
111:         printk(KERN_DEBUG fmt,##arg)

这里 arg 表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构
成 arg 的值,在宏扩展时替换 arg,例如:

    pr_debug("%s:%d",filename,line)

扩展为

    printk("<7>" "%s:%d", filename, line)

使用 ## 的原因是处理 arg 不匹配任何参数的情况,这时 arg 的值为空,GNU
C 预处理器在这种特殊情况下,丢弃 ## 之前的逗号,这样

    pr_debug("success!\n")

扩展为

    printk("<7>" "success!\n")

注意最后没有逗号。


*/
  60}

下面是注册和注销class的两个函数:
int class_register(struct class *cls);
void class_unregister(struct class *cls);

int class_register(struct class * cls)
 138{
 139        int error;
 140
 141        pr_debug("device class '%s': registering\n", cls->name);
 142
 143        INIT_LIST_HEAD(&cls->children);//这个函数用来初始化class_device链表
/*内联函数INIT_LIST_HEAD的定义
static inline void INIT_LIST_HEAD(struct list_head *list)
  31{
  32        list->next = list;
  33        list->prev = list;
  34}
*/
 144        INIT_LIST_HEAD(&cls->devices);    //功能同上
 145        INIT_LIST_HEAD(&cls->interfaces);      //功能同上
 146        kset_init(&cls->class_dirs);

 147        init_MUTEX(&cls->sem);           //初始化信号量
 148        error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);   //设置相应的kobject对象的名称
 149        if (error)
 150                return error;
 151
 152        subsys_set_kset(cls, class_subsys);     //设置相应的subsystem中的kset对象
 153
 154        error = subsystem_register(&cls->subsys);  //注册subsystem
 155        if (!error) {
 156                error = add_class_attrs(class_get(cls));  //添加类属性
 157                class_put(cls);  //减少类的计数
 158        }
 159        return error;
 160}
 161
 162void class_unregister(struct class * cls)
 163{
 164        pr_debug("device class '%s': unregistering\n", cls->name);
 165        remove_class_attrs(cls);  //进行类属性移除操作
 166        subsystem_unregister(&cls->subsys);   //从所挂载的subsystem中移除
 167}
 168

二、class_device结构体
一个class可以看成是一个容器,容器总包含了很多的class_device,这些class_device是由class这个大的容器来管理的,而每个class_device都对应着一个具体的设备。
一个类的真正目的是作为一个是该类成员的设备的容器. 一个成员由 struct class_device 来表示:

每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备并通过struct class_device中的dev成员(一个指向struct device的指针)关联一个物理设备。一个逻辑设备总是对应一个物理设备,而一个物理设备却可以对应多个逻辑设备。

struct class_device
{
    struct list_head node;

    struct kobject  kobj;//内嵌的kobject;

    struct class   *class;//所属的类;

    dev_t   devt;//dev_t设备号

    struct class_device_attribute  *devt_attr;

    struct class_device_attribute  uevent_attr;

    struct device  *dev;//如果存在,创建到/sys/devices相应入口的符号链接

    void *class_data;//私有数据

    struct class_device  *parent;//父设备

    void (*release)(struct class_device *dev);//释放对应类实际设备的方法

    int(*uevent)(struct class_device *dev,char **envp,int num_envp,char *buffer,int buffer_size);

    char class_id[BUS_IO_SIZE];  //  u32 class_id;类标志
}
class_id 成员持有设备名子, 如同它在 sysfs 中的一样. class 指针应当指向持有这个设备的类, 并且 dev 应当指向关联的设备结构. 设置 dev 是可选的; 如果它是非 NULL, 它用来创建一个符号连接从类入口到对应的在 /sys/devices 下的入口, 使得易于在用户空间找到设备入口. 类可以使用 class_data 来持有一个私有指针.
一、class_device_attribute  
这个结构体为类设备属性,定义如下:
struct class_device_attribute
{
    struct attribute attr;
    ssize_t (*show)(struct class_device *,char *buf);//对相应的类设备进行读。
    ssize_t (*store)(struct class_device *,const char *buf, size)t count);//对相应的类设备进行写操作
}




注册和注销class_device
int class_device_register(struct class_device *class_dev);    //注册
void class_device_unregister(struct class_device *class_dev);   //注销



 675int class_device_register(struct class_device *class_dev)
 676{
 677        class_device_initialize(class_dev);   //初始化类的对应的类设备
 678        return class_device_add(class_dev);    //返回class_device_add(class_dev);
 679}
下面是class_device_register中两个函数的具体实现代码:

 567int class_device_add(struct class_device *class_dev)   //该函数用于进行设备的添加
 568{
 569        struct class *parent_class = NULL;           //初始化父指针为空,
 570        struct class_device *parent_class_dev = NULL;    //初始化设备类父指针为空,实现功能和上面的函数想类似
 571        struct class_interface *class_intf;
 572        int error = -EINVAL;//初始化error为-EINVAL(对应数值22)
 573
 574        class_dev = class_device_get(class_dev);   //此函数是class_device对象的计数加1,返回class_device对象的指针
 575        if (!class_dev)     
 576                return -EINVAL;    
 577
 578        if (!strlen(class_dev->class_id))    
 579                goto out1;
 580
 581        parent_class = class_get(class_dev->class);     
 582        if (!parent_class)
 583                goto out1;
 584
 585        parent_class_dev = class_device_get(class_dev->parent);
 586
 587        pr_debug("CLASS: registering class device: ID = '%s'\n",
 588                 class_dev->class_id);
 589
 590        /* first, register with generic layer. */
 591        error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
 592        if (error)
 593                goto out2;
 594
 595        if (parent_class_dev)
 596                class_dev->kobj.parent = &parent_class_dev->kobj;
 597        else
 598                class_dev->kobj.parent = &parent_class->subsys.kobj;
 599
 600        error = kobject_add(&class_dev->kobj);
 601        if (error)
 602                goto out2;
 603
 604        /* add the needed attributes to this device */
 605        error = sysfs_create_link(&class_dev->kobj,
 606                                  &parent_class->subsys.kobj, "subsystem");
 607        if (error)
 608                goto out3;
 609
 610        error = class_device_create_file(class_dev, &class_uevent_attr);
 611        if (error)
 612                goto out3;
 613
 614        if (MAJOR(class_dev->devt)) {
 615                error = class_device_create_file(class_dev, &class_devt_attr);
 616                if (error)
 617                        goto out4;
 618        }
 619
 620        error = class_device_add_attrs(class_dev);
 621        if (error)
 622                goto out5;
 623
 624        if (class_dev->dev) {
 625                error = sysfs_create_link(&class_dev->kobj,
 626                                          &class_dev->dev->kobj, "device");
 627                if (error)
 628                        goto out6;
 629        }
 630
 631        error = class_device_add_groups(class_dev);
 632        if (error)
 633                goto out7;
 634
 635        error = make_deprecated_class_device_links(class_dev);
 636        if (error)
 637                goto out8;
 638
 639        kobject_uevent(&class_dev->kobj, KOBJ_ADD);
 640
 641        /* notify any interfaces this device is now here */

 642        down(&parent_class->sem);
 643        list_add_tail(&class_dev->node, &parent_class->children);
 644        list_for_each_entry(class_intf, &parent_class->interfaces, node) {
 645                if (class_intf->add)
 646                        class_intf->add(class_dev, class_intf);
 647        }
 648        up(&parent_class->sem);
 649
 650        goto out1;
 651
 652 out8:
 653        class_device_remove_groups(class_dev);
 654 out7:
 655        if (class_dev->dev)
 656                sysfs_remove_link(&class_dev->kobj, "device");
 657 out6:
 658        class_device_remove_attrs(class_dev);
 659 out5:
 660        if (MAJOR(class_dev->devt))
 661                class_device_remove_file(class_dev, &class_devt_attr);
 662 out4:
 663        class_device_remove_file(class_dev, &class_uevent_attr);
 664 out3:
 665        kobject_del(&class_dev->kobj);
 666 out2:
 667        if(parent_class_dev)
 668                class_device_put(parent_class_dev);
 669        class_put(parent_class);
 670 out1:
 671        class_device_put(class_dev);
 672        return error;
 673}
上面的这个函数比较容易理解,这里就不再一一的给出每个代码的解释了。

 674

下面这个函数为初始化class_device.
void class_device_initialize(struct class_device *class_dev)
 561{
 562        kobj_set_kset_s(class_dev, class_obj_subsys);
 563        kobject_init(&class_dev->kobj);//对于这个函数,一般情况下,在进行设备对象的初始化时都会设计到这个函数的调用,每一个设备对象都对应kobject这样的一个对象,这个对象的作用很重要,主要是用于对设备模型的结构化的描述。
 564        INIT_LIST_HEAD(&class_dev->node);
 565}


三、类接口 class_interface
struct class_interface
{
    struct list_head node;   //
    struct class *class;   //相应的class
    int (*add)(struct class_device *,struct class_interface *);  //设备加入时触发
    void (*remove)(struct class_device *,struct class_interface *);//设备移出时触发
}
与class和class_device相似,class_interface也存在两个函数用于注册和注销class_interface
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
如果想对上面的两个函数进行彻底的了解,请参阅http://lxr.linux.no/#linux/init/do_mounts_rd.c#L17

posted on 2019-10-23 10:52  yangjunhe  阅读(1228)  评论(0编辑  收藏  举报