即插即用(Plug and Play -- PnP)管理器使用主功能码为IRP_MJ_PNP的IRP与设备驱动程序交换信息和请求。这种类型的请求是新引入到Windows 2000和WDM中的,在以前版本的Windows NT中,大部分检测和配置设备的工作由设备驱动程序自己做。而WDM驱动程序可以让PnP管理器做这个工作。为了与PnP管理器协同工作,驱动程序开发者需要了解一些相关的IRP。
在WDM中,PnP请求扮演了两个角色。在第一个角色中,这些请求指示驱动程序何时以及如何配置或取消其硬件或自身的设置。表3-3-4列出了PnP请求可以指定的二十多个副功能码。其中有9个副功能码仅能由总线驱动程序处理,以星号标出;过滤器驱动程序或功能驱动程序仅下传这些IRP。剩下的副功能码中,有三个对过滤器驱动程序或功能驱动程序特别重要。PnP管理器使用IRP_MN_START_DEVICE来通知功能驱动程序其硬件被赋予了什么I/O资源,以及指导功能驱动程序做任何必要的硬件或软件设置以便设备能正常工作。IRP_MN_STOP_DEVICE告诉功能驱动程序关闭设备。IRP_MN_REMOVE_DEVICE告诉功能驱动程序关闭设备并释放与之关联的设备对象。
表3-3-4. IRP_MJ_PNP的副功能码 (*指出仅由总线驱动程序处理)
IRP副功能码 |
描述 |
IRP_MN_START_DEVICE |
配置并初始化设备 |
IRP_MN_QUERY_REMOVE_DEVICE |
设备可以被安全地删除吗? |
IRP_MN_REMOVE_DEVICE |
关闭并删除设备 |
IRP_MN_CANCEL_REMOVE_DEVICE |
取消设备移除 |
IRP_MN_STOP_DEVICE |
关闭设备 |
IRP_MN_QUERY_STOP_DEVICE |
设备可以被安全地关闭吗? |
IRP_MN_CANCEL_STOP_DEVICE |
忽略以前的QUERY_STOP |
IRP_MN_QUERY_DEVICE_RELATIONS |
查询特定的设备列表 |
IRP_MN_QUERY_INTERFACE |
获得直接调用函数地址 |
IRP_MN_QUERY_CAPABILITIES |
取设备能力 |
IRP_MN_QUERY_RESOURCES* |
取引导配置 |
IRP_MN_QUERY_RESOURCE_REQUIREMENTS* |
取I/O资源需求 |
IRP_MN_QUERY_DEVICE_TEXT* |
获得描述信息或位置串 |
IRP_MN_FILTER_RESOURCE_REQUIREMENTS |
修改I/O资源需求列表 |
IRP_MN_READ_CONFIG* |
读配置空间 |
IRP_MN_WRITE_CONFIG* |
写配置空间 |
IRP_MN_EJECT* |
弹出设备 |
IRP_MN_SET_LOCK* |
设备弹出锁定/解除 |
IRP_MN_QUERY_ID* |
取设备硬件ID |
IRP_MN_QUERY_PNP_DEVICE_STATE |
取设备状态 |
IRP_MN_QUERY_BUS_INFORMATION* |
取父总线类型 |
IRP_MN_DEVICE_USAGE_NOTIFICATION |
通知分页、dump、睡眠文件被创建或删除 |
IRP_MN_SURPRISE_REMOVAL |
通知设备已经被删除 |
PnP请求的第二个角色是指导驱动程序完成一系列状态转换。WORKING和STOPPED是设备的两个基本状态。当你创建设备对象后,设备就立即进入STOPPED状态。WORKING状态指出设备是全部可操作的。此外,还有两个中间状态,PENDINGSTOP和PENDINGREMOVE,它们出现在WORKING状态前。SURPRISEREMOVED发生在物理硬件突然被移去的情况下。
USB操作主要分读、写和I/O控制。正如前几小节中介绍USB信息流动是以包的方式进行。在USB的过程当中端点的包传输大小是有一定限制的,依据传输类型的不同而不同,如表3-3-4.所示。
表3-3-4. 包大小
传输类型 |
描述 |
纠错 |
包容量(字节) |
延迟保证? |
控制 |
用于发送和接收USB定义的结构化信息 |
是 |
少于或等于8,16,32,64 |
尽最大能力保证不延迟 |
批量 |
用于发送或接收小块无结构数据 |
是 |
少于或等于8,16,32,64 |
无 |
中断 |
与批量管道相似,但包括一个最大延迟 |
是 |
少于或等于64 |
以保证的最小速率轮询 |
等时 |
用于发送或接收有周期保证的大块无结构数据 |
否 |
少于或等于1023 |
每1毫秒帧中的固定部分 |
当读写数据存在限制的时候,我们开始要考虑大批量数据传输了。在大批量数据传输过程当中,我们必须将数据进行分段,即逐段进行发送,同时段大小最好和包大小一致。分段传输过程处理的时候,一般采用完成用例进行处理。通过完成例程,当一段数据发送完成之后返回STATUS_MORE_PROCESSING_REQUIRED使得驱动仍然使用该IRP继续发送数据,同时在完成例程函数中反复设置该完成例程,指导数据传输完毕,这种方式很像递归。
假设处理IRP_MJ_READ和IRP_MF_WRITE利用直接数据存储(deviceObject->Flags |= DO_DIRECT_IO)时,还需要定于_LPC2478_USB_RW_CONTEXT结构体,该结构体描述如下;
typedef struct _LPC2478_USB_RW_CONTEXT { PURB Urb; PMDL Mdl; ULONG Length; ULONG Numxfer; ULONG_PTR VirtualAddress; PDEVICE_EXTENSION DeviceExtension; } LPC2478_USB_RW_CONTEXT, * PLPC2478_USB_RW_CONTEXT; |
Ø Length域 MDL中剩余的长度。
Ø Numxfer域已经传输的数据量。
Ø VirtualAddress域数据保存地址。
有了这个结构体,已经可以帮助我们进行大批数据量传输,如图3-3-2-4设计的大批量数据读写算法。
USB驱动由初始化、卸载、电源管理、WMI和PNP等几大模块构成驱动结构。所有的模块在DriverEntry提供给体现,如表3-3-5.列出所有的功能模块。
表3-3-5. 功能模块(IRP和I/O方式表示)
一级结构 |
二级结构 |
IRP_MJ_DEVICE_CONTROL |
IOCTL_LPC2478_USB_RESET_PIPE |
IOCTL_LPC2478_USB_GET_CONFIG_DESCRIPTOR |
|
IOCTL_LPC2478_USB_RESET_DEVICE |
|
IRP_MJ_POWER |
IRP_MN_SET_POWER |
IRP_MN_QUERY_POWER |
|
IRP_MN_WAIT_WAKE |
|
IRP_MN_POWER_SEQUENCE |
|
IRP_MJ_PNP |
IRP_MN_START_DEVICE |
IRP_MN_QUERY_STOP_DEVICE |
|
IRP_MN_CANCEL_STOP_DEVICE |
|
IRP_MN_STOP_DEVICE |
|
IRP_MN_QUERY_REMOVE_DEVICE |
|
IRP_MN_CANCEL_REMOVE_DEVICE |
|
IRP_MN_SURPRISE_REMOVAL |
|
IRP_MN_REMOVE_DEVICE |
|
IRP_MN_QUERY_CAPABILITIES |
|
IRP_MJ_CREATE |
|
IRP_MJ_CLOSE |
|
IRP_MJ_CLEANUP |
|
IRP_MJ_READ |
|
IRP_MJ_WRITE |
|
IRP_MJ_SYSTEM_CONTROL |
QueryWmiRegInfo |
QueryWmiDataBlock |
|
SetWmiDataBlock |
|
SetWmiDataItem |
|
DriverUnload |
|
DriverExtension->AddDevice |
|
在整个USB驱动中,所有的操作都是围绕二级结构进行,以IRP和I/O方式显示驱动的结构再好不过。
Windows WDM模式驱动安装一般借助.INF文件进行,.INF是由段组成的,其中描述驱动的各种信息,有GUID、版本号、生产厂商等。把一个大的INF文件看成是一个树结构的线形描述可以更容易理解INF文件,如图3-3-4-0描述INF的文件结构。一个段就是树上的一个节点,而每个指令就是指向另一个段的指针。
在INF文件设计的时候,其中最重要的是找出设备标识符信息,这个信息标志着设备的存在且给出设备类型,如下图3-3-4-1获取设备的设备标识符信息。在图3-3-4-1中我们可以获取USB/VID0471&PID_2400/5&8897032&0&5,但是USB/VID0471&PID_2400才是我们所需要的。
获取设备标识符信息之后,开始可以设计INF文件信息,如下为设计后的INF文件内容。
; Installation inf for the ZLG EasyARM2400 USB Board ; ; (c) Copyright 2009 ZLG ; [Version] Signature="$CHICAGO$" Class=USB ClassGUID={36FC9E60-C465-11CF-8056-444553540000} provider=%MSFT% DriverVer=08/05/1999
[SourceDisksNames] 1="LPC2478USB Installation Disk",,,
[SourceDisksFiles] LPC2400_USB.sys = 1 LPC2400_USB.inf = 1
[Manufacturer] %MfgName%=ZLG
;设置设备标识符USB/VID_0471&PID_2400。 [ZLG] %USB/VID_0471&PID_2400.DeviceDesc%=LPC2478USB.Dev, USB/VID_0471&PID_2400
;[PreCopySection] ;HKR,,NoSetupUI,,1
[DestinationDirs] LPC2478USB.Files.Ext = 10,System32/Drivers LPC2478USB.Files.Inf = 10,INF
[LPC2478USB.Dev] CopyFiles= LPC2478USB.Files.Ext, LPC2478USB.Files.Inf AddReg= LPC2478USB.AddReg
[LPC2478USB.Dev.NT] CopyFiles= LPC2478USB.Files.Ext, LPC2478USB.Files.Inf AddReg= LPC2478USB.AddReg
[LPC2478USB.Dev.NT.Services] Addservice = LPC2478USB, 0x00000002, BULKUSB.AddService
[LPC2478USB.AddService] DisplayName = % LPC2478USB.SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%/System32/Drivers/LPC2400_USB.sys LoadOrderGroup = Base
[LPC2478USB.AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,LPC2400_USB.sys HKLM,"System/Currentcontrolset/Services/LPC2478USB /Parameters","MaximumTransferSize",0x10001,4096 HKLM,"System/Currentcontrolset/Services/LPC2478USB /Parameters","DebugLevel",0x10001,2 HKLM,"System/Currentcontrolset/services/LPC2478USB /Parameters","BulkUsbEnable",0x10001,1
[LPC2478USB.Files.Ext] LPC2400_USB.sys
[LPC2478USB.Files.Inf] LPC2400_USB.inf
;---------------------------------------------------------------;
[Strings] MSFT="ZLG" MfgName="NXP" USB/VID_0471&PID_2400.DeviceDesc="ZLG EasyARM2400 USB Device" BULKUSB.SvcDesc="LPC2400_USB.sys EasyARM2400 USB Bulk IO driver" |
Ø INF文件开始于一个Version段,该段确定文件中描述的设备类型。
Ø SourceDisksFiles段指出安装程序可以在1号盘上找到PKTDMA.SYS文件。
Ø SourceDisksNames段指出磁盘1有一个人类可读的标签“LPC2478USB Installation Disk”并包含一个名为disk1的文件,用于安装程序寻找并检验驱动器中是否有正确的磁盘。注意这两个段名中有一个容易忘记的字母“s”。
Ø DestinationDirs段指出文件复制操作的目标路径。DefaultDestDir是未指定目标目录文件的默认目标路径。你可以用一个数值代码来指定目标路径,这个代码的完整列表见DDK文档,经常使用的仅有如下几个:
n 目录“10”为Windows目录(例如“/Windows”或“/Winnt”)。
n 目录“11”是System目录(例如,“/Windows/System”或“/Winnt/System32”)。
n 目录“12”是Drivers目录,指在Windows 2000系统中(例如,“/Winnt/System32/Drivers”)。但在Windows 98中这个数值有不同的含义(例如“/Windows/System/Iosubsys”)。
WDM驱动程序存在于Driver目录中。如果你的CopyFiles段仅用于向Windows 2000安装,那么仅指定目录数值12就可以了。如果你还想使该段能向Windows 98中安装,我推荐你用“10,System32/Drivers”,它以两种方式标识Driver目录。
Ø 有时候我们需要为PnP管理器做些安排,以使它知道应该载入哪个文件。段.Services用于实现这个目标。
Ø BULKUSB.AddReg段通过内核的方式,向相关注册表中创建或则写入参数以供驱动采用。