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

版权信息:

   版权归smilestone322所有,保留所有权利,仅用来学习,请勿用于商业用途,欢迎转载,转载请注明出处。谢谢!

3.2 ddk驱动开发

 

上面讲解了driver studio+ddk开发驱动的方法,但是开发driver studio的公司已经停止对ds3.2进行维护了,ds版本最终定格在3.2,换句话说ds 3.2 已经成为了过气的明星,不过初学者学习 driver studio进行入门还是挺不错的,等入门了,学习DDK也变得相对容易了,因为ds3.2就是对ddk进行封装,ddk采用C语言,而ds3.2使用c++开发驱动,其实原理都是一样的,呵呵linux的创始人linus就说过,不建议使用c++,采用c语言可以搞定一切,所以在linux系统中都是采用c语言编写的,包括它的内核,在linux的驱动开发基础中,我们也讲过linux内核里面无处不包含c++的思想,可见dw3.2ddk是通的,就像学习武林秘籍,一通百通,题外话说多了。下面转入正题,讲解ddk驱动开发,在这一部分中,边讲解边和dw3.2驱动开发进行对比,这样才能提高。

       分析驱动程序和c语言一样,从main函数开始,但是驱动里面的main函数就是DriverEntry函数,下面我以ddk 2600下的bulkusb的例子开始讲解本节的内容。

DriverEntry例程是驱动程序的入口点,由I/O管理器在驱动程序加载时调用,它负责执行一些初始化操作,主要工作是设置驱动程序对象中指向各种例程的指针。在驱动程序中必须包含这些例程的具体函数实现,以便主机系统软件的调用。ddkDriverEntry的函数如下:NTSTATUS

DriverEntry(

    IN PDRIVER_OBJECT  DriverObject,

    IN PUNICODE_STRING UniRegistryPath

    )

