0.前言

linux设备模型是学习linux驱动的很重要的内容。了解这些知识,对于学习linux设备驱动有很大的帮助。 linux设备类型的内容还是比较多的,这里就重点说明device_add函数的流程。

1.总体框架

linux设备模型:设备device,驱动driver,总线bus。设备代表物理设备,驱动代表了设备操作方法,bus则是用来管理和匹配它们。device和driver里面都有一个成员变量bus,表示它们归哪个总线管理,bus里面则有两个链表,device链表和driver链表。当有新的设备加入的时候,就会将它加入它对应的bus的device链表,然后在它的驱动链表中寻找是否有驱动driver和该device匹配成功,如果匹配成功设备就可以正常使用了,否则,不好意思继续等待。当有新的驱动加入的时候,就会将它加入它对应的bus的driver链表,然后在它的设备链表中寻找是否有设备device和该driver匹配成功,如果成功设备就可以正常使用了。

device_add就是将设备加入到Linux设备模型的关键,它的内部将找到它的bus,然后让它的bus给它找到它的driver,其调用顺序为:

2.1 device_add

int device_add(struct device *dev)dev = get_device(dev);//增加该设备的引用计数if (!dev->p) {error = device_private_init(dev);//初始化设备的私有成员pif (error)goto done;}if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name);//初始化设备内部的kobject的名字dev->init_name = NULL;}if (!dev_name(dev) && dev->bus && dev->bus->dev_name)dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);//使用bus以及设备id来初始化设备内部kobject名字if (!dev_name(dev)) {//获得设备的名字error = -EINVAL;goto name_error;}parent = get_device(dev->parent);增加设备父设备并增加父设备引用计数kobj = get_device_parent(dev, parent);if (kobj)dev->kobj.parent = kobj;//在kobject层实现设备父子关系if (parent)set_dev_node(dev, dev_to_node(parent));error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);//将设备加入到kobject模型中,创建sys相关目录/* notify platform of device entry */if (platform_notify)platform_notify(dev);error = device_create_file(dev, &uevent_attr);//创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件if (MAJOR(dev->devt)) {error = device_create_file(dev, &devt_attr);//创建sys目录下设备的设备号属性,即major和minorerror = device_create_sys_dev_entry(dev);devtmpfs_create_node(dev);}error = device_add_class_symlinks(dev);error = device_add_attrs(dev);//创建sys目录下设备其他属性文件error = bus_add_device(dev);//将设备加入到管理它的bus总线的设备连表上error = dpm_sysfs_add(dev);//电源管理相关device_pm_add(dev);/* Notify clients of device addition.  This call must come* after dpm_sysfs_add() and before kobject_uevent().*/if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);//通知注册监听该总线的设备,有新设备加入kobject_uevent(&dev->kobj, KOBJ_ADD);//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制bus_probe_device(dev);//------------开始寻找设备所对应的驱动------------if (parent)klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);//建立设备与总线间的父子关系if (dev->class) {//如果设备的属于某个设备类,比如Mass storage,HID等等mutex_lock(&dev->class->p->mutex);/* tie the class to the device */klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);//将设备挂接在其设备类上面/* notify any interfaces that the device is here */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->add_dev)class_intf->add_dev(dev, class_intf);//通知有新设备加入mutex_unlock(&dev->class->p->mutex);}

2.2  bus_probe_device

//为设备找到一个驱动
void bus_probe_device(struct device *dev)
{struct bus_type *bus = dev->bus;//获得设备的隶属的总线,该值在设备初始化时设置struct subsys_interface *sif;int ret;if (!bus)return;if (bus->p->drivers_autoprobe) {ret = device_attach(dev);//-------尝试为该设备找一个driver-------WARN_ON(ret < 0);}mutex_lock(&bus->p->mutex);list_for_each_entry(sif, &bus->p->interfaces, node)if (sif->add_dev)sif->add_dev(dev, sif);mutex_unlock(&bus->p->mutex);
}

2.3 device_attach

/*** device_attach - 尝试为设备寻找到一个驱动*遍历设备隶属的总线上所有的driver,然后调用driver_probe_device匹配设备和驱动,成功就结束循环退出*成功返回值为1,失败为0,-ENODEV表示设备没有被注册*/
int device_attach(struct device *dev)
{int ret = 0;device_lock(dev);if (dev->driver) {//如果设备已经有驱动if (klist_node_attached(&dev->p->knode_driver)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev);if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {//设备没有驱动pm_runtime_get_noresume(dev);ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);-------遍历总线上的driver链表-------pm_runtime_put_sync(dev);}
out_unlock:device_unlock(dev);return ret;
}

2.4 bus_for_each_drv

/*** bus_for_each_drv - driver迭代器* @bus: 设备隶属的总线* @start: 迭代器轮训的起始元素* @data: 传递给回调函数的参数* @fn: 回调函数*/
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, int (*fn)(struct device_driver *, void *))
{struct klist_iter i;struct device_driver *drv;int error = 0;if (!bus)return -EINVAL;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);//-------对于总线中的每个driver调用fn函数进行匹配,fn为__device_attach-------klist_iter_exit(&i);return error;
}

2.5  __device_attach

