赞
踩
Linux内核需要兼容多个平台,不同平台的寄存器设计不同导致操作方法不同,故内核提出分层思想,抽象出与硬件无关的软件层作为核心层来管理下层驱动,各厂商根据自己的硬件编写驱动代码作为硬件驱动层
Linux内核建立的 设备-总线-驱动 模型,定义如下:
- 1、device
- include\linux\device.h
- struct device {
- ...
- struct bus_type *bus; /* type of bus device is on */
- struct device_driver *driver; /* which driver has allocated this device */
- struct device_node *of_node; /* associated device tree node */
-
- ...
- }
-
- 2、driver
- include\linux\device\driver.h
- struct device_driver {
- ...
- struct bus_type *bus;
- ...
- }
-
- 3、bus
- include\linux\bus\bus.h
- struct bus_type {
- ...
- int (*match)(struct device *dev, struct device_driver *drv);
- int (*probe)(struct device *dev);
- ...
- }
这里提到的是虚拟总线,总线能将对应的设备和驱动进行匹配,可以用下面的命令查看不同总线类型
- /sys/bus # ls -l
- ......
- drwxr-xr-x 4 root root 0 2023-02-21 13:35 i2c
- drwxr-xr-x 4 root root 0 2023-02-21 13:35 mmc
- drwxr-xr-x 5 root root 0 2023-02-21 13:35 pci
- drwxr-xr-x 4 root root 0 2023-02-20 07:09 platform
- drwxr-xr-x 4 root root 0 2023-02-21 13:35 scsi
- drwxr-xr-x 4 root root 0 2023-02-21 13:35 usb
- ......
总线类型 | 描述 |
I2C总线 | 挂在i2c总线(硬件)下的从设备,比如加密芯片、rtc芯片、触摸屏芯片等等都需要驱动,自然也要按照分离思想来设计。内核中的i2c 总线就是用来帮助i2c从设备的设备信息和驱动互相匹配的 |
Platform总线 | 像i2c、spi这样硬件有实体总线的,从设备驱动可以用总线来管理。那么没有总线的硬件外设怎么办?比如gpio、uart、i2c控制器、spi 控制器…等等,这些通通用 platform 总线来管理 |
在写驱动时会用到一些注册函数比如:platform_driver_register,spi_register_driver、i2c_add_driver,接下来分析内核驱动和设备匹配的流程,原理就是在注册到总线的时候,去获取对方的链表并根据规则检测,匹配后调用probe(),也就是驱动的入口函数
以Platform Driver举例,整个匹配过程如下
- module_platform_driver
- |-- module_driver
- |-- __platform_driver_register
- |-- driver_register
- |-- bus_add_driver
- |-- driver_attach
- |-- bus_for_each_dev
- |-- __driver_attach
- |-- driver_match_device
- |-- platform_match
- |-- of_driver_match_device
- |-- of_match_device
- |-- __of_match_node
- |-- driver_probe_device
- |-- really_probe
- |-- call_driver_probe
- |-- platform_probe
- |-- drv->probe()
-
封装了一层,展开后实际上就是module_init和module_exit
- /* module_platform_driver() - Helper macro for drivers that don't do
- * anything special in module init/exit. This eliminates a lot of
- * boilerplate. Each module may only use this macro once, and
- * calling it replaces module_init() and module_exit()
- */
- #define module_platform_driver(__platform_driver) \
- module_driver(__platform_driver, platform_driver_register, \
- platform_driver_unregister)
例如对于MTK某平台UFS驱动,传入__platform_driver 参数为
- static struct platform_driver ufs_mtk_pltform = {
- .probe = ufs_mtk_probe,
- .remove = ufs_mtk_remove,
- .shutdown = ufshcd_pltfrm_shutdown,
- .driver = {
- .name = "ufshcd-mtk",
- .pm = &ufs_mtk_pm_ops,
- .of_match_table = ufs_mtk_of_match,
- },
- };
- /**
- * module_driver() - Helper macro for drivers that don't do anything
- * special in module init/exit. This eliminates a lot of boilerplate.
- * Each module may only use this macro once, and calling it replaces
- * module_init() and module_exit().
- *
- * @__driver: driver name
- * @__register: register function for this driver type
- * @__unregister: unregister function for this driver type
- * @...: Additional arguments to be passed to __register and __unregister.
- *
- * Use this macro to construct bus specific macros for registering
- * drivers, and do not use it on its own.
- */
- #define module_driver(__driver, __register, __unregister, ...) \
- static int __init __driver##_init(void) \
- { \
- return __register(&(__driver) , ##__VA_ARGS__); \
- } \
- module_init(__driver##_init); \
- static void __exit __driver##_exit(void) \
- { \
- __unregister(&(__driver) , ##__VA_ARGS__); \
- } \
- module_exit(__driver##_exit);
注意此处的__register是传进来的__platform_driver_register
- /**
- * __platform_driver_register - register a driver for platform-level devices
- * @drv: platform driver structure
- * @owner: owning module/driver
- */
- int __platform_driver_register(struct platform_driver *drv,
- struct module *owner)
- {
- drv->driver.owner = owner;
- drv->driver.bus = &platform_bus_type;
-
- return driver_register(&drv->driver);
- }
- EXPORT_SYMBOL_GPL(__platform_driver_register);
对bus参数进行赋值
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_groups = platform_dev_groups,
- .match = platform_match,
- .uevent = platform_uevent,
- .probe = platform_probe,
- .remove = platform_remove,
- .shutdown = platform_shutdown,
- .dma_configure= platform_dma_configure,
- .dma_cleanup= platform_dma_cleanup,
- .pm = &platform_dev_pm_ops,
- };
- EXPORT_SYMBOL_GPL(platform_bus_type);
- /**
- * driver_register - register driver with bus
- * @drv: driver to register
- *
- * We pass off most of the work to the bus_add_driver() call,
- * since most of the things we have to do deal with the bus
- * structures.
- */
- int driver_register(struct device_driver *drv)
- {
- ......
- other = driver_find(drv->name, drv->bus);
- if (other) {
- pr_err("Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EBUSY;
- }
-
- ret = bus_add_driver(drv);
- ......
- }
- EXPORT_SYMBOL_GPL(driver_register);
drv->bus->p->drivers_autoprobe默认是1,结构体定义时就赋值了
- struct subsys_private {
- ...
- unsigned int drivers_autoprobe:1;
- }
- /**
- * bus_add_driver - Add a driver to the bus.
- * @drv: driver.
- */
- int bus_add_driver(struct device_driver *drv)
- {
- ......
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv);
- if (error)
- goto out_del_list;
- }
- ......
- }
- /**
- * driver_attach - try to bind driver to devices.
- * @drv: driver.
- *
- * Walk the list of devices that the bus has on it and try to
- * match the driver with each one. If driver_probe_device()
- * returns 0 and the @dev->driver is set, we've found a
- * compatible pair.
- */
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
- EXPORT_SYMBOL_GPL(driver_attach);
此函数 fn 即为 __driver_attach 函数指针,data参数 是 drv
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus || !bus->p)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while (!error && (dev = next_device(&i)))
- error = fn(dev, data);
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_dev);
- static int __driver_attach(struct device *dev, void *data){
- ......
- ret = driver_match_device(drv, dev);
- ......
- ret = driver_probe_device(drv, dev);
- ......
- }
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
-
- /* 返回 1 是可以继续往下走的 ret <= 0 不行*/
可以看到在Register时有match回调
- struct bus_type platform_bus_type = {
- ......
- .match = platform_match,
- .probe = platform_probe,
- ......
- };
- 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 - Tell if a driver's of_match_table matches a device.
- * @drv: the device_driver structure to test
- * @dev: the device structure to match against
- */
- static inline int of_driver_match_device(struct device *dev,
- const struct device_driver *drv)
- {
- return of_match_device(drv->of_match_table, dev) != NULL;
- }
of_match_table定义如下
- static struct platform_driver ufs_mtk_pltform = {
- .probe = ufs_mtk_probe,
- .remove = ufs_mtk_remove,
- .shutdown = ufshcd_pltfrm_shutdown,
- .driver = {
- .name = "ufshcd-mtk",
- .pm = &ufs_mtk_pm_ops,
- .of_match_table = ufs_mtk_of_match,
- },
- };
- static const struct of_device_id ufs_mtk_of_match[] = {
- { .compatible = "mediatek,mtxxxx-ufshci" },
- };
- const struct of_device_id *of_match_device(const struct of_device_id *matches,
- const struct device *dev)
- {
- if (!matches || !dev->of_node || dev->of_node_reused)
- return NULL;
- return of_match_node(matches, dev->of_node);
- }
- EXPORT_SYMBOL(of_match_device);
- const struct of_device_id *of_match_node(const struct of_device_id *matches,
- const struct device_node *node)
- {
- match = __of_match_node(matches, node);
- }
- EXPORT_SYMBOL(of_match_node);
- static
- const struct of_device_id *__of_match_node(const struct of_device_id *matches,
- const struct device_node *node)
- {
-
- for (; matches->name[0] ||
- matches->type[0] ||
- matches->compatible[0]; matches++) {
- /* 每次循环,选择Vendor驱动中的match table结构体数组的下一个比较 */
- 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;
- }
- static int __of_device_is_compatible(const struct device_node *device,
- const char *compat, const char *type, const char *name)
- {
- ......
- if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
- score = INT_MAX/2 - (index << 2);
- break;
- }
- ......
- }
cp即为从设备树节点中获取的compatible信息,示例如下
- ufshci: ufshci@112b0000 {
- compatible = "mediatek,mtxxxx-ufshci";
- reg = <0 0x112b0000 0 0x2a00>;
- }
- static int driver_probe_device(struct device_driver *drv,
- struct device *dev)
- {
- ......
- ret = __driver_probe_device(drv, dev);
- ......
- }
initcall_debug是一个内核参数,可以跟踪initcall,用来定位内核初始化的问题。在cmdline中增加initcall_debug后,内核启动过程中会在调用每一个init函数前有一句打印,结束后再有一句打印并且输出了该Init函数运行的时间,通过这个信息可以用来定位启动过程中哪个init函数运行失败以及哪些init函数运行时间较长
really_probe_debug()内部还是调用了really _probe()
- static int __driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- ......
- if (initcall_debug)
- ret = really_probe_debug(dev, drv);
- else
- ret = really_probe(dev, drv);
- ......
- }
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- ......
- ret = call_driver_probe(dev, drv);
- ......
- }
- static int call_driver_probe(struct device *dev,
- struct device_driver *drv)
- {
- ......
- if (dev->bus->probe)
- ret = dev->bus->probe(dev);
- else if (drv->probe)
- ret = drv->probe(dev);
- ......
- }
不管走没有dev->bus->probe,最终都会走到drv->probe
- static int platform_probe(struct device *_dev)
- {
- struct platform_driver *drv = to_platform_driver(_dev->driver);
- struct platform_device *dev = to_platform_device(_dev);
- ......
- if (drv->probe) {
- ret = drv->probe(dev);
- if (ret)
- dev_pm_domain_detach(_dev, true);
- }
- ......
- }
此时驱动匹配设备成功,会走到之前Register的probe
- static struct platform_driver ufs_mtk_pltform = {
- .probe = ufs_mtk_probe,
- ......
- };
【参考博客】
[1] Linux设备驱动和设备匹配过程_linux驱动和设备匹配过程-CSDN博客
[2] platform 总线_怎么查询platform 总线-CSDN博客
[3] Linux Driver 和Device匹配过程分析(1)_linux设备驱动和设备树的match过程-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。