本文将详细讲述2.6.22 下的一个USB 设备插上linux 系统的PC 后是如何一步一步调到我们的usb 设备驱动的probe 函数的,我们知道我们的USB 驱动的probe 函数中的一个参数是interface 结构,因此一般来说,一个USB 设备中的任何一个接口都应该有对应的一个驱动程序, 当然也有例外( 如cdc-acm).

我们知道USB 设备都是通过插入上层HUB 的一个Port 来连入系统并进而被系统发现的,当USB 设备插入一个HUB 时, 该HUB 的那个port 的状态就会改变,从而系统就会知道这个改变,此时会调用hub_port_connect_change()  /*driver/usb/core/hub.c*/

static void hub_connect_change(struct usb_hub *hub, int portl, u16 portstatus, u16 portchange)

{

….

usb_new_device(udev);

}

该函数创建一个usb_device 的对象udev,并初始化它, 接着调用usb_new_device() 来获取这个usb 设备的各种描述符并为每个interface 找到对用的driver.

int usb_new_device(struct usb_device *udev)

{

….

err = usb_get_configuration(udev);

….

device_add(&udev->dev);

}

该函数首先调用usb_get_configuration() 来获取设备的各种描述符( 设备描述符, 配置描述符等), 接着调用device_add() 来把这个USB 设备添加到USB 系统中去,也就是在这个过程中系统回去为这个设备找到相应的驱动.在2.6 的早期的一些版本中在分析配置描述符后得到interface 的同时把interface 作为设备来调用device_add() 的

int device_add(struct device *dev)

{

….

if((error = bus_add_device(dev)))

bus_attach_device(dev);

}

这个函数是个通用的设备管理函数, 它会为每个设备调用bus_add_device 来把这个设备添加到相应bus 的设备列表中去.接着调用bus_attach_device() 来匹配对应的驱动程序,对于USB 设备来说第一次调用bus_attach_device() 时的参数dev 代表的是整个usb 设备( 以后usb 设备中的interface 也会作为设备调用这个函数).

int bus_attach_device(struct device *dev)

{

ret = device_attach(dev);

}

这个函数就是用来为设备找到相应的设备驱动程序的( 通过调用device_attach() 实现).

int device_attach(struct device *dev)

{

ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

}

该函数调用bus_for_each_drv() 来从总线上已注册的所有驱动中找出匹配的驱动程序.

int bus_for_each_drv(struct bus_type *bus,

struct device_driver *start,

void *data,

int (*fn)(struct device_driver *, void *))

{

….

while((drv = next_driver(&i)) && !error)

error = fn(drv, data);  // 返回0 将继续搜索, 返回错误值将停止搜索.

}

该函数遍历bus 上的所有驱动程序, 并为每个驱动调用fn() 来查看是否匹配.这里的fn 就是__device_attach.

static int __device_attach(struct device_driver *drv, void *data)

{

struct device *dev = data;

return driver_probe_device(drv, dev);

}

int driver_probe_device(struct device *drv, struct device *dev)

{

if(drv->bus->match && !drv->bus_match(dev, drv))

ret = really_probe(dev, drv);

}

对于usb 驱动来说, 我们通过usb_registe()r 来注册我们的驱动程序, 这个函数会为我们的驱动程序对象(usb_driver) 中的bus 指定为usb_bus_type:

Struct bus_type usb_bus_type = {

.match = usb_device_match,

….

}

因此对于usb 驱动会首先调用usb_device_match().

static int usb_device_match(struct device *dev, struct device_driver *drv)

{

if(is_usb_device(dev)) {  /*dev 代表整个usb 设备*/

….

}

else   /*dev 代表一个usb 设备interface*/

{

usb_match_id();

usb_match_dynamic_id();

}

}

这个函数只是做一些粗略的匹配,如果匹配成功则返回1, 然后由really_probe 来做进一步的匹配,如果匹配失败则返回0,并且really_probe 也不会在执行.这个函数的调用保证了dev, drv 要么都是设备级别的( 即dev 代表usb 设备,drv 代表usb 设备驱动),要么都是接口级别的( 即dev 代表usb 设备的一个interface,drv 代表usb 接口驱动).

static int really_probe(struct device *dev, struct device_driver *drv)

{

dev->driver = drv;  // 先赋值, 以后的probe 过程中会用到

else if(drv->probe)

ret = drv->probe(dev);

probe_failed:

dev->drvier = NULL;  //probe 失败,重设它

}

对于usb 来说这个函数的调用有2 种分支, 1: dev,drv 代表的是设备级别的, 2 dev,drv 代表的是接口级别的.其他情况组合在usb_device_match 中被过滤掉了,

分支1: dev,drv 代表的是设备级别:

此时的drv 肯定是usb_generic_driver.因为在当前的usb 系统中只有这个driver 是代表整个设备的驱动, 它是在usb_init 中被注册的,而我们通常写的usb 驱动都是代表一个interface 的.

struct usb_device_driver usb_generic_driver = {

.probe = generic_probe,

}

因此, 此时的drv->probe 将调用generic_probe().

static int generic_probe(struct usb_device *udev)

{

c = choose_configuration(dev);

if(c >= 0) {

err = usb_set_configuration(udev, c);  // 设置配置, 并注册interface.

}

}

该函数为这个usb 设备选择一个合适的配置, 并注册这个配置下面的interface.

int usb_set_configuration(struct usb_device *dev, int configuration)

{

for(I = 0; I < nintf; i++) {

struct usb_interface *intf = cp->interface[i];

device_add(&intf->dev);

}

}

该函数比较重要,但我们只关心probe 过程因此省掉了很多东西.它为当前配置下的每个interface 调用device_add() 函数,根据前面的分析可知,这个过程将会走到接下来我们要分析的分支2.