{

    NTSTATUS        ntStatus;

    PUNICODE_STRING registryPath;

   //

   // initialization of variables

   //

    registryPath = &Globals.BulkUsb_RegistryPath;

   //

   // Allocate pool to hold a null-terminated copy of the path.

   // Safe in paged pool since all registry routines execute at

   // PASSIVE_LEVEL.

   //

    registryPath->MaximumLength = UniRegistryPath->Length + sizeof(UNICODE_NULL);

    registryPath->Length        = UniRegistryPath->Length;

    registryPath->Buffer        = ExAllocatePool(PagedPool,

                                                 registryPath->MaximumLength);

    if (!registryPath->Buffer) {

        BulkUsb_DbgPrint(1, ("Failed to allocate memory for registryPath\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;

        goto DriverEntry_Exit;

    }

    RtlZeroMemory (registryPath->Buffer,

                   registryPath->MaximumLength);

    RtlMoveMemory (registryPath->Buffer,

                   UniRegistryPath->Buffer,

                   UniRegistryPath->Length);

    ntStatus = STATUS_SUCCESS;

   //

   // Initialize the driver object with this driver's entry points.

   //

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = BulkUsb_DispatchDevCtrl;

  /*BulkUsb_DispatchDevCtrl设备控制的函数,相当于driver studio 3.2中的DeviceIoControl函数对应的驱动里面的DeviceControl函数*/

    DriverObject->MajorFunction[IRP_MJ_POWER]          = BulkUsb_DispatchPower;

 /* BulkUsb_DispatchPower电源管理派遣例程 */

    DriverObject->MajorFunction[IRP_MJ_PNP]           = BulkUsb_DispatchPnP;

/* BulkUsb_DispatchPnP即插即用派遣例程 */

    DriverObject->MajorFunction[IRP_MJ_CREATE]         = BulkUsb_DispatchCreate;

/*CreateFile对应的打开文件对象派遣例程*/

    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = BulkUsb_DispatchClose;

   /*CreateFile对应的派遣例程*/

    DriverObject->MajorFunction[IRP_MJ_CLEANUP]        = BulkUsb_DispatchClean;

  /*CloseFile对应的派遣例程*/

    DriverObject->MajorFunction[IRP_MJ_READ]           =

    DriverObject->MajorFunction[IRP_MJ_WRITE]          = BulkUsb_DispatchReadWrite;

   /*ReadFile/WriteFile对应的读写函数派遣例程*/

    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = BulkUsb_DispatchSysCtrl;

    /*系统控制派遣例程*/

    DriverObject->DriverUnload                         = BulkUsb_DriverUnload;

   /*卸载设备对应的派遣例程*/

    DriverObject->DriverExtension->AddDevice           = (PDRIVER_ADD_DEVICE)

                                                         BulkUsb_AddDevice;

  /*AddDevice对象的派遣例程*/

DriverEntry_Exit:

    return ntStatus;

}

一看这个DriverEntry就比driver studio 3.2usbbulk的那个复杂的多,在该例程中,指出了驱动程序其他例程的名字,如即插即用例程为DispatchPnP,分发例程中为DispatchReadWrite(数据读写)、DispatchCreate(创建句柄)等。ds3.2中的DriverEntry就是空壳子,什么都没有,因为driver studio将它们都封装起来了,比如说在ds3.2中应用程序采用ReadFile进行读数据的时候,我们知道驱动程序是通过IRP和应用程序对应起来的,但是这种对应关系是怎样建立起来的呢,ds3.2中就看不到了,都封装起来了。但是在ddk中就看的很明显了,在DriverEntry中,将Irp和派遣函数联系起来了,比如:

DriverObject->MajorFunction[IRP_MJ_WRITE]          = BulkUsb_DispatchReadWrite;

当应用程序调用WriteFile时,会产生一个IRP_MJ_WRITE,它对应着BulkUsb_DispatchReadWrite函数,在ds3.2就没有派遣函数的概念。

    WDMddk中,AddDevice函数也比ds3.2中清楚多了,同样也是作为一个派遣例程调用的,同时pnp也是作为派遣例程调用,这样我们就可以清楚的看到它们的实现,及应用程序与驱动通信的过程。

       ddkbulkusb中,驱动程序向系统注册了一个AddDevice例程,名字叫BulkUsb_AddDevice该例程由pnp管理器负责调用,其主要职责是创建设备对象。BulkUsb_AddDevice源码如下,它有2个参数,DriverObject是由pnp管理器传递进来的驱动对象,PhysicalDeviceObjectpnp管理器传递的底层驱动对象。

NTSTATUS

BulkUsb_AddDevice(

    IN PDRIVER_OBJECT DriverObject,

    IN PDEVICE_OBJECT PhysicalDeviceObject

    )

{

    NTSTATUS          ntStatus;

    PDEVICE_OBJECT    deviceObject;

    PDEVICE_EXTENSION deviceExtension;

    POWER_STATE       state;

    KIRQL             oldIrql;

    BulkUsb_DbgPrint(3, ("BulkUsb_AddDevice - begins\n"));

    deviceObject = NULL;

    ntStatus = IoCreateDevice(

                    DriverObject,                     // our driver object

                    sizeof(DEVICE_EXTENSION),      // extension size for us

                    NULL,                          // name for this device

                    FILE_DEVICE_UNKNOWN,

                    FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics

                    FALSE,                         // Not exclusive

                    &deviceObject);                   // Our device object

/*

      IoCreateDevice 用于创建设备对象,它的原型如下:

      NTSTATUS IoCreateDevice

  (

  IN PDRIVER_OBJECTDriverObject,

  IN ULONGDeviceExtensionSize,

  IN PUNICODE_STRINGDeviceName OPTIONAL,

  IN DEVICE_TYPEDeviceType,

  IN ULONGDeviceCharacteristics,

  IN BOOLEANExclusive,

  OUT PDEVICE_OBJECT *DeviceObject

);

参数说明:

DriverObject:一个指向调用该函数的驱动程序对象,每个驱动程序在它的DriverEntry过程里接收一个指向它的驱动程序对象。

DeviceExtensionSize指定驱动程序为设备扩展对象而定义的结构体的大小。

(可选的参数)指向一个以零结尾的包含Unicode字符串的缓冲区,那是这个设备的名称,该字符串必须是一个完整的设备路径名。

DeviceType指定一个由一个系统定义的FILE_DEVICE_XXX常量,表明了这个设备的类型。如FILE_DEVICE_KEYBOARD等。

DeviceCharacteristics指定一个或多个系统定义的常量,连接在一起,提供有关驱动程序的设备其他信息.对于可能的设备特征信息, 见DEVICE_OBJECT结构体.

Exclusive如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果是独占的话设置为TRUE,非独占设置为FALSE

DeviceObject一个指向DEVICE_OBJECT结构体指针的指针,这是一个指针的指针,指向的指针用来接收DEVICE_OBJECT结构体的指针。

返回值:IoCreateDevice函数成功时返回STATUS_SUCCESS

*/

    if(!NT_SUCCESS(ntStatus)) {        

        BulkUsb_DbgPrint(1, ("Failed to create device object\n"));

        return ntStatus;

    }

    // 初始化设备扩展

    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;

deviceExtension->FunctionalDeviceObject =deviceObject;

/*设备扩展中保存IoCreateDevice建立的deviceObject*/

    deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;

    deviceObject->Flags |= DO_DIRECT_IO;                        //直接方式读写

   //

   // initialize the device state lock and set the device state

   //

 

    KeInitializeSpinLock(&deviceExtension->DevStateLock);     //初始化自旋锁

    INITIALIZE_PNP_STATE(deviceExtension);

    //

   //initialize OpenHandleCount

//

    deviceExtension->OpenHandleCount = 0;

   //

   // Initialize the selective suspend variables

   //

    KeInitializeSpinLock(&deviceExtension->IdleReqStateLock);

    deviceExtension->IdleReqPend = 0;

    deviceExtension->PendingIdleIrp = NULL;

   //

   // Hold requests until the device is started

   //

    deviceExtension->QueueState = HoldRequests;

    //

   // Initialize the queue and the queue spin lock

   //

    InitializeListHead(&deviceExtension->NewRequestsQueue);

    KeInitializeSpinLock(&deviceExtension->QueueLock);

   //

   // Initialize the remove event to not-signaled.

   //

    KeInitializeEvent(&deviceExtension->RemoveEvent,

                      SynchronizationEvent,

                      FALSE);

 

    //

   // Initialize the stop event to signaled.

   // This event is signaled when the OutstandingIO becomes 1

   //

 

    KeInitializeEvent(&deviceExtension->StopEvent,

                      SynchronizationEvent,

                      TRUE);

 

    //

   // OutstandingIo count biased to 1.

   // Transition to 0 during remove device means IO is finished.

   // Transition to 1 means the device can be stopped

   //

 

    deviceExtension->OutStandingIO = 1;

    KeInitializeSpinLock(&deviceExtension->IOCountLock);

   //

   // Delegating to WMILIB

   //

    ntStatus = BulkUsb_WmiRegistration(deviceExtension);

    if(!NT_SUCCESS(ntStatus)) {

 

        BulkUsb_DbgPrint(1, ("BulkUsb_WmiRegistration failed with %X\n", ntStatus));

        IoDeleteDevice(deviceObject);

        return ntStatus;

    }

 

    //

   // set the flags as underlying PDO

   //

 

    if(PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) {

        deviceObject->Flags |= DO_POWER_PAGABLE;      //电源管理

    }

   //初始化电源管理的状态 D0

    deviceExtension->DevPower = PowerDeviceD0;

    deviceExtension->SysPower = PowerSystemWorking;

    state.DeviceState = PowerDeviceD0;

    PoSetPowerState(deviceObject, DevicePowerState, state);

 

   //

   // attach our driver to device stack

   // The return value of IoAttachDeviceToDeviceStack is the top of the

   // attachment chain.  This is where all the IRPs should be routed.

   //

    deviceExtension->TopOfStackDeviceObject =

                IoAttachDeviceToDeviceStack(deviceObject,

                                            PhysicalDeviceObject);

/* IoAttachDeviceToDeviceStack函数的作用是将IoCreateDevice创建的设备对象挂载到pdo,并且返回deviceObjcet下层的堆栈指针,如果没有过滤驱动的话就是返回pdo*/

 

    if(NULL == deviceExtension->TopOfStackDeviceObject) {

 

        BulkUsb_WmiDeRegistration(deviceExtension);

        IoDeleteDevice(deviceObject);

        return STATUS_NO_SUCH_DEVICE;

    }

       

   //

   // Register device interfaces

   //

 

    ntStatus = IoRegisterDeviceInterface(deviceExtension->PhysicalDeviceObject,

                                         &GUID_CLASS_CCD_BULK,

                                         NULL,

                                         &deviceExtension->InterfaceName);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_WmiDeRegistration(deviceExtension);

        IoDetachDevice(deviceExtension->TopOfStackDeviceObject);

        IoDeleteDevice(deviceObject);

        return ntStatus;

    }

    if(IoIsWdmVersionAvailable(1, 0x20)) {

        deviceExtension->WdmVersion = WinXpOrBetter;

    }

    else if(IoIsWdmVersionAvailable(1, 0x10)) {

        deviceExtension->WdmVersion = Win2kOrBetter;

    }

    else if(IoIsWdmVersionAvailable(1, 0x5)) {

        deviceExtension->WdmVersion = WinMeOrBetter;

    }

    else if(IoIsWdmVersionAvailable(1, 0x0)) {

        deviceExtension->WdmVersion = Win98OrBetter;

    }

    deviceExtension->SSRegistryEnable = 0;

    deviceExtension->SSEnable = 0;

   //

   // WinXP only

   // check the registry flag -

   // whether the device should selectively

   // suspend when idle

   //

    if(WinXpOrBetter == deviceExtension->WdmVersion) {

        BulkUsb_GetRegistryDword(BULKUSB_REGISTRY_PARAMETERS_PATH,

                                 L"BulkUsbEnable",

                                 &deviceExtension->SSRegistryEnable);

        if(deviceExtension->SSRegistryEnable) {

            //

           // initialize DPC

           //

            KeInitializeDpc(&deviceExtension->DeferredProcCall,

                            DpcRoutine,

                            deviceObject);

            //

           // initialize the timer.

           // the DPC and the timer in conjunction,

           // monitor the state of the device to

           // selectively suspend the device.

           //

            KeInitializeTimerEx(&deviceExtension->Timer,

                                NotificationTimer);                     //初始化定时器

 

            //

           // Initialize the NoDpcWorkItemPendingEvent to signaled state.

           // This event is cleared when a Dpc is fired and signaled

           // on completion of the work-item.

            //

            KeInitializeEvent(&deviceExtension->NoDpcWorkItemPendingEvent,

                              NotificationEvent,

                              TRUE);

            //

           // Initialize the NoIdleReqPendEvent to ensure that the idle request

           // is indeed complete before we unload the drivers.

           //

            KeInitializeEvent(&deviceExtension->NoIdleReqPendEvent,

                              NotificationEvent,

                              TRUE);

        }

    }

   //

   // Clear the DO_DEVICE_INITIALIZING flag.

   // Note: Do not clear this flag until the driver has set the

   // device power state and the power DO flags.

   //

    deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    BulkUsb_DbgPrint(3, ("BulkUsb_AddDevice - ends\n"));

    return ntStatus;

}

       这个函数和ds 3.2比的话,复杂多了,主要的工作是创建一个设备对象,并将它挂载到设备堆栈,在这个函数还初始化了设备扩展对象,电源管理等等。

       Windows驱动程序一个重要的数据结构就是IRP,应用程序采用CreateFileReadFileWriteFileCloseFilei/o操作,在驱动程序中都会转化成IRP,然后在驱动中会处理这个IRP,然后完成IRP请求,完成IRP的函数为IoCompleteRequest。它的原型如下:

       VOID IoCompleteRequest

                     IN PIRP Irp

                    IN CCHAR PriorityBoost

                           );

   其中参数:Irp表示需要结束的IRPPriorityBoost:表示线程恢复时的优先级别,一般为IO_NO_INCREMENT

   讲解完DriverEntryAddDevice函数就接着讲解DriverEntry中重要的派遣函数了,对于我们开发驱动的人员来说,ddk bulkusb这个例子很有借鉴意义。因为它已经为我们实现了Pnp及电源管理,同时很好的支持了bulk读写,这些几乎在开发驱动中都不要修改,我们需要修改的是给硬件发送控制命令,即发送厂商请求的部分代码。我们可以在BulkUsb_DispatchDevCtrl里面添加一个case分支用来发送厂商请求,就可以将bulkusb改造成我们的usb驱动程序了。NTSTATUS

