
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Copyright (c) Osamu Tamura @ Recursion Co., Ltd.  Portion of rights resereved.

Module Name:

    lowcdc.c

Abstract:

    This module is a filter driver to enable cdc transfer on low speed USB.
    Use with usbser.sys.

Environment:

    Kernel mode

Revision History:

    Fixed bugs - March 15, 2001

    Added Ioctl interface - Aug 16, 2001
    
    Updated to use IoCreateDeviceSecure function - Sep 17, 2002

    Updated to use RemLocks - Oct 29, 2002

	------------------------------

    FilterDispatchIo() added by Osamu Tamura @ Recursion Co., Ltd. - Jun 30, 2008

    Updated to use interrupt pipes for cdc transfer - Dec 22, 2008

	Added registry to switch bulk/interrupt transfer
	Disabled the interrupt pipe for notification message - Jun 07, 2009

	Added support for Win 2000 (bulk mode only) - Aug 20, 2009
--*/

#include <wdm.h>
#include <usbdi.h>
#include "lowcdc.h"


#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, FilterAddDevice)
#pragma alloc_text (PAGE, FilterDispatchPnp)
#pragma alloc_text (PAGE, FilterUnload)
#pragma alloc_text (PAGE, FilterDispatchIoXp)
#pragma alloc_text (PAGE, FilterDispatchIoVista)
#endif


NTSTATUS
DriverEntry(
    __in PDRIVER_OBJECT  DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path,
                   to driver-specific key in the registry.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise.

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               ulIndex;
    PDRIVER_DISPATCH  * dispatch;
	ULONG				value;

    UNREFERENCED_PARAMETER (RegistryPath);

    DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL, "Entered the Driver Entry\n" ));

    //
    // Create dispatch points
    //
    for (ulIndex = 0, dispatch = DriverObject->MajorFunction;
         ulIndex <= IRP_MJ_MAXIMUM_FUNCTION;
         ulIndex++, dispatch++) {

        *dispatch = FilterPass;
    }

    DriverObject->MajorFunction[IRP_MJ_PNP]            = FilterDispatchPnp;
    DriverObject->MajorFunction[IRP_MJ_POWER]          = FilterDispatchPower;
    DriverObject->DriverExtension->AddDevice           = FilterAddDevice;
    DriverObject->DriverUnload                         = FilterUnload;

	DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
		( RtlIsNtDdiVersionAvailable(NTDDI_VISTA) ||
		( RtlIsNtDdiVersionAvailable(NTDDI_WINXP) &&
		  RtlCheckRegistryKey(RTL_REGISTRY_SERVICES,USE_BULK)!=STATUS_SUCCESS ) )?
		FilterDispatchIoVista:FilterDispatchIoXp;

	return status;
}


NTSTATUS
FilterAddDevice(
    __in PDRIVER_OBJECT DriverObject,
    __in PDEVICE_OBJECT PhysicalDeviceObject
    )
