linux 中bus驱动解析
总线(bus)是linux发展过程中抽象出来的一种设备模型,为了统一管理所有的设备,内核中每个设备都会被挂载在总线上,这个bus可以是对应硬件的bus(i2c bus、spi bus)、可以是虚拟bus(platform bus)。
简述bus的工作流程
bus将所有挂在上面的具体设备抽象成两部分,driver和device。
driver实现了同类型设备的驱动程序实现,而device则向系统注册具体的设备需要的资源,每当添加一个新的driver(device)到bus中时,都将调用bus的match函数,试图寻找匹配的device(driver)。
如果匹配成功,就调用probe函数,在probe函数中实现设备的初始化、各种配置以及生成用户空间的文件接口。
举个例子,针对AT24CXX(一种常用的存储设备)这种同系列产品,他们的操作方式都是非常相似的,不同的无非是容量大小。
那么我们就没有必要为AT24C01、AT24C02去分别写一份驱动程序,而是统一为其写一份兼容所有AT24CXX的驱动程序,然后再传入不同的参数以对应具体的型号。
在linux驱动管理模型中的体现就是:驱动程序对应driver、需要的具体型号的硬件资源对应device,将其挂在bus上。
将driver注册到bus上,当用户需要使用AT24C01时,以AT24C01的参数构建一个对应device,注册到bus中,bus的match函数匹配上之后,调用probe函数,即可完成AT24C01的初始化,完成在用户空间的文件接口注册。
以此类推,添加所有的AT24CXX设备都可以以这样的形式实现,只需要构建一份device,而不用为每个设备重写一份驱动,提高了复用性,节省了内存空间。
linux bus结构体
linux将设备挂在总线上,对应设备的注册和匹配流程由总线进行管理,在linux内核中,每一个bus,都由struct bus_type来描述:
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
...
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);
struct subsys_private *p;
...
};
为了突出重点,省去了一些暂时不需要深入了解的成员,我们来看看其中最主要的几个成员:
name : 该bus的名字,这个名字是这个bus在sysfs文件系统中的体现,对应/sys/bus/$name.
dev_name : 这个dev_name并不对应bus的名称,而是对应bus所包含的struct device的名字,即对应dev_root。