BulkUsb_DispatchDevCtrl(

    IN PDEVICE_OBJECT DeviceObject,

    IN PIRP Irp

    )

{

    ULONG              code;

    PVOID              ioBuffer;

    ULONG              inputBufferLength;

    ULONG              outputBufferLength;

    ULONG              info;

    NTSTATUS           ntStatus;

    PDEVICE_EXTENSION  deviceExtension;

    PIO_STACK_LOCATION irpStack;

    info = 0;

    irpStack = IoGetCurrentIrpStackLocation(Irp);                                                   

    code = irpStack->Parameters.DeviceIoControl.IoControlCode;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    ioBuffer           = Irp->AssociatedIrp.SystemBuffer;

    inputBufferLength  = irpStack->Parameters.DeviceIoControl.InputBufferLength;   //输入缓冲区的大小

    outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; //得到输出缓冲区的大小

    if(deviceExtension->DeviceState != Working) {

        BulkUsb_DbgPrint(1, ("Invalid device state\n"));

        Irp->IoStatus.Status = ntStatus = STATUS_INVALID_DEVICE_STATE;

        Irp->IoStatus.Information = info;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return ntStatus;

    }

    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchDevCtrl::"));

    BulkUsb_IoIncrement(deviceExtension);

    BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));

    if(deviceExtension->SSEnable) {

        KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,

                              Executive,

                              KernelMode,

                              FALSE,

                              NULL);

    }

    switch(code) {

    case IOCTL_BULKUSB_RESET_PIPE://设置端点

    {

        PFILE_OBJECT           fileObject;

        PUSBD_PIPE_INFORMATION pipe;

        pipe = NULL;

        fileObject = NULL;

        fileObject = irpStack->FileObject;

        if(fileObject == NULL) {

            ntStatus = STATUS_INVALID_PARAMETER;

            break;

        }

        pipe = (PUSBD_PIPE_INFORMATION) fileObject->FsContext;

        if(pipe == NULL) {

            ntStatus = STATUS_INVALID_PARAMETER;

        }

        else {

            ntStatus = BulkUsb_ResetPipe(DeviceObject, pipe);

        }

        break;

    }

    case IOCTL_BULKUSB_GET_CONFIG_DESCRIPTOR://获取设备描述符

    {

        ULONG length;

        if(deviceExtension->UsbConfigurationDescriptor) {

            length = deviceExtension->UsbConfigurationDescriptor->wTotalLength;

            if(outputBufferLength >= length) {

                RtlCopyMemory(ioBuffer,

                              deviceExtension->UsbConfigurationDescriptor,

                              length);

                info = length;

                ntStatus = STATUS_SUCCESS;

            }

            else {

                ntStatus = STATUS_BUFFER_TOO_SMALL;

            }

        }

        else {

            ntStatus = STATUS_UNSUCCESSFUL;

        }

        break;

    }

    case IOCTL_BULKUSB_RESET_DEVICE:        //设置设备

        ntStatus = BulkUsb_ResetDevice(DeviceObject);

        break;

       

    case IOCTL_BULKUSB_XXXXXX://我们自己的命令

       {

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

       }  

       break;

    default :

        ntStatus = STATUS_INVALID_DEVICE_REQUEST;

        break;

    }

    Irp->IoStatus.Status = ntStatus;

    Irp->IoStatus.Information = info;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchDevCtrl::"));

    BulkUsb_IoDecrement(deviceExtension);

    return ntStatus;

}

       IOCTL_BULKUSB_XXXXXX中我们可以这样写,首先通过UsbBuildVendorRequest构建一个厂商请求,然后调用CallUSBD将厂商请求的URB发送给底层usb总线,然后在转发给硬件。UsbBuildVendorRequest的函数原型如下:

         void UsbBuildVendorRequest(

 [in]            PURB Urb,             //

 [in]            USHORT Function,

 [in]            USHORT Length,

 [in]            ULONG TransferFlags,

 [in]            UCHAR ReservedBits,

 [in]            UCHAR Request,//对应usb设备请求中的bRequest

 [in]            USHORT Value,       //对应usb设备请求中的wValue

 [in]            USHORT Index,//对应usb设备请求中wIndex

 [in, optional]  PVOID TransferBuffer,       //缓冲区

 [in, optional]  PMDL TransferBufferMDL,

 [in]            ULONG TransferBufferLength,//传输的长度,对应usb设备请求中WLength

 [in]            PURB Link

);

 

       调用实例:

       UsbBuildVendorRequest(

                                          urb,

                                          URB_FUNCTION_VENDOR_DEVICE,

                                          (USHORT)sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),

                                          0,

                                          0x0,

                                          XXX, //请求类型

                                          0,

                                          0,

                                          (PVOID)((unsigned char*)buffer),

                                          NULL,

                                          8,

                                          NULL                         

                                          );

 

