/*++

Copyright (C) Microsoft Corporation, 1997 - 1999

Module Name:

    ioctl.c

Abstract:

Author:

Environment:

    kernel mode only

Notes:

Revision History:

--*/

#include <stdio.h>
#include <stddef.h>
#include <wdm.h>
#include <usbscan.h>
#include "usbd_api.h"
#include "private.h"
//#include "missdef.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USDeviceControl)
#pragma alloc_text(PAGE, USReadWriteRegisters)
#pragma alloc_text(PAGE, USCancelPipe)
#pragma alloc_text(PAGE, USAbortResetPipe)
#endif

#ifdef _WIN64
BOOLEAN
IoIs32bitProcess(
    IN PIRP Irp
    );
#endif // _WIN64

NTSTATUS
USDeviceControl(
    IN PDEVICE_OBJECT pDeviceObject,
    IN PIRP pIrp
)
/*++

Routine Description:

Arguments:
    pDeviceObject - Device object for a device.
    pIrp          - DEVICE IOCTL irp

Return Value:
    NT Status - STATUS_SUCCESS

--*/
{
    PIO_STACK_LOCATION          pIrpStack;
    PIO_STACK_LOCATION          pNextIrpStack;
    PFILE_OBJECT                fileObject;
    PUSBSCAN_FILE_CONTEXT       pFileContext;
    ULONG                       IoControlCode;
    PUSBSCAN_DEVICE_EXTENSION   pde;
    NTSTATUS                    Status;
    PDRV_VERSION                pVersion;
    PDEVICE_DESCRIPTOR          pDesc;
    PUSBSCAN_GET_DESCRIPTOR     pGetDesc;
    PUSBSCAN_PIPE_CONFIGURATION pPipeConfig;
    PVOID                       pBuffer;

    IO_BLOCK                    LocalIoBlock;
    IO_BLOCK_EX                 LocalIoBlockEx;
    PIO_BLOCK                   pIoBlock;
    PIO_BLOCK_EX                pIoBlockEx;

    ULONG                       InLength;
    ULONG                       OutLength;
    BOOLEAN                     fRead = FALSE;
    BOOLEAN                     fAbort = TRUE;
    ULONG                       i;
    PURB                        pUrb;

    PAGED_CODE();

    DebugTrace(TRACE_PROC_ENTER,("USDeviceControl: Enter...\n"));

    //
    // Check arguments.
    //

    if( (NULL == pDeviceObject)
     || (NULL == pDeviceObject->DeviceExtension)
     || (NULL == pIrp) )
    {
        DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid parameter passed.\n"));
        Status = STATUS_INVALID_PARAMETER;
        DebugTrace(TRACE_PROC_LEAVE,("USDeviceControl: Leaving.. Status = %x.\n", Status));
        return Status;
    }

    //
    // Indicates I/O processing increase.
    //

    USIncrementIoCount( pDeviceObject );

    pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;

    if (FALSE == pde -> AcceptingRequests) {
        DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! IOCTL issued after device stopped/removed!\n"));
        Status = STATUS_DELETE_PENDING;
        pIrp -> IoStatus.Status = Status;
        pIrp -> IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

        goto USDeviceControl_return;
    }

    //
    // Check device power state.
    //

    if (PowerDeviceD0 != pde -> CurrentDevicePowerState) {
        DebugTrace(TRACE_WARNING,("USDeviceControl: WARNING!! Device is suspended.\n"));
        Status = STATUS_DELETE_PENDING;
        pIrp -> IoStatus.Status = Status;
        pIrp -> IoStatus.Information = 0;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

        goto USDeviceControl_return;
    }


    pIrpStack       = IoGetCurrentIrpStackLocation( pIrp );
    pNextIrpStack   = IoGetNextIrpStackLocation( pIrp );
    IoControlCode   = pIrpStack->Parameters.DeviceIoControl.IoControlCode;

    InLength        = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    OutLength       = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    pBuffer         = pIrp -> AssociatedIrp.SystemBuffer;

    fileObject      = pIrpStack->FileObject;
    pFileContext    = fileObject->FsContext;

    DebugTrace(TRACE_STATUS,("USDeviceControl: Control code 0x%x = ", IoControlCode));

    switch (IoControlCode) {

        case IOCTL_GET_VERSION:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_GET_VERSION\n"));

            if (OutLength < sizeof(DRV_VERSION) ) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Buffer(0x%x) too small(<0x%x)\n", OutLength, sizeof(DRV_VERSION)));
                DEBUG_BREAKPOINT();
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            pVersion            = (PDRV_VERSION)pBuffer;
            pVersion->major     = 1;
            pVersion->minor     = 0;
            pVersion->internal  = 0;

            pIrp -> IoStatus.Information = sizeof(DRV_VERSION);

            Status = STATUS_SUCCESS;
            break;

        case IOCTL_CANCEL_IO:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_CANCEL_IO\n"));
            fAbort = TRUE;

            //
            // Falling through to the next case, this is intentional. We want to reset pipe when
            // cancel requested
            //

        case IOCTL_RESET_PIPE:
            if(IOCTL_RESET_PIPE == IoControlCode){
                DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_RESET_PIPE\n"));
                fAbort = FALSE;
            }

            //
            // Validate buffer size
            //

            if (InLength < sizeof(PIPE_TYPE) ) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Pipe type buffer (0x%x bytes) too small\n" ,InLength));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            Status = USCancelPipe(pDeviceObject,
                                  pIrp,
                                  *(PIPE_TYPE*)pBuffer,
                                  fAbort);
            break;

        case IOCTL_WAIT_ON_DEVICE_EVENT:
        {
            ULONG   Index;
            ULONG   Timeout;
            PULONG  pTimeout;


            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_WAIT_ON_DEVICE_EVENT\n"));

            Index = USGetPipeIndexToUse(pDeviceObject,
                                        pIrp,
                                        pde -> IndexInterrupt);

            if (OutLength < pde -> PipeInfo[Index].MaximumPacketSize) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! User buffer(0x%x) too small(<)\n"
                                        , OutLength
                                        , pde -> PipeInfo[Index].MaximumPacketSize));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            //
            // Copy timeout value from file context.
            //

            Timeout = pFileContext->TimeoutEvent;

            //
            // If timeout value is 0, then never timeout.
            //

            if(0 == Timeout){
                pTimeout = NULL;
            } else {
                DebugTrace(TRACE_STATUS,("USDeviceControl: Timeout is set to 0x%x sec.\n", Timeout));
                pTimeout = &Timeout;
            }

            Status = USTransfer(pDeviceObject,
                                pIrp,
                                Index,
                                pIrp -> AssociatedIrp.SystemBuffer,
                                NULL,
                                pde  -> PipeInfo[Index].MaximumPacketSize,
                                pTimeout);

            //
            // IRP should be completed in USTransfer or its completion routine.
            //

            goto USDeviceControl_return;
        }

        case IOCTL_READ_REGISTERS:
            fRead = TRUE;
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_READ_REGISTERS\n"));

        case IOCTL_WRITE_REGISTERS:{

            if (IOCTL_WRITE_REGISTERS == IoControlCode) {
                DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_WRITE_REGISTERS\n"));
                fRead = FALSE;
            }
#ifdef _WIN64
            
            if(IoIs32bitProcess(pIrp)){
                PIO_BLOCK_32    pIoBlock32;

                if (InLength < sizeof(IO_BLOCK_32) ) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid input 32bit buffer size(0x%x<0x%x)\n"
                                            , InLength,
                                            sizeof(IO_BLOCK_32)));
                    Status = STATUS_INVALID_PARAMETER;
                    break;
                }

                //
                // Copy all parameters from 32bit structure.
                //
                
                pIoBlock32  = (PIO_BLOCK_32)pBuffer;
                pIoBlock    = &LocalIoBlock;

                pIoBlock -> uOffset = pIoBlock32 -> uOffset;
                pIoBlock -> uLength = pIoBlock32 -> uLength;
                pIoBlock -> pbyData = pIoBlock32 -> pbyData;
                pIoBlock -> uIndex  = pIoBlock32 -> uIndex;

            } else { // if(IoIs32bitProcess(pIrp))
#endif // _WIN64

            if (InLength < sizeof(IO_BLOCK) ) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid input buffer size(0x%x<0x%x)\n"
                                        , InLength,
                                        sizeof(IO_BLOCK)));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }
            pIoBlock = (PIO_BLOCK)pBuffer;