/*++

Routine Description:

    The Plug & Play subsystem is handing us a brand new PDO, for which we
    (by means of INF registration) have been asked to provide a driver.

    We need to determine if we need to be in the driver stack for the device.
    Create a function device object to attach to the stack
    Initialize that device object
    Return status success.

    Remember: We can NOT actually send ANY non pnp IRPS to the given driver
    stack, UNTIL we have received an IRP_MN_START_DEVICE.

Arguments:

    DeviceObject - pointer to a device object.

    PhysicalDeviceObject -  pointer to a device object created by the
                            underlying bus driver.

Return Value:

    NT status code.

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDEVICE_OBJECT          deviceObject = NULL;
    PDEVICE_EXTENSION       deviceExtension;
    ULONG                   deviceType = FILE_DEVICE_SERIAL_PORT;

    PAGED_CODE ();


    //
    // IoIsWdmVersionAvailable(1, 0x20) returns TRUE on os after Windows 2000.
    //
//    if (!IoIsWdmVersionAvailable(1, 0x20)) {
    if (!RtlIsNtDdiVersionAvailable(NTDDI_WIN2K)) {
        //
        // Win2K system bugchecks if the filter attached to a storage device
        // doesn't specify the same DeviceType as the device it's attaching
        // to. This bugcheck happens in the filesystem when you disable
        // the devicestack whose top level deviceobject doesn't have a VPB.
        // To workaround we will get the toplevel object's DeviceType and
        // specify that in IoCreateDevice.
        //
        deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
        deviceType = deviceObject->DeviceType;
        ObDereferenceObject(deviceObject);
    }

    //
    // Create a filter device object.
    //

    status = IoCreateDevice (DriverObject,
                             sizeof (DEVICE_EXTENSION),
                             NULL,  // No Name
                             deviceType,
                             FILE_DEVICE_SECURE_OPEN,
                             FALSE,
                             &deviceObject);


    if (!NT_SUCCESS (status)) {
        //
        // 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.
        //
        return status;
    }

    DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL,
                "AddDevice PDO (0x%p) FDO (0x%p)\n", PhysicalDeviceObject, deviceObject ));

    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;

    deviceExtension->Type = DEVICE_TYPE_FIDO;

    deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack (
                                       deviceObject,
                                       PhysicalDeviceObject);
    //
    // Failure for attachment is an indication of a broken plug & play system.
    //

    if (NULL == deviceExtension->NextLowerDriver) {

        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    deviceObject->Flags |= deviceExtension->NextLowerDriver->Flags &
                            (DO_BUFFERED_IO | DO_DIRECT_IO |
                            DO_POWER_PAGABLE );


    deviceObject->DeviceType = deviceExtension->NextLowerDriver->DeviceType;

    deviceObject->Characteristics =
                          deviceExtension->NextLowerDriver->Characteristics;

    deviceExtension->Self = deviceObject;

    //
    // Let us use remove lock to keep count of IRPs so that we don't 
    // deteach and delete our deviceobject until all pending I/Os in our
    // devstack are completed. Remlock is required to protect us from
    // various race conditions where our driver can get unloaded while we
    // are still running dispatch or completion code.
    //
    
    IoInitializeRemoveLock (&deviceExtension->RemoveLock , 
                            POOL_TAG,
                            1, // MaxLockedMinutes 
                            100); // HighWatermark, this parameter is 
                                // used only on checked build. Specifies 
                                // the maximum number of outstanding 
                                // acquisitions allowed on the lock
                                

    //
    // Set the initial state of the Filter DO
    //

    INITIALIZE_PNP_STATE(deviceExtension);

    DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL,
                "AddDevice: %p to %p->%p \n", deviceObject,
                       deviceExtension->NextLowerDriver,
                       PhysicalDeviceObject ));

    deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    return STATUS_SUCCESS;
}


NTSTATUS
FilterPass (
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
/*++

Routine Description:

    The default dispatch routine.  If this driver does not recognize the
    IRP, then it should send it down, unmodified.
    If the device holds iris, this IRP must be queued in the device extension
    No completion routine is required.

    For demonstrative purposes only, we will pass all the (non-PnP) Irps down
    on the stack (as we are a filter driver). A real driver might choose to
    service some of these Irps.

    As we have NO idea which function we are happily passing on, we can make
    NO assumptions about whether or not it will be called at raised IRQL.
    For this reason, this function must be in put into non-paged pool
    (aka the default location).

Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

      NT status code

--*/
{
    PDEVICE_EXTENSION           deviceExtension;
    NTSTATUS    status;
    
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status)) {
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }

   IoSkipCurrentIrpStackLocation (Irp);
   status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
   IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
   return status;
}


