对于驱动工程师而言,在移植porting对应设备的driver时,要在devicetree中增加对应的设备节点,其中有一个compatible属性,这个属性的字符串要和driver里面的of_device_id.compatible字符串要一致才能匹配调用驱动probe函数。 那device和driver是如何匹配?device,driver匹配后三者之间的数据结构联系是什么样的?

这是三者之间的联系图,后面我们从platform bus注册,platform_device创建注册和platform_driver注册。在其注册过程中产生如下的关系

图一

1.platform bus注册时主要做了什么

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,
};int __init platform_bus_init(void)
{int error;early_platform_cleanup();error = device_register(&platform_bus);if (error) {put_device(&platform_bus);return error;}error =  bus_register(&platform_bus_type);if (error)device_unregister(&platform_bus);of_platform_register_reconfig_notifier();return error;
}int bus_register(struct bus_type *bus)
{int retval;struct subsys_private *priv;struct lock_class_key *key = &bus->lock_key;priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);   //先分配struct subsys_private空间if (!priv)return -ENOMEM;priv->bus = bus;    //指向busbus->p = priv;       //把bus的p指针指向struct subsys_privateBLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);   //把bus的name赋值给subsys.kobj,用于后面再/sys/bus/下面创建platform文件夹if (retval)goto out;priv->subsys.kobj.kset = bus_kset;  //给subsys.kob指定bus_kset, 由于没由给subsys.kobj指定parent,所以后面subsys.kobj->parent会指向bus_kset.kobjpriv->subsys.kobj.ktype = &bus_ktype; //主要是这种类型的release的回调函数,和sysfs_opspriv->drivers_autoprobe = 1;  //允许增加device或driver时自动匹配proberetval = kset_register(&priv->subsys);  //在前面的bus_kset.kobj下面创建文件夹,这里就是上面的bus_kset,也就是对应/sys/bus文件夹if (retval)goto out;retval = bus_create_file(bus, &bus_attr_uevent); //在/sys/bus/platform下创建uevent属性文件if (retval)goto bus_uevent_fail;priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);   //在/sys/bus/platform下创建devices文件夹用于存放platform_deviceif (!priv->devices_kset) {retval = -ENOMEM;goto bus_devices_fail;}priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);   //在/sys/bus/platform下创建drivers文件夹用于存放platform_driverif (!priv->drivers_kset) {retval = -ENOMEM;goto bus_drivers_fail;}INIT_LIST_HEAD(&priv->interfaces);__mutex_init(&priv->mutex, "subsys mutex", key);klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //初始化bus的存放devices的链表,用于存放deviceklist_init(&priv->klist_drivers, NULL, NULL); //初始化bus的存放drivers的链表,用于存放driverretval = add_probe_files(bus);  //在/sys/bus/platform下创建drivers_autoprobe和drivers_probe两个属性文件if (retval)goto bus_probe_files_fail;retval = bus_add_groups(bus, bus->bus_groups);  //这一块没看懂,但是理论上就是在对应的platform/devices/xxx/创建modalias和driver_overrideif (retval)goto bus_groups_fail;pr_debug("bus: '%s': registered\n", bus->name);return 0;
}

注册platform bus,最主要的事情,应该有一下几点:
1). 初始化bus的存放klist_devices的链表,用于链接platform_device.dev.p->knode_bus
2.) 初始化bus的存放klist_drivers的链表,用于链接platform_driver.device_driver.p->knode_bus
3.) 注册platform_match回调函数,用于platform_device, platform_driver的匹配函数

2.platform_device创建注册

由于流程比较长,这里先上图

图二

platform_device初始化

static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,void *platform_data,struct device *parent)
{struct platform_device *dev;if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;
/** of_device_alloc主要给platform_device分配空间,调用了device_initialize,* 并初始化了一些resource包含irq, reg,并将resource赋值给platform_device->resource*/dev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);if (!dev->dev.dma_mask)dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
/** 注意这里,这里将platform_device->dev.bus赋值为platform_bus_type, 后面* platform_device, platform_driver就是根据这里的bus调用对应的platform_match函数*/dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data;of_msi_configure(&dev->dev, dev->dev.of_node);if (of_device_add(dev) != 0) {platform_device_put(dev);goto err_clear_flag;}return dev;err_clear_flag:of_node_clear_flag(np, OF_POPULATED);return NULL;
}

根据上面图二的调用流程,我们解下来简单分析一下device_add函数