#ifdef _WIN64
            } // if(IoIs32bitProcess(pIrp))
#endif // _WIN64 

            if(TRUE == fRead){

                //
                // Check the size of Output buffer.
                //

                if (OutLength < pIoBlock -> uLength) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Out buffer(0x%x) too small(<0x%x)\n"
                                            , OutLength
                                            , pIoBlock -> uLength));
                    Status = STATUS_INVALID_PARAMETER;
                    break;
                } // if (OutLength < pIoBlock -> uLength)
            } // if(TRUE == fRead)

            pIrp -> IoStatus.Information = pIoBlock -> uLength;

            //
            // Caller gives us a pointer, embedded into IOCTL buffer. If call is made from
            // user-mode , we need to validate that given pointer is readable.
            //

            if (pIrp->RequestorMode != KernelMode) {

                try {
                    ProbeForRead(pIoBlock->pbyData,
                                 pIoBlock -> uLength,
                                 sizeof(UCHAR));

                } except(EXCEPTION_EXECUTE_HANDLER) {

                    DebugTrace(TRACE_ERROR,("USDeviceControl: Read/Write registers  buffer pointer is invalid\n"));
                    DEBUG_BREAKPOINT();

                    Status = GetExceptionCode();

                    pIrp -> IoStatus.Information = 0;
                    break;
                } // except
            } // !kernelmode

            //
            // Now go to worker function
            //

            Status = USReadWriteRegisters(pDeviceObject,
                                          pIoBlock,
                                          fRead,
                                          InLength);
            if (STATUS_SUCCESS != Status) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! USReadWriteRegisters failed\n"));
                DEBUG_BREAKPOINT();
                pIrp -> IoStatus.Information = 0;
            }
            break;
        } // case IOCTL_WRITE_REGISTERS:
        case IOCTL_GET_CHANNEL_ALIGN_RQST:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_GET_CHANNEL_ALIGN_REQUEST\n"));

            if (OutLength < sizeof(CHANNEL_INFO) ) {

                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Buffer(0x%x) too small(<0x%x)\n"
                                        , OutLength
                                        ,sizeof(CHANNEL_INFO)));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            pIoBlock = (PIO_BLOCK)pBuffer;
            RtlZeroMemory((PCHANNEL_INFO)pIoBlock, sizeof(CHANNEL_INFO));

            for (i = 0; i < pde -> NumberOfPipes; i++) {

                //
                // Have to check which pipe to use
                //

                ULONG Index;
                Index = USGetPipeIndexToUse(pDeviceObject,
                                            pIrp,
                                            i);
                switch (pde -> PipeInfo[Index].PipeType) {
                    case USB_ENDPOINT_TYPE_INTERRUPT:
                        ((PCHANNEL_INFO)pIoBlock)->EventChannelSize = pde -> PipeInfo[Index].MaximumPacketSize;
                        break;
                    case USB_ENDPOINT_TYPE_BULK:
                        if (pde -> pEndpointDescriptor[Index].bEndpointAddress & BULKIN_FLAG) {
                            ((PCHANNEL_INFO)pIoBlock) -> uReadDataAlignment  = pde -> PipeInfo[Index].MaximumPacketSize;
                        } else {
                            ((PCHANNEL_INFO)pIoBlock) -> uWriteDataAlignment = pde -> PipeInfo[Index].MaximumPacketSize;
                        }
                        break;
                }
            }
            pIrp -> IoStatus.Information = sizeof(CHANNEL_INFO);
            Status = STATUS_SUCCESS;
            break;

        case IOCTL_GET_DEVICE_DESCRIPTOR:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_GET_DEVICE_DESCRIPTOR\n"));

            if (OutLength < sizeof(DEVICE_DESCRIPTOR)) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Out buffer(0x%x) is too small(<0x%x)\n"
                                        , OutLength
                                        , sizeof(DEVICE_DESCRIPTOR)));
                Status = STATUS_INVALID_PARAMETER_6;
                break;
            }

            pDesc = (PDEVICE_DESCRIPTOR)pBuffer;
            pDesc -> usVendorId   = pde -> pDeviceDescriptor -> idVendor;
            pDesc -> usProductId  = pde -> pDeviceDescriptor -> idProduct;
            pDesc -> usBcdDevice  = pde -> pDeviceDescriptor -> bcdDevice;

            DebugTrace(TRACE_STATUS,("USDeviceControl: Vendor ID:%d\n", pDesc -> usVendorId));
            DebugTrace(TRACE_STATUS,("USDeviceControl: Product ID:%d\n", pDesc -> usProductId));
            DebugTrace(TRACE_STATUS,("USDeviceControl: BcdDevice:%d\n", pDesc -> usBcdDevice));

            pIrp -> IoStatus.Information = sizeof(DEVICE_DESCRIPTOR);
            Status = STATUS_SUCCESS;
            break;


        case IOCTL_GET_USB_DESCRIPTOR:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_GET_USB_DESCRIPTOR\n"));

            if (OutLength < sizeof(USBSCAN_GET_DESCRIPTOR)) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Out buffer(0x%x) is too small(<0x%x)\n"
                                        , OutLength
                                        , sizeof(USBSCAN_GET_DESCRIPTOR)));
                Status = STATUS_INVALID_PARAMETER_6;
                break;
            }

            pGetDesc = (PUSBSCAN_GET_DESCRIPTOR)pBuffer;
            pUrb = USAllocatePool(NonPagedPool,
                                  sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
            if (NULL == pUrb) {
                DebugTrace(TRACE_CRITICAL,("USDeviceControl: ERROR!! Can't allocate control descriptor URB.\n"));
                DEBUG_BREAKPOINT();
                Status = STATUS_INSUFFICIENT_RESOURCES;
                break;
            }

#ifdef DEBUG
            switch(pGetDesc -> DescriptorType){
                case USB_DEVICE_DESCRIPTOR_TYPE:
                    DebugTrace(TRACE_STATUS,("USDeviceControl: USB_DEVICE_DESCRIPTOR_TYPE\n"));
                    break;
                case USB_CONFIGURATION_DESCRIPTOR_TYPE:
                    DebugTrace(TRACE_STATUS,("USDeviceControl: USB_CONFIGURATION_DESCRIPTOR_TYPE\n"));
                    break;
                case USB_STRING_DESCRIPTOR_TYPE:
                    DebugTrace(TRACE_STATUS,("USDeviceControl: USB_STRING_DESCRIPTOR_TYPE\n"));
                    break;
                default:
                    DebugTrace(TRACE_WARNING,("USDeviceControl: WARNING!! 0x%x = Undefined.\n", pGetDesc -> DescriptorType));
                    Status = STATUS_INVALID_PARAMETER_3;

                    USFreePool(pUrb);
                    pUrb = NULL;
                    pIrp -> IoStatus.Information = 0;
                    goto USDeviceControl_return;
            }
            DebugTrace(TRACE_STATUS, ("USDeviceControl: Index         :%d\n",pGetDesc -> Index));
            DebugTrace(TRACE_STATUS, ("USDeviceControl: LanguageID    :%d\n", pGetDesc -> LanguageId));
#endif //DEBUG

            UsbBuildGetDescriptorRequest(pUrb,
                                         (USHORT)sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         pGetDesc -> DescriptorType,
                                         pGetDesc -> Index,
                                         pGetDesc -> LanguageId,
                                         pBuffer,
                                         NULL,
                                         OutLength,
                                         NULL);

            Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);
