设备模型结构

来源:互联网 发布:保密检查清除软件 编辑:程序博客网 时间:2024/06/06 15:41

设备模型结构

 

如表2-1,Linux设备模型包含以下四个基本结构:

类型

所包含的内容

内核数据结构

对应/sys

设备(Devices)

设备是此模型中最基本的类型,以设备本身的连接按层次组织

struct device

/sys/devices/*/*/.../

驱动(Drivers)

在一个系统中安装多个相同设备,只需要一份驱动程序的支持

struct device_driver

/sys/bus/pci/drivers/*/

总线

(Bus)

在整个总线级别对此总线上连接的所有设备进行管理

struct bus_type

/sys/bus/*/

类别(Classes)

这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在/sys/class/input/下

struct class

/sys/class/*/

表2-1:设备模型基本结构

 

device、driver、bus、class是组成设备模型的基本数据结构。kobject是构成这些基本结构的核心,kset又是相同类型结构kobject的集合。kobject和kset共同组成了sysfs的底层数据体系。本节采用从最小数据结构到最终组成一个大的模型的思路来介绍。当然,阅读时也可先从Device、Driver、Bus、Class的介绍开始,先总体了解设备模型的构成,然后再回到kobject和kset,弄清它们是如何将Device、Driver、Bus、Class穿插链接在一起的,以及如何将这些映像成文件并最终形成一个sysfs文件系统。

kobject

kobject是组成device、driver、bus、class的基本结构。如果把前者看成基类,则后者均为它的派生产物。device、driver、bus、class构成了设备模型,而kobject内嵌于其中,将这些设备模型的部件组织起来,并形成了sysfs文件系统。kobject就是device、driver、bus、class在文件系统中的代表。在sysfs操作设备时,也必须通过kobject这个中间人来完成。kobject的主要功能如下:

 

对象的引用计数

通常一个内核对象被创建时,不可能知道该对象存活的时间。跟踪此对象生命周期的一个方法是使用引用计数。当内核中没有代码持有该对象的引用时,该对象将结束自己的有效生命周期,并且可以被删除。

sysfs表述

在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述。

数据结构关联

从整体上看,设备模型是一个友好而复杂的数据结构,通过在其间的大量连接而构成一个多层次的体系结构。Kobject实现了该结构并把它们聚合在一起。

uevent事件处理

       当系统中的硬件被热插拔时,在kobject子系统控制下,将产生事件以通知用户空间。

 

下面以2.6.29版本(本文涉及代码均为此版本)内核源码一一介绍kobject的功能。在kernel/include/linux/kobject.h中,kobject结构定义如下:

struct kobject {

       const char             *name;

       struct list_head       entry;

       struct kobject         *parent;

       struct kset             *kset;

       struct kobj_type     *ktype;

       struct sysfs_dirent  *sd;

       struct kref             kref;

       unsigned int state_initialized:1;

       unsigned int state_in_sysfs:1;

       unsigned int state_add_uevent_sent:1;

       unsigned int state_remove_uevent_sent:1;

};

 

*name

kobject的名字,每个kobject都对应着sysfs下的一个文件夹,该名字也是对应的文件夹的名字。

entry

       双向链表指针,用于将同一kset集合中的kobject链接到一起,便于访问。

*parent

       kobject对应的父kobject节点,在sysfs表现为上一级目录。

*kset

       kobject所在的集合的指针,kset概念将在kset一节中描述。

*ktype

       kobject对象类型指针,随后将会介绍。

*sd

sd用于表示VFS文件系统的目录项,由此可见它是设备与文件之间的桥梁。在sysfs节会对此结构进行分析。

kref

       对象引用计数器。引用计数器的作用前面已经讲过。

state_initialized

       初始化标志位,在对象初始化时被置位。

state_in_sysfs

kobject对象在sysfs中的状态,创建则置1,否则为0。亦即kobject对应的目录在sysfs中是否被创建。

state_add_uevent_sent

添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。

state_remove_uevent_sent

删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备。

kset

kset是嵌入相同类型结构的kobject集合。我们可以认为它是kobject的顶层容器类。kset也是基于sysfs的,维系着设备、驱动等等分类与链接关系。图2-1(来自LDD3,但稍作修改)可清晰表示kset与kobject的关系。

图2-1:kset与kobject关系

下面我们来看一下kset的结构:

struct kset {

       struct list_head list;

       spinlock_t list_lock;

       struct kobject kobj;

       struct kset_uevent_ops *uevent_ops;

};

从结构体中我们可以看到kset与kobject最大的不同就是多了kset_uevent_ops类型的成员。因此,我们可理解kset就是为了让一组kobject使用相同的uevent处理函数。

uevent知识请参考uevent和udev一章。

attribute

前面说过kobject对应sysfs中的文件夹,但作为一个文件系统,不可能没有文件。下面将要讲到的属性即对应这里的文件。我们先来看kobject中的一个重要成员*ktype,类型如下:

struct kobj_type {

       void (*release)(struct kobject *kobj);

       struct sysfs_ops *sysfs_ops;

       struct attribute **default_attrs;

};

 

*release

意如其名,即当kobject引用计数器为0时,用来释放kobject对象。

*sysfs_ops

根据default_attrs中的mode要求,提供方法操作指定属性文件。一般只有读写两个函数,如下:

struct sysfs_ops {

       ssize_t     (*show)(struct kobject *, struct attribute *,char *);

       ssize_t     (*store)(struct kobject *,struct attribute *,const char *, size_t);

};

**default_attrs

保存了属性列表,用于创建该类型的每一个kobject文件。结构如下:

struct attribute {

       const char             *name;

       struct module         *owner;

       mode_t                  mode;

};

*name

       属性名,对应于kobject的sysfs目录中的一个文件。

*owner

指向模块的指针,该模块负责实现这些属性。源码注释已明确指出该字段已不在使用,目前存在的原因就是为了保持向上兼容。

       mode

              指明该属性文件是只读只写还是可读可写,谁可写等等。

             

除了在初始化时指定属性外,我们还可以根据需要使用函数对属性进行增删,如下:

int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);

void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

请注意,属性可以任意增删,但方法sysfs_ops确是唯一的不可改变,所以必须确保该方法可以处理新的属性。

设备

具有特定功能的硬件,比如键盘、鼠标等。

数据结构

struct device {

       struct klist             klist_children;

       struct klist_node     knode_parent; /* node in sibling list */

       struct klist_node     knode_driver;

       struct klist_node     knode_bus;

       struct device          *parent;

 

       struct kobject               kobj;

       char                      bus_id[BUS_ID_SIZE];  /* position on parent bus */

       unsigned                uevent_suppress:1;

       const char             *init_name;    /* initial name of the device */

 

       struct bus_type      *bus;             /* type of bus device is on */

       struct device_driver *driver;         /* which driver has allocated this device */

       void                      *driver_data;   /* data private to the driver */

 

       dev_t                    devt;                   /* dev_t, creates the sysfs "dev" */

 

       struct klist_node     knode_class;

       struct class            *class;

       struct attribute_group **groups;   /* optional groups */

 

       void (*release)(struct device *dev);

 