分支2: dev,drv 代表的是interface 级别:

此时的dev 代表着一个interface,而drv 就代表了我们自己的usb 驱动.但是我们应当看到drv 是device_driver 类型,而我们写的usb 驱动的类型一般是usb_driver,因此这里的probe 和我们自己写的probe 显然不是同一个.实际上这里的drv 是我们的驱动对象里内嵌的一个子对象( 因为linux 下所以的驱动都必须用device_driver 来代表,).那这个子对象的probe 函数是在哪里赋值的呢?这就要看usb_register 函数了,

跟踪这个函数我们可以看到这里的probe 函数实际上是usb_probe_interface( 所有的usb interface 驱动都是一样的).

static int usb_probe_interface(struct device *dev)

{

struct driver = to_usb_driver(dev->driver);  //dev->driver 在really_probe 中设置.

error = driver->probe(intf, id);   // 这个就是我们自己写的probe 函数了.

}

driver->probe(intf, id);这就调用到我们自己写的代码里面了,

整个流程大概就是这样:

linux 设备驱动程序 过滤,Linux驱动发开,usb设备的probe全过程相关推荐

  1. 嵌入式linux查看usb设备驱动程序,嵌入式Linux下USB驱动程序的设计

    嵌入式Linux下USB驱动程序的设计 usb概念:  USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不 ...

  2. linux u 驱动程序,在uClinux中增加自己的设备驱动程序

    驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另一种是编译成模块以供动态加载.由于 uClinux不支持模块动态加载,而且嵌入式Linux不能够象桌面Linux那样灵活的使用insmod/ ...

  3. linux红外驱动程序,基于Linux操作系统和红外发射器实现系统模块的设计

    引 言 随着嵌入式系统及集成电路技术的飞速发展,针对移动手持终端的专用芯片获得了长足发展.芯片的RAM和ROM的容量越大,在上面跑操作系统也越来越容易.Linux是当今流行的操作系统之一.由于其内核健 ...

  4. linux gpio设备驱动程序,嵌入式Linux设备驱动开发之:GPIO驱动程序实例-嵌入式系统-与非网...

    11.3  GPIO驱动程序实例 11.3.1  GPIO工作原理 FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO 8个端口组,分别为GPA(2 ...

  5. 从零开始之驱动发开、linux驱动(二十三、platform总线之数据驱动分离)

    本节开始引入总线概念. 总线是一种虚拟的概念,不针对任何具体的外设,但是它可以比较好的管理外设. 总线对外设的管理从设备和驱动两个方面说明. 比如我们有3个led灯要控制,一种是向我们之前的那样在软件 ...

  6. 从零开始之驱动发开、linux驱动(四十四、虚拟网卡驱动)

    网卡驱动的书写格式很简单 1.申请一个网卡设备结构体 2.设置这个结构体,硬件相关初始化 3.注册这个网卡设备 参考的韦东山老师的视屏,代码如下 #include <linux/module.h ...

  7. 从零开始之驱动发开、linux驱动(四十七、linux下的IIC框架【1】)

    I2C总线仅仅使用SCL. SDA这两根信号线就实现了设备之间的数据交互, 极大地简化了对硬件资源和PCB板布线空间的占用. 因此, I2C总线非常广泛地应用在EEPROM. 实时钟. 小型LCD等设 ...

  8. 从零开始之驱动发开、linux驱动(二十五、framebuffer 子系统框架)

    一.概念 Framebuffer,也叫帧缓冲,其内容对应于屏幕上的界面显示,可以将其简单理解为屏幕上显示内容对应的缓存,修改Framebuffer中的内容,即表示修改屏幕上的内容,所以,直接操作Fra ...

  9. 从零开始之驱动发开、linux驱动(六十七、内核调试篇--printk使用)

    printk的使用我们在内核,驱动调试的时候使用的非常多 比如前面在调试usb驱动的时候 #include <linux/init.h> #include <linux/usb/in ...

最新文章

  1. ListView和数据适配器SimpleAdapter例子
  2. 数学建模 随机动态规划
  3. Linux-进程内存占用情况
  4. SSM+Netty项目结合思路
  5. 通过回调函数阻止进程创建(验证结束,方案完全可行)
  6. Java 对用户密码加密(Jeecg 登录密码加密方式)MD5andDES方式
  7. android shape使用总结
  8. 阿里广告技术最新突破!全链路联动——面向最终目标的全链路一致性建模
  9. 关于android隐式启动activity的分析和说明,Android学习之Intent中显示意图和隐式意图的用法实例分析...
  10. [导入]DotNetNuke 模組偵錯(DNN module debug)
  11. Asp.Net异步加载
  12. handlersocket mysql_[原创]MongoDB、HandlerSocket和MySQL性能测试及其结果分析
  13. IE8-阿里icon字体引用不显示的问题
  14. win7设置自动开机时间_使计算机自动开机
  15. Vue教程-day05-2018年12月25日笔记
  16. 程序员接私活的7大利器以及建议
  17. 你投的简历提示不合适,建议参考STAR法则
  18. 在Home Assistant中配置天气信息
  19. windows搭建Git服务器之Bonobo Git Server
  20. Wireless Communication学习笔记-路径损耗,阴影和多径效应

热门文章

  1. nginx连接php-fpm sock文件失败502
  2. NSMutableArray 记住取不到时要进行强转
  3. SolrJ查询Solr数据
  4. 打开sql server 验证
  5. 【Linux迁移到Windows服务器时的注意事项】
  6. android SQL 语句
  7. linux centos7 xen虚拟机 安装方法
  8. python 遍历文件 获取文件修改时间
  9. linux top 命令信息解释
  10. web 点击劫持 X-Frame-Options