#ifdef DEBUG
            if ( (STATUS_SUCCESS == Status)
//           &&  (USB_DEVICE_DESCRIPTOR_TYPE == pGetDesc -> DescriptorType)
            )
            {
                PUSB_DEVICE_DESCRIPTOR pDeviceDescriptor;
                pDeviceDescriptor = (PUSB_DEVICE_DESCRIPTOR)pBuffer;
                DebugTrace(TRACE_STATUS,("USDeviceControl: Device Descriptor = %x, len %x\n",
                                           pDeviceDescriptor,
                                           pUrb->UrbControlDescriptorRequest.TransferBufferLength));

                DebugTrace(TRACE_STATUS,("USDeviceControl: USBSCAN Device Descriptor:\n"));
                DebugTrace(TRACE_STATUS,("USDeviceControl: -------------------------\n"));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bLength            %d\n",   pDeviceDescriptor->bLength));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bDescriptorType    0x%x\n", pDeviceDescriptor->bDescriptorType));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bcdUSB             0x%x\n", pDeviceDescriptor->bcdUSB));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bDeviceClass       0x%x\n", pDeviceDescriptor->bDeviceClass));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bDeviceSubClass    0x%x\n", pDeviceDescriptor->bDeviceSubClass));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bDeviceProtocol    0x%x\n", pDeviceDescriptor->bDeviceProtocol));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bMaxPacketSize0    0x%x\n", pDeviceDescriptor->bMaxPacketSize0));
                DebugTrace(TRACE_STATUS,("USDeviceControl: idVendor           0x%x\n", pDeviceDescriptor->idVendor));
                DebugTrace(TRACE_STATUS,("USDeviceControl: idProduct          0x%x\n", pDeviceDescriptor->idProduct));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bcdDevice          0x%x\n", pDeviceDescriptor->bcdDevice));
                DebugTrace(TRACE_STATUS,("USDeviceControl: iManufacturer      0x%x\n", pDeviceDescriptor->iManufacturer));
                DebugTrace(TRACE_STATUS,("USDeviceControl: iProduct           0x%x\n", pDeviceDescriptor->iProduct));
                DebugTrace(TRACE_STATUS,("USDeviceControl: iSerialNumber      0x%x\n", pDeviceDescriptor->iSerialNumber));
                DebugTrace(TRACE_STATUS,("USDeviceControl: bNumConfigurations 0x%x\n", pDeviceDescriptor->bNumConfigurations));

            } else {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR! Status = %d\n", Status));
            }

