USB Probe流程
来源:互联网 发布:马刺热火第六场数据 编辑:程序博客网 时间:2024/05/21 15:45
CP注:原文地址为http://blog.csdn.net/jiang_dlut/article/details/5832237 ,原文参考linux版本为2.6,博主比对了linux3.12源码,对文章内容进行了部分调整。
——————————————————————————————————————————————————————————
本文将详细讲述一个USB 设备插上Linux 系统的PC 后是如何一步一步调到我们的usb 设备驱动的probe 函数的, 我们知道我们的USB 驱动的probe 函数中的一个参数是interface 结构, 因此一般来说, 一个USB 设备中的任何一个接口都应该有对应的一个驱动程序, 当然也有例外(如cdc-acm).
我们知道USB 设备都是通过插入上层HUB 的一个Port 来连入系统并进而被系统发现的,。当USB设备插入一个HUB 时, 系统会调用中断服务程序hub_irq()来唤醒USB守护线程,, 此时会调用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_enumerate_device(udev); //枚举设备 CP注
….
device_add(&udev->dev);
}
该函数首先调用usb_enumerate_device() 来进行设备枚举,它内部会进行一系列的信息获取和解析过程, 接着调用device_add() 来把这个USB 设备添加到USB 系统中去, 也就是在这个过程中系统回去为这个设备找到相应的驱动. 在2.6 的早期的一些版本中在分析配置描述符后得到interface 的同时把interface 作为设备来调用device_add() 的
int device_add(struct device *dev)
{
….
if((error = bus_add_device(dev)))
…
bus_probe_device(dev);
…
}
这个函数是个通用的设备管理函数, 它会为每个设备调用bus_add_device 来把这个设备添加到相应bus 的设备列表中去. 接着调用bus_probe_device() 来匹配对应的驱动程序, 对于USB 设备来说第一次调用bus_probe_device() 时的参数dev 代表的是整个usb 设备( 以后usb 设备中的interface 也会作为设备调用这个函数).
int bus_probe_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;
if (!driver_match_device(drv, dev))//判断drv和dev或者都是设备级的或者都是接口级的
return 0;
return driver_probe_device(drv, dev);
}
int driver_probe_device(struct device *drv, struct device *dev)
{
……
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 if (is_usb_interface(dev)) /*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 过程中会用到
if(dev->bus->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 是device_driver类型,那它只能是我们之前注册在USB设备驱动(usb_device_driver)中的内嵌的设备驱动(device_driver)的probe---usb_probe_device.
int usb_register_device_driver(struct usb_device_driver *new_udriver,struct module *owner)
{
……
new_udriver->drvwrap.driver.probe = usb_probe_device; //用来查看特定设备是否存在,所有设备的probe都是这个
……
}
而usb_probe_device的实现是
static int usb_probe_device(struct device *dev)
{
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
……
if (!error)
error = udriver->probe(udev);//执行在generic.c中的 usb_generic_driver 的generic_probe()函数
……
}
usb_probe_device最终调用的是usb_probe_device的probe函数,在我们的系统中那就是usb_generic_driver. 因为在当前的usb 系统中只有这个driver 是代表整个USB设备的驱动, 它是在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() 函数, 它的参数是接口&intf->dev!!根据前面的分析可知, 这个过程将会走到接下来我们要分析的情况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 usb_driver= to_usb_driver(dev->driver); //dev->driver 在really_probe 中设置.
…
error = driver->probe(intf, id); // 这个就是我们自己写的probe 函数了.
…
}
driver->probe(intf, id); 这就调用到我们自己写的代码里面了。也就是使用usb_register()注册的驱动,如hub的接口驱动hub_driver。
整个流程大概就是这样:
- USB Probe流程
- Android USB gadget probe流程
- usb probe
- 从USB设备插上到驱动probe调用流程分析
- 从USB设备插上到驱动probe调用流程分析
- 从USB设备插上到驱动probe调用流程分析
- 从USB设备插上到驱动probe调用流程分析
- USB学习二:从USB设备插上到驱动probe调用流程分析(转)
- usb设备probe过程
- probe 调用流程
- usb设备的probe全过程
- USB Storage Probe 函数分析
- usb设备的probe全过程
- usb设备的probe全过程
- usb设备的probe全过程
- usb设备的probe全过程
- usb设备的probe全过程
- usb设备的probe全过程
- pull 解析 xml
- RSA加密解密java实现
- Dubbo教程
- #409 – 加入Grid中的子元素默认占满所在单元格(Child Elements in a Grid Size to Fit the Containing Cell)
- Photoshop 实时切图功能 Generate
- USB Probe流程
- Zookeeper客户端不能启动问题
- java开发常见单词
- X-Plugins框架使用
- Android BindService出现的问题
- (十二)jemter-常见报错/乱码问题---学习笔记
- SRM 552 Div1
- Spark二次排序
- Linux openssl MD5 C语言例程(使用crypto库)