/* 省略了部分成员 */

};

klist_children

子设备双向链表头,用于指向该设备所有子设备链表的表头。通过此表头可以查找所有子设备。操作函数如下:

int device_for_each_child(struct device *parent, void *data,

int (*fn)(struct device *dev, void *data));

      struct device *device_find_child(struct device *parent, void *data,

                                                int (*match)(struct device *dev, void *data));

这两个函数的区别是:前者遍历子设备,并作fn()处理,如果fn()返回非0值,则停止遍历,返回错误码;后者是遍历子设备,并作match()处理,如果match()返回非0,则调用get_device()获取对应的子设备(将对应的子设备参考值曾1),然后停止遍历,返回子设备。注意后者获取的子设备用完后,需要用put_device()去除子设备。

knode_parent

       父设备的子设备集节点,用于链入父设备的klist_children链表中。

knode_driver

设备所属驱动的设备集节点,用于链入具有相同驱动的设备链表中。该链的表头在device_driver->p ->klist_devices。

knode_bus

设备所属总线的设备集节点,用于链入具有相同总线的设备链表中。该链的表头为bus_type->p -> klist_devices。

*parent

设备的父设备,即该设备所属的设备。在大多数情况下,一个父设备通常是某种总线或者是宿主控制器。

kobj

       表示该设备并把该它连接到sysfs体系中的kobject。