#endif //DEBUG

            USFreePool(pUrb);
            pUrb = NULL;
            pIrp -> IoStatus.Information = ((PUSB_DEVICE_DESCRIPTOR)pBuffer)->bLength;
            break;

        case IOCTL_SEND_USB_REQUEST:
        {

            //
            // Generic pass-through mechanism for USB vendor requests.
            //

            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_SEND_USB_REQUEST\n"));

            //
            // Validate length parameters.
            //

#ifdef _WIN64
            
            if(IoIs32bitProcess(pIrp)){
                PIO_BLOCK_EX_32 pIoBlockEx32;

                if (InLength < sizeof(IO_BLOCK_EX_32) ) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid input 32bit buffer size(0x%x<0x%x)\n"
                                            , InLength,
                                            sizeof(IO_BLOCK_EX_32)));
                    Status = STATUS_INVALID_PARAMETER;
                    break;
                }

                //
                // Copy all parameters from 32bit structure.
                //
                
                pIoBlockEx32    = (PIO_BLOCK_EX_32)pBuffer;
                pIoBlockEx      = &LocalIoBlockEx;

                pIoBlockEx -> uOffset               = pIoBlockEx32 -> uOffset;
                pIoBlockEx -> uLength               = pIoBlockEx32 -> uLength;
                pIoBlockEx -> pbyData               = pIoBlockEx32 -> pbyData;
                pIoBlockEx -> uIndex                = pIoBlockEx32 -> uIndex;
                pIoBlockEx -> bRequest              = pIoBlockEx32 -> bRequest;
                pIoBlockEx -> bmRequestType         = pIoBlockEx32 -> bmRequestType;
                pIoBlockEx -> fTransferDirectionIn  = pIoBlockEx32 -> fTransferDirectionIn;

            } else { // if(IoIs32bitProcess(pIrp))
#endif // _WIN64

            if (InLength < sizeof(IO_BLOCK_EX) ) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! I/O buffer(0x%x) too small(<0x%x)\n"
                                        , InLength
                                        , sizeof(IO_BLOCK_EX)));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            pIoBlockEx = (PIO_BLOCK_EX)pBuffer;

#ifdef _WIN64
            } // if(IoIs32bitProcess(pIrp))
#endif // _WIN64 

            if (pIoBlockEx->fTransferDirectionIn) {

                //
                // Check output buffer length is valid.
                //

                if (OutLength < pIoBlockEx -> uLength) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! OutLength too small\n"));
                    DEBUG_BREAKPOINT();
                    Status = STATUS_INVALID_PARAMETER;
                    pIrp -> IoStatus.Information = 0;
                    break;
                }
                pIrp -> IoStatus.Information = pIoBlockEx -> uLength;

            } else {

                //
                // No output to the caller.
                //

                pIrp -> IoStatus.Information = 0;
            }

            //
            // Validate user buffer.
            //

            if (pIrp->RequestorMode != KernelMode) {

                try {
                    ProbeForRead(pIoBlockEx->pbyData,
                                 pIoBlockEx->uLength,
                                 sizeof(UCHAR));

                } except(EXCEPTION_EXECUTE_HANDLER) {

                    DebugTrace(TRACE_ERROR,("USDeviceControl: User buffer pointer is invalid\n"));

                    Status = GetExceptionCode();

                    pIrp -> IoStatus.Information = 0;
                    break;
                } // except
            } // !kernelmode

            //
            // Now go to worker function
            //

            Status = USPassThruUSBRequest(pDeviceObject,
                                          (PIO_BLOCK_EX)pBuffer,
                                          InLength,
                                          OutLength
                                          );

            if (STATUS_SUCCESS != Status) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! USPassThruUSBRequest failed\n"));
                DEBUG_BREAKPOINT();
                pIrp -> IoStatus.Information = 0;
            }

            break;

        } // case IOCTL_SEND_USB_REQUEST:
        
        case IOCTL_SEND_USB_REQUEST_PTP:
        {

            //
            // Generic pass-through mechanism for USB vendor requests.
            //

            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_SEND_USB_REQUEST_PTP\n"));

            //
            // Validate length parameters.
            //

#ifdef _WIN64
            
            if(IoIs32bitProcess(pIrp)){
                PIO_BLOCK_EX_32 pIoBlockEx32;

                if (InLength < sizeof(IO_BLOCK_EX_32) ) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid input 32bit buffer size(0x%x<0x%x)\n"
                                            , InLength,
                                            sizeof(IO_BLOCK_EX_32)));
                    Status = STATUS_INVALID_PARAMETER;
                    break;
                }

                //
                // Copy all parameters from 32bit structure.
                //
                
                pIoBlockEx32    = (PIO_BLOCK_EX_32)pBuffer;
                pIoBlockEx      = &LocalIoBlockEx;

                pIoBlockEx -> uOffset               = pIoBlockEx32 -> uOffset;
                pIoBlockEx -> uLength               = pIoBlockEx32 -> uLength;
                pIoBlockEx -> pbyData               = pIoBlockEx32 -> pbyData;
                pIoBlockEx -> uIndex                = pIoBlockEx32 -> uIndex;
                pIoBlockEx -> bRequest              = pIoBlockEx32 -> bRequest;
                pIoBlockEx -> bmRequestType         = pIoBlockEx32 -> bmRequestType;
                pIoBlockEx -> fTransferDirectionIn  = pIoBlockEx32 -> fTransferDirectionIn;

            } else { // if(IoIs32bitProcess(pIrp))
#endif // _WIN64

            if (InLength < sizeof(IO_BLOCK_EX) ) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! I/O buffer(0x%x) too small(<0x%x)\n"
                                        , InLength
                                        , sizeof(IO_BLOCK_EX)));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            pIoBlockEx = (PIO_BLOCK_EX)pBuffer;

#ifdef _WIN64
            } // if(IoIs32bitProcess(pIrp))
