为了降低设备多样性带来的Linux驱动开发的复杂度,以及设备热拔插处理、电源管理等,Linux内核提出了设备模型(也称作Driver Model)的概念。设备模型将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口。驱动的开发,就简化为对内核所规定的数据结构的填充和实现。
http://www.wowotech.net/device_model/13.html
bus device driver class
bus
: bus的主要功能是管理device和driver之间的关系. 它维护着一个device列表和一个driver列表, 并且负责匹配device和driver.
device
: 对硬件设备的抽象, 主要是描述硬件设备的属性.
driver
: 对设备驱动的抽象. 负责管理设备.
class
: 是一个device的集合,
kobject sysfs
kobject
是内核对象的抽象, 它是一个基类, 在c语言中通过结构体内嵌的方式实现, device就是内核对象他们的数据结构会内嵌一个kobject结构体.
kobject主要有以下功能:
- 通过parent指针,可以将所有Kobject(内核对象)以层次结构的形式组合起来。
- 用于管理内核对象的生命周期,使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放。
- 和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间.
sysfs
是一个虚拟文件系统, 将kobject(内核对象)的属性以文件的形式展现出来, 供用户空间程序读写.
比如一个device, 它挂在某个bus下同时也属于某个class.
首先sys目录下有给device的目录, 里面会有这个device的目录
也可以通过sys/bus/device/XXX
找到
也可以通过sys/class/XXX
找到
如果这个device有*dev(设备号)
属性, 会在device目录下有给dev文件, 里面存放设备号(mdev就读取这个文件自动创建设备节点). 并且在/dev/char/下会有一个以设备号命名的链接, 指向这个设备.
bus
bus 的构成
name
: 总线的名字, 会在sysfs中以目录的形式存在dev_name
: 如果bus下的device没有设置init name, 内核会以"bus->dev_name+device ID"的形式,为这样的设备生成一个名称。bus_attrs
、dev_attrs
、drv_attrs
: 一些默认的attribute,可以在bus、device或者device_driver添加到内核时,自动为它们添加相应的attribute。match
: 匹配device和driver的函数.uevent
: 当任何属于该Bus的device,发生添加、移除或者其它动作时,Bus模块的核心逻辑就会调用该接口,以便bus driver能够修改环境变量。probe
,remove
: bus driver的probe和remove函数
sys/bus里能看到已注册的总线. 在bus目录下使用tree
可以看到总线的结构.
bus 的操作
通常我们不会新建一个新的总线, 所以这里不介绍如何创建管理总线.
bus_for_each_dev
bus_for_each_drv
BUS_ATTR(name, mode, show, store)
;
device
device的构成
parent
: 该设备的父设备,一般是该设备所从属的busp
: 该指针中会保存子设备链表。kobj
: 如上文所说,通过嵌入struct kobject实现类似继承的功能(复用kobject相关的代码)。init_name
: 该设备的名称,最终是设置到kobj配合sysfs创建目录, 不设置时内核会根据bus->dev_name + device ID生成一个名称。type
:bus
: 该设备所从属的busdriver
: 该设备所使用的driver, 通常在bus match成功后调用driver的probe函数,在验证设备可用后设置该指针。platform_data
: 供driver使用, 放一些私有数据power
,pm_domain
: 电源管理相关devt
: 设备号. 一般什么时候被设置?release
: 释放设备时调用的函数, 上文提到kobject会自动释放, 就靠这实现.class
: 该设备所从属的classgroups
: 该设备的attribute 集合, sysfs会根据这个集合创建对应的文件.
device的操作
DEVICE_ATTR(name, mode, show, store);
//使用这个宏定义一个attribute. mode是文件权限, show和store是读写函数.
device_register(struct device *dev);
//注册一个设备
device_unregister(struct device *dev);
device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
//创建并注册一个设备
通常device也是基类嵌入到一个结构体中的, 比如platform_device. 所以更常用的是其子类的操作函数, 比如platform_device_register.
driver
driver 的构成
name
: 驱动的名字bus
: 该驱动所从属的busowner
,mod_name
: 和module相关probe
: 当设备匹配成功后调用的函数remove
: 当设备被移除时调用的函数shutdown
,suspend
,resume
,pm
: 电源管理相关suppress_bind_attrs
: 是否启动sysfs的bind/unbind功能. bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制.groups
: 该driver的attribute 集合, sysfs会根据这个集合创建对应的文件.p
: 私有数据指针
DRIVER_ATTR(name, mode, show, store);
//和DEVICE_ATTR一样, 用于定义一个attribute
在这个架构下, 驱动开发者基本的工作就是实现probe和remove函数.这两个函数的参数都是device, 这样就能获取到device的数据了.
probe 要做以下事情:
- 验证设备(device)是否可用
- 字符设备注册, 包括设备号分配, file_operations设置, cdev注册等
remove 是probe的逆操作, 释放资源.
probe 触发的时机:
- 将struct device类型的变量注册到内核中时自动触发
- 将struct device_driver类型的变量注册到内核中时自动触发
- 手动查找bus下的设备/驱动时手动触发
class
把一些设备归类, 同一类设备就会有相同的属性/操作, 把他们都放到一个class下, 这样就可以把这些共性的东西放到class下, 而不是每个device都有一份.
class 的构成
name
: class的名字, 会在/sys/class/下以目录的形式存在class_atrrs
: 该class的默认attribute, 在/sys/class/xxx_class”下创建对应的attribute文件。dev_attrs
: 共性属性, 该class下每个device的attribute, 在/sys/class/xxx_class/xxx_device”下创建对应的attribute文件。dev_uevent
: 当该class下有设备发生变化时,会调用class的uevent回调函数。class_release
: 用于release自身的回调函数。dev_release
: 用于release class内设备的回调函数。在device_release接口中,会依次检查Device、Device Type以及Device所在的class,是否注册release接口,如果有则调用相应的release接口release设备指针。