bus_id[BUS_ID_SIZE]

       在总线上唯一标识该设备的字符串。

uevent_suppress

       过滤该设备的uevent事件标志位。为1,则过滤。

*init_name

设备初始化名称,device_add()里将该值拷贝给bus_id,因此也表示总线上该设备标识。注意,文件系统中显示的设备名称并非此处指定,而是由该设备对应的kobj中指定。

*bus

       标识了该设备连接在何种类型的总线上。

*driver

       管理该设备的驱动程序。在下一节中将介绍device_driver结构。

*driver_data

       由设备驱动程序使用的私有数据成员。

devt

       32位主从设备号,高12位为主设备号,低20位为从设备号。

knode_class

设备所属类的设备集节点,用于链入具有相同类的设备链表中。该链的表头为class->p -> class_devices。

*class

       标识设备所属类。

**groups

       设备属性组,会体现到sysfs系统中,用户可操作。

*release

当指向设备的最后一个引用被删除时,内核调用该方法;它将从内嵌的kobject的release方法中调用。所有指向核心注册的device结构都必须有一release方法,否则内核将打印出错误信息。

 

图2-2给出了设备与总线、驱动等连接关系。文件系统子框表明设备是通过kobj将自己与sysfs中的文件夹连接到一起的。子设备、总线、驱动、类等子系统给设备进行相应分类,并使用klist和klist_node将它们链到一起。每个子系统同时提供***_for_each_*** 和 ***_find_***两个遍历函数来查找并操作指定设备。

 

 

图2-2:设备结构图

方法

设备架构初始化:

int __init devices_init(void);

1 在sysfs目录下创建设备子集(kset)和目录/sys/devices;并将设备通用事件处理函数device_uevent_ops注册给该设备子集。之后所有设备都会添加到devices目录下。

2另外,kset在初始化时,会将公共的kobj_type一组函数注册到kset中内嵌的kobj中,其中包括释放kset函数kset_release和一组属性show/store函数kobj_attr_show/kobj_attr_store。

3  kset注册成功后,向用户空间发送添加对象的uevent事件。

4 创建目录/sys/dev,并在该目录下创建两个目录/sys/dev/block和/sys/dev/char。这两个目录下维护一个按字符设备/块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件。

 

设备注册/去注册函数:

int device_register(struct device *dev);

void device_unregister(struct device *dev);

注册函数先后执行的操作有:获取设备所在子集,初始化设备数据,将设备加入到sysfs系统中,增加设备默认属性及操作方法,加设备到类设备中,加设备到总线,向用户空间发送添加设备uevent事件,尝试为设备获取驱动。

采用此方法注册的设备都会获取释放对象(device_release)及处理所属属性(dev_attr_show/dev_attr_store)的方法(也就是kobj_type结构)。

如果用户空间程序用sysfs来读取设备属性的值,sysfs的read函数先调用的是对象属性处理函数dev_attr_show,因为sysfs能处理或者说能看得见的只有对象级的东西,我们从图3的图中也可表明这一点。

去注册是注册的逆向操作。去注册后,用户不可再操作设备。

 

遍历子设备:

int device_for_each_child(struct device *parent, void *data,

int (*fn)(struct device *dev, void *data));

struct device *device_find_child(struct device *parent, void *data,

                                         int (*match)(struct device *dev, void *data));

函数说明请参考前一节数据结构分析中klist_children项。

 

设备通用事件(uevent)处理函数:

static int dev_uevent_filter(struct kset *kset, struct kobject *kobj);

static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj);

static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);

dev_uevent_filter设备级事件过滤函数,发送uevent事件前会先调用此函数检查是否发送该事件,返回0则不发送,为1则调用总线或类级过滤函数再次检查。

dev_uevent_name获取设备所属总线或类的名称。

dev_uevent设备级事件处理函数,向事件中添加指定设备的环境变量,如:设备主次编号,设备类型,设备使用的驱动。如果设备有所属总线或类,本函数还会调用总线或类级事件处理函数。

