/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    usbdiag.c

Abstract:

    USB device driver for Intel/Microsoft USB diagnostic apps 

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
    7-21-97 : Todd Carpenter adds Chap11 IOCTL's

--*/

#define DRIVER

#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"

#pragma pack (push,1)
#include "usb100.h"
#include "usbdi.h"
#include "usbdlib.h"
#include "usbioctl.h"
#pragma pack (pop) //disable 1-byte alignment
 
#include "opaque.h"

#pragma pack (push,1)
#include "ioctl.h"
#include "chap9drv.h"
#include "USBDIAG.h"
#pragma pack (pop) //disable 1-byte alignment

PDEVICE_OBJECT          USBDIAG_GlobalDeviceObject = NULL;

ULONG USBDIAG_NextDeviceNumber = 0;
ULONG USBDIAG_NumberDevices = 0;
ULONG gulMemoryAllocated = 0;

USBD_VERSION_INFORMATION gVersionInformation;


/* UCHAR *SystemPowerStateString[] = {
   "PowerSystemUnspecified",
   "PowerSystemWorking",
   "PowerSystemSleeping1",
   "PowerSystemSleeping2",
   "PowerSystemSleeping3",
   "PowerSystemHibernate",
   "PowerSystemShutdown",
   "PowerSystemMaximum"
};

UCHAR *DevicePowerStateString[] = {
   "PowerDeviceUnspecified",
   "PowerDeviceD0",
   "PowerDeviceD1",
   "PowerDeviceD2",
   "PowerDeviceD3",
   "PowerDeviceMaximum"
}; */



//
// Global pointer to Driver Object
//

PDRIVER_OBJECT USBDIAG_DriverObject;

#define REMOTE_WAKEUP 0x20

#ifdef PAGE_CODE
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBD_SubmitSynchronousURB)
#pragma alloc_text(PAGE, USBD_CloseEndpoint)
#endif
#endif


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

Routine Description:

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

Arguments:

    DriverObject - pointer to the driver object

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

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    //PDEVICE_OBJECT deviceObject = NULL;

    DbgPrint("USBDIAG.SYS: entering (USBDIAG) DriverEntry\n");
    DbgPrint("USBDIAG.SYS: USBDIAG Driver Build Date/Time: %s  %s\n",
              __DATE__, __TIME__);

    USBDIAG_DriverObject = DriverObject;

    //
    // Create dispatch points for device control, create, close.
    //

    DriverObject->MajorFunction[IRP_MJ_PNP]     = USBDIAG_PnP;
    DriverObject->MajorFunction[IRP_MJ_POWER]   = USBDIAG_Power;
                
    DriverObject->MajorFunction[IRP_MJ_CREATE]  =
    DriverObject->MajorFunction[IRP_MJ_CLOSE]   =  USBDIAG_Dispatch;

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  =  USBDIAG_ProcessIOCTL;
    DriverObject->MajorFunction[IRP_MJ_WRITE]   =  NULL;
    DriverObject->MajorFunction[IRP_MJ_READ]    =  NULL;
    DriverObject->DriverUnload                  =  USBDIAG_Unload;
                                            
    DriverObject->DriverExtension->AddDevice    = USBDIAG_PnPAddDevice;

    DbgPrint ("'USBDIAG.SYS: exiting (USBDIAG) DriverEntry (%x)\n", ntStatus);


    // determine the os version and store in a global.
    USBD_GetUSBDIVersion(&gVersionInformation);

    return ntStatus;
}


NTSTATUS
USBDIAG_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;
    NTSTATUS ntStatus;
    PDEVICE_EXTENSION globalDeviceExtension;
    
    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_Dispatch\n"));

    //
    // Default return status unless overridden later
    //
    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);

    switch (irpStack->MajorFunction) {
    case IRP_MJ_CREATE:
        //USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MJ_CREATE\n"));
        ASSERT(USBDIAG_GlobalDeviceObject != NULL);
        globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;
        globalDeviceExtension->OpenFRC++;
                break;
                
    case IRP_MJ_CLOSE:
        //USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MJ_CLOSE\n"));
        ASSERT(USBDIAG_GlobalDeviceObject != NULL);
        globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;
        globalDeviceExtension->OpenFRC--;
        break;

    default:
        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;    
        break;
        
    } /* case MajorFunction */

    ntStatus = Irp->IoStatus.Status;

    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    //USBDIAG_KdPrint(("USBDIAG.SYS: Exit USBDIAG_Dispatch %x\n", ntStatus));
    
    return ntStatus;
}


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

Routine Description:

    Process the PnP IRPs sent to this device.

Arguments:

    DeviceObject - pointer to a device object

    Irp          - pointer to an I/O Request Packet

Return Value:


--*/
{
    NTSTATUS            ntStatus;
    PIO_STACK_LOCATION  irpStack;
    PDEVICE_EXTENSION   deviceExtension;
    PDEVICE_EXTENSION   globalDeviceExtension;
    PDEVICE_LIST_ENTRY  device, foundDevice;
    BOOLEAN             passDownIrp;


    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_PnP\n"));

    //
    // Default to passing down all Irps unless overridden later.
    //
    passDownIrp = TRUE;

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

    ASSERT (deviceExtension != NULL);

    //
    // Get a pointer to the global device extension
    //
    globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;
    
    //
    // Switch on the PnP minor function
    //
    switch (irpStack->MinorFunction) {
        case IRP_MN_START_DEVICE:
            //USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MN_START_DEVICE\n"));
            ntStatus = USBDIAG_PassDownIrp(DeviceObject, Irp);
            USBDIAG_KdPrint (("Back from passing down IRP_MN_START_DEVICE; status: %#X\n",
                              ntStatus));

            if (NT_SUCCESS(ntStatus))
            {
                // Now we can begin our configuration actions on the device
                //
                ntStatus = USBDIAG_StartDevice(DeviceObject);
            }
            passDownIrp = FALSE;
            break;
        
        case IRP_MN_QUERY_CAPABILITIES: // 0x09
            USBDIAG_KdPrint (("*********************************\n"));
            USBDIAG_KdPrint (("IRP_MN_QUERY_CAPABILITIES\n"));
            passDownIrp = FALSE;

            IoCopyCurrentIrpStackLocationToNext(Irp);
            IoSetCompletionRoutine(Irp,
                                   USBDIAG_QueryCapabilitiesCompletionRoutine,
                                   DeviceObject,
                                   TRUE,
                                   TRUE,
                                   TRUE);

            ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp);
            break;


        case IRP_MN_STOP_DEVICE:
            //USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MN_STOP_DEVICE\n"));
            ntStatus = USBDIAG_StopDevice(DeviceObject);
            break;

        case IRP_MN_REMOVE_DEVICE:
            {
                int i = 1;
                //USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MN_REMOVE_DEVICE\n"));
                ntStatus = USBDIAG_StopDevice(DeviceObject);

                // remove all downstream devices
                for (i = 1; i <= MAX_DOWNSTREAM_DEVICES; i++)
                {
                    if (deviceExtension->DeviceData[i])
                    {
                        USBDIAG_KdPrint(("IRP_MN_REMOVE_DEVICE: Removing device on downstream port %d\n", i));
                        USBDIAG_RemoveDownstreamDevice(deviceExtension->DeviceData[i],
                                                       deviceExtension->StackDeviceObject);

                    }
                }
                USBDIAG_KdPrint(("Done removing downstream devices\n"));
                //
                // Remove this device object from our list
                //

                device = globalDeviceExtension->DeviceList;

                USBDIAG_KdPrint(("USBDIAG.SYS: IRP_MN_REMOVE_DEVICE devobj = %x dev = %x\n", 
                                  DeviceObject, device));

                ASSERT(device != NULL);

                if (device->DeviceObject == DeviceObject) {
                    //
                    // DeviceObject is the first one on the list.  Delete from the
                    // list by setting the head of the list to point to the next
                    // one on the list.
                    //
                    globalDeviceExtension->DeviceList = device->Next;                

                    USBDIAG_ExFreePool(device);
                
                } else {
                    //
                    // DeviceObject is not the first one on the list.  Walk the
                    // list and find it.
                    //
                    while (device->Next) {
                        if (device->Next->DeviceObject == DeviceObject) {
                            //
                            // DeviceObject is the next one on the list, remember
                            // the next one and delete it by setting the next one
                            // to the next next one.
                            //
                            foundDevice = device->Next;
                            device->Next = foundDevice->Next;

                            USBDIAG_ExFreePool(foundDevice);
                            break;
                                }
                        device = device->Next;
                    }
                }

                //USBDIAG_KdPrint(("USBDIAG.SYS: Detaching stack device object...%X\n",
                                  //deviceExtension->StackDeviceObject));
                IoDetachDevice(deviceExtension->StackDeviceObject);

                //
                // Pass the REMOVE_DEVICE Irp down now after detaching and
                // removing the device instead of later.
                //
                passDownIrp = FALSE;

                IoSkipCurrentIrpStackLocation (Irp);

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

                //USBDIAG_KdPrint(("USBDIAG.SYS: Deleting device object...%X\n", DeviceObject));
                IoDeleteDevice(DeviceObject);

                USBDIAG_NumberDevices--;

                //
                // Free the GlobalDeviceObject if this was the last real
                // DeviceObject.   XXXXX Take a careful look at what the
                // hell this routine is all about.
                //
                USBDIAG_RemoveGlobalDeviceObject();
            }
            break;
            
        default:
            //USBDIAG_KdPrint(("USBDIAG.SYS: PnP IOCTL not handled: (%#X)\n",
                              //irpStack->MinorFunction));
            break;
    }

    if (passDownIrp)
    {
        //
        // Pass the PnP Irp down the stack
        //
        IoSkipCurrentIrpStackLocation (Irp);
        ntStatus = IoCallDriver(deviceExtension->StackDeviceObject,
                                Irp);
    }

    //USBDIAG_KdPrint(("USBDIAG.SYS: Exit USBDIAG_PnP %x\n", ntStatus));
    
    return ntStatus;
}

