内容和参考
内容
如何加载驱动?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成功后再执行初始化操作
// include/linux/component.h
// driver/base/compponent.c
#include <linux/component.h>
// 设备接口 : 在设备的probe/remove函数执行,将自己注册到component中
int component_add(struct device *, const struct component_ops *);
void component_del(struct device *, const struct component_ops *);
// master接口
int component_bind_all(struct device *master, void *master_data);
void component_unbind_all(struct device *master, void *master_data);
void component_master_del(struct device *,
const struct component_master_ops *);
int component_master_add_with_match(struct device *,
const struct component_master_ops *, struct component_match *);
void component_match_add_release(struct device *master,
struct component_match **matchptr,
void (*release)(struct device *, void *),
int (*compare)(struct device *, void *), void *compare_data);
static inline void component_match_add(struct device *master,
struct component_match **matchptr,
int (*compare)(struct device *, void *), void *compare_data);
设备驱动框架
struct component_ops {
int (*bind)(struct device *comp, struct device *master,
void *master_data);
void (*unbind)(struct device *comp, struct device *master,
void *master_data);
};
struct component_ops xxx_comp_ops {
bind = xxx_comp_bind;
unbind = xxx_comp_unbind;
};
static int xxx_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &xxx_comp_ops);
}
static int xxx_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &xxx_comp_ops);
return 0;
}
参考:linux-4.14/drivers/gpu/drm/armada/armada_crtc.c
master驱动框架
static const struct component_master_ops xxx_master_ops = {
.bind = xxx_master_bind,
.unbind = xxx_master_unbind,
};
// 自定义匹配函数方法
static int compare_dev_name(struct device *dev, void *data)
{
const char *name = data;
return !strcmp(dev_name(dev), name);
}
// component_master_add_with_match函数注册自己到component框架中
static int xxx_drm_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
// 将所有子设备挂载到当前master中,
for (xxx);
component_match_add(&pdev->dev, &match, compare_dev_name, port);
return component_master_add_with_match(&pdev->dev, &armada_master_ops,
match);
}
// 当所有设备匹配成功后,会先调用master的bind接口来 回调所有component接口
static int xxx_master_bind(struct device *dev)
{
...
/* Now try and bind all our sub-components */
ret = component_bind_all(dev, xxx); // 这时回调所有子设备的bind接口
if (ret)
goto err_kms;
...
}
参考:linux-4.14/drivers/gpu/drm/armada/armada_drv.c
软件测试
参考: test_componet
代码中先注册了一堆虚拟平台总线设备,然后使用component来加载和使用; 当前使用非设备树环境;