文章目录
- 1. 前言
- 2. 总线驱动模型三要素
- 2.1 总线
- 2.2 设备
- 2.3 驱动
- 3. Demo Code
- 3.1 virt_bus_core.c
- 3.2 virt_device.c
- 3.3 virt_driver.c
- 4. 工程代码下载地址
- 5. 参考资料
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/nameconst 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的namestruct 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));elsestatus = -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");
4. 工程代码下载地址
完整的实验工程Demo代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/87741928
5. 参考资料
https://blog.csdn.net/qq_16504163/article/details/118562670