当前位置:   article > 正文

Linux 驱动&设备匹配过程

Linux 驱动&设备匹配过程

一、Linux 驱动-总线-设备模型

1、驱动分层        

        Linux内核需要兼容多个平台,不同平台的寄存器设计不同导致操作方法不同,故内核提出分层思想,抽象出与硬件无关的软件层作为核心层来管理下层驱动,各厂商根据自己的硬件编写驱动代码作为硬件驱动层

2、设备&总线&驱动

        Linux内核建立的 设备-总线-驱动 模型,定义如下:

  1. 1、device
  2. include\linux\device.h
  3. struct device {
  4. ...
  5. struct bus_type *bus; /* type of bus device is on */
  6. struct device_driver *driver; /* which driver has allocated this device */
  7. struct device_node *of_node; /* associated device tree node */
  8. ...
  9. }
  10. 2、driver
  11. include\linux\device\driver.h
  12. struct device_driver {
  13. ...
  14. struct bus_type *bus;
  15. ...
  16. }
  17. 3、bus
  18. include\linux\bus\bus.h
  19. struct bus_type {
  20. ...
  21. int (*match)(struct device *dev, struct device_driver *drv);
  22. int (*probe)(struct device *dev);
  23. ...
  24. }

        这里提到的是虚拟总线,总线能将对应的设备和驱动进行匹配,可以用下面的命令查看不同总线类型

  1. /sys/bus # ls -l
  2. ......
  3. drwxr-xr-x 4 root root 0 2023-02-21 13:35 i2c
  4. drwxr-xr-x 4 root root 0 2023-02-21 13:35 mmc
  5. drwxr-xr-x 5 root root 0 2023-02-21 13:35 pci
  6. drwxr-xr-x 4 root root 0 2023-02-20 07:09 platform
  7. drwxr-xr-x 4 root root 0 2023-02-21 13:35 scsi
  8. drwxr-xr-x 4 root root 0 2023-02-21 13:35 usb
  9. ......
总线类型描述
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举例,整个匹配过程如下

2.1 整体调用逻辑

  1. module_platform_driver
  2. |-- module_driver
  3. |-- __platform_driver_register
  4. |-- driver_register
  5. |-- bus_add_driver
  6. |-- driver_attach
  7. |-- bus_for_each_dev
  8. |-- __driver_attach
  9. |-- driver_match_device
  10. |-- platform_match
  11. |-- of_driver_match_device
  12. |-- of_match_device
  13. |-- __of_match_node
  14. |-- driver_probe_device
  15. |-- really_probe
  16. |-- call_driver_probe
  17. |-- platform_probe
  18. |-- drv->probe()

2.2 module_platform_driver

        封装了一层,展开后实际上就是module_init和module_exit

  1. /* module_platform_driver() - Helper macro for drivers that don't do
  2. * anything special in module init/exit. This eliminates a lot of
  3. * boilerplate. Each module may only use this macro once, and
  4. * calling it replaces module_init() and module_exit()
  5. */
  6. #define module_platform_driver(__platform_driver) \
  7. module_driver(__platform_driver, platform_driver_register, \
  8. platform_driver_unregister)

        例如对于MTK某平台UFS驱动,传入__platform_driver 参数为

  1. static struct platform_driver ufs_mtk_pltform = {
  2. .probe = ufs_mtk_probe,
  3. .remove = ufs_mtk_remove,
  4. .shutdown = ufshcd_pltfrm_shutdown,
  5. .driver = {
  6. .name = "ufshcd-mtk",
  7. .pm = &ufs_mtk_pm_ops,
  8. .of_match_table = ufs_mtk_of_match,
  9. },
  10. };

