Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb

 

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家提出意见,一起讨论!

 参考书籍<<Windows驱动开发技术详解>>

 

需要示例源码的请独自联系我.

1、该示例介绍如何进行USB驱动的开发。

它全面地支持了即插即用的处理,

也很全面地支持了电源管理,同时很好地支持了USB设备的bulk读写.

如果从头开发 USB 驱动,往往很难达到USB驱动的稳定性,所以建议在此驱动修改基础上进行USB驱动开发。

 

 

2、功能驱动与物理总线驱动

程序员不需要了解USB如何将请求化成数据包等细节,只需要指定何种管道,发送何种数据即可。

当功能驱动想向某个管道发出读写请求时,首先构造请求发给USB总线驱动。这种请求是标准的USB请求,称为URB(USB Request Block)。

它被USB总线驱动所解释,进而转化成请求发给HOST驱动或者USB HUB驱动。

Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb_第1张图片

3、实现过程

3、1 构造USB请求包

USB驱动与USB设备通信时,如在控制管道中获取设备描述符、配置描述符、端点描述符、或在Bulk管道中获取大量数据,

都是通过创建USB请求包(URB)来完成的。URB中填充需要对USB的请求,然后将URB作为IRP一个参数传递给底层的USB总线驱动。

3、1、1  在DriverEntry中给了对应IRP_MJ_PNP的回调函数

 DriverObject->MajorFunction[IRP_MJ_PNP]            = BulkUsb_DispatchPnP;

3、1、2   BulkUsb_DispatchPnP的实现

BulkUsb_DispatchPnP为即插即用分发函数.

在 case IRP_MN_START_DEVICE时有处理函数ntStatus = HandleStartDevice(DeviceObject, Irp);

 

NTSTATUS
BulkUsb_DispatchPnP(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++
 
Routine Description:

   即插即用分发函数.
    Most of these requests the driver will completely ignore.
    In all cases it must pass on the IRP to the lower driver.

Arguments:

    DeviceObject - pointer to a device object.

    Irp - 指向一个I/O请求包

Return Value:

    NT status value

--*/
{
    PIO_STACK_LOCATION irpStack;
    PDEVICE_EXTENSION  deviceExtension;
    KEVENT             startDeviceEvent;
    NTSTATUS           ntStatus;

    //
    // 初始化变量
    //

    irpStack = IoGetCurrentIrpStackLocation(Irp);
    deviceExtension = DeviceObject->DeviceExtension;

    //
    // 如查它被移除,那么Irp失败


    if(Removed == deviceExtension->DeviceState) 
	{

        ntStatus = STATUS_DELETE_PENDING;

        Irp->IoStatus.Status = ntStatus;
        Irp->IoStatus.Information = 0;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return ntStatus;
    }

    BulkUsb_DbgPrint(3, ("///////////////////////////////////////////\n"));
    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::"));
	// 提升I/O计数.
    BulkUsb_IoIncrement(deviceExtension);

    if(irpStack->MinorFunction == IRP_MN_START_DEVICE)
	{

        ASSERT(deviceExtension->IdleReqPend == 0);
    }
    else 
	{

        if(deviceExtension->SSEnable) {

            CancelSelectSuspend(deviceExtension);
        }
    }

    BulkUsb_DbgPrint(2, (PnPMinorFunctionString(irpStack->MinorFunction)));

    switch(irpStack->MinorFunction) 
	{

    case IRP_MN_START_DEVICE:
		
        ntStatus = HandleStartDevice(DeviceObject, Irp);

        break;

    case IRP_MN_QUERY_STOP_DEVICE:

        //
        // if we cannot stop the device, we fail the query stop irp
        //

        ntStatus = CanStopDevice(DeviceObject, Irp);

        if(NT_SUCCESS(ntStatus)) {

            ntStatus = HandleQueryStopDevice(DeviceObject, Irp);

            return ntStatus;
        }
        break;

    case IRP_MN_CANCEL_STOP_DEVICE:

        ntStatus = HandleCancelStopDevice(DeviceObject, Irp);

        break;
     
    case IRP_MN_STOP_DEVICE:

        ntStatus = HandleStopDevice(DeviceObject, Irp);

        BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::IRP_MN_STOP_DEVICE::"));
        BulkUsb_IoDecrement(deviceExtension);

        return ntStatus;

    case IRP_MN_QUERY_REMOVE_DEVICE:

        //
        // if we cannot remove the device, we fail the query remove irp
        //
        ntStatus = HandleQueryRemoveDevice(DeviceObject, Irp);

        return ntStatus;

    case IRP_MN_CANCEL_REMOVE_DEVICE:

        ntStatus = HandleCancelRemoveDevice(DeviceObject, Irp);

        break;

    case IRP_MN_SURPRISE_REMOVAL:

        ntStatus = HandleSurpriseRemoval(DeviceObject, Irp);

        BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::IRP_MN_SURPRISE_REMOVAL::"));
        BulkUsb_IoDecrement(deviceExtension);

        return ntStatus;

    case IRP_MN_REMOVE_DEVICE:

        ntStatus = HandleRemoveDevice(DeviceObject, Irp);

        return ntStatus;

    case IRP_MN_QUERY_CAPABILITIES:

        ntStatus = HandleQueryCapabilities(DeviceObject, Irp);

        break;

    default:

        IoSkipCurrentIrpStackLocation(Irp);

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

        BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::default::"));
        BulkUsb_IoDecrement(deviceExtension);

        return ntStatus;

    } // switch

//
// complete request 
//

    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

//
// decrement count
//
    BulkUsb_DbgPrint(3, ("BulkUsb_DispatchPnP::"));
    BulkUsb_IoDecrement(deviceExtension);

    return ntStatus;
}

(1)一开始判断当前设备的状态,如果为Removed那么IRP失败,直接返回。

这里讲一下设备的状态的维护方法:

(1、1)在设备扩展中留出两字段,分别存当前状态和之前状态。

 // current state of device
    DEVSTATE DeviceState;

    // state prior to removal query
    DEVSTATE PrevDevState;


(1、2)在初始化时

(1、3)在改变时

通过以下宏来改变状态

#define SET_NEW_PNP_STATE(_Data_, _state_) \
        (_Data_)->PrevDevState =  (_Data_)->DeviceState;\
        (_Data_)->DeviceState = (_state_);

需更新状态信息的有以下几个地方:

A、BulkUsb_DispatchPnP里case IRP_MN_START_DEVICE时,在选择设备描述符和选择端点完成后设置为Working

 //
    // 读设备描述符,配置描述符并选择接口描述符
    //

    ntStatus = ReadandSelectDescriptors(DeviceObject);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
        return ntStatus;
    }

    //
    // 为系统组件去打开设备句柄而使能象征链

    ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, 
                                         TRUE);


 