static int __device_attach(struct device_driver *drv, void *data)
{struct device *dev = data;if (!driver_match_device(drv, dev))//设备和驱动是否匹配函数,成功就继续下面,否则退出,将调用总线的match函数进行匹配return 0;return driver_probe_device(drv, dev);//-------设备和驱动匹配成功,调用probe函数-------
}-

2.6 driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)
{int ret = 0;if (!device_is_registered(dev))//如果设备已经被注册过了,直接退出return -ENODEV;pr_debug("bus: '%s': %s: matched device %s with driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);pm_runtime_get_noresume(dev);pm_runtime_barrier(dev);ret = really_probe(dev, drv);//-------继续调用really_probe函数-------pm_runtime_put_sync(dev);return ret;
}

2.7 really_probe

static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = 0;atomic_inc(&probe_count);pr_debug("bus: '%s': %s: probing driver %s with device %s\n",drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;//匹配好后,将驱动信息记录到设备内部if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->bus->probe) {//如果总线存在probe函数,则调用总线的probe函数ret = dev->bus->probe(dev);if (ret)goto probe_failed;} else if (drv->probe) {ret = drv->probe(dev);//如果总线中没有probe函数,则调用驱动的probe函数if (ret)goto probe_failed;}driver_bound(dev);//将设备加入到驱动支持的设备链表中,一个设备需要一个驱动,一个驱动支持多个设备ret = 1;atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;
}

在驱动或者总线的probe函数中,一般会在/dev/目录先创建相应的设备节点,这样应用程序就可以通过该设备节点来使用设备了。

Linux设备模型之device_add相关推荐

  1. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

  2. Linux设备模型(总结)

    转:http://www.360doc.com/content/11/1219/16/1299815_173418267.shtml 看了一段时间的驱动编程,从LDD3的hello wrod到后来的字 ...

  3. linux lddbus设备,Linux设备驱动程序学习(14)-Linux设备模型(各环节的整合)

    Linux设备驱动程序学习(14) -Linux设备模型(各环节的整合) 通过一个设备在内核中生命周期的各个阶段,可以更好地理解Linux设备模型.我将通过分析lddbus和sculld的源码来了解L ...

  4. linux设备模型深探

    ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...

  5. Linux设备模型之platform设备

    Linux设备模型之platform设备 1. Platform模块的软件架构 2. Platform设备 2.1 platform_device原型 2.2 注册添加device 2.2.1 pla ...

  6. linux设备模型 —— sysfs

    1 sysfs初探 "sysfs is a ram-based filesystem initially based on ramfs. It provides a means to exp ...

  7. linux设备模型--sysfs

    1 sysfs初探 "sysfs is a ram-based filesystem initially based on ramfs. It provides a means to exp ...

  8. linux设备模型:devtmpfs虚拟文件系统分析

    devtmpfs是一个设备文件系统,它将其所有文件保存在虚拟内存中. devtmpfs中的所有内容都是临时的,因为不会在您的硬盘驱动器上创建任何文件.如果卸载devtmpfs实例,其中存储的所有内容都 ...

  9. Linux设备模型——设备驱动模型和sysfs文件系统解读笔记

    Linux设备模型--设备驱动模型和sysfs文件系统解读笔记 原文:https://blog.csdn.net/yj4231/article/details/7799245 将对Linux系统中的s ...

最新文章

  1. MySQL中锁详解(行锁、表锁、页锁、悲观锁、乐观锁等)
  2. iOS runtime实用篇:让你快速上手一个项目
  3. opencv-python 视频处理之时光倒流
  4. nvm install node没反应_前端开发,你要懂得Node.js的安装和使用方法
  5. java concurrent 框架_Java Concurrent 框架图
  6. 解决只可以上QQ却不可以上网问题
  7. Linux文件浏览命令
  8. PHPStudy介绍、下载与安装
  9. 美团设计模式在外卖营销业务中的实践-学习笔记(一)
  10. 灌篮高手总决赛下载地址,都是pdf文件,黑白的,很清晰
  11. php创蓝253四要素认证_PHP调用创蓝253国际短信验证码
  12. Java自动生成编号
  13. 百度杀毒+7654联盟
  14. 深夜街头被偷拍的扎心瞬间:成年人的体面,都是易碎品
  15. 春考天津计算机知识点资料,天津春季高考统一考试计算机基础科目考试大纲
  16. 网易云音乐api,硅谷云音乐调用登录API出现,网络太拥挤,登录失败(最简单的解决方案,有效哦)
  17. mysql 格式化函数总结_Mysql字符串处理函数详细介绍、总结
  18. 百余署名AI论文被爆抄袭 智源现已致歉
  19. 【语音之家】AI产业沙龙—语音技术在贝壳的应用
  20. 关于重申快件出、入仓录单扫描、问题件处理等操作流程的通知

热门文章

  1. J1签证的“两年美国境外居住要求”是指什么?
  2. quasi-Newton method 拟牛顿法
  3. iOS设备管理器有人推荐iTunes,有人推荐iMazing,到底如何选择
  4. 生成xslx文件,写入并读取
  5. 掘金100道(2)['1', '2', '3'].map(parseInt) what why ?
  6. Ubuntu apt update无论使用什么源都出现类似的错误
  7. uni-app 在mac电脑连接安卓手机进行真机调试
  8. 使用EasyExcel从Excel表格读取链接地址下载文件
  9. Unity中的进度条(内含计数器)
  10. 机器学习核心算法各个击破