在Linux设备模型中,Bus(总线)是一类特殊的设备,它是连接处理器和其它设备之间的通道(channel)。为了方便设备模型的实现,内核规定,系统中的每个设备都要连接在一个Bus上,这个Bus可以是一个内部Bus、虚拟Bus或者Platform Bus。
device和device driver是Linux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以kernel就为设备和驱动它的driver定义了两个数据结构,分别是device和device_driver
bus_type
内核通过struct bus_type结构,抽象Bus
name,该bus的名称,会在sysfs中以目录的形式存在,如platform bus在sysfs中表现为"/sys/bus/platform”。
dev_name,该名称和下面讲到的struct device结构中的init_name有关。对有些设备而言,允许将设备的名字留空。这样当设备注册到内核后,设备模型的核心逻辑就会用"bus->dev_name+device ID”的形式,为这样的设备生成一个名称。
dev_attrs,被下边的groups取代
bus_groups、dev_groups、drv_groups,一些默认的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的过程相反。
并不是所有的bus都需要probe和remove接口的,因为对有些bus来说(例如platform bus),它本身就是一个虚拟的总线,无所谓初始化,直接就能使用,因此这些bus的driver就可以将这两个回调函数留空。
shutdown、suspend、resume,和probe、remove的原理类似,电源管理相关的实现。
online、offline,和属于这个总线设备的online属性相关。
pm,电源管理相关的逻辑。
iommu_ops,总线的IOMMU相关操作,IOMMU与MMU功能类似,可以给设备一个内核空间的地址(或叫总线地址),而不限于可以直接访问的常规内存区域。
p,一个struct subsys_private类型的指针,kobject隐藏在这个结构后面。这个结构也很重要。
device
device结构很复杂,这里将会选一些对理解设备模型非常关键的字段进行说明。
parent,该设备的父设备,一般是该设备所从属的bus、controller等设备。
p,一个用于设备的私有数据结构指针,保存子设备链表,添加父节点,邻居节点和总线链表等。
kobj,该数据结构对应的struct kobject。
init_name,该设备的名称。
在设备模型中,名称是一个非常重要的变量,任何注册到内核中的设备,都必须有一个合法的名称,可以在初始化时给出,也可以由内核根据“bus name + device ID”的方式创造。见bus_type.dev_name的说明。
type,struct device_type结构是新版本内核新引入的一个结构,它和struct device关系,非常类似stuct kobj_type和struct kobject之间的关系。
bus,该device属于哪个总线。
driver,该device对应的device driver。
platform_data,一个指针,用于保存具体的平台相关的数据。linux经常用来保存一些单板相关的数据,来描述包含哪些设备,以及它们如何互联。以便大幅减少BSP的代码量和驱动中ifdef的使用。
power、pm_domain,电源管理相关的逻辑,后续会由电源管理专题讲解。
pins,"PINCTRL”功能。
numa_node,"NUMA”功能。
devt,设备号。在这里,该变量主要用于在sys文件系统中,为每个具有设备号的device,创建/sys/dev/*下的对应目录,如下:
class,该设备属于哪个class。这从侧面说明了class是device的集合。
groups,该设备的默认attribute集合。将会在设备注册时自动在sysfs中创建对应的文件。
device_driver
name,该driver的名称。和device结构一样,该名称非常重要,和device和driver的匹配有关。
bus,该driver所驱动设备的总线设备。内核要保证在driver运行前,设备所依赖的总线能够正确初始化。
owner、mod_name,內核module相关的变量。
suppress_bind_attrs,通过sysfs启用bind和unbind的attribute,如下:
# ls /sys/bus/platform/drivers/switch-gpio/
bind uevent unbind
在kernel中,bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制。
probe、remove,这两个接口函数用于实现driver逻辑的开始和结束。在设备模型的结构下,只有driver和device同时存在时,才需要开始执行driver的代码逻辑。这也是probe和remove两个接口名称的由来:检测到了设备和移除了设备(就是为热拔插起的!)。
shutdown、suspend、resume、pm,电源管理相关的内容。
groups,和struct device结构中的同名变量类似,driver也可以定义一些默认attribute,这样在将driver注册到内核中时,内核设备模型部分的代码会自动将这些attribute添加到sysfs中。
p,私有数据的指针。
subsys_private
旧的linux存在独立的子系统数据结构subsystem,2.6.35抛弃了这个数据结构。转而用subsys_private表示。
什么是子系统?无论是bus,还是class,还是一些虚拟的子系统,它都构成了一个“子系统(sub-system)”,该子系统会包含形形色色的device或device_driver,就像一个独立的王国一样,存在于内核中。而这些子系统的表现形式,就是/sys/bus(或/sys/class,或其它)目录下面的子目录,每一个子目录,都是一个子系统(如/sys/bus/spi/)。从子系统的角度看bus和后面的class很类似,它们都用subsys_private表示子系统。
subsys、devices_kset、drivers_kset是三个kset。其中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。bus_type与kobject的关系通过subsys成员体现。通过宏to_subsys_private(obj)可以看出。
interface,用于保存该bus下所有的interface。interface抽象了此类子系统的专有功能。
klist_devices、klist_drivers,分别保存了本bus下所有的device和device_driver的指针,以方便查找。
drivers_autoprobe,用于控制该bus下的drivers或者device是否自动probe。
bus、class,分别保存上层的bus或者class指针。
device_private
klist_childre,包含此设备所有的子设备。
knode_parent,连入父设备的klist_children时所用的节点。
knode_driver,连入驱动的设备链表所用的节点,driver_private的klist_devices节点。
knode_bus,连入总线的设备链表时所用的节点,subsys_private中klist_devices的节点。
deferred_probe,deferred_probe_list的入口,deferred_probe_list用来重新绑定那些暂时不能获得device全部资源的驱动,这种情况常常是因为某一个驱动需要另一个驱动首先probe。
device,指向包含device_private的device
driver_private
kboj,结构对应的kobject。
klist_devices,包含此驱动可以驱动的设备。
knode_bus,连入bus的驱动链表的节点,代表subsys_private中klist_drivers的节点。
mkobj,内核module相关变量。
driver,指向包含driver_private的driver。
device_type
name,表示该类型的名称,当该类型的设备添加到内核时,内核会发出"DEVTYPE=‘name’”类型的uevent,告知用户空间某个类型的设备available了。
groups,该类型设备的公共attribute集合。设备注册时,会同时注册这些attribute。这就是面向对象中“继承”的概念。
uevent,所有相同类型的设备,会有一些共有的uevent需要发送,由该接口实现。
release,如果device结构没有提供release接口,就要查询它所属的type是否提供,用于释放device变量所占的空间。
bus_type,device和device_driver与kobject的关系
从device和device_driver的角度看
device和device_driver直接继承kobject,bus_type通过subsys_private与kobject发生联系。图中kset和kobject的关系是从kset与包含在它里边的kobject的角度看的,与前面kset与kobjcet的关系角度不同。由此可见,总线、设备和驱动是建立在kobject基础上的,借由kobject和kset建立层次关系。
bus_type,device、device_driver的关系
一个device_driver可以支持多个device。bus_type通过subsys_private与device_driver和device发生关联。
bus_type、device、device_driver与sysfs的关系
总线,设备和设备驱动与sysfs的对应关系相对简单直观