详细解析windows usb驱动和linux usb驱动的相似和差异(八)

时间:2021-08-21 17:42:00

版权信息:

   版权归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目标对象,同时可以有多个远程的目标对象。正因为WDFDEVICEWDFIOTARGET的这种关系,它们之间有通过其中一个获取另外一个的函数;如知道了WDFIOTARGET,就可以通过WdfIoTargetGetDevice得到WDFDEVICE对象的设备句柄,函数原型如下:

         WDFDEVICE WdfIoTargetGetDevice(IN WDFIOTARGET IoTarget);

同理,如果知道WDFDEVICE对象,通过WdfDeviceGetIoTarget得到WDFIOTARGET对象,函数原型如下:

         WDFIOTARGET WdfDeviceGetIoTarget((IN WDFDEVICE Device);

所获取的Io目标对象就是设备堆栈中的下一个设备了。在usbsamp中在遇到这个函数,到时就一目了然的知道它的作用了。

下面以wdk中的usbsamp的例子对wdfusb驱动开发进行讲解,讲解时对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例程。从ddkbulkusb中,我们知道,增加设备例程是通过IRP的派遣函数BulkUsb_AddDevice实现的,所以在bulkusb中,就很容易理解AddDevice函数是怎样的调用的了。而wdk中都是由事件驱动的,和ddk类似,它们都是由pnp在设备加载时进行调用。但是从WDK中看不到派遣函数的存在,难道WDF不采用派遣函数了吗,不是的,WDF还是和WDM一样采用派遣函数的,但是这部分内容都由WDF的框架为我们做完了,我们要做的就是填充几个回调函数了。讲到这里,我们知道WDFWDM的又一个不同就是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类型,有WdfDeviceIoDirectWdfDeviceIoBuffered两种,在本文中,设置成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中的bulkusbAddDevice一样,也是在DriverEntry定义:

DriverObject->DriverExtension->AddDevice           = (PDRIVER_ADD_DEVICE)

                                                        BulkUsb_AddDevice;

那么WDM中的AddDeviceWDF中的AddDevice有什么不同的,从代码中可以看出,在bulkusbDriverEntry中设置了很多回调函数,当设备插入系统时,有系统调用BulkUsb_AddDevice函数,然后AddDevice调用成功后,Pnp管理就开始发送PNP_MN_START_DEVICEIRP_MJ_PNP请求,然后会调用一个开始设备的函数(StartDevice)。在WDF中,EvtDevicePrepareHardware的功能和WDMbulkusb中的StartDevice类似,EvtDevicePrepareHardware的作用就是为设备申请资源,但是调用完它后,设备还不能正常的工作,因为还电源管理,因为电源管理还没有切换到D0供电状态。

 

DeviceAdd中创建了3个并行的队列。下面对DeviceAdd里面的内容再进行讲解。

1WdfDeviceCreate函数的作用是创建设备,原型如下:

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.

 

2WdfIoQueueCreate

函数功能: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个队列。这个队列是怎样调度的呢?

 

3WdfDeviceCreateDeviceInterface

函数功能:创建设备接口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

)

           。。。。。。。。。。。。。。。。。。。

  其中的大部分内容,这里就不列举了,在这个里面和ddkbulkusb类似,我们可以构建厂商请求,然后通过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

ddkbulkusb进行比较,我们前面讲解了ddk中构建一个厂商请求,然后发送给底层usb总线的方法。它首先通过UsbBuildVendorRequest构建一个厂商请求,然后调用CallUSBD将厂商请求的URB发送给底层usb总线驱动,而CallUSBD中又首先将urbIrp关联起来,然后通过IoCallDriver函数发送给usb总线驱动。而在wdkusbsamp中,构建厂商请求直接通过一个宏就完成了,该宏为:

WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR,然后通过:

WdfUsbTargetDeviceSendControlTransferSynchronously发送给底层的usb总线驱动。该函数是采用同步的方式发送,发送完成后必须等待函数返回。对于usb设备来说,wdfdriverstudio 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

在前面讲解WDFDEVICEWDFIOTARGET之间的关系时,我们说了在usb驱动程序中还会用到它们之间的转换函数WdfDeviceGetIoTarget获取WdfRequestSend的第2个参数,也就是将请求转发给下一层堆栈,或者说转发给下一个IO目标,只是在这里WdfDeviceGetIoTargetWdfUsbTargetDeviceGetIoTarget代替了而已。

 

讲解完上面的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及采用DDKbulkusb进行讲解时候,就讲到了类似的这些函数,比如在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,如果超过这个大小,就必须经过多次传输,上面的完成函数的作用就是做多次传输数据。到此为此,我们对wdkusbsamp分析完了。下面讲解下wdf下其它读写usb管道的函数,比如读写管道:

1WdfUsbTargetPipeFormatRequestForWrite

函数功能: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句柄用来存储需要写到管道的数据。

 

2WdfUsbTargetPipeFormatRequestForRead

函数功能:用来完成读管道的准备工作,参数和写操作一样,作用也很相近,用来完成读管道的准备工作。

NTSTATUS WdfUsbTargetPipeFormatRequestForRead(

  [in]            WDFUSBPIPE Pipe,

  [in]            WDFREQUEST Request,

  [in, optional]  WDFMEMORY ReadMemory,

  [in, optional]  PWDFMEMORY_OFFSET ReadOffset

);

 

以上两个函数还必须配合WdfRequestSend函数将请求发送给下层的Io目标,下面的2个函数是同步的方式读写管道。

3WdfUsbTargetPipeWriteSynchronously

函数功能: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.

Copy Code

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

                                           );

 

4WdfUsbTargetPipeReadSynchronously

函数功能: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