《WDF USB驱动开发指南》-- USB软件结构
PDF全文下载地址:http://download.csdn.net/source/2320280
http://bbs.driverdevelop.com/read.php?tid-120461.html
《USB 软件结构 》
软件结构比硬件来的复杂很多。因为它包含了许多从表面上看不到的层次。比如总线驱动、功能驱动、过滤驱动等。套用社会学的话,这体现了功能应用中的分工和统筹。下面我们逐层来看它们。
总线驱动
总线驱动位于驱动栈的最低层,处理复杂的任务,必须资源分配,子设备管理。作为下层驱动,负责处理上层驱动发下来的请求。 USB 设备中的总线驱动主要有两类:控制器驱动、 HUB 驱动;另外还有一个端口驱动。
1 ) 控制器驱动: Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 。
首先解释一下 HCI ,它是主机控制接口( Host Control Interface )的缩写。前后一共有三种 HCI 协议出现: USB 1.1 时代,有 OHCI (开发 HCI )协议和 UHCI (通用 HCI )协议; USB2.0 时代,有 EHCI (扩展 HCI )协议。三个协议分别对应了上面的三个驱动程序。因为 USB 是向后兼容的,所以 UsbEHC.sys 中也包含了 UsbOhci 和 UsbUhci 的功能。请看下图。
图 1 总线设备
上图中设备 1 、 3 是控制器设,从名称上就可以区别它们的不同: Universal Host Controller 和 Enhanced Host Controller 。所以设备 1 的驱动程序是 USBUhci.sys ,设备 3 的驱动程序是 USBEhci.sys 。
这说明同一台主机特别是笔记本电脑中, 1.1 和 2.0 的控制器并存,像笔记本电脑中的键盘通过内置 USB 接口与系统连接,其数据吞吐量小,只需要 1.1 的控制器就能满足;而暴露在外供用户使用的接口则需要 2.0 。
2) Hub 驱动: UsbHub.sys 。 Hub 驱动是所有 USB 设备的父驱动。下图描绘了这一景况:
图2
上图中看到, Hub 驱动的子设备要不是独立设备,如左侧图;要么是一个含有多个子设备的父设备,如右侧图。 Hub 驱动只为直系子设备创建唯一的物理设备对象。上图中, Hub 驱动为设备 1 和设备 2 创建物理设备对象,但并不为设备 2 的子设备创建物理设备对象。
3 ) Port 驱动: UsbPort.sys 。这是个框架驱动,比较复杂,很少人会用到。而上面的 Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 其实都是他的微端口驱动。对于这么偏门的框架,不提也罢。
系统类驱动
所以出现类驱动,体现了 USB 总线在应用上的繁荣景象。只有用得多了,才有被归类的可能。就像人类社会中有几百个国家,几千个民族,正是体现了人类这个团体的多样性与繁荣。只有在“多”的基础上,分类才是有必要的;少数人的小群体,再怎么独立特行,也都不足以被分类,甚至定义为“ XX 民族”。
USB 设备包含很多的通用的功能类,比如: USB 集线器设备, USB HID 设备, USB 音频设备, USB MIDI 设备, USB 存储设备。为了让开发工作变得更加简单, Windows 操作系统为他们提供了系统驱动程序。
大部分时候,系统类驱动就是功能驱动。下表是系统提供的 USB 类驱动。
USB 类名称
类代码
驱动名称
系统支持
蓝牙设备
0xE0
Bthusb.sys
Vista 、 XP
USB 芯片智能卡接口设备
(CCID)
0x0B
Usbccid.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
Hub 设备
0x09
Usbhub.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
HID 设备
0x03
Hidusb.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
USB 大容量存储设备
0x08
Usbstor.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
打印设备
0x07
Usbprint.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
扫描设备
0x06
WpdUsb.sys Usbscan.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
媒体传输设备 (MTP)
0x06
WpdUsb.sys
XP 、 2K3 、 Vista 、 2K8
音频设备
0x01
Usbaudio.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
Modem 设备 (CDC)
0x02
Usbser.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
视频设备 (UVC)
0x0E
Usbvideo.sys
XP 、 Vista
表 1 系统提供的 USB 类驱动
功能驱动
不是所有的 USB 设备都有类驱动,但功能驱动却是它们唯一的身份证。没有功能驱动,设备就不足以在系统中存在。它的作用是为设备创造一个独一无二的内核设备对象( DEVICE_OBJCET ),并因此而在需要的时候,系统能够通过此内核设备对象找到它。
如果要让用户层也能够知道并使用 USB 设备,功能驱动更加不可少。它为设备在用户程序可见的名字空间中,为它起一个别名,这个别名可以是一个符号链接,也可以是一个由 GUID 定义的设备 Interface 。通过对这个别名进行操作,也就是对设备本身进行操作。
OK ,上面的话说得太满了,有例外的!唯一的例外是以 RAW 模式驱动的设备。这种设备直接由总线驱动来驱动其工作,不需要功能驱动。这种例子真的不多见,也许只有很很底层的控制器设备、 Hub 设备之类,才会这样做。对于 RAW 模式驱动的设备,当收到 IRP_MN_QUERY_CAPABILITIES 查询请求的时候,在返回的 DEVICE_CAPABILITIES 结构体中,必须将 RawDeviceOK 位设置为 TRUE 。
建议读者在需要的时候使用 WinOBJ.exe 工具查看系统空间中的设备与别名。
父驱动与混合设备
通过一定的设置, USB 设备中的每个接口可以拥有不同的 Class 和 Protocol 定义,从而实现:一个设备,多个功能。这种设备被称为混合设备。混合设备的前提是拥有多个接口,对单接口设备谈 “ 混合 ” 是没有意义的。
满足了如下两个条件的多接口 USB 设备,被系统认为是混合设备:
1. 设备描述符中,设备类的值为 0 : (bDeviceClass , bDeviceSubClass, bDeviceProtocol ) = (0, 0, 0);
2. 只有唯一的配置描述符,即设备描述符中: (bNumConfigurations ) = (1)
从 WinXP SP2 以后,还支持另外一种混合设备的判别方式,称作: Interface Association Descriptor ( IAD )。其实 IAD 描述符是用来组织 “ 接口组( Interfaces Group ) ” 的。配置描述符中可以有多个 IAD 存在,如果将某两个接口将组成接口组,那么首先这两个接口必须是紧挨着的,其次,必须有一个 IAD 描述符位于这两个接口描述符的前面,也必须是紧挨着的, IAD 描述符中的 bFirstInterface 用来描述接口组中的第一个接口 ID , bInterfaceCount 用来描述接口组中包含多少个接口。这样接口集合 : [bFirstInterface, bFirstInterface+bInterfaceCount) 为一个接口组。
当然,能够用上 IAD 的设备,一定是有多接口存在了,否则就是多此一举了。 IAD 描述符的识别和实现是通用父驱动完成的,所以有 IAD 支持的设备,都被认作混合设备。而识别设备是否有 IAD 支持,是通过设备描述符中的如下值判断的:
(bDeviceClass, bDeviceSubClass, bDeviceProtocol) = (0xEF, 0x02, 0x01)
IAD 普及率不是很广,一个原因就是 XP sp2 以后的操作系统版本才对它支持,这样如果把设备插入到 Win 2000 甚至 XP SP1 上,都不能被正确识别。这样,厂商可能必须为不同的系统写两套驱动程序。
我们下面来说说当一个多接口混合设备插入电脑后,系统是如何识别它,并为它加载驱动的。这里大家要注意到一点,就是系统是如何把一个设备,通过多接口,识别为多个物理设备的。
一开始,系统 PNP 管理器安装常规,读取并分析 USB 设备描述符,然后为它分配如下设备 ID :
USB\VID_vvvv&PID_pppp
USB\VID_vvvv&PID_pppp&REV_rrrr
(vvvv, pppp, rrrr: 4 位 16 进制数,分别代表了厂商 ID ,产品 ID , USB 版本。对应于设备描述符中的这些值: idVendor/ idProduct/ bcdDevice)
和兼容 ID :
USB\COMPOSITE
PNP 管理器首先按照常规,根据上述的设备 ID 为设备寻找合适的驱动程序:根据设备 ID 到注册表的设备安装信息库(对应于 Enum 和 Class 两个键)中进行搜索,如果找到了安装记录,就根据记录中的信息加载驱动程序。
问题是如果找不到合法的记录怎么办?这时候就用的着兼容 ID 了,兼容 ID “ USB\COMPOSITE ”是在系统中有注册记录的,并且就对应着通用父设备驱动( USBCCGP.sys) 。于是,系统为混合设备加载通用父设备驱动。读者可到目录 Windows\Inf 下查看 usb.inf 文件,此文件中包含了通用父设备驱动的安装信息。
通用父设备驱动通过分析配置描述符,完成两个动作:首先为每个 USB 接口分配一个的设备 ID 和兼容 ID ;然后为每个 USB 接口创建一个物理设备对象( Physical Device Object )。设备 ID 形式如下:
USB\VID_vvvv&PID_pppp&MI_mm
USB\VID_vvvv&PID_pppp&REV_rrrr&MI_mm
(mm: 两个 16 位数字表示的接口号 )
根据接口描述符中的 clsss 类型,分配兼容 ID ,其形式如下:
USB\CLASS_cc
USB\CLASS_cc&SUBCLASS_ss
USB\CLASS_cc&SUBCLASS_ss&PROT_pp
(cc/ ss / pp: 两位 16 进制数。分别对应于接口描述符中的: bInterfaceClass/ bInterfaceSubClass/ bInterfaceProtocol)
通用父设备驱动为每个接口创建了物理设备对象后,将接口的设备 ID 、兼容 ID 信息提交给 PNP 管理器, PNP 管理器就有责任为这些“虚假的”物理设备安装驱动。仍然重复上面的过程:根据每个接口的设备 ID 和兼容 ID 在注册表的设备安装信息库中搜索,试图找到相关的安装记录,如果找到了,就为接口加载相应的驱动程序。如果找不到,系统就会弹出“发现新设备”的对话框,启动驱动安装向导。此时用户需为这些接口手动安装驱动。
所以,对于多接口的混合设备( composite device ),其设备驱动的安装分为两个过程,先尝试安装混合驱动,如果找不到,就默认安装通用父设备驱动;接下来通用父设备驱动为每个接口分配设备 ID ,并要求系统为每个接口启动 PNP 过程。
最后,那么来说,在 USB 混合设备中,一定是 X 个接口对应于 X 个功能设备(有一个物理设备对象)吗?一般情况下是这样的,但也有例外,这就是接口组:在符合一定条件的情况下,多个接口中的某些接口可以被组合为一个接口组,一个接口组代表一个功能,父设备驱动只为之创建一个物理设备对象。
接口组的详情,大家看后面的章节内容。
过滤驱动
过滤驱动无处不在。在很多时候,它被称作 Hook ,是一种 Hack 手段;很多时候它又是必不可少的,这种技术甚至被操作系统自己使用。没有哪一个杀防毒软件不使用过滤驱动,我们在使用网上银行的时候,会用到一些安全控件,基本上都借助了过滤驱动的技术。
过滤驱动可以位于任何一层驱动的上面,或下面。过滤的对象也包括已经存在于系统中的其他的过滤驱动。当它位于某层驱动( D 驱动)上面的时候,所有目标发往 D 驱动的请求,都首先被它截取;当它位于某层驱动下面的时候,所有和 D 驱动相关的从更底层驱动反馈回来的的“完成消息”都预先被过滤驱动截取。这正是它威力强大的原因所在。对于被过滤的驱动来说,过滤驱动简直就是它的先知了。
但使用过滤驱动,要很慎重。很容易把系统搞得很不稳定。读者在写过滤驱动的时候,要明白这样一件事:你想过滤谁,得先了解谁;好像你追求一个人,要先认识这个人。否则死机蓝屏都会与你不期而遇。
USB 驱动栈、设备栈
请大家不要把这里的“栈”理解成“程序堆栈”的那个栈,朋友们要回到这个字最简单的本意来理解它,而不是想象成一个数据结构。就看成草垛柴堆一样。
驱动栈、设备栈本质上是并行概念。驱动之间的联系是通过设备对象进行的,所以驱动之间是间接联系的,驱动栈多少也就只是概念上的,他用来表示一个设备能够在系统中识别、运行,从上到下中共需要哪些驱动程序支持。
设备栈则是由据可查的。系统中每个 DevNode 就表现一个设备栈。可以这样理解,多个设备栈,串联成了驱动栈。
使用 WinDBG 的 !devnode 命令,可以列举系统中的设备树。下图截取了 CY001 相关片段。
图 3 CY001 DevNode 片段
上图中红色框标出的三个 DevNode ,正好对应于三个内核驱动,建立了 CY001 的驱动栈。从上到下分别是控制器驱动 (usbEHCI) ,集线器驱动 (usbHUB) ,和功能驱动 (CY001) 。下图以 CY001 为例,更加清晰详细地描绘了驱动栈、设备栈的面貌。
图 4 CY001 的驱动栈和设备栈
上图是单接口 CY001 设备的驱动栈、设备栈全图。也适用于所有其他的单接口 USB 设备。如果是多接口混合设备,就要多一个通用父驱动,稍微复杂一点。
最上面是可能存在的过滤驱动,因为只是“可能存在”,所以都用虚框表示。其实过滤驱动可以存在于设备栈的任何一个位置,而不仅仅是最上层。笔者不可能尽皆画全,以上层过滤为例,能说明问题也就可以了。
过滤驱动生成的过滤设备对象,挂载到 CY001 驱动生成的功能设备对象上;这样所有发送给 CY001 功能设备对象的请求,过滤设备对象总是先得到。根集线器生成了 CY001 驱动的物理设备对象。三个设备连在一起,就是 CY001 驱动程序的设备栈。
但还没有完,根集线器驱动并不是最底层驱动,他必须得到控制器驱动的支持,于是加上可能存在的过滤驱动,由此形成中间的那条设备栈。
仍旧未结束,控制器驱动也不是直接和系统交互的,而是通过更底层的系统总线如 PCI 、 ACPI 进行的。这样,右侧的第三条设备栈也形成了。
由此也可以看出设备栈、驱动栈之间的关系了。驱动栈是形而上的,设备栈是形而下的。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/changpei/archive/2010/05/06/5562542.aspx