#endif // _WIN64 

            if (pIoBlockEx->fTransferDirectionIn) {

                //
                // Check output buffer length is valid.
                //

                if (OutLength < pIoBlockEx -> uLength) {
                    DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! OutLength too small\n"));
                    DEBUG_BREAKPOINT();
                    Status = STATUS_INVALID_PARAMETER;
                    pIrp -> IoStatus.Information = 0;
                    break;
                }
                pIrp -> IoStatus.Information = pIoBlockEx -> uLength;

            } else {

                //
                // No output to the caller.
                //

                pIrp -> IoStatus.Information = 0;
            }

            //
            // Validate user buffer.
            //

            if (pIrp->RequestorMode != KernelMode) {

                try {
                    ProbeForRead(pIoBlockEx->pbyData,
                                 pIoBlockEx->uLength,
                                 sizeof(UCHAR));

                } except(EXCEPTION_EXECUTE_HANDLER) {

                    DebugTrace(TRACE_ERROR,("USDeviceControl: User buffer pointer is invalid\n"));

                    Status = GetExceptionCode();

                    pIrp -> IoStatus.Information = 0;
                    break;
                } // except
            } // !kernelmode

            //
            // Now go to worker function
            //

            Status = USPassThruUSBRequestPTP(pDeviceObject,
                                             (PIO_BLOCK_EX)pBuffer,
                                             InLength,
                                             OutLength);

            if (STATUS_SUCCESS != Status) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! USPassThruUSBRequestPTP failed\n"));
                DEBUG_BREAKPOINT();
                pIrp -> IoStatus.Information = 0;
            }

            break;

        } // case IOCTL_SEND_USB_REQUEST_PTP:

        case IOCTL_GET_PIPE_CONFIGURATION:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_GET_PIPE_CONFIGURATION\n"));

            //
            // Check output buffer length
            //

            if (OutLength < sizeof(USBSCAN_PIPE_CONFIGURATION)) {
                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! GetPipeConfig buffer(0x%x) too small(<0x%x)\n"
                                        , OutLength
                                        , sizeof(USBSCAN_PIPE_CONFIGURATION)));
                Status = STATUS_INVALID_PARAMETER_6;
                break;
            }

            //
            // Copy Pipe configuration to user buffer.
            //

            pPipeConfig = (PUSBSCAN_PIPE_CONFIGURATION)pBuffer;
            RtlZeroMemory(pPipeConfig, sizeof(USBSCAN_PIPE_CONFIGURATION));

            pPipeConfig->NumberOfPipes = pde->NumberOfPipes;
            for(i=0; i < pPipeConfig->NumberOfPipes; i++){
                pPipeConfig->PipeInfo[i].MaximumPacketSize = pde->PipeInfo[i].MaximumPacketSize;
                pPipeConfig->PipeInfo[i].EndpointAddress = pde->PipeInfo[i].EndpointAddress;
                pPipeConfig->PipeInfo[i].Interval = pde->PipeInfo[i].Interval;
                pPipeConfig->PipeInfo[i].PipeType = pde->PipeInfo[i].PipeType;
            }

            pIrp -> IoStatus.Information = sizeof(USBSCAN_PIPE_CONFIGURATION);
            Status = STATUS_SUCCESS;
            break;


        case IOCTL_SET_TIMEOUT:
            DebugTrace(TRACE_STATUS,("USDeviceControl: IOCTL_SET_TIMEOUT\n"));

            //
            // Make sure input buffer size is big enough.
            //

            if(sizeof(USBSCAN_TIMEOUT) > InLength){

                //
                // Incorrect Input buffer size.
                //

                DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Invalid input buffer size\n"));
                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            //
            // Copy timeout value.
            //

            pFileContext -> TimeoutRead     = ((PUSBSCAN_TIMEOUT)pBuffer) -> TimeoutRead;
            pFileContext -> TimeoutWrite    = ((PUSBSCAN_TIMEOUT)pBuffer) -> TimeoutWrite;
            pFileContext -> TimeoutEvent    = ((PUSBSCAN_TIMEOUT)pBuffer) -> TimeoutEvent;

            pIrp -> IoStatus.Information = 0;

            Status = STATUS_SUCCESS;
            break;

        default:
            DebugTrace(TRACE_ERROR,("USDeviceControl: ERROR!! Unsupported IOCTL\n"));
            Status = STATUS_NOT_SUPPORTED;
            break;
    }


    pIrp -> IoStatus.Status = Status;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

USDeviceControl_return:
    USDecrementIoCount(pDeviceObject);
    DebugTrace(TRACE_PROC_LEAVE,("USDeviceControl: Leaving.. Status = 0x%x\n", Status));
    return Status;


} // end USDeviceControl()



NTSTATUS
USReadWriteRegisters(
   IN   PDEVICE_OBJECT  pDeviceObject,
   IN   PIO_BLOCK       pIoBlock,
   IN   BOOLEAN         fRead,
   IN   ULONG           IoBlockSize
)
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS                    Status;
    PUSBSCAN_DEVICE_EXTENSION   pde;
    PURB                        pUrb;
    ULONG                       siz;
    UCHAR                       Request;
    PVOID                       pBuffer = NULL;
    //USHORT                      uIndex;
    unsigned                   uIndex;

    PAGED_CODE();

    DebugTrace(TRACE_PROC_ENTER,("USReadWriteRegisters: Enter..\n"));

    pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;

    //
    // Allocate URB
    //

    siz = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
    pUrb = USAllocatePool(NonPagedPool, siz);
    if (NULL == pUrb) {
        DebugTrace(TRACE_CRITICAL,("USReadWriteRegisters: ERROR!! cannot allocated URB\n"));
        DEBUG_BREAKPOINT();
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto USReadWriteRegisters_return;
    }

    RtlZeroMemory(pUrb, siz);

    //
    // Setup URB
    //

    Request  = REGISTER_AREA;
    if (pIoBlock -> uLength > 1) {
        DebugTrace(TRACE_STATUS,("USReadWriteRegisters: ULength > 1, turning on automatic increment\n"));
        Request |= OPCODE_SEQ_TRANSFER;           // automatic address increment after the read
    } else {
        Request |= OPCODE_SINGLE_ADDR_TRANSFER;   // no address increment after the read
    }

    //
    // Reading registers will read into pIoBlock itself.
    //

    pBuffer = pIoBlock;

    //
    // If we are writing registers, then we need to make a copy of the
    // register block into a non-paged block of memory before handing it off
    // to usbd.
    //

    if (!fRead) {
        DebugTrace(TRACE_STATUS,("USReadWriteRegisters: Write request, allocating non-paged reg buffer, len = %d\n",pIoBlock->uLength));
        pBuffer = USAllocatePool(NonPagedPool, pIoBlock->uLength);
        if (NULL == pBuffer) {
            DebugTrace(TRACE_CRITICAL,("USReadWriteRegisters: ERROR!! cannot allocate write reg buffer\n"));
            DEBUG_BREAKPOINT();
            USFreePool(pUrb);
            pUrb = NULL;
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto USReadWriteRegisters_return;
        }

        //
        // Caller gives us a pointer, embedded into IOCTL buffer. We need to
        // validate that given pointer is readable.
        //

        try{
            RtlCopyMemory(pBuffer,
                          pIoBlock->pbyData,
                          pIoBlock->uLength);

        } except(EXCEPTION_EXECUTE_HANDLER) {

            //
            // Caller buffer is not valid, or worse..
            //

            DebugTrace(TRACE_ERROR,("USReadWriteRegisters: ERROR!! Copying caller buffer failed.\n"));
            DEBUG_BREAKPOINT();
            Status = GetExceptionCode();

            //
            // Clear allocated pool
            //

            USFreePool(pUrb);
            USFreePool(pBuffer);
            pUrb = NULL;
            pBuffer = NULL;

            goto USReadWriteRegisters_return;
        }

    }

    //
    // If the IoBlock is new style (Intel has added a uIndex field to the end of it),
    // then make sure we pass the corrected uIndex value to usbd.
    //

    uIndex = 0;
    if (IoBlockSize == sizeof(IO_BLOCK)) {
        DebugTrace(TRACE_STATUS,("USReadWriteRegisters: New (intel) style IoBlock -- setting uIndex to pIoBlock -> uIndex\n"));
        uIndex = pIoBlock -> uIndex;
    }


    UsbBuildVendorClassSpecificCommand(pUrb,
                                       fRead ? USBD_TRANSFER_DIRECTION_IN : 0,
                                       pIoBlock->uLength,
                                       pBuffer,
                                       NULL,
                                       fRead ? 0xc0 : 0x40,
                                       Request,
                                       (SHORT)pIoBlock->uOffset,
                                       (USHORT)uIndex);

    Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);

    if (!fRead) {
        DebugTrace(TRACE_STATUS,("USReadWriteRegisters: freeing temp reg buffer\n"));
        USFreePool(pBuffer);
        pBuffer = NULL;
    }

    USFreePool(pUrb);
    pUrb = NULL;

