总线是Linux内核用来匹配设备与驱动的内核对象。Linux内核遵循设备与驱动分开的原则,驱动可以通过一些规则来获取对应的设备数据,这样就可以实现一个驱动对应该多个设备的情况。
Linux的总线下面分了platform总线、IIC总线、SPI总线等。总线下面又分了设备与驱动。驱动和设备通过总线的一些规则来进行匹配。下面就来讲解一下platform总线的工作原理。
platform总线是Linux内核的一种虚拟总线,是用来匹配那些没有明确框架的设备,比如Linux早期内核的PWM,ADC等设备。
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus); // 注册platform设备
if (error)
return error;
error = bus_register(&platform_bus_type); // 注册platform总线
if (error)
device_unregister(&platform_bus);
of_platform_register_reconfig_notifier();
return error;
}
platform_bus_init是platform总线的初始化函数,device_register用来注册platform设备,bus_register用来注册platform总线的一些规则。
在Linux中内核的sys文件系统中可以查看对应的设备和总线
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
在platform_bus_type结构体中,重点关注platform_match函数,此函数是用来进行设备与驱动的匹配。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv)) // 设备树的匹配方式
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); // 利用名字进行匹配
}
platform_match函数内有5中匹配方式,常用的有设备树匹配方式和名字匹配方式。
设备树的匹配方式主要是匹配设备中的compatible值是否与驱动中的compatible值相同,相同就执行驱动程序。
- 有设备树下的platform驱动匹配
mytestled: atkled{
compatible = "fsl,imx6ull atkled";
#address-cells = <1>;
#size-cells = <1>;
reg = < 0x20E02F4 0x04 // SW_PAD_CTL_PAD_GPIO1_IO03
0x20E0068 0x04 // SW_MUX_CTL_PAD_GPIO1_IO03
0x209C000 0x4000 // GPIO1
>;
status = "okay";
};
驱动
static const struct of_device_id led_of_match[] = {
{.compatible = "fsl,imx6ull atkled",},
{},
};
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "led_dtb",
.of_match_table = led_of_match,
},
};
上面的例子中,在设备树的文件中有一个compatible属性,属性名为"fsl,imx6ull atkled"。在驱动中也有一个compatible属性,属性名也为"fsl,imx6ull atkled"。当着两个属性名相同时,就会触发platform_match函数的匹配规则,从而驱动程序就会被执行。
static const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *best_match = NULL;
int score, best_score = 0;
if (!matches)
return NULL;
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {
best_match = matches;
best_score = score;
}
}
return best_match;
}
通过内核源代码可以看到,设备树的匹配方式不仅有compatible属性,name和type属性也可以进行匹配,但是很多内核驱动程序都习惯用compatible属性进行匹配。
- 无设备树下的platform驱动匹配
在没有设备树的情况下也是一样的,只不过变成了设备与驱动的名字是否相同
static struct platform_device led_device = {
.name = "led_nodtb",
.id = -1,
.num_resources = ARRAY_SIZE(led_resources),
.resource = led_resources,
};
static struct platform_driver serial8250_isa_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "led_nodtb",
},
};
在上面程序中设备和驱动的名字都为led_nodtb,当两个名字相同时也会触发platform总线的匹配规则。