详细事件处理流程将在uevent章节中描述。

 

设备属性文件添加删除:

int device_create_file(struct device *dev, struct device_attribute *attr);

void device_remove_file(struct device *dev, struct device_attribute *attr);

创建/删除设备属性文件。

 

设备的对象(kobj)属性文件操作函数:

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf);

static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,

                                                                         const char *buf, size_t count);

从sysfs层面看,只能识别对象级的属性文件处理函数(如上面这两个函数的格式)。设备使用这两个函数实现对象到设备属性文件处理的转换。它们使用container_of方法实现内核对象(kobj)到对应设备的转换。通过设备找到属性处理函数,并调用找到的函数(格式如下)来读取/存储指定设备的属性值。

struct device_attribute {

       struct attribute       attr;

       ssize_t (*show)(struct device *dev, struct device_attribute *attr,

                     char *buf);

       ssize_t (*store)(struct device *dev, struct device_attribute *attr,

                      const char *buf, size_t count);

};

驱动

让设备运行起来的一组函数。

数据结构

struct device_driver {

       const char             *name;

       struct bus_type      *bus;

 

       int (*probe) (struct device *dev);

       int (*remove) (struct device *dev);

       void (*shutdown) (struct device *dev);

       struct attribute_group **groups;

 

       struct driver_private *p;

      

/* 省略了部分成员 */

};

 

*name

       驱动程序的名称。与kobj->name相同。

*bus

       该驱动使用的总线。

*probe

       用来查询特定设备是否存在,以及该驱动程序能否操作它。

*remove

       删除设备时使用remove函数告知驱动不可再操作此设备。

*shutdown

       关机时调用shutdown函数关闭设备。

**groups

       驱动属性组,会体现到sysfs系统中,用户可操作。

*p

       该成员用于处理与设备、总线以及sysfs之间的连接关系,如下:

struct driver_private {

       struct kobject                      kobj;

       struct klist                   klist_devices;

       struct klist_node           knode_bus;

       struct module_kobject   *mkobj;

       struct device_driver      *driver;

};

方法

操作device_driver结构的函数有注册/去注册函数,增删属性及属性组文件函数,遍历设备函数。

 

注册函数:

int driver_register(struct device_driver *drv);

该函数实现向相应总线的驱动子集(kset)中注册驱动对象,并将总线上可以使用该驱动的设备加入到该驱动的设备链表中。

注意,每个驱动对象均赋予driver_ktype类型,用来释放对象和处理属性文件。

可以用下面函数去注册一个驱动:

void driver_unregister(struct device_driver *drv);

 

驱动对象释放函数:

static void driver_release(struct kobject *kobj);

该函数同过driver_ktype注册给驱动对象,用来释放对象。

 

驱动对象属性文件处理函数:

static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr, char *buf);

static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,

                                                                         const char *buf, size_t count);

这两个函数也是通过driver_ktype注册给驱动对象的,用于操作对象的属性。如同驱动一节对应函数描述的那样。这两个函数也只是个适配函数,每个驱动有自己的属相处理函数。sysfs只是通过这两个适配函数去访问驱动属性处理函数。实现方法仍然是通过container_of行为由对象获取外层包含该对象的驱动结构,从而获取驱动属性处理函数。注意,这两个函数定义在bus.c中。

 

增删属性文件函数:

int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);

void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);

用户可以通过操作增加到sysfs中驱动属性文件来修改驱动属性值。

 

增删属性组文件函数:

static int driver_add_groups(struct device_driver *drv, struct attribute_group **groups);

static void driver_remove_groups(struct device_driver *drv, struct attribute_group **groups);

这两个函数可以一次向系统增删一组驱动属性。

 

遍历设备函数:

int driver_for_each_device(struct device_driver *drv, struct device *start,

                                        void *data, int (*fn)(struct device *, void *));

struct device *driver_find_device(struct device_driver *drv, struct device *start, void *data,

                                         int (*match)(struct device *dev, void *data));

这两函数均用于遍历驱动上的设备,并对设备执行fn或match操作。具体差异请读代码。

总线