NTSTATUS
FilterDispatchPnp (
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
/*++

Routine Description:

    The plug and play dispatch routines.

    Most of these 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 - pointer to an I/O Request Packet.

Return Value:

      NT status code

--*/
{
    PDEVICE_EXTENSION           deviceExtension;
    PIO_STACK_LOCATION         irpStack;
    NTSTATUS                            status;
    KEVENT                               event;

    PAGED_CODE();

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

    DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL,
                "FilterDO %s IRP:0x%p \n",
                PnPMinorFunctionString(irpStack->MinorFunction), Irp ));

    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status)) {
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }
    

    switch (irpStack->MinorFunction) {
    case IRP_MN_START_DEVICE:

        //
        // The device is starting.
        // We cannot touch the device (send it any non pnp irps) until a
        // start device has been passed down to the lower drivers.
        //
        KeInitializeEvent(&event, NotificationEvent, FALSE);
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutine(Irp,
                               (PIO_COMPLETION_ROUTINE) FilterStartCompletionRoutine,
                               &event,
                               TRUE,
                               TRUE,
                               TRUE);

        status = IoCallDriver(deviceExtension->NextLowerDriver, Irp);
        
        //
        // Wait for lower drivers to be done with the Irp. Important thing to
        // note here is when you allocate memory for an event in the stack  
        // you must do a KernelMode wait instead of UserMode to prevent 
        // the stack from getting paged out.
        //
        if (status == STATUS_PENDING) {

           KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);          
           status = Irp->IoStatus.Status;
        }

        if (NT_SUCCESS (status)) {

            //
            // As we are successfully now back, we will
            // first set our state to Started.
            //

            SET_NEW_PNP_STATE(deviceExtension, Started);

            //
            // On the way up inherit FILE_REMOVABLE_MEDIA during Start.
            // This characteristic is available only after the driver stack is started!.
            //
            if (deviceExtension->NextLowerDriver->Characteristics & FILE_REMOVABLE_MEDIA) {

                DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
            }
        }
        
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
        return status;

    case IRP_MN_REMOVE_DEVICE:

        //
        // Wait for all outstanding requests to complete
        //
        DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL, "Waiting for outstanding requests\n" ));

        IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp);

        IoSkipCurrentIrpStackLocation(Irp);

        status = IoCallDriver(deviceExtension->NextLowerDriver, Irp);

        SET_NEW_PNP_STATE(deviceExtension, Deleted);
        
        IoDetachDevice(deviceExtension->NextLowerDriver);
        IoDeleteDevice(DeviceObject);
        return status;


    case IRP_MN_QUERY_STOP_DEVICE:
        SET_NEW_PNP_STATE(deviceExtension, StopPending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_STOP_DEVICE:

        //
        // Check to see whether you have received cancel-stop
        // without first receiving a query-stop. This could happen if someone
        // above us fails a query-stop and passes down the subsequent
        // cancel-stop.
        //

        if (StopPending == deviceExtension->DevicePnPState)
        {
            //
            // We did receive a query-stop, so restore.
            //
            RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
        }
        status = STATUS_SUCCESS; // We must not fail this IRP.
        break;

    case IRP_MN_STOP_DEVICE:
        SET_NEW_PNP_STATE(deviceExtension, Stopped);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_QUERY_REMOVE_DEVICE:

        SET_NEW_PNP_STATE(deviceExtension, RemovePending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_SURPRISE_REMOVAL:

        SET_NEW_PNP_STATE(deviceExtension, SurpriseRemovePending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_REMOVE_DEVICE:

        //
        // Check to see whether you have received cancel-remove
        // without first receiving a query-remove. This could happen if
        // someone above us fails a query-remove and passes down the
        // subsequent cancel-remove.
        //

        if (RemovePending == deviceExtension->DevicePnPState)
        {
            //
            // We did receive a query-remove, so restore.
            //
            RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
        }

        status = STATUS_SUCCESS; // We must not fail this IRP.
        break;

    case IRP_MN_DEVICE_USAGE_NOTIFICATION:

        //
        // On the way down, pagable might become set. Mimic the driver
        // above us. If no one is above us, just set pagable.
        //
        if ((DeviceObject->AttachedDevice == NULL) ||
            (DeviceObject->AttachedDevice->Flags & DO_POWER_PAGABLE)) {

            DeviceObject->Flags |= DO_POWER_PAGABLE;
        }

        IoCopyCurrentIrpStackLocationToNext(Irp);

        IoSetCompletionRoutine(
            Irp,
            FilterDeviceUsageNotificationCompletionRoutine,
            NULL,
            TRUE,
            TRUE,
            TRUE
            );

        return IoCallDriver(deviceExtension->NextLowerDriver, Irp);

    default:
        //
        // If you don't handle any IRP you must leave the
        // status as is.
        //
        status = Irp->IoStatus.Status;

        break;
    }

    //
    // Pass the IRP down and forget it.
    //
    Irp->IoStatus.Status = status;
    IoSkipCurrentIrpStackLocation (Irp);
    status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
    return status;
}

NTSTATUS
FilterStartCompletionRoutine(
    __in PDEVICE_OBJECT   DeviceObject,
    __in PIRP             Irp,
    __in PVOID            Context
    )
/*++
Routine Description:
    A completion routine for use when calling the lower device objects to
    which our filter deviceobject is attached.

Arguments:

    DeviceObject - Pointer to deviceobject
    Irp          - Pointer to a PnP Irp.
    Context      - NULL
Return Value:

    NT Status is returned.

--*/

{
    PKEVENT             event = (PKEVENT)Context;

    UNREFERENCED_PARAMETER (DeviceObject);

    //
    // If the lower driver didn't return STATUS_PENDING, we don't need to 
    // set the event because we won't be waiting on it. 
    // This optimization avoids grabbing the dispatcher lock, and improves perf.
    //
    if (Irp->PendingReturned == TRUE) {
        KeSetEvent (event, IO_NO_INCREMENT, FALSE);
    }

    //
    // The dispatch routine will have to call IoCompleteRequest
    //

    return STATUS_MORE_PROCESSING_REQUIRED;

}

NTSTATUS
FilterDeviceUsageNotificationCompletionRoutine(
    __in PDEVICE_OBJECT   DeviceObject,
    __in PIRP             Irp,
    __in PVOID            Context
    )
/*++
Routine Description:
    A completion routine for use when calling the lower device objects to
    which our filter deviceobject is attached.

Arguments:

    DeviceObject - Pointer to deviceobject
    Irp          - Pointer to a PnP Irp.
    Context      - NULL
Return Value:

    NT Status is returned.

--*/

{
    PDEVICE_EXTENSION       deviceExtension;

    UNREFERENCED_PARAMETER(Context);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;


    if (Irp->PendingReturned) {

        IoMarkIrpPending(Irp);
    }

    //
    // On the way up, pagable might become clear. Mimic the driver below us.
    //
    if (!(deviceExtension->NextLowerDriver->Flags & DO_POWER_PAGABLE)) {

        DeviceObject->Flags &= ~DO_POWER_PAGABLE;
    }

    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 

    return STATUS_CONTINUE_COMPLETION;

}

NTSTATUS
FilterDispatchPower(
    __in PDEVICE_OBJECT    DeviceObject,
    __in PIRP              Irp
    )
/*++

Routine Description:

    This routine is the dispatch routine for power irps.

Arguments:

    DeviceObject - Pointer to the device object.

    Irp - Pointer to the request packet.

Return Value:

    NT Status code
--*/
{
    PDEVICE_EXTENSION   deviceExtension;
    NTSTATUS    status;
    
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status)) { // may be device is being removed.
        Irp->IoStatus.Status = status;
        PoStartNextPowerIrp(Irp);
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }

    PoStartNextPowerIrp(Irp);
    IoSkipCurrentIrpStackLocation(Irp);
    status = PoCallDriver(deviceExtension->NextLowerDriver, Irp);
    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
    return status;
}