B、BulkUsb_DispatchPnP里case IRP_MN_QUERY_STOP_DEVICE时,设置为PendingStop;  

C、BulkUsb_DispatchPnP里case IRP_MN_STOP_DEVICE时,在取消定时器这样DPCs就不再激活后等待两个事件激活后设置为PendingStop

 KeCancelTimer(&deviceExtension->Timer);

            //
            // 在设备停止后,它能被强拨了.
            // 我们设置它为0,这样我们在强拨或移除Irps时不再偿试去取消定时器 .
            // 当我们再获得设备请求时,此标志会再被初始化
            //
            deviceExtension->SSEnable = 0;

            //
            // make sure that if a DPC was fired before we called cancel timer,
            // then the DPC and work-time have run to their completion
            //
            KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);

            //
            // make sure that the selective suspend request has been completed.
            //
            KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);

D、BulkUsb_DispatchPnP里case IRP_MN_QUERY_REMOVE_DEVICE时,设置为PendingRemove

E、BulkUsb_DispatchPnP里case IRP_MN_SURPRISE_REMOVAL时,设置为SurpriseRemoved

对IRP_MN_SURPRISE_REMOVAL的处理与对IRP_MN_STOP_DEVICE的处理是很相似的,

只不过前者情况下认为所有请求失败,处理了队列里的所有请求,且设置设备的Interface状态为失败,中止了所有Pipe的使用:

 ProcessQueuedRequests(deviceExtension);

    ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, 
                                         FALSE);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState::disable:failed\n"));
    }

    BulkUsb_AbortPipes(DeviceObject);

而后者只是反配置了设备:

  ReleaseMemory(DeviceObject);

    ntStatus = DeconfigureDevice(DeviceObject);


F、BulkUsb_DispatchPnP里case IRP_MN_REMOVE_DEVICE时,设置为Removed
在这个情况下做以下事情:反注册USB插入时创建的设备对象,把此USB的FDO从设备栈中移除;
如果之前的设备状态不是SurpriseRemoved,那么要做IRP_MN_SURPRISE_REMOVAL、IRP_MN_STOP_DEVICE两个case时做的所有事情

 

3、1、3   HandleStartDevice的实现

它调用ReadandSelectDescriptors来读取设备描述符

NTSTATUS
HandleStartDevice(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP              Irp
    )
/*++
 
Routine Description:

   IRP_MN_START_DEVICE对应的处理函数

Arguments:

    DeviceObject - pointer to a device object.

    Irp - I/O request packet

Return Value:

    NT status value

--*/
{
    KIRQL             oldIrql;
    KEVENT            startDeviceEvent;
    NTSTATUS          ntStatus;
    PDEVICE_EXTENSION deviceExtension;
    LARGE_INTEGER     dueTime;

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

    //
    // 初始化变量
    //
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    deviceExtension->UsbConfigurationDescriptor = NULL;
    deviceExtension->UsbInterface = NULL;
    deviceExtension->PipeContext = NULL;

    //
    // 我们不能触摸设备 (给它发送任何非pnp 的 irps) until a
    // start device has been passed down to the lower drivers.
    // first pass the Irp down
    //

    KeInitializeEvent(&startDeviceEvent, NotificationEvent, FALSE);

    IoCopyCurrentIrpStackLocationToNext(Irp);

    IoSetCompletionRoutine(Irp, 
                           (PIO_COMPLETION_ROUTINE)IrpCompletionRoutine, 
                           (PVOID)&startDeviceEvent, 
                           TRUE, 
                           TRUE, 
                           TRUE);

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

    if(ntStatus == STATUS_PENDING) {

        KeWaitForSingleObject(&startDeviceEvent, 
                              Executive, 
                              KernelMode, 
                              FALSE, 
                              NULL);

        ntStatus = Irp->IoStatus.Status;
    }

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("Lower drivers failed this Irp\n"));
        return ntStatus;
    }

    //
    // 读设备描述符,配置描述符并选择接口描述符
    //

    ntStatus = ReadandSelectDescriptors(DeviceObject);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
        return ntStatus;
    }

    //
    // 为系统组件去打开设备句柄而使能象征链

    ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, 
                                         TRUE);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState:enable:failed\n"));
        return ntStatus;
    }

    KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);

    SET_NEW_PNP_STATE(deviceExtension, Working);
    deviceExtension->QueueState = AllowRequests;

    KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);

    //
    // 初始化等待唤醒的标志到false  false.
    // and issue a wait wake.
    
    deviceExtension->FlagWWOutstanding = 0;
    deviceExtension->FlagWWCancel = 0;
    deviceExtension->WaitWakeIrp = NULL;
    
    if(deviceExtension->WaitWakeEnable) {

        IssueWaitWake(deviceExtension);
    }

    ProcessQueuedRequests(deviceExtension);


    if(WinXpOrBetter == deviceExtension->WdmVersion)
	{


        deviceExtension->SSEnable = deviceExtension->SSRegistryEnable;

        //
        // set timer.for selective suspend requests
        //

        if(deviceExtension->SSEnable) {

            dueTime.QuadPart = -10000 * IDLE_INTERVAL;               // 5000 ms

            KeSetTimerEx(&deviceExtension->Timer, 
                         dueTime,
                         IDLE_INTERVAL,                              // 5000 ms
                         &deviceExtension->DeferredProcCall);

            deviceExtension->FreeIdleIrpCount = 0;
        }
    }

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

    return ntStatus;
}