//下面函数有精简
int device_add(struct device *dev)
{struct device *parent;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL, fw_ret;struct kobject *glue_dir = NULL;dev = get_device(dev);if (!dev)goto done;if (!dev->p) {
/** 初始化device.p,后续会用到p->knode_driver, p->knode_bus,分别用来关联driver, bus*/error = device_private_init(dev);if (error)goto done;}/* notify platform of device entry */if (platform_notify)platform_notify(dev);error = device_create_file(dev, &dev_attr_uevent);if (error)goto attrError;error = device_add_class_symlinks(dev);if (error)goto SymlinkError;error = device_add_attrs(dev);if (error)goto AttrsError;
/** 将dev->p->knode_bus加入到bus的&bus->p->klist_devices链表里面去,这就将device和bus关* 联起来了*/error = bus_add_device(dev);if (error)goto BusError;/** 下面是从bus的klist_drivers链表里面每个节点的driver进行匹配,匹配上后调用driver的probe*/bus_probe_device(dev);if (parent)klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);done:put_device(dev);return error;}

接着我们跳到driver_match_device函数

static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{
/** 能走到这里的前提是platform_driver已经注册到了platform bus上去了,那么这里先看流程。* platform_driver注册的时候platform_driver.drv->bus已经赋值为platform_bus_type,所以* 调用bus->match函数就会调用到platform_bus_type.match, 也就是platform_match。*/return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

上面就match完成了,解下来要调用platform_driver的probe函数。下面看看调用probe前做了哪些事。

//下面这个函数略有精简
static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = -EPROBE_DEFER;int local_trigger_count = atomic_read(&deferred_trigger_count);bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&!drv->suppress_bind_attrs;re_probe://这里将device->driver指向匹配的device_driver, 如果probe失败下面会赋值为NULLdev->driver = drv;/* If using pinctrl, bind pins now before probing */
/** 会从pinctrl_list里面获取device对应的pinctrl, 如果存在"init"名称的pinctrl, 那么就选择* 设置“init”的pinctrl模式。如不存在"init"的pinctrl, 就选择设置为"default"名字的pinctrl*/ret = pinctrl_bind_pins(dev);if (ret)goto pinctrl_bind_failed;
//dma配置相关的ret = dma_configure(dev);if (ret)goto probe_failed;if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->pm_domain && dev->pm_domain->activate) {ret = dev->pm_domain->activate(dev);if (ret)goto probe_failed;}
// 对于platform_bus_type这里的probe是没有赋值的,所以不会走这个分支if (dev->bus->probe) {BOOTPROF_TIME_LOG_START(ts);ret = dev->bus->probe(dev);BOOTPROF_TIME_LOG_END(ts);bootprof_probe(ts, dev, drv, (unsigned long)dev->bus->probe);if (ret)goto probe_failed;} else if (drv->probe) {BOOTPROF_TIME_LOG_START(ts);
/* 具体platform_driver的probe是有赋值的,这里device_driver->probe会回调到platform_driver的 * Probe。后面分析platform_driver注册的时候,再来看看。*/ret = drv->probe(dev);BOOTPROF_TIME_LOG_END(ts);bootprof_probe(ts, dev, drv, (unsigned long)drv->probe);if (ret)goto probe_failed;}pinctrl_init_done(dev);/* * 将&dev->p->knode_driver放到driver的dev->driver->p->klist_devices链表中,这样device和 * driver就建立了联系*/driver_bound(dev);ret = 1;pr_debug("bus: '%s': %s: bound device %s to driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);goto done;ret = 0;
done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;
}

上面platform_device整个注册流程就走完了。

3.platform_driver的注册流程

图三

下面看看platform_driver的注册流程

#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;
//这里直接将platform_driver->driver.bus直接赋值为platform_bus_typedrv->driver.bus = &platform_bus_type;
//将platform_driver->driver.probe 直接赋值为platform_drv_probedrv->driver.probe = platform_drv_probe;drv->driver.remove = platform_drv_remove;drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);
}static int platform_drv_probe(struct device *_dev)
{struct platform_driver *drv = to_platform_driver(_dev->driver);struct platform_device *dev = to_platform_device(_dev);int ret;ret = of_clk_set_defaults(_dev->of_node, false);if (ret < 0)return ret;ret = dev_pm_domain_attach(_dev, true);if (ret)goto out;
//如果platform_driver->probe有赋值的,那么直接调用platform_driver->probe, 这里就是上面讲relly_probe时说的,会回调到platform_driver->probeif (drv->probe) {ret = drv->probe(dev);if (ret)dev_pm_domain_detach(_dev, true);}out:if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {dev_warn(_dev, "probe deferral not supported\n");ret = -ENXIO;}return ret;
}
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))printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);
//到对应的bus上找又没有名字叫drv->name,如存在则说明已经这个driver在该类型的bus上已经注册上了other = driver_find(drv->name, drv->bus);if (other) {printk(KERN_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;
}

根据图三函数调用流程__platform_driver_register->driver_register, 来看一下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);
//这里初始化driver_private,driver->p priv = kzalloc(sizeof(*priv), GFP_KERNEL);if (!priv) {error = -ENOMEM;goto out_put_bus;}
//初始化klist_devices,后面用来存放对应的device->p->knode_driver节点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;
//这里讲drvier和bus建立链接,把driver->p->knode_bus加入到了bus的bus->p->klist_drivers链表中klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);if (drv->bus->p->drivers_autoprobe) {if (driver_allows_async_probing(drv)) {pr_debug("bus: '%s': probing driver %s asynchronously\n",drv->bus->name, drv->name);async_schedule(driver_attach_async, drv);} else {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;
}

