kobject kset ktype
基本概念
由“Linux设备模型(1)_基本概念”可知,Linux设备模型的核心是使用Bus、Class、Device、Driver四个核心数据结构,将大量的、不同功能的硬件设备(以及驱动该硬件设备的方法),以树状结构的形式,进行归纳、抽象,从而方便Kernel的统一管理。
目前为止,Kobject主要提供如下功能:
1.通过parent指针,可以将所有Kobject以层次结构的形式组合起来。
2.使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的唯一功能)。
3.和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间。
代码分析
主要的数据结构
在描述数据结构之前,有必要说明一下Kobject, Kset和Ktype这三个概念。
Kobject是基本数据类型,每个Kobject都会在"/sys/“文件系统中以目录的形式出现。
Ktype代表Kobject(严格地讲,是包含了Kobject的数据结构)的属性操作集合(由于通用性,多个Kobject可能共用同一个属性操作集,因此把Ktype独立出来了)。
- 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;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
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;
unsigned int uevent_suppress:1;
};
name,该Kobject的名称,同时也是sysfs中的目录名称。由于Kobject添加到Kernel时,需要根据名字注册到sysfs中,之后就不能再直接修改该字段。如果需要修改Kobject的名字,需要调用kobject_rename接口,该接口会主动处理sysfs的相关事宜。
entry,用于将Kobject加入到Kset中的list_head。
parent,指向parent kobject,以此形成层次结构(在sysfs就表现为目录结构)。
kset,该kobject属于的Kset。可以为NULL。如果存在,且没有指定parent,则会把Kset作为parent(别忘了Kset是一个特殊的Kobject)。
ktype,该Kobject属于的kobj_type。每个Kobject必须有一个ktype,或者Kernel会提示错误。
sd,该Kobject在sysfs中的表示。
kref,"struct kref”类型(在include/linux/kref.h中定义)的变量,为一个可用于原子操作的引用计数。
state_initialized,指示该Kobject是否已经初始化,以在Kobject的Init,Put,Add等操作时进行异常校验。
state_in_sysfs,指示该Kobject是否已在sysfs中呈现,以便在自动注销时从sysfs中移除。
state_add_uevent_sent/state_remove_uevent_sent,记录是否已经向用户空间发送ADD uevent,如果有,且没有发送remove uevent,则在自动注销时,补发REMOVE uevent,以便让用户空间正确处理。
uevent_suppress,如果该字段为1,则表示忽略所有上报的uevent事件。
- kset原型
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
list/list_lock,用于保存该kset下所有的kobject的链表。
kobj,该kset自己的kobject(kset是一个特殊的kobject,也会在sysfs中以目录的形式体现)。
uevent_ops,该kset的uevent操作函数集。当任何Kobject需要上报uevent时,都要调用它所从属的kset的uevent_ops,添加环境变量,或者过滤event(kset可以决定哪些event可以上报)。因此,如果一个kobject不属于任何kset时,是不允许发送uevent的。
- ktype原型
/* include/linux/kobject.h, line 108 */
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject*kobj);
};
release,通过该回调函数,可以将包含该种类型kobject的数据结构的内存空间释放掉。
sysfs_ops,该种类型的Kobject的sysfs文件系统接口。
default_attrs,该种类型的Kobject的atrribute列表(所谓attribute,就是sysfs文件系统中的一个文件)。将会在Kobject添加到内核时,一并注册到sysfs中。
child_ns_type/namespace,和文件系统(sysfs)的命名空间有关,这里不再详细说明。
kobject ktype kset 三者关系图解
kobject ktype kset与统一设备模型的关系
从面向对象的角度来看:
- struct kobj(及其相关结构如kset, ktype等)属于最抽象的基类,代码最简洁,最不具体;
- struct device(及其相关结构如device_driver,device_type等)是对kobj的封装,是第一层派生类;
- 再上层的结构(如platform_device等),是在struct device的基础上再封装一次,是第二层派生类。
因此,例如我们创建了一个struct platform_device的实例,使用完毕后要释放它。那么这个过程按道理应该是:
- 系统内部先调用platform_device的remove函数,它只处理自己层特有的变量;
- 完毕后,系统调用第一层派生类struct device的release函数,处理了自己这一层的特有变量;
- 最后,调用基类kobject的release函数,将整个空间释放掉。
整个过程应该会跟C++析构过程比较类似,上述的“系统内部”也应该类似于C++编译器自动生成的代码,因为C++中析构函数的逆向调用是自动进行的,并没有在派生类的析构函数中显示调用。类似地,在此处上层的release中也不会显式调用下层的release,都是由系统内部完成的。
简而言之:Linux统一设备模型就是在kobject kset ktype的基础之上逐层封装起来的。
因此,在Linux中,Kobject几乎不会单独存在。它的主要功能,就是内嵌在一个大型的数据结构中,为这个数据结构提供一些底层的功能实现。 Linux driver开发者,很少会直接使用Kobject以及它提供的接口,而是使用构建在Kobject之上的设备模型接口。
device device_driver和bus
前言
device和device driver是Linux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以kernel就为设备和驱动它的driver定义了两个数据结构,分别是device和device_driver。
在Linux设备模型中,Bus(总线)是一类特殊的设备,它是连接处理器和其它设备之间的通道(channel)。为了方便设备模型的实现,内核规定,系统中的每个设备都要连接在一个Bus上,这个Bus可以是一个内部Bus、虚拟Bus或者Platform Bus。
内核通过struct bus_type结构,抽象Bus,它是在include/linux/device.h中定义的。本文会围绕该结构,描述Linux内核中Bus的功能
代码分析
- device
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct acpi_dev_node acpi_node; /* associated ACPI device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
bool offline_disabled:1;
bool offline:1;
};
device结构很复杂(不过linux内核的开发人员素质是很高的,该接口的注释写的非常详细,感兴趣的同学可以参考内核源代码),这里将会选一些对理解设备模型非常关键的字段进行说明。
parent,该设备的父设备,一般是该设备所从属的bus、controller等设备。
p,一个用于struct device的私有数据结构指针,该指针中会保存子设备链表、用于添加到bus/driver/prent等设备中的链表头等等,具体可查看源代码。
kobj,该数据结构对应的struct kobject。
init_name,该设备的名称。
注1:在设备模型中,名称是一个非常重要的变量,任何注册到内核中的设备,都必须有一个合法的名称,可以在初始化时给出,也可以由内核根据“bus name + device ID”的方式创造。
type,struct device_type结构是新版本内核新引入的一个结构,它和struct device关系,非常类似stuct kobj_type和struct kobject之间的关系,后续会再详细说明。
bus,该device属于哪个总线(后续会详细描述)。
driver,该device对应的device driver。
platform_data,一个指针,用于保存具体的平台相关的数据。具体的driver模块,可以将一些私有的数据,暂存在这里,需要使用的时候,再拿出来,因此设备模型并不关心该指针得实际含义。
power、pm_domain,电源管理相关的逻辑,后续会由电源管理专题讲解。
pins,"PINCTRL”功能,暂不描述。
numa_node,"NUMA”功能,暂不描述。
dma_mask~archdata,DMA相关的功能,暂不描述。
devt,dev_t是一个32位的整数,它由两个部分(Major和Minor)组成,在需要以设备节点的形式(字符设备和块设备)向用户空间提供接口的设备中,当作设备号使用。在这里,该变量主要用于在sys文件系统中,为每个具有设备号的device,创建/sys/dev/* 下的对应目录,如下:
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:
1:1/ 1:11/ 1:13/ 1:14/ 1:2/ 1:3/ 1:5/ 1:7/ 1:8/ 1:9/
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:1
1:1/ 1:11/ 1:13/ 1:14/
1|root@android:/storage/sdcard0 # ls /sys/dev/char/1:1
/sys/dev/char/1:1
class,该设备属于哪个class。
groups,该设备的默认attribute集合。将会在设备注册时自动在sysfs中创建对应的文件。
- device_driver
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
name,该driver的名称。和device结构一样,该名称非常重要,后面会再详细说明。
bus,该driver所驱动设备的总线设备。为什么driver需要记录总线设备的指针呢?因为内核要保证在driver运行前,设备所依赖的总线能够正确初始化。
owner、mod_name,內核module相关的变量,暂不描述。
suppress_bind_attrs,是不在sysfs中启用bind和unbind attribute,如下:root@android:/storage/sdcard0 # ls /sys/bus/platform/drivers/switch-gpio/
bind uevent unbind
在kernel中,bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制。这种机制是在bus.c中完成的,后面会详细解释。
probe、remove,这两个接口函数用于实现driver逻辑的开始和结束。Driver是一段软件code,因此会有开始和结束两个代码逻辑,就像PC程序,会有一个main函数,main函数的开始就是开始,return的地方就是结束。而内核driver却有其特殊性:在设备模型的结构下,只有driver和device同时存在时,才需要开始执行driver的代码逻辑。这也是probe和remove两个接口名称的由来:检测到了设备和移除了设备(就是为热拔插起的!)。
shutdown、suspend、resume、pm,电源管理相关的内容,会在电源管理专题中详细说明。
groups,和struct device结构中的同名变量类似,driver也可以定义一些默认attribute,这样在将driver注册到内核中时,内核设备模型部分的代码(driver/base/driver.c)会自动将这些attribute添加到sysfs中。
p,私有数据的指针,具体的driver代码可以把任何需要的内容放在这里,反正设备模型代码不关心。
- bus
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs; /* use bus_groups instead */
struct device_attribute *dev_attrs; /* use dev_groups instead */
struct driver_attribute *drv_attrs; /* use drv_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
name,该bus的名称,会在sysfs中以目录的形式存在,如platform bus在sysfs中表现为"/sys/bus/platform”。
dev_name,该名称和"Linux设备模型(5)_device和device driver”所讲述的struct device结构中的init_name有关。对有些设备而言(例如批量化的USB设备),设计者根本就懒得为它起名字的,而内核也支持这种懒惰,允许将设备的名字留空。这样当设备注册到内核后,设备模型的核心逻辑就会用"bus->dev_name+device ID”的形式,为这样的设备生成一个名称。
bus_attrs、dev_attrs、drv_attrs,一些默认的attribute,可以在bus、device或者device_driver添加到内核时,自动为它们添加相应的attribute。
dev_root,根据内核的注释,dev_root设备为bus的默认父设备(Default device to use as the parent),但在内核实际实现中,只和一个叫sub system的功能有关,随后会介绍。
match,一个由具体的bus driver实现的回调函数。当任何属于该Bus的device或者device_driver添加到内核时,内核都会调用该接口,如果新加的device或device_driver匹配上了自己的另一半的话,该接口要返回非零值,此时Bus模块的核心逻辑就会执行后续的处理。
uevent,一个由具体的bus driver实现的回调函数。当任何属于该Bus的device,发生添加、移除或者其它动作时,Bus模块的核心逻辑就会调用该接口,以便bus driver能够修改环境变量。
probe、remove,这两个回调函数,和device_driver中的非常类似,但它们的存在是非常有意义的。可以想象一下,如果需要probe(其实就是初始化)指定的device话,需要保证该device所在的bus是被初始化过、确保能正确工作的。这就要就在执行device_driver的probe前,先执行它的bus的probe。remove的过程相反。
注1:并不是所有的bus都需要probe和remove接口的,因为对有些bus来说(例如platform bus),它本身就是一个虚拟的总线,无所谓初始化,直接就能使用,因此这些bus的driver就可以将这两个回调函数留空。
shutdown、suspend、resume,和probe、remove的原理类似,电源管理相关的实现,暂不说明。
pm,电源管理相关的逻辑,暂不说明。
iommu_ops,暂不说明。
p,一个struct subsys_private类型的指针
**其中subsys_private源码**
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
subsys、devices_kset、drivers_kset是三个kset,由"Linux设备模型(2)_Kobject”中对kset的描述可知,kset是一个特殊的kobject,用来集合相似的kobject,它在sysfs中也会以目录的形式体现。其中subsys,代表了本bus(如/sys/bus/spi),它下面可以包含其它的kset或者其它的kobject;devices_kset和drivers_kset则是bus下面的两个kset(如/sys/bus/spi/devices和/sys/bus/spi/drivers),分别包括本bus下所有的device和device_driver。
interface是一个list head,用于保存该bus下所有的interface。有关interface的概念后面会详细介绍。
klist_devices和klist_drivers是两个链表,分别保存了本bus下所有的device和device_driver的指针,以方便查找。
drivers_autoprobe,用于控制该bus下的drivers或者device是否自动probe,"Linux设备模型(5)_device和device driver”中有提到。
bus和class指针,分别保存上层的bus或者class指针。
无论是bus,还是class,还是我们会在后面看到的一些虚拟的子系统,它都构成了一个“子系统(sub-system)”,该子系统会包含形形色色的device或device_driver,就像一个独立的王国一样,存在于内核中。而这些子系统的表现形式,就是/sys/bus(或/sys/class,或其它)目录下面的子目录,每一个子目录,都是一个子系统(如/sys/bus/spi/)。
device device_driver和bus是在kobject kset ktype的基础上的一次封装;因此,一般情况下,Linux驱动开发很少直接使用device和device_driver,因为内核在它们之上又封装了一层,如soc device、platform device等等,而这些层次提供的接口更为简单、易用(也正是因为这个原因,本文并不会过多涉及device、device_driver等模块的实现细节)
driver开发者可以在struct device变量中,保存描述设备特征的信息,如寻址空间、依赖的GPIOs等,因为device指针会在执行probe等接口时传入,这时driver就可以根据这些信息,执行相应的逻辑操作了。
platform设备
前言
概括来说,Platform设备包括:基于端口的设备(已不推荐使用,保留下来只为兼容旧设备,legacy);连接物理总线的桥设备;集成在SOC平台上面的控制器;连接在其它bus上的设备(很少见)。等等。
这些设备有一个基本的特征:可以通过CPU bus直接寻址(例如在嵌入式系统常见的“寄存器”)。因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动
Platform模块的软件架构
由图片可知,Platform设备在内核中的实现主要包括三个部分:
Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;
Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;
Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备。
其中Platform Device和Platform Driver会会其它Driver提供封装好的API,具体可参考后面的描述。
代码分析
- platform_device
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
dev,真正的设备(Platform设备只是一个特殊的设备,因此其核心逻辑还是由底层的模块实现)。
name,设备的名称,和struct device结构中的init_name("Linux设备模型(5)_device和device driver”)意义相同。实际上,该名称在设备注册时,会拷贝到dev.init_name中。
id,用于标识该设备的ID。
在“Linux设备模型(6)_Bus”中有提过,内核允许存在多个名称相同的设备。而设备驱动的probe,依赖于名称,Linux采取的策略是:在bus的设备链表中查找device,和对应的device_driver比对name,如果相同,则查看该设备是否已经绑定了driver(查看其dev->driver指针是否为空),如果已绑定,则不会执行probe动作,如果没有绑定,则以该device的指针为参数,调用driver的probe接口。
因此,在driver的probe接口中,通过判断设备的ID,可以知道此次驱动的设备是哪个。
id_auto,指示在注册设备时,是否自动赋予ID值(不需要人为指定啦,可以懒一点啦)。
**num_resources、resource,该设备的资源描述,由struct resource(include/linux/ioport.h)结构抽象。在Linux中,系统资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型。**这些资源大多具有独占性,不允许多个设备同时使用,因此Linux内核提供了一些API,用于分配、管理这些源。当某个设备需要使用某些资源时,只需利用struct resource组织这些资源(如名称、类型、起始、结束地址等),并保存在该设备的resource指针中即可。然后在设备probe时,设备需求会调用资源管理接口,分配、使用这些资源。而内核的资源管理逻辑,可以判断这些资源是否已被使用、是否可被使用等等。
id_entry,和内核模块相关的内容,暂不说明。
mfd_cell,和MFD设备相关的内容,暂不说明。
archdata,一个奇葩的存在!!它的目的是为了保存一些architecture相关的数据,去看看arch/arm/include/asm/device.h中struct pdev_archdata结构的定义,就知道这种放纵的设计有多么垃圾了。不管它了!!
- platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
struct platform_driver结构和struct device_driver非常类似,无非就是提供probe、remove、suspend、resume等回调函数,这里不再细说。
另外这里有一个id_table的指针,该指针和device和device_driver所描述的of_match_table、acpi_match_table的功能类似:提供其它方式的设备probe。
platform执行逻辑
- driver
1.用户初始化platform_driver结构体,例如:
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
.pm = &gpio_keys_pm_ops,
.of_match_table = of_match_ptr(gpio_keys_of_match),
}
};
**由于软件分层,只有当前层次才知道应该申请那些变量以及在结束时释放那些变量。因此每个层次都有自己的释放函数(release/remove)。**
2.调用:int __platform_driver_register(struct platform_driver *drv, struct module *owner)
int __platform_driver_register(struct platform_driver *drv,struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
将该driver的bus指定为platform_bus_type(drv->driver.bus = &platform_bus_type)。
如果该platform driver提供了probe、remove、shutdown等回调函数,将该它内嵌的struct driver变量的probe、remove、shutdown等指针,设置为platform模块提供函数,包括platform_drv_probe、platform_drv_remove和platform_drv_shutdown。因为probe等动作会从struct driver变量开始,经过platform_drv_xxx等接口的转接,就可以到达platform diver自身的回调函数中。
调用driver_register接口,将内嵌的struct driver变量添加到内核中。
3.调用:int driver_register(struct device_driver *drv)
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, ""aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
4.调用:int bus_add_driver(struct device_driver *drv)
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error)
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
__func__, drv->name);
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
为该driver的struct driver_private指针(priv)分配空间,并初始化其中的priv->klist_devices、priv->driver、priv->kobj.kset等变量,同时将该指针保存在device_driver的p处
将driver的kset(priv->kobj.kset)设置为bus的drivers kset(bus->p->drivers_kset),这就意味着所有driver的kobject都位于bus->p->drivers_kset之下(寄/sys/bus/xxx/drivers目录下)
以driver的名字为参数,调用kobject_init_and_add接口,在sysfs中注册driver的kobject,体现在/sys/bus/xxx/drivers/目录下,如/sys/bus/spi/drivers/spidev
将该driver保存在bus的klist_drivers链表中,并根据drivers_autoprobe的值,选择是否调用driver_attach进行probe
调用driver_create_file接口,在sysfs的该driver的目录下,创建uevent attribute
调用driver_add_attrs接口,在sysfs的该driver的目录下,创建由bus->drv_attrs指针定义的默认attribute
同时根据suppress_bind_attrs标志,决定是否在sysfs的该driver的目录下,创建bind和unbind attribute(具体可参考"Linux设备模型(5)_device和device driver”中的介绍)