(1) IoCopyCurrentIrpStackLocationToNext介绍:

在下发IRP到底层驱动处理前,本层驱动必须负责设置下层IO堆栈的内容。这样下一层驱动调用IoGetCurrentIrpStackLocation()时能得到相应的数据。
设置下层IO堆栈的内容,一般用两个函数来实现:
IoCopyCurrentIrpStackLocationToNext( Irp )
此函数 一般用在本驱动设置了完成例程时调用,把本层IO _STACK_LOCATION 中的参数 copy到下层,但与完成例程相关的参数信息例外。因为本驱动设置的完成例程只对本层驱动有效。
IoSkipCurrentIrpStackLocationToNext(Irp)
此函数的作用是:直接 把本层驱动IO堆栈的内容设置为下层驱动IO堆栈指针的指向。因两层驱动IO堆栈的内容完全一致, 省却copy过程
(2)根据以上知识点,我们开始分析HandleStartDevice函数的实现过程:
(2、1)把本层IO _STACK_LOCATION 中的参数 copy到下层,设置完成例程,等待完成。
 KeInitializeEvent(&startDeviceEvent, NotificationEvent, FALSE);

    IoCopyCurrentIrpStackLocationToNext(Irp);

    IoSetCompletionRoutine(Irp, 
                           (PIO_COMPLETION_ROUTINE)IrpCompletionRoutine, 
                           (PVOID)&startDeviceEvent, 
                           TRUE, 
                           TRUE, 
                           TRUE);

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

    if(ntStatus == STATUS_PENDING) {

        KeWaitForSingleObject(&startDeviceEvent, 
                              Executive, 
                              KernelMode, 
                              FALSE, 
                              NULL);

        ntStatus = Irp->IoStatus.Status;
    }

(2、2)读设备描述符,配置描述符并选择接口描述符(重点,参见3、1、4 );
(2、3)开启定时器(5S间隔);

 if(DeviceExtension->SSEnable) {

        BulkUsb_DbgPrint(3, ("Set the timer to fire DPCs\n"));

        dueTime.QuadPart = -10000 * IDLE_INTERVAL;               // 5000 ms

        KeSetTimerEx(&DeviceExtension->Timer, 
                     dueTime,
                     IDLE_INTERVAL,                              // 5000 ms
                     &DeviceExtension->DeferredProcCall);

        BulkUsb_DbgPrint(3, ("IdleNotificationRequestCompete - ends\n"));
    }

这里补充定时器的使用:

实例中用到的定时器是为了与DCP协调使用,这体现在初始化时,如下面的 A、

A、初始化定时器

在系统扩展中的变量DriverObject->DriverExtension->AddDevice指定的处理函数 BulkUsb_AddDevice中初始化定时器:

 //
            // 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);

B、启动定时器;

它发生在两个地方:HandleStartDevice SubmitIdleRequestIrp 函数设备的完成例程中。

C、关闭定时器;

它发生在三个地方:HandleStopDevice、HandleSurpriseRemoval、HandleRemoveDevice三个地方。

定时器每5S后触发的事件的目的是:

检查设备的idle状态并为设备提交一个idle请求。

步一:检查是设备是否是suspend状态。判断方法如下:

 if((DeviceExtension->OpenHandleCount == 0) &&
        (DeviceExtension->OutStandingIO == 1)) {
        
        return TRUE;
    }
    else {

        return FALSE;
    }


OpenHandleCount 、OutStandingIO都是LONG类型,它们通过InterlockedIncrement、InterlockedDecrement来自增1与自减1;

我们可以发现在进入每个派遣函数时,几乎都有对OutStandingIO加1,然后结束时减1,而刚开始时是1。根据条件判断,当在派遣函数中时此时是不能置为设备SUSPEND状态的。

OpenHandleCount作为打开设备的计数,在AddDevice时是0,在Create后是加了1,这样就是设备还没有Create时是可Idle的状态。

以上讲到的是是USB开发中用到的同步机制,它的思想与COM的计数思想是类似的。

步二:首先调用IoAllocateWorkItem函数为work item分配内存,然后调用IoQueueWorkItem函数将任务放置到工作线程队列中;

 IO_WORKITEM 结构体是一个不透明的结构体,它描述了一个系统工作线程的工作Item。驱动可以通过IoAllocateWorkItem分配一个工作ITEM,或者驱动分配它自己的Buffer,然后调用IoInitializeWorkItem来初始化这个Buffer作为一个工作ITEM

IoQueueWorkItem把一个回调函数绑定在一个工作ITEM中,并把它插入的队列中,等待系统工作线程来处理
 步三:处理Idle请求任务IdleRequestWorkerRoutine

  IdleRequestWorkerRoutine调用SubmitIdleRequestIrp提交Idle请求的IRP,然后将事件NoDpcWorkItemPendingEvent激活。
步三、1     SubmitIdleRequestIrp 过程分析

