版权信息:
版权归smilestone322所有,保留所有权利,仅用来学习,请勿用于商业用途,欢迎转载,转载请注明出处。谢谢!
3.3 wdk驱动开发
通过driver studio 3.2 和DDK驱动的比较分析,我们发现它们两个开发驱动程序都是类似的,毕竟ds3.2就是对ddk的又一次封装而已,那么wdk和前面两种方法有什么不同呢,本质的不同就是前面两种采用的是wdm驱动模型,而wdk采用的是wdf驱动模型。WDF提供了面向对象和事件驱动的驱动程序开发框架,大大降低了驱动开发的难度。WDF驱动程序包括2个类型,一个是内核级的KMDF,另外一个是用户级别的UMDF。下面我们主要讲内核级的驱动程序KMDF。
KMDF中所有的事物都由对象来表示,各种事件处理都由事件回调例程来完成。KMDF编程主要是针对KMDF的各种对象,对象函数和回调例程的编程。
根据WDM对象,WDF定义了一系列的KMDF对象。
WDM对象 |
对应的KMDF对象 |
DEVICE_OBJECT |
WDFDEVICE,WDFFILEOBJECT,WDFIOTARGET |
KDPC |
WDFDPC |
DRIVER_OBJECT |
WDFDRIVER |
KTIMER |
WDFTIMER |
IRP |
WDFREQUEST |
IO_WORKITEM |
WDFWORKITEM |
IO_CSQ |
WDFQUEUE |
通过一个指向内存分配函数的指针 |
WDFMEMORY |
KSPIN_LOCK |
WDFSPINLOCK |
USB_INTERFACE_DESCRIPTOR |
WDFUSBINTERFACE |
/ |
WDFUSBDEVICE /WDFUSBPIPE |
其中WDFIOTARGET对象感觉就是WDFDEVICE的又一次封装,学过设计模式和微软的com组件的人都知道,在设计模式中有适配器,以我的理解就是如果想继承已有的类而不改变类的函数和接口,那么最好的方法就是采用适配器建立一个新的类,对原来的类再次进行封装,这样的好处是可以添加新的功能,而不用改变原来的类。在com组件中这种思想是很常见的,在这里就不多说了。WDFDEVICE对象和DEVICE_OBJECT对象是一一对应的,WDFDEVICE对象仅保留了指向DEVICE_OBJECT的指针。
WDFDEVICE是对DEVICE_OBJECT的封装,而WDFIOTAGET又是对WDFDEVICE的又一次封装,在WDF框架中,每个WDFDEVICE都会有一个默认的WDFIOTARGET目标对象,同时可以有多个远程的目标对象。正因为WDFDEVICE和WDFIOTARGET的这种关系,它们之间有通过其中一个获取另外一个的函数;如知道了WDFIOTARGET,就可以通过WdfIoTargetGetDevice得到WDFDEVICE对象的设备句柄,函数原型如下:
WDFDEVICE WdfIoTargetGetDevice(IN WDFIOTARGET IoTarget);
同理,如果知道WDFDEVICE对象,通过WdfDeviceGetIoTarget得到WDFIOTARGET对象,函数原型如下:
WDFIOTARGET WdfDeviceGetIoTarget((IN WDFDEVICE Device);
所获取的Io目标对象就是设备堆栈中的下一个设备了。在usbsamp中在遇到这个函数,到时就一目了然的知道它的作用了。
下面以wdk中的usbsamp的例子对wdf下usb驱动开发进行讲解,讲解时对WDF驱动开发的基础知识进行解析,争取做到讲解完usbsamp,我们对WDF下驱动开发有深入的理解,而不仅仅针对usb的部分。
我们知道在WDF框架下,对象包含属性,方法和事件等。属性:可以获取或设置的一个数值,方法:对对象操作的函数,比如:WdfDriverCreate()表示创建驱动对象;而事件:当特定事件发生时,KMDF调用事件例程。这个有点像WDM驱动模型中的派遣例程,例如:当ReadFile,会触发一个Read的派遣例程(回调函数)。
在WDF框架下,可以定义属于对象的环境变量结构,就是xxx_Context,比如说设备上下文,其实就是一个结构体,里面保存和设备操作相关的东西,在wcdma 3G ps域中,也有类似的结构的,比如Ms上下文和Pdp上下文,可以采用这种类型的结构已经是软件开发的一种设计模式了,在WDF中的上下文环境就是用来保存和设备相关的资源,信息,比如一些全局的变量和函数,WDF的上下文环境和WDM中的设备扩展(EVICE_EXTENSION )是相似的。对于每一个上下文结构,在创建时会分配一个对应的内存单元,usbsamp环境结构变量都是定义在private.h头文件中。
对于设备对象,就有设备上下文:
typedef struct _DEVICE_CONTEXT {
USB_DEVICE_DESCRIPTOR UsbDeviceDescriptor; //设备描述符
PUSB_CONFIGURATION_DESCRIPTOR UsbConfigurationDescriptor;//配置描述符
WDFUSBDEVICE WdfUsbTargetDevice; //usb目标设备
ULONG WaitWakeEnable; //等待唤醒使能
BOOLEAN IsDeviceHighSpeed; //是否高速
WDFUSBINTERFACE UsbInterface; //usb接口
UCHAR NumberConfiguredPipes; //配置管道数
ULONG MaximumTransferSize; //最大的传输size
WDFQUEUE IsochReadQ; //同步读queue
WDFQUEUE IsochWriteQ; //同步写队列
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT,
GetDeviceContext)
这个宏定义的含义就是定义一个获取DEVICE_CONTEXT结构体指针的函数,即GetDeviceContext,获取结构体指针的函数还有另外一个宏,
WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT),这个宏没有指出具体的函数,那么获取该结构体指针的函数为:WdfObjectGet_DEVICE_CONTEXT。
同样,private还定义了其它的结构变量:
typedef struct _FILE_CONTEXT {
WDFUSBPIPE Pipe;
} FILE_CONTEXT, *PFILE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILE_CONTEXT,
GetFileContext)
typedef struct _REQUEST_CONTEXT {
WDFMEMORY UrbMemory;
PMDL Mdl;
ULONG Length; // remaining to xfer
ULONG Numxfer; // cumulate xfer
ULONG_PTR VirtualAddress; // va for next segment of xfer.
WDFCOLLECTION SubRequestCollection; // used for doing Isoch
WDFSPINLOCK SubRequestCollectionLock;// used to sync access to collection atDISPATCH_LEVEL
BOOLEAN Read; // TRUE if Read
} REQUEST_CONTEXT, * PREQUEST_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT ,
GetRequestContext)
typedef struct _WORKITEM_CONTEXT {
WDFDEVICE Device;
WDFUSBPIPE Pipe;
} WORKITEM_CONTEXT, *PWORKITEM_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WORKITEM_CONTEXT,
GetWorkItemContext)
//
// Required for doing ISOCH transfer. This context is associated with every
// subrequest created by the driver to do ISOCH transfer.
//
typedef struct _SUB_REQUEST_CONTEXT {
WDFREQUEST UserRequest;
PURB SubUrb;
PMDL SubMdl;
LIST_ENTRY ListEntry; // used in CancelRoutine
} SUB_REQUEST_CONTEXT, *PSUB_REQUEST_CONTEXT ;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SUB_REQUEST_CONTEXT, GetSubRequestContext)
上面定义的都是和驱动相关的环境结构体变量,所有都定义在private.h头文件中,而和应用程序和驱动都有关的东西就定义在public.h了,在public.h中定义了一个GUID和几个IOCTL_INDEX。如下:
DEFINE_GUID(GUID_CLASS_USBSAMP_USB,
0x19A073D9, 0xC30C, 0x4164, 0x82, 0xFC, 0x6A, 0x01, 0x04, 0x0E, 0x85, 0x9F);
#define IOCTL_INDEX 0x0000
#define IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN, \
IOCTL_INDEX, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define IOCTL_USBSAMP_RESET_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, \
IOCTL_INDEX + 1, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define IOCTL_USBSAMP_RESET_PIPE CTL_CODE(FILE_DEVICE_UNKNOWN, \
IOCTL_INDEX + 2, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
/
#endif
这些知识和wdm中是一样的,应用程序打开设备可以通过2种方式进行,usbsamp中采用GUID的方式,另一种方式符号链接。上面public.h中的IOCTL用于应用程序采用DeviceIoControl与驱动程序进行通信。
接着像分析ds3.2 usb驱动和ddk中的bulkusb一样,我们分析usbsamp也从入口函数DriverEntry开始,DriverEntry的源码如下:
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
UsbSamp_DbgPrint(3, ("UsbSamp Driver Sample - Driver Framework Edition.\n"));
UsbSamp_DbgPrint(3, ("Built %s %s\n", __DATE__, __TIME__));
WDF_DRIVER_CONFIG_INIT(
&config,
UsbSamp_EvtDeviceAdd
);
/*初始化驱动对象配置结构,设置UsbSamp_EvtDeviceAdd例程的入口地址*/
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
&config, // Driver Config Info
WDF_NO_HANDLE // hDriver
);
/*创建驱动对象,没有对象属性和驱动对象环境变量结构*/
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfDriverCreate failed with status 0x%x\n", status));
}
return status;
}
从DriverEntry函数可以看到它的作用就是初始化一个UsbSamp_EvtDeviceAdd的派遣函数(回调函数),然后创建一个驱动对象,这样,当DriverEntry调用时,会调用UsbSamp_EvtDeviceAdd函数,生成设备,并初始化一系列的pnp回调函数及电源管理回调函数,WDF相对WDM来说的一个优点的就是用户几乎不用管pnp和电源管理,这个部分都由WDF框架为我们做完了。
其中WdfDriverCreate:函数功能:为了调用驱动,建立驱动对象
NTSTATUS WdfDriverCreate(
[in] PDRIVER_OBJECT DriverObject,
[in] PCUNICODE_STRING RegistryPath,
[in, optional] PWDF_OBJECT_ATTRIBUTES DriverAttributes,
[in] PWDF_DRIVER_CONFIG DriverConfig,
[out, optional] WDFDRIVER *Driver
);
DriverObject [in]
A pointer to a DRIVER_OBJECT structure that represents a Windows Driver Model (WDM) driver object. The driver receives this pointer as input to itsDriverEntry routine.
RegistryPath [in]
A pointer to a UNICODE_STRING structure that contains the registry path string that the driver received as input to itsDriverEntry routine.
DriverAttributes [in, optional]
A pointer to a caller-allocated WDF_OBJECT_ATTRIBUTES structure. (The structure'sParentObject member must be NULL.) This parameter is optional and can be WDF_NO_OBJECT_ATTRIBUTES.
DriverConfig [in]
A pointer to a caller-allocated WDF_DRIVER_CONFIG structure.
Driver [out, optional]
A pointer to a location that receives a handle to the new framework driver object. This parameter is optional and can be WDF_NO_HANDLE.
typedef struct _WDF_OBJECT_ATTRIBUTES {
ULONG Size;
PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback;
PFN_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyCallback;
WDF_EXECUTION_LEVEL ExecutionLevel;
WDF_SYNCHRONIZATION_SCOPE SynchronizationScope;
WDFOBJECT ParentObject;
size_t ContextSizeOverride;
PCWDF_OBJECT_CONTEXT_TYPE_INFO ContextTypeInfo;
} WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES;
其中:WDF_DRIVER_CONFIG结构体定义如下:
typedef struct _WDF_DRIVER_CONFIG {
ULONG Size;
PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;
PFN_WDF_DRIVER_UNLOAD EvtDriverUnload;
ULONG DriverInitFlags;
ULONG DriverPoolTag;
} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;
参数解释如下:
Size
The size, in bytes, of this structure.
EvtDriverDeviceAdd
A pointer to a driver'sEvtDriverDeviceAdd callback function.
EvtDriverUnload
A pointer to a driver'sEvtDriverUnload callback function.
DriverInitFlags
A bitwise OR of one or moreWDF_DRIVER_INIT_FLAGS-typed values that identify driver initialization flags.
DriverPoolTag
(KMDF versions 1.5 and later.) A driver-defined pool tag that the framework will assign to all of the driver's pool allocations. Debuggers display this tag. For more information about specifying a pool tag, see the following Remarks section.
那么WDF_DRIVER_CONFIG_INIT(&config,UsbSamp_EvtDeviceAdd);就是填充config结构体了。
如果DriverEntry中包含对象属性和驱动对象环境变量结构,那么WdfDriverCreate函数的调用形式就变了,必须填充第3个和第5个参数,这里不多讲了。例子如下:
DriverEntry(
。。。。。)
{
。。。。。。。
WDFDRIVER driver;
WDF_OBJECT_ATTRIBUTES driverAttributes;
//初始化驱动对象的环境变量结构
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&driverAttributes,DRIVER_CONTEXT)WDF_DRIVER_CONFIG_INIT(
&config,
UsbSamp_EvtDeviceAdd
);
/*初始化驱动对象配置结构,设置UsbSamp_EvtDeviceAdd例程的入口地址*/
status = WdfDriverCreate(
DriverObject,
RegistryPath,
&driverAttributes, // Driver Attributes
&config, // Driver Config Info
&driver // hDriver
);
。。。。。。。。
}
分析到这里,DriverEntry函数就分析完了,主要是创建一个驱动对象,及填充config结构的add函数。讲解完DriverEntry函数按照常理就应该讲解DeviceAdd函数了。DeviceAdd函数是由pnp管理器对驱动程序进行初始化时调用的,在DeviceAdd函数中,驱动程序创建一个设备对象作为目标I/O设备,并将设备对象附着到设备堆栈中,这点和ddk中附着类似。DeviceAdd在设备首次枚举时调用,或系统运行时,有新的设备到来时,系统将调用DeviceAdd例程。从ddk的bulkusb中,我们知道,增加设备例程是通过IRP的派遣函数BulkUsb_AddDevice实现的,所以在bulkusb中,就很容易理解AddDevice函数是怎样的调用的了。而wdk中都是由事件驱动的,和ddk类似,它们都是由pnp在设备加载时进行调用。但是从WDK中看不到派遣函数的存在,难道WDF不采用派遣函数了吗,不是的,WDF还是和WDM一样采用派遣函数的,但是这部分内容都由WDF的框架为我们做完了,我们要做的就是填充几个回调函数了。讲到这里,我们知道WDF和WDM的又一个不同就是WDF下驱动开发更加简单,很多内容,WDF框架已经为我们做完了。下面是usbsamp中的DeviceAdd函数:UsbSamp_EvtDeviceAdd。
NTSTATUS
UsbSamp_EvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
WDF_FILEOBJECT_CONFIG fileConfig;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES fdoAttributes;
WDF_OBJECT_ATTRIBUTES fileObjectAttributes;
WDF_OBJECT_ATTRIBUTES requestAttributes;
WDF_OBJECT_ATTRIBUTES queueAttributes;
NTSTATUS status;
WDFDEVICE device;
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
PDEVICE_CONTEXT pDevContext;
WDFQUEUE queue;
ULONG maximumTransferSize;
UNREFERENCED_PARAMETER(Driver);
UsbSamp_DbgPrint (3, ("UsbSamp_EvtDeviceAdd routine\n"));
PAGED_CODE();
//设置电源管理回调例程
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware =UsbSamp_EvtDevicePrepareHardware;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WDF_OBJECT_ATTRIBUTES_INIT(&requestAttributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&requestAttributes, REQUEST_CONTEXT);
WdfDeviceInitSetRequestAttributes(DeviceInit, &requestAttributes);
//
// Initialize WDF_FILEOBJECT_CONFIG_INIT结构处理create open close文件对象请求
WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
UsbSamp_EvtDeviceFileCreate,
WDF_NO_EVENT_CALLBACK,
WDF_NO_EVENT_CALLBACK
);
//初始化File_Context文件对象结构
WDF_OBJECT_ATTRIBUTES_INIT(&fileObjectAttributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fileObjectAttributes, FILE_CONTEXT);
WdfDeviceInitSetFileObjectConfig(DeviceInit,
&fileConfig,
&fileObjectAttributes);
#if !defined(BUFFERED_READ_WRITE)
//设置I/O类型,有WdfDeviceIoDirect和WdfDeviceIoBuffered两种,在本文中,设置成WdfDeviceIoDirect
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
#endif
WDF_OBJECT_ATTRIBUTES_INIT(&fdoAttributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fdoAttributes, DEVICE_CONTEXT);
//创建设备
status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfDeviceCreate failed with Status code %!STATUS!\n", status));
return status;
}
pDevContext = GetDeviceContext(device); //GetDeviceContex返回获得的设备上下文结构指针
//
//Get MaximumTransferSize from registry //从注册表中得到最大的传输大小
//
maximumTransferSize=0;
status = UsbSamp_ReadFdoRegistryKeyValue(Driver,
L"MaximumTransferSize",
&maximumTransferSize);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("UsbSamp_ReadFdoRegistryKeyValue failed with Status code %!STATUS!\n", status));
return status;
}
if(maximumTransferSize){
pDevContext->MaximumTransferSize = maximumTransferSize;
}
else {
pDevContext->MaximumTransferSize=DEFAULT_REGISTRY_TRANSFER_SIZE;
}
//初始化即插即用例程
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
pnpCaps.SurpriseRemovalOK = WdfTrue;
WdfDeviceSetPnpCapabilities(device, &pnpCaps);
//初始化默认的队列配置,设置I/O请求分发处理方式为并行
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchParallel);
ioQueueConfig.EvtIoRead = UsbSamp_EvtIoRead; //读回调函数
ioQueueConfig.EvtIoWrite = UsbSamp_EvtIoWrite; //写回调函数
ioQueueConfig.EvtIoDeviceControl = UsbSamp_EvtIoDeviceControl;//deviceiocontrol回调函数
ioQueueConfig.EvtIoStop = UsbSampEvtIoStop; //Io停止例程
ioQueueConfig.EvtIoResume = UsbSampEvtIoResume;.//Io恢复例程
//创建队列
status = WdfIoQueueCreate(device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue);// pointer to default queue
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for Default Queue %!STATUS!\n", status));
return status;
}
//初始化队列为并行
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
WdfIoQueueDispatchParallel);
WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes);
queueAttributes.SynchronizationScope=WdfSynchronizationScopeQueue;
//同步读
ioQueueConfig.EvtIoRead = UsbSamp_EvtIsochRead;
//创建队列
status = WdfIoQueueCreate(device,
&ioQueueConfig,
&queueAttributes,
&pDevContext->IsochReadQ);// pointer to IsochRead queue
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for IsochRead Queue %!STATUS!\n", status));
return status;
}
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
WdfIoQueueDispatchParallel);
WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes);
queueAttributes.SynchronizationScope=WdfSynchronizationScopeQueue;
ioQueueConfig.EvtIoWrite = UsbSamp_EvtIsochWrite;
status = WdfIoQueueCreate(device,
&ioQueueConfig,
&queueAttributes,
&pDevContext->IsochWriteQ);// pointer to IsochWrite queue
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for IsochWrite Queue %!STATUS!\n", status));
return status;
}
//
// Register a device interface so that app can find our device and talk to it.
//创建GUID接口
status = WdfDeviceCreateDeviceInterface(device,
(LPGUID) &GUID_CLASS_USBSAMP_USB,
NULL);// Reference String
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status));
return status;
}
UsbSamp_DbgPrint(3, ("EvtDriverDeviceAdd - ends\n"));
return status;
}
在上面的UsbSamp_EvtDeviceAdd函数中最重要的就是UsbSamp_ EvtDevicePrepareHardware函数了,下面主要讲解下这个回调函数,UsbSamp_EvtDevicePrepareHardware的作用,因为UsbSamp_EvtDeviceAdd是在DriverEntry中被调用的,就好像ddk中的bulkusb的AddDevice一样,也是在DriverEntry定义:
DriverObject->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)
BulkUsb_AddDevice;
那么WDM中的AddDevice和WDF中的AddDevice有什么不同的,从代码中可以看出,在bulkusb的DriverEntry中设置了很多回调函数,当设备插入系统时,有系统调用BulkUsb_AddDevice函数,然后AddDevice调用成功后,Pnp管理就开始发送PNP_MN_START_DEVICE的IRP_MJ_PNP请求,然后会调用一个开始设备的函数(StartDevice)。在WDF中,EvtDevicePrepareHardware的功能和WDM的bulkusb中的StartDevice类似,EvtDevicePrepareHardware的作用就是为设备申请资源,但是调用完它后,设备还不能正常的工作,因为还电源管理,因为电源管理还没有切换到D0供电状态。
在DeviceAdd中创建了3个并行的队列。下面对DeviceAdd里面的内容再进行讲解。
1)WdfDeviceCreate函数的作用是创建设备,原型如下:
WdfDeviceCreate函数功能:建立一个设备对象框架(creates a framework device object)
NTSTATUS WdfDeviceCreate(
[in, out] PWDFDEVICE_INIT *DeviceInit,
[in, optional] PWDF_OBJECT_ATTRIBUTES DeviceAttributes,
[out] WDFDEVICE *Device
);
参数:
DeviceInit [in, out]
The address of a pointer to a WDFDEVICE_INIT structure. If WdfDeviceCreate encounters no errors, it sets the pointer to NULL.
DeviceAttributes [in, optional]
A pointer to a caller-allocated WDF_OBJECT_ATTRIBUTES structure that contains attributes for the new object. (The structure'sParentObject member must be NULL.) This parameter is optional and can be WDF_NO_OBJECT_ATTRIBUTES.
Device [out]
A pointer to a location that receives a handle to the new framework device object.
2)WdfIoQueueCreate
函数功能:creates and configures an I/O queue for a specified device
NTSTATUS WdfIoQueueCreate(
[in] WDFDEVICE Device,
[in] PWDF_IO_QUEUE_CONFIG Config,
[in, optional] PWDF_OBJECT_ATTRIBUTES QueueAttributes,
[out, optional] WDFQUEUE *Queue
);
Device [in]
A handle to the framework device object that the queue will be associated with.
Config [in]
A pointer to a caller-allocated WDF_IO_QUEUE_CONFIG structure.
QueueAttributes [in, optional]
A pointer to a caller-allocated WDF_OBJECT_ATTRIBUTES structure that specifies object attributes for the new object. This parameter is optional and can be WDF_NO_OBJECT_ATTRIBUTES.
Queue [out, optional]
A pointer to a location that receives a handle to a framework queue object.
评论:
Each call to WdfIoQueueCreatecreates an I/O queue for a device. Your driver can create multiple I/O queues for each device.
The Config and QueueAttributes parameters specify the queue's configuration and object attributes.
在上面的函数中创建了3个队列。这个队列是怎样调度的呢?
3)WdfDeviceCreateDeviceInterface
函数功能:创建设备接口creates a device interface for a specified device.
NTSTATUS WdfDeviceCreateDeviceInterface(
[in] WDFDEVICE Device,
[in] const GUID *InterfaceClassGUID,
[in, optional] PCUNICODE_STRING ReferenceString
);
参数:
Device [in]
A handle to a framework device object.
InterfaceClassGUID [in]
A pointer to a GUID that identifies the device interface class.
ReferenceString [in, optional]
A pointer to a UNICODE_STRING structure that describes a reference string for the device interface. This parameter is optional and can be NULL. For more information, see the following Remarks section.
在AddDevice函数中还有一个函数UsbSamp_EvtDevicePrepareHardware函数,主要作用是初始化硬件,比如读取设备描述符,设置接口描述符。源码如下:
NTSTATUS
UsbSamp_EvtDevicePrepareHardware(
IN WDFDEVICE Device,
IN WDFCMRESLIST ResourceList,
IN WDFCMRESLIST ResourceListTranslated
)
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
WDF_USB_DEVICE_INFORMATION info;
UNREFERENCED_PARAMETER(ResourceList);
UNREFERENCED_PARAMETER(ResourceListTranslated);
UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - begins\n"));
PAGED_CODE();
pDeviceContext = GetDeviceContext(Device); //获取设备的上下文
//
// Read the device descriptor, configuration descriptor
// and select the interface descriptors
//
status = ReadAndSelectDescriptors(Device);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
return status;
}
WDF_USB_DEVICE_INFORMATION_INIT(&info);
//
// Retrieve USBD version information, port driver capabilites and device
// capabilites such as speed, power, etc.
//
status = WdfUsbTargetDeviceRetrieveInformation(pDeviceContext->WdfUsbTargetDevice,
&info);
if (NT_SUCCESS(status)) {
pDeviceContext->IsDeviceHighSpeed =
(info.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? TRUE : FALSE;
UsbSamp_DbgPrint(3, ("DeviceIsHighSpeed: %s\n",
pDeviceContext->IsDeviceHighSpeed ? "TRUE" : "FALSE"));
} else {
pDeviceContext->IsDeviceHighSpeed = FALSE;
}
UsbSamp_DbgPrint(3, ("IsDeviceSelfPowered: %s\n",
(info.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE"));
pDeviceContext->WaitWakeEnable =
info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;
UsbSamp_DbgPrint(3, ("IsDeviceRemoteWakeable: %s\n",
(info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE) ? "TRUE" : "FALSE"));
//
// Enable wait-wake and idle timeout if the device supports it
//
if(pDeviceContext->WaitWakeEnable){
status = UsbSampSetPowerPolicy(Device);
if (!NT_SUCCESS (status)) {
UsbSamp_DbgPrint(3, ("UsbSampSetPowerPolicy failed\n"));
return status;
}
}
UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - ends\n"));
return status;
}
分析完AddDevice函数后,接着就是分析io操作了,在Queue.c中有一个函数UsbSamp_EvtIoDeviceControl,这个函数和ddk里面的DeviceControl函数类似,只是ddk bulkusb中的这个函数是在DriverEntry中定义的派遣函数,而UsbSamp_EvtIoDeviceControl是在AddDevice中定义的事件回调例程。
VOID
UsbSamp_EvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
。。。。。。。。。。。。。。。。。。。
其中的大部分内容,这里就不列举了,在这个里面和ddk的bulkusb类似,我们可以构建厂商请求,然后通过urb发送给底层usb总线驱动,达到控制usb设备的目的。完全可以来个自己的case分支,下面进行介绍:
case IOCTL_USBSAMP_XXXXXXXX:
。。。。。。。。。。。。。。。。。。。。。。
WDF_REQUEST_SEND_OPTIONS_INIT(
&sendOptions,
WDF_REQUEST_SEND_OPTION_TIMEOUT
);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
&sendOptions,
DEFAULT_CONTROL_TRANSFER_TIMEOUT
);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, //建立厂商请求
BmRequestHostToDevice,
BmRequestToDevice,
cmd, // Request
0, // Value
0); // Index
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc,ioBuffer,bufLength);
status = WdfUsbTargetDeviceSendControlTransferSynchronously( //发送厂商请求
pDevContext->WdfUsbTargetDevice,
WDF_NO_HANDLE, // Optional WDFREQUEST
&sendOptions,
&controlSetupPacket,
&memDesc, // MemoryDescriptor
&bufLength); // BytesTransferred
if(!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("UsbBuildVendorRequest Failed\n"));
WdfUsbTargetDeviceResetPortSynchronously(pDevContext->WdfUsbTargetDevice);
WdfRequestComplete(Request,STATUS_UNSUCCESSFUL);
goto exit;
}
status= STATUS_SUCCESS;
break;
}
和ddk的bulkusb进行比较,我们前面讲解了ddk中构建一个厂商请求,然后发送给底层usb总线的方法。它首先通过UsbBuildVendorRequest构建一个厂商请求,然后调用CallUSBD将厂商请求的URB发送给底层usb总线驱动,而CallUSBD中又首先将urb和Irp关联起来,然后通过IoCallDriver函数发送给usb总线驱动。而在wdk的usbsamp中,构建厂商请求直接通过一个宏就完成了,该宏为:
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR,然后通过:
WdfUsbTargetDeviceSendControlTransferSynchronously发送给底层的usb总线驱动。该函数是采用同步的方式发送,发送完成后必须等待函数返回。对于usb设备来说,wdf和driverstudio 3.2是类似的,只能发送控制请求,发送控制请求还有一个方式,函数如下:WdfUsbTargetDeviceFormatRequestForControlTransfer,该函数和WdfRequestSend一起使用,针对 USB 控制传输设置请求格式。驱动程序可以调用WdfRequestSend以同步或异步方式发送请求。
WdfUsbTargetDeviceFormatRequestForControlTransfer的原型如下:
NTSTATUS WdfUsbTargetDeviceFormatRequestForControlTransfer(
[in] WDFUSBDEVICE UsbDevice,
[in] WDFREQUEST Request,
[in] PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,
[in, optional] WDFMEMORY TransferMemory,
[in, optional] PWDFMEMORY_OFFSET TransferOffset
);
参数:
UsbDevice [in]
A handle to a USB device object that was obtained from a previous call toWdfUsbTargetDeviceCreate.
Request [in]
A handle to a framework request object. For more information, see the following Remarks section.
SetupPacket [in]
A pointer to a caller-allocated WDF_USB_CONTROL_SETUP_PACKET structure that describes the control transfer.
TransferMemory [in, optional]
A handle to a framework memory object that describes either an input or an output buffer, depending on the device-specific command. This pointer is optional and can be NULL. For more information, see the following Remarks section.
TransferOffset [in, optional]
A pointer to a caller-allocated WDFMEMORY_OFFSET structure that supplies optional byte offset and length values. The framework uses these values to determine the beginning address and length, within the buffer thatTransferMemory specifies. If this pointer is NULL, the framework uses the entire buffer
WdfUsbTargetDeviceFormatRequestForControlTransfer的作用只是格式化控制传输请求,至于是采用同步还是异步传输由WdfRequestSend函数决定(根据有没有完成例程决定吗);该函数原型如下:
WdfRequestSend
函数功能:sends a specified I/O request to a specified I/O target.
BOOLEAN WdfRequestSend(
[in] WDFREQUEST Request,
[in] WDFIOTARGET Target,
[in, optional] PWDF_REQUEST_SEND_OPTIONS RequestOptions
);
参数:
Request [in]
A handle to a framework request object.
Target [in]
A handle to a framework I/O target object. For more information about how to obtain this handle, see the following Remarks section.
RequestOptions [in, optional]
A pointer to a WDF_REQUEST_SEND_OPTIONS structure that contains caller-supplied request options. This parameter is optional and can be NULL if you do not want to enable any request options.
Target 可以通过WdfUsbTargetDeviceGetIoTarget or
WdfUsbTargetPipeGetIoTarget获取。
返回值:
returns TRUE if the request was sent to the target. Otherwise, this method returns FALSE.
A bug check occurs if the driver supplies an invalid object handle.
如果你的驱动程序异步地向I/O目标转发I/O请求,你需要让你的驱动程序获得通知,当一个底层驱动程序完成请求时。如果你的驱动程序注册了一个CompletionRoutine回调函数,KMDF会在I/O目标完成请求后调用它。典型地,CompletionRoutine回调函数调用WdfRequestComplete。如果没有定义完成例程,就是采用同步方式传输吗???
如果你的驱动程序不需要在I/O目标完成一个异步转发时被通知,驱动程序不必注册CompletionRoutine回调函数,而是要在调用WdfRequestSend时设置WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET标志。在这种情况下驱动程序不调用WdfRequestComplete。
在前面讲解WDFDEVICE和WDFIOTARGET之间的关系时,我们说了在usb驱动程序中还会用到它们之间的转换函数WdfDeviceGetIoTarget获取WdfRequestSend的第2个参数,也就是将请求转发给下一层堆栈,或者说转发给下一个IO目标,只是在这里WdfDeviceGetIoTarget用WdfUsbTargetDeviceGetIoTarget代替了而已。
讲解完上面的Io操作,就要讲解io读写操作了。I/O read/write操作:
VOID
ReadWriteBulkEndPoints(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN ULONG Length,
IN WDF_REQUEST_TYPE RequestType
)
{
PMDL newMdl=NULL, requestMdl = NULL;
PURB urb = NULL;
WDFMEMORY urbMemory;
ULONG totalLength = Length; //总的长度
ULONG stageLength = 0;
ULONG urbFlags = 0; //传输的方向
NTSTATUS status;
ULONG_PTR virtualAddress = 0; //地址
PREQUEST_CONTEXT rwContext = NULL; //读写上下文
PFILE_CONTEXT fileContext = NULL; //文件上下文
WDFUSBPIPE pipe; //管道
WDF_USB_PIPE_INFORMATION pipeInfo; //管道信息
WDF_OBJECT_ATTRIBUTES objectAttribs; //对象属性
USBD_PIPE_HANDLE usbdPipeHandle; //usb管道句柄
PDEVICE_CONTEXT deviceContext; //设备上下文
WDF_REQUEST_SEND_OPTIONS sendOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions,-2000000); //延迟200ms
UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - begins\n"));
//
// First validate input parameters.
//
deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));
/*其中WdfIoQueueGetDevice的作用是从Queue对象中获取设备对象,然后通过GetDeviceContext得到设备上下文*/
if (totalLength > deviceContext->MaximumTransferSize) {//设备的最大长度 UsbSamp_DbgPrint(1, ("Transfer length > circular buffer\n"));
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if ((RequestType != WdfRequestTypeRead) &&
(RequestType != WdfRequestTypeWrite)) {
UsbSamp_DbgPrint(1, ("RequestType has to be either Read or Write\n"));
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
//
// Get the pipe associate with this request.
//
fileContext = GetFileContext(WdfRequestGetFileObject(Request)); //获取文件对象上下文
pipe = fileContext->Pipe; //得到管道
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); //初始化管道信息
WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); //得到管道信息
if((WdfUsbPipeTypeBulk != pipeInfo.PipeType) &&
(WdfUsbPipeTypeInterrupt != pipeInfo.PipeType)) {
UsbSamp_DbgPrint(1, ("Usbd pipe type is not bulk or interrupt\n"));
status = STATUS_INVALID_DEVICE_REQUEST;
goto Exit;
}
rwContext = GetRequestContext(Request); //得到Request上下文,判断是读还是写
if(RequestType == WdfRequestTypeRead) {
status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl);
if(!NT_SUCCESS(status)){
UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl failed %x\n", status));
goto Exit;
}
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
rwContext->Read = TRUE;
UsbSamp_DbgPrint(3, ("Read operation\n"));
} else {
status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl);
if(!NT_SUCCESS(status)){
UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl failed %x\n", status));
goto Exit;
}
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
rwContext->Read = FALSE;
UsbSamp_DbgPrint(3, ("Write operation\n"));
}
urbFlags |= USBD_SHORT_TRANSFER_OK; //可以返回少的数据
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(requestMdl);
if (totalLength > MAX_TRANSFER_SIZE) {
stageLength = MAX_TRANSFER_SIZE;
}
else {
stageLength = totalLength;
}
//分配一个新的MDL
newMdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);
if (newMdl == NULL) {
UsbSamp_DbgPrint(1, ("Failed to alloc mem for mdl\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
//
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(requestMdl,
newMdl,
(PVOID) virtualAddress,
stageLength);
WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs);
objectAttribs.ParentObject = Request;
//分配Urb内存,非分页
status = WdfMemoryCreate(&objectAttribs,
NonPagedPool,
POOL_TAG,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
&urbMemory,
(PVOID*) &urb);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("Failed to alloc mem for urb\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe);//得到管道的句柄
//建立中断bulk request
UsbBuildInterruptOrBulkTransferRequest(urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
usbdPipeHandle,
NULL,
newMdl,
stageLength,
urbFlags,
NULL);
status = WdfUsbTargetPipeFormatRequestForUrb(pipe, Request, urbMemory, NULL);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
WdfRequestSetCompletionRoutine(Request, ReadWriteCompletion, NULL);
//
// set REQUEST_CONTEXT parameters.
//
rwContext->UrbMemory = urbMemory;
rwContext->Mdl = newMdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
//将WDF_NO_SEND_OPTIONS替换成&sendOptions
if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), &sendOptions)) {
status = WdfRequestGetStatus(Request);
ASSERT(!NT_SUCCESS(status));
}
Exit:
if (!NT_SUCCESS(status)) {
WdfRequestCompleteWithInformation(Request, status, 0);
if (newMdl != NULL) {
IoFreeMdl(newMdl);
}
}
UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - ends\n"));
return;
}
其中要重点讲解的是跟读写有关的几个函数,我们在dw3.2及采用DDK的bulkusb进行讲解时候,就讲到了类似的这些函数,比如在dw3.2中我们对usb管道,有4种建立传输的函数,分别对应控制传输,中断传输,同步传输和bulk传输,然后在通过函数SubmitUrb将提交Urb给底层usb总线,在ddk中我们也进行类似的操作,不如有UsbBuildInterruptOrBulkTransferRequest构建bulk或中断传输的Urb,然后通过IoCallDriver将请求转发给底层usb总线驱动,类似的在Wdf中,也是通过UsbBuildInterruptOrBulkTransferRequest构建bulk或中断传输请求,然后通过WdfUsbTargetPipeFormatRequestForUrb函数格式化请求,在通过WdfRequestSend函数转发给下层设备堆栈中的目标对象。UsbBuildInterruptOrBulkTransferRequest的函数原型如下:
VOID
UsbBuildInterruptOrBulkTransferRequest(
IN OUT PURB Urb,
IN USHORT Length,
IN USBD_PIPE_HANDLE PipeHandle,
IN PVOID TransferBuffer OPTIONAL,
IN PMDL TransferBufferMDL OPTIONAL,
IN ULONG TransferBufferLength,
IN ULONG TransferFlags,
IN PURB Link
);
Parameters
Urb
Pointer to an URB to be formatted as an interrupt or bulk transfer request.
Length
Specifies the size, in bytes, of the URB.
PipeHandle
Specifies the handle for this pipe returned by the HCD when a configuration was selected.
TransferBuffer
Pointer to a resident buffer for the transfer or is NULL if an MDL is supplied inTransferBufferMDL. The contents of this buffer depend on the value ofTransferFlags. If USBD_TRANSFER_DIRECTION_IN is specified, this buffer will contain data read from the device on return from the HCD. Otherwise, this buffer contains driver-supplied data to be transferred to the device.
TransferBufferMDL
Pointer to an MDL that describes a resident buffer or is NULL if a buffer is supplied inTransferBuffer. The contents of the buffer depend on the value ofTransferFlags. If USBD_TRANSFER_DIRECTION_IN is specified, the described buffer will contain data read from the device on return from the HCD. Otherwise, the buffer contains driver-supplied data to be transferred to the device. The MDL must be allocated from nonpaged pool.
TransferBufferLength
Specifies the length, in bytes, of the buffer specified inTransferBuffer or described inTransferBufferMDL.
TransferFlags
Specifies zero, one, or a combination of the following flags:
USBD_TRANSFER_DIRECTION_IN
Is set to request data from a device. To transfer data to a device, this flag must be clear.
USBD_SHORT_TRANSFER_OK
Can be used if USBD_TRANSFER_DIRECTION_IN is set. If set, directs the HCD not to return an error if a packet is received from the device that is shorter than the maximum packet size for the endpoint. Otherwise, a short request returns an error condition.
Link
Must be set to NULL. .
函数功能:formats an URB to send or receive data on a bulk pipe, or to receive data from an interrupt pipe.
对于usb设备,读写操作只有针对设备或管道进行,而不能针对接口进行,dw3.2,ddk,wdk都是这样的,对usb设备的进行io操作的话,只能进行控制传输,如建立厂商请求等,而不能进行其它如中断或bulk的读写操作,下面回调usbsamp的读写函数,接着讲解WdfUsbTargetPipeFormatRequestForUrb,它的作用是格式Urb,原型如下:
NTSTATUS WdfUsbTargetPipeFormatRequestForUrb(
[in] WDFUSBPIPE Pipe, //usb管道
[in] WDFREQUEST Request,
[in] WDFMEMORY UrbMemory,
[in, optional] PWDFMEMORY_OFFSET UrbMemoryOffset
);
Parameters
Pipe [in]
A handle to a framework pipe object that was obtained by callingWdfUsbInterfaceGetConfiguredPipe.
Request [in]
A handle to a framework request object. For more information, see the following Remarks section.
UrbMemory [in]
A handle to a framework memory object that contains aURB structure.
UrbMemoryOffset [in, optional]
A pointer to a caller-allocated WDFMEMORY_OFFSET structure that supplies optional byte offset and length values. The framework uses these values to determine the beginning address of the URB within the memory thatUrbMemory specifies. If this pointer is NULL, the URB is located at the beginning of theUrbMemory memory.
函数功能:builds an USB request for a specified USB pipe, using request parameters that a specifiedURB describes, but it does not send the request.
VOID
ReadWriteCompletion(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
IN WDFCONTEXT Context
)
{
PMDL requestMdl;
WDFUSBPIPE pipe;
ULONG stageLength;
NTSTATUS status;
PREQUEST_CONTEXT rwContext;
PURB urb;
PCHAR operation;
ULONG bytesReadWritten;
UNREFERENCED_PARAMETER(Context);
rwContext = GetRequestContext(Request); //获取rwContext
if (rwContext->Read) {
operation = "Read";
} else {
operation = "Write";
}
pipe = (WDFUSBPIPE) Target ; //得到管道
status = CompletionParams->IoStatus.Status;
if (!NT_SUCCESS(status)){
//
// Queue a workitem to reset the pipe because the completion could be
// running at DISPATCH_LEVEL.
//
urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL);
bytesReadWritten = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
rwContext->Numxfer += bytesReadWritten;
UsbSamp_DbgPrint(1, ("the transfer size is %d\n", rwContext->Numxfer));
QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe); //重设管道
goto End;
}
urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL); //得到urb
bytesReadWritten = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
rwContext->Numxfer += bytesReadWritten;
//
// If there is anything left to transfer.
//
if (rwContext->Length == 0) {
WdfRequestSetInformation(Request, rwContext->Numxfer);
goto End;
}
UsbSamp_DbgPrint(3, ("Stage next %s transfer...\n", operation));
if (rwContext->Length > MAX_TRANSFER_SIZE) {
stageLength = MAX_TRANSFER_SIZE;
}
else {
stageLength = rwContext->Length;
}
//
// Following call is required to free any mapping made on the partial MDL
// and reset internal MDL state.
//
MmPrepareMdlForReuse(rwContext->Mdl);
if (rwContext->Read) {
status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl);
if(!NT_SUCCESS(status)){
UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl for Read failed %x\n", status));
goto End;
}
} else {
status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl);
if(!NT_SUCCESS(status)){
UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl for Write failed %x\n", status));
goto End;
}
}
//将新Mdl进行映射
IoBuildPartialMdl(requestMdl,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength;
rwContext->VirtualAddress += stageLength;
rwContext->Length -= stageLength;
//
// Format the request to send a URB to a USB pipe.
//
status = WdfUsbTargetPipeFormatRequestForUrb(pipe,
Request,
rwContext->UrbMemory,
NULL);
if (!NT_SUCCESS(status)) {
UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto End;
}
WdfRequestSetCompletionRoutine(Request, ReadWriteCompletion, NULL);
//
// Send the request asynchronously.
//
if(!WdfRequestSend(Request,WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) {
UsbSamp_DbgPrint(1, ("WdfRequestSend for %s failed\n", operation));
status = WdfRequestGetStatus(Request);
goto End;
}
return;
End:
DbgPrintRWContext(rwContext);
IoFreeMdl(rwContext->Mdl);
UsbSamp_DbgPrint(3, ("%s request completed with status 0x%x\n",
operation, status));
WdfRequestComplete(Request, status);
return;
}
上面的完成例程函数就没有什么可讲解的了,主要的作用是针对应用程序的传输size来判断做几个读操作,因为每次的读写数据不能超过驱动中规定的MaxTransferSize,如果超过这个大小,就必须经过多次传输,上面的完成函数的作用就是做多次传输数据。到此为此,我们对wdk的usbsamp分析完了。下面讲解下wdf下其它读写usb管道的函数,比如读写管道:
1)WdfUsbTargetPipeFormatRequestForWrite
函数功能:builds a write request for a USB output pipe, but it does not send the request(用来完成写管道的准备工作)。
NTSTATUS WdfUsbTargetPipeFormatRequestForWrite(
[in] WDFUSBPIPE Pipe, // 管道句柄
[in] WDFREQUEST Request, //Request对象句柄,可以是新建的,也可以是老对象的重用
[in, optional] WDFMEMORY WriteMemory,//Memory句柄,内部含有一块含有写内容的缓冲区
[in, optional] PWDFMEMORY_OFFSET WriteOffset //缓冲偏移,如果为NULL,表示从缓冲区的头部开始写。
);
将一个Request句柄和一个Memory句柄,关联到这个管道上,使用Request句柄用来跟踪写操作的各个阶段(开始,完成等),Memory句柄用来存储需要写到管道的数据。
2)WdfUsbTargetPipeFormatRequestForRead
函数功能:用来完成读管道的准备工作,参数和写操作一样,作用也很相近,用来完成读管道的准备工作。
NTSTATUS WdfUsbTargetPipeFormatRequestForRead(
[in] WDFUSBPIPE Pipe,
[in] WDFREQUEST Request,
[in, optional] WDFMEMORY ReadMemory,
[in, optional] PWDFMEMORY_OFFSET ReadOffset
);
以上两个函数还必须配合WdfRequestSend函数将请求发送给下层的Io目标,下面的2个函数是同步的方式读写管道。
3)WdfUsbTargetPipeWriteSynchronously
函数功能:builds a write request and sends it synchronously to a specified USB output pipe.
NTSTATUS WdfUsbTargetPipeWriteSynchronously(
[in] WDFUSBPIPE Pipe,
[in, optional] WDFREQUEST Request,
[in, optional] PWDF_REQUEST_SEND_OPTIONS RequestOptions,
[in, optional] PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,
[out, optional] PULONG BytesWritten
);
Pipe [in]
A handle to a framework pipe object that was obtained by callingWdfUsbInterfaceGetConfiguredPipe.
Request [in, optional]
A handle to a framework request object. This parameter is optional and can be NULL. For more information, see the following Remarks section.
RequestOptions [in, optional]
A pointer to a caller-allocated WDF_REQUEST_SEND_OPTIONS structure that specifies options for the request. This pointer is optional and can be NULL. For more information, see the following Remarks section.
MemoryDescriptor [in, optional]
A pointer to a caller-allocated WDF_MEMORY_DESCRIPTOR structure that describes the buffer that contains data that will be written to the device. For more information about this buffer, see the following Remarks section.
BytesWritten [out, optional] :
A pointer to a location that receives the number of bytes written, if the operation succeeds. This parameter is optional and can be NULL.
Supply a local buffer:
Because handles I/O requests synchronously, the driver can create request buffers that are local to the calling routine, as the following code example shows.
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
MY_BUFFER_TYPE myBuffer;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
(PVOID) &myBuffer,
sizeof(myBuffer));
· WDF_MEMORY_DESCRIPTOR memoryDescriptor;
· WDFMEMORY memoryHandle = NULL;
· status = WdfMemoryCreate(NULL,
· NonPagedPool,
· POOL_TAG,
· MY_BUFFER_SIZE,
· &memoryHandle,
· NULL);
· WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&memoryDescriptor,
· memoryHandle,
· NULL);
WDF_MEMORY_DESCRIPTOR writeBufDesc;
WDFMEMORY wdfMemory;
ULONG writeSize, bytesWritten;
size_t bufferSize;
NTSTATUS status;
writeSize = SMALL_BUF_LEN;
status = WdfMemoryCreate(
WDF_NO_OBJECT_ATTRIBUTES,
NonPagedPool,
0,
writeSize,
&wdfMemory,
NULL
);
if (!NT_SUCCESS(status)){
return status;
}
writeBuffer = WdfMemoryGetBuffer(
wdfMemory,
&bufferSize
);
FillMyBuffer(
writeBuffer,
writeSize
);
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(
&writeBufDesc,
writeBuffer,
writeSize
);
status = WdfUsbTargetPipeWriteSynchronously(
pipeHandle,
NULL,
NULL,
&writeBufDesc,
&bytesWritten
);
4)WdfUsbTargetPipeReadSynchronously
函数功能:builds a read request and sends it synchronously to a specified USB input pipe.
NTSTATUS WdfUsbTargetPipeReadSynchronously(
[in] WDFUSBPIPE Pipe, //input端点
[in, optional] WDFREQUEST Request, //Request
[in, optional] PWDF_REQUEST_SEND_OPTIONS RequestOptions,
[in, optional] PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,
[out, optional] PULONG BytesRead //指向接收数据的指针
);
Pipe [in]
A handle to a framework pipe object that was obtained by callingWdfUsbInterfaceGetConfiguredPipe.
Request [in, optional]
A handle to a framework request object. This parameter is optional and can be NULL. For more information, see the following Remarks section.
RequestOptions [in, optional]
A pointer to a caller-allocated WDF_REQUEST_SEND_OPTIONS structure that specifies options for the request. This pointer is optional and can be NULL. For more information, see the following Remarks section.
MemoryDescriptor [in, optional]
A pointer to a caller-allocated WDF_MEMORY_DESCRIPTOR structure that describes the buffer that will receive data from the device. The buffer size must be a multiple of the pipe's maximum packet size unless the driver has calledWdfUsbTargetPipeSetNoMaximumPacketSizeCheck. For more information about this buffer, see the following Remarks section.
BytesRead [out, optional]
A pointer to a location that receives the number of bytes that were read, if the operation succeeds. This parameter is optional and can be NULL
Msdn example
WDFMEMORY wdfMemory;
WDF_MEMORY_DESCRIPTOR readBufDesc;
ULONG BytesRead;
status = WdfMemoryCreate( WDF_NO_OBJECT_ATTRIBUTES,
NonPagedPool,
0,
readSize,
&wdfMemory,
NULL
);
if (!NT_SUCCESS(status)){
return ;
}
buffer = WdfMemoryGetBuffer(
wdfMemory,
NULL
);
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(
&readBufDesc,
buffer,
readSize
);
status = WdfUsbTargetPipeReadSynchronously(
Pipe, //input端点号,端点6
NULL,
NULL,
&readBufDesc, //缓冲区,缓冲区的大小必须是maxpacketsize的整数倍
&BytesRead