一、Linux 驱动-总线-设备模型
1、驱动分层
Linux内核需要兼容多个平台,不同平台的寄存器设计不同导致操作方法不同,故内核提出分层思想,抽象出与硬件无关的软件层作为核心层来管理下层驱动,各厂商根据自己的硬件编写驱动代码作为硬件驱动层
2、设备&总线&驱动
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举例,整个匹配过程如下
2.1 整体调用逻辑
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()
2.2 module_platform_driver
封装了一层,展开后实际上就是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,
},
};
2.3 module_driver
/**
* 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);
2.4 __platform_driver_register
注意此处的__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);
2.5 driver_register
/**
* 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);
2.6 bus_add_driver
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;
}
......
}
2.7 driver_attach
/**
* 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);
2.8 bus_for_each_dev
此函数 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);
2.9 __driver_attach
static int __driver_attach(struct device *dev, void *data){
......
ret = driver_match_device(drv, dev);
......
ret = driver_probe_device(drv, dev);
......
}
2.9.1 driver_match_device
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,
......
};
2.9.1.1 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);
}
2.9.1.2 of_driver_match_device
/**
* 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" },
};
2.9.1.3 of_match_device
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);
2.9.1.4 of_match_node
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);
2.9.1.5 __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;
}
2.9.1.6 __of_device_is_compatible
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>;
}
2.9.2 driver_probe_device
static int driver_probe_device(struct device_driver *drv,
struct device *dev)
{
......
ret = __driver_probe_device(drv, dev);
......
}
2.9.2.1 __driver_probe_device
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);
......
}
2.9.2.2 really_probe
static int really_probe(struct device *dev, struct device_driver *drv)
{
......
ret = call_driver_probe(dev, drv);
......
}
2.9.2.3 call_driver_probe
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);
......
}
2.9.2.4 platform_probe
不管走没有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,
......
};
三、设备匹配驱动过程简述
3.1 整体调用逻辑
解析设备树
|-- of_platform_default_populate_init
|-- of_platform_default_populate
|-- of_platform_populate
|-- of_platform_bus_create
|-- of_platform_device_create_pdata
|-- of_device_add
|-- device_add
|-- bus_probe_device
|-- device_initial_probe
|-- __device_attach
|-- bus_for_each_drv
|-- __device_attach_driver
|-- driver_match_device
|-- driver_probe_device
/* 自己编写module,使用Platform_device_register()也会走到device_add() */
3.2 解析设备树
在Linux kernel初始化时,会解析Bootloader传递的设备树信息,设备树中满足下列条件的节点能被转换为内核里的platform_device
(1)根节点下含有compatile属性的子节点,会转换为platform_device;
(2)含有特定compatile属性的节点的子节点,会转换为platform_device,
如果一个节点的compatile属性,它的值是这4者之一:“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”, 那么它的子结点(需含compatile属性)也可以转换为platform_device。
(3)总线I2C、SPI节点下的子节点:不转换为platform_device, 某个总线下到子节点,应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device。
3.2.1 start_kernel
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
......
setup_arch(&command_line);
......
}
3.2.2 setup_arch
void __init setup_arch(char **cmdline_p)
{
......
arch_mem_init(cmdline_p);
......
}
3.2.3 arch_mem_init
static void __init arch_mem_init(char **cmdline_p)
{
plat_mem_setup(); //1.解析设备树三个重要节点
......
device_tree_init();//2.解析所有子节点
}
3.2.4 plat_mem_setup
void __init plat_mem_setup(void)
{
......
if (loongson_fdt_blob)
__dt_setup_arch(loongson_fdt_blob);
}
3.2.4.1 __dt_setup_arch
void __init __dt_setup_arch(void *bph)
{
if (!early_init_dt_scan(bph))
return;
mips_set_machine_name(of_flat_dt_get_machine_name());
}
3.2.4.2 early_init_dt_scan
bool __init early_init_dt_scan(void *params)
{
......
status = early_init_dt_verify(params);
......
early_init_dt_scan_nodes();
}
3.2.4.3 early_init_dt_scan_nodes
解析三个对于系统非常重要的节点
void __init early_init_dt_scan_nodes(void)
{
/* chosen节点操作,将bootargs拷贝到boot_command_line指向的内存,
boot_command_line是一个全局变量 */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* 根据根节点的#address-cells属性和#size-cells属性初始化全局变量
dt_root_size_cells和dt_root_addr_cells */
of_scan_flat_dt(early_init_dt_scan_root, NULL);
/* 配置内存 起始地址,大小等 */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
3.2.5 device_tree_init
void __init device_tree_init(void)
{
......
if (early_init_dt_verify(initial_boot_params))
unflatten_and_copy_device_tree();
}
3.2.5.1 unflatten_and_copy_device_tree
void __init unflatten_and_copy_device_tree(void)
{
......
unflatten_device_tree();
}
3.2.5.2 unflatten_device_tree
void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
......
}
3.2.5.3 __unflatten_device_tree
static void __unflatten_device_tree(const void *blob,
struct device_node **mynodes,
void * (*dt_alloc)(u64 size, u64 align))
{
/* First pass, scan for size */
......
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
......
}
3.2.5.4 __unflatten_device_tree
static void * unflatten_dt_node(const void *blob,
void *mem,
int *poffset,
struct device_node *dad,
struct device_node **nodepp,
unsigned long fpsize,
bool dryrun)
{
const __be32 *p;
struct device_node *np;
struct property *pp, **prev_pp = NULL;
const char *pathp;
unsigned int l, allocl;
static int depth;
int old_depth;
int offset;
int has_name = 0;
int new_format = 0;
/* 获取node节点的name指针到pathp中 */
pathp = fdt_get_name(blob, *poffset, &l);
if (!pathp)
return mem;
allocl = ++l;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
* it later. We detect this because the first character of the name is
* not '/'.
*/
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) {
/* root node: special case. fpsize accounts for path
* plus terminating zero. root node only has '/', so
* fpsize should be 2, but we want to avoid the first
* level nodes to have two '/' so we use fpsize 1 here
*/
fpsize = 1;
allocl = 2;
l = 1;
pathp = "";
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
*/
fpsize += l;
allocl = fpsize;
}
}
/* 分配struct device_node内存,包括路径全称大小 */
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
/* 填充full_name,full_name指向该node节点的全路径名称字符串 */
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
if (dad && dad->parent) {
strcpy(fn, dad->full_name);
fn += strlen(fn);
}
*(fn++) = '/';
}
memcpy(fn, pathp, l);
/* 节点挂接到相应的父节点、子节点和姊妹节点 */
prev_pp = &np->properties;
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
/* 处理该node节点下面所有的property */
for (offset = fdt_first_property_offset(blob, *poffset);
(offset >= 0);
(offset = fdt_next_property_offset(blob, offset))) {
const char *pname;
u32 sz;
if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
offset = -FDT_ERR_INTERNAL;
break;
}
if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
}
if (strcmp(pname, "name") == 0)
has_name = 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
if (!dryrun) {
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both
* appear and have different values, things
* will get weird. Don't do that. */
/* 处理phandle,得到phandle值 */
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
np->phandle = be32_to_cpup(p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
np->phandle = be32_to_cpup(p);
pp->name = (char *)pname;
pp->length = sz;
pp->value = (__be32 *)p;
*prev_pp = pp;
prev_pp = &pp->next;
}
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
/* 为每个node节点添加一个name的属性 */
if (!has_name) {
const char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;
/* 属性name的value值为node节点的名称,取“/”和“@”之间的子串,设备和驱动的别名匹配用的就是这个地方的name */
while (*p1) {
if ((*p1) == '@')
pa = p1;
if ((*p1) == '/')
ps = p1 + 1;
p1++;
}
if (pa < ps)
pa = p1;
sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property));
if (!dryrun) {
pp->name = "name";
pp->length = sz;
pp->value = pp + 1;
*prev_pp = pp;
prev_pp = &pp->next;
memcpy(pp->value, ps, sz - 1);
((char *)pp->value)[sz - 1] = 0;
}
}
/* 填充device_node结构体中的name和type成员 */
if (!dryrun) {
*prev_pp = NULL;
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "<NULL>";
if (!np->type)
np->type = "<NULL>";
}
old_depth = depth;
*poffset = fdt_next_node(blob, *poffset, &depth);
if (depth < 0)
depth = 0;
/* 递归调用node节点下面的子节点 */
while (*poffset > 0 && depth > old_depth)
mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
fpsize, dryrun);
if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
pr_err("unflatten: error %d processing FDT\n", *poffset);
/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun && np->child) {
struct device_node *child = np->child;
np->child = NULL;
while (child) {
struct device_node *next = child->sibling;
child->sibling = np->child;
np->child = child;
child = next;
}
}
if (nodepp)
*nodepp = np;
return mem;
}
3.3 设备发起匹配
如果不是走的platform_device_register(),那么会走下面的流程,从device_add()开始后面就是一样的
3.3.1 of_platform_default_populate_init
此函数就是为了在设备树解析出来后进行驱动匹配的,会在内核初始化阶段调用
static int __init of_platform_default_populate_init(void)
{
...
...
/* Populate everything else. */
of_platform_default_populate(NULL, NULL, NULL);
...
...
}
arch_initcall_sync(of_platform_default_populate_init);
arch_initcall_sync(of_platform_default_populate_init);
start_kernel
|-- rest_init();
|-- pid = kernel_thread(kernel_init, NULL, CLONE_FS);
|-- kernel_init
|-- kernel_init_freeable();
|-- do_basic_setup();
|-- do_initcalls();
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level); // do_initcall_level(3)
for (fn = initcall_levels[3]; fn < initcall_levels[3+1]; fn++)
do_one_initcall(initcall_from_entry(fn)); /*就是调用"arch_initcall_sync*/
3.3.2 of_platform_default_populate
int of_platform_default_populate(struct device_node *root,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
return of_platform_populate(root, of_default_bus_match_table, lookup,
parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
3.3.3 of_platform_populate
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
......
//遍历根节点下的每一个子设备节点并把device_node的信息填充到创建platform_device中
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
......
}
EXPORT_SYMBOL_GPL(of_platform_populate);
3.3.4 of_platform_bus_create
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
......
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
}
3.3.5 of_platform_device_create_pdata
static struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
......
//of_device_add函数就是把platform_device用平台总线去匹配驱动了
if (of_device_add(dev) != 0) {
platform_device_put(dev);
goto err_clear_flag;
}
}
3.3.6 of_device_add
int of_device_add(struct platform_device *ofdev)
{
......
return device_add(&ofdev->dev);
}
3.3.7 device_add
int device_add(struct device *dev)
{
......
bus_probe_device(dev);
}
EXPORT_SYMBOL_GPL(device_add);
3.3.8 bus_probe_device
void bus_probe_device(struct device *dev)
{
......
if (bus->p->drivers_autoprobe)
device_initial_probe(dev);
......
}
3.3.9 device_initial_probe
void device_initial_probe(struct device *dev)
{
__device_attach(dev, true);
}
3.3.10 __device_attach
static int __device_attach(struct device *dev, bool allow_async)
{
......
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
}
3.3.11 bus_for_each_drv
遍历bus上的driver进行匹配
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
......
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);
3.3.12 __device_attach_driver
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
......
ret = driver_match_device(drv, dev);
/* 匹配成功调用platform_driver的probe函数进行硬件的初始化动作 */
return driver_probe_device(drv, dev);
}
此后,函数的内容就和驱动匹配设备时流程一致了,先判断是否match,然后调用probe
【参考博客】
[1] Linux设备驱动和设备匹配过程_linux驱动和设备匹配过程-CSDN博客
[2] platform 总线_怎么查询platform 总线-CSDN博客
[3] Linux Driver 和Device匹配过程分析(1)_linux设备驱动和设备树的match过程-CSDN博客
[4] Linux驱动(四)platform总线匹配过程_platform平台设备匹配过程-CSDN博客