SubmitIdleRequestIrp用一个分配的回调函数例程(PUSB_IDLE_CALLBACK_INFO)一个IDLE通知的完成例程(IdleNotificationRequestComplete)建立一个IDLE请求IRP

idleCallbackInfo = ExAllocatePool(NonPagedPool, 
                                      sizeof(struct _USB_IDLE_CALLBACK_INFO));

    if(idleCallbackInfo) {

        idleCallbackInfo->IdleCallback = IdleNotificationCallback;

        idleCallbackInfo->IdleContext = (PVOID)DeviceExtension;

        ASSERT(DeviceExtension->IdleCallbackInfo == NULL);

        DeviceExtension->IdleCallbackInfo = idleCallbackInfo;


 

irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize,
						FALSE);
						
nextStack = IoGetNextIrpStackLocation(irp);
nextStack->MajorFunction = 
			IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = 
			IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
			idleCallbackInfo;
nextStack->Parameters.DeviceIoControl.InputBufferLength =
			sizeof(struct _USB_IDLE_CALLBACK_INFO);
IoSetCompletionRoutine(irp, 
					   IdleNotificationRequestComplete,
					   DeviceExtension, 
					   TRUE, 
					   TRUE, 
					   TRUE);
DeviceExtension->PendingIdleIrp = irp;


 

并把IRP传到栈下面:

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

步三、2     IDLE通知的完成例程分析

判断IDLE请求的IRP的执行结果,如果有错误发生,那么通过PoRequestPowerIrp告诉设备栈的顶层驱动电源状态为PowerDeviceD0。

 powerState.DeviceState = PowerDeviceD0;

        ntStatus = PoRequestPowerIrp(
                          DeviceExtension->PhysicalDeviceObject, 
                          IRP_MN_SET_POWER, 
                          powerState, 
                          (PREQUEST_POWER_COMPLETE) PoIrpAsyncCompletionFunc, 
                          DeviceExtension, 
                          NULL);


PoRequestPowerIrp 分析一个电源IRP并把它发送到设备栈的顶层驱动中。

、、、、、、

、、、、、、

、、、、、、

3、1、4   ReadandSelectDescriptors的实现

A、首先分配一个控制描述符请求的URB内存,然后再分析一个设备描述符内存PUSB_DEVICE_DESCRIPTOR

B、 然后通过UsbBuildGetDescriptorRequest填充URB后得到

#define UsbBuildGetDescriptorRequest(urb, \
                                     length, \
                                     descriptorType, \
                                     descriptorIndex, \
                                     languageId, \
                                     transferBuffer, \
                                     transferBufferMDL, \
                                     transferBufferLength, \
                                     link) { \
            (urb)->UrbHeader.Function =  URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE; \
            (urb)->UrbHeader.Length = (length); \
            (urb)->UrbControlDescriptorRequest.TransferBufferLength = (transferBufferLength); \
            (urb)->UrbControlDescriptorRequest.TransferBufferMDL = (transferBufferMDL); \
            (urb)->UrbControlDescriptorRequest.TransferBuffer = (transferBuffer); \
            (urb)->UrbControlDescriptorRequest.DescriptorType = (descriptorType); \
            (urb)->UrbControlDescriptorRequest.Index = (descriptorIndex); \
            (urb)->UrbControlDescriptorRequest.LanguageId = (languageId); \
            (urb)->UrbControlDescriptorRequest.UrbLink = (link); }


C、请求设备描述符的URB, 将此URB请求自己封装的CallUSBD通过发送出去。

CallUSBD用于同步地提交一个URB到栈,步骤如下:

C、1   创建一个I/O控制码的IRP;

C、2   通过IoGetNextIrpStackLocation得到irp的下一层设备栈,并把urb值给nextStack->Parameters.Others.Argument1;

C、3  用IoCallDriver将irp发送到底层总线驱动上;

C、4 由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断,当异步完成IRP时,用事件等待总线驱动完成这个IRP。(参见3、2 )

 