下面看一下__driver_attach, driver_attach->__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.*/
//这里还是调用drv->bus->match(dev, drv),也就是调用到platform_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", ret);return ret;} /* ret > 0 means positive match */if (dev->parent && dev->bus->need_parent_lock)device_lock(dev->parent);device_lock(dev);if (!dev->p->dead && !dev->driver)
//这函数里面会调用really_probe,这个就和前面注册device是一样,就不往下分析了driver_probe_device(drv, dev);device_unlock(dev);if (dev->parent && dev->bus->need_parent_lock)device_unlock(dev->parent);return 0;
}

设备驱动模型:device, bus, driver之间的联系相关推荐

  1. linux设备驱动模型 - device/bus/driver

    在linux驱动模型中,为了便于管理各种设备,我们把不同设备分别挂在他们对应的总线上,设备对应的驱动程序也在总线上找,这样就提出了deivce-bus-driver的模型,硬件上有许多设备总线,那么我 ...

  2. linux内核组件分析之--设备驱动模型之bus

    前面我们分析了设备驱动模型中的device和driver,device和driver本来是不相关的东西,只因为bus的存在,才被联系到了一起.本节就来看看设备驱动模型中起枢纽作用的bus.本节的头文件 ...

  3. linux驱动-设备驱动模型(driver驱动)

    文章目录 1.数据结构 1) device_driver 2) driver_private 2.driver的注册 3.driver_register 总结 1) 在sys/创建对应节点 2) 匹配 ...

  4. linux i2c adapter 增加设备_LINUX设备驱动模型分析之四 设备模块相关(DEVICE)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> <LINUX设备驱动模型分析之三 ...

  5. linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...

  6. Linux内核部件分析 设备驱动模型之driver ---mark 详细

    Linux内核部件分析 设备驱动模型之driver 转载:https://www.linuxidc.com/Linux/2011-10/44627p7.htm 上节我们分析设备驱动模型中的device ...

  7. LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析

    上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device ...

  8. 【Bus】编写一个Demo虚拟的总线-设备-驱动模型

    文章目录 1. 前言 2. 总线驱动模型三要素 2.1 总线 2.2 设备 2.3 驱动 3. Demo Code 3.1 virt_bus_core.c 3.2 virt_device.c 3.3 ...

  9. Linux SPI总线设备驱动模型详解

    随着技术不断进步,系统的拓扑结构越来越复杂,对热插拔.跨平台移植性的要求越来越高,早期的内核难以满足这些要求,从linux2.6内核开始,引入了总线设备驱动模型.其实在linux2.4总线的概念就已经 ...

最新文章

  1. 物联网技术与应用(第1-2课时)(cont.)
  2. RecyclerView的基本使用
  3. Haproxy+Heartbeat 高可用集群方案操作记录
  4. MySQL触发器简介
  5. SAP中批量更改凭证行项目方法
  6. mysql和oracle的锁_关于数据库行锁与表锁的认识
  7. OpenCV gapi模块API的引用(附完整代码)
  8. 深入理解PHP的运行模式
  9. 深度学习(三)转-可视化理解卷积神经网络 直接查看卷积神经网络的过程特征...
  10. librtmp分析(发送数据包处理)
  11. php查到的内容追加到html,javascript - 请问php中如何将查询出来的结果数组转化成自己想要的格式,并在前台利用js输出到html中...
  12. win10录屏_截屏、录屏这种小事,Win10自带功能比微信QQ强 N 倍!
  13. chrome浏览器使用console代码让115网盘免扫二维码登陆
  14. 思考-IT行业设备分销代理商的运营模式
  15. 网宿cdn api 刷新缓存函数
  16. unity material之tiling和offset属性
  17. be idle sometimes to_一生中不该错过的经典语录,深刻有道理,看了让人爱不释手!...
  18. c# ascii转换方法
  19. 有没有测试牙齿需不需要修正的软件,三步图测法,就能知道自己牙齿是否需要矫正...
  20. oppo实现appium 自动化测试

热门文章

  1. Git和jdk1.8百度云盘分享
  2. 景联文科技—专业数据标注公司和智能数据标注平台
  3. 调光器(Dimmer Switch)调光理论和原理
  4. ubuntu -swap
  5. 【Jupyter】ipynb转换成pdf文件
  6. ZooKeeper典型应用
  7. Gradle重新安装后下载插件失败 Read timed out 问题解决
  8. 简谈命令执行漏洞绕过过滤
  9. vmware中的CPU占用率100%
  10. VS开发利器-Visual Assist X