这段时间在搞嵌入式相关的知识,其中很多地方涉及到了驱动编程,对驱动程序文件的结构有所了解,但是其原理不是很清楚,因此本文主要对platform的相关流程进行介绍,Linux内核代码版本为5.7.19。
1.platform
在关注驱动程序注册的过程前,首先需要明确什么是platform,什么样的设备称为platform device。
基于分层的思想,Linux的设备与驱动进行分离,分为设备、总线、驱动三个层次,多个设备与多个驱动通过总线相连,总线将设备与驱动进行绑定,系统每注册一个设备,会去寻找匹配的驱动;每注册一个驱动的同时,也会寻找匹配的设备,匹配的过程由总线完成。也就是说,设备与驱动的注册过程其实是分开的。
一般情况下,Linux设备与驱动挂载在同一总线例如PCI、SPI等,但是对于嵌入式的SOC等外设,却没有总线进行挂载。因此在Linxu2.6以后,引入了虚拟总线platform总线,相应的设备称为platform device,驱动则是platform driver。
2.总线、设备与驱动
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
设备中的信息主要包括设备名、设备号等基本信息。
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
驱动主要包括调用的函数,例如probe()、shutdown()、remove()等,参数均包含platform_device,与其本身的特性相符合。
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
Linux为platform总线定义了一个bus_type的实例,match函数负责驱动与device的匹配。
3.设备注册
static int __init uart8250_init(void)
{
return platform_device_register(&uart8250_device);
}
在Linux下任选某一platform设备,查看其初始化函数。这里以uart8250为例。发现执行了platform_device_register()函数。
进入该函数:
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
setup_pdev_dma_masks(pdev);
return platform_device_add(pdev);
}
该函数的作用是初始化platform_device中的device结构,设置DMA mask,将设备挂载到platform总线上。
int platform_device_add(struct platform_device *pdev)
{
u32 i;
int ret;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;//设置父节点
pdev->dev.bus = &platform_bus_type; //设置总线
//根据设备的ID,修改或者设置设备的名称
switch (pdev->id) {
//对于多个同名的设备,使用ID区分
default:
//将数据写入到dev中的kobject对象
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
/*
* Automatically allocated device ID. We mark it as such so
* that we remember it must be freed, and we append a suffix
* to avoid namespace collision with explicit IDs.
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p) {
ret = insert_resource(p, r);
if (ret) {
dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
goto failed;
}
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
//将device变量添加到内核中
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
while (i--) {
struct resource *r = &pdev->resource[i];
if (r->parent)
release_resource(r);
}
err_out:
return ret;
}
这是一个完整的device注册过程。
4.驱动注册
任选一个驱动注册程序,其初始化函数为:
static struct platform_driver locomo_device_driver = {
.probe = locomo_probe,
.remove = locomo_remove,
.driver = {
.name = "locomo",
},
};
static int __init locomo_init(void)
{
int ret = bus_register(&locomo_bus_type);
if (ret == 0)
platform_driver_register(&locomo_device_driver);
return ret;
}
该函数调用了platform_driver_register()函数,继续寻找调用路径,发现该函数定义为__platform_driver_register()函数:
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
在函数中,设置了该驱动所在的总线,为platform_bus_type,同时设定了platform_driver内置的driver的probe、remove、shutdown函数。最后将驱动添加到内核中。
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
int ret;
ret = of_clk_set_defaults(_dev->of_node, false);
if (ret < 0)
return ret;
ret = dev_pm_domain_attach(_dev, true);
if (ret)
goto out;
if (drv->probe) {
ret = drv->probe(dev);
if (ret)
dev_pm_domain_detach(_dev, true);
}
out:
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
dev_warn(_dev, "probe deferral not supported\n");
ret = -ENXIO;
}
return ret;
}
进入platform_drv_probe()查看,将会执行了drv->probe()函数,这也是驱动程序本身的probe()函数。remove()函数与shutdown()函数与之类似。
设备与驱动的匹配
设备与驱动的匹配都是由bus总线完成的。设备与驱动在注册的过程中,都会引发总线调用match()函数来寻找该设备(或驱动)匹配的驱动(或设备),如果存在则将双方绑定。如果是设备先注册,设备在挂载到到总线上时,无法匹配对应的驱动。当接下来注册驱动时,由于设备已被注册,那么总线会先对设备与驱动进行绑定,再调用驱动中的probe()函数等。如果是驱动先注册,则probe()函数会等到设备注册成功并匹配绑定后才会调用。
设备注册时的匹配:
int platform_device_add(struct platform_device *pdev)
int device_add(struct device *dev)
void bus_probe_device(struct device *dev)
void device_initial_probe(struct device *dev)
static int __device_attach(struct device *dev, bool allow_async)
static int __device_attach_driver(struct device_driver *drv, void *_data)
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
这就是设备注册时对match()函数的调用过程。
int driver_register(struct device_driver *drv)
int bus_add_driver(struct device_driver *drv)
int driver_attach(struct device_driver *drv)
static int __driver_attach(struct device *dev, void *data)
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
其基本流程与设备注册类似,最后都会在driver_match_device()中调用bus总线的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);
}
首先,如果在设备初始化时,设置了设备的驱动名称,则比较设备中的属性名与驱动名;
其次,调用of_driver_match_device()函数进行匹配,该方法主要对Linux中的设备树中的设备进行匹配;
接着,对ACPI标准的驱动、设备进行匹配。
然后,查看驱动的id_table是否有该设备的名称。这样是因为一个驱动可能支持多个设备,所以建表来进行存储支持的所有设备。
最后,比较设备名与驱动名是否相等。
在驱动与设备匹配完毕后,会调用驱动的probe()函数,其调用过程为:
static int __driver_attach(struct device *dev, void *data)
int device_driver_attach(struct device_driver *drv, struct device *dev)
int driver_probe_device(struct device_driver *drv, struct device *dev)
static int really_probe(struct device *dev, struct device_driver *drv)
dev->bus->probe(dev);
最后调用bus总线的probe()函数,也就是之前分析的platform_drv_probe()函数。同样,当设备注册后,也会先与驱动匹配,如果匹配完成,则同样会调用driver_probe_device()函数。这也说明,只有当驱动与设备匹配完成后,驱动的probe()函数才会调用。