NTSTATUS
ReadandSelectDescriptors(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++
 
Routine Description:

    配置USB设备.
    我们先获得设备描述符,配置描述符然后选择配置描述符

Arguments:

    DeviceObject - pointer to a device object

Return Value:

    NTSTATUS - NT status value.

--*/
{
    PURB                   urb;
    ULONG                  siz;
    NTSTATUS               ntStatus;
    PUSB_DEVICE_DESCRIPTOR deviceDescriptor;
    
    //
    // initialize variables
    //

    urb = NULL;
    deviceDescriptor = NULL;

    //
    // 1.读设备描述符

    urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if(urb)
	{

        siz = sizeof(USB_DEVICE_DESCRIPTOR);
        deviceDescriptor = ExAllocatePool(NonPagedPool, siz);

        if(deviceDescriptor) 
		{

			// 构造请求
            UsbBuildGetDescriptorRequest(
                    urb, 
                    (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                    USB_DEVICE_DESCRIPTOR_TYPE, 
                    0, 
                    0, 
                    deviceDescriptor, 
                    NULL, 
                    siz, 
                    NULL);

			// 发送请求
            ntStatus = CallUSBD(DeviceObject, urb);

            if(NT_SUCCESS(ntStatus))
			{

                ASSERT(deviceDescriptor->bNumConfigurations);
                ntStatus = ConfigureDevice(DeviceObject);    
            }
                            
            ExFreePool(urb);                
            ExFreePool(deviceDescriptor);
        }
        else
		{

            BulkUsb_DbgPrint(1, ("Failed to allocate memory for deviceDescriptor"));

            ExFreePool(urb);
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }
    else
	{

        BulkUsb_DbgPrint(1, ("Failed to allocate memory for urb"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    return ntStatus;
}

 D、请求配置描述符

读取的过程是:

第一步:读取配置描述符的大小;

分配的配置描述符内存大小为 sizeof(USB_CONFIGURATION_DESCRIPTOR),也是把内存绑定到URB,URB再绑到IRP,然后通过IoCallDriver提交请求;

(实现与上步C类似)

第二步:根据第一步得到的大小,重新分析PUSB_CONFIGURATION_DESCRIPTOR内存, 与第一步一样的方法提交。

由于希望得到的配置描述符的大小变化了,所以得提交的请求返回的内存就是真正的配置描述符数据

E、保存上步D得到的配置描述符指针到extension中,根据指针内容判断是否支持远程唤醒;

F、根据返回的配置描述符数据选择其中一个Interfaces。(重点)

 

NTSTATUS
SelectInterfaces(
    IN PDEVICE_OBJECT                DeviceObject,
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
    )

FConfigurationDescriptor描述符数据中通过USBD_ParseConfigurationDescriptorEx取出一个一个的PUSB_INTERFACE_DESCRIPTOR,组成链表

USBD_ParseConfigurationDescriptorEx为DDK提供的API, 用于解析从设备返回的标准的USB配置描述符,以得到一个指定的接口,可改变的设备类子类或协议代码。

 

DECLSPEC_IMPORT
PUSB_INTERFACE_DESCRIPTOR
USBD_ParseConfigurationDescriptorEx(
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    IN PVOID StartPosition,
    IN LONG InterfaceNumber,
    IN LONG AlternateSetting,
    IN LONG InterfaceClass,
    IN LONG InterfaceSubClass,
    IN LONG InterfaceProtocol
    );


 

ConfigurationDescriptor - 指向从设备返回的USB配置描述符, (包含所有的接口和端点描述符);
StartPosition - 指向将要解析的配置描述符的开始位置

InterfaceNumber - 要查找的接口号,(-1)表满足所有;
AlternateSetting - 要查找的可改变的设备号,(-1)表满足所有;

InterfaceClass - 要查找的类,(-1)表满足所有;
InterfaceSubClass - 要查找的子类,(-1)表满足所有;
InterfaceProtocol - 要查找的协议,(-1)表满足所有;

tmp = interfaceList =
	ExAllocatePool(
		   NonPagedPool, 
		   sizeof(USBD_INTERFACE_LIST_ENTRY) * (numberOfInterfaces + 1));

if(!tmp) {

	BulkUsb_DbgPrint(1, ("Failed to allocate mem for interfaceList\n"));
	return STATUS_INSUFFICIENT_RESOURCES;
}


while(interfaceNumber < numberOfInterfaces) {

	interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
										ConfigurationDescriptor, 
										ConfigurationDescriptor,
										interfaceindex,
										0, -1, -1, -1);

	if(interfaceDescriptor) {

		interfaceList->InterfaceDescriptor = interfaceDescriptor;
		interfaceList->Interface = NULL;
		interfaceList++;
		interfaceNumber++;
	}

	interfaceindex++;
}

F、2   通过DDK提供的USBD_CreateConfigurationRequestEx函数,

USBD_CreateConfigurationRequestEx函数基于传进来的接口列表,来分配并初始化一个有足够大小的URB来配置设备, 接口列表是一个连续的 USBD_INTERFACE_LIST_ENTRIES数组,每一个指定接口描述符的指针被包含在请求中,列表通过指向NULL的列表入口来终止。

DECLSPEC_IMPORT
PURB
USBD_CreateConfigurationRequestEx(
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    IN PUSBD_INTERFACE_LIST_ENTRY InterfaceList
    );


 

F、3  把E、2返回的URB请求发送出去;把Interface信息urb->UrbSelectConfiguration.Interface保存在extension中;

F、4  初始化BULKUSB_PIPE_CONTEXT  PipeContext



 

 

 

3、2  然后调用CallUSBD将此请求发送出去

URB包要和一个IRP相关联起来。这就需要IoBuildDeviceIoControlRequest创建一个I/O控制码的IRP

然后将URB作为IRP的参数,用IoCallDriver将URB发送到底层总线驱动上。

由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断当异步完成IRP时( if(ntStatus == STATUS_PENDING)),用事件等待总线驱动完成这个IRP。

 

NTSTATUS
CallUSBD(
    IN PDEVICE_OBJECT DeviceObject,
    IN PURB           Urb
    )
/*++
 
Routine Description:

    同步地提交一个URB到栈

Arguments:

    DeviceObject - pointer to device object
    Urb - USB request block

Return Value:

--*/
{
    PIRP               irp;
    KEVENT             event;
    NTSTATUS           ntStatus;
    IO_STATUS_BLOCK    ioStatus;
    PIO_STACK_LOCATION nextStack;
    PDEVICE_EXTENSION  deviceExtension;

    //
    // initialize the variables
    //

    irp = NULL;
    deviceExtension = DeviceObject->DeviceExtension;
    
    KeInitializeEvent(&event, NotificationEvent, FALSE);

	// 创建一个I/O控制码的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);

	// 用IoCallDriver将URB发送到底层总线驱动上 
    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);

	// 由于上层无法知道底层驱动是同步还异步完成的,因此需要做一个判断.If 语句判断当异步完成
	//	IRP时,用事件等待总线驱动完成这个IRP。
    if(ntStatus == STATUS_PENDING)
	{

        KeWaitForSingleObject(&event, 
                              Executive, 
                              KernelMode, 
                              FALSE, 
                              NULL);

        ntStatus = ioStatus.Status;
    }
    
    BulkUsb_DbgPrint(3, ("CallUSBD::"));
    BulkUsb_IoDecrement(deviceExtension);
    return ntStatus;
}


3、3  USB设备初始化

在AddDevice全程中,创建功能设备对象,然后将该对象挂载在总线设备对象之上,从而形成设备栈。

另外为设备创建一个设备链接,便于应用程序可以找到这个设备。

NTSTATUS
BulkUsb_AddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject
    )
/*++

Description:

Arguments:

    DriverObject - Store the pointer to the object representing us.

    PhysicalDeviceObject - Pointer to the device object created by the
                           undelying bus driver.

Return:
	
    STATUS_SUCCESS - if successful 
    STATUS_UNSUCCESSFUL - otherwise

--*/
{
    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

    if(!NT_SUCCESS(ntStatus)) 
	{
        //
        // returning failure here prevents the entire stack from functioning,
        // but most likely the rest of the stack will not be able to create
        // device objects either, so it is still OK.
        //                
        BulkUsb_DbgPrint(1, ("Failed to create device object\n"));
        return ntStatus;
    }

    //
    // 初始化设备扩展

    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
    deviceExtension->FunctionalDeviceObject = deviceObject;
    deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
    deviceObject->Flags |= DO_DIRECT_IO;

    //
    // 初始化设备栈并设置设备状态

    KeInitializeSpinLock(&deviceExtension->DevStateLock);
    INITIALIZE_PNP_STATE(deviceExtension);

    //
    // 初始化 OpenHandleCount
    //
    deviceExtension->OpenHandleCount = 0;

    //
    // 初始化可选的中止变量
    KeInitializeSpinLock(&deviceExtension->IdleReqStateLock);
    deviceExtension->IdleReqPend = 0;
    deviceExtension->PendingIdleIrp = NULL;

    //
    // Hold住请求一直到设备开始

    deviceExtension->QueueState = HoldRequests;

    //
    // 初始化队列和队列 spin 锁

    InitializeListHead(&deviceExtension->NewRequestsQueue);
    KeInitializeSpinLock(&deviceExtension->QueueLock);

    //
    // 初始化可移除的事件到没信号

    KeInitializeEvent(&deviceExtension->RemoveEvent, 
                      SynchronizationEvent, 
                      FALSE);

    //
    // 初始化停止事件到有信号.
    // 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;
    }

    //
    // 设置标示为 underlying PDO
    //

    if(PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) {

        deviceObject->Flags |= DO_POWER_PAGABLE;
    }

    //
    // Typically, the function driver for a device is its 
    // power policy owner, although for some devices another 
    // driver or system component may assume this role. 
    // Set the initial power state of the device, if known, by calling 
    // PoSetPowerState.
    // 

    deviceExtension->DevPower = PowerDeviceD0;
    deviceExtension->SysPower = PowerSystemWorking;

    state.DeviceState = PowerDeviceD0;
    PoSetPowerState(deviceObject, DevicePowerState, state);

    //
    // 把我们的驱动接入到设备栈
    // IoAttachDeviceToDeviceStack返回值是接入链的顶层对象
	//  This is where all the IRPs should be routed.
    //

    deviceExtension->TopOfStackDeviceObject = 
                IoAttachDeviceToDeviceStack(deviceObject,
                                            PhysicalDeviceObject);

    if(NULL == deviceExtension->TopOfStackDeviceObject) {

        BulkUsb_WmiDeRegistration(deviceExtension);
        IoDeleteDevice(deviceObject);
        return STATUS_NO_SUCH_DEVICE;
    }
        
    //
    // 注册设备接口

    ntStatus = IoRegisterDeviceInterface(deviceExtension->PhysicalDeviceObject, 
                                         &GUID_CLASS_I82930_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;
}



3、4 USB设备的插拔

插拔设备会设计3个即插即用IRP,包括IRP_MN_START_DEVICE、

IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL.

3、4、1

IRP_MN_START_DEVICE 是当驱动争取加载并运行时,操作系统的即插即用管理器会将

这个IRP发给设备驱动。因此当获得这个IRP后,USB驱动需要获得USB设备类别描述符,并通过这些

描述符,从中获取有用信息,记录在设备扩展中。

处理函数就是上面讲到的 HandleStartDevice 函数

3、4、2

IRP_MN_STOP_DEVICE 是设备关闭前,即插即用管理器发的IRP。USB驱动获得这个IRP时,

应该尽快结束当前执行的IRP, 并将其逐个取消掉。别外在设备扩展中还应该有表示当前状态的变量,

当IRP_MN_STOP_DEVICE来临时,将当前状态记录成停止状态。

它的处理函数是:

NTSTATUS
HandleStopDevice(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++

Routine Description:

    This routine services Irp of minor type IRP_MN_STOP_DEVICE

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet sent by the pnp manager.

Return Value:

    NT status value

--*/
{
    KIRQL             oldIrql;
    NTSTATUS          ntStatus;
    PDEVICE_EXTENSION deviceExtension;

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

    //
    // initialize variables
    //

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;


    if(WinXpOrBetter == deviceExtension->WdmVersion) {

        if(deviceExtension->SSEnable) {

            //
            // 取消定时器这样DPCs就不再激活.
            // Thus, we are making judicious usage of our resources.
            // 我们不再需要DPCs,因为设备已经停止.
            // The timers are re-initialized while handling the start
            // device irp.
            //

            KeCancelTimer(&deviceExtension->Timer);

            //
            // 在设备停止后,它能被强拨了.
            // 我们设置它为0,这样我们在强拨或移除Irps时不再偿试去取消定时器 .
            // 当我们再获得设备请求时,此标志会再被初始化
            //
            deviceExtension->SSEnable = 0;

            //
            // make sure that if a DPC was fired before we called cancel timer,
            // then the DPC and work-time have run to their completion
            //
            KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);

            //
            // make sure that the selective suspend request has been completed.
            //
            KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);
        }
    }

    //
    // after the stop Irp is sent to the lower driver object, 
    // the driver must not send any more Irps down that touch 
    // the device until another Start has occurred.
    //

    if(deviceExtension->WaitWakeEnable) {
    
        CancelWaitWake(deviceExtension);
    }

    KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);

	// 将当前状态记录成停止状态
    SET_NEW_PNP_STATE(deviceExtension, Stopped);
    
    KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);

    //
    // This is the right place to actually give up all the resources used
    // This might include calls to IoDisconnectInterrupt, MmUnmapIoSpace, 
    // etc.
    //

    ReleaseMemory(DeviceObject);

    ntStatus = DeconfigureDevice(DeviceObject);

    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;
    
    IoSkipCurrentIrpStackLocation(Irp);
    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);

    BulkUsb_DbgPrint(3, ("HandleStopDevice - ends\n"));
    
    return ntStatus;
}


 我们看到它在取消定时器后做了等待,这样能确保我们取消定时器时万一还在定时器的完成例程中时能等待例程完成,

