文章目录
1. 前言
Linux平台为了驱动的可重用性,虚拟了很多的虚拟总线。很经典的就是platform总线,只要platform device和platform driver的名字匹配就调用driver的probe函数。
在分析内核源码时,经常会遇到各种总线。为了方便和加深理解,本篇文章写了一个虚拟Demo总线来加深对总线模型的理解。下面是总线-设备-驱动模型大致架构如下:
2. 总线驱动模型三要素
三要素主要是包括:总线、设备、驱动。
2.1 总线
- struct bus_type 结构体:
这里定义了一个name为struct bus_type { const char *name; // 总线名称,如:/sys/bus/name const struct attribute_group **bus_groups; // 总线属性 const struct attribute_group **dev_groups; // 设备属性,指向为每个加入总线的设备建立的默认属性链表 const struct attribute_group **drv_groups; // 驱动程序属性 /* match:当任何属于该Bus的device或者device_driver添加到内核时,内核都会调用该接口,如果新加的device或device_driver匹配上了自己的另一半的话,该接口要返回非零值,此时Bus模块的核心逻辑就会执行后续的处理。 */ int (*match)(struct device *dev, struct device_driver *drv); /* uevent:一个由具体的bus core层实现的回调函数。当任何属于该Bus的device,发生添加、移除或者其它动作时,Bus模块的核心逻辑就会调用该接口,以便bus core层能够修改环境变量。 */ int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); // device和driver匹配后调用,并且最终会调用到driver的probe。 int (*remove)(struct device *dev); // 设备移除调用 void (*shutdown)(struct device *dev); // 关机调用 ...... };
virt_bus
的虚拟总线,只实现了部分功能。非常重要的是virt_device_match
函数,它决定了device和driver是否匹配成功,定义如下: - 总线注册关键函数:
Demo的平台总线的注册、注销如下:int bus_register(struct bus_type *bus) // 注册一条总线 void bus_unregister(struct bus_type *bus) // 注销一条总线
注册成功后,会在/sys/bus/
目录下生成一个新的bus name:virt_bus
,如下:
2.2 设备
- struct device 结构体:
struct device // linux/device.h { struct bus_type *bus; //代表该设备挂在哪条总线上 ... }
- 设备注册关键函数:
Demo的设备注册、注销如下:int device_register(struct device *dev) // 注册一个设备 void device_unregister(struct device *dev) // 注销一个设备
(备注:dev_set_name
这个函数很关键,一定要设置device的name,否则会导致device注册失败。)
设备注册成功后,会在/sys/bus/virt_bus/devices
总线目录下生成该设备的name:tst3125-002d
,它就是通过dev_set_name
函数来设置的。效果如下:
2.3 驱动
当新的driver注册到该总线时,会遍历该总线上所有设备是否有匹配。同理,当新的device注册到该总线时,也会遍历总线上所有的驱动是否有匹配。
- struct device 结构体:
struct device_driver //device.h { const char *name; // driver的name struct bus_type *bus; //代表该驱动挂在哪条总线上 int (*probe) (struct device *dev); //探测函数 int (*remove) (struct device *dev); //移除驱动 ... }
- 驱动注册关键函数:
Demo的设备注册、注销如下:int driver_register(struct device_driver *drv) void driver_unregister(struct device_driver *drv)
驱动注册成功后,会在/sys/bus/virt_bus/drivers
总线目录下生成该驱动的name:tst3125
。如果匹配成功,就会调用driver里面的probe函数。具体效果如下:
3. Demo Code
3.1 virt_bus_core.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include "virt_bus_core.h"
extern struct device_type virt_client_type;
const struct virt_device_id *virt_match_id(const struct virt_device_id *id,
const struct virt_client *client)
{
if (!(id && client))
return NULL;
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
struct virt_client *virt_verify_client(struct device *dev)
{
return (dev->type == &virt_client_type)
? to_virt_client(dev)
: NULL;
}
static int virt_device_match(struct device *dev, struct device_driver *drv)
{
struct virt_client *client = virt_verify_client(dev);
struct virt_driver *driver;
driver = to_virt_driver(drv);
/* Finally an virt match */
if (virt_match_id(driver->id_table, client))
return 1;
return 0;
}
static int virt_device_probe(struct device *dev)
{
struct virt_client *client = virt_verify_client(dev);
struct virt_driver *driver;
int status;
if (!client)
return 0;
driver = to_virt_driver(dev->driver);
if (driver->probe)
status = driver->probe(client,
virt_match_id(driver->id_table, client));
else
status = -EINVAL;
return status;
}
static int virt_device_remove(struct device *dev)
{
struct virt_client *client = virt_verify_client(dev);
struct virt_driver *driver;
int status = 0;
if (!client || !dev->driver)
return 0;
driver = to_virt_driver(dev->driver);
if (driver->remove) {
dev_dbg(dev, "remove\n");
status = driver->remove(client);
}
return status;
}
static void virt_client_dev_release(struct device *dev)
{
kfree(to_virt_client(dev));
}
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", dev->type == &virt_client_type ?
to_virt_client(dev)->name : "unknown");
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct attribute *virt_dev_attrs[] = {
&dev_attr_name.attr,
NULL
};
ATTRIBUTE_GROUPS(virt_dev);
struct bus_type virt_bus_type = {
.name = "virt_bus",
.match = virt_device_match,
.probe = virt_device_probe,
.remove = virt_device_remove,
};
EXPORT_SYMBOL_GPL(virt_bus_type);
struct device_type virt_client_type = {
.name = "virt_client_device",
.groups = virt_dev_groups,
.release = virt_client_dev_release,
};
EXPORT_SYMBOL_GPL(virt_client_type);
struct virt_client *
virt_register_device(struct virt_board_info const *info)
{
struct virt_client *client;
int status;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return ERR_PTR(-ENOMEM);
client->addr = info->addr;
strlcpy(client->name, info->type, sizeof(client->name));
client->dev.bus = &virt_bus_type;
client->dev.type = &virt_client_type;
dev_set_name(&client->dev, "%s-%04x", info->type, info->addr);
status = device_register(&client->dev);
if (status)
goto out_err_silent;
printk("device [%s] registered\n", client->name);
return client;
out_err_silent:
kfree(client);
return ERR_PTR(status);
}
EXPORT_SYMBOL_GPL(virt_register_device);
void virt_unregister_device(struct virt_client *client)
{
if (IS_ERR_OR_NULL(client))
return;
device_unregister(&client->dev);
}
EXPORT_SYMBOL_GPL(virt_unregister_device);
int virt_register_driver(struct module *owner, struct virt_driver *driver)
{
int res;
/* add the driver to the list of virt drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &virt_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
printk("driver [%s] registered\n", driver->driver.name);
return 0;
}
EXPORT_SYMBOL(virt_register_driver);
void virt_unregister_driver(struct virt_driver *driver)
{
driver_unregister(&driver->driver);
printk("driver [%s] unregistered\n", driver->driver.name);
}
EXPORT_SYMBOL(virt_unregister_driver);
static int __init virt_bus_core_init(void)
{
int retval;
retval = bus_register(&virt_bus_type);
if (retval)
return retval;
return 0;
}
static void __exit virt_bus_core_exit(void)
{
bus_unregister(&virt_bus_type);
}
module_init(virt_bus_core_init);
module_exit(virt_bus_core_exit);
MODULE_LICENSE("GPL");
3.2 virt_device.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/string.h>
#include "virt_bus_core.h"
static struct virt_client *tst3125_client = NULL;
static int __init virt_device_tst3125_init(void)
{
static struct virt_board_info board_info = {
.type = "tst3125",
.addr = 0x2d,
};
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
tst3125_client = virt_register_device(&board_info);
return 0;
}
static void __exit virt_device_tst3125_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
virt_unregister_device(tst3125_client);
}
module_init(virt_device_tst3125_init);
module_exit(virt_device_tst3125_exit);
MODULE_LICENSE("GPL");
3.3 virt_driver.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/string.h>
#include "virt_bus_core.h"
static int tst3125_probe(struct virt_client *client, const struct virt_device_id *id)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
printk("[%s] client->name = %s id->name = %s\n", __FUNCTION__, client->name, id->name);
return 0;
}
static int tst3125_remove(struct virt_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
printk("[%s] client->name = %s\n", __FUNCTION__, client->name);
return 0;
}
static const struct of_device_id of_match_ids_tst3125[] = {
{ .compatible = "virtual,tst3125", .data = NULL },
{ /* END OF LIST */ },
};
static const struct virt_device_id tst3125_ids[] = {
{ "tst3125", (kernel_ulong_t)NULL },
{ /* END OF LIST */ }
};
static struct virt_driver virt_tst3125_driver = {
.driver = {
.name = "tst3125",
.of_match_table = of_match_ids_tst3125,
},
.probe = tst3125_probe,
.remove = tst3125_remove,
.id_table = tst3125_ids,
};
static int __init virt_driver_tst3125_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return virt_register_driver(THIS_MODULE, &virt_tst3125_driver);
}
static void __exit virt_driver_tst3125_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
virt_unregister_driver(&virt_tst3125_driver);
}
module_init(virt_driver_tst3125_init);
module_exit(virt_driver_tst3125_exit);
MODULE_LICENSE("GPL");
问题一:virt_device_match匹配函数是如何被调用的?
为了方便分析问题,在match函数中添加错误代码,通过内核报错的打印信息来分析它的调用关系。
通过错误LOG分析调用关系如下:
通过错误LOG分析调用关系如下:
4. 工程代码下载地址
完整的实验工程Demo代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/87741928