VOID
FilterUnload(
    __in PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    Free all the allocated resources in DriverEntry, etc.

Arguments:

    DriverObject - pointer to a driver object.

Return Value:

    VOID.

--*/
{
    PAGED_CODE ();

    //
    // The device object(s) should be NULL now
    // (since we unload, all the devices objects associated with this
    // driver must be deleted.
    //
    ASSERT(DriverObject->DeviceObject == NULL);

    //
    // We should not be unloaded until all the devices we control
    // have been removed from our queue.
    //
    DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL, "unload\n" ));

    return;
}


NTSTATUS
FilterDispatchIoXp(
    __in PDEVICE_OBJECT    DeviceObject,
    __in PIRP              Irp
    )
/*++

Routine Description:

    This routine is the dispatch routine for non passthru irps.
    We will check the input device object to see if the request
    is meant for the control device object. If it is, we will
    handle and complete the IRP, if not, we will pass it down to 
    the lower driver.
    
Arguments:

    DeviceObject - Pointer to the device object.

    Irp - Pointer to the request packet.

Return Value:

    NT Status code
--*/
{
    PDEVICE_EXTENSION   deviceExtension;
    PURB                urb    = URB_FROM_IRP(Irp);
    NTSTATUS            status;
    static UCHAR        intr_addr;
    

    PAGED_CODE ();

	deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status)) {
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }

    //	retrieve the interrupt pipe address
    if( URB_FUNCTION_SELECT_CONFIGURATION==urb->UrbHeader.Function ) {
        USB_CONFIGURATION_DESCRIPTOR	*pdsc;
        UCHAR                           *ptr;
        UCHAR                           *pend;

		DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL, " XP: Bulk\n" ));

		pdsc	= urb->UrbSelectConfiguration.ConfigurationDescriptor;
        ptr		= (UCHAR *)pdsc;
        for( pend=ptr+pdsc->wTotalLength,ptr+=*ptr; ptr<pend; ptr+=*ptr ) {
            if( *(ptr+1)==USB_ENDPOINT_DESCRIPTOR_TYPE && *(ptr+3)==USB_ENDPOINT_TYPE_INTERRUPT ) {
                intr_addr   = *(ptr+2);
				break;
            }
        }
    }

	//	ignore unused interrupt request
    if( URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER==urb->UrbHeader.Function &&
        *((UCHAR *)urb->UrbBulkOrInterruptTransfer.PipeHandle+6)==intr_addr ) {
		IoSkipCurrentIrpStackLocation (Irp);
        Irp->IoStatus.Status = status	= STATUS_INTERNAL_ERROR;
        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
        return status;
	}

    IoSkipCurrentIrpStackLocation (Irp);
    status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
    return status;
}