以免把设备停止了,可是DPC还在访问设备。所以在DPC例程中,首先做的事就是把自己的事件置为无信号

KeClearEvent(&deviceExtension->NoDpcWorkItemPendingEvent);

 

 

 

3、4、3  IRP_MN_SURPRISE_REMOVAL

IRP_MN_EJECT 是设备被正常弹出,而IRP_MN_SURPRISE_REMOVAL则是设备非自然弹出,

有可能是掉电或强行拨出。在这种IRP到来时,应该强迫所有未完成的读写IRP结束并取消。

NTSTATUS
HandleSurpriseRemoval(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++
 
Routine Description:

    This routine services Irp of minor type IRP_MN_SURPRISE_REMOVAL

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet sent by the pnp manager.

Return Value:

    NT status value

--*/
{
    KIRQL             oldIrql;
    NTSTATUS          ntStatus;
    PDEVICE_EXTENSION deviceExtension;

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

    //
    // initialize variables
    //

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // 1. fail pending requests
    // 2. return device and memory resources
    // 3. disable interfaces
    //

    if(deviceExtension->WaitWakeEnable) {
    
        CancelWaitWake(deviceExtension);
    }


    if(WinXpOrBetter == deviceExtension->WdmVersion) {

        if(deviceExtension->SSEnable) {

            //
            // Cancel the timer so that the DPCs are no longer fired.
            // we do not need DPCs because the device has been surprise
            // removed
            //  
        
            KeCancelTimer(&deviceExtension->Timer);

            deviceExtension->SSEnable = 0;

            //  
            // make sure that if a DPC was fired before we called cancel timer,
            // then the DPC and work-time have run to their completion
            //
            KeWaitForSingleObject(&deviceExtension->NoDpcWorkItemPendingEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);

            //
            // make sure that the selective suspend request has been completed.
            //
            KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  NULL);
        }
    }

    KeAcquireSpinLock(&deviceExtension->DevStateLock, &oldIrql);

    deviceExtension->QueueState = FailRequests;
    SET_NEW_PNP_STATE(deviceExtension, SurpriseRemoved);

    KeReleaseSpinLock(&deviceExtension->DevStateLock, oldIrql);

    ProcessQueuedRequests(deviceExtension);

    ntStatus = IoSetDeviceInterfaceState(&deviceExtension->InterfaceName, 
                                         FALSE);

    if(!NT_SUCCESS(ntStatus)) {

        BulkUsb_DbgPrint(1, ("IoSetDeviceInterfaceState::disable:failed\n"));
    }

    BulkUsb_AbortPipes(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

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

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

    return ntStatus;
}

3、4 USB设备的读写

USB设备接口主要是为了传送数据,80%的传输是通过Bulk管道。在BulkUSB驱动中,Bulk管道的读取是在IRP_MJ_READ和IRP_MJ_WRITE的函数中。

IRP_MJ_READ和IRP_MJ_WRITE的派遣函数我们设置为同一个BulkUsb_DispatchReadWrite:

 

NTSTATUS
BulkUsb_DispatchReadWrite(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
/*++
 
Routine Description:

    Dispatch routine for read and write.
    This routine creates a BULKUSB_RW_CONTEXT for a read/write.
    This read/write is performed in stages of BULKUSB_MAX_TRANSFER_SIZE.
    once a stage of transfer is complete, then the irp is circulated again, 
    until the requested length of tranfer is performed.

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet

Return Value:

    NT status value

--*/
{
    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;
    }

    //
    // It is true that the client driver cancelled the selective suspend
    // request in the dispatch routine for create Irps.
    // But there is no guarantee that it has indeed completed.
    // so wait on the NoIdleReqPendEvent and proceed only if this event
    // is signalled.
    //
    BulkUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
    
    //
    // make sure that the selective suspend request has been completed.
    //

    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);
    }

    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;
    }

	// 设置URB标志
    urbFlags = USBD_SHORT_TRANSFER_OK;
    virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);

	// 判断是读还是写
    if(read) 
	{

        urbFlags |= USBD_TRANSFER_DIRECTION_IN;
        BulkUsb_DbgPrint(3, ("Read operation\n"));
    }
    else
	{

        urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
        BulkUsb_DbgPrint(3, ("Write operation\n"));
    }

    //
    // the transfer request is for totalLength.
    // we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
    // in each stage.
    //
    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;
    }

    //
    // 将新MDL进行映射
	// map the portion of user-buffer described by an mdl to another 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;
    }

	// 建立Bulk管道的URB
    UsbBuildInterruptOrBulkTransferRequest(
                            urb,
                            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                            pipeInformation->PipeHandle,
                            NULL,
                            mdl,
                            stageLength,
                            urbFlags,
                            NULL);

    //
    // 设置完成例程参数  BULKUSB_RW_CONTEXT  
    //
    
    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);

    //
    // since we return STATUS_PENDING call IoMarkIrpPending.
    // This is the boiler plate code.
    // This may cause extra overhead of an APC for the Irp completion
    // but this is the correct thing to do.
    //

	// 将当前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 the device was yanked out, then the pipeInformation 
        // field is invalid.
        // similarly if the request was cancelled, then we need not
        // invoked reset pipe/device.
        //
        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"));
        }
    }

    //
    // we return STATUS_PENDING and not the status returned by the lower layer.
    //
    return STATUS_PENDING;