CallUSBD的源码如下:

NTSTATUS

CallUSBD(

    IN PDEVICE_OBJECT DeviceObject,

    IN PURB           Urb

    )

{

    PIRP               irp;

    KEVENT             event;

    NTSTATUS           ntStatus;

    IO_STATUS_BLOCK    ioStatus;

    PIO_STACK_LOCATION nextStack;

    PDEVICE_EXTENSION  deviceExtension;

    irp = NULL;

    deviceExtension = DeviceObject->DeviceExtension;

KeInitializeEvent(&event, NotificationEvent, FALSE);

//创建我们的IO控制码的相关的IRP

    irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,

                                        deviceExtension->TopOfStackDeviceObject,

                                        NULL,

                                        0,

                                        NULL,

                                        0,

                                        TRUE,

                                        &event,

                                        &ioStatus);

    if(!irp) {

        BulkUsb_DbgPrint(1, ("IoBuildDeviceIoControlRequest failed\n"));

        return STATUS_INSUFFICIENT_RESOURCES;

}

//得到下一层的设备堆栈

    nextStack = IoGetNextIrpStackLocation(irp);

    ASSERT(nextStack != NULL);

    nextStack->Parameters.Others.Argument1 = Urb;

    BulkUsb_DbgPrint(3, ("CallUSBD::"));