2.3 module_driver

  1. /**
  2. * module_driver() - Helper macro for drivers that don't do anything
  3. * special in module init/exit. This eliminates a lot of boilerplate.
  4. * Each module may only use this macro once, and calling it replaces
  5. * module_init() and module_exit().
  6. *
  7. * @__driver: driver name
  8. * @__register: register function for this driver type
  9. * @__unregister: unregister function for this driver type
  10. * @...: Additional arguments to be passed to __register and __unregister.
  11. *
  12. * Use this macro to construct bus specific macros for registering
  13. * drivers, and do not use it on its own.
  14. */
  15. #define module_driver(__driver, __register, __unregister, ...) \
  16. static int __init __driver##_init(void) \
  17. { \
  18. return __register(&(__driver) , ##__VA_ARGS__); \
  19. } \
  20. module_init(__driver##_init); \
  21. static void __exit __driver##_exit(void) \
  22. { \
  23. __unregister(&(__driver) , ##__VA_ARGS__); \
  24. } \
  25. module_exit(__driver##_exit);

2.4 __platform_driver_register

        注意此处的__register是传进来的__platform_driver_register

  1. /**
  2. * __platform_driver_register - register a driver for platform-level devices
  3. * @drv: platform driver structure
  4. * @owner: owning module/driver
  5. */
  6. int __platform_driver_register(struct platform_driver *drv,
  7. struct module *owner)
  8. {
  9. drv->driver.owner = owner;
  10. drv->driver.bus = &platform_bus_type;
  11. return driver_register(&drv->driver);
  12. }
  13. EXPORT_SYMBOL_GPL(__platform_driver_register);

        对bus参数进行赋值 

  1. struct bus_type platform_bus_type = {
  2. .name = "platform",
  3. .dev_groups = platform_dev_groups,
  4. .match = platform_match,
  5. .uevent = platform_uevent,
  6. .probe = platform_probe,
  7. .remove = platform_remove,
  8. .shutdown = platform_shutdown,
  9. .dma_configure= platform_dma_configure,
  10. .dma_cleanup= platform_dma_cleanup,
  11. .pm = &platform_dev_pm_ops,
  12. };
  13. EXPORT_SYMBOL_GPL(platform_bus_type);

2.5 driver_register

  1. /**
  2. * driver_register - register driver with bus
  3. * @drv: driver to register
  4. *
  5. * We pass off most of the work to the bus_add_driver() call,
  6. * since most of the things we have to do deal with the bus
  7. * structures.
  8. */
  9. int driver_register(struct device_driver *drv)
  10. {
  11. ......
  12. other = driver_find(drv->name, drv->bus);
  13. if (other) {
  14. pr_err("Error: Driver '%s' is already registered, "
  15. "aborting...\n", drv->name);
  16. return -EBUSY;
  17. }
  18. ret = bus_add_driver(drv);
  19. ......
  20. }
  21. EXPORT_SYMBOL_GPL(driver_register);

2.6 bus_add_driver

        drv->bus->p->drivers_autoprobe默认是1,结构体定义时就赋值了

  1. struct subsys_private {
  2. ...
  3. unsigned int drivers_autoprobe:1;
  4. }
  1. /**
  2. * bus_add_driver - Add a driver to the bus.
  3. * @drv: driver.
  4. */
  5. int bus_add_driver(struct device_driver *drv)
  6. {
  7. ......
  8. if (drv->bus->p->drivers_autoprobe) {
  9. error = driver_attach(drv);
  10. if (error)
  11. goto out_del_list;
  12. }
  13. ......
  14. }

2.7 driver_attach

  1. /**
  2. * driver_attach - try to bind driver to devices.
  3. * @drv: driver.
  4. *
  5. * Walk the list of devices that the bus has on it and try to
  6. * match the driver with each one. If driver_probe_device()
  7. * returns 0 and the @dev->driver is set, we've found a
  8. * compatible pair.
  9. */
  10. int driver_attach(struct device_driver *drv)
  11. {
  12. return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  13. }
  14. EXPORT_SYMBOL_GPL(driver_attach);

2.8 bus_for_each_dev

        此函数 fn 即为  __driver_attach 函数指针,data参数 是 drv

  1. int bus_for_each_dev(struct bus_type *bus, struct device *start,
  2. void *data, int (*fn)(struct device *, void *))
  3. {
  4. struct klist_iter i;
  5. struct device *dev;
  6. int error = 0;
  7. if (!bus || !bus->p)
  8. return -EINVAL;
  9. klist_iter_init_node(&bus->p->klist_devices, &i,
  10. (start ? &start->p->knode_bus : NULL));
  11. while (!error && (dev = next_device(&i)))
  12. error = fn(dev, data);
  13. klist_iter_exit(&i);
  14. return error;
  15. }
  16. EXPORT_SYMBOL_GPL(bus_for_each_dev);

2.9 __driver_attach 

  1. static int __driver_attach(struct device *dev, void *data){
  2. ......
  3. ret = driver_match_device(drv, dev);
  4. ......
  5. ret = driver_probe_device(drv, dev);
  6. ......
  7. }
2.9.1.1 driver_match_device
  1. static inline int driver_match_device(struct device_driver *drv,
  2. struct device *dev)
  3. {
  4. return drv->bus->match ? drv->bus->match(dev, drv) : 1;
  5. }
  6. /* 返回 1 是可以继续往下走的 ret <= 0 不行*/

        可以看到在Register时有match回调 

  1. struct bus_type platform_bus_type = {
  2. ......
  3. .match = platform_match,
  4. .probe = platform_probe,
  5. ......
  6. };
2.9.1.2 platform_match
  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3. struct platform_device *pdev = to_platform_device(dev);
  4. struct platform_driver *pdrv = to_platform_driver(drv);
  5. /* When driver_override is set, only bind to the matching driver */
  6. if (pdev->driver_override)
  7. return !strcmp(pdev->driver_override, drv->name);
  8. /* Attempt an OF style match first */
  9. if (of_driver_match_device(dev, drv))
  10. return 1;
  11. /* Then try ACPI style match */
  12. if (acpi_driver_match_device(dev, drv))
  13. return 1;
  14. /* Then try to match against the id table */
  15. if (pdrv->id_table)
  16. return platform_match_id(pdrv->id_table, pdev) != NULL;
  17. /* fall-back to driver name match */
  18. return (strcmp(pdev->name, drv->name) == 0);
  19. }
2.9.1.3 of_driver_match_device
  1. /**
  2. * of_driver_match_device - Tell if a driver's of_match_table matches a device.
  3. * @drv: the device_driver structure to test
  4. * @dev: the device structure to match against
  5. */
  6. static inline int of_driver_match_device(struct device *dev,
  7. const struct device_driver *drv)
  8. {
  9. return of_match_device(drv->of_match_table, dev) != NULL;
  10. }

         of_match_table定义如下

  1. static struct platform_driver ufs_mtk_pltform = {
  2. .probe = ufs_mtk_probe,
  3. .remove = ufs_mtk_remove,
  4. .shutdown = ufshcd_pltfrm_shutdown,
  5. .driver = {
  6. .name = "ufshcd-mtk",
  7. .pm = &ufs_mtk_pm_ops,
  8. .of_match_table = ufs_mtk_of_match,
  9. },
  10. };
  11. static const struct of_device_id ufs_mtk_of_match[] = {
  12. { .compatible = "mediatek,mtxxxx-ufshci" },
  13. };
2.9.1.4 of_match_device
  1. const struct of_device_id *of_match_device(const struct of_device_id *matches,
  2. const struct device *dev)
  3. {
  4. if (!matches || !dev->of_node || dev->of_node_reused)
  5. return NULL;
  6. return of_match_node(matches, dev->of_node);
  7. }
  8. EXPORT_SYMBOL(of_match_device);
2.9.1.5 of_match_node
  1. const struct of_device_id *of_match_node(const struct of_device_id *matches,
  2. const struct device_node *node)
  3. {
  4. match = __of_match_node(matches, node);
  5. }
  6. EXPORT_SYMBOL(of_match_node);
2.9.1.6 __of_match_node
  1. static
  2. const struct of_device_id *__of_match_node(const struct of_device_id *matches,
  3. const struct device_node *node)
  4. {
  5. for (; matches->name[0] ||
  6. matches->type[0] ||
  7. matches->compatible[0]; matches++) {
  8. /* 每次循环,选择Vendor驱动中的match table结构体数组的下一个比较 */
  9. score = __of_device_is_compatible(node, matches->compatible,
  10. matches->type, matches->name);
  11. if (score > best_score) {
  12. best_match = matches;
  13. best_score = score;
  14. }
  15. }
  16. return best_match;
  17. }
2.9.1.7 __of_device_is_compatible
  1. static int __of_device_is_compatible(const struct device_node *device,
  2. const char *compat, const char *type, const char *name)
  3. {
  4. ......
  5. if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
  6. score = INT_MAX/2 - (index << 2);
  7. break;
  8. }
  9. ......
  10. }

        cp即为从设备树节点中获取的compatible信息,示例如下

  1. ufshci: ufshci@112b0000 {
  2. compatible = "mediatek,mtxxxx-ufshci";
  3. reg = <0 0x112b0000 0 0x2a00>;
  4. }
2.9.2.1 driver_probe_device
  1. static int driver_probe_device(struct device_driver *drv,
  2. struct device *dev)
  3. {
  4. ......
  5. ret = __driver_probe_device(drv, dev);
  6. ......
  7. }
2.9.2.2 __driver_probe_device

        initcall_debug是一个内核参数,可以跟踪initcall,用来定位内核初始化的问题。在cmdline中增加initcall_debug后,内核启动过程中会在调用每一个init函数前有一句打印,结束后再有一句打印并且输出了该Init函数运行的时间,通过这个信息可以用来定位启动过程中哪个init函数运行失败以及哪些init函数运行时间较长

        really_probe_debug()内部还是调用了really _probe()

  1. static int __driver_probe_device(struct device_driver *drv, struct device *dev)
  2. {
  3. ......
  4. if (initcall_debug)
  5. ret = really_probe_debug(dev, drv);
  6. else
  7. ret = really_probe(dev, drv);
  8. ......
  9. }
2.9.2.3 really_probe
  1. static int really_probe(struct device *dev, struct device_driver *drv)
  2. {
  3. ......
  4. ret = call_driver_probe(dev, drv);
  5. ......
  6. }
2.9.2.4 call_driver_probe
  1. static int call_driver_probe(struct device *dev,
  2. struct device_driver *drv)
  3. {
  4. ......
  5. if (dev->bus->probe)
  6. ret = dev->bus->probe(dev);
  7. else if (drv->probe)
  8. ret = drv->probe(dev);
  9. ......
  10. }
2.9.2.5 platform_probe

        不管走没有dev->bus->probe,最终都会走到drv->probe

  1. static int platform_probe(struct device *_dev)
  2. {
  3. struct platform_driver *drv = to_platform_driver(_dev->driver);
  4. struct platform_device *dev = to_platform_device(_dev);
  5. ......
  6. if (drv->probe) {
  7. ret = drv->probe(dev);
  8. if (ret)
  9. dev_pm_domain_detach(_dev, true);
  10. }
  11. ......
  12. }

        此时驱动匹配设备成功,会走到之前Register的probe 

  1. static struct platform_driver ufs_mtk_pltform = {
  2. .probe = ufs_mtk_probe,
  3. ......
  4. };

【参考博客】

[1] Linux设备驱动和设备匹配过程_linux驱动和设备匹配过程-CSDN博客

[2] platform 总线_怎么查询platform 总线-CSDN博客

[3] Linux Driver 和Device匹配过程分析(1)_linux设备驱动和设备树的match过程-CSDN博客

[4] Linux驱动(四)platform总线匹配过程_platform平台设备匹配过程-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/643770
推荐阅读
相关标签
  

闽ICP备14008679号