总线是处理器与一个或者多个设备之间的通道。在设备模型中,所有的设备都通过总线相连。

数据结构

设备模型用bus_type结构表示总线,如下:

struct bus_type {

       const char                    *name;

       struct bus_attribute        *bus_attrs;

       struct device_attribute    *dev_attrs;

       struct driver_attribute    *drv_attrs;

 

       int (*match)(struct device *dev, struct device_driver *drv);

       int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

       int (*probe)(struct device *dev);

 

       struct bus_type_private *p;

 

/* 省略了部分成员 */   

};

*name

       总线名称。

*bus_attrs

       总线属性集。

*dev_attrs

       总线设备属性集。

*drv_attrs

       总线驱动属性集。

*match

当一个总线上的新设备或者新驱动程序被添加时,会调用此函数。如果指定的驱动程序可以处理指定的设备,该函数返回1。

*uevent

总线级别的uevent处理。向用户空间发送uevent时间前,使用该方法给总线增加环境变量。

*probe

       探测指定设备是否在总线上。

*p

struct bus_type_private {

       struct kset subsys;

       struct kset *drivers_kset;

       struct kset *devices_kset;

       struct klist klist_devices;

       struct klist klist_drivers;

};

subsys 表示总线集;*drivers_kset和*devices_kset是从sysfs角度描述总线支持的驱动集和设备集,而klist_devices和klist_drivers是总线支持的设备及驱动链表,用于遍历总线上的驱动和设备。

方法

总线初始化函数:

int __init buses_init(void);

系统初始化时调用。在sysfs根目录下增加bus目录,并创建bus子集(kset),注册子集uevent事件处理函数。完成后向用户空间发送增加对象uevent事件。

另外,kset在初始化时,会将公共的kobj_type一组函数注册到kset中内嵌的kobj中,其中包括释放kset函数kset_release和一组属性show/store函数kobj_attr_show/kobj_attr_store。注意,所有系统子集都会这么做,且使用一组同样的函数,包括设备子集和类子集。

 

总线注册函数:

int bus_register(struct bus_type *bus);

void bus_unregister(struct bus_type *bus);

向系统注册/去注册总线时,使用此两函数。这里要注意的是注册的每一个总线在sysfs中不仅仅是一个目录(kobject),它还是一个子集。即总线是以子集(kset)方式注册的,而非对象(kobject)。更重要的是每个总线子集下同时注册了设备(devices)和驱动(drivers)两个子集(kset)。前面讲到设备初始化时也会注册devices子集。事实上,这儿的设备子集只是保存设备的链接。而驱动没有初始化函数,因此驱动对象都保存在这儿各总线的驱动子集里。

 

uevent属性文件操作函数:

static ssize_t bus_uevent_store(struct bus_type *bus, const char *buf, size_t count);

用户可以通过修改总线文件夹下的uevent文件内容,触发内核向用户空间发送uevent事件,事件类型由用户写入的内容决定。

 

总线属性文件操作函数:

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr, char *buf);

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,

                                                                         const char *buf, size_t count);

与设备和驱动对应的对象属性操作函数功能近似,这里不再赘述。记住,这只是个适配函数。为sysfs服务。

 

增删属性函数:

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);

void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

增加删除总线属性文件,一次一个。

static int bus_add_attrs(struct bus_type *bus);

static void bus_remove_attrs(struct bus_type *bus);

增加删除总线属性文件,一次多个,其实就是将总线提供的属性列表一次性增加到系统中。我们从数据结构中可知bus_type不仅为自己提供了属性列表。也为设备和驱动各提供了一份。因此,总线也提供了相应的添加/删除方法,类似于上面两个函数,不再列出。

 

总线遍历设备函数:

int bus_for_each_dev(struct bus_type *bus, struct device *start,

                           void *data, int (*fn)(struct device *, void *));

struct device *bus_find_device(struct bus_type *bus, struct device *start, void *data,

                            int (*match)(struct device *dev, void *data));

前者对指定总线上所有设备进行fn()操作。若fn()返回非零值,则视为出错,退出遍历。而后者只是用match()对总线上每个设备进行匹配,找到则返回对应的设备。

 