BulkUsb_IoIncrement(deviceExtension);

//irp请求发给底层usb总线驱动,然后转发给硬件

ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);

//如果irp为异步完成,必须等待它结束,KeWaitForSingleObject函数和应用程序的WaitForSingleObject类似,等待event有信号。

    if(ntStatus == STATUS_PENDING) {

        KeWaitForSingleObject(&event,

                              Executive,

                              KernelMode,

                              FALSE,

                              NULL);

        ntStatus = ioStatus.Status;

    }

    BulkUsb_DbgPrint(3, ("CallUSBD::"));

    BulkUsb_IoDecrement(deviceExtension);

    return ntStatus;

}

      

其中IoBuildDeviceIoControlRequest函数的作用在我们采用UsbBuildVendorRequest构造完URB后,URB要和一个IRP关联起来,这就是这个函数的作用,在driverstudio 3.2中,构造完厂商请求后,直接调用SubmitUrb直接将urb发送到底层usb总线了,但是在ddk中必须采用IoBuildDeviceIoControlRequest创建一个io请求的控制码,然后将URB作为Irp的参数,然后调用IoCallDriverUrb发送到底层usb总线。

函数功能:allocates and sets up an IRP for a synchronously processed device control request.函数主要用来构造一个用于设备i/o控制请求的irp包,该irp包将被同步处理。

PIRPIoBuildDeviceIoControlRequest(

 __in       ULONG IoControlCode,

 __in       PDEVICE_OBJECT DeviceObject,

 __in_opt   PVOID InputBuffer,

 __in       ULONG InputBufferLength,

 __out_opt  PVOID OutputBuffer,

 __in       ULONG OutputBufferLength,

 __in       BOOLEAN InternalDeviceIoControl,

 __in       PKEVENT Event,

 __out      PIO_STATUS_BLOCK IoStatusBlock

);

 