USReadWriteRegisters_return:
    if(!NT_SUCCESS(Status)){
        DebugTrace(TRACE_ERROR,("USReadWriteRegisters: ERROR!! Still had unfreed pointer. Free it...\n"));

        if(pUrb){
            USFreePool(pUrb);
        }
        if( (pBuffer)
         && (!fRead ) )
        {
            USFreePool(pBuffer);
        }
    }
    DebugTrace(TRACE_PROC_LEAVE,("USReadWriteRegisters: Leaving.. Status = 0x%x\n", Status));
    return Status;
}


NTSTATUS
USCancelPipe(
    IN PDEVICE_OBJECT   pDeviceObject,
    IN PIRP             pIrp,
    IN PIPE_TYPE        PipeType,
    IN BOOLEAN          fAbort          // TRUE = Abort, FALSE = Reset
)
/*++

Routine Description:

    Cansel URB or reset pipe. If PipeType is PIPE_ALL, it applies to every pipes a device has.
    If not, it applies to only one default pipe even if a device supports multipul same type of pipes.

Arguments:
    pDeviceObject   -   Pointer to Device Object
    pIrp            -   Can be NULL if PipeType is ALL_PIPE
    PipeType        -   Specifies type of pipe
    fAbort          -   Specifies type of operation

Return Value:

    Returns status
--*/
{
    NTSTATUS                    Status, temp;
    PUSBSCAN_DEVICE_EXTENSION   pde;

    PAGED_CODE();

//    DebugTrace(TRACE_PROC_ENTER,("USCancelPipe: Enter.. - "));

    Status = STATUS_SUCCESS;
    pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;

    switch (PipeType) {

        case EVENT_PIPE:

            DebugTrace(TRACE_STATUS,("USCancelPipe: EVENT_PIPE\n"));

            if(NULL == pIrp){
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! pIrp not valid\n"));
                break;
            }

            if (-1 == pde -> IndexInterrupt) {
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! Interrupt pipe not valid\n"));
                DEBUG_BREAKPOINT();
                Status = STATUS_NOT_SUPPORTED;
                break;
            }

            Status = USAbortResetPipe(pDeviceObject,
                                      USGetPipeIndexToUse(pDeviceObject, pIrp, pde -> IndexInterrupt),
                                      fAbort);
            DebugTrace(TRACE_STATUS,("Event Pipe aborted/reset, Status = 0x%x\n",Status));
            break;

        case READ_DATA_PIPE:

            DebugTrace(TRACE_STATUS,("USCancelPipe: READ_DATA_PIPE\n"));

            if(NULL == pIrp){
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! pIrp not valid\n"));
                break;
            }

            if (-1 == pde -> IndexBulkIn) {
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! bulk-in pipe not valid\n"));
                DEBUG_BREAKPOINT();
                Status = STATUS_NOT_SUPPORTED;
                break;
            }
            Status = USAbortResetPipe(pDeviceObject,
                                      USGetPipeIndexToUse(pDeviceObject, pIrp, pde -> IndexBulkIn),
                                      fAbort);
            DebugTrace(TRACE_STATUS,("USCancelPipe: Read Pipe aborted/reset, Status = 0x%x\n",Status));
            break;

        case WRITE_DATA_PIPE:

            DebugTrace(TRACE_STATUS,("USCancelPipe: WRITE_DATA_PIPE\n"));

            if(NULL == pIrp){
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! pIrp not valid\n"));
                break;
            }

            if (-1 == pde -> IndexBulkOut) {
                DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! bulk-out pipe not valid\n"));
                DEBUG_BREAKPOINT();
                Status = STATUS_NOT_SUPPORTED;
                break;
            }
            Status = USAbortResetPipe(pDeviceObject,
                                      USGetPipeIndexToUse(pDeviceObject, pIrp, pde -> IndexBulkOut),
                                      fAbort);
            DebugTrace(TRACE_STATUS,("Write Pipe aborted/reset, Status = 0x%x\n",Status));
            break;

        case ALL_PIPE:
        {
            ULONG i;

            DebugTrace(TRACE_STATUS,("USCancelPipe: ALL_PIPE\n"));

            for(i=0; i < pde -> NumberOfPipes; i++){
                temp = USAbortResetPipe(pDeviceObject, i, fAbort);
                // DebugTrace(TRACE_STATUS,("USCancelPipe: pipe[%d] aborted/reset, Status = 0x%x\n", i, temp));
                if(STATUS_SUCCESS != temp){
                    Status = temp;
                }
            }
            break;
        }

        default:

            DebugTrace(TRACE_ERROR,("USCancelPipe: ERROR!! INVALID_PIPE\n"));

            Status = STATUS_INVALID_PARAMETER;
            break;
    }

    DebugTrace(TRACE_PROC_LEAVE,("USCancelPipe: Leaving.. Status = 0x%x\n", Status));
    return Status;
}


