设备类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