参数:

IoControlCode

提供i/o控制请求所需的i/o控制码。这个i/o控制码可以在msdn中查询到。

DeviceObject

   指向下层驱动的设备对象的指针。这个就是构造的irp要被发向的目标对象。

InputBuffer

   指向输入缓冲区的指针,这个缓冲区中的内容是给下层驱动使用的。此指针可为NULL

InputBufferLength [in]

   输入缓冲区的长度,按字节计算。如果InputBufferNULL,则此参数必须为0

OutputBuffer

指向输出缓冲区的指针,这个缓冲区是用于给下层驱动返回数据用的。此指针可为NULL

 OutputBufferLength

          输出缓冲区的长度,按字节计算。如果OutputBufferNULL,则此参数必须为0

InternalDeviceIoControl

如果此参数为TRUE,这个函数设置所构造的irp的主函数码(major function code)为IRP_MJ_INTERNAL_DEVICE_CONTROL,否则这个函数设置所构造的irp的主函数码(major function code)为IRP_MJ _DEVICE_CONTROL

Event

提供一个指向事件对象的指针,该事件对象由调用者分配并初始化。当下层驱动程序完成这个irp请求时i/o管理器将此事件对象设置为通知状态(signaled)。当调用IoCallDriver后,调用者可以等待这个事件对象成为通知状态。

IoStatusBlock

调用者指定一个i/o状态块,当这个irp完成时,下层驱动会把相应信息填入这个i/o状态块。

 

返回值:

       当这个函数调用成功时,将返回一个指向所构造的irp的指针并且下一层驱动的i/o堆栈会根据调用此函数提供的参数设置好,若调用失败,将返回NULL

 

注意事项:

1 此函数构造的irp包将被同步处理。当构造好irp包后,调用者调用IoCallDriver将这个irp发送给目标对象,如果IoCallDriver返回STATUS_PENDING,调用者必须调用KeWaitForSingleObject等待调用IoBuildDeviceIoControlRequest时所提供的那个Event。对于大多数的驱动程序我们不用给该irp设置完成函数。

2 IoBuildDeviceIoControlRequest构造的irp必须由某个驱动调用IoCompleteRequest来完成,并且注意调用IoBuildDeviceIoControlRequest的驱动程序不能调用IoFreeIrp来释放这些构造的irp,因为i/o管理器会在IoCompleteRequest被调用后自动释放这些irp

3  IoBuildDeviceIoControlRequest将把它构造的irp放在当前线程特有的一个irp队列上,如果当前线程退出,则i/o管理器将取消这些irp

4  InputBufferOutputBuffer这两个参数如何存放在所构造的irp中将取决于IoControlCodeTransferType,具体可查相关资料。

5  IoBuildDeviceIoControlRequest的调用者必须运行在IRQL <= APC_LEVEL

6 这个函数并不初始化所构造irp中的FileObject指针,因此如果你在写和文件系统相关的驱动,你必须自己初始化这个指针。

7 使用IoBuildDeviceIoControlRequest构造的irp其主函数代码只能是IRP_MJ_DEVICE_CONTROLIRP_MJ_INTERNAL_DEVICE_CONTROL

 

IoCallDriver的函数原型如下:

1.       NTSTATUS    

2.         IoCallDriver(   

3.           IN PDEVICE_OBJECT  DeviceObject,   

4.           IN OUT PIRP  Irp   

5.           );  

 

The IoCallDriver routine sends an IRP to the driver associated with a specified device object.( IoCallDriver这个函数向DeviceObject设备对象的驱动对象发送一个IRP请求)

 

发送控制命令的部分讲完后,就轮到讲解usb设备的读写了,在bulkusb中,读写派遣例程放在同一个函数中,这个函数可以不用修改,直接搬到我们的驱动程序中,在DriverEntry我们知道这个派遣例程就是BulkUsb_DispatchReadWrite,源码如下:

NTSTATUS

BulkUsb_DispatchReadWrite(

    IN PDEVICE_OBJECT DeviceObject,

    IN PIRP           Irp

    )