NTSTATUS
USBDIAG_PassDownIrp (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp
    )
{
    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
    KEVENT              localevent;
    NTSTATUS            ntStatus;

    PAGED_CODE();

    // Initialize the event we'll wait on
    //
    KeInitializeEvent(&localevent,
                      SynchronizationEvent,
                      FALSE);

    // Copy down Irp params for the next driver
    //
    IoCopyCurrentIrpStackLocationToNext(Irp);

    // Set the completion routine, which will signal the event
    //
    IoSetCompletionRoutine(Irp,
                           USBDIAG_GenericCompletionRoutine,
                           &localevent,
                           TRUE,    // InvokeOnSuccess
                           TRUE,    // InvokeOnError
                           TRUE);   // InvokeOnCancel

    // Pass the Irp down the stack
    //
    ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp);

    // If the request is pending, block until it completes
    //
    if (ntStatus == STATUS_PENDING)
    {
        KeWaitForSingleObject(&localevent,
                              Executive,
                              KernelMode,
                              FALSE,
                              NULL);

        ntStatus = Irp->IoStatus.Status;
    }

    return ntStatus;
}

NTSTATUS
USBDIAG_GenericCompletionRoutine(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{
    PKEVENT kevent;

    kevent = (PKEVENT)Context;
    KeSetEvent(kevent, IO_NO_INCREMENT,FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}


VOID
USBDIAG_Unload(
    IN PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    Free all the allocated resources, etc.

Arguments:

    DriverObject - pointer to a driver object

Return Value:


--*/
{
    DbgPrint ("USBDIAG.SYS: enter USBDIAG_Unload\n");

    //
    // Free any global resources allocated
    // in DriverEntry
    //

    // free the global deviceobject here

        USBDIAG_RemoveGlobalDeviceObject();
    DbgPrint ("USBDIAG.SYS: exit USBDIAG_Unload\n");
}



NTSTATUS
USBDIAG_StartDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes a given instance of the device on the USB.

Arguments:

    DeviceObject - pointer to the device object for this instance of a 
                    UTB

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
    PURB urb;
    ULONG siz;

    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_StartDevice\n"));    

    //
    // Fetch the device descriptor for the device
    // 
    urb = USBDIAG_ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
                         
    if (urb) 
    {
        siz = sizeof(USB_DEVICE_DESCRIPTOR);

        deviceDescriptor = USBDIAG_ExAllocatePool(NonPagedPool, siz);

        if (deviceDescriptor) 
        {    
            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_DEVICE_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         deviceDescriptor,
                                         NULL,
                                         siz,
                                         NULL);
                                                                  
            ntStatus = USBDIAG_CallUSBD(DeviceObject, urb);

            if (NT_SUCCESS(ntStatus)) 
            {
                //USBDIAG_KdPrint(("USBDIAG.SYS: Device Descriptor = %x, len %x\n", 
                                //deviceDescriptor, 
                                //urb->UrbControlDescriptorRequest.TransferBufferLength));    

                //USBDIAG_KdPrint(("USBDIAG.SYS: USB Device Descriptor:\n"));
                //USBDIAG_KdPrint(("USBDIAG.SYS: -------------------------\n"));                
                //USBDIAG_KdPrint(("USBDIAG.SYS: bLength %d\n", deviceDescriptor->bLength));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bcdUSB 0x%x\n", deviceDescriptor->bcdUSB));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0));
                //USBDIAG_KdPrint(("USBDIAG.SYS: idVendor 0x%x\n", deviceDescriptor->idVendor));
                //USBDIAG_KdPrint(("USBDIAG.SYS: idProduct 0x%x\n", deviceDescriptor->idProduct));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bcdDevice 0x%x\n", deviceDescriptor->bcdDevice));
                //USBDIAG_KdPrint(("USBDIAG.SYS: iManufacturer 0x%x\n", deviceDescriptor->iManufacturer));
                //USBDIAG_KdPrint(("USBDIAG.SYS: iProduct 0x%x\n", deviceDescriptor->iProduct));
                //USBDIAG_KdPrint(("USBDIAG.SYS: iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber));
                //USBDIAG_KdPrint(("USBDIAG.SYS: bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations));
            }
        } 
        else 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }            
        
        if (NT_SUCCESS(ntStatus)) 
        {
            deviceExtension->pDeviceDescriptor = deviceDescriptor;
            deviceExtension->Stopped = FALSE;
        } 
        else if (deviceDescriptor) 
        {
             USBDIAG_ExFreePool(deviceDescriptor);
        }

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


    //USBDIAG_KdPrint(("USBDIAG.SYS: exit USBDIAG_StartDevice (%x)\n", ntStatus));

    return ntStatus;
}

NTSTATUS
USBDIAG_QueryCapabilitiesCompletionRoutine(
    IN PDEVICE_OBJECT NullDeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
{
    PDEVICE_OBJECT deviceObject       = (PDEVICE_OBJECT) Context;
    PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    ULONG ulPowerLevel;

    USBDIAG_KdPrint(("enter USBDIAG_QueryCapabilitiesCompletionRoutine (Irp->IoStatus.Status = 0x%x)\n", Irp->IoStatus.Status));

    //  If the lower driver returned PENDING, mark our stack location as pending also.
    if (Irp->PendingReturned) 
    {
        IoMarkIrpPending(Irp);
    }
    ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
    ASSERT(irpStack->MinorFunction == IRP_MN_QUERY_CAPABILITIES);

    irpStack = IoGetCurrentIrpStackLocation (Irp);

    USBDIAG_KdPrint(("sizeof(DEVICE_CAPABILITIES) = %d (0x%x)\n",sizeof(DEVICE_CAPABILITIES),sizeof(DEVICE_CAPABILITIES)));

    // this is for Win2k
    irpStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
    irpStack->Parameters.DeviceCapabilities.Capabilities->Removable         = TRUE;

    RtlCopyMemory(&deviceExtension->DeviceCapabilities, 
                  irpStack->Parameters.DeviceCapabilities.Capabilities, 
                  sizeof(DEVICE_CAPABILITIES));


    // print out capabilities info
    USBDIAG_KdPrint(("************ Device Capabilites ************\n"));
    USBDIAG_KdPrint(("SystemWake = 0x%x\n", deviceExtension->DeviceCapabilities.SystemWake));
    USBDIAG_KdPrint(("DeviceWake = 0x%x\n", deviceExtension->DeviceCapabilities.DeviceWake));

//    USBDIAG_KdPrint(("SystemWake = %s\n",
//                    SystemPowerStateString[deviceExtension->DeviceCapabilities.SystemWake]));
//    USBDIAG_KdPrint(("DeviceWake = %s\n",
//                    DevicePowerStateString[deviceExtension->DeviceCapabilities.DeviceWake]));

    USBDIAG_KdPrint(("Device Address: 0x%x\n", deviceExtension->DeviceCapabilities.Address));

    for (ulPowerLevel=PowerSystemUnspecified; ulPowerLevel< PowerSystemMaximum; ulPowerLevel++) 
    {
//        USBDIAG_KdPrint(("Dev State Map: sys st %s = dev st %s\n",
//                        SystemPowerStateString[ulPowerLevel],
//                        DevicePowerStateString[deviceExtension->DeviceCapabilities.DeviceState[ulPowerLevel]] ));
    }
    Irp->IoStatus.Status = STATUS_SUCCESS;

    return ntStatus;
}


NTSTATUS
USBDIAG_StopDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Stops a given instance of a UTB device on the 82930.

Arguments:

    DeviceObject - pointer to the device object for this instance of a 82930 

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    NTSTATUS          ntStatus        = STATUS_SUCCESS;

    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_StopDevice\n"));    

    if (deviceExtension->Stopped != TRUE)
    {
        ntStatus = USBDIAG_CancelAllIrps(deviceExtension);
        // 
        // if we are already stopped then just exit
        //

        if (deviceExtension->pDeviceDescriptor)
        {
            USBDIAG_ExFreePool(deviceExtension->pDeviceDescriptor);
            deviceExtension->pDeviceDescriptor = NULL;
        }
        deviceExtension->Stopped = TRUE;
    }

    //USBDIAG_KdPrint(("USBDIAG.SYS: exit USBDIAG_StopDevice (%x)\n", ntStatus));

    return ntStatus;
}



NTSTATUS
USBDIAG_PnPAddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN OUT 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 USBDIAG

    PhysicalDeviceObject - pointer to 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, globalDeviceExtension = NULL;
//    PDEVICE_OBJECT          tempdeviceObject = NULL;
    
    USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_PnPAddDevice\n"));

    //
    // Are we given the physical device object?
    //
    if (PhysicalDeviceObject) {

            if (USBDIAG_GlobalDeviceObject == NULL) {

                //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice Creating Global Device Object\n"));

                ntStatus = 
                    USBDIAG_CreateDeviceObject(DriverObject, &USBDIAG_GlobalDeviceObject, TRUE);
                    
                if (NT_SUCCESS(ntStatus)) {  
                    globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;
                    
                    USBDIAG_GlobalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
            
                    globalDeviceExtension->DeviceList = NULL;    
                    globalDeviceExtension->OpenFRC = 0;                    
                }                                        
            } 
            else 
            {
                 globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;
            }

            //
            // create our funtional device object (FDO)
            //

            if (NT_SUCCESS(ntStatus)) 
            {            
                //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice creating new USB device object\n"));

                ntStatus = USBDIAG_CreateDeviceObject(DriverObject, &deviceObject, FALSE);

                if (NT_SUCCESS(ntStatus)) 
                {
                    //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice DONE creating new USB device object\n"));
                }//if
            }
            if (NT_SUCCESS(ntStatus)) 
            {
                PDEVICE_LIST_ENTRY device;
                
                deviceExtension = deviceObject->DeviceExtension;
                RtlZeroMemory(deviceExtension->DeviceData, MAX_DOWNSTREAM_DEVICES * sizeof(PUSBD_DEVICE_DATA));

                // We support buffered I/O only
                deviceObject->Flags |= DO_BUFFERED_IO;

                // 
                // remember the Physical device Object
                //
                deviceExtension->PhysicalDeviceObject=PhysicalDeviceObject;

                //
                // Attach to the PDO
                //
                //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice attaching device to Stack.\n"));

                //
                // The stackdeviceobject is what we use to send Urbs down the stack
                //
                deviceExtension->StackDeviceObject =
                    IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);

                RtlZeroMemory(deviceExtension->DeviceData, MAX_DOWNSTREAM_DEVICES * sizeof(PUSBD_DEVICE_DATA));

                if ((deviceExtension->StackDeviceObject) != NULL) 
                {

                    //passed the IoAttachDeviceToDeviceStack call
                    //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice successfully attached device to stack\n"));
                    
                    USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice stackDevObj: %X\n",deviceExtension->StackDeviceObject));
                    
                    USBDIAG_KdPrint(("USBDIAG.SYS: Saving in deviceExtension 0x%x.\n",
                                      deviceExtension));

                    if (gVersionInformation.USBDI_Version >= USBD_WIN98_SE_VERSION) // Win98 SE, Win2K and beyond
                    {
                        device = USBDIAG_ExAllocatePool(NonPagedPool, sizeof(DEVICE_LIST_ENTRY));

                        if (device) 
                        {
                            PDEVICE_OBJECT RootHubPdo = NULL;
                            PDEVICE_OBJECT TopOfHcdStackDeviceObject = NULL;

                            ASSERT(globalDeviceExtension != NULL);
                            device->Next = globalDeviceExtension->DeviceList;
                            globalDeviceExtension->DeviceList = device;
                            device->DeviceNumber = USBDIAG_NextDeviceNumber++;
                            device->PhysicalDeviceObject = PhysicalDeviceObject;
                            device->DeviceObject = deviceObject;

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


                            // Get the RootHubPdo & TopOfHcdStackDeviceObject
                            ntStatus = USBDIAG_SyncGetRootHubPdo(deviceExtension->StackDeviceObject,
                                                                                                                         PhysicalDeviceObject,
                                                                 &RootHubPdo,
                                                                 &TopOfHcdStackDeviceObject);

                            if (NT_SUCCESS(ntStatus))
                            {
                                ASSERT(RootHubPdo);
                                deviceExtension->RootHubPdo = RootHubPdo;

                                //ASSERT(TopOfHcdStackDeviceObject);
                                //deviceExtension->TopOfHcdStackDeviceObject = TopOfHcdStackDeviceObject;
                            }
                            else
                            {
                                ASSERT(FALSE);
                                deviceExtension->RootHubPdo = NULL;
                                deviceExtension->TopOfHcdStackDeviceObject = NULL;

                            }
                            ntStatus = STATUS_SUCCESS;
                        } //if device allocate was successful
                    }
                        
                }//if attach device was successful
                else
                {
                    ntStatus = STATUS_NO_SUCH_DEVICE;
                    //USBDIAG_KdPrint(("USBDIAG.SYS: PnPAddDevice FAILED attaching device to stack\n"));
                } //else attach failed     

            }// if successfully created device object                

    } 
    else 
    {
        //
        // Given no physical device object, therefore asked to do detection.
        // This is a dream on as all USB controller are PCI devices.
        //

        ntStatus = STATUS_NO_MORE_ENTRIES;
    }//else no PDO given

    //USBDIAG_KdPrint(("USBDIAG.SYS: exit USBDIAG_PnPAddDevice (%x)\n", ntStatus));

        if(NT_SUCCESS(ntStatus))
                USBDIAG_NumberDevices++;

    return ntStatus;
}