NTSTATUS
USAbortResetPipe(
    IN PDEVICE_OBJECT pDeviceObject,
    IN ULONG uIndex,
    IN BOOLEAN fAbort       // TRUE = Abort, FALSE = Reset
)
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS                    Status = STATUS_SUCCESS;
    NTSTATUS                    StatusReset = STATUS_SUCCESS;
    PUSBSCAN_DEVICE_EXTENSION   pde;
    PURB                        pUrb;
    ULONG                       siz;

    PAGED_CODE();

    DebugTrace(TRACE_PROC_ENTER,("USAbortResetPipe: Enter... \n"));

    pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;
    pUrb = NULL;

    //
    // Allocate URB
    //

    siz = sizeof(struct _URB_PIPE_REQUEST);
    pUrb = USAllocatePool(NonPagedPool, siz);
    if (NULL == pUrb) {
        DebugTrace(TRACE_ERROR,("USAbortResetPipe: ERROR!! cannot allocated URB\n"));
        DEBUG_BREAKPOINT();
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto USAbortResetPipe_return;
    }
    RtlZeroMemory(pUrb, siz);

    if (fAbort) {

        DebugTrace(TRACE_STATUS,("USAbortResetPipe: Aborting pipe[%d]\n", uIndex));

        //
        // Issue abort pipe call to USBD.
        //

        UsbBuildAbortPipeRequest(pUrb,
                                 siz,
                                 pde -> PipeInfo[uIndex].PipeHandle);

        Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);

        if (STATUS_SUCCESS != Status) {
            DebugTrace(TRACE_ERROR,("USAbortResetPipe: ERROR!! Abort pipe failed. Status = 0x%x\n",Status));
            goto USAbortResetPipe_return;
        }

        UsbBuildResetPipeRequest(pUrb,
                                 siz,
                                 pde -> PipeInfo[uIndex].PipeHandle);

        StatusReset = USBSCAN_CallUSBD(pDeviceObject, pUrb);

        if (STATUS_SUCCESS != StatusReset) {
            DebugTrace(TRACE_ERROR,("USAbortResetPipe: ERROR!! resetting pipe. Status = 0x%x\n",StatusReset));
            goto USAbortResetPipe_return;
        }


    } else {

        DebugTrace(TRACE_STATUS,("Reseting pipe[%d]\n", uIndex));

        //
        // Issue reset pipe call to USBD.
        //

        UsbBuildResetPipeRequest(pUrb,
                                 siz,
                                 pde -> PipeInfo[uIndex].PipeHandle);

        Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);

        if (STATUS_SUCCESS != Status) {
            DebugTrace(TRACE_ERROR,("USAbortResetPipe: ERROR!! Reset pipe failed. Status = 0x%x\n",Status));
            goto USAbortResetPipe_return;
        }
    }

USAbortResetPipe_return:
    //
    // Clean up.
    //

    if(pUrb){
        USFreePool(pUrb);
    }

    DebugTrace(TRACE_PROC_LEAVE,("USAbortResetPipe: Leaving.. Status = 0x%x\n", Status));
    return Status;
}

NTSTATUS
USPassThruUSBRequest(
    IN PDEVICE_OBJECT   pDeviceObject,
    IN PIO_BLOCK_EX     pIoBlockEx,
    IN ULONG            InLength,
    IN ULONG            OutLength
)
/*++

Routine Description:

    Implements generic pass-thru for vendor request to USBD

Arguments:

    pDeviceObject   -   Device object
    pIoBlockEx      -   Pointer to I/O block as described in USBSCAN.H, passed from user mode client
    InLength        -   In length from IRP
    OutLength       -   Out length from IRP

Return Value:

    NTSTATUS type

--*/
{
    NTSTATUS                    Status;
    PUSBSCAN_DEVICE_EXTENSION   pde;
    PURB                        pUrb;
    ULONG                       siz;
    PVOID                       pBuffer;
    BOOLEAN                     fDirectionIn;

    PAGED_CODE();

    DebugTrace(TRACE_PROC_ENTER,("USPassThruUSBRequest: Enter..\n"));

    //
    // Initialize local variable.
    //

    pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;

    Status  = STATUS_SUCCESS;
    pUrb    = NULL;
    pBuffer = NULL;
    fDirectionIn = TRUE;

    //
    // Allocate memory for URB
    //

    siz = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
    pUrb = USAllocatePool(NonPagedPool, siz);
    if (NULL == pUrb) {
        DebugTrace(TRACE_CRITICAL,("USPassThruUSBRequest: ERROR!! cannot allocated URB\n"));
        DEBUG_BREAKPOINT();
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto USPassThruUSBRequest_return;
    }

    RtlZeroMemory(pUrb, siz);

    //
    // Setup URB
    //

    pBuffer = pIoBlockEx;

    //
    // If we are writing data, then we need to make a copy of the
    // register block into a non-paged block of memory before handing it off
    // to usbd.
    //

    if (!pIoBlockEx->fTransferDirectionIn) {

        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Write request, allocating non-paged buffer, len = %d\n",pIoBlockEx->uLength));

        fDirectionIn = FALSE;

        if ( pIoBlockEx->uLength ) {

            pBuffer = USAllocatePool(NonPagedPool, pIoBlockEx->uLength);
            if (NULL == pBuffer) {

                DebugTrace(TRACE_CRITICAL,("USPassThruUSBRequest: ERROR!! cannot allocate write buffer"));
                DEBUG_BREAKPOINT();

                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto USPassThruUSBRequest_return;
            }

            //
            // Caller gives us a pointer, embedded into IOCTL buffer. We need to
            // validate that given pointer is readable.
            //

            try{
                RtlCopyMemory(pBuffer,
                              pIoBlockEx->pbyData,
                              pIoBlockEx->uLength);

            } except(EXCEPTION_EXECUTE_HANDLER) {

                //
                // Caller buffer is not valid, or worse..
                //

                DebugTrace(TRACE_ERROR,("USPassThruUSBRequest: ERROR!! Copying caller buffer failed.\n"));
                DEBUG_BREAKPOINT();
                Status = GetExceptionCode();

                goto USPassThruUSBRequest_return;
            }


        } else {

            //
            // Zero length buffer used for Write , IHV claim that's useful.
            //

            pBuffer = NULL;
        } // if ( pIoBlockEx->uLength )
    }

    UsbBuildVendorClassSpecificCommand(pUrb,
                                       pIoBlockEx->fTransferDirectionIn ? USBD_TRANSFER_DIRECTION_IN : 0,
                                       pIoBlockEx->uLength,
                                       pBuffer,
                                       NULL,
                                       pIoBlockEx->bmRequestType,
                                       pIoBlockEx->bRequest,
                                       (SHORT)pIoBlockEx->uOffset,
                                       (USHORT)pIoBlockEx -> uIndex
                                       );

    Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);