{

    PMDL                   mdl;

    PURB                   urb;

    ULONG                  totalLength;

    ULONG                  stageLength;

    ULONG                  urbFlags;

    BOOLEAN                read;

    NTSTATUS               ntStatus;

    ULONG_PTR              virtualAddress;

    PFILE_OBJECT           fileObject;

    PDEVICE_EXTENSION      deviceExtension;

    PIO_STACK_LOCATION     irpStack;

    PIO_STACK_LOCATION     nextStack;

    PBULKUSB_RW_CONTEXT    rwContext;

    PUSBD_PIPE_INFORMATION pipeInformation;

 

    urb = NULL;

    mdl = NULL;

    rwContext = NULL;

    totalLength = 0;

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    fileObject = irpStack->FileObject;

    read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;//判断是读还是写

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - begins\n"));

    if(deviceExtension->DeviceState != Working) {

        BulkUsb_DbgPrint(1, ("Invalid device state\n"));

        ntStatus = STATUS_INVALID_DEVICE_STATE;

        goto BulkUsb_DispatchReadWrite_Exit;

    }

    BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));

    if(deviceExtension->SSEnable) {

        KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, //

                              Executive,

                              KernelMode,

                              FALSE,

                              NULL);

    }

    if(fileObject && fileObject->FsContext) {

        pipeInformation = fileObject->FsContext;

        if(UsbdPipeTypeBulk != pipeInformation->PipeType) {

            BulkUsb_DbgPrint(1, ("Usbd pipe type is not bulk\n"));

            ntStatus = STATUS_INVALID_HANDLE;

            goto BulkUsb_DispatchReadWrite_Exit;

        }

    }

    else {

        BulkUsb_DbgPrint(1, ("Invalid handle\n"));

        ntStatus = STATUS_INVALID_HANDLE;

        goto BulkUsb_DispatchReadWrite_Exit;

    }

    rwContext = (PBULKUSB_RW_CONTEXT)

                ExAllocatePool(NonPagedPool,

                               sizeof(BULKUSB_RW_CONTEXT));

    if(rwContext == NULL) {

        BulkUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;

        goto BulkUsb_DispatchReadWrite_Exit;

    }

    if(Irp->MdlAddress) {

        totalLength = MmGetMdlByteCount(Irp->MdlAddress);

    }

   

//如果总的长度比BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE还是要大,那么就退出

    if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE) {

        BulkUsb_DbgPrint(1, ("Transfer length > circular buffer\n"));

        ntStatus = STATUS_INVALID_PARAMETER;

        ExFreePool(rwContext);

        goto BulkUsb_DispatchReadWrite_Exit;

    }

    if(totalLength == 0) {

        BulkUsb_DbgPrint(1, ("Transfer data length = 0\n"));

        ntStatus = STATUS_SUCCESS;

        ExFreePool(rwContext);

        goto BulkUsb_DispatchReadWrite_Exit;

    }

    urbFlags = USBD_SHORT_TRANSFER_OK;

    virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);

    if(read) {

        urbFlags |= USBD_TRANSFER_DIRECTION_IN;           //传输方向为In,为读端点

        BulkUsb_DbgPrint(3, ("Read operation\n"));

    }

    else {

        urbFlags |= USBD_TRANSFER_DIRECTION_OUT;

        BulkUsb_DbgPrint(3, ("Write operation\n"));      //传输方向为Out,为写端点

}

//如果总的传输大小大于BULKUSB_MAX_TRANSFER_SIZE,那么

    if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {

        stageLength = BULKUSB_MAX_TRANSFER_SIZE;

    }

    else {

        stageLength = totalLength;

    }

 

//建立MDL

    mdl = IoAllocateMdl((PVOID) virtualAddress,

                        totalLength,

                        FALSE,

                        FALSE,

                        NULL);

    if(mdl == NULL) {

        BulkUsb_DbgPrint(1, ("Failed to alloc mem for mdl\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;

        ExFreePool(rwContext);

        goto BulkUsb_DispatchReadWrite_Exit;//转退出

    }

   //

   // map the portion of user-buffer described by an mdl to another mdl

   //将新MDL进行映射

    IoBuildPartialMdl(Irp->MdlAddress,

                      mdl,

                      (PVOID) virtualAddress,

                      stageLength);

//申请URB数据结构

    urb = ExAllocatePool(NonPagedPool,

                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));

    if(urb == NULL) {

        BulkUsb_DbgPrint(1, ("Failed to alloc mem for urb\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;

        ExFreePool(rwContext);

        IoFreeMdl(mdl);

        goto BulkUsb_DispatchReadWrite_Exit;

    }

//建立中断或批传输请求,这个也是构建urb请求

    UsbBuildInterruptOrBulkTransferRequest(

                            urb,

                            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),

                            pipeInformation->PipeHandle,

                            NULL,

                            mdl,

                            stageLength,

                            urbFlags,

                            NULL);

   //

   // set BULKUSB_RW_CONTEXT parameters.

   //设置完成例程参数

    rwContext->Urb             = urb;

    rwContext->Mdl             = mdl;

    rwContext->Length          = totalLength - stageLength;

    rwContext->Numxfer         = 0;

    rwContext->VirtualAddress  = virtualAddress + stageLength;

    rwContext->DeviceExtension = deviceExtension;

   //

   // use the original read/write irp as an internal device control irp

   //设置设备堆栈

    nextStack = IoGetNextIrpStackLocation(Irp);

    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    nextStack->Parameters.Others.Argument1 = (PVOID) urb;

    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;

       //设置完成例程

    IoSetCompletionRoutine(Irp,

                        (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,

                           rwContext,

                           TRUE,

                           TRUE,

                           TRUE);

 

       //将当前IRP阻塞

    IoMarkIrpPending(Irp);

    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite::"));

    BulkUsb_IoIncrement(deviceExtension);

      //IRP转发到底层USB总线驱动

    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,Irp);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("IoCallDriver fails with status %X\n", ntStatus));

        if((ntStatus != STATUS_CANCELLED) &&

           (ntStatus != STATUS_DEVICE_NOT_CONNECTED)) {

            ntStatus = BulkUsb_ResetPipe(DeviceObject,

                                     pipeInformation);

            if(!NT_SUCCESS(ntStatus)) {

                BulkUsb_DbgPrint(1, ("BulkUsb_ResetPipe failed\n"));

                ntStatus = BulkUsb_ResetDevice(DeviceObject);

            }

        }

        else {

            BulkUsb_DbgPrint(3, ("ntStatus is STATUS_CANCELLED or "

                                 "STATUS_DEVICE_NOT_CONNECTED\n"));

        }

    }

    return STATUS_PENDING;

 