BulkUsb_DispatchReadWrite_Exit:

    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

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

    return ntStatus;
}

(1)如果设备状态不是Working,直接返回,因为此时设备已经拔掉了;

(2)如果应用程序要读的Pipe不是Bulk(Pipe信息从设备栈中的FileObject对象中获得),那么也直接返回;

(3)调用MmGetMdlByteCount从Irp中的MdlAddress得到Byte数量(这个数量就是应用层想读或写的数量)及virtualAddress,然后根据它们建立MDL,

并调用IoBuildPartialMdl将用户的Buffer将映射到新建的虚地址中;

如果Byte数量过大或为0,那么直接返回

// 建立MDL
    mdl = IoAllocateMdl((PVOID) virtualAddress,
                        totalLength,
                        FALSE,
                        FALSE,
                        NULL);
 IoBuildPartialMdl(Irp->MdlAddress,
                      mdl,
                      (PVOID) virtualAddress,
                      stageLength);

 

(4)根据MDL建立Bulk管道的URB;

// 建立Bulk管道的URB
    UsbBuildInterruptOrBulkTransferRequest(
                            urb,
                            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                            pipeInformation->PipeHandle,
                            NULL,
                            mdl,
                            stageLength,
                            urbFlags,
                            NULL);


(5)把上面得到的URB组合成一个新的IRP(操作码为IOCTL_INTERNAL_USB_SUBMIT_URB),设置完成例程(如3、5所讲)传递给总线管理器;

 

 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);


 

 