NTSTATUS
USBDIAG_CreateDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT *DeviceObject,
    BOOLEAN Global
    )
/*++

Routine Description:

    Creates a Functional DeviceObject for the diag driver
    
Arguments:

    DriverObject - pointer to the driver object for device

    DeviceObject - pointer to DeviceObject pointer to return
                    created device object.

    Global - create the global device object and symbolic link.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus;
    WCHAR deviceLinkBuffer[]  = L"\\DosDevices\\USBDIAG";
    UNICODE_STRING deviceLinkUnicodeString;
    WCHAR deviceNameBuffer[]  = L"\\Device\\USBDIAG";
    UNICODE_STRING deviceNameUnicodeString;
    PDEVICE_EXTENSION deviceExtension;
    STRING deviceName;

    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_CreateDeviceObject\n"));


    //
    // fix up device names based on Instance
    //

    //USBDIAG_KdPrint(("USBDIAG.SYS: USBDIAG_CreateDeviceObject InitUnicode String\n"));

    RtlInitUnicodeString (&deviceNameUnicodeString,
                          deviceNameBuffer);


    //USBDIAG_KdPrint(("USBDIAG.SYS: USBDIAG_CreateDeviceObject Convert unicode to ansi\n"));

    //Print out the unicode string

    deviceName.Buffer = NULL;

    ntStatus = RtlUnicodeStringToAnsiString (&deviceName,
                                             &deviceNameUnicodeString, 
                                             TRUE);


    if (NT_SUCCESS(ntStatus)) {
        //USBDIAG_KdPrint(("USBDIAG.SYS: Create Device Name (%s)\n", deviceName.Buffer));
        RtlFreeAnsiString (&deviceName);
        if (!NT_SUCCESS(ntStatus)) {
            //USBDIAG_KdPrint(("USBDIAG.SYS: Failed freeing ansi string!\n"));
        }//if not successful ntstatus
    } else {
        //USBDIAG_KdPrint(("USBDIAG.SYS: Unicode to Ansi str failed w/ ntStatus: 0x%x\n",ntStatus));
    }

    
    //USBDIAG_KdPrint(("USBDIAG.SYS: USBDIAG_CreateDeviceObject IOCreateDevice \n"));

    ntStatus = IoCreateDevice (DriverObject,
                                   sizeof (DEVICE_EXTENSION),
                               Global ? &deviceNameUnicodeString : NULL,
                                   FILE_DEVICE_UNKNOWN,
                               0,
                                   FALSE,
                               DeviceObject);

    if (NT_SUCCESS(ntStatus)) {

        //
        // Initialize our device extension
        //
        
        deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);

        if (Global)
                {
            RtlInitUnicodeString (&deviceLinkUnicodeString,
                                  deviceLinkBuffer);
                                  
            //USBDIAG_KdPrint(("USBDIAG.SYS: Global: Create DosDevice name (%s)\n", deviceLinkBuffer));        
            
            ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
                                             &deviceNameUnicodeString);

            RtlCopyMemory(deviceExtension->DeviceLinkNameBuffer, 
                      deviceLinkBuffer,        
                      sizeof(deviceLinkBuffer));
        }//if Global
                else
                {
                deviceExtension->Stopped = TRUE;
            (*DeviceObject)->Flags |= DO_POWER_PAGABLE;
                        (*DeviceObject)->Flags &= ~DO_DEVICE_INITIALIZING;
                }//else


        //Setup the ptr to the device extension for this device & init the Irp field (for now)
        //deviceExtension->IrpHead = NULL;

        InitializeListHead(&deviceExtension->ListHead);
        KeInitializeSpinLock(&deviceExtension->SpinLock);
        KeInitializeEvent(&deviceExtension->CancelEvent, NotificationEvent, FALSE);

        // this event is triggered when self-requested power irps complete
        //KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);
        deviceExtension->SelfRequestedPowerIrpEvent = NULL;

        // initialize original power level as fully on
        deviceExtension->CurrentDeviceState.DeviceState = PowerDeviceD0;
        deviceExtension->CurrentSystemState.SystemState = PowerSystemWorking;

        deviceExtension->WaitWakeIrp  = NULL;
        deviceExtension->InterruptIrp = NULL;
   
    }//if ntsuccess 

    //USBDIAG_KdPrint(("USBDIAG.SYS: exit USBDIAG_CreateDeviceObject (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
USBDIAG_CallUSBD(
    IN PDEVICE_OBJECT DeviceObject,
    IN PURB Urb
    )
/*++

Routine Description:

    Passes a URB to the USBD class driver

Arguments:

    DeviceObject - pointer to the device object for this instance of an 82930  

    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;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;

        //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_CallUSBD\n"));

    deviceExtension = DeviceObject->DeviceExtension;

    //
    // issue a synchronous request   
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    irp = IoBuildDeviceIoControlRequest(
                IOCTL_INTERNAL_USB_SUBMIT_URB,
                deviceExtension->StackDeviceObject,
                NULL,
                0,
                NULL,
                0,
                TRUE, /* INTERNAL */
                &event,
                &ioStatus);

    //
    // 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->Parameters.Others.Argument1 = Urb;
    

    //USBDIAG_KdPrint(("USBDIAG.SYS: calling USBD\n"));

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

    //USBDIAG_KdPrint(("USBDIAG.SYS: return from IoCallDriver USBD %x\n", ntStatus));

        {
                KIRQL irql;
                irql = KeGetCurrentIrql();
                ASSERT(irql <= PASSIVE_LEVEL);
        }

    if (ntStatus == STATUS_PENDING) {
        //USBDIAG_KdPrint(("USBDIAG.SYS: Wait for single object\n"));

        status = KeWaitForSingleObject(
                       &event,
                       Suspended,
                       KernelMode,
                       FALSE,
                       NULL);    

        //USBDIAG_KdPrint(("USBDIAG.SYS: Wait for single object, returned %x\n", status));

    } else {
        ioStatus.Status = ntStatus;        
    }        

    //USBDIAG_KdPrint(("USBDIAG.SYS: URB status = %x status = %x irp status %x\n", 
