text: /*++                                                   
  
  Copyright (c) 1997-1998  Microsoft Corporation
  
  Module Name:
  
      USBPRINT.c
  
  Abstract:
  
      Device driver for USB printers
  
  Environment:
  
      kernel mode only
  
  Notes:
  
    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) 1996 Microsoft Corporation.  All Rights Reserved.
  
  
  Revision History:
  
      5-4-96 : created
  
  --*/
  
  #define DRIVER
  //Windows includes
  #include "wdm.h"
  #include "ntddpar.h"
  #include "initguid.h"
  #include "wdmguid.h"
  
  
  
  NTSTATUS
  USBPRINT_SystemControl(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      );
  
  #ifdef ALLOC_PRAGMA
  #pragma alloc_text(PAGE, USBPRINT_SystemControl)
  #endif
  
  
  #include "stdarg.h"
  #include "stdio.h"
  
  //USB includes
  #include <usb.h>
  #include <usbdrivr.h>
  #include "usbdlib.h"
  
  //My includes
  #include "usbprint.h"
  #include "deviceid.h"
  
  //
  // Global pointer to Driver Object
  //
  
  PDRIVER_OBJECT USBPRINT_DriverObject;
  
  int iGMessageLevel;
  PFREE_PORTS pGPortList;
  HANDLE GLogHandle;
  
  
  
  NTSTATUS QueryDeviceRelations(PDEVICE_OBJECT DeviceObject,PIRP Irp,DEVICE_RELATION_TYPE,BOOL *pbComplete);
  NTSTATUS GetPortNumber(HANDLE hInterfaceKey,ULONG *ulPortNumber);
  NTSTATUS ProduceQueriedID(PDEVICE_EXTENSION deviceExtension,PIO_STACK_LOCATION irpStack,PIRP Irp,PDEVICE_OBJECT DeviceObject);
  int iGetMessageLevel();
  NTSTATUS USBPRINT_ProcessChildPowerIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
  NTSTATUS USBPRINT_ProcessFdoPowerIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
  
  NTSTATUS InitFreePorts( PFREE_PORTS * pHead );
  NTSTATUS bAddPortInUseItem(PFREE_PORTS * pFreePorts,ULONG iPortNumber );
  NTSTATUS LoadPortsUsed(GUID *pPrinterGuid,PFREE_PORTS * pPortList,WCHAR *wcBaseName);
  void ClearFreePorts(PFREE_PORTS * pHead);
  NTSTATUS LoadPortsUsed(GUID *pPrinterGuid,PFREE_PORTS * pPortList,WCHAR *wcBaseName);
  void vClaimPortNumber(ULONG ulPortNumber,HANDLE hInterfaceKey,PFREE_PORTS * pPortsUsed);
  NTSTATUS GetNewPortNumber(PFREE_PORTS * pFreePorts, ULONG *pulPortNumber);
  BOOL bDeleteIfRecyclable(HANDLE hInterfaceKey);
  NTSTATUS SetValueToZero(HANDLE hRegKey,PUNICODE_STRING ValueName);
  USBPRINT_GetDeviceID(PDEVICE_OBJECT ParentDeviceObject);
  void WritePortDescription(PDEVICE_EXTENSION deviceExtension);
  void vOpenLogFile(IN HANDLE *pHandle);
  void vWriteToLogFile(IN HANDLE *pHandle,IN CHAR *pszString);
  void vCloseLogFile(IN HANDLE *pHandle);
  
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  ZwDeleteValueKey(
      IN HANDLE KeyHandle,
      IN PUNICODE_STRING ValueName
      );
  
  
  
  
  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 ntStatus = STATUS_SUCCESS;
      PDEVICE_OBJECT deviceObject = NULL;
      
  
      
  
  
  
      USBPRINT_DriverObject = DriverObject;
  
      //
      // Create dispatch points for device control, create, close.
      //
  
      DriverObject->MajorFunction[IRP_MJ_CREATE] = USBPRINT_Create;
      DriverObject->MajorFunction[IRP_MJ_CLOSE] = USBPRINT_Close;
      DriverObject->DriverUnload = USBPRINT_Unload;
  
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = USBPRINT_ProcessIOCTL;
      DriverObject->MajorFunction[IRP_MJ_WRITE] = USBPRINT_Write;
      DriverObject->MajorFunction[IRP_MJ_READ] = USBPRINT_Read;
  
      DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = USBPRINT_SystemControl;
      DriverObject->MajorFunction[IRP_MJ_PNP] = USBPRINT_Dispatch;
      DriverObject->MajorFunction[IRP_MJ_POWER] = USBPRINT_ProcessPowerIrp;
      DriverObject->DriverExtension->AddDevice = USBPRINT_PnPAddDevice;
  
      iGMessageLevel=iGetMessageLevel();
      USBPRINT_KdPrint2 (("USBPRINT.SYS: entering (USBPRINT) DriverEntry\n")); 
      USBPRINT_KdPrint2 (("USBPRINT.SYS: MessageLevel=%d\n",iGMessageLevel));
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: About to load ports\n"));
      pGPortList = NULL;
      ntStatus=InitFreePorts(&pGPortList);
      if(NT_SUCCESS(ntStatus) && pGPortList!=NULL)
      {
          ntStatus=LoadPortsUsed((GUID *)&USBPRINT_GUID,&pGPortList,USB_BASE_NAME);
          if(!NT_SUCCESS(ntStatus))
          {
            USBPRINT_KdPrint1 (("USBPRINT.SYS: DriverInit: Unable to load used ports; error=%u\n", ntStatus));
          }
      }
      else
      {
          USBPRINT_KdPrint1 (("USBPRINT.SYS: exiting (USBPRINT) DriverEntry (%x)\n", ntStatus));
          if(NT_SUCCESS(ntStatus))
          {
              ntStatus=STATUS_INSUFFICIENT_RESOURCES;
          }
      }
  
      
      
  
  
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exiting (USBPRINT) DriverEntry (%x)\n", ntStatus));
  
      if ( !NT_SUCCESS(ntStatus))
          ClearFreePorts(&pGPortList);
  
      return ntStatus;
  }
  
  /*********************************************
   * Message Levels:
   * 0 == None, except critical, about to crash the machine failures
   * 1 == Error messages only
   * 2 == Informative messages
   * 3 == Verbose informative messages
   ******************************************************/
  int iGetMessageLevel()
  {
    OBJECT_ATTRIBUTES rObjectAttribs;
    HANDLE hRegHandle;
    UNICODE_STRING KeyName;
    UNICODE_STRING ValueName;
    ULONG ulSizeUsed;
    PKEY_VALUE_PARTIAL_INFORMATION pValueStruct;
    NTSTATUS ntStatus;
    int iReturn;
  
   
  
    
    RtlInitUnicodeString(&KeyName,L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\USBPRINT");
    RtlInitUnicodeString(&ValueName,L"DriverMessageLevel");
    InitializeObjectAttributes(&rObjectAttribs,&KeyName,OBJ_CASE_INSENSITIVE,NULL,NULL);
    ntStatus=ZwOpenKey(&hRegHandle,KEY_QUERY_VALUE,&rObjectAttribs); 
    if(NT_SUCCESS(ntStatus))
    {
      ulSizeUsed=sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG); //this is a byte to much.  Oh well
      pValueStruct=ExAllocatePoolWithTag(NonPagedPool,ulSizeUsed, USBP_TAG); 
      if(pValueStruct==NULL)
      {
        USBPRINT_KdPrint0(("'USBPRINT.SYS: iGetMessageLevel; Unable to allocate memory\n"));
        ZwClose(hRegHandle);
        return 1;
      }
      ntStatus=ZwQueryValueKey(hRegHandle,&ValueName,KeyValuePartialInformation,pValueStruct,ulSizeUsed,&ulSizeUsed);
      if(!NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint3(("Failed to Query value Key\n"));
        iReturn=1;
      }
      else
      {
        iReturn=(int)*((ULONG *)(pValueStruct->Data));
      }
      ExFreePool(pValueStruct);
      ZwClose(hRegHandle);
    }
    else
    {
       iReturn=1;
    }
    return iReturn;
  } /*end iGetMessageLevel*/
  
  
  NTSTATUS
  USBPRINT_PoRequestCompletion(
      IN PDEVICE_OBJECT       DeviceObject,
      IN UCHAR                MinorFunction,
      IN POWER_STATE          PowerState,
      IN PVOID                Context,
      IN PIO_STATUS_BLOCK     IoStatus
      )
  /*++
  
  Routine Description:
  
      This routine is called when the port driver completes an IRP.
  
  Arguments:
  
      DeviceObject - Pointer to the device object for the class device.
  
      Context - Driver defined context.
  
  Return Value:
  
      The function value is the final status from the operation.
  
  --*/
  {
      PIRP irp;
      PDEVICE_EXTENSION deviceExtension;
      PDEVICE_OBJECT deviceObject = Context;
      NTSTATUS ntStatus;
  
      deviceExtension = deviceObject->DeviceExtension;
      irp = deviceExtension->PowerIrp;
      
      USBPRINT_KdPrint2(("USBPRINT_PoRequestCompletion\n"));
      
      PoStartNextPowerIrp(irp);
      IoCopyCurrentIrpStackLocationToNext(irp);      
      ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
           irp);   
  
      USBPRINT_DecrementIoCount(deviceObject);                 
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_PowerIrp_Complete(
      IN PDEVICE_OBJECT NullDeviceObject,
      IN PIRP Irp,
      IN PVOID Context
      )
  /*++
  
  Routine Description:
  
      This routine is called when the port driver completes an IRP.
  
  
  Arguments:
  
      DeviceObject - Pointer to the device object for the class device.
  
      Irp - Irp completed.
  
      Context - Driver defined context.
  
  Return Value:
  
      The function value is the final status from the operation.
  
  --*/
  {
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PDEVICE_OBJECT deviceObject;
      PIO_STACK_LOCATION irpStack;
      PDEVICE_EXTENSION deviceExtension;
  
      USBPRINT_KdPrint2(("USBPRINT.SYS:   enter USBPRINT_PowerIrp_Complete\n"));
  
      deviceObject = (PDEVICE_OBJECT) Context;
  
      deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
  
  
      if (Irp->PendingReturned) {
      IoMarkIrpPending(Irp);
      }
  
      irpStack = IoGetCurrentIrpStackLocation (Irp);
  
      ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
      ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
      ASSERT(irpStack->Parameters.Power.Type==DevicePowerState);
      ASSERT(irpStack->Parameters.Power.State.DeviceState==PowerDeviceD0);
  
      deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
      deviceExtension->bD0IrpPending=FALSE;
  
      
      
   //   if (deviceExtension->Interface) 
   //     ExFreePool(deviceExtension->Interface);
   //   ntStatus=USBPRINT_ConfigureDevice(deviceObject);
   //   ntStatus = USBPRINT_BuildPipeList(deviceObject);
   //   if(!NT_SUCCESS(ntStatus))
   //       USBPRINT_KdPrint1(("USBPRINT.SYS :  Unable to reconfigure device after wakeup.  Error %x\n",ntStatus));
  
      Irp->IoStatus.Status = ntStatus;
  
      USBPRINT_DecrementIoCount(deviceObject); 
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_SetDevicePowerState(
      IN PDEVICE_OBJECT DeviceObject,
      IN DEVICE_POWER_STATE DeviceState,
      IN PBOOLEAN HookIt
      )
  /*++
  
  Routine Description:
  
  Arguments:
  
      DeviceObject - Pointer to the device object for the class device.
  
      Irp - Irp completed.
  
      DeviceState - Device specific power state to set the device in to.
  
  Return Value:
  
  
  --*/
  {
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PDEVICE_EXTENSION deviceExtension;
  
      deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  
      switch (DeviceState) {
      case PowerDeviceD3:
  
      //
      // device will be going OFF, save any state now.
      //
  
      USBPRINT_KdPrint2(("USBPRINT.SYS:  PowerDeviceD3 (OFF)*******************************/*dd\n"));
  
      deviceExtension->CurrentDevicePowerState = DeviceState;
      break;
  
      case PowerDeviceD1:
      case PowerDeviceD2:
      //
      // power states D1,D2 translate to USB suspend
  
      USBPRINT_KdPrint2(("USBPRINT.SYS:  PowerDeviceD1/D2 (SUSPEND)*******************************/*dd\n"));        
  
      deviceExtension->CurrentDevicePowerState = DeviceState;
      break;
  
      case PowerDeviceD0:
  
  
      USBPRINT_KdPrint2(("USBPRINT.SYS:  PowerDeviceD0 (ON)*******************************/*dd\n"));
  
      //
      // finish the rest in the completion routine
      //
  
      *HookIt = TRUE;
  
      // pass on to PDO
      break;
  
      default:
      
      USBPRINT_KdPrint1(("USBPRINT.SYS:  Bogus DeviceState = %x\n", DeviceState));
      }
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_DeferIrpCompletion(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP Irp,
      IN PVOID Context
      )
  /*++
  
  Routine Description:
  
      This routine is called when the port driver completes an IRP.
  
  
  Arguments:
  
      DeviceObject - Pointer to the device object for the class device.
  
      Irp - Irp completed.
  
      Context - Driver defined context.
  
  Return Value:
  
      The function value is the final status from the operation.
  
  --*/
  {
      PKEVENT event = Context;
  
  
      KeSetEvent(event,1,FALSE);
  
      return STATUS_MORE_PROCESSING_REQUIRED;
      
  }
  
  
  NTSTATUS
  USBPRINT_QueryCapabilities(
      IN PDEVICE_OBJECT PdoDeviceObject,
      IN PDEVICE_CAPABILITIES DeviceCapabilities
      )
  
  /*++
  
  Routine Description:
  
      This routine reads or write config space.
  
  Arguments:
  
      DeviceObject        - Physical DeviceObject for this USB controller.
  
  Return Value:
  
      None.
  
  --*/
  
  {
      PIO_STACK_LOCATION nextStack;
      PIRP irp;
      NTSTATUS ntStatus;
      KEVENT event;
  
      PAGED_CODE();
      irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);
  
      if (!irp) {
      return STATUS_INSUFFICIENT_RESOURCES;
      }
  
      irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  
      nextStack = IoGetNextIrpStackLocation(irp);
      ASSERT(nextStack != NULL);
      nextStack->MajorFunction= IRP_MJ_PNP;
      nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;
  
      KeInitializeEvent(&event, NotificationEvent, FALSE);
  
      IoSetCompletionRoutine(irp,
                 USBPRINT_DeferIrpCompletion,
                 &event,
                 TRUE,
                 TRUE,
                 TRUE);
                 
      //this is different from the latest version of busdd.doc
      nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
  
      ntStatus = IoCallDriver(PdoDeviceObject,
                  irp);
  
      USBPRINT_KdPrint3(("USBPRINT.SYS:  ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));
  
      if (ntStatus == STATUS_PENDING) {
         // wait for irp to complete
         
         
         
         KeWaitForSingleObject(
          &event,
          Suspended,
          KernelMode,
          FALSE,
          NULL);
      }
  
  #if DBG                    
      if (!NT_SUCCESS(ntStatus)) {
      // failed? this is probably a bug
      USBPRINT_KdPrint1(("USBPRINT.SYS:  QueryCapabilities failed, why?\n"));
      }
  #endif
  
      IoFreeIrp(irp);
  
      return STATUS_SUCCESS;
  }
  
  
  
  
  NTSTATUS
  USBPRINT_ProcessPowerIrp(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      )
  {
    PDEVICE_EXTENSION deviceExtension;
    BOOLEAN hookIt = FALSE;
      NTSTATUS ntStatus;
  
    USBPRINT_KdPrint2(("USBPRINT.SYS:  /*****************************************************************IRP_MJ_POWER\n"));
  
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
      if(deviceExtension->IsChildDevice)
          ntStatus=USBPRINT_ProcessChildPowerIrp(DeviceObject,Irp);
    else
          ntStatus=USBPRINT_ProcessFdoPowerIrp(DeviceObject,Irp);
      USBPRINT_KdPrint3(("USBPRINT.SYS:  /*****************************************************************Leaving power IRP_MJ_POWER\n"));
      return ntStatus;    
  
  }  /*end function USBPRINT_ProcessPowerIrp*/
  
  
  NTSTATUS
  USBPRINT_ProcessChildPowerIrp(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      )
  {
     PIO_STACK_LOCATION irpStack;
     NTSTATUS ntStatus;
     PCHILD_DEVICE_EXTENSION pDeviceExtension;
   
  
       USBPRINT_KdPrint2(("USBPRINT.SYS: IRP_MJ_POWER for child PDO\n"));
  
       pDeviceExtension=(PCHILD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
       irpStack=IoGetCurrentIrpStackLocation(Irp);
  
       switch(irpStack->MinorFunction)
       {
           case IRP_MN_SET_POWER:
             USBPRINT_KdPrint3(("USBPRINT.SYS: IRP_MJ_POWER, IRP_MN_SET_POWER\n"));
               ntStatus=STATUS_SUCCESS;
           break;
  
  
           case IRP_MN_QUERY_POWER:
               USBPRINT_KdPrint3(("USBPRINT.SYS: IRP_MJ_POWER, IRP_MN_QUERY_POWER\n"));
               ntStatus=STATUS_SUCCESS;
           break;
  
           default:
              ntStatus = Irp->IoStatus.Status;
       } /*end switch irpStack->MinorFunction*/
  
     PoStartNextPowerIrp(Irp);
     Irp->IoStatus.Status=ntStatus;
       IoCompleteRequest(Irp,IO_NO_INCREMENT);
  
       return ntStatus;
  
  } /*end function USBPRINT_ProcessChildPowerIrp*/
  
  
  
  NTSTATUS
  USBPRINT_ProcessFdoPowerIrp(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      )
  /*++
  
  Routine Description:
  
      Process the Power IRPs sent to the PDO for this device.
  
      
  
  Arguments:
  
      DeviceObject - pointer to a hcd device object (FDO)
  
      Irp          - pointer to an I/O Request Packet
  
  Return Value:
  
      NT status code
  
  --*/
  {
  
      PIO_STACK_LOCATION irpStack;
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PDEVICE_EXTENSION deviceExtension;
      BOOLEAN hookIt = FALSE;
  
      deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
      if(deviceExtension->IsChildDevice)
      {
          USBPRINT_KdPrint1(("USBPRINT.SYS  Is child device inside fdo function.  Error!*/\n"));
      }
      irpStack = IoGetCurrentIrpStackLocation (Irp);
      USBPRINT_IncrementIoCount(DeviceObject);
  
      switch(irpStack->MinorFunction)
      {
      
      case IRP_MN_SET_POWER:
          {
  
              switch(irpStack->Parameters.Power.Type)
              {
              case SystemPowerState:
                  //
                  // find the device power state equivalent to the given system state
                  //
  
                  {
                      POWER_STATE powerState;
  
                      USBPRINT_KdPrint3(("USBPRINT.SYS:  Set Power, SystemPowerState (%d)\n", 
                                         irpStack->Parameters.Power.State.SystemState));                    
  
                      powerState.DeviceState = deviceExtension->DeviceCapabilities.DeviceState[irpStack->Parameters.Power.State.SystemState];
  
                      //
                      // are we already in this state?
                      //
  
                      if(powerState.DeviceState != deviceExtension->CurrentDevicePowerState)
                      {
  
                          // No,
                          // request that we be put into this state
                          //Don't touch the Irp any more after this.  It could complete at any time
                          deviceExtension->PowerIrp = Irp;
                          IoMarkIrpPending(Irp); 
                          ntStatus = PoRequestPowerIrp(deviceExtension->PhysicalDeviceObject,
                                                       IRP_MN_SET_POWER,
                                                       powerState,
                                                       USBPRINT_PoRequestCompletion,
                                                       DeviceObject,
                                                       NULL);
                          hookIt = TRUE;
  
                      }
                      else
                      {
                          // Yes,
                          // just pass it on
                          PoStartNextPowerIrp(Irp);
                          IoCopyCurrentIrpStackLocationToNext(Irp);
                          ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                                  Irp);
  
                      }
  
                  } 
                  break;
  
              case DevicePowerState:
  
                  ntStatus = USBPRINT_SetDevicePowerState(DeviceObject,
                                                          irpStack->Parameters.Power.State.DeviceState,
                                                          &hookIt);
  
                  PoStartNextPowerIrp(Irp);
                  IoCopyCurrentIrpStackLocationToNext(Irp);
  
                  if(hookIt)
                  {
                      USBPRINT_KdPrint2(("USBPRINT.SYS:  Set PowerIrp Completion Routine\n"));
                      IoSetCompletionRoutine(Irp,USBPRINT_PowerIrp_Complete,DeviceObject,TRUE,TRUE,TRUE);
                  }
                  ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,Irp);
                  break;
              } /* switch irpStack->Parameters.Power.Type */
  
          } 
          break; /* IRP_MN_SET_POWER */
  
      default:
  
          USBPRINT_KdPrint1(("USBPRINT.SYS:  UNKNOWN POWER MESSAGE (%x)\n", irpStack->MinorFunction));
  
          //
          // All unahndled PnP messages are passed on to the PDO
          //
  
          PoStartNextPowerIrp(Irp);
          IoCopyCurrentIrpStackLocationToNext(Irp);
          ntStatus = PoCallDriver(deviceExtension->TopOfStackDeviceObject,
                                  Irp);
  
      } /* irpStack->MinorFunction */
  
      if( !hookIt )
          USBPRINT_DecrementIoCount(DeviceObject);
      return ntStatus;
  } /*end function ProcessFdoPowerIrp*/
  
  
  NTSTATUS
  USBPRINT_Dispatch(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      )
  /*++
  
  Routine Description:
  
      Process the IRPs sent to this device.
  
  Arguments:
  
      DeviceObject - pointer to a device object
                                           
      Irp          - pointer to an I/O Request Packet
  
  Return Value:
  
  
  --*/
  {
  
      PIO_STACK_LOCATION irpStack;
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PDEVICE_OBJECT stackDeviceObject;
      BOOL bHandled=FALSE;
  
      //Irp->IoStatus.Status = STATUS_SUCCESS;
      //Irp->IoStatus.Information = 0;
  
      //
      // Get a pointer to the current location in the Irp. This is where
      //     the function codes and parameters are located.
      //
  
      irpStack = IoGetCurrentIrpStackLocation (Irp);
  
      //
      // Get a pointer to the device extension
      //
  
      deviceExtension = DeviceObject->DeviceExtension;
      stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
  
  #ifdef  MYDEBUG
      DbgPrint("USBPRINT_Dispatch entry for pnp event %d\n", irpStack->MinorFunction);
      ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  #endif
      
      USBPRINT_IncrementIoCount(DeviceObject);
  
          switch (irpStack->MinorFunction) 
          {
            case IRP_MN_START_DEVICE:
            {
              if(deviceExtension->IsChildDevice==FALSE)
              {
                KEVENT event;
                USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_START_DEVICE\n"));
                KeInitializeEvent(&event, NotificationEvent, FALSE);
                IoCopyCurrentIrpStackLocationToNext(Irp);  
                IoSetCompletionRoutine(Irp,USBPRINT_DeferIrpCompletion,&event,TRUE,TRUE,TRUE);
                ntStatus = IoCallDriver(stackDeviceObject,Irp);
                if (ntStatus == STATUS_PENDING) 
                {
                  KeWaitForSingleObject(&event,Suspended,KernelMode,FALSE,NULL);
                  ntStatus = Irp->IoStatus.Status;
                }
                if ( NT_SUCCESS(ntStatus) ) {
  
                  //
                  // You start the device after everyone below you have started it
                  //
                  Irp->IoStatus.Status = ntStatus = USBPRINT_StartDevice(DeviceObject);
                }
              } /*end if not child*/
              else
              {
                  ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS;
              }
  
              bHandled = TRUE;
              IoCompleteRequest(Irp, IO_NO_INCREMENT);
              USBPRINT_DecrementIoCount(DeviceObject);
              
            } //end case IRP_MN_START_DEVICE
            break;
  
            case IRP_MN_STOP_DEVICE:
              if(deviceExtension->IsChildDevice)
              {
                  Irp->IoStatus.Status = STATUS_SUCCESS;
                  ntStatus=STATUS_SUCCESS;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              }
              else
              {
                USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_STOP_DEVICE\n")); 
                
                //
                // You stop the device first and then let everyone below you deal with it
                //
                ntStatus = USBPRINT_StopDevice(DeviceObject);
                ASSERT(NT_SUCCESS(ntStatus));
  
                //
                // We want to stop the device anyway ..
                //
                Irp->IoStatus.Status = STATUS_SUCCESS;
                IoSkipCurrentIrpStackLocation(Irp);
                ntStatus = IoCallDriver(stackDeviceObject,Irp);
              }
              bHandled = TRUE;
              USBPRINT_DecrementIoCount(DeviceObject);
            break;
  
            case IRP_MN_SURPRISE_REMOVAL:
              if(deviceExtension->IsChildDevice)
              {
                  Irp->IoStatus.Status = STATUS_SUCCESS;
                  ntStatus=STATUS_SUCCESS;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              }
              else
              {
                  USBPRINT_KdPrint2(("USBPRINT.SYS:   Surprise Remove")); 
                  
                  ntStatus = USBPRINT_StopDevice(DeviceObject);
                  ASSERT(NT_SUCCESS(ntStatus));
                  Irp->IoStatus.Status=STATUS_SUCCESS;  
  
                  deviceExtension->AcceptingRequests=FALSE;
                  IoSkipCurrentIrpStackLocation(Irp);
                  ntStatus = IoCallDriver(stackDeviceObject,Irp);
              } /*end else NOT child device*/
  
              bHandled = TRUE;
              USBPRINT_DecrementIoCount(DeviceObject);
            break;
  
            case IRP_MN_REMOVE_DEVICE:
               
              if(deviceExtension->IsChildDevice==FALSE)
              {
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_REMOVE_DEVICE\n")); 
                  
                  // match the inc at the begining of the dispatch routine
                  USBPRINT_DecrementIoCount(DeviceObject);
  
                  ntStatus = USBPRINT_StopDevice(DeviceObject);
                  ASSERT(NT_SUCCESS(ntStatus));
                  Irp->IoStatus.Status=STATUS_SUCCESS;  
  
                  //
                  // ounce this flag is set no irps will be pased 
                  // down the stack to lower drivers
                  //
                  deviceExtension->AcceptingRequests = FALSE;
                  if(deviceExtension->bChildDeviceHere)
                  {
                    deviceExtension->bChildDeviceHere=FALSE;
                    IoDeleteDevice(deviceExtension->ChildDevice);
                    USBPRINT_KdPrint3(("USBPRINT.SYS: Deleted child device\n"));
                  }
                if (NT_SUCCESS(ntStatus)) 
                {
                  LONG pendingIoCount;
                  USBPRINT_KdPrint3(("USBPRINT.SYS: About to copy current IrpStackLocation\n"));
                  IoCopyCurrentIrpStackLocationToNext(Irp);  
                  ntStatus = IoCallDriver(stackDeviceObject,Irp);
                  
  
      //          Irp->IoStatus.Information = 0;
                  //
                  // final decrement will trigger the remove
                  //
                  pendingIoCount = USBPRINT_DecrementIoCount(DeviceObject);
  
                  {
                    NTSTATUS status;
  
                    // wait for any io request pending in our driver to
                    // complete for finishing the remove
                    status = KeWaitForSingleObject(&deviceExtension->RemoveEvent,Suspended,KernelMode,FALSE,NULL);
  //                    TRAP();
                  } /*end of non-controled code block*/
                  //
                  // Delete the link and FDO we created
                  //
                  USBPRINT_RemoveDevice(DeviceObject);
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Detaching from %08X\n",deviceExtension->TopOfStackDeviceObject));
                  IoDetachDevice(deviceExtension->TopOfStackDeviceObject);
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Deleting %08X\n",DeviceObject));
  
                  IoDeleteDevice (DeviceObject);
                  } /*end if NT_SUCCESS(ntStatus)*/
              } /*end if IsChildDevice==FALSE*/
              else
              {
                  USBPRINT_DecrementIoCount(DeviceObject);
                  Irp->IoStatus.Status = STATUS_SUCCESS;
                  ntStatus=STATUS_SUCCESS;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              }
              bHandled = TRUE;
            break; //case IRP_MN_REMOVE_DEVICE
  
            case IRP_MN_QUERY_CAPABILITIES:
            {
              if(deviceExtension->IsChildDevice==FALSE) //if it's the parent, pass down the irp, and set SurpriseRemovalOK on the way back up
              {
                KEVENT event;
                KeInitializeEvent(&event, NotificationEvent, FALSE);
                IoCopyCurrentIrpStackLocationToNext(Irp);  
                IoSetCompletionRoutine(Irp,USBPRINT_DeferIrpCompletion,&event,TRUE,TRUE,TRUE);
                ntStatus = IoCallDriver(stackDeviceObject,Irp);
                if (ntStatus == STATUS_PENDING) 
                {
                  KeWaitForSingleObject(&event,Suspended,KernelMode,FALSE,NULL);
                  ntStatus = Irp->IoStatus.Status;
                }
  
                if ( NT_SUCCESS(ntStatus) )
                  irpStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
  
                  // get device wake for selective suspend
                  deviceExtension->DeviceWake = irpStack->Parameters.DeviceCapabilities.Capabilities->DeviceWake;
              }
              else
              {
                 irpStack->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = TRUE;
                 irpStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
   
                  Irp->IoStatus.Status = STATUS_SUCCESS;
  
                ntStatus=STATUS_SUCCESS;
              }
              IoCompleteRequest(Irp, IO_NO_INCREMENT);
            
              bHandled = TRUE;
              USBPRINT_DecrementIoCount(DeviceObject);
            }
            break;
  
            case IRP_MN_QUERY_DEVICE_TEXT:
              USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_QUERY_DEVICE_TEXT\n"));
              if(deviceExtension->IsChildDevice==TRUE)
              {
                  PCHILD_DEVICE_EXTENSION pChildDeviceExtension=(PCHILD_DEVICE_EXTENSION)deviceExtension;
                  PDEVICE_EXTENSION pParentExtension=pChildDeviceExtension->ParentDeviceObject->DeviceExtension;
                  USBPRINT_KdPrint2(("USBPRINT.SYS: Is child PDO, will complete locally\n"));
                  switch(irpStack->Parameters.QueryDeviceText.DeviceTextType)
                  {
                    case DeviceTextDescription:
                    {
                       ANSI_STRING     AnsiTextString;
                       UNICODE_STRING  UnicodeDeviceText;
  
                       RtlInitAnsiString(&AnsiTextString,pParentExtension->DeviceIdString);
                       ntStatus=RtlAnsiStringToUnicodeString(&UnicodeDeviceText,&AnsiTextString,TRUE);
                       USBPRINT_KdPrint2(("USBPRINT.SYS: QUERY_DEVICE_TEXT, DeviceID=%s\n",pParentExtension->DeviceIdString));
                       if(NT_SUCCESS(ntStatus))
                           Irp->IoStatus.Information=(ULONG_PTR)UnicodeDeviceText.Buffer;
                    }
                    break;
                    default:
                      ntStatus=Irp->IoStatus.Status;
                  }
                  bHandled=TRUE;
                  USBPRINT_DecrementIoCount(DeviceObject);
                  Irp->IoStatus.Status = ntStatus;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              }
  
  
            break;
  
  
            case IRP_MN_QUERY_BUS_INFORMATION:
  
                if(deviceExtension->IsChildDevice==TRUE)
                {
                  PPNP_BUS_INFORMATION  pBusInfo = ExAllocatePool( PagedPool, sizeof(PNP_BUS_INFORMATION) );
      
                  USBPRINT_KdPrint2(("USBPRINT.SYS: IRP_MN_QUERY_BUS_INFORMATION\n"));
      
                  if( pBusInfo )
                  {
                      pBusInfo->BusTypeGuid      = GUID_BUS_TYPE_USBPRINT;
                      pBusInfo->LegacyBusType    = PNPBus;
                      pBusInfo->BusNumber        = 0;
                      ntStatus                   = STATUS_SUCCESS;
                      Irp->IoStatus.Information = (ULONG_PTR)pBusInfo;
                  }
                  else
                  {
                      ntStatus = STATUS_NO_MEMORY;
                  }
      
                  bHandled = TRUE;
                  USBPRINT_DecrementIoCount(DeviceObject);
                  Irp->IoStatus.Status = ntStatus;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
                }
            break;
  
  
  
  
  
  
            case IRP_MN_QUERY_ID:
            {  
              USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_QUERY_ID\n"));
              if(deviceExtension->IsChildDevice==TRUE)
              {
                  USBPRINT_KdPrint2(("USBPRINT.SYS: Is child PDO, will complete locally\n"));
                  ntStatus=ProduceQueriedID(deviceExtension,irpStack,Irp,DeviceObject);
                  bHandled = TRUE;
                  USBPRINT_DecrementIoCount(DeviceObject);
                  Irp->IoStatus.Status = ntStatus;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              } /*end if child PDO*/
            } /*end case QUERY_ID*/
            break;
  
            case IRP_MN_QUERY_DEVICE_RELATIONS:
              USBPRINT_KdPrint2 (("USBPRINT.SYS: IRP_MN_QUERY_DEVICE_RELATIONS\n"));
              ntStatus=QueryDeviceRelations(DeviceObject,Irp,irpStack->Parameters.QueryDeviceRelations.Type,&bHandled);
              if ( bHandled )
                  USBPRINT_DecrementIoCount(DeviceObject);
            break;
  
            case IRP_MN_QUERY_STOP_DEVICE:
            case IRP_MN_CANCEL_STOP_DEVICE:
            case IRP_MN_QUERY_REMOVE_DEVICE:
            case IRP_MN_CANCEL_REMOVE_DEVICE:
              if(deviceExtension->IsChildDevice)
              {
                  Irp->IoStatus.Status = STATUS_SUCCESS;
                  ntStatus=STATUS_SUCCESS;
                  IoCompleteRequest(Irp, IO_NO_INCREMENT);
              }
              else
              {
                  ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS;
                  IoSkipCurrentIrpStackLocation(Irp);
                  ntStatus = IoCallDriver(stackDeviceObject,Irp);
              }
              USBPRINT_DecrementIoCount(DeviceObject);
              bHandled = TRUE;
              break;
            
    
          } /* end IRP_MN swich inside IRP_MJ_PNP case */
  
  
          if(!bHandled)
          {
            if(deviceExtension->IsChildDevice==TRUE)
            {
              USBPRINT_KdPrint3(("USBPRINT.SYS: unsupported child pnp IRP\n"));
              ntStatus = Irp->IoStatus.Status;
              IoCompleteRequest (Irp,IO_NO_INCREMENT);
            } /*end if child device*/
            else
            {
              IoSkipCurrentIrpStackLocation(Irp);
              ntStatus = IoCallDriver(stackDeviceObject,Irp);
            }
  
            USBPRINT_DecrementIoCount(DeviceObject);
          } /*end if !bHandled*/
  
  #ifdef  MYDEBUG
      DbgPrint("Returning %d\n", ntStatus);
  #endif
      return ntStatus;
  } /*end function USBPRINT_Dispatch*/
  
  
  NTSTATUS
  USBPRINT_SystemControl(
      IN PDEVICE_OBJECT DeviceObject,
      IN PIRP           Irp
      )
  /*++
  
  Routine Description:
  
      Process the IRPs sent to this device.
  
  Arguments:
  
      DeviceObject - pointer to a device object
  
      Irp          - pointer to an I/O Request Packet
  
  Return Value:
  
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus;
      PDEVICE_OBJECT stackDeviceObject;
  
      PAGED_CODE();
  
      //
      // Get a pointer to the device extension
      //
      deviceExtension = DeviceObject->DeviceExtension;
      stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
  
      USBPRINT_IncrementIoCount(DeviceObject);
  
      if(deviceExtension->IsChildDevice==TRUE)
      {
          USBPRINT_KdPrint3(("USBPRINT.SYS: unsupported child SystemControl IRP\n"));
          ntStatus = Irp->IoStatus.Status;
          IoCompleteRequest (Irp,IO_NO_INCREMENT);
      } /*end if child device*/
      else
      {
          IoSkipCurrentIrpStackLocation(Irp);
          ntStatus = IoCallDriver(stackDeviceObject,Irp);
      }
  
      USBPRINT_DecrementIoCount(DeviceObject);
      return ntStatus;
  }
  
  
  NTSTATUS QueryDeviceRelations(PDEVICE_OBJECT DeviceObject,PIRP Irp,DEVICE_RELATION_TYPE RelationType,BOOL *pbComplete)
  {
          PIO_STACK_LOCATION irpSp;
          NTSTATUS ntStatus;
          PDEVICE_EXTENSION pExtension;
          PDEVICE_RELATIONS pRelations;
          *pbComplete=FALSE;
  
  
  
          pExtension=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
          ntStatus=Irp->IoStatus.Status;
          irpSp=IoGetCurrentIrpStackLocation(Irp);
  
          if(!pExtension->IsChildDevice)
          {
              USBPRINT_KdPrint2 (("USBPRINT.SYS: Parent QueryDeviceRelations\n"));
                  if(RelationType==BusRelations)
                  {
                    *pbComplete=TRUE;
                    pRelations=(PDEVICE_RELATIONS)ExAllocatePoolWithTag(NonPagedPool,sizeof(DEVICE_RELATIONS), USBP_TAG);
                    if(pRelations!=NULL)
                    {
                            //Some drivers check for pre-existing children and preserve them.  This would happen if there is a filter driver above us, but we're not REALLY a bus driver
  
                            pRelations->Objects[0]=pExtension->ChildDevice;
                            pRelations->Count = 1;
                            ObReferenceObject(pExtension->ChildDevice);
                            Irp->IoStatus.Information=(ULONG_PTR)pRelations;
                Irp->IoStatus.Status = STATUS_SUCCESS;
  
                    IoCopyCurrentIrpStackLocationToNext(Irp);
                    ntStatus = IoCallDriver(pExtension->TopOfStackDeviceObject,Irp);
                    } /*end !NULL*/
                    else
                    {
                           ntStatus=STATUS_NO_MEMORY;
                           Irp->IoStatus.Status = ntStatus;
                           IoCompleteRequest(Irp, IO_NO_INCREMENT);
                    }
                  //Port info will be written to the registry in the IRP_MN_QUERY_ID case.  It can't be used before then anyway
                  } /*end if BusRelations*/
  
          } else {
  
              USBPRINT_KdPrint2 (("USBPRINT.SYS: Child QueryDeviceRelations\n"));
              if(RelationType==TargetDeviceRelation)
                  {
                    *pbComplete=TRUE;
                    pRelations=(PDEVICE_RELATIONS)ExAllocatePoolWithTag(NonPagedPool,sizeof(DEVICE_RELATIONS), USBP_TAG);
                    if(pRelations!=NULL)
                    {
                          pRelations->Count = 1;
                            pRelations->Objects[0]=DeviceObject;
                            ObReferenceObject(DeviceObject);
                            Irp->IoStatus.Information=(ULONG_PTR)pRelations;
                            ntStatus = STATUS_SUCCESS;
                            Irp->IoStatus.Status = ntStatus;
                            IoCompleteRequest(Irp, IO_NO_INCREMENT);
  
                    } /*end !NULL*/
                    else
                    {
                           ntStatus=STATUS_NO_MEMORY;
                           Irp->IoStatus.Status = ntStatus;
                           IoCompleteRequest(Irp, IO_NO_INCREMENT);
                    }
                  //Port info will be written to the registry in the IRP_MN_QUERY_ID case.  It can't be used before then anyway
                  } /*end if BusRelations*/
          }
      return ntStatus;
  }
  
  
  VOID
  USBPRINT_Unload(
      IN PDRIVER_OBJECT DriverObject
      )
  /*++    
  
  Routine Description:
  
      Free all the allocated resources, etc.
  
  Arguments:
  
      DriverObject - pointer to a driver object
  
  Return Value:
  
  
  --*/
  {
      USBPRINT_KdPrint2 (("USBPRINT.SYS:  enter USBPRINT_Unload\n"));
      
     if(pGPortList!=NULL)
      {
          ClearFreePorts(&pGPortList);
      }
   
  //  if(pPortsUsed!=NULL)
  //    ExFreePool(pPortsUsed);
  
  
  
      //
      // Free any global resources allocated
      // in DriverEntry
      //
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS:  exit USBPRINT_Unload\n"));
  }
  
  
  NTSTATUS
  USBPRINT_StartDevice(
      IN  PDEVICE_OBJECT DeviceObject
      
      )
  /*++
  
  Routine Description:
  
      Initializes a given instance of the device on the USB.
      All we do here is get the device descriptor and store it
  
  Arguments:
  
      DeviceObject - pointer to the device object for this instance of a  printer
                                            
  
  Return Value:
  
      NT status code
      
        --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus;
      UNICODE_STRING KeyName;
      PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
      PURB urb=NULL;
      ULONG siz;
      ULONG dwVidPid;
      PDEVICE_OBJECT NewDevice;
      LARGE_INTEGER   timeOut;
          
      PCHILD_DEVICE_EXTENSION pChildExtension;
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_StartDevice\n")); 
      
  
      
      deviceExtension = DeviceObject->DeviceExtension;
      
      
      ntStatus = USBPRINT_ConfigureDevice(DeviceObject);
      if(NT_SUCCESS(ntStatus))
      {
        urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USBP_TAG);
      }
      else
      {
          USBPRINT_KdPrint1(("USBPRINT.SYS:  USBPRINT_ConfigureDevice Failed\n"));   
          urb=NULL;
      }
      if (urb) 
      {
           siz = sizeof(USB_DEVICE_DESCRIPTOR);
          
          deviceDescriptor = ExAllocatePoolWithTag(NonPagedPool,siz, USBP_TAG); 
          
          if (deviceDescriptor) 
          {
              
              
              UsbBuildGetDescriptorRequest(urb,
                  (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                  USB_DEVICE_DESCRIPTOR_TYPE,
                  0,
                  0,
                  deviceDescriptor,
                  NULL,
                  siz,
                  NULL);
              
              timeOut.QuadPart = FAILURE_TIMEOUT;
              ntStatus = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
              
              
              if (NT_SUCCESS(ntStatus)) 
              {
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Device Descriptor = %x, len %x\n",
                      deviceDescriptor,
                      urb->UrbControlDescriptorRequest.TransferBufferLength));
                  
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: USBPRINT Device Descriptor:\n"));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: -------------------------\n"));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bLength %d\n", deviceDescriptor->bLength));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bcdUSB 0x%x\n", deviceDescriptor->bcdUSB));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: idVendor 0x%x\n", deviceDescriptor->idVendor));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: idProduct 0x%x\n", deviceDescriptor->idProduct));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bcdDevice 0x%x\n", deviceDescriptor->bcdDevice));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: iManufacturer 0x%x\n", deviceDescriptor->iManufacturer));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: iProduct 0x%x\n", deviceDescriptor->iProduct));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations));
                  
                  dwVidPid=deviceDescriptor->idVendor;
                  dwVidPid<<=16;
                  dwVidPid+=deviceDescriptor->idProduct;
                  
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Math OK\n"));
                  
              }
              else
              {
                USBPRINT_KdPrint1(("USBPRINT.SYS: Get Device Descriptor failed\n"));
                ntStatus=STATUS_DEVICE_CONFIGURATION_ERROR;
              }
          } 
          else 
          {
              ntStatus = STATUS_INSUFFICIENT_RESOURCES;
              USBPRINT_KdPrint1(("USBPRINT.SYS: Insufficient resources to allocate device descriptor in StartDevice\n"));
          }
          
          if (NT_SUCCESS(ntStatus)) 
          {
              deviceExtension->DeviceDescriptor = deviceDescriptor;
          } else if (deviceDescriptor) 
          {
              ExFreePool(deviceDescriptor);
          }
          
          ExFreePool(urb);
          
      }
      else 
      {
          ntStatus = STATUS_INSUFFICIENT_RESOURCES;
          USBPRINT_KdPrint1(("USBPRINT.SYS: Insufficient resources to allocate urb in StartDevice\n"));
   
      }
      
  
      if(deviceExtension->bChildDeviceHere==FALSE)
      {
        if(NT_SUCCESS(ntStatus))
        {
           ntStatus=IoCreateDevice(USBPRINT_DriverObject,
           sizeof(CHILD_DEVICE_EXTENSION),
           NULL,
           FILE_DEVICE_PARALLEL_PORT,
           FILE_AUTOGENERATED_DEVICE_NAME,
           TRUE,
           &NewDevice);    
        
        }
        if(NT_SUCCESS(ntStatus))
        {
     
           USBPRINT_KdPrint3(("USBPRINT.SYS:  IoCreateDevice succeeded for child device\n"));
           NewDevice->Flags|=DO_POWER_PAGABLE;
           pChildExtension=NewDevice->DeviceExtension;
           pChildExtension->ParentDeviceObject=DeviceObject;
           deviceExtension->ChildDevice=NewDevice;
           deviceExtension->bChildDeviceHere=TRUE;
           pChildExtension->IsChildDevice=TRUE;
           pChildExtension->ulInstanceNumber=deviceExtension->ulInstanceNumber;
  
        
        }
      
        else
        {
             USBPRINT_KdPrint1(("USBPRINT.SYS:  IoCreateDevice failed for child device\n"));
        }
      } /*end if we need to make a child device*/
      if(NT_SUCCESS(ntStatus))
      {
     
          USBPRINT_GetDeviceID(DeviceObject);
          WritePortDescription(deviceExtension);
          ntStatus=IoSetDeviceInterfaceState(&(deviceExtension->DeviceLinkName),TRUE);
  
  
      }
  
      if (NT_SUCCESS(ntStatus)) 
      {
          ntStatus = USBPRINT_BuildPipeList(DeviceObject);
          if(!deviceExtension->IsChildDevice)
          {
              USBPRINT_FdoSubmitIdleRequestIrp(deviceExtension);
          }
      }
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_StartDevice (%x)\n", ntStatus)); 
      
      return ntStatus;
  }
  
  void WritePortDescription(PDEVICE_EXTENSION deviceExtension)
  {
      UNICODE_STRING ValueName;
      ANSI_STRING     AnsiTextString;
      UNICODE_STRING Description;
      UNICODE_STRING BaseName,BaseValueName;
   
  
  
  
      RtlInitUnicodeString(&ValueName,L"Port Description");
      
      RtlInitAnsiString(&AnsiTextString,deviceExtension->DeviceIdString);
      RtlAnsiStringToUnicodeString(&Description,&AnsiTextString,TRUE);
  
                                                                                                             
      ZwSetValueKey(deviceExtension->hInterfaceKey,&ValueName,0,REG_SZ,Description.Buffer,Description.Length+2);
      RtlFreeUnicodeString(&Description);
  
  
      RtlInitUnicodeString(&BaseName,L"USB");
      RtlInitUnicodeString(&BaseValueName,L"Base Name");
      ZwSetValueKey(deviceExtension->hInterfaceKey,&BaseValueName,0,REG_SZ,BaseName.Buffer,BaseName.Length+2);
  }
  
  
  NTSTATUS
  USBPRINT_RemoveDevice(
      IN  PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
      Stops a given instance of printer
  
  Arguments:
  
      DeviceObject - pointer to the device object for this instance of the (parent) printer object
  
  Return Value:
  
      NT status code
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus = STATUS_SUCCESS;
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_RemoveDevice\n"));
      
  
      deviceExtension = DeviceObject->DeviceExtension;
  
  
      ZwClose(deviceExtension->hInterfaceKey);
      USBPRINT_KdPrint2(("USBPRINT.SYS:  Closeing interface key in RemoveDevice\n"));  
  
      ntStatus=IoSetDeviceInterfaceState(&(deviceExtension->DeviceLinkName),FALSE);
      if(!NT_SUCCESS(ntStatus))
      {
          USBPRINT_KdPrint1 (("USBPRINT.SYS: ioSetDeviceInterface to false failed\n"));
      }
  
      RtlFreeUnicodeString(&(deviceExtension->DeviceLinkName));
  
      
  
      //
      // Free device descriptor structure
      //
  
      if (deviceExtension->DeviceDescriptor) {
      ExFreePool(deviceExtension->DeviceDescriptor);
      }
  
      //
      // Free up any interface structures
      //
  
      if (deviceExtension->Interface) {
      ExFreePool(deviceExtension->Interface);
      }
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_RemoveDevice (%x)\n", ntStatus));
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_StopDevice(
      IN  PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
      Stops a given instance of a printer  on the USB, this is only
      stuff we need to do if the device is still present.
  
  Arguments:
  
      DeviceObject - pointer to the device object for this printer
  
  Return Value:
  
      NT status code
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PURB urb;
      ULONG siz;
      LARGE_INTEGER   timeOut;
  
  
      timeOut.QuadPart = FAILURE_TIMEOUT;
  
  
      USBPRINT_KdPrint3 (("USBPRINT.SYS: enter USBPRINT_StopDevice\n"));
  
      deviceExtension = DeviceObject->DeviceExtension;
  
      //
      // Send the select configuration urb with a NULL pointer for the configuration
      // handle, this closes the configuration and puts the device in the 'unconfigured'
      // state.
      //
  
      siz = sizeof(struct _URB_SELECT_CONFIGURATION);
  
      urb = ExAllocatePoolWithTag(NonPagedPool,siz, USBP_TAG);
  
      if (urb) {
      NTSTATUS status;
  
      UsbBuildSelectConfigurationRequest(urb,
                        (USHORT) siz,
                        NULL);
  
      status = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
  
      USBPRINT_KdPrint3 (("USBPRINT.SYS: Device Configuration Closed status = %x usb status = %x.\n",
              status, urb->UrbHeader.Status));
  
      ExFreePool(urb);                                                                                   
      } else {
      ntStatus = STATUS_INSUFFICIENT_RESOURCES;
      }
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_StopDevice (%x)\n", ntStatus));
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_PnPAddDevice(
      IN PDRIVER_OBJECT DriverObject,
      IN PDEVICE_OBJECT PhysicalDeviceObject
      )
  /*++
  
  Routine Description:
  
      This routine is called to create a new instance of the device
  
  Arguments:
  
      DriverObject - pointer to the driver object for this instance of USBPRINT
  
      PhysicalDeviceObject - pointer to a device object created by the bus
  
  Return Value:
  
      STATUS_SUCCESS if successful,
      STATUS_UNSUCCESSFUL otherwise
  
  --*/
  {
      NTSTATUS                ntStatus = STATUS_SUCCESS;
      PDEVICE_OBJECT          deviceObject = NULL;
      PDEVICE_EXTENSION       deviceExtension;
      USBD_VERSION_INFORMATION versionInformation;
      ULONG ulPortNumber;
      GUID * pPrinterGuid;
      
      static ULONG instance = 0;
      //UNICODE_STRING deviceLinkUnicodeString;
      HANDLE hInterfaceKey;
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS:  enter USBPRINT_PnPAddDevice\n"));
      
  
  
      //
      // create our funtional device object (FDO)
      //
  
      ntStatus =
      USBPRINT_CreateDeviceObject(DriverObject, &deviceObject);
  
      if (NT_SUCCESS(ntStatus)) {
      deviceExtension = deviceObject->DeviceExtension;
  
      //
      // we support direct io for read/write
      //
      deviceObject->Flags |= DO_DIRECT_IO;
      deviceObject->Flags |= DO_POWER_PAGABLE;
      
  
      //** initialize our device extension
      //
      // remember the Physical device Object
      //
      deviceExtension->PhysicalDeviceObject=PhysicalDeviceObject;
  
      // init selective suspend stuff
      deviceExtension->PendingIdleIrp 	= NULL;
      deviceExtension->IdleCallbackInfo 	= NULL;
      deviceExtension->OpenCnt=0;
      deviceExtension->bD0IrpPending=FALSE;
      KeInitializeSpinLock(&(deviceExtension->WakeSpinLock));
  
      //
      // Attach to the PDO
      //
  
      deviceExtension->TopOfStackDeviceObject=IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
      if(deviceExtension->TopOfStackDeviceObject==NULL)
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS:  IoAttachDeviceToDeviceStack failed\n"));
      }                                                                                                                                                        
      else
      {
        USBPRINT_KdPrint3(("USBPRINT.SYS:  IoAttachDeviceToDeviceStack worked\n"));
      }
  
      deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
      USBPRINT_KdPrint3(("'USBPRINT.SYS:  Before ioRegisterDeviceInterface\n"));
      pPrinterGuid=(GUID *)&USBPRINT_GUID;
      ntStatus=IoRegisterDeviceInterface(PhysicalDeviceObject,pPrinterGuid,NULL,&(deviceExtension->DeviceLinkName));
      if(!NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint1(("'USBPRINT.SYS:  ioRegisterDeviceInterface failed\n"));  
        goto AddDeviceFailure;
      }
  
  
      ntStatus=IoOpenDeviceInterfaceRegistryKey(&(deviceExtension->DeviceLinkName),KEY_ALL_ACCESS,&hInterfaceKey);
      USBPRINT_KdPrint2(("USBPRINT.SYS:  Opened Device Interface reg key in AddDevice\n"));  
      // moved to RemoveDevice RtlFreeUnicodeString(&deviceLinkUnicodeString);
      if(!NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS: IoOpenDeviceInterfaceRegistryKey failed\n"));
        goto AddDeviceFailure;
      }
      USBPRINT_KdPrint3(("USBPRINT.SYS: IoOpenDeviceInterfaceRegistryKey succeeded\n"));
      deviceExtension->hInterfaceKey=hInterfaceKey;
      
      ntStatus=GetPortNumber(hInterfaceKey,&ulPortNumber);
      if(!NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS: GetPortNumber failed\n"));
        goto AddDeviceFailure;
      }
      deviceExtension->ulInstanceNumber=ulPortNumber;
      USBPRINT_KdPrint2(("USBPRINT.SYS:   Allocated port # %u\n",ulPortNumber));
      
  /*    ntStatus=IoSetDeviceInterfaceState(&(deviceExtension->DeviceLinkName),TRUE);
  
  
      if(NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint3(("USBPRINT.SYS:  IoSetDeviceInterfaceState worked\n"));
      }
      else
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS:  IoSetDeviceInterfaceState did not work\n"));
        goto AddDeviceFailure;
      }
  
    */
      USBPRINT_QueryCapabilities(PhysicalDeviceObject,
                   &deviceExtension->DeviceCapabilities);            
  
      //
      // display the device  caps
      //
  #if DBG
      {
      ULONG i;
      
      USBPRINT_KdPrint3(("USBPRINT.SYS:  >>>>>> DeviceCaps\n"));  
      USBPRINT_KdPrint3(("USBPRINT.SYS:  SystemWake = (%d)\n", 
          deviceExtension->DeviceCapabilities.SystemWake));    
      USBPRINT_KdPrint3(("USBPRINT.SYS:  DeviceWake = (D%d)\n",
          deviceExtension->DeviceCapabilities.DeviceWake-1));
  
      for (i=PowerSystemUnspecified; i< PowerSystemMaximum; i++) {
          
          USBPRINT_KdPrint3(("USBPRINT.SYS:  Device State Map: sysstate %d = devstate 0x%x\n", i, 
           deviceExtension->DeviceCapabilities.DeviceState[i]));       
      }
      USBPRINT_KdPrint3(("USBPRINT.SYS:  '<<<<<<<<DeviceCaps\n"));
      }
  #endif
      //
      // transition to zero signals the event
      //
      USBPRINT_IncrementIoCount(deviceObject);                                 
      }
  
      USBD_GetUSBDIVersion(&versionInformation);
  AddDeviceFailure:
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_PnPAddDevice (%x)\n", ntStatus));
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_CreateDeviceObject(
      IN PDRIVER_OBJECT DriverObject,
      IN PDEVICE_OBJECT *DeviceObject
      )
  /*++
  
  Routine Description:
  
      Creates a Functional DeviceObject
  
  Arguments:
  
      DriverObject - pointer to the driver object for device
  
      DeviceObject - pointer to DeviceObject pointer to return
              created device object.
  
      Instance - instnace of the device create.
  
  Return Value:
  
      STATUS_SUCCESS if successful,
      STATUS_UNSUCCESSFUL otherwise
  
  --*/
  {
      NTSTATUS ntStatus;
      PDEVICE_EXTENSION deviceExtension;
      ULONG instance;
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_CreateDeviceObject\n"));
  
      //
      // This driver supports up to 9 instances
      //
  
  
  
      ntStatus = IoCreateDevice (DriverObject,
                     sizeof (DEVICE_EXTENSION),
                     NULL,
                     FILE_DEVICE_UNKNOWN,
                     0,
                     FALSE,
                     DeviceObject);
      //
      // Initialize our device extension
      //
  
      deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);
  
      deviceExtension->IsChildDevice=FALSE;
      deviceExtension->ResetWorkItemPending=0; //init to "no workitem pending"
      deviceExtension->bChildDeviceHere=FALSE;
  
      deviceExtension->DeviceDescriptor = NULL;
      deviceExtension->Interface = NULL;
      deviceExtension->ConfigurationHandle = NULL;
      deviceExtension->AcceptingRequests = TRUE;
      deviceExtension->PendingIoCount = 0;
  
      deviceExtension->DeviceCapabilities.Size    = sizeof(DEVICE_CAPABILITIES);
      deviceExtension->DeviceCapabilities.Version = DEVICE_CAPABILITY_VERSION;
      deviceExtension->DeviceCapabilities.Address = (ULONG) -1;
      deviceExtension->DeviceCapabilities.UINumber= (ULONG) -1;
  
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemWorking] = PowerDeviceD0;
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemSleeping1] = PowerDeviceD3;
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
      deviceExtension->DeviceCapabilities.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
  
      KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_CreateDeviceObject (%x)\n", ntStatus));
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_CallUSBD(
      IN PDEVICE_OBJECT   DeviceObject,
      IN PURB             Urb,
      IN PLARGE_INTEGER   pTimeout 
      )
  /*++
  
  Routine Description:
  
      Passes a URB to the USBD class driver
  
  Arguments:
  
      DeviceObject - pointer to the device object for this printer
  
      Urb - pointer to Urb request block
  
  Return Value:
  
      STATUS_SUCCESS if successful,
      STATUS_UNSUCCESSFUL otherwise
  
  --*/
  {
      NTSTATUS ntStatus, status = STATUS_SUCCESS;
      PDEVICE_EXTENSION deviceExtension;
      PIRP irp;
      KEVENT event;
      PIO_STACK_LOCATION nextStack;
  
  
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_CallUSBD\n"));
  
      deviceExtension = DeviceObject->DeviceExtension;
  
      //
      // issue a synchronous request
      //
  
      KeInitializeEvent(&event, NotificationEvent, FALSE);
  
      if ( (irp = IoAllocateIrp(deviceExtension->TopOfStackDeviceObject->StackSize,
                                FALSE)) == NULL )
          return STATUS_INSUFFICIENT_RESOURCES;
  
      //
      // Call the class driver to perform the operation.  If the returned status
      // is PENDING, wait for the request to complete.
      //
  
      nextStack = IoGetNextIrpStackLocation(irp);
      ASSERT(nextStack != NULL);
  
      //
      // pass the URB to the USB driver stack
      //
      nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
      nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
      nextStack->Parameters.Others.Argument1 = Urb;
  
      IoSetCompletionRoutine(irp,
                 USBPRINT_DeferIrpCompletion,
                 &event,
                 TRUE,
                 TRUE,
                 TRUE);
                 
      ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
                  irp);
  
      if ( ntStatus == STATUS_PENDING ) 
      {
          status = KeWaitForSingleObject(&event,Suspended,KernelMode,FALSE,pTimeout);
          //
          // If the request timed out cancel the request
          // and wait for it to complete
          //
          if ( status == STATUS_TIMEOUT ) {
  
  #ifdef  MYDEBUG
              DbgPrint("Call_USBD: Cancelling IRP %X because of timeout\n", irp);
  #endif
  
              IoCancelIrp(irp);
              KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
          }
  
          ntStatus = irp->IoStatus.Status;
      }
  
      IoFreeIrp(irp);
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_CallUSBD (%x)\n", ntStatus));
  
      USBPRINT_KdPrint3 (("USBPRINT.SYS: About to return from CallUSBD, status=%x\n", ntStatus));
  
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_ConfigureDevice(
                           IN  PDEVICE_OBJECT DeviceObject
                           )
  /*++
                           
  Routine Description:
       Initializes a given instance of the device on the USB and selects the configuration.
                               
  Arguments:
       DeviceObject - pointer to the device object for this printer devcice.
  
   Return Value:  
       NT status code
                                         
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus;
      PURB urb;
      ULONG siz;
      PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;
      LARGE_INTEGER   timeOut;
      int tries=0;
  
  
      timeOut.QuadPart = FAILURE_TIMEOUT;
  
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_ConfigureDevice\n")); 
      
      deviceExtension = DeviceObject->DeviceExtension;
      
      //
      // first configure the device
      //
      
      urb = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USBP_TAG);
      
      if (urb) 
      {
          siz = sizeof(USB_CONFIGURATION_DESCRIPTOR)+256;
          
  get_config_descriptor_retry:
          
          configurationDescriptor = ExAllocatePoolWithTag(NonPagedPool,siz, USBP_TAG);
          
          if (configurationDescriptor) 
          {
              
              UsbBuildGetDescriptorRequest(urb,
                  (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                  USB_CONFIGURATION_DESCRIPTOR_TYPE,
                  0,
                  0,
                  configurationDescriptor,
                  NULL,
                  siz,
                  NULL);
              
              ntStatus = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
              if(!NT_SUCCESS(ntStatus))
              {
                  USBPRINT_KdPrint1 (("USBPRINT.SYS: Get Configuration descriptor failed\n"));
              }
              else
              {
                  //
                  // if we got some data see if it was enough.
                  //
                  // NOTE: we may get an error in URB because of buffer overrun
                  if (urb->UrbControlDescriptorRequest.TransferBufferLength>0 &&configurationDescriptor->wTotalLength > siz)
                  {
                  
                      #define MAX_MEM_FOR_DESCRIPTOR 1024 * 25
  
                      if((siz>MAX_MEM_FOR_DESCRIPTOR)||((tries++)==10))
                      {
                          USBPRINT_KdPrint3 (("USBPRINT.SYS: ConfigureDevice, this device appears to be lying to us about descriptor size\n"));
                          ExFreePool(configurationDescriptor);
                          configurationDescriptor = NULL;
                          ntStatus=STATUS_DEVICE_CONFIGURATION_ERROR;
                      }
                      else
                      {
                      
                          siz = configurationDescriptor->wTotalLength;
                          ExFreePool(configurationDescriptor);
                          configurationDescriptor = NULL;
                          goto get_config_descriptor_retry;
                      }
                  } //end if not big enough
              }  //end else we got some sort of descriptor
              
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Configuration Descriptor = %x, len %x\n",
                  configurationDescriptor,
                  urb->UrbControlDescriptorRequest.TransferBufferLength));
          } 
          else 
          {
              ntStatus = STATUS_INSUFFICIENT_RESOURCES;
              USBPRINT_KdPrint1(("USBPRINT.SYS: Insufficient resources to allocate configuration descriptor in ConfigureDevice\n"));
          }
                  
          ExFreePool(urb);
          
      } 
      else 
      {
          ntStatus = STATUS_INSUFFICIENT_RESOURCES;
      }
      
      if (configurationDescriptor) 
      {
          
          USBPRINT_KdPrint2(("USBPRINT.SYS: ConfigureDevice, We have a configuration descriptor!\n"));
          //
          // We have the configuration descriptor for the configuration
          // we want.
          //
          // Now we issue the select configuration command to get
          // the  pipes associated with this configuration.
          //
          if(NT_SUCCESS(ntStatus))
          {
            ntStatus = USBPRINT_SelectInterface(DeviceObject,configurationDescriptor);
            
          }
          ExFreePool(configurationDescriptor);
      }
      else
      {
              USBPRINT_KdPrint1(("USBPRINT.SYS: ConfigureDevice, No Configuration descriptor.\n"));
      }
      
      
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_ConfigureDevice (%x)\n", ntStatus));
      
      return ntStatus;
  }
  
  
  NTSTATUS USBPRINT_SelectInterface(IN PDEVICE_OBJECT DeviceObject,IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
  /*++
  
  Routine Description:
  
      Initializes a printer with multiple interfaces
  
  Arguments:
  
      DeviceObject - pointer to the device object for this printer
              
  
      ConfigurationDescriptor - pointer to the USB configuration
              descriptor containing the interface and endpoint
              descriptors.
  
  Return Value:
  
      NT status code
  
    --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus;
      PURB urb = NULL;
      PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;
      PUSBD_INTERFACE_INFORMATION Interface = NULL;
      USBD_INTERFACE_LIST_ENTRY InterfaceList[2];
      LARGE_INTEGER   timeOut;
  
  
      timeOut.QuadPart = FAILURE_TIMEOUT;
  
      
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_SelectInterface\n"));
      deviceExtension = DeviceObject->DeviceExtension;
      
      //starting at offset 0, search for an alternate interface with protocol code 2;  Ignore InterfaceNumber, AlternateSetting, InterfaceClass, InterfaceSubClass
      interfaceDescriptor=USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor,ConfigurationDescriptor,-1,-1,-1,-1,2);
      if(!interfaceDescriptor)
      {
          USBPRINT_KdPrint3 (("USBPRINT.SYS:  First ParseConfigurationDescriptorEx failed\n"));
          interfaceDescriptor=USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor,ConfigurationDescriptor,-1,-1,-1,-1,1);
          if(!interfaceDescriptor)
          {
              USBPRINT_KdPrint1 (("USBPRINT.SYS:  second ParseConfigurationDescriptorEx failed\n"));
              ntStatus=STATUS_DEVICE_CONFIGURATION_ERROR;
          }
          else
          {
              USBPRINT_KdPrint3 (("USBPRINT.SYS:  second ParseConfigurationDescriptorEx success\n"));
              deviceExtension->bReadSupported=FALSE;
          } /*end second ParseConfigDescriptor worked*/
      }
      else
      {
          deviceExtension->bReadSupported=TRUE;
          USBPRINT_KdPrint3 (("USBPRINT.SYS:  First ParseConfigurationDescriptorEx success\n"));
      }
      if(interfaceDescriptor)
      {
          InterfaceList[0].InterfaceDescriptor=interfaceDescriptor;
          InterfaceList[1].InterfaceDescriptor=NULL;
          urb = USBD_CreateConfigurationRequestEx(ConfigurationDescriptor,InterfaceList);
          if (urb) 
          {
              Interface = InterfaceList[0].Interface;
              ntStatus = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
          } 
          else 
          {
              ntStatus = STATUS_INSUFFICIENT_RESOURCES;
              USBPRINT_KdPrint1 (("USBPRINT.SYS: CreateConfigurationRequest failed\n"));
          }
      } //end found good interface
      else
      {
          USBPRINT_KdPrint1 (("USBPRINT.SYS: failed to locate apropriate interface\n"));
      } //end no interface
      
     
      if (NT_SUCCESS(ntStatus)) 
      {
          
          //
          // Save the configuration handle for this device
          //
          
          USBPRINT_KdPrint3 (("USBPRINT.SYS: SelectInterface, Inside good config case\n"));
          deviceExtension->ConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle;
          
          deviceExtension->Interface = ExAllocatePoolWithTag(NonPagedPool,Interface->Length, USBP_TAG);
          
          if (deviceExtension->Interface) 
          {
              ULONG j;
              //
              // save a copy of the interface information returned
              //
              RtlCopyMemory(deviceExtension->Interface, Interface, Interface->Length);
              
              //
              // Dump the interface to the debugger
              //
              USBPRINT_KdPrint3 (("USBPRINT.SYS: ---------\n"));
              USBPRINT_KdPrint3 (("USBPRINT.SYS: NumberOfPipes 0x%x\n", deviceExtension->Interface->NumberOfPipes));
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Length 0x%x\n", deviceExtension->Interface->Length));
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Alt Setting 0x%x\n", deviceExtension->Interface->AlternateSetting));
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Interface Number 0x%x\n", deviceExtension->Interface->InterfaceNumber));
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Class, subclass, protocol 0x%x 0x%x 0x%x\n",
                  deviceExtension->Interface->Class,
                  deviceExtension->Interface->SubClass,
                  deviceExtension->Interface->Protocol));
              
              // Dump the pipe info
              
              for (j=0; j<Interface->NumberOfPipes; j++) 
              {
                  PUSBD_PIPE_INFORMATION pipeInformation;
                  
                  pipeInformation = &deviceExtension->Interface->Pipes[j];
                  
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: ---------\n"));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: PipeType 0x%x\n", pipeInformation->PipeType));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: EndpointAddress 0x%x\n", pipeInformation->EndpointAddress));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Interval 0x%x\n", pipeInformation->Interval));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: Handle 0x%x\n", pipeInformation->PipeHandle));
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize));
              }
              
              USBPRINT_KdPrint3 (("USBPRINT.SYS: ---------\n"));
          } /*end if interface Alloc OK*/
          else
          {
              ntStatus = STATUS_INSUFFICIENT_RESOURCES;
              USBPRINT_KdPrint1 (("USBPRINT.SYS: Alloc failed in SelectInterface\n"));
          }
      }
      
      if (urb) 
      {
          ExFreePool(urb);
      }
      USBPRINT_KdPrint2 (("USBPRINT.SYS: exit USBPRINT_SelectInterface (%x)\n", ntStatus));
      
      return ntStatus;
  }
  
  
  NTSTATUS
  USBPRINT_BuildPipeList(
      IN  PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
  Arguments:
  
      DeviceObject - pointer to the device object for this printer
              devcice.
  
  
  Return Value:
  
      NT status code
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      ULONG i;
      WCHAR Name[] = L"\\PIPE00";
      PUSBD_INTERFACE_INFORMATION InterfaceDescriptor;
      BOOL bFoundWritePipe=FALSE,bFoundReadPipe=FALSE,bNeedReadPipe=FALSE;
      
  
      deviceExtension = DeviceObject->DeviceExtension;
      InterfaceDescriptor = deviceExtension->Interface;
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_BuildPipeList\n"));
  
      deviceExtension = DeviceObject->DeviceExtension;
      if(InterfaceDescriptor->Protocol==2)
          bNeedReadPipe=TRUE;
      else
          bNeedReadPipe=FALSE;
  
  
      for (i=0; i<InterfaceDescriptor->NumberOfPipes; i++) {
          USBPRINT_KdPrint3 (("USBPRINT.SYS: about to look at endpoint with address 0x%x)\n",InterfaceDescriptor->Pipes[i].EndpointAddress));
          if(((InterfaceDescriptor->Pipes[i].EndpointAddress)&0x80)==0) //if bit 7 is 0, it's an OUT endpoint
          {
            if(bFoundWritePipe==TRUE)
            {
              USBPRINT_KdPrint1 (("USBPRINT.SYS: Warning!!  Multiple OUT pipes detected on printer.  Defaulting to first pipe\n"));
            } /*end if we've already found a write pipe*/
            else
            {
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Found write pipe\n"));
              deviceExtension->pWritePipe=&(InterfaceDescriptor->Pipes[i]);
              bFoundWritePipe=TRUE;
            } /*else we haven't seen an OUT endpont before*/
          } /*end if it's an OUT endpoint*/
          else
          {
            if(!bNeedReadPipe)
            {
              USBPRINT_KdPrint1 (("USBPRINT.SYS: Warning!!  unexpected IN pipe (not specified in protocol field)\n"));
            } /*end if we don't need a read pipe, but we found one*/
            else if(bFoundReadPipe)
            {
                USBPRINT_KdPrint1 (("USBPRINT.SYS: Warning!!  Multiple IN pipes detected on printer.  Defaulting to first pipe\n"));
            } /*end if we've already found a read pipe*/
            else
            {     
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Found read pipe\n"));
              deviceExtension->pReadPipe=&(InterfaceDescriptor->Pipes[i]);
              bFoundReadPipe=TRUE;
            } /*end else we're supposed to have an IN pipe, and this is the first one we've seen*/
          } /*end else it's an IN endpoint*/
      } /*end for*/
      if((bNeedReadPipe==TRUE)&&(bFoundReadPipe==FALSE))
      {
          USBPRINT_KdPrint1 (("USBPRINT.SYS: Warning!!  IN pipe was specified in protocol field, but was not found\n"));
      } /*end if we needed a read pipe, and didn't find one*/
      deviceExtension->bReadPipeExists=bFoundReadPipe;
      return STATUS_SUCCESS;
  } /*end function BuildPipeList*/
  
  
  NTSTATUS
  USBPRINT_ResetPipe(
      IN PDEVICE_OBJECT DeviceObject,
      IN PUSBD_PIPE_INFORMATION Pipe,
      IN BOOLEAN IsoClearStall
      )
  /*++
  
  Routine Description:
  
      Reset a given USB pipe.
      
      NOTES:
  
      This will reset the host to Data0 and should also reset the device
      to Data0 for Bulk and Interrupt pipes.
  
      For Iso pipes this will set the virgin state of pipe so that ASAP
      transfers begin with the current bus frame instead of the next frame
      after the last transfer occurred.
  
  Arguments:
  
  Return Value:
  
  
  --*/
  {
      NTSTATUS ntStatus;
      PURB urb;
      LARGE_INTEGER   timeOut;
  
  
      timeOut.QuadPart = FAILURE_TIMEOUT;
  
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: Entering Reset Pipe; pipe # %x\n", Pipe)); 
  
      urb = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct _URB_PIPE_REQUEST), USBP_TAG);
  
      if (urb) {
  
      urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
      urb->UrbPipeRequest.PipeHandle =
          Pipe->PipeHandle;
  
      ntStatus = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
      
      if(!NT_SUCCESS(ntStatus))
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS: CallUSBD failed in ResetPipe\n"));
      }
      else
      {
        USBPRINT_KdPrint3(("USBPRINT.SYS: CallUSBD Succeeded in ResetPipe\n"));
      }
  
      ExFreePool(urb);
  
      } else {
      ntStatus = STATUS_INSUFFICIENT_RESOURCES;
      }
  
      //
      // Memphis RESET_PIPE will send a Clear-Feature Endpoint Stall to
      // reset the data toggle of non-Iso pipes as part of a RESET_PIPE
      // request.  It does not do this for Iso pipes as Iso pipes do not use
      // the data toggle (all Iso packets are Data0).  However, we also use
      // the Clear-Feature Endpoint Stall request in our device firmware to
      // reset data buffer points inside the device so we explicitly send
      // this request to the device for Iso pipes if desired.
      //
      if (NT_SUCCESS(ntStatus) && IsoClearStall &&
      (Pipe->PipeType == UsbdPipeTypeIsochronous)) {
      
      urb = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct _URB_CONTROL_FEATURE_REQUEST), USBP_TAG);
  
      if (urb) {
  
          UsbBuildFeatureRequest(urb,
                     URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT,
                     USB_FEATURE_ENDPOINT_STALL,
                     Pipe->EndpointAddress,
                     NULL);
  
          ntStatus = USBPRINT_CallUSBD(DeviceObject, urb, &timeOut);
              
  
          ExFreePool(urb);
      } else {
          ntStatus = STATUS_INSUFFICIENT_RESOURCES;
      }
      }
  
      return ntStatus;
  }
  
  
  LONG
  USBPRINT_DecrementIoCount(
      IN PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
  Arguments:
  
  Return Value:
  
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      LONG ioCount=0;
  
      deviceExtension = DeviceObject->DeviceExtension;
      if(!(deviceExtension->IsChildDevice))
      {
        ioCount = InterlockedDecrement(&deviceExtension->PendingIoCount);
  
  #ifdef  MYDEBUG
      DbgPrint("USBPRINT_DecrementIoCount -- IoCount %d\n", deviceExtension->PendingIoCount);
  #endif
        USBPRINT_KdPrint3 (("USBPRINT.SYS: Pending io count = %x\n", ioCount));
  
        if (ioCount==0) {
        KeSetEvent(&deviceExtension->RemoveEvent,
             1,
             FALSE);
        }
      } /*end if ! child device*/
  
      return ioCount;
  }
  
  
  VOID
  USBPRINT_IncrementIoCount(
      IN PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
  Arguments:
  
  Return Value:
  
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
  
      deviceExtension = DeviceObject->DeviceExtension;
      if(!(deviceExtension->IsChildDevice))
      {
        InterlockedIncrement(&deviceExtension->PendingIoCount);
  #ifdef  MYDEBUG
      DbgPrint("USBPRINT_IncrementIoCount -- IoCount %d\n", deviceExtension->PendingIoCount);
  #endif
        //
        // Everytime iocount goes to 0 we set this event
        // so we must cleat it when we have a new io
        //
        KeClearEvent(&deviceExtension->RemoveEvent);
      }
  }
  
  
  NTSTATUS
  USBPRINT_ReconfigureDevice(
      IN  PDEVICE_OBJECT DeviceObject
      )
  /*++
  
  Routine Description:
  
      Initializes a given instance of the device on the USB and selects the
      configuration.
  
  Arguments:
  
      DeviceObject - pointer to the device object for this printer
              
  
  
  Return Value:
  
      NT status code
  
  --*/
  {
      PDEVICE_EXTENSION deviceExtension;
      NTSTATUS ntStatus = STATUS_SUCCESS;
      PUSBD_INTERFACE_INFORMATION InterfaceDescriptor;
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: enter USBPRINT_ReconfigureDevice\n"));
  
      deviceExtension = DeviceObject->DeviceExtension;
  
      if (NT_SUCCESS(ntStatus)) {
      ntStatus = USBPRINT_ConfigureDevice(DeviceObject);
      }
  
      //
      // new InterfaceDescriptor structure is now set up
      //
  
      InterfaceDescriptor = deviceExtension->Interface;
  
      //
      // set up the pipe handles again
      //
  
  
      return ntStatus;
  }
  
  
  NTSTATUS LoadPortsUsed(GUID *pPrinterGuid,PFREE_PORTS * pPortList,WCHAR *wcBaseName)
  {
      NTSTATUS ReturnStatus=STATUS_SUCCESS,Result=STATUS_SUCCESS;
      PWSTR pDeviceList;
      PWSTR pWalk;
      UNICODE_STRING wNumberValueName,wBaseValueName,wLinkName;
      ULONG ulPortNum;
      ULONG ulBaseNameSizeIn,ulBaseNameSizeOut,ulPortNumSizeIn,ulPortNumSizeOut;
      PKEY_VALUE_PARTIAL_INFORMATION pBaseValueStruct,pNumberValueStruct;
      HANDLE hInterfaceKey;
      BOOL bFoundUsbPort;
      
      
      
      Result=IoGetDeviceInterfaces(pPrinterGuid,NULL,DEVICE_INTERFACE_INCLUDE_NONACTIVE,&pDeviceList);
      if(Result==STATUS_SUCCESS)
      {
          RtlInitUnicodeString(&wNumberValueName,PORT_NUM_VALUE_NAME);
          RtlInitUnicodeString(&wBaseValueName,PORT_BASE_NAME);
          pWalk=pDeviceList;
          ulBaseNameSizeIn=sizeof(KEY_VALUE_PARTIAL_INFORMATION)+((wcslen(wcBaseName)+1)*sizeof(WCHAR)); //this is a byte to much.  Oh well
          ulPortNumSizeIn=sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG);
          pBaseValueStruct=ExAllocatePoolWithTag(NonPagedPool,ulBaseNameSizeIn, USBP_TAG);
          pNumberValueStruct=ExAllocatePoolWithTag(NonPagedPool,ulPortNumSizeIn, USBP_TAG);
          if((pBaseValueStruct!=NULL)&&(pNumberValueStruct!=NULL))
          {
              while( *pWalk!=0 && NT_SUCCESS(ReturnStatus) )
              {
                  RtlInitUnicodeString(&wLinkName,pWalk);
                  Result=IoOpenDeviceInterfaceRegistryKey(&wLinkName,KEY_ALL_ACCESS,&hInterfaceKey);
                  if(NT_SUCCESS(Result))
                  {
                      
                      //The following is:  If there is not a value, or there is a value that matches what we expect, then set bFoundUsbPort to true
                      bFoundUsbPort=TRUE;
                      Result=ZwQueryValueKey(hInterfaceKey,&wBaseValueName,KeyValuePartialInformation,pBaseValueStruct,ulBaseNameSizeIn,&ulBaseNameSizeOut);
                      if(NT_SUCCESS(Result))
                      {
                          if(wcscmp(wcBaseName,(WCHAR *)(pBaseValueStruct->Data))!=0)
                              bFoundUsbPort=FALSE;
                      }//end if Query OK 
                      else if(STATUS_OBJECT_NAME_NOT_FOUND!=Result)
                      {
                          bFoundUsbPort=FALSE;
                      }
                      if(bFoundUsbPort)
                      {
                          Result=ZwQueryValueKey(hInterfaceKey,&wNumberValueName,KeyValuePartialInformation,pNumberValueStruct,ulPortNumSizeIn,&ulPortNumSizeOut);
                          if(NT_SUCCESS(Result))
                          {
                              ulPortNum=*((ULONG *)(pNumberValueStruct->Data));
                              if(!bDeleteIfRecyclable(hInterfaceKey))
                              {
                                  USBPRINT_KdPrint2(("USBPRINT.SYS:  Adding port number\n"));
                                  ReturnStatus=bAddPortInUseItem(pPortList,ulPortNum);
                                  if(!NT_SUCCESS(ReturnStatus))
                                  {
                                      USBPRINT_KdPrint1(("USBPRINT.SYS:  Unable to add port %u to port list\n",ulPortNum));
                                      USBPRINT_KdPrint1(("USBPRINT.SYS:  Failing out of LoadPortsUsed due to ntstatus failure %d\n",ReturnStatus));
                                  } //end if AddPortInUse failed
                              } //end if port not deleted
                              else
                              {
  //                                ReturnStatus=STATUS_INVALID_PARAMETER;
                                  USBPRINT_KdPrint1(("USBPRINT.SYS:  Invalid port number %u\n",ulPortNum));
                              }
                          } //end if Query Port Number OK
                          //no else.  If there's no port number, we ignore this interface
                      } //End if bFoundUSbPort
                      ZwClose(hInterfaceKey);
                  } //end if OpenReg ok
                  pWalk=pWalk+wcslen(pWalk)+1;
              } //end while
          } //end ExAllocatePool OK
          else
          {
              USBPRINT_KdPrint1(("USBPRINT.SYS:  Unable to allocate memory"));
              ReturnStatus=STATUS_INSUFFICIENT_RESOURCES;
          }   /*end else ExAllocatePool failed*/
          if(pBaseValueStruct!=NULL)
              ExFreePool(pBaseValueStruct);
          if(pNumberValueStruct!=NULL)
              ExFreePool(pNumberValueStruct);
          ExFreePool(pDeviceList);
      } /*end if IoGetDeviceInterfaces success*/
      else
      {
          USBPRINT_KdPrint1(("USBPRINT.SYS:  IoGetDeviceInterfaces failed"));
          ReturnStatus=Result; //do some error translation here?
      }
      return ReturnStatus;
  } /*end function LoadPortsUsed*/
                                  
  
  NTSTATUS GetPortNumber(HANDLE hInterfaceKey,
                         ULONG *ulReturnNumber)
  {
    ULONG ulPortNumber,ulSizeUsed;
    NTSTATUS ntStatus=STATUS_SUCCESS;
    UNICODE_STRING uncValueName;
    PKEY_VALUE_PARTIAL_INFORMATION pValueStruct;
  
  
    ulSizeUsed=sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG); //this is a byte to much.  Oh well
    pValueStruct=ExAllocatePoolWithTag(PagedPool,ulSizeUsed, USBP_TAG);
    if(pValueStruct==NULL)
        return STATUS_INSUFFICIENT_RESOURCES;
    RtlInitUnicodeString(&uncValueName,PORT_NUM_VALUE_NAME);
    ntStatus=ZwQueryValueKey(hInterfaceKey,&uncValueName,KeyValuePartialInformation,(PVOID)pValueStruct,ulSizeUsed,&ulSizeUsed);
    if(!NT_SUCCESS(ntStatus))
    {
      USBPRINT_KdPrint2(("USBPRINT.SYS: GetPortNumber; ZwQueryValueKey failed\n"));
      switch(ntStatus)
      {
      case STATUS_BUFFER_OVERFLOW:          
        USBPRINT_KdPrint2(("USBPRINT.SYS: GetPortNumber zwQueryValueKey returned STATUS_BUFFER_OVERFLOW\n"));
      break;
      
      case STATUS_INVALID_PARAMETER:
        USBPRINT_KdPrint2(("USBPRINT.SYS: GetPortNumber zwQueryValueKey returned STATUS_INVALID_PARAMETER\n"));
      break;
  
  
      case STATUS_OBJECT_NAME_NOT_FOUND:
        USBPRINT_KdPrint2(("USBPRINT.SYS: GetPortNumber zwQueryValueKey returned STATUS_OBJECT_NAME_NOT_FOUND\n"));
      break;
  
      default:
            USBPRINT_KdPrint2(("USBPRINT.SYS: GetPortNumber zwQueryValueKey returned unkown error\n"));
      }
      ntStatus=GetNewPortNumber(&pGPortList,&ulPortNumber);
  
    }
    else
    { 
      ulPortNumber=*((ULONG *)&(pValueStruct->Data));
      if(ulPortNumber==0) //zero is a placeholder for "not there" which we use because win9x is missing the zwDeleteValueKey api
        ntStatus=GetNewPortNumber(&pGPortList,&ulPortNumber);
      else
        vClaimPortNumber(ulPortNumber,hInterfaceKey,&pGPortList);
    }
    if(!NT_SUCCESS(ntStatus))
    {
      USBPRINT_KdPrint1(("USBPRINT.SYS: GetPortNumber; failed to allocate new port number\n"));
    }
    else
    {
      
        *ulReturnNumber=ulPortNumber;
        USBPRINT_KdPrint3(("USBPRINT.SYS: GetPortNumber; Inside \"write back to reg\" case, ulPortNumber==%d\n",ulPortNumber));
        USBPRINT_KdPrint3(("USBPRINT.SYS: GetPortNumber; Before ntstatys=success\n"));
        ntStatus=STATUS_SUCCESS;
        USBPRINT_KdPrint3(("USBPRINT.SYS: GetPortNumber; Before ZwSetValueKey\n"));
        ntStatus=ZwSetValueKey(hInterfaceKey,&uncValueName,0,REG_DWORD,&ulPortNumber,sizeof(ulPortNumber));
        if(!NT_SUCCESS(ntStatus))
        {
          USBPRINT_KdPrint1(("USBPRINT.SYS: GetPortNumber; Unable to set value key\n"));
        }
        else
        {
          *ulReturnNumber=ulPortNumber;
        }
    }
    ExFreePool(pValueStruct);
    return ntStatus;
  } /*end function GetPortNumber*/
  
  
  USBPRINT_GetDeviceID(PDEVICE_OBJECT ParentDeviceObject)
  {
      UCHAR *p1284Id;
      NTSTATUS ntStatus;
      int iReturnSize;
      PDEVICE_EXTENSION pParentExtension;
      
  
      pParentExtension=ParentDeviceObject->DeviceExtension;
  
      USBPRINT_KdPrint1 (("USBPRINT.SYS: GetDeviceID enter\n"));  /*dd*/
  
  
      p1284Id=ExAllocatePoolWithTag(NonPagedPool,MAX_ID_SIZE, USBP_TAG);
      if(p1284Id==NULL)
      {
          pParentExtension->bBadDeviceID=TRUE;
          USBPRINT_KdPrint1 (("USBPRINT.SYS: Memory Allocation failed in GetDeviceID\n"));
          sprintf(pParentExtension->DeviceIdString,BOGUS_PNP_ID);
      }
      else
      {
          
          iReturnSize=USBPRINT_Get1284Id(ParentDeviceObject,p1284Id,MAX_ID_SIZE-ID_OVERHEAD); //
          
          if(iReturnSize==-1)
          {
              pParentExtension->bBadDeviceID=TRUE;
              USBPRINT_KdPrint1 (("USBPRINT.SYS: Get1284Id Failed\n"));
              sprintf(pParentExtension->DeviceIdString,BOGUS_PNP_ID);
          } /*end if Get1284 failed*/
          else
          {
              USBPRINT_KdPrint3 (("USBPRINT.SYS: Get1284Id Succeeded\n"));
              USBPRINT_KdPrint2 (("USBPRINT.SYS: 1284 ID == %s\n",(p1284Id+2)));
              ntStatus=ParPnpGetId(p1284Id+2,BusQueryDeviceID,pParentExtension->DeviceIdString);
              USBPRINT_KdPrint3 (("USBPRINT.SYS: After call to ParPnpGetId"));
              if(!NT_SUCCESS(ntStatus))
              {
                  USBPRINT_KdPrint1 (("USBPRINT.SYS: ParPnpGetId failed, error== 0x%0X\n",ntStatus));
                  sprintf(pParentExtension->DeviceIdString,BOGUS_PNP_ID);
                  pParentExtension->bBadDeviceID=TRUE;
  
              }
              else
              {
                  USBPRINT_KdPrint3 (("USBPRINT.SYS: After ParPnpGetID\n"));
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: DeviceIdString=%s\n",pParentExtension->DeviceIdString));
              }
          } /*end if the request didn't fail*/
          ExFreePool(p1284Id);
      }
      USBPRINT_KdPrint2 (("USBPRINT.SYS: GetDeviceID exit\n"));
  } /*end function USBPRINT_GetDeviceID*/
  
  
  NTSTATUS ProduceQueriedID(PDEVICE_EXTENSION deviceExtension,PIO_STACK_LOCATION irpStack,PIRP Irp,PDEVICE_OBJECT DeviceObject)
  {
  
      PDEVICE_EXTENSION pParentExtension;
      NTSTATUS ntStatus=STATUS_SUCCESS;
      WCHAR wTempString1[30];
      PWSTR pWalk;
      HANDLE hChildRegKey;
      UCHAR *pRawString,*pTempString;
      UNICODE_STRING UnicodeDeviceId;
      UNICODE_STRING uncPortValueName;
      ANSI_STRING AnsiIdString;
      PCHILD_DEVICE_EXTENSION pChildExtension;
      int iReturnSize;
      int iFirstLen,iSecondLen, iTotalLen;
      
      pChildExtension=(PCHILD_DEVICE_EXTENSION)deviceExtension;
       pParentExtension=pChildExtension->ParentDeviceObject->DeviceExtension;
      
      USBPRINT_KdPrint1(("USBPRINT.SYS:  head of ProduceQueriedID\n")); /*dd*/
      
      pRawString=ExAllocatePool(NonPagedPool,MAX_ID_SIZE);
      pTempString=ExAllocatePool(NonPagedPool,MAX_ID_SIZE);
      if((pTempString==NULL)||(pRawString==NULL))
      {
          USBPRINT_KdPrint1 (("USBPRINT.SYS: BusQueryDeviceIDs; No memory.  Failing\n"));
          ntStatus=STATUS_NO_MEMORY;
          iReturnSize=-1;
      }
      else
      {
          if(pParentExtension->DeviceIdString[0]!=0)
          {
              switch(irpStack->Parameters.QueryId.IdType)
              { 
              case BusQueryDeviceID:
                  USBPRINT_KdPrint1 (("USBPRINT.SYS: Received BusQueryDeviceID message\n"));
                  sprintf(pRawString,"USBPRINT\\%s",pParentExtension->DeviceIdString); //this sprintf safe.. DeviceIDString guaranteed to be 15 less than RawString
                  FixupDeviceId((PUCHAR)pRawString);
                  RtlInitAnsiString(&AnsiIdString,pRawString);
                  if(!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeDeviceId,&AnsiIdString,TRUE))) //Make a unicode string out of this
                  {
                    USBPRINT_KdPrint1 (("USBPRINT.SYS: RtlAnsiStringToUnicodeString failed\n"));
                    ntStatus=STATUS_NO_MEMORY;
                    iReturnSize=-1;
                    Irp->IoStatus.Information=0;
                    break;
                  }
                  ntStatus=STATUS_SUCCESS;
                  Irp->IoStatus.Information=(ULONG_PTR)UnicodeDeviceId.Buffer;
                  USBPRINT_KdPrint1(("USBPRINT.SYS: returing DeviceID\n")); /*dd*/
                  break;
                  
              case BusQueryInstanceID:
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: Received BusQueryInstanceID message\n"));
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: returning instance %u\n",pChildExtension->ulInstanceNumber));
                  sprintf(pRawString,"USB%03u",pChildExtension->ulInstanceNumber);
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: RawString=%s\n",pRawString));
                  RtlInitAnsiString(&AnsiIdString,pRawString);
                  if(!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeDeviceId,&AnsiIdString,TRUE))) //Make a unicode string out of this
                  {
                    ntStatus=STATUS_NO_MEMORY;
                    iReturnSize=-1;
                    Irp->IoStatus.Information=0;
                    break;
                  }
  
                  ntStatus=STATUS_SUCCESS;
                  Irp->IoStatus.Information=(ULONG_PTR)UnicodeDeviceId.Buffer;
                  break;
                  
              case BusQueryHardwareIDs:
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: Received BusQueryHardwareIDs message\n")); 
  #ifndef WIN9XBUILD
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: inside IF NT\n"));
                  ntStatus=IoOpenDeviceRegistryKey(DeviceObject,PLUGPLAY_REGKEY_DEVICE,KEY_ALL_ACCESS,&hChildRegKey);
  #else
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: inside not NT\n")); 
                 
                  ntStatus=IoOpenDeviceRegistryKey(pParentExtension->PhysicalDeviceObject,PLUGPLAY_REGKEY_DEVICE,KEY_ALL_ACCESS,&hChildRegKey);
  #endif
                  if(!NT_SUCCESS(ntStatus))
                  {
                      USBPRINT_KdPrint1 (("USBPRINT.SYS: BusQueryHardwareIDs; IoOpenDeviceRegistryKey failed\n"));
                      break;
                  }
                  swprintf(wTempString1,L"USB%03u",pChildExtension->ulInstanceNumber);
                  RtlInitUnicodeString(&uncPortValueName,L"PortName");
                  ntStatus=ZwSetValueKey(hChildRegKey,&uncPortValueName,0,REG_SZ,wTempString1,(wcslen(wTempString1)+1)*sizeof(WCHAR));
                  if(!NT_SUCCESS(ntStatus))
                  {
                      USBPRINT_KdPrint1 (("USBPRINT.SYS: BusQueryHardwareIDs; ZwSetValueKey failed\n"));
                  }
                  else
                  {
                      USBPRINT_KdPrint3 (("USBPRINT.SYS: BusQueryHardwareIDs; ZwSetValueKey worked, wcslen(wTempString1)==%u\n",wcslen(wTempString1)));
                      ntStatus=STATUS_SUCCESS;
                  }
                  ZwClose(hChildRegKey);
                  
                  if(pParentExtension->DeviceIdString[0]==0)
                  {
                      ntStatus=STATUS_NOT_FOUND;
                      USBPRINT_KdPrint2 (("USBPRINT.SYS: BusQueryCompatibleIDs; DeviceIdString is null.  Can't continue\n"));
                      break;
                  }
                  ntStatus=ParPnpGetId(pParentExtension->DeviceIdString,irpStack->Parameters.QueryId.IdType,pRawString); 
                  if(!NT_SUCCESS(ntStatus))
                  {
                      USBPRINT_KdPrint1 (("USBPRINT.SYS: BusQueryDeviceIDs; ParPnpGetID failed\n"));
                      break;
                  } 
                  
                  if((strlen(pRawString)+ID_OVERHEAD)*2>MAX_ID_SIZE)
                  {
                    ntStatus=STATUS_NO_MEMORY;
                    USBPRINT_KdPrint1 (("USBPRINT.SYS: BusQueryDeviceIDs; ID's to long.  Failing\n"));
                    iReturnSize=-1;
                    break;
                  }
                  sprintf(pTempString,"USBPRINT\\%s",pRawString);
                  iFirstLen=strlen(pTempString);
                  *(pTempString+iFirstLen)=' ';  //make the old null be a space so that RtlInitAnsiString will step past it
                  *(pTempString+iFirstLen+1)='\0'; //add an extra null at the end of the string
                  strcat(pTempString,pRawString);
                  iTotalLen=strlen(pTempString);
  #ifdef USBPRINT_LIE_ABOUT_LPT
                  *(pTempString+iTotalLen)=' ';
                  *(pTempString+iTotalLen+1)='\0';
                  iSecondLen=iTotalLen;
                  strcat(pTempString,"LPTENUM\\");
                  strcat(pTempString,pRawString);
                  iTotalLen=strlen(pTempString);
  #endif
                  *(pTempString+iTotalLen)=' ';
                  *(pTempString+iTotalLen+1)='\0';
                  
                  FixupDeviceId((PUCHAR)pTempString);
  
                  
                  RtlInitAnsiString(&AnsiIdString,pTempString);  //make a counted ansi string
                  if(!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeDeviceId,&AnsiIdString,TRUE))) //Make a unicode string out of this
                  {
                    ntStatus=STATUS_NO_MEMORY;
                    iReturnSize=-1;
                    Irp->IoStatus.Information=0;
                    break;
                  }
                  pWalk = UnicodeDeviceId.Buffer+iFirstLen; //Set a pointer to the beginning of the string
                  *pWalk=L'\0'; //set the space to be a unicode null
  
  #ifdef USBPRINT_LIE_ABOUT_LPT
                  pWalk = UnicodeDeviceId.Buffer+iSecondLen; //Set a pointer to the beginning of the string
                  *pWalk=L'\0'; //set the space to be a unicode null
  #endif
  
                  pWalk = UnicodeDeviceId.Buffer+iTotalLen; //set a pointer to the space at the end of the total string
                  *pWalk=L'\0';   //set the space to be a unicode null, so that we now have a double unicode null.
                  Irp->IoStatus.Information = (ULONG_PTR)UnicodeDeviceId.Buffer;
                  break;
                  
              case BusQueryCompatibleIDs:
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: Received BusQueryCompatibleIDs message\n")); 
                  Irp->IoStatus.Information = (ULONG_PTR) NULL; //(ULONG_PTR)UnicodeDeviceId.Buffer;
                  break;
  
              default:
               {
                  USBPRINT_KdPrint2 (("USBPRINT.SYS: Received unkown BusQuery message\n")); 
                  ntStatus = Irp->IoStatus.Status;
               }
              } /*end switch ID type*/
              
          }  /*end no 1284 ID*/
          else
          {
              ntStatus=STATUS_NOT_FOUND;
          }
      }
      if(pTempString!=NULL)
          ExFreePool(pTempString);
      if(pRawString!=NULL)
          ExFreePool(pRawString);
      return ntStatus;
  } /*End function QueryID*/
  
  
  //
  //  Function: bAddPortInUseItem
  // 
  //  Description : iPortNumber is removed from the free ports list structure.
  //  
  //  Parameters: IN\OUT pFreePorts - is the beginning of the list and on return will contain the beginning of the list.
  //                                  pFreePorts may change during the call.
  //              IN iPortNumber - the port number that is in use.
  //
  //  Returns: NTSTATUS value - STATUS_NO_MEMORY
  //                          - STATUS_SUCCESS
  //
  NTSTATUS bAddPortInUseItem(PFREE_PORTS * pFreePorts,ULONG iPortNumber )
  {
      NTSTATUS ntstatus     = STATUS_SUCCESS;
      PFREE_PORTS pBefore   = *pFreePorts;
      PFREE_PORTS pHead     = *pFreePorts;
      PFREE_PORTS pNewBlock = NULL;
  
      USBPRINT_KdPrint2 (("  USBPRINT.SYS:  Head of bAddPortInUseItem\n"));  
      
      //
      // Traverse the FREE_PORT structure to remove the port number from the list.
      // Note - This function will not be needed to be called by anyone else other than LoadPortsUsed
      // as the GetNewPortNumber will do this functionality automatically.
      //
      while( *pFreePorts )
      {
          if( iPortNumber >= (*pFreePorts)->iBottomOfRange && iPortNumber <= (*pFreePorts)->iTopOfRange )
          {
              // We're where we want to be - so decide what to do...
              if( iPortNumber == (*pFreePorts)->iBottomOfRange )
              {
                  if( (++((*pFreePorts)->iBottomOfRange)) > (*pFreePorts)->iTopOfRange )
                  {
                      // Case of the Port Number being the first and only element in the first block.
                      if( *pFreePorts == pHead )
                      {
                          pHead = (*pFreePorts)->pNextBlock;
                      }
                      else    // Case of the Port Number being the first element in another block.
                      {
                          pBefore->pNextBlock = (*pFreePorts)->pNextBlock;
                      }
                      ExFreePool( *pFreePorts );
                  }
              }
              else 
              {
                  if( iPortNumber == (*pFreePorts)->iTopOfRange )
                  {   // Deletion case handled in the above case, so just need to decrement.
                      ((*pFreePorts)->iTopOfRange)--;
                  }
                  else    // Otherwise we're in the middle of the block and we need to split it.
                  {
                      pNewBlock = ExAllocatePoolWithTag( NonPagedPool, sizeof(FREE_PORTS), USBP_TAG);
                      if( !pNewBlock )
                      {
                          ntstatus = STATUS_NO_MEMORY;
                          goto Cleanup;
                      }
  
                      pNewBlock->iTopOfRange    = (*pFreePorts)->iTopOfRange;
                      (*pFreePorts)->iTopOfRange   = iPortNumber - 1;
                      pNewBlock->iBottomOfRange = iPortNumber + 1;
                      pNewBlock->pNextBlock     = (*pFreePorts)->pNextBlock;
                      (*pFreePorts)->pNextBlock    = pNewBlock;
                  }
              }
              break;
          }
          else
          {
              if( iPortNumber < (*pFreePorts)->iBottomOfRange )
              {   // The port number has already been used - not in the free list.
                  USBPRINT_KdPrint2 (("  USBPRINT.SYS:  Port number %n is allocated already from free list.\n", iPortNumber));
                  break;
              }
              pBefore = *pFreePorts;
              *pFreePorts = (*pFreePorts)->pNextBlock;
          }
      }
  
      if( NULL == *pFreePorts )
      {
          ntstatus = STATUS_INVALID_PARAMETER;
          // Assert this as we could never allocate a port number that is not in the initial ranges 1-999 
          //     - but if we assert here, we have run off the end of the port allocation numbers.
          ASSERT( *pFreePorts );
      }
  
  Cleanup:
      *pFreePorts = pHead;
  
      return ntstatus;
  
  } /*end function bAddPortInUseItem*/
  
  
  void vClaimPortNumber(ULONG ulPortNumber,HANDLE hInterfaceKey,PFREE_PORTS * pPortsUsed)
  {
      UNICODE_STRING wRecycle;
      WCHAR *pName;
  
      pName=L"RECYCLABLE";
      RtlInitUnicodeString(&wRecycle,pName);
      #if WIN95_BUILD==1
      SetValueToZero(hInterfaceKey,&wRecycle);
      #else
      ZwDeleteValueKey(hInterfaceKey,&wRecycle);
      #endif
  
      // Do we need to fail out gracefully from the below?  
      // The func doesn't have a return, but we could fail a mem alloc inside the below call!!
  //    bAddPortInUseItem(pPortsUsed,ulPortNumber);
  } /*end function vClaimPortNumber*/
  
  
  NTSTATUS GetNewPortNumber(PFREE_PORTS * pFreePorts, ULONG *pulPortNumber)
  {
      NTSTATUS ntstatus = STATUS_SUCCESS;
      PFREE_PORTS pTemp  = *pFreePorts;
  
      USBPRINT_KdPrint2 (("USBPRINT.SYS: Head of GetNewPortNumber\n"));
  
      if( NULL == *pFreePorts )
      {
          // If the pFreePorts list is empty - try to reconstruct it.
          ntstatus=InitFreePorts(pFreePorts);
          if(NT_SUCCESS(ntstatus))
              ntstatus=LoadPortsUsed((GUID *)&USBPRINT_GUID,pFreePorts,USB_BASE_NAME);
          if( NULL == *pFreePorts && NT_SUCCESS(ntstatus))
          {
              ntstatus=STATUS_INVALID_PORT_HANDLE;    
          }
  
          if(!NT_SUCCESS(ntstatus)) 
          {
              *pulPortNumber = 0;
              goto Cleanup;
          }
      }
  
      *pulPortNumber = (*pFreePorts)->iBottomOfRange;
  
      if( (++((*pFreePorts)->iBottomOfRange)) > (*pFreePorts)->iTopOfRange )
      {
          // Case of the Port Number being the first and only element in the first block.
          *pFreePorts = (*pFreePorts)->pNextBlock;
          ExFreePool( pTemp );
      }
  
  Cleanup:
  
      return ntstatus;
  
  } /*end function GetNewPortNumber*/
  
  
  BOOL bDeleteIfRecyclable(HANDLE hRegKey)
  {
      BOOL bReturn=FALSE;
      UNICODE_STRING wcValueName;
      NTSTATUS ntStatus;
      USBPRINT_KdPrint2 (("USBPRINT.SYS:  Head of bDeleteifRecyclable\n"));
      RtlInitUnicodeString(&wcValueName,L"recyclable");
      #if WIN95_BUILD==1
      ntStatus=SetValueToZero(hRegKey,&wcValueName);
      #else
      ntStatus=ZwDeleteValueKey(hRegKey,&wcValueName);
      #endif
      if(NT_SUCCESS(ntStatus))
      {
          RtlInitUnicodeString(&wcValueName,L"Port Number");
          #if WIN95_BUILD==1
          ntStatus=SetValueToZero(hRegKey,&wcValueName);
          #else
          ntStatus=ZwDeleteValueKey(hRegKey,&wcValueName);
          #endif
          if(NT_SUCCESS(ntStatus)) 
              bReturn=TRUE;
      } // end function bDeleteIfRecyclable
      if(bReturn)
      {
          USBPRINT_KdPrint3 (("USBPRINT.SYS: bDeleteIfRecyclable, returning TRUE\n"));
      }
      else
      {
          USBPRINT_KdPrint3 (("USBPRINT.SYS: bDeleteIfRecyclable, returning FALSE\n"));
      }
      return bReturn;
  } //End function bDeleteIfRecycable
     
  //
  // Initialises the free ports structure list.
  // pHead must be NULL or a valid pointer to a FREE_PORTS structure.
  //
  NTSTATUS InitFreePorts( PFREE_PORTS * pHead )
  {
      PFREE_PORTS pNext = *pHead;
      NTSTATUS ntstatus = STATUS_SUCCESS;
  
      while(pNext)
      {
          pNext = (*pHead)->pNextBlock;
          ExFreePool(*pHead);
          *pHead = pNext;
      }
  
      //
      // Any old list will be cleared from memory and pHead will be NULL
      //
  
      *pHead = ExAllocatePoolWithTag(NonPagedPool, sizeof(FREE_PORTS), USBP_TAG);
      if( *pHead )
      {
          (*pHead)->iBottomOfRange = MIN_PORT_NUMBER;
          (*pHead)->iTopOfRange = MAX_PORT_NUMBER;
          (*pHead)->pNextBlock = NULL;
      }
      else
          ntstatus = STATUS_NO_MEMORY;
  
      return ntstatus;
  }
  
  void ClearFreePorts( PFREE_PORTS * pHead )
  {
      PFREE_PORTS pTemp = *pHead;
  
      while( *pHead )
      {
          *pHead = (*pHead)->pNextBlock;
          ExFreePool( pTemp );
          pTemp = *pHead;
      }
  }
  
  
  /********************************************************
   * SetValueToZero.  Sets and interger reg key to zero.    
   * Returns failure if reg key does not exist, or if     
   * The key already is set to zero.  Mimics ZwDeleteValueKey
   * (which is not currently avaiable on Milinium) by 
   * useing the value 0 to mean deleted
   **************************************************************/
  NTSTATUS SetValueToZero(HANDLE hRegKey,PUNICODE_STRING ValueName)
  {
      PKEY_VALUE_PARTIAL_INFORMATION pValueStruct;
      NTSTATUS ReturnCode;
      ULONG dwZero=0;
      ULONG ulSizeUsed;
      NTSTATUS ntStatus;
      int iValue;
  
      ulSizeUsed=sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG); //this is a byte to much.  Oh well
      pValueStruct=ExAllocatePool(NonPagedPool,ulSizeUsed); 
      if(pValueStruct==NULL)
      {
        USBPRINT_KdPrint1(("USBPRINT.SYS: SetValueToZero; Unable to allocate memory\n"));
        return STATUS_NO_MEMORY;
       
      }
      ntStatus=ZwQueryValueKey(hRegKey,ValueName,KeyValuePartialInformation,pValueStruct,ulSizeUsed,&ulSizeUsed);
      if(!NT_SUCCESS(ntStatus))
      {
  	  USBPRINT_KdPrint3(("Failed to Query value Key\n"));
        ExFreePool(pValueStruct);
        return STATUS_OBJECT_NAME_NOT_FOUND;
      }
      iValue=(int)*((ULONG *)(pValueStruct->Data));
      ExFreePool(pValueStruct);
      if(iValue==0)
          return STATUS_OBJECT_NAME_NOT_FOUND;
  
      //if we make it to here, the value exists, and is nonzero
      ReturnCode=ZwSetValueKey(hRegKey,ValueName,0,REG_DWORD,&dwZero,sizeof(dwZero));
      return ReturnCode;
  } /*end function SetValueToZero*/
  
  VOID
  USBPRINT_FdoIdleNotificationCallback(IN PDEVICE_EXTENSION DevExt)
  /*++
  
  Routine Description:
  
      Called when it is time to idle out USB printer
  
  
  --*/
  {
      POWER_STATE 	powerState;
      NTSTATUS 		ntStatus;
  
      USBPRINT_KdPrint1(("USB Printer (%08X) going idle\n", DevExt));
  
      if(!DevExt->AcceptingRequests ||  DevExt->OpenCnt) 
      {
  
          // Don't idle this printer if the printer is not accepting requests
  
          USBPRINT_KdPrint1(("USB Printer (%08X) not accepting requests, abort idle\n", DevExt));
          return;
      }
  
  
      powerState.DeviceState = DevExt->DeviceWake;
  
  	// request new device power state, wait wake Irp will be posted on request
      PoRequestPowerIrp(DevExt->PhysicalDeviceObject,
                        IRP_MN_SET_POWER,
                        powerState,
                        NULL,
                        NULL,
                        NULL);
  
  } // USBPRINT_FdoIdleNotificationCallback
  
  
  NTSTATUS
  USBPRINT_FdoIdleNotificationRequestComplete(
      PDEVICE_OBJECT DeviceObject,
      PIRP Irp,
      PDEVICE_EXTENSION DevExt
      )
  /*++
  
  Routine Description:
  
      Completion routine for the Idle request IRP for the USB printer device
  
  --*/
  {
      NTSTATUS 					ntStatus;
      PUSB_IDLE_CALLBACK_INFO 	idleCallbackInfo;
  
      //
      // DeviceObject is NULL because we sent the irp
      //
      UNREFERENCED_PARAMETER(DeviceObject);
  
      USBPRINT_KdPrint1(("Idle notification IRP for USB Printer (%08X) completed (%08X)\n",
              DevExt, Irp->IoStatus.Status));
  
  	// save completion status in device extension
      idleCallbackInfo 			= DevExt->IdleCallbackInfo;
      DevExt->IdleCallbackInfo 	= NULL;
      DevExt->PendingIdleIrp 		= NULL;
  
  	// free up callback info
      if(idleCallbackInfo) 
      {
          ExFreePool(idleCallbackInfo);
      }
  
      ntStatus = Irp->IoStatus.Status;
  
      return ntStatus;
  } // USBPRINT_FdoIdleNotificationRequestComplete
  
  
  NTSTATUS
  USBPRINT_FdoSubmitIdleRequestIrp(IN PDEVICE_EXTENSION DevExt)
  /*++
  
  Routine Description:
  
      Called when all handles to the USB printer are closed. This function allocates 
      an idle request IOCTL IRP and passes it to the parent's PDO.
  
  --*/
  {
      PIRP 					irp = NULL;
      NTSTATUS 				ntStatus = STATUS_SUCCESS;
      PUSB_IDLE_CALLBACK_INFO idleCallbackInfo = NULL;
  
      USBPRINT_KdPrint1(("USBPRINT_FdoSubmitIdleRequestIrp (%08X)\n", DevExt));
  
      // if we have an Irp pending, don't bother to send another
      if(DevExt->PendingIdleIrp || DevExt->CurrentDevicePowerState == DevExt->DeviceWake)
          return ntStatus;
  
      idleCallbackInfo = ExAllocatePoolWithTag(NonPagedPool,
                                               sizeof(struct _USB_IDLE_CALLBACK_INFO), USBP_TAG);
  
      if (idleCallbackInfo) 
      {
  
          idleCallbackInfo->IdleCallback 	= USBPRINT_FdoIdleNotificationCallback;
          idleCallbackInfo->IdleContext 	= (PVOID)DevExt;
  
          DevExt->IdleCallbackInfo = idleCallbackInfo;
  
          irp = IoBuildDeviceIoControlRequest(
                  IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION,
                  DevExt->PhysicalDeviceObject,
                  idleCallbackInfo,
                  sizeof(struct _USB_IDLE_CALLBACK_INFO),
                  NULL,
                  0,
                  TRUE, /* INTERNAL */
                  NULL,
                  NULL);
  
          if (irp == NULL) 
          {
          
              ExFreePool(idleCallbackInfo);
              return STATUS_INSUFFICIENT_RESOURCES;
          }
  
          IoSetCompletionRoutine(irp,
                                 USBPRINT_FdoIdleNotificationRequestComplete,
                                 DevExt,
                                 TRUE,
                                 TRUE,
                                 TRUE);
  
          ntStatus = IoCallDriver(DevExt->PhysicalDeviceObject, irp);
  
          if(ntStatus == STATUS_PENDING) 
          {
              // Successfully posted an Idle IRP.
  
              DevExt->PendingIdleIrp 	= irp;
          }
      }
  
      return ntStatus;
  } // USBPRINT_FdoSubmitIdleRequestIrp
  
  VOID
  USBPRINT_FdoRequestWake(IN PDEVICE_EXTENSION DevExt)
  /*++
  
  Routine Description:
  
  	Called when we want to wake up the device after an idle request
  
  --*/
  {
      POWER_STATE 	powerState;
      KIRQL OldIrql;
      BOOL bExit=FALSE;
  
      USBPRINT_KdPrint1(("USBPRINT: USB Printer (%08X) waking up\n", DevExt));
  
      KeAcquireSpinLock(&(DevExt->WakeSpinLock),&OldIrql);
      if(!DevExt->AcceptingRequests || DevExt->CurrentDevicePowerState == PowerDeviceD0 || DevExt->bD0IrpPending) 
      {
  
          // Don't wake this printer if it's not accepting requests or we're already at power state D0
          if(!DevExt->AcceptingRequests)
            USBPRINT_KdPrint1(("USBPRINT: USB Printer (%08X) not accepting requests, abort wake\n", DevExt));
          if(DevExt->CurrentDevicePowerState == PowerDeviceD0)
            USBPRINT_KdPrint1(("USBPRINT: USB Printer (%08X) already at D0, abort wake\n", DevExt));
          if(DevExt->bD0IrpPending == TRUE)
            USBPRINT_KdPrint1(("USBPRINT: USB Printer (%08X) already has D0 irp pending, abort wake\n", DevExt));
          bExit=TRUE;
      }
      else
        DevExt->bD0IrpPending=TRUE;  
      KeReleaseSpinLock(&(DevExt->WakeSpinLock),OldIrql);
      if(bExit)
          return;
  
  
  
      
      powerState.DeviceState = PowerDeviceD0;
  
  	// request new device power state, wake up the device
  
   
      PoRequestPowerIrp(DevExt->PhysicalDeviceObject,
                        IRP_MN_SET_POWER,
                        powerState,
                        NULL,
                        NULL,
                        NULL);
  
  } // USBPRINT_FdoRequestWake
  
  
  void vOpenLogFile(HANDLE *pHandle)
  {
      NTSTATUS ntStatus;
      OBJECT_ATTRIBUTES FileAttributes;
      IO_STATUS_BLOCK StatusBlock;
      UNICODE_STRING PathName;
  
      RtlInitUnicodeString(&PathName,L"\\??\\C:\\USBPRINT.LOG");
  
      InitializeObjectAttributes(&FileAttributes,&PathName,0,NULL,NULL);
      ntStatus=ZwCreateFile(pHandle,
                            GENERIC_WRITE,
                            &FileAttributes,
                            &StatusBlock,
                            0,
                            0,
                            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                            FILE_OPEN_IF,
                            FILE_NON_DIRECTORY_FILE|FILE_WRITE_THROUGH|FILE_SYNCHRONOUS_IO_NONALERT,
                            NULL,
                            0);
      if(!NT_SUCCESS(ntStatus))
      {
          USBPRINT_KdPrint1(("USBPRINT: Unable to open C:\\USBPRINT.LOG"));
      }
      else
      {
          USBPRINT_KdPrint1(("USBPRINT: Opened logfile C:\\USBPRINT.LOG")); /*dd*/
      }
  }
  
  
  void vWriteToLogFile(HANDLE *pHandle,IN CHAR *pszString)
  {
      HANDLE hFileHandle;
      ULONG BufferSize;
      NTSTATUS ntStatus;
      IO_STATUS_BLOCK StatusBlock;
      LARGE_INTEGER WriteOffset;
  
      WriteOffset.LowPart=FILE_WRITE_TO_END_OF_FILE;
      WriteOffset.HighPart=-1;
      
      BufferSize=strlen(pszString);
      ntStatus=ZwWriteFile(*pHandle,
                           NULL,
                           NULL,
                           NULL,
                           &StatusBlock,
                           pszString,
                           BufferSize,
                           &WriteOffset,
                           NULL);
  
      if(!NT_SUCCESS(ntStatus))
      {
          USBPRINT_KdPrint1(("USBPRINT: Unable to write to log file C:\\USBPRINT.LOG"));
      }
      else
      {
          USBPRINT_KdPrint1(("USBPRINT: write to log file C:\\USBPRINT.LOG")); /*dd*/
      }
  } /*end function vWriteToLog*/
  
  
  void vCloseLogFile(IN HANDLE *pHandle)
  {
    ZwClose(*pHandle);
  } /*end function vCloseLogFile*/
  
  
  
  
  