USPassThruUSBRequest_return:

    //
    // Clean up.
    //

    if(NULL != pUrb){
        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Free USB Request Block.\n"));
            USFreePool(pUrb);
        }

    if( (!fDirectionIn)
     && (NULL != pBuffer) )
    {
        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Free temp buffer.\n"));
            USFreePool(pBuffer);
        }

    DebugTrace(TRACE_PROC_LEAVE,("USPassThruUSBRequest: Leaving.. Status = 0x%x\n", Status));
    return Status;

}

NTSTATUS
USPassThruUSBRequestPTP(
    IN PDEVICE_OBJECT   pDeviceObject,
    IN PIO_BLOCK_EX     pIoBlockEx,
    IN ULONG            InLength,
    IN ULONG            OutLength
)
/*++

Routine Description:

    Implements generic pass-thru for vendor request to USBD

Arguments:

    pDeviceObject   -   Device object
    pIoBlockEx      -   Pointer to I/O block as described in USBSCAN.H, passed from user mode client
    InLength        -   In length from IRP
    OutLength       -   Out length from IRP

Return Value:

    NTSTATUS type

--*/
{
    NTSTATUS                    Status;
    PUSBSCAN_DEVICE_EXTENSION   pde;
    PURB                        pUrb;
    ULONG                       siz;
    PVOID                       pBuffer;
    BOOLEAN                     fDirectionIn;
    USHORT                      usUsbFunction;

    PAGED_CODE();

    DebugTrace(TRACE_PROC_ENTER,("USPassThruUSBRequest: Enter..\n"));

    //
    // Initialize local variable.
    //

    pde             = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension;

    Status          = STATUS_SUCCESS;
    pUrb            = NULL;
    pBuffer         = NULL;
    fDirectionIn    = TRUE;
    usUsbFunction   = 0;

    //
    // Allocate memory for URB
    //

    siz = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
    pUrb = USAllocatePool(NonPagedPool, siz);
    if (NULL == pUrb) {
        DebugTrace(TRACE_CRITICAL,("USPassThruUSBRequest: ERROR!! cannot allocated URB\n"));
        DEBUG_BREAKPOINT();
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto USPassThruUSBRequest_return;
    }

    RtlZeroMemory(pUrb, siz);

    //
    // Setup URB
    //

    pBuffer = pIoBlockEx;

    //
    // If we are writing data, then we need to make a copy of the
    // register block into a non-paged block of memory before handing it off
    // to usbd.
    //

    if (!pIoBlockEx->fTransferDirectionIn) {

        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Write request, allocating non-paged buffer, len = %d\n",pIoBlockEx->uLength));

        fDirectionIn = FALSE;

        if ( pIoBlockEx->uLength ) {

            pBuffer = USAllocatePool(NonPagedPool, pIoBlockEx->uLength);
            if (NULL == pBuffer) {

                DebugTrace(TRACE_CRITICAL,("USPassThruUSBRequest: ERROR!! cannot allocate write buffer"));
                DEBUG_BREAKPOINT();

                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto USPassThruUSBRequest_return;
            }

            //
            // Caller gives us a pointer, embedded into IOCTL buffer. We need to
            // validate that given pointer is readable.
            //

            try{
                RtlCopyMemory(pBuffer,
                              pIoBlockEx->pbyData,
                              pIoBlockEx->uLength);

            } except(EXCEPTION_EXECUTE_HANDLER) {

                //
                // Caller buffer is not valid, or worse..
                //

                DebugTrace(TRACE_ERROR,("USPassThruUSBRequest: ERROR!! Copying caller buffer failed.\n"));
                DEBUG_BREAKPOINT();
                Status = GetExceptionCode();

                goto USPassThruUSBRequest_return;
            }


        } else {

            //
            // Zero length buffer used for Write , IHV claim that's useful.
            //

            pBuffer = NULL;
        } // if ( pIoBlockEx->uLength )
    }

    //
    // Set proper USB funtion depends on bmRequestType.
    //
    
    if(0xa1 == pIoBlockEx->bmRequestType){          // USB_PTPREQUEST_TYPE_IN: Class/Interface Device to Host.
        usUsbFunction = URB_FUNCTION_CLASS_INTERFACE;
    } else if(0x21 == pIoBlockEx->bmRequestType){   // USB_PTPREQUEST_TYPE_OUT: Class/Interface Host to Device.
        usUsbFunction = URB_FUNCTION_CLASS_INTERFACE;
    } else {                                        // Default.
        usUsbFunction = URB_FUNCTION_VENDOR_DEVICE;
    }

    UsbBuildVendorClassSpecificCommandPTP(usUsbFunction,
                                          pUrb,
                                          pIoBlockEx->fTransferDirectionIn ? USBD_TRANSFER_DIRECTION_IN : 0,
                                          pIoBlockEx->uLength,
                                          pBuffer,
                                          NULL,
                                          pIoBlockEx->bmRequestType,
                                          pIoBlockEx->bRequest,
                                          (SHORT)pIoBlockEx->uOffset,
                                          (USHORT)pIoBlockEx -> uIndex);

    Status = USBSCAN_CallUSBD(pDeviceObject, pUrb);

USPassThruUSBRequest_return:

    //
    // Clean up.
    //

    if(NULL != pUrb){
        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Free USB Request Block.\n"));
            USFreePool(pUrb);
        }

    if( (!fDirectionIn)
     && (NULL != pBuffer) )
    {
        DebugTrace(TRACE_STATUS,("USPassThruUSBRequest: Free temp buffer.\n"));
            USFreePool(pBuffer);
        }

    DebugTrace(TRACE_PROC_LEAVE,("USPassThruUSBRequest: Leaving.. Status = 0x%x\n", Status));
    return Status;

} // USPassThruUSBRequestPTP()