BulkUsb_DispatchReadWrite_Exit:

    Irp->IoStatus.Status = ntStatus;

       Irp->IoStatus.Information= rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;

//完成Irp请求

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchReadWrite - ends\n"));

    return ntStatus;

}

  在应用程序调用了ReadFileWriteFile函数后,会产生2IRP请求,IRP_MJ_READIRP_MJ_WRITE,也就是对应上面的派遣例程了,在BulkUsb_DispatchReadWrite中设置了完成例程,在完成例程中将读写的size分成BULKUSB_MAX_TRANSFER_SIZE的若干块,一次将它们发送到usb总线驱动,然后调用完成例程读完BULKUSB_MAX_TRANSFER_SIZE的大小数据后,在BulkUsb_ReadWriteCompletion函数中有调用了IoCallDriver将请求发送给usb总线驱动,。。。。。。,直到数据都读写完成。完成例程的源码如下:

NTSTATUS

BulkUsb_ReadWriteCompletion(

    IN PDEVICE_OBJECT DeviceObject,

    IN PIRP           Irp,

    IN PVOID          Context

    )

{

    ULONG               stageLength;

    NTSTATUS            ntStatus;

    PIO_STACK_LOCATION  nextStack;

    PBULKUSB_RW_CONTEXT rwContext;

    rwContext = (PBULKUSB_RW_CONTEXT) Context;

    ntStatus = Irp->IoStatus.Status;

    UNREFERENCED_PARAMETER(DeviceObject);

    BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - begins\n"));

    if(NT_SUCCESS(ntStatus)) {

        if(rwContext) {

            rwContext->Numxfer +=

rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;//得到一次传输的长度

            if(rwContext->Length) {                                                                                                               //length=0表示已经传输完

                BulkUsb_DbgPrint(3, ("Another stage transfer...\n"));

                if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {

                    stageLength = BULKUSB_MAX_TRANSFER_SIZE;

                }

                else {

                    stageLength = rwContext->Length;

                }

                IoBuildPartialMdl(Irp->MdlAddress,

                                  rwContext->Mdl,

                                  (PVOID) rwContext->VirtualAddress,

                                  stageLength);

                rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength

                                                                  = stageLength;

                rwContext->VirtualAddress += stageLength;

                rwContext->Length -= stageLength;

                nextStack = IoGetNextIrpStackLocation(Irp);

                nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

                nextStack->Parameters.Others.Argument1 = rwContext->Urb;

                nextStack->Parameters.DeviceIoControl.IoControlCode =

                                           IOCTL_INTERNAL_USB_SUBMIT_URB;

                IoSetCompletionRoutine(Irp,

                                      BulkUsb_ReadWriteCompletion,

                                       rwContext,

                                       TRUE,

                                       TRUE,

                                       TRUE);

                IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,

                             Irp);

                return STATUS_MORE_PROCESSING_REQUIRED;

            }

            else {

                Irp->IoStatus.Information = rwContext->Numxfer;

            }

        }

    }

    else {

        BulkUsb_DbgPrint(1, ("ReadWriteCompletion - failed with status = %X\n", ntStatus));

    }

   

    if(rwContext) {

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

    }

 

    BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion - ends\n"));

    return ntStatus;

}

 

  其它派遣例程就不在进行讲解了,几乎可以不用修改的应用,但是电源管理派遣例程中的一个函数,在这里在讲解下,PoCallDriver,初一看这个函数和IoCallDriver函数一样,连参数都一样,其函数原型:

1.             NTSTATUS   

2.         PoCallDriver(   

3.           IN PDEVICE_OBJECT  DeviceObject,   

4.           IN OUT PIRP  Irp   

5.           );  

但是它的功能和不同的,PoCallDriver函数向设备栈中的下层设备传递一个主功能

号为IRP_MJ_POWER的请求,且限于特定的OSThePoCallDriver routine passes a power IRP to the next-lower driver in the device stack. (Windows Server 2003, Windows XP, and Windows 2000 only.)

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值