做Linux做么多年,虽然说看了很多,还没有写过这边方便的博客,今天接学习的机会,来记录下同时复习下
1. 引入设备驱动模型的目的
关于linux设备驱动模型应该有很多文章和博客已经记录和阐述了,至于为什么linux要设计这么一个设备驱动的软件架构,可以看看宋宝华老师的《Linux驱动设备开发详解》第十二章
设备驱动模型有三个重要组件,分别是总线(bus type)、设备(device)和驱动(driver)
- 总线,主要起连接设备和驱动的作用
- 设备,顾名思义对应硬件上的设备
- 驱动,一套操作硬件的方法
在不使用设备驱动模型是,一个外设的驱动在特定的CPU上运行需要一套特定的驱动,那么在多个不同的CPU上就需要多个驱动,按照这种逻辑,N个同类型的外设在M个不同的CPU上就需要N*M份驱动,这种是典型的强耦合,不符合软件工程的基本原则,引用宋宝华老师书上的图片如下
使用了设备驱动模型后只需要N+M个驱动
2. 设备驱动模型分析
典型的设备驱动模型有platform设备驱动、i2c/spi驱动、块设备、input设备、tty、usb等诸多设备驱动,下面我们具体分析从platform驱动设备来理清bus、driver、device之前的关系
2.1 bus注册
汇编代码.S文件调用
kernel-5.10/init/main.c start_kernel ---->arch_call_rest_init----->rest_init---->kernel_init ---->kernel_init_freeable ---->do_basic_setup ---->driver_init ---->platform_bus_init---->bus_register
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
bool need_parent_lock;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
};
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);
}
上面是bus_register时注册的platform_bus_type,其中需要注意的是.match函数,当挂在platform总线上的driver和device匹配时会调用,会根据of_match_table、acpi、id_table、drv_name来匹配,至于bus_register函数中的其他调用有兴趣的可以自行去了解kobject/kset,这里不再赘述
相关也可以看看博客linux驱动 平台设备驱动模型,我看看写的比较详细,注意这篇博客的kernel版本和我的不一样,我这是kernel5.10
2.2 driver注册
这里已display中的panel注册为例
static struct mipi_dsi_driver panel_simple_dsi_driver = {
.driver = {
.name = "panel-simple-dsi",
.of_match_table = dsi_of_match,
},
.probe = panel_simple_dsi_probe,
.remove = panel_simple_dsi_remove,
.shutdown = panel_simple_dsi_shutdown,
};
static int __init panel_simple_init(void)
{
int err;
//注册platform设备
err = platform_driver_register(&panel_simple_platform_driver);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
if (err < 0)
return err;
}
return 0;
}
module_init(panel_simple_init);
在kernel开始运行时会调用运行.init.text代码段的函数,而运行module_init宏定义的内容,关于module_init、late_initcall、subsys_init等可以查看kernel-5.10/include/linux/module.h、init.h
platform_driver_register
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
if (!drv->bus->p) {
pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
drv->name, drv->bus->name);
return -EINVAL;
}
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
pr_warn("Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
//检查driver是否已经注册在bus上
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);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
//将knode_bus加入到bus的klist_drivers链表中
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
//调用bus中的match函数
error = driver_attach(drv);
if (error)
goto out_unregister;
}
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
/* drv->p is freed in driver_release() */
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
int ret;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
//调用bus_type中的match函数
ret = driver_match_device(drv, dev);
if (ret == 0) {
/* no match */
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev);
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d\n", ret);
return ret;
} /* ret > 0 means positive match */
if (driver_allows_async_probing(drv)) {
/*
* Instead of probing the device synchronously we will
* probe it asynchronously to allow for more parallelism.
*
* We only take the device lock here in order to guarantee
* that the dev->driver and async_driver fields are protected
*/
dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
device_lock(dev);
if (!dev->driver) {
get_device(dev);
dev->p->async_driver = drv;
async_schedule_dev(__driver_attach_async_helper, dev);
}
device_unlock(dev);
return 0;
}
//driver和device匹配完成,调用driver中的probe函数完成注册
device_driver_attach(drv, dev);
return 0;
}
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
2.3 device注册
在kernel未引入dts之前和引入dts之后的device注册时不同的
具体可参考platform_device的生成过程
总之引入设备驱动模型后,隔离了bsp和驱动,使得驱动有更好的可扩展性和跨平台性,另外一个驱动可以支持多个设备