NTSTATUS
FilterDispatchIoVista(
    __in PDEVICE_OBJECT    DeviceObject,
    __in PIRP              Irp
    )
/*++

Routine Description:

    This routine is the dispatch routine for non passthru irps.
    We will check the input device object to see if the request
    is meant for the control device object. If it is, we will
    handle and complete the IRP, if not, we will pass it down to 
    the lower driver.
    
Arguments:

    DeviceObject - Pointer to the device object.

    Irp - Pointer to the request packet.

Return Value:

    NT Status code
--*/
{
    PDEVICE_EXTENSION   deviceExtension;
    PURB                urb    = URB_FROM_IRP(Irp);
    NTSTATUS            status;
    static UCHAR        intr_addr;
	static BOOLEAN		use_bulk;
    
 
	PAGED_CODE ();

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status)) {
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }

    //    reform the configuration descripter
    if( URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE==urb->UrbHeader.Function &&
        USB_CONFIGURATION_DESCRIPTOR_TYPE==urb->UrbControlDescriptorRequest.DescriptorType ) {
        KEVENT        event;

        KeInitializeEvent(&event, NotificationEvent, FALSE);

        IoCopyCurrentIrpStackLocationToNext(Irp);

        IoSetCompletionRoutine(Irp,
                               (PIO_COMPLETION_ROUTINE) FilterStartCompletionRoutine,
                               &event,
                               TRUE,
                               TRUE,
                               TRUE
                               );

        status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);

        if (status == STATUS_PENDING) {
           KeWaitForSingleObject(&event,
                                 Executive,        // WaitReason
                                 KernelMode,    // must be Kernelmode to prevent the stack getting paged out
                                 FALSE,
                                 NULL            // indefinite wait
                                 );
           status = Irp->IoStatus.Status;
        }

        if( NT_SUCCESS(status) &&
            urb->UrbControlDescriptorRequest.TransferBuffer!=NULL &&
            urb->UrbControlDescriptorRequest.TransferBufferLength<256 ) {
            UCHAR    *pdsc, *ptr, *pend, *p0, *p1, buf[256];
            ULONG    len;

            //    swap the Communication/Data interface class descripters
            pdsc    = urb->UrbControlDescriptorRequest.TransferBuffer;
            len        = urb->UrbControlDescriptorRequest.TransferBufferLength;
            memcpy( buf, pdsc, len );
            p0    = NULL;
            p1    = NULL;
            for( ptr=buf,pend=ptr+len; ptr<pend; ptr+=*ptr ) {
                if( *(ptr+1)==USB_INTERFACE_DESCRIPTOR_TYPE ) {
                    if( *(ptr+5)==2 )			//    Communication Class
                        p0    = ptr;
                    else if( *(ptr+5)==10 )		//    Data Class
                        p1    = ptr;
                    if( p0!=NULL && p1!=NULL && p0<p1 ) {
                        memcpy( pdsc+(p0-buf), p1, len-(p1-buf) );
                        memcpy( pdsc+(len-(p1-p0)), p0, p1-p0 );

                        DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL,
                            "[GET_CONFIGURATION_DESCRIPTOR]: interface class swapped. 0x%p\n", 
                            Irp ));
                        break;
                    }
                }
            }
        }

        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
        return status;
    }