//        Urb->UrbHeader.Status, status, ioStatus.Status));

    //
    // USBD maps the error code for us
    //
    ntStatus = ioStatus.Status;

        //USBDIAG_KdPrint(("USBDIAG.SYS: exit USBDIAG_CallUSBD (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
USBDIAG_RemoveGlobalDeviceObject(
    )
/*++

Routine Description:

Arguments:

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION globalDeviceExtension;
    UNICODE_STRING deviceLinkUnicodeString;
        KIRQL irql;

    //USBDIAG_KdPrint(("USBDIAG.SYS: enter USBDIAG_RemoveGlobalDeviceObjec\n"));    

        irql = KeGetCurrentIrql();
        ASSERT(irql <= PASSIVE_LEVEL);

    if (USBDIAG_GlobalDeviceObject != NULL) {
    
        globalDeviceExtension = USBDIAG_GlobalDeviceObject->DeviceExtension;

        //USBDIAG_KdPrint(("USBDIAG.SYS: USBDIAG_RemoveGlobalDeviceObject open frc = %x, list = %x\n",
                          //globalDeviceExtension->OpenFRC,
                          //globalDeviceExtension->DeviceList));    

        if ( globalDeviceExtension->DeviceList == NULL && 
             globalDeviceExtension->OpenFRC == 0) {

            //USBDIAG_KdPrint(("USBDIAG.SYS: Deleting global device object\n"));

            // delete our global device object once we have no more devices
        
            RtlInitUnicodeString (&deviceLinkUnicodeString,
                                  globalDeviceExtension->DeviceLinkNameBuffer);

            //USBDIAG_KdPrint(("USBDIAG.SYS: Deleting Symbolic Link (UnicodeStr) at addr %X\n",
                              //&deviceLinkUnicodeString));

            ntStatus = IoDeleteSymbolicLink(&deviceLinkUnicodeString);

            if (NT_SUCCESS(ntStatus)) {

                //USBDIAG_KdPrint(("USBDIAG.SYS: Deleting Global Device Object at addr %X\n",
                                //  USBDIAG_GlobalDeviceObject));

                IoDeleteDevice( USBDIAG_GlobalDeviceObject );
    
                USBDIAG_GlobalDeviceObject = NULL;

                //USBDIAG_KdPrint(("USBDIAG.SYS: Successfully Deleted Global Device Object\n"));
            
            }            
        }
    }

    return ntStatus;
}


#define MEM_SIGNATURE                   ((ULONG) 'CLLA')
#define MEM_FREED_SIGNATURE             ((ULONG) 'EERF')


PVOID
USBDIAG_ExAllocatePool(
    IN POOL_TYPE    PoolType,
    IN ULONG        NumberOfBytes
    )
{
        PULONG  pMem;

        // allocate memory plus a little extra for our own use
        pMem = ExAllocatePool(PoolType, NumberOfBytes + (2 * sizeof(ULONG)));

        // see if we actually allocated any memory
        if(pMem)
        {
                // store number of bytes allocated at start of memory allocated
                *pMem++ = NumberOfBytes;

                // now we are pointing at the memory allocated for caller
                // put signature word at end

                // get new pointer that points to end of buffer - ULONG
                pMem = (PULONG) (((PUCHAR) pMem) + NumberOfBytes);

                // write signature
                *pMem = MEM_SIGNATURE;

                // get back pointer to return to caller
                pMem = (PULONG) (((PUCHAR) pMem) - NumberOfBytes);

        gulMemoryAllocated += NumberOfBytes;
        //USBDIAG_KdPrint(("USBDIAG_ExAllocatePool: bytes allocated: %d\n", gulMemoryAllocated));
        }

        return (PVOID) pMem;    
}

VOID
USBDIAG_ExFreePool(
    IN PVOID    P
    )
{
        PULONG  pTmp = (PULONG) P;
        ULONG   buffSize;
        //PULONG  pSav=pTmp;
        
        // point at size ULONG at start of buffer, and address to free
        pTmp--;

        // get the size of memory allocated by caller
        buffSize = *pTmp;

        // point at signature and make sure it's O.K.
        ((PCHAR) P) += buffSize;

        if(*((PULONG) P) == MEM_SIGNATURE)
        {
                // let's go ahead and get rid of signature in case we get called
                // with this pointer again and memory is still paged in
                *((PULONG) P) = MEM_FREED_SIGNATURE;
                
                // free real pointer
                ExFreePool(pTmp);

        gulMemoryAllocated -= buffSize;

        //USBDIAG_KdPrint(("USBDIAG_ExFreePool: bytes allocated: %d\n", gulMemoryAllocated));
        }
        else {
                TRAP();
    }//else    
}

NTSTATUS
USBDIAG_ResetParentPort(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Reset the our parent port

Arguments:

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus, status = STATUS_SUCCESS;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;//USBDIAG_GlobalDeviceObject->DeviceExtension;

    USBDIAG_KdPrint(("enter USBDIAG_ResetParentPort\n"));

    ASSERT(deviceExtension);
    ASSERT(deviceExtension->StackDeviceObject);

    if (!deviceExtension->StackDeviceObject)
        return STATUS_UNSUCCESSFUL;
    //
    // issue a synchronous request
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);


    irp = IoBuildDeviceIoControlRequest(
                IOCTL_INTERNAL_USB_RESET_PORT,
                deviceExtension->StackDeviceObject,
                NULL,
                0,
                NULL,
                0,
                TRUE, /* INTERNAL */
                &event,
                &ioStatus);

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

    USBDIAG_KdPrint(("USBDIAG_ResetParentPort() calling USBD enable port api\n"));

    ntStatus = IoCallDriver(deviceExtension->StackDeviceObject,
                            irp);
                            
    USBDIAG_KdPrint(("USBDIAG_ResetParentPort() return from IoCallDriver USBD %x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) 
    {
        USBDIAG_KdPrint(("USBDIAG_ResetParentPort() Wait for single object\n"));

        status = KeWaitForSingleObject(
                       &event,
                       Suspended,
                       KernelMode,
                       FALSE,
                       NULL);

        USBDIAG_KdPrint(("USBDIAG_ResetParentPort() Wait for single object, returned %x\n", status));
    } 
    else 
    {
        ioStatus.Status = ntStatus;
    }

    //
    // USBD maps the error code for us
    //
    ntStatus = ioStatus.Status;

    USBDIAG_KdPrint(("Exit USBDIAG_ResetPort (%x)\n", ntStatus));

    return ntStatus;
}

// *************************************
PWCHAR
GetString(PWCHAR pwc, BOOLEAN MultiSZ);

// **************************************************************************
// **************************************************************************
// downstream manipulation routines

NTSTATUS
USBDIAG_RemoveDownstreamDevice(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PUSBD_PIPE defaultPipe = &DeviceData->DefaultPipe;

        //USBDIAG_KdPrint(("- USBDIAG_RemoveDownstreamDevice calling USBD_CloseEndpoint -\n"));
    ntStatus = USBD_CloseEndpoint(DeviceData,
                                  DeviceObject,
                                  defaultPipe,
                                  NULL);

    DeviceData = NULL;

    return ntStatus;

}

NTSTATUS
USBDIAG_Chap11SetConfiguration(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

        ASSERT(DeviceObject);

    USBDIAG_KdPrint(("USBDIAG_Chap11SetConfiguration: DeviceData 0x%x (& 0x%x), DeviceObject 0x%x\n",
                     *DeviceData,
                     DeviceData,
                     DeviceObject));

    ntStatus = USBD_SendCommand(DeviceData,
                                DeviceObject,
                                STANDARD_COMMAND_SET_CONFIGURATION,
                                0x01,   // wValue  = 1
                                0,      // wIndex  = 0
                                0,      // wLength = 0
                                NULL,
                                0,
                                NULL,
                                NULL);

    return ntStatus;
}

NTSTATUS
USBDIAG_Chap11EnableRemoteWakeup(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    ASSERT(DeviceData);

    USBDIAG_KdPrint(("USBDIAG_Chap11EnableRemoteWakeup: DeviceData 0x%x (& 0x%x), DeviceObject 0x%x\n",
                     *DeviceData,
                     DeviceData,
                     DeviceObject));

    ntStatus = USBD_SendCommand(DeviceData,
                                DeviceObject,
                                STANDARD_COMMAND_SET_DEVICE_FEATURE,
                                0x01,   // wValue  = 2 for rwu
                                0,      // wIndex  = 0
                                0,      // wLength = 0
                                NULL,
                                0,
                                NULL,
                                NULL);

    return ntStatus;
}

NTSTATUS
USBDIAG_Chap11SendPacketDownstream(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject,
        IN PREQ_SEND_PACKET_DOWNSTREAM pSendPacketDownstream
    )
{
    NTSTATUS                     ntStatus               = STATUS_SUCCESS;
        PCHAP11_SETUP_PACKET pSetupPacket       = &pSendPacketDownstream->SetupPacket;
        PUCHAR                           pucTempBuffer  = NULL;

    if (pSetupPacket->wLength)
    {
        pucTempBuffer = ExAllocatePool(NonPagedPool, pSetupPacket->wLength);
            if (!pucTempBuffer)
                    return STATUS_INSUFFICIENT_RESOURCES;
    }


    ASSERT(DeviceData);

    ntStatus = USBD_SendCommand(DeviceData,
                                DeviceObject,
                                pSetupPacket->wRequest,
                                pSetupPacket->wValue,
                                pSetupPacket->wIndex,
                                                                pSetupPacket->wLength,
                                pucTempBuffer,
                                pSetupPacket->wLength,
                                &pSendPacketDownstream->dwBytes,
                                &pSendPacketDownstream->ulUrbStatus);

    if (NT_SUCCESS(ntStatus) && pSetupPacket->wLength && pSendPacketDownstream->pucBuffer)
    {
        RtlCopyMemory(pSendPacketDownstream->pucBuffer, pucTempBuffer, pSendPacketDownstream->dwBytes);
    }

    if (pSetupPacket->wLength && pSendPacketDownstream->pucBuffer && pucTempBuffer)
    {
        ExFreePool(pucTempBuffer);
        pucTempBuffer = NULL;
    }

    return ntStatus;
}

NTSTATUS
USBDIAG_CreateInitDownstreamDevice(
    PREQ_ENUMERATE_DOWNSTREAM_DEVICE pEnumerate,
    PDEVICE_EXTENSION deviceExtension
    )
{
    NTSTATUS    ntStatus            = STATUS_SUCCESS;
    UCHAR       ucPortNumber        = pEnumerate->ucPortNumber;
    PUSBD_DEVICE_DATA DeviceData    = NULL; 
    BOOLEAN     bLowSpeed           = pEnumerate->bLowSpeed;
    ULONG       MaxPacketSize0      = 8;
    ULONG       DeviceHackFlags;

    PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
    ULONG deviceDescriptorLength = 0;
        PUSB_CONFIGURATION_DESCRIPTOR configDescriptor = NULL;
        ULONG configDescriptorLength = 0;

    if (deviceExtension->DeviceData[ucPortNumber])
        return STATUS_SUCCESS;

        USBDIAG_KdPrint(("***************************************************\n"));
    USBDIAG_KdPrint(("USBDIAG.SYS: REQ_FUNCTION_CHAP11_CREATE_USBD_DEVICE\n"));
    USBDIAG_KdPrint(("- Downstream device:\n"));
    USBDIAG_KdPrint(("- Port:     %d\n",   pEnumerate->ucPortNumber));
    USBDIAG_KdPrint(("- Lowspeed: %d\n",   pEnumerate->bLowSpeed));

        if (!deviceExtension->RootHubPdo)
                return STATUS_INVALID_PARAMETER;

    ntStatus = USBD_CreateDevice(&DeviceData,
                               deviceExtension->RootHubPdo,
                               bLowSpeed, 
                               MaxPacketSize0,
                               &DeviceHackFlags);

    USBDIAG_KdPrint(("* After USBD_CreateDevice, DeviceData = 0x%x\n", DeviceData));

    if (NT_SUCCESS(ntStatus))
    {
        ASSERT(DeviceData);

        //USBDIAG_KdPrint(("deviceExtension->DeviceData[%d] = 0x%x\n", deviceExtension->DeviceData[ucPortNumber]));


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

        if (!deviceDescriptor)
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
                else
                {
                        configDescriptorLength = 0xFF;
                        configDescriptor = ExAllocatePool(NonPagedPool, configDescriptorLength);
                }

                if (!configDescriptor)
                {
                        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                }

    }

    if (NT_SUCCESS(ntStatus))
    { 
                USBDIAG_KdPrint(("== Chap9Control calling USBD_InitializeDevice ==\n"));
        ntStatus = USBD_InitializeDevice(DeviceData,
                                                                                 deviceExtension->RootHubPdo,
                                         deviceDescriptor,
                                         deviceDescriptorLength,
                                                                                 configDescriptor,
                                                                                 configDescriptorLength); 

        }

    if (NT_SUCCESS(ntStatus))
    {
        deviceExtension->DeviceData[ucPortNumber] = DeviceData;

        USBDIAG_KdPrint(("SAVING...\n"));
        USBDIAG_KdPrint(("PortNumber: %d\n", ucPortNumber));
        USBDIAG_KdPrint(("DeviceData: 0x%x\n", DeviceData));

        if (configDescriptor)
        {
            deviceExtension->DownstreamConfigDescriptor[ucPortNumber] = configDescriptor;
        }
        else
        {
            USBDIAG_KdPrint(("configDescriptor after USBD_InitializeDevice is NULL\n!"));
        }

        //USBDIAG_KdPrint(("deviceExtension->DeviceData[%d]: 0x%x\n", 
                          //ucPortNumber,
                          //deviceExtension->DeviceData[ucPortNumber]));

        //USBDIAG_KdPrint(("deviceExtension: 0x%x\n", deviceExtension));
        //USBDIAG_KdPrint(("deviceExtension->DeviceData: 0x%x\n", 
                           //deviceExtension->DeviceData));
        //USBDIAG_KdPrint(("&deviceExtension->DeviceData[0]: 0x%x\n", 
                          //&deviceExtension->DeviceData[0]));
        //USBDIAG_KdPrint(("&deviceExtension->DeviceData[%d]: 0x%x\n", 
                          //ucPortNumber,
                          //&deviceExtension->DeviceData[ucPortNumber]));

    }

        if (!deviceExtension->DeviceData[ucPortNumber])
    {
        USBDIAG_KdPrint(("Attempt to create/init downstream device FAILED!\n"));
                ntStatus = STATUS_UNSUCCESSFUL;
    }

    return ntStatus;
}

NTSTATUS
USBDIAG_SetCfgEnableRWu(
    PDEVICE_EXTENSION deviceExtension, 
    PREQ_ENUMERATE_DOWNSTREAM_DEVICE pEnumerate
    )
{
    UCHAR ucPortNumber = pEnumerate->ucPortNumber;
    PUSBD_DEVICE_DATA DeviceData = deviceExtension->DeviceData[ucPortNumber];
    NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
    PUSB_CONFIGURATION_DESCRIPTOR configDescriptor = deviceExtension->DownstreamConfigDescriptor[ucPortNumber];


        USBDIAG_KdPrint(("*************************************************\n"));
    USBDIAG_KdPrint(("USBDIAG.SYS: REQ_FUNCTION_CHAP11_INIT_USBD_DEVICE\n"));
    USBDIAG_KdPrint(("PortNumber: %d\n", ucPortNumber));
    USBDIAG_KdPrint(("DeviceData: 0x%x\n", DeviceData));
    USBDIAG_KdPrint(("deviceExtension->DeviceData[%d]: 0x%x\n", 
                      ucPortNumber,
                      deviceExtension->DeviceData[ucPortNumber]));

        ASSERT(deviceExtension->RootHubPdo);

        ASSERT(DeviceData);

        USBDIAG_KdPrint(("- Chap9Control calling USBDIAG_Chap11SetConfiguration -\n"));
        if (DeviceData)
        {
                ntStatus = USBDIAG_Chap11SetConfiguration(DeviceData,
                                                                                                  deviceExtension->RootHubPdo);

        USBDIAG_KdPrint(("Set Config On Downstream Device On Port %d %s\n",
                         ucPortNumber,
                         NT_SUCCESS(ntStatus) ? "Passed" : "FAILED"));


                if (NT_SUCCESS(ntStatus))
                {
            if (configDescriptor->bmAttributes & REMOTE_WAKEUP)
            {
                            USBDIAG_KdPrint((" Chap9Control calling USBDIAG_Chap11EnableRemoteWakeup -\n"));
                            ntStatus = USBDIAG_Chap11EnableRemoteWakeup(DeviceData,
                                                                                                                    //deviceExtension->StackDeviceObject);
                                                                                                                    deviceExtension->RootHubPdo);
                USBDIAG_KdPrint(("Enable RWu On Downstream Device On Port %d %s\n",
                                 ucPortNumber,
                                 NT_SUCCESS(ntStatus) ? "Passed" : "FAILED"));
            }
                }
    }
    return ntStatus;
}

NTSTATUS
USBD_SendCommand(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject,
    IN USHORT RequestCode,
    IN USHORT WValue,
    IN USHORT WIndex,
    IN USHORT WLength,
    IN PVOID Buffer,
    IN ULONG BufferLength,
    OUT PULONG BytesReturned,
    OUT USBD_STATUS *UsbStatus
    )
/*++

Routine Description:

    Send a standard USB command on the default pipe.

Arguments:

    DeviceData - ptr to USBD device structure the command will be sent to

    DeviceObject -

    RequestCode -

    WValue - wValue for setup packet

    WIndex - wIndex for setup packet

    WLength - wLength for setup packet

    Buffer - Input/Output Buffer for command                                                                                                                                                                                                                  
  BufferLength - Length of Input/Output buffer.

    BytesReturned - pointer to ulong to copy number of bytes
                    returned (optional)

    UsbStatus - USBD status code returned in the URB.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus;
    PHCD_URB urb = NULL;
    PUSBD_PIPE defaultPipe = &(DeviceData->DefaultPipe);
    PUSB_STANDARD_SETUP_PACKET setupPacket;
    PUSBD_EXTENSION deviceExtension;

    PAGED_CODE();
    //USBDIAG_KdPrint(("enter USBD_SendCommand\n"));
    ASSERT_DEVICE(DeviceData);

    deviceExtension = DeviceObject->DeviceExtension;
    

    if (deviceExtension->DeviceHackFlags & 
        USBD_DEVHACK_SLOW_ENUMERATION) {
        
        //
        // if noncomplience switch is on in the
        // registry we'll pause here to give the
        // device a chance to respond.
        //
        
        LARGE_INTEGER deltaTime;
        deltaTime.QuadPart = 100 * -10000;
        (VOID) KeDelayExecutionThread(KernelMode,
                                      FALSE,
                                      &deltaTime);
    }

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

    if (!urb) {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    } else {

        urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_TRANSFER);

        urb->UrbHeader.Function = URB_FUNCTION_CONTROL_TRANSFER;

        setupPacket = (PUSB_STANDARD_SETUP_PACKET) 
            urb->HcdUrbCommonTransfer.Extension.u.SetupPacket;
        setupPacket->RequestCode = RequestCode;
        setupPacket->wValue = WValue;
        setupPacket->wIndex = WIndex;
        setupPacket->wLength = WLength;

        urb->HcdUrbCommonTransfer.hca.HcdEndpoint = defaultPipe->HcdEndpoint;
        urb->HcdUrbCommonTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK;

        // USBD is responsible for setting the transfer direction
        //
        // TRANSFER direction is implied in the command

        if (RequestCode & USB_DEVICE_TO_HOST)
            USBD_SET_TRANSFER_DIRECTION_IN(urb->HcdUrbCommonTransfer.TransferFlags);
        else
            USBD_SET_TRANSFER_DIRECTION_OUT(urb->HcdUrbCommonTransfer.TransferFlags);

        urb->HcdUrbCommonTransfer.TransferBufferLength = BufferLength;
        urb->HcdUrbCommonTransfer.TransferBuffer = Buffer;
        urb->HcdUrbCommonTransfer.TransferBufferMDL = NULL;
        urb->HcdUrbCommonTransfer.UrbLink = NULL;

        //USBDIAG_KdPrint(("SendCommand cmd = 0x%x buffer = 0x%x length = 0x%x direction = 0x%x\n",
                         //setupPacket->RequestCode,
                         //urb->HcdUrbCommonTransfer.TransferBuffer,
                         //urb->HcdUrbCommonTransfer.TransferBufferLength,
                         //urb->HcdUrbCommonTransfer.TransferFlags
                         //));

        ntStatus = USBD_SubmitSynchronousURB((PURB)urb, DeviceObject, DeviceData);

        if (BytesReturned) {
            *BytesReturned = urb->HcdUrbCommonTransfer.TransferBufferLength;
        }            

        if (UsbStatus) {
            *UsbStatus = urb->HcdUrbCommonTransfer.Status;
        }            

        // free the transfer URB

        ExFreePool(urb);

    }

    //USBDIAG_KdPrint(("exit USBD_SendCommand 0x%x\n", ntStatus));

    return ntStatus;
}


NTSTATUS
USBD_CloseEndpoint(
    IN PUSBD_DEVICE_DATA DeviceData,
    IN PDEVICE_OBJECT DeviceObject,
    IN PUSBD_PIPE PipeHandle,
    IN OUT USBD_STATUS *UsbStatus
    )
/*++

Routine Description:

    Close an Endpoint

Arguments:

    DeviceData - ptr to USBD device data structure.

    DeviceObject - USBD device object.

    PipeHandle - USBD pipe handle associated with the endpoint.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus;
    PHCD_URB urb;
    PUSBD_EXTENSION deviceExtension;

    PAGED_CODE();
    //USBDIAG_KdPrint(("enter USBD_CloseEndpoint\n"));
    ASSERT_DEVICE(DeviceData);

    deviceExtension = DeviceObject->DeviceExtension;

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

    if (!urb) {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    } else {

        urb->UrbHeader.Length = sizeof(struct _URB_HCD_CLOSE_ENDPOINT);
        urb->UrbHeader.Function = URB_FUNCTION_HCD_CLOSE_ENDPOINT;


        urb->HcdUrbCloseEndpoint.HcdEndpoint = PipeHandle->HcdEndpoint;

        //
        // Serialize Close Endpoint requests
        //

        ntStatus = USBD_SubmitSynchronousURB((PURB) urb, DeviceObject, 
                DeviceData);

       if (UsbStatus)
            *UsbStatus = urb->UrbHeader.Status;

        ExFreePool(urb);
    }

    //USBDIAG_KdPrint(("exit USBD_CloseEndpoint 0x%x\n", ntStatus));

    return ntStatus;
}




// downstream manipulation routines done
// **************************************************************************
// **************************************************************************

NTSTATUS
USBDIAG_WaitForWakeup(
    PDEVICE_EXTENSION deviceExtension
    )
{
    NTSTATUS ntStatus;
    
    USBDIAG_KdPrint(("'USBDIAG_WaitForWakeup: Waiting for Wait/Wake completion event\n"));

    USBDIAG_KdPrint(("Waiting for WaitWakeEvent...\n"));
    ntStatus = KeWaitForSingleObject(&deviceExtension->WaitWakeEvent,
                                     Suspended,
                                     KernelMode,
                                     FALSE,
                                     NULL);

    USBDIAG_KdPrint(("'WaitWakeEvent Signalled, Clearing ...\n"));
    KeClearEvent(&deviceExtension->WaitWakeEvent);

    return ntStatus;
}







PWCHAR
GetString(PWCHAR pwc, BOOLEAN MultiSZ)
{
    PWCHAR  psz, p;
    SIZE_T  Size;

    PAGED_CODE();
    psz=pwc;
    while (*psz!='\0' || (MultiSZ && *(psz+1)!='\0')) {
        psz++;
    }

    Size=(psz-pwc+1+(MultiSZ ? 1: 0))*sizeof(*pwc);

    // We use pool here because these pointers are passed
    // to the PnP code who is responsible for freeing them
    if ((p=ExAllocatePool(PagedPool, Size))!=NULL) {
        RtlCopyMemory(p, pwc, Size);
    }

    return(p);
}

#if DBG
ULONG USBD_Debug_Trace_Level =
#ifdef MAX_DEBUG
9;
#else
#ifdef NTKERN
1;
#else
0;
#endif /* NTKERN */
#endif /* MAX_DEBUG */
#endif /* DBG */

#ifdef DEBUG_LOG
struct USBD_LOG_ENTRY {
    CHAR    le_name[4];        // Identifying string
    ULONG    le_info1;        // entry specific info
    ULONG    le_info2;        // entry specific info
    ULONG    le_info3;        // entry specific info
}; /* USBD_LOG_ENTRY */


struct USBD_LOG_ENTRY *LStart = 0;    // No log yet
struct USBD_LOG_ENTRY *LPtr;
struct USBD_LOG_ENTRY *LEnd;
#endif /* DEBUG_LOG */






#if DBG
ULONG
_cdecl
USBD_KdPrintX(
    PCH Format,
    ...
    )
{
    va_list list;
    int i;
    int arg[5];

    if (USBD_Debug_Trace_Level == 1) {
        DbgPrint("USBD: ");
    } else {        
        DbgPrint("'USBD: ");
    }        
    va_start(list, Format);
    for (i=0; i<4; i++)
        arg[i] = va_arg(list, int);

    DbgPrint(Format, arg[0], arg[1], arg[2], arg[3]);

    return 0;
}


VOID
USBD_Warning(
    PUSBD_DEVICE_DATA DeviceData,
    PUCHAR Message,
    BOOLEAN DebugBreak
    )
{                                                                                               
    DbgPrint("USBD: Warning ****************************************************************\n");
    if (DeviceData) {
        DbgPrint("Device PID %04.4x, VID %04.4x\n",     
                 DeviceData->DeviceDescriptor.idProduct, 
                 DeviceData->DeviceDescriptor.idVendor); 
    }
    DbgPrint("%s", Message);

    DbgPrint("******************************************************************************\n");

//    if (DebugBreak) {
//        DBGBREAK();
//    }
}
 

VOID
USBD_Assert(
    IN PVOID FailedAssertion,
    IN PVOID FileName,
    IN ULONG LineNumber,
    IN PCHAR Message
    )
/*++

Routine Description:

    Debug Assert function.

Arguments:

    DeviceObject - pointer to a device object

    Irp          - pointer to an I/O Request Packet

Return Value:


--*/
{
#ifdef NTKERN  
    // this makes the compiler generate a ret
    ULONG stop = 1;
    
assert_loop:
#endif
    // just call the NT assert function and stop
    // in the debugger.
    RtlAssert( FailedAssertion, FileName, LineNumber, Message );

    // loop here to prevent users from going past
    // are assert before we can look at it
#ifdef NTKERN    
    DBGBREAK();
    if (stop) {
        goto assert_loop;
    }        
#endif
    return;
}
#endif /* DBG */

#define DEADMAN_TIMER
#define DEADMAN_TIMEOUT     5000     //timeout in ms
                                     //use a 5 second timeout
typedef struct _USBD_DEADMAN_TIMER {
    PIRP Irp;
    KTIMER TimeoutTimer;
    KDPC TimeoutDpc;
} USBD_DEADMAN_TIMER, *PUSBD_DEADMAN_TIMER;


NTSTATUS
USBD_SubmitSynchronousURB(
    IN PURB Urb,
    IN PDEVICE_OBJECT DeviceObject,
    IN PUSBD_DEVICE_DATA DeviceData
    )
/*++

Routine Description:

    Submit a Urb to HCD synchronously

Arguments:

    Urb - Urb to submit

    DeviceObject USBD device object

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS, status;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;
//#ifdef DEADMAN_TIMER
#if 0
    BOOLEAN haveTimer = FALSE;
    PUSBD_DEADMAN_TIMER timer;
#endif /* DEADMAN_TIMER */     

    PAGED_CODE();

    //USBDIAG_KdPrint(("enter USBD_SubmitSynchronousURB\n"));
    ASSERT_DEVICE(DeviceData);

    irp = IoBuildDeviceIoControlRequest(
                IOCTL_INTERNAL_USB_SUBMIT_URB,
                HCD_DEVICE_OBJECT(DeviceObject),
                NULL,
                0,
                NULL,
                0,
                TRUE, /* INTERNAL */
                &event,
                &ioStatus);

    //
    // Call the hc driver to perform the operation.  If the returned status
    // is PENDING, wait for the request to complete.
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    nextStack = IoGetNextIrpStackLocation(irp);
    ASSERT(nextStack != NULL);
    nextStack->Parameters.Others.Argument1 = Urb;

    //
    // initialize flags field
    // for internal request
    //
    Urb->UrbHeader.UsbdFlags = 0;

    //
    // Init the Irp field for transfers
    //

    switch(Urb->UrbHeader.Function) {
    case URB_FUNCTION_CONTROL_TRANSFER:
    case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
        HC_URB(Urb)->HcdUrbCommonTransfer.hca.HcdIrp = irp;

        if (HC_URB(Urb)->HcdUrbCommonTransfer.TransferBufferMDL == NULL &&
            HC_URB(Urb)->HcdUrbCommonTransfer.TransferBufferLength != 0) {

            if ((HC_URB(Urb)->HcdUrbCommonTransfer.TransferBufferMDL =
                IoAllocateMdl(HC_URB(Urb)->HcdUrbCommonTransfer.TransferBuffer,
                              HC_URB(Urb)->HcdUrbCommonTransfer.TransferBufferLength,
                              FALSE,
                              FALSE,
                              NULL)) == NULL)
                ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            else {
                Urb->UrbHeader.UsbdFlags |= USBD_REQUEST_MDL_ALLOCATED;
                MmBuildMdlForNonPagedPool(HC_URB(Urb)->HcdUrbCommonTransfer.TransferBufferMDL);
            }

        }
        break;
    }

    //USBDIAG_KdPrint(("USBD_SubmitSynchronousURB: calling HCD with URB\n"));

    if (NT_SUCCESS(ntStatus)) {
        // set the renter bit on the URB function code
        Urb->UrbHeader.Function |= 0x2000;
        
        ntStatus = IoCallDriver(HCD_DEVICE_OBJECT(DeviceObject),
                                irp);
    }                                

    //USBDIAG_KdPrint(("ntStatus from IoCallDriver = 0x%x\n", ntStatus));

    status = STATUS_SUCCESS;
    if (ntStatus == STATUS_PENDING) {
    
//#ifdef DEADMAN_TIMER
#if 0

        LARGE_INTEGER dueTime;

        timer = ExAllocatePool(NonPagedPool, sizeof(USBD_DEADMAN_TIMER));
        if (timer) {
            timer->Irp = irp;
            KeInitializeTimer(&timer->TimeoutTimer);
            KeInitializeDpc(&timer->TimeoutDpc,
                            USBD_SyncUrbTimeoutDPC,
                            timer);

            dueTime.QuadPart = -10000 * DEADMAN_TIMEOUT;

            KeSetTimer(&timer->TimeoutTimer,
                       dueTime,
                       &timer->TimeoutDpc);        

            haveTimer = TRUE;
        }
        
#endif /* DEADMAN_TIMER */           

        status = KeWaitForSingleObject(
                            &event,
                            Suspended,
                            KernelMode,
                            FALSE,
                            NULL);  
                            
        ntStatus = ioStatus.Status;
    } else {
        ioStatus.Status = ntStatus;
    }   

//#ifdef DEADMAN_TIMER
#if 0
    //
    // remove our timeoutDPC from the queue
    //
    if (haveTimer) {
        KeCancelTimer(&timer->TimeoutTimer);
        ExFreePool(timer);
    }                
#endif /* DEADMAN_TIMER */

// NOTE:
// mapping is handled by completion routine
// called by HCD

    //USBDIAG_KdPrint(("Leave Synch URB urb status = 0x%x ntStatus = 0x%x\n", Urb->UrbHeader.Status, ntStatus));

    return ntStatus;
}



