内容和参考

内容

如何加载驱动?insmod xxx.ko 或者 modprobe xxx.ko
驱动中如何匹配? 设备树 / platform_device 匹配 platform_driver

那么引来一个问题:
如何有顺序的完整的安装驱动?
有顺序的安装驱动方式可以有:

  • initcall级别修改(如果initcall级别一样,那么静态编译进内核时 Makefile的顺序影响了加载顺序,

因为编译后 模块按照顺序依次放在init 代码段中, 内核启动后会依次加载驱动);

  • initcall级别不一致,比如device_initcall 和 device_initcall_late
  • component组件

component组件不只可以确定启动顺序, 而且可以在模块内所有模块准备好后,依次安装;

参考

component框架

component的作用

component系统架构的提出就是出于这样一种现实需求:按顺序完整的构建功能系统!

所以component架构构建功能系统就包括两方面的作用:

  • 保证系统安装了所有的组件。
  • 规定了系统各组件初始化的顺序。

component架构在linux内核中现在主要的应用是用来构建display-subsystem (drm子系统中)


系统就是各种功能单元(组件)的有序结合。一个系统,只有集齐了它的所有组件才能正常工作。

可以打个比方:

  • 一辆车,只装上发动机,或变速箱。是不能工作的,必须安装了所有的组件,才能开始发动。
  • 而发动的过程,也是有顺序要求的,如先采离合,再挂当(变速箱初始化),再踩油门(发动机初始化),车轮再开始转动。(有序)

component的组成

在component中,包含两个基本概念,master和component。

  • master是设备树中的“超级设备(superdevice)”,负责管理该超级设备下的普通设备。component是由master管理的普通设备,要先初始化。
  • component是普通的设备节点,其下有与master的prots属性值一样的节点

component软件

初始化分为两部分:
master即超级设备,执行probe函数时使用component_master_add_with_match函数注册自己到component框架中。
component即普通设备,执行probe函数时使用component_add函数注册自己到component框架中。
两种流程先后顺序并无要求,可随意顺序。每一个设备加入到框架中,框架就尝试进行匹配,当master匹配上所有component后,会调用master的bind回调,开始按顺序进行初始化。保证了当所有子设备全部probe成功后再执行初始化操作

  1. // include/linux/component.h
  2. // driver/base/compponent.c
  3. #include <linux/component.h>
  4. // 设备接口 : 在设备的probe/remove函数执行,将自己注册到component中
  5. int component_add(struct device *, const struct component_ops *);
  6. void component_del(struct device *, const struct component_ops *);
  7. // master接口
  8. int component_bind_all(struct device *master, void *master_data);
  9. void component_unbind_all(struct device *master, void *master_data);
  10. void component_master_del(struct device *,
  11. const struct component_master_ops *);
  12. int component_master_add_with_match(struct device *,
  13. const struct component_master_ops *, struct component_match *);
  14. void component_match_add_release(struct device *master,
  15. struct component_match **matchptr,
  16. void (*release)(struct device *, void *),
  17. int (*compare)(struct device *, void *), void *compare_data);
  18. static inline void component_match_add(struct device *master,
  19. struct component_match **matchptr,
  20. int (*compare)(struct device *, void *), void *compare_data);

设备驱动框架

  1. struct component_ops {
  2. int (*bind)(struct device *comp, struct device *master,
  3. void *master_data);
  4. void (*unbind)(struct device *comp, struct device *master,
  5. void *master_data);
  6. };
  7. struct component_ops xxx_comp_ops {
  8. bind = xxx_comp_bind;
  9. unbind = xxx_comp_unbind;
  10. };
  11. static int xxx_probe(struct platform_device *pdev)
  12. {
  13. return component_add(&pdev->dev, &xxx_comp_ops);
  14. }
  15. static int xxx_remove(struct platform_device *pdev)
  16. {
  17. component_del(&pdev->dev, &xxx_comp_ops);
  18. return 0;
  19. }

参考:linux-4.14/drivers/gpu/drm/armada/armada_crtc.c

master驱动框架

  1. static const struct component_master_ops xxx_master_ops = {
  2. .bind = xxx_master_bind,
  3. .unbind = xxx_master_unbind,
  4. };
  5. // 自定义匹配函数方法
  6. static int compare_dev_name(struct device *dev, void *data)
  7. {
  8. const char *name = data;
  9. return !strcmp(dev_name(dev), name);
  10. }
  11. // component_master_add_with_match函数注册自己到component框架中
  12. static int xxx_drm_probe(struct platform_device *pdev)
  13. {
  14. struct component_match *match = NULL;
  15. // 将所有子设备挂载到当前master中,
  16. for (xxx);
  17. component_match_add(&pdev->dev, &match, compare_dev_name, port);
  18. return component_master_add_with_match(&pdev->dev, &armada_master_ops,
  19. match);
  20. }
  21. // 当所有设备匹配成功后,会先调用master的bind接口来 回调所有component接口
  22. static int xxx_master_bind(struct device *dev)
  23. {
  24. ...
  25. /* Now try and bind all our sub-components */
  26. ret = component_bind_all(dev, xxx); // 这时回调所有子设备的bind接口
  27. if (ret)
  28. goto err_kms;
  29. ...
  30. }

参考:linux-4.14/drivers/gpu/drm/armada/armada_drv.c

软件测试

参考: test_componet
代码中先注册了一堆虚拟平台总线设备,然后使用component来加载和使用; 当前使用非设备树环境;