#define    MAX_NUMBER_OF_ENDPOINTS        8

    //    deceive bulk pipes as the interrupt
    if( URB_FUNCTION_SELECT_CONFIGURATION==urb->UrbHeader.Function ) {
        USB_CONFIGURATION_DESCRIPTOR    *pdsc;
        UCHAR                           *ptr;
        UCHAR                           *pend;
        int                             i, j, bulk_count = 0;
        UCHAR                           bulk_addr[MAX_NUMBER_OF_ENDPOINTS];

        pdsc = urb->UrbSelectConfiguration.ConfigurationDescriptor;

		use_bulk	= RtlCheckRegistryKey(RTL_REGISTRY_SERVICES,USE_BULK)==STATUS_SUCCESS;
		DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL, " Vista/XP: %s\n", use_bulk? "Bulk":"Interrupt" ));

		//    replace bulk pipes to the interrupt
        ptr    = (UCHAR *)pdsc;
        for( pend=ptr+pdsc->wTotalLength,ptr+=*ptr; ptr<pend; ptr+=*ptr ) {
            if( *(ptr+1)==USB_ENDPOINT_DESCRIPTOR_TYPE ) {
                if( *(ptr+3)==USB_ENDPOINT_TYPE_BULK ) {
                    *(ptr+3)    = USB_ENDPOINT_TYPE_INTERRUPT;
					if( !use_bulk ) {
						if( *(ptr+4)<8 )
							*(ptr+4)    = 8;
					}
                    *(ptr+6)    = 10;
                    if( bulk_count<MAX_NUMBER_OF_ENDPOINTS )
                        bulk_addr[bulk_count++]    = *(ptr+2);
                }
                else if( *(ptr+3)==USB_ENDPOINT_TYPE_INTERRUPT )
                    intr_addr   = *(ptr+2);
            }
        }

        IoSkipCurrentIrpStackLocation (Irp);

        status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);

        if( NT_SUCCESS(status) && bulk_count ) {
            USBD_INTERFACE_INFORMATION    *interface_info;

            interface_info = (USBD_INTERFACE_INFORMATION *)&urb->UrbSelectConfiguration.Interface;
            if( interface_info!=NULL ) {

                for( i=0; i<(int)interface_info->NumberOfPipes; i++ ) {
                    USBD_PIPE_INFORMATION    *pipe_info = &interface_info->Pipes[i];

                    //    retrieve the replaced endpoints
                    if( pipe_info!=NULL ) {
                        for( j=0; j<bulk_count; j++ ) {
                            if( pipe_info->EndpointAddress==bulk_addr[j] ) {
                                if(pipe_info->PipeHandle!=NULL) {
									if( use_bulk ) {
										UCHAR    *ph = (UCHAR *)pipe_info->PipeHandle;

										if( *(ph+7)==UsbdPipeTypeInterrupt ) {
											*(ph+7)     = UsbdPipeTypeBulk;
											*(ph+10)    = 0;
										}
									}
                                    pipe_info->PipeType    = UsbdPipeTypeBulk;    
                                    pipe_info->Interval    = 0;    
                                }
                                break;
                            }
                        }

                        DbgPrt(( DPFLTR_IHVBUS_ID, DBG_LEVEL,
                            "[SELECT_CONFIGURATION]: (retrieved pipe) [%x-%x]-[%x-%x-%x-%x] IRP:0x%p\n",
                            pipe_info->MaximumTransferSize,
                            pipe_info->PipeFlags,
                            pipe_info->MaximumPacketSize,
                            pipe_info->EndpointAddress,
                            pipe_info->Interval,
                            pipe_info->PipeType,
                            Irp ));
                    }
                }
            }
        }

        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
        return status;
    }

	//  ignore unused interrupt request
    if( URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER==urb->UrbHeader.Function &&
        *((UCHAR *)urb->UrbBulkOrInterruptTransfer.PipeHandle+6)==intr_addr ) {
		IoSkipCurrentIrpStackLocation (Irp);
        Irp->IoStatus.Status = status	= STATUS_INTERNAL_ERROR;
        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
        return status;
	}

    IoSkipCurrentIrpStackLocation (Irp);
    status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
    return status;
}

