Linux设备驱动之总线,设备,驱动模型

时间:2022-04-11 17:32:50

  在Linux设备模型中,Bus(总线)是一类特殊的设备,它是连接处理器和其它设备之间的通道。为了方便设备模型的实现,内核规定,系统中的每个设备都要连接在一个Bus上,这个Bus可以是一个system bus、virtual bus或platform bus等。内核通过struct bus_type类型来抽象Bus从而定义各种不同的总线,例如platform bus这个抽象总线以及I2C 总线这种具体存在的总线等,4.4.3版本的内核中它们的定义如下所示:

struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_groups    = platform_dev_groups,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .pm        = &platform_dev_pm_ops,
};

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
};

  I2C总线比较好理解,platform这种虚拟总线有什么用呢?我们知道一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成独立的外设控制器,挂接在SoC 内存空间的外设等不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform总线,我们在写这一类外设的驱动时便可以使用platform总线来挂接这些设备和驱动。我们注意到上面的platform_bus_type并未定义probe和remove这两个成员,而i2c_bus_type里却定义了,这就要说到总线是如何匹配挂接在上面的设备和驱动的了。当一个新的设备(驱动)挂接到该总线上时,总线便会从该总线上寻找能与之匹配的驱动(设备)。这通过调用总线的probe函数来实现,以I2C总线的probe函数为例,里面有如下一行代码:

 status = driver->probe(client, i2c_match_id(driver->id_table, client));

再进入i2c_match_id函数内部:

 1 static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
 2                         const struct i2c_client *client)
 3 {
 4     while (id->name[0]) {
 5         if (strcmp(client->name, id->name) == 0)
 6             return id;
 7         id++;
 8     }
 9     return NULL;
10 }

我们便得知总线通过比较i2c_client里的name和i2c_driver里的id_table是否匹配来判断i2c设备和驱动是否匹配,如果匹配,就调用该驱动的probe函数,完成一些检测和初始化的工作,以完成两者的连接。

  上面是是对i2c总线的分析,大部分总线基本都是如此匹配设备和驱动的,然而并不是所有的总线都需要probe和remove接口的,因为对有些总线来说(例如platform bus),它本身就是一个虚拟的总线,无所谓检测和初始化,直接就能使用,因此这些总线就可以将这两个回调函数留空。