总线遍历驱动函数:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,

                           void *data, int (*fn)(struct device_driver *, void *));

对指定总线上的所有驱动进行fn()操作。若fn()返回非零值,则视为出错,退出遍历。

 

向总线增加驱动:

int bus_add_driver(struct device_driver *drv);

该函数为驱动注册函数的一部分,将驱动对象注册到对应总线的驱动(drivers)子集下。

 

向总线增加设备:

int bus_add_device(struct device *dev);

该函数为设备注册函数的一部分,但并不像驱动那样,它只将设备子集的指定设备链接到总线的设备(devices)子集下。

 

设备绑定驱动函数:

void bus_attach_device(struct device *dev);

为设备获取合适的驱动。

int device_reprobe(struct device *dev);

将设备与其驱动解绑,然后为设备获取新的驱动。这通常为热插拔服务。

int bus_rescan_devices(struct bus_type *bus);

为指定的bus上的每个设备重新绑定驱动,如果设备还没有驱动,则为其重新探测驱动并绑定。

类是一个设备的高层视图,它抽象出了底层的实现细节。比如驱动程序看到的是SCSI磁盘和ATA磁盘,但是在类的层次上,它们都是磁盘而已。类允许用户空间使用设备所能提供的功能,而不关心设备是如何连接的,以及它们是如何工作的。

数据结构

struct class {

       const char                           *name;

       struct module                       *owner;

 

       struct class_attribute             *class_attrs;

       struct device_attribute           *dev_attrs;

       struct kobject                       *dev_kobj;

 

       int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);

 

       void (*class_release)(struct class *class);

       void (*dev_release)(struct device *dev);

 

       struct class_private *p;

};

 

*name

       类名称。
*owner

       类所属模块。

*class_attrs

       类属性集。

*dev_attrs

       类给其所有设备定义的公共属性集。

*dev_kobj

在devices_init函数中创建了/sys/dev/char和/sys/dev/block两个对象,dev_kobj指向二者之一,添加对象时(device_add),会将对象链接到这个目录下。按照我的理解,内核设计者是想在/sys/dev/char或/sys/dev/block目录下将各设备按类分目录存放,此处的dev_kobj即是为了创建这个分目录。遗憾的是该功能并未使用,我们看到char和block下没有分目录,全是以major:minor格式命名的链接文件。这样我们就可以通过主次设备号快速找到对应的设备了。

*dev_uevent

       类级uevent事件构造函数。

*class_release

       用于释放指定的类。

*dev_release

       用于释放指定的设备。

*p

       struct class_private {

       struct kset class_subsys;

       struct klist class_devices;

       struct list_head class_interfaces;

       struct class *class;

};

class_subsys是该类在sysfs中的代表,它表明每个类都是一个子集。class_devices将所有归属于该类的设备串成一个链表,便于遍历查询或对设备进行操作。class_interfaces用于链接所有类接口。class指向所属的class对象。

      

方法

类初始化函数:

int __init classes_init(void);

系统初始化时调用。在sysfs根目录下增加class(/sys/class)目录,并创建class子集(kset),注册子集uevent事件处理函数。完成后向用户空间发送增加对象uevent事件。

另外,kset在初始化时,会将公共的kobj_type一组函数注册到kset中内嵌的kobj中,其中包括释放kset函数kset_release和一组属性show/store函数kobj_attr_show/kobj_attr_store。注意,所有系统子集都会这么做,且使用一组同样的函数,包括设备子集和总线子集。

 

类注册/去注册函数:

int __class_register(struct class *cls, struct lock_class_key *key);

void class_unregister(struct class *cls);

向系统增加/去除类子集和相应路径。

 

类创建/删除函数:

struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key);

void class_destroy(struct class *cls);

与类注册/去注册函数功能一致,只是对以上两个函数的简单封装。

 

遍历类设备函数:

int class_for_each_device(struct class *class, struct device *start,

                       void *data, int (*fn)(struct device *, void *));

struct device *class_find_device(struct class *class, struct device *start, void *data,

                             int (*match)(struct device *, void *));

与总线或设备中的遍历功能类似,不再赘述。

 

0 0
原创粉丝点击