#if DBG

PCHAR
PnPMinorFunctionString (
    UCHAR MinorFunction
)
{
    switch (MinorFunction)
    {
        case IRP_MN_START_DEVICE:
            return "IRP_MN_START_DEVICE";
        case IRP_MN_QUERY_REMOVE_DEVICE:
            return "IRP_MN_QUERY_REMOVE_DEVICE";
        case IRP_MN_REMOVE_DEVICE:
            return "IRP_MN_REMOVE_DEVICE";
        case IRP_MN_CANCEL_REMOVE_DEVICE:
            return "IRP_MN_CANCEL_REMOVE_DEVICE";
        case IRP_MN_STOP_DEVICE:
            return "IRP_MN_STOP_DEVICE";
        case IRP_MN_QUERY_STOP_DEVICE:
            return "IRP_MN_QUERY_STOP_DEVICE";
        case IRP_MN_CANCEL_STOP_DEVICE:
            return "IRP_MN_CANCEL_STOP_DEVICE";
        case IRP_MN_QUERY_DEVICE_RELATIONS:
            return "IRP_MN_QUERY_DEVICE_RELATIONS";
        case IRP_MN_QUERY_INTERFACE:
            return "IRP_MN_QUERY_INTERFACE";
        case IRP_MN_QUERY_CAPABILITIES:
            return "IRP_MN_QUERY_CAPABILITIES";
        case IRP_MN_QUERY_RESOURCES:
            return "IRP_MN_QUERY_RESOURCES";
        case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
            return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
        case IRP_MN_QUERY_DEVICE_TEXT:
            return "IRP_MN_QUERY_DEVICE_TEXT";
        case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
            return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
        case IRP_MN_READ_CONFIG:
            return "IRP_MN_READ_CONFIG";
        case IRP_MN_WRITE_CONFIG:
            return "IRP_MN_WRITE_CONFIG";
        case IRP_MN_EJECT:
            return "IRP_MN_EJECT";
        case IRP_MN_SET_LOCK:
            return "IRP_MN_SET_LOCK";
        case IRP_MN_QUERY_ID:
            return "IRP_MN_QUERY_ID";
        case IRP_MN_QUERY_PNP_DEVICE_STATE:
            return "IRP_MN_QUERY_PNP_DEVICE_STATE";
        case IRP_MN_QUERY_BUS_INFORMATION:
            return "IRP_MN_QUERY_BUS_INFORMATION";
        case IRP_MN_DEVICE_USAGE_NOTIFICATION:
            return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
        case IRP_MN_SURPRISE_REMOVAL:
            return "IRP_MN_SURPRISE_REMOVAL";

        default:
            return "unknown_pnp_irp";
    }
}

#endif