NTSTATUS 
USBDIAG_SyncGetRootHubPdo(
        IN PDEVICE_OBJECT     StackDeviceObject,
    IN PDEVICE_OBJECT     PhysicalDeviceObject,
    IN OUT PDEVICE_OBJECT *RootHubPdo,
    IN OUT PDEVICE_OBJECT *TopOfHcdStackDeviceObject
    )
{
    NTSTATUS ntStatus, status;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;

    PAGED_CODE();
    
    USBDIAG_KdPrint(("enter USBDIAG_SyncGetRootHubPdo\n"));

    //
    // issue a synchronous request to the RootHubBdo
    //
    KeInitializeEvent(&event, NotificationEvent, FALSE);

        //USBDIAG_KdPrint(("USBDIAG_SyncGetRootHubPdo: ioctl code: 0x%x\n", IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO));

    irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO,
                                     //StackDeviceObject, //PhysicalDeviceObject,
                                     PhysicalDeviceObject,
                                     NULL,
                                     0,
                                     NULL,
                                     0,
                                     TRUE,  // INTERNAL
                                     &event,
                                     &ioStatus);

    if (NULL == irp) {
        USBDIAG_KdPrint(("USBUSBDIAG_SyncGetRootHubPdo build Irp failed\n"));
        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);
        //nextStack = IoGetCurrentIrpStackLocation(irp);

    //
    // pass the URB to the USBD 'class driver'
    //
    nextStack->Parameters.Others.Argument1 = NULL;
    nextStack->Parameters.Others.Argument2 = NULL;
    //nextStack->Parameters.Others.Argument3 = NULL;
    nextStack->Parameters.Others.Argument4 = RootHubPdo;