3、5  读写完成的例子程是

 

NTSTATUS
BulkUsb_ReadWriteCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
/*++
 
Routine Description:

    This is the completion routine for reads/writes
    If the irp completes with success, we check if we
    need to recirculate this irp for another stage of
    transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
    if the irp completes in error, free all memory allocs and
    return the status.

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    Context - context passed to the completion routine.

Return Value:

    NT status value

--*/
{
    ULONG               stageLength;
    NTSTATUS            ntStatus;
    PIO_STACK_LOCATION  nextStack;
    PBULKUSB_RW_CONTEXT rwContext;

    //
    // initialize variables
    //
    rwContext = (PBULKUSB_RW_CONTEXT) Context;
    ntStatus = Irp->IoStatus.Status;

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

    //
    // successfully performed a stageLength of transfer.
    // check if we need to recirculate the irp.
    //
    if(NT_SUCCESS(ntStatus)) {

        if(rwContext) {

            rwContext->Numxfer += 
              rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
        
            if(rwContext->Length) {

                //
                // another stage transfer
                //
                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);
            
                //
                // reinitialize the urb
                //
                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 {

                //
                // this is the last transfer
                //

                Irp->IoStatus.Information = rwContext->Numxfer;
            }
        }
    }
    else {

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

        //
        // dump rwContext
        //
        BulkUsb_DbgPrint(3, ("rwContext->Urb             = %X\n", 
                             rwContext->Urb));
        BulkUsb_DbgPrint(3, ("rwContext->Mdl             = %X\n", 
                             rwContext->Mdl));
        BulkUsb_DbgPrint(3, ("rwContext->Length          = %d\n", 
                             rwContext->Length));
        BulkUsb_DbgPrint(3, ("rwContext->Numxfer         = %d\n", 
                             rwContext->Numxfer));
        BulkUsb_DbgPrint(3, ("rwContext->VirtualAddress  = %X\n", 
                             rwContext->VirtualAddress));
        BulkUsb_DbgPrint(3, ("rwContext->DeviceExtension = %X\n", 
                             rwContext->DeviceExtension));

        BulkUsb_DbgPrint(3, ("BulkUsb_ReadWriteCompletion::"));
        BulkUsb_IoDecrement(rwContext->DeviceExtension);

        ExFreePool(rwContext->Urb);
        IoFreeMdl(rwContext->Mdl);
        ExFreePool(rwContext);
    }

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

    return ntStatus;
}


 


4、编译过程中出来以下报错。

 

1>LINK : error LNK2001: unresolved external symbol _mainCRTStartup
1>rwbulk.obj : error LNK2019: unresolved external symbol _printf referenced in function _OpenOneDevice@12
1>rwbulk.obj : error LNK2019: unresolved external symbol _free referenced in function _OpenOneDevice@12
1>rwbulk.obj : error LNK2019: unresolved external symbol _malloc referenced in function _OpenOneDevice@12
1>rwbulk.obj : error LNK2019: unresolved external symbol _calloc referenced in function _OpenUsbDevice@8
1>rwbulk.obj : error LNK2019: unresolved external symbol _realloc referenced in function _OpenUsbDevice@8
1>rwbulk.obj : error LNK2019: unresolved external symbol _atoi referenced in function _parse@8
1>rwbulk.obj : error LNK2019: unresolved external symbol __assert referenced in function _main


 

解决方法:在sources中添加USE_MSVCRT=1项.

 

 

 

 

你可能感兴趣的:(object,XP,null,resources,extension,Descriptor)