// _asm int 3

    ntStatus = IoCallDriver(PhysicalDeviceObject, irp);

    USBDIAG_KdPrint(("return from IoCallDriver USBD %x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) 
    {
        USBDIAG_KdPrint(("Wait for single object\n"));

        status = KeWaitForSingleObject(&event,
                                       Suspended,
                                       KernelMode,
                                       FALSE,
                                       NULL);

        USBDIAG_KdPrint(("Wait for single object, returned %x\n", status));
    } else {
        ioStatus.Status = ntStatus;
    }

    ntStatus = ioStatus.Status;

    USBDIAG_KdPrint(("exit USBDIAG_SyncGetRootHubPdo with ntStatus: 0x%x)\n", ntStatus));

    return ntStatus;
}


//#ifdef DEADMAN_TIMER
#if 0
VOID
USBD_SyncUrbTimeoutDPC(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )
/*++

Routine Description:

    This routine runs at DISPATCH_LEVEL IRQL. 

    
    
Arguments:

    Dpc - Pointer to the DPC object.

    DeferredContext - 

    SystemArgument1 - not used.
    
    SystemArgument2 - not used.

Return Value:

    None.

--*/
{
    PUSBD_DEADMAN_TIMER timer;
#if DBG
    BOOLEAN status;
#endif

    timer = DeferredContext;    

    
#if DBG
    status = 
#endif
        IoCancelIrp(timer->Irp);

#if DBG
    USBD_ASSERT(status == TRUE);    
#endif    
}
#endif /* DEADMAN_TIMER */

NTSTATUS
USBD_SetPdoRegistryParameter (
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN PWCHAR KeyName,
    IN ULONG KeyNameLength,
    IN PVOID Data,
    IN ULONG DataLength,
    IN ULONG KeyType,
    IN ULONG DevInstKeyType
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus;
    HANDLE handle;
    UNICODE_STRING keyNameUnicodeString;

    PAGED_CODE();

    RtlInitUnicodeString(&keyNameUnicodeString, KeyName);

    ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
                                     DevInstKeyType,
                                     STANDARD_RIGHTS_ALL,
                                     &handle);


    if (NT_SUCCESS(ntStatus)) {

        USBD_SetRegistryKeyValue(handle,
                                 &keyNameUnicodeString,
                                 Data,
                                 DataLength,
                                 KeyType);

        ZwClose(handle);
    }

    //USBDIAG_KdPrint((" RtlQueryRegistryValues status 0x%x\n"));

    return ntStatus;
}


NTSTATUS
USBD_SetRegistryKeyValue (
    IN HANDLE Handle,
    IN PUNICODE_STRING KeyNameUnicodeString,
    IN PVOID Data,
    IN ULONG DataLength,
    IN ULONG KeyType
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;

    PAGED_CODE();

    //
    // Create the key or open it, as appropriate based on the caller's
    // wishes.
    //

    ntStatus = ZwSetValueKey(Handle,
                             KeyNameUnicodeString,
                             0,
                             KeyType,
                             Data,
                             DataLength);

    //USBDIAG_KdPrint((" ZwSetKeyValue = 0x%x\n", ntStatus));

    return ntStatus;
}