/*++

Copyright (c) 1995,1996 Microsoft Corporation
:ts=4

Module Name:

    uhcd.c

Abstract:

    The UHC driver for USB, this module contains the initialization code.

Environment:

    kernel mode only

Notes:

Revision History:

    10-08-95 : created

--*/

#include "wdm.h"
#include <windef.h>
#include <unknown.h>
#ifdef DRM_SUPPORT
#include <ks.h>
#include <ksmedia.h>
#include <drmk.h>
#include <ksdrmhlp.h>
#endif
#include "stdarg.h"
#include "stdio.h"

#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"

#ifdef DRM_SUPPORT
NTSTATUS
UHCD_PreUSBD_SetContentId
(
    IN PIRP                          irp,
    IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
    IN PKSDRMAUDIOSTREAM_CONTENTID   pvData
    );

NTSTATUS
UHCD_PostUSBD_SetContentId
(
    IN PIRP                          irp,
    IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
    IN PKSDRMAUDIOSTREAM_CONTENTID   pvData
    );

#endif

#ifdef PAGE_CODE
#ifdef ALLOC_PRAGMA
// WIN98 breaks if we have an INIT segment
//#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, UHCD_CreateDeviceObject)
#pragma alloc_text(PAGE, UHCD_ReadWriteConfig)
#pragma alloc_text(PAGE, UHCD_QueryCapabilities)
#pragma alloc_text(PAGE, UHCD_StartDevice)
#pragma alloc_text(PAGE, UHCD_InitializeSchedule)
#pragma alloc_text(PAGE, UHCD_StartGlobalReset)
#pragma alloc_text(PAGE, UHCD_Suspend)
#pragma alloc_text(PAGE, UHCD_StopBIOS)
#ifdef DRM_SUPPORT
#pragma alloc_text(PAGE, UHCD_PreUSBD_SetContentId)
#pragma alloc_text(PAGE, UHCD_PostUSBD_SetContentId)
#endif
#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:

    NT status code

--*/
{

    PDEVICE_OBJECT deviceObject = NULL;
    NTSTATUS ntStatus = STATUS_SUCCESS;

    UHCD_KdPrint((2, "'entering DriverEntry\n"));

    UHCD_KdPrint ((1, "'UHCI Universal Serial Bus Host Controller Driver.\n"));
    UHCD_KdPrint ((1, "'HCD using USBDI version %x\n", USBDI_VERSION));

#ifdef DEBUG_LOG
    //
    // Initialize our debug trace log
    //
    UHCD_LogInit();
#endif

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

    DriverObject->MajorFunction[IRP_MJ_CREATE]=
    DriverObject->MajorFunction[IRP_MJ_CLOSE] =
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = UHCD_Dispatch;

    DriverObject->MajorFunction[IRP_MJ_PNP] = UHCD_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_POWER] = UHCD_Dispatch;

    DriverObject->DriverExtension->AddDevice = UHCD_PnPAddDevice;

    DriverObject->DriverUnload = UHCD_Unload;
    DriverObject->DriverStartIo = UHCD_StartIo;

    return ntStatus;
}


NTSTATUS
UHCD_ProcessPnPIrp(
    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;
    PDEVICE_OBJECT stackDeviceObject;
    BOOLEAN touchTheHardware = TRUE;

    UHCD_KdPrint((2, "'IRP_MJ_PNP\n"));

    // we should only process PnP messages sent to our
    // FDO for the HCD, USBD will handle any others.
    //   UHCD_ASSERT(DeviceObject == hcdDeviceObject);

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

    UHCD_PrintPnPMessage("PNP DISPATCH:", irpStack->MinorFunction);
    UHCD_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);

    switch (irpStack->MinorFunction) {

    case IRP_MN_START_DEVICE:

        //
        // USB handles start for us so we
        // should not get here.
        //

        LOGENTRY(LOG_MISC, 'STR!', deviceExtension->TopOfStackDeviceObject, 0, 0);
        UHCD_KdTrap(("HCD START_DEVICE Irp\n"));
        break;

    //
    // STOP & REMOVE messages unload the driver
    // when we get a STOP message it is still possible
    // touch the hardware, when we get a REMOVE message
    // we have to assume that the hardware is gone.
    //

    case IRP_MN_STOP_DEVICE:

        stackDeviceObject =  deviceExtension->TopOfStackDeviceObject;

        ntStatus = UHCD_StopDevice(DeviceObject);

        UHCD_CleanupDevice(DeviceObject);

        LOGENTRY(LOG_MISC, 'STOP', deviceExtension->TopOfStackDeviceObject, 0, 0);
        // Pass on to PDO
        break;

    case IRP_MN_SURPRISE_REMOVAL:
        touchTheHardware = FALSE;
   LOGENTRY(LOG_MISC, 'SRMV', deviceExtension->TopOfStackDeviceObject,
       ntStatus, 0);

    case IRP_MN_REMOVE_DEVICE:

        stackDeviceObject =  deviceExtension->TopOfStackDeviceObject;

        //
        // BUGBUG
        // we really only want stop processing if we are
        // sure the device is present.
        //

        ntStatus = UHCD_StopDevice(DeviceObject);

        UHCD_CleanupDevice(DeviceObject);

        LOGENTRY(LOG_MISC, 'REMV', deviceExtension->TopOfStackDeviceObject,
       ntStatus, 0);

        //
        // Undo anything we did in the PnPAddDevice function
        //

        UHCD_KdPrint((2, "'UHCD -- removing device object\n"));

        //
        // Detach FDO from PDO
        //

        IoCopyCurrentIrpStackLocationToNext(Irp);

        //
        // pass on to our PDO
        //

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

        //
        // important to detach after we pass the irp on
        //

        IoDetachDevice( deviceExtension->TopOfStackDeviceObject );

        USBD_FreeDeviceName(deviceExtension->DeviceNameHandle);

        //
        // Delete the device object we created for this controller
        //

        IoDeleteDevice (DeviceObject);
        goto UHCD_ProcessPnPIrp_Done;

        break;

    //
    // All other PnP messages passed on to our PDO
    //

    default:
        stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
        UHCD_ASSERT(stackDeviceObject != NULL);
        UHCD_KdPrint((2, "'UNKNOWN PNP MESSAGE (%x)\n", irpStack->MinorFunction));

        //
        // All unahndled PnP messages are passed on to the PDO
        //

    } /* case PNP minor function */

    IoCopyCurrentIrpStackLocationToNext(Irp);

    //
    // pass on to our PDO
    //

    ntStatus = IoCallDriver(stackDeviceObject,
                            Irp);

UHCD_ProcessPnPIrp_Done:

    return ntStatus;
}


#ifdef DRM_SUPPORT

NTSTATUS
UHCD_PreUSBD_SetContentId
(
    IN PIRP                          irp,
    IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
    IN PKSDRMAUDIOSTREAM_CONTENTID   pvData
)
 /* ++
  *
  * Description:
  *
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    PVOID Handlers[1];
    ULONG ContentId;

    PAGED_CODE();

    ASSERT(irp);
    ASSERT(pKsProperty);
    ASSERT(pvData);

    ContentId = pvData->ContentId;
    Handlers[0] = USBD_Dispatch;
    return pKsProperty->DrmAddContentHandlers(ContentId, Handlers, SIZEOF_ARRAY(Handlers));
}

NTSTATUS
UHCD_PostUSBD_SetContentId
(
    IN PIRP                          irp,
    IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
    IN PKSDRMAUDIOSTREAM_CONTENTID   pvData
)
 /* ++
  *
  * Description:
  *
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    NTSTATUS ntStatus;

    PAGED_CODE();

    ASSERT(irp);
    ASSERT(pKsProperty);
    ASSERT(pvData);

    // ioStackLocation = IoGetCurrentIrpStackLocation(irp);
    // deviceExtension = ioStackLocation->DeviceObject->DeviceExtension;
    // Context = pKsProperty->Context;
    // ContentId = pvData->ContentId;;

    return STATUS_SUCCESS;
}

#endif


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

Routine Description:

    Process the IRPs sent to this device.

    Power States for the USB host controller
        D0 - On.
        D1 - USB defined Suspend per 1.00 specification.
        D2 - undefined, reserved for future use.
        D3 - Off

Arguments:

    DeviceObject - pointer to a device object

    Irp          - pointer to an I/O Request Packet

Return Value:

    NT status code

--*/
{

    PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    PDEVICE_OBJECT hcdDeviceObject;

    UHCD_KdPrint((2, "'enter UHCD_Dispatch\n"));

#ifdef DRM_SUPPORT

    //
    // Need to check DRM request before passing to USBD and advise DRM of
    // the USBD entry point.  Otherwise, a rogue USBD could circumvent DRM.
    //
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    if (IRP_MJ_DEVICE_CONTROL == irpStack->MajorFunction && IOCTL_KS_PROPERTY == irpStack->Parameters.DeviceIoControl.IoControlCode) {
        NTSTATUS ntStatus;
        ntStatus = KsPropertyHandleDrmSetContentId(Irp, UHCD_PreUSBD_SetContentId);
        if (!NT_SUCCESS(ntStatus) && ntStatus != STATUS_PROPSET_NOT_FOUND) {
            Irp->IoStatus.Status = ntStatus;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            return ntStatus;
        }
    }

#endif

    //
    // pass the irp to USBD
    //
    if (!USBD_Dispatch(DeviceObject,
                       Irp,
                       &hcdDeviceObject,
                       &ntStatus)) {
        //
        // Irp was completed by USBD just exit our dispatch
        // routine.
        //
        goto UHCD_Dispatch_Done;
    }

    deviceExtension = (PDEVICE_EXTENSION) hcdDeviceObject->DeviceExtension;

    irpStack = IoGetCurrentIrpStackLocation (Irp);

    UHCD_KdPrint((2, "'UHCD_Dispatch IRP = %x, stack = %x\n", Irp, irpStack));

    switch (irpStack->MajorFunction) {

    case IRP_MJ_CREATE:

        UHCD_KdPrint((2, "'IRP_MJ_CREATE\n"));
        UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);

        break;

    case IRP_MJ_CLOSE:

        UHCD_KdPrint((2, "'IRP_MJ_CLOSE\n"));
        UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);

        break;

    case IRP_MJ_INTERNAL_DEVICE_CONTROL:
        UHCD_KdPrint((2, "'IRP_MJ_INTERNAL_DEVICE_CONTROL\n"));

        switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {

        case IOCTL_INTERNAL_USB_SUBMIT_URB:

            UHCD_KdPrint((2, "'IOCTL_USB_SUBMIT_URB\n"));
            ntStatus = UHCD_URB_Dispatch(hcdDeviceObject, Irp);

            break;
        default:

            // this IOCTL not handled by the HCD, we need
            // to invetigate why
            UHCD_KdTrap(("why is this IOCTL NOT HANDLED by HCD?\n"));

            // BUGBUG
            UHCD_CompleteIrp(hcdDeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
            break;
        } /* case */

        break;

    case IRP_MJ_DEVICE_CONTROL:
        UHCD_KdPrint((2, "'IRP_MJ_DEVICE_CONTROL\n"));

        switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {

#ifdef DRM_SUPPORT

        case IOCTL_KS_PROPERTY:
            {
            ntStatus = KsPropertyHandleDrmSetContentId(Irp, UHCD_PostUSBD_SetContentId);
            UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
            break;
            }
#endif

        case IOCTL_USB_HCD_GET_STATS_1:
            {
            PVOID ioBuffer;
            ULONG inputBufferLength;
            ULONG outputBufferLength;
            PHCD_STAT_INFORMATION_1 uhcdStatInfo;

            UHCD_KdPrint((1, "'IOCTL_USB_HCD_GET_STATS 1\n"));

            //
            // Get a pointer to the current location in the Irp. This is where
            //     the function codes and parameters are located.
            //

            irpStack = IoGetCurrentIrpStackLocation (Irp);

            //
            // Get the pointer to the input/output buffer and it's length
            //

            uhcdStatInfo = (PHCD_STAT_INFORMATION_1) ioBuffer = Irp->AssociatedIrp.SystemBuffer;
            inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
            outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

            if (outputBufferLength >= sizeof(HCD_STAT_INFORMATION_1)) {

                //
                // return the IOBASE address of the controller
                // followed by the Phys address of the persistent queue head
                //
                // This is for Intels TD-Poker app on Memphis
#ifdef NTKERN
                uhcdStatInfo->Reserved1 =
                    *((PULONG) &deviceExtension->DeviceRegisters[0]);
                uhcdStatInfo->Reserved2 =
                    (ULONG) deviceExtension->PersistantQueueHead->PhysicalAddress;
#endif
                // reg counters
                RtlCopyMemory(&uhcdStatInfo->Counters,
                              &deviceExtension->Stats,
                              sizeof(uhcdStatInfo->Counters));


                KeQuerySystemTime(&uhcdStatInfo->TimeRead);

                if (uhcdStatInfo->ResetCounters) {
                    UHCD_KdPrint((1, "'<Reset Stats>\n"));
                    RtlZeroMemory(&deviceExtension->Stats,
                                  sizeof(deviceExtension->Stats));
                }
                ntStatus = STATUS_SUCCESS;
            } else {
                ntStatus = STATUS_BUFFER_TOO_SMALL;
            }

            UHCD_KdPrint((2, "'inputBufferLength = %d outputBufferLength = %d\n",
                        inputBufferLength, outputBufferLength));

            UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus,
               sizeof(HCD_STAT_INFORMATION_1), NULL);
            }
            break;



        case IOCTL_USB_HCD_GET_STATS_2:
            {
            PVOID ioBuffer;
            ULONG inputBufferLength;
            ULONG outputBufferLength;
            PHCD_STAT_INFORMATION_2 uhcdStatInfo;

            UHCD_KdPrint((1, "'IOCTL_USB_HCD_GET_STATS 2\n"));

            //
            // Get a pointer to the current location in the Irp. This is where
            //     the function codes and parameters are located.
            //

            irpStack = IoGetCurrentIrpStackLocation (Irp);

            //
            // Get the pointer to the input/output buffer and it's length
            //

            uhcdStatInfo = (PHCD_STAT_INFORMATION_2) ioBuffer = Irp->AssociatedIrp.SystemBuffer;
            inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
            outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

            if (outputBufferLength >= sizeof(HCD_STAT_INFORMATION_2)) {
                extern LONG UHCD_CommonBufferBytes;
                //
                // return the IOBASE address of the controller
                // followed by the Phys address of the persistent queue head
                //
                // This is for Intels TD-Poker app on Memphis
#ifdef NTKERN
                uhcdStatInfo->Reserved1 =
                    *((PULONG) &deviceExtension->DeviceRegisters[0]);
                uhcdStatInfo->Reserved2 =
                    (ULONG) deviceExtension->PersistantQueueHead->PhysicalAddress;
#endif

                // reg counters
                RtlCopyMemory(&uhcdStatInfo->Counters,
                              &deviceExtension->Stats,
                              sizeof(uhcdStatInfo->Counters));


                // iso counters
                RtlCopyMemory(&uhcdStatInfo->IsoCounters,
                              &deviceExtension->IsoStats,
                              sizeof(uhcdStatInfo->IsoCounters));

                KeQuerySystemTime(&uhcdStatInfo->TimeRead);

                uhcdStatInfo->LockedMemoryUsed = UHCD_CommonBufferBytes;


                if (uhcdStatInfo->ResetCounters) {
                    UHCD_KdPrint((1, "'<Reset Stats>\n"));
                    RtlZeroMemory(&deviceExtension->Stats,
                                  sizeof(deviceExtension->Stats));
                    RtlZeroMemory(&deviceExtension->IsoStats,
                                  sizeof(deviceExtension->IsoStats));
                    deviceExtension->LastFrameInterrupt = 0;
                }
                ntStatus = STATUS_SUCCESS;
            } else {
                ntStatus = STATUS_BUFFER_TOO_SMALL;
            }

            UHCD_KdPrint((2, "'inputBufferLength = %d outputBufferLength = %d\n",
                        inputBufferLength, outputBufferLength));

            UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus,
                sizeof(HCD_STAT_INFORMATION_2), NULL);
            }
            break;


            #if DBG
            //
            // This is a test IOCTL only in debug versions
            //

        case IOCTL_USB_HCD_DISABLE_PORT:
            {
                PVOID ioBuffer;
                ULONG inputBufferLength;
                ULONG outputBufferLength;
                PIO_STACK_LOCATION pIrpSp;
                ULONG portIndex;
                PROOTHUB pRootHub;

                pIrpSp = IoGetCurrentIrpStackLocation(Irp);

                ioBuffer = Irp->AssociatedIrp.SystemBuffer;

                pRootHub = deviceExtension->RootHub;

                inputBufferLength
                    = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;

                if (inputBufferLength < sizeof(ULONG)) {
                    ntStatus = STATUS_BUFFER_TOO_SMALL;
                    goto IoctlDisablePortError;
                }

                portIndex = *(ULONG *)ioBuffer;

                if (portIndex >= pRootHub->NumberOfPorts) {
                    ntStatus = STATUS_INVALID_PARAMETER;
                    goto IoctlDisablePortError;
                }

                //
                // Flag the port as having no devices in there and
                // status changed.
                //

                pRootHub->DisabledPort[portIndex]
                    |= UHCD_FAKE_CONNECT_CHANGE | UHCD_FAKE_DISCONNECT;

IoctlDisablePortError:;

                //
                // Complete the IRP
                //

                UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
            }
            break;

       case IOCTL_USB_HCD_ENABLE_PORT:
       {
      PVOID ioBuffer;
      ULONG inputBufferLength;
      ULONG outputBufferLength;
      PIO_STACK_LOCATION pIrpSp;
      ULONG portIndex;
      PROOTHUB pRootHub;

      pIrpSp = IoGetCurrentIrpStackLocation(Irp);

      ioBuffer = Irp->AssociatedIrp.SystemBuffer;

      pRootHub = deviceExtension->RootHub;

      inputBufferLength
          = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;

      if (inputBufferLength < sizeof(ULONG)) {
          ntStatus = STATUS_BUFFER_TOO_SMALL;
          goto IoctlEnablePortError;
      }

      portIndex = *(ULONG *)ioBuffer;

      if (portIndex >= pRootHub->NumberOfPorts) {
          ntStatus = STATUS_INVALID_PARAMETER;
          goto IoctlEnablePortError;
      }

      //
      // Flag the port as having no devices in there and
      // status changed.
      //

      pRootHub->DisabledPort[portIndex] = UHCD_FAKE_CONNECT_CHANGE;

IoctlEnablePortError:;

      //
      // Complete the IRP
      //

      UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
       }
       break;

#endif

   default:
       ntStatus = STATUS_INVALID_DEVICE_REQUEST;

            UHCD_CompleteIrp(hcdDeviceObject, Irp,
                             STATUS_INVALID_DEVICE_REQUEST, 0, NULL);


        } /* case */

        break;

    //
    // Process PnP and Power messages
    //
    case IRP_MJ_POWER:
        // should not get here
        UHCD_KdTrap(("Power Message to HCD\n"));
        break;

    case IRP_MJ_PNP:

        ntStatus = UHCD_ProcessPnPIrp(hcdDeviceObject, Irp);
        break;

    default:
        UHCD_KdPrint((2, "'unrecognized IRP_MJ_ function (%x)\n", irpStack->MajorFunction));
        ntStatus = STATUS_INVALID_PARAMETER;
        UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
    } /* case MJ_FUNCTION */

UHCD_Dispatch_Done:

    UHCD_KdPrint((2, "'exit UHCD_Dispatch 0x%x\n", ntStatus));

    return ntStatus;
}


NTSTATUS
UHCD_SetDevicePowerState(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN DEVICE_POWER_STATE DeviceState
    )
/*++

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;
    BOOLEAN hookIt = FALSE;
    PIO_STACK_LOCATION irpStack;
    PUSBD_EXTENSION usbdExtension;
    PDEVICE_CAPABILITIES hcDeviceCapabilities;
    BOOLEAN bIsSuspend;

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

    if (irpStack->Parameters.Power.Type ==
            SystemPowerState) {

        switch (irpStack->Parameters.Power.State.SystemState) {
        case PowerSystemSleeping1:
        case PowerSystemSleeping2:
        case PowerSystemSleeping3:

            // suspend coming thru
            UHCD_KdPrint((1, "'Shutdown (Suspend) Host Controller\n"));
            deviceExtension->HcFlags |= HCFLAG_SUSPEND_NEXT_D3;

            ntStatus = STATUS_SUCCESS;
            break;

        case PowerSystemShutdown:

            //
            // this is a shutdown request
            //

            UHCD_KdPrint((1, "'Shutdown Host Controller\n"));

            //
            // do a stop to unhook the interrupt
            //
            UHCD_StopDevice(DeviceObject);

#ifdef NTKERN
            //
            // now give control back to the BIOS if we have one
            //
            if (deviceExtension->HcFlags & HCFLAG_USBBIOS) {
                UHCD_StartBIOS(DeviceObject);
            }
#endif

            ntStatus = STATUS_SUCCESS;
            break;

        default:
            // should not get here
            TRAP();
            break;
        }

    } else {
        bIsSuspend = (deviceExtension->HcFlags & HCFLAG_SUSPEND_NEXT_D3) ? 1:0;
        deviceExtension->HcFlags &= ~HCFLAG_SUSPEND_NEXT_D3;

        switch (DeviceState) {
        case PowerDeviceD3:
            //
            // Request for HC to power off
            // we will:
            //
            // 1. stop the controller schedule
            // 2. clean up the schedule
            // 3. reset the frame counter
            //

            LOGENTRY(LOG_MISC, 'D3go', deviceExtension, 0, 0);

            // it is possible (although remote) to are get a D3 with no
            // root hub attached if so we will turn off the hardware here
            if (!(deviceExtension->HcFlags & HCFLAG_RH_OFF)) {

                UHCD_SaveHCstate(DeviceObject);
                UHCD_Suspend(DeviceObject, FALSE);
            }

            UHCD_KdPrint((2, "'PowerDeviceD3 (OFF)\n"));

            // In the NT power management model, D3 is not necessarily "OFF".
            // What governs this is the DeviceWake setting in the DeviceCaps
            // structure.  If DeviceWake for our controller device is D3, then
            // we know that it is possible for the controller to wake the
            // machine from this power level.  The controller must have power
            // to be able to do so, therefore, we suppress setting the
            // HCFLAG_LOST_POWER flag in this case.  Setting it actually has
            // the undesired effect of causing us to reset the controller on
            // resume, which in turn causes the hub to fail and the devices to
            // be surprise removed/reenumerated unnecessarily when the hub is
            // reinitialized.  This normally isn't more than a minor annoyance
            // (e.g. slow resume time), except in the case where one of these
            // devices is a USB mass storage device.  Surprise removal is
            // dangerous for mass storage devices, and the user is presented
            // with the annoying "don't surprise remove this device" dialog
            // when the system is resumed, even though the user himself did not
            // directly cause the device removal.
            //
            // Note that the case where the host controller really does lose
            // power could result in the same problem, but that will have to
            // be addressed in the hub driver.

            usbdExtension = (PUSBD_EXTENSION)deviceExtension;
            hcDeviceCapabilities = &usbdExtension->HcDeviceCapabilities;
            if (!bIsSuspend ||
                DeviceState > hcDeviceCapabilities->DeviceWake) {
                deviceExtension->HcFlags |= HCFLAG_LOST_POWER;
                UHCD_KdPrint((1, "'HC will lose power in D3\n"));
            }
#if DBG
              else {
                UHCD_KdPrint((1, "'HC will NOT lose power in D3\n"));
            }
#endif

            // ensure no interrupts are generated by the controller
            {
                USHORT legsup;

                UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                                     TRUE,
                                     &legsup,
                                     0xc0,     // offset of legacy bios reg
                                     sizeof(legsup));

                LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
                // clear the PIRQD routing bit
                legsup &= ~LEGSUP_USBPIRQD_EN;

                UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                                     FALSE,
                                     &legsup,
                                     0xc0,     // offset of legacy bios reg
                                     sizeof(legsup));
            }


            deviceExtension->CurrentDevicePowerState = DeviceState;
            UHCD_KdPrint((1, "'Host Controller entered (D%d)\n", DeviceState-1));

            // pass on to PDO
            break;

        case PowerDeviceD1:
        case PowerDeviceD2:
            //
            // power states D1,D2 translate to USB suspend

            UHCD_KdPrint((2, "'PowerDeviceD1/D2 (SUSPEND) HC\n"));
#ifdef DEBUG_LOG
            if (DeviceState == PowerDeviceD1) {
                LOGENTRY(LOG_MISC, 'D1go', deviceExtension, 0, 0);
            } else {
                LOGENTRY(LOG_MISC, 'D2go', deviceExtension, 0, 0);
            }
#endif

            // change the state of the PRIQD routing bit
            {
            USHORT legsup;

            UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                    TRUE,
                                    &legsup,
                                    0xc0,     // offset of legacy bios reg
                                    sizeof(legsup));

            LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
            // clear the PIRQD routing bit
            legsup &= ~LEGSUP_USBPIRQD_EN;

            UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                    FALSE,
                                    &legsup,
                                    0xc0,     // offset of legacy bios reg
                                    sizeof(legsup));
            }

            //
            // Note, we should not get here unless all the children of the HC
            // have been suspended.
            //

            deviceExtension->CurrentDevicePowerState = DeviceState;
            UHCD_KdPrint((1, "'Host Controller entered (D%d)\n", DeviceState-1));

            // pass on to PDO
            break;

        case PowerDeviceD0:

            //
            // Request for HC to go to resume
            // we will:
            //
            // 1. start the controller in the completetion routine
            //

            UHCD_KdPrint((2, "'PowerDeviceD0 (ON), defer\n"));
            LOGENTRY(LOG_MISC, 'D0go', deviceExtension, 0, 0);

            //
            // finish the rest in the completion routine
            //

            hookIt = TRUE;

            // pass on to PDO
            break;

        default:

            UHCD_KdTrap(("Bogus DeviceState = %x\n", DeviceState));
        }

        if (hookIt) {
            UHCD_KdPrint((2, "'Set PowerIrp Completion Routine\n"));
            IoSetCompletionRoutine(Irp,
                   UHCD_PowerIrp_Complete,
                   // always pass FDO to completion routine
                   DeviceObject,
                   hookIt,
                   hookIt,
                   hookIt);
        }
    }

    return ntStatus;
}


NTSTATUS
UHCD_PowerIrp_Complete(
    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.

--*/
{
    PDEVICE_OBJECT deviceObject;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus;

    UHCD_KdPrint((2, "' enter UHCD_PowerIrp_Complete\n"));

    deviceObject = (PDEVICE_OBJECT) Context;
    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation (Irp);

    UHCD_KdPrint((1, "'Controller is in D0\n"));
    LOGENTRY(LOG_MISC, 'POWc', deviceExtension->CurrentDevicePowerState,
        0, Irp);

    // This function should only be called whe the controller
    // is put in D0
    UHCD_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
    UHCD_ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
    UHCD_ASSERT(irpStack->Parameters.Power.Type==DevicePowerState);
    UHCD_ASSERT(irpStack->Parameters.Power.State.DeviceState==PowerDeviceD0);

#ifdef JD
    //TEST_TRAP();
#endif

    ntStatus = deviceExtension->LastPowerUpStatus = Irp->IoStatus.Status;

    if (NT_SUCCESS(ntStatus)) {
       deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
    }

    UHCD_KdPrint((2, "' exit UHCD_PowerIrp_Complete\n"));

    return ntStatus;
}

VOID
UHCD_Unload(
    IN PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    Free all the allocated resources, etc.

Arguments:

    DriverObject - pointer to a driver object

Return Value:

    None

--*/
{
    //
    // Free any global resources
    //

    UHCD_KdPrint((2, "'unloading\n"));

#ifdef DEBUG_LOG
    //
    // free our debug trace log
    //
    UHCD_LogFree();
#endif
}


NTSTATUS
UHCD_CreateDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN OUT PDEVICE_OBJECT *DeviceObject,
    IN PUNICODE_STRING DeviceNameUnicodeString
    )
/*++

Routine Description:

    This routine is called to create a new instance of a USB host
    controller.

Arguments:

    DriverObject - pointer to the driver object for USBD.

    *DeviceObject - ptr to DeviceObject ptr to be filled
                    in with the device object we create.

    Configuration - ptr to configuration data to be stored
                    in the device extension.

    DeviceNameUnicodeString - optional pointer to a device
                    name for this FDO, can be NULL

Return Value:

    NT status code

--*/
{
    NTSTATUS ntStatus;
    PDEVICE_EXTENSION deviceExtension;

    PAGED_CODE();

    UHCD_KdPrint((2, "'enter UHCD_CreateDeviceObject\n"));

    ntStatus = IoCreateDevice(DriverObject,
                              sizeof (DEVICE_EXTENSION),
                              DeviceNameUnicodeString, // Name
                              FILE_DEVICE_CONTROLLER,
                              0,
                              FALSE, //NOT Exclusive
                              DeviceObject);

    if (NT_SUCCESS(ntStatus)) {

        deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);


        UHCD_KdPrint((2, "'UHCD_CreateDeviceObject: device object %x device extension = %x\n",
                 *DeviceObject, deviceExtension));

    } else if (*DeviceObject) {
        IoDeleteDevice(*DeviceObject);
    }

    UHCD_KdPrint((2, "'exit UHCD_CreateDeviceObject (%x)\n", ntStatus));

    return ntStatus;
}


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

Routine Description:

    This routine is called to create a new instance of a USB host controller

Arguments:

    DriverObject - pointer to the driver object for this instance of UHCD

    PhysicalDeviceObject - pointer to a device object created by the bus

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus;
    PDEVICE_OBJECT deviceObject = NULL;
    PDEVICE_EXTENSION deviceExtension;
    UNICODE_STRING deviceNameUnicodeString;
    ULONG deviceNameHandle;
    ULONG disableController = 0;

    UHCD_KdBreak((2, "'UHCD_PnPAddDevice\n"));

    LOGENTRY(LOG_MISC, 'ADDd', 0, 0, PhysicalDeviceObject);
//#ifdef JD
//    TEST_TRAP();
//#endif

    //UHCD_GetGlobalRegistryParameters(&disableController);

    if (disableController) {
        ntStatus = STATUS_UNSUCCESSFUL;
        goto UHCD_PnPAddDevice_Done;
    }


    //
    // Let USBD generate a device name
    //
    deviceNameHandle = USBD_AllocateDeviceName(&deviceNameUnicodeString);

    ntStatus = UHCD_CreateDeviceObject( DriverObject,
                                        &deviceObject,
                                        &deviceNameUnicodeString);

    LOGENTRY(LOG_MISC, 'cdnS', 0, 0, ntStatus);

    if (NT_SUCCESS(ntStatus)) {

        deviceExtension = deviceObject->DeviceExtension;

        RtlZeroMemory(deviceExtension, sizeof(DEVICE_EXTENSION));

        deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
        deviceExtension->DeviceNameHandle = deviceNameHandle;

        //
        // until we get a start we will comsider ourselves OFF
        //
        deviceExtension->CurrentDevicePowerState = PowerDeviceD3;

        //deviceExtension->NeedCleanup = FALSE;
        //deviceExtension->BWReclimationEnabled = FALSE;
        //deviceExtension->Stopped = FALSE;

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

        //
        // Indicate that the device object is ready for requests.
        //

        if (deviceExtension->TopOfStackDeviceObject) {

            //
            // a device object has been created, register with the bus driver now
            //

            USBD_RegisterHostController(PhysicalDeviceObject,
                                        deviceObject,
                                        deviceExtension->TopOfStackDeviceObject,
                                        DriverObject,
                                        UHCD_DeferredStartDevice,
                                        UHCD_SetDevicePowerState,
                                        UHCD_ExternalGetCurrentFrame,
#ifndef WIN98
                                        UHCD_ExternalGetConsumedBW,
#endif
                                        UHCD_SubmitFastIsoUrb,
                                        deviceNameHandle);
            {
            // make sure our USBD and HCD used matching hcdi header files
            PUSBD_EXTENSION usbdExtension;
            usbdExtension = deviceObject->DeviceExtension;
            if (usbdExtension->Length != sizeof(USBD_EXTENSION)) {
                UHCD_KdTrap(("UHCD/USBD version mismatch\n"));
                ntStatus = STATUS_UNSUCCESSFUL;
            }
            }

        }

        deviceObject->Flags |= DO_POWER_PAGABLE;
        deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;


    }

    RtlFreeUnicodeString(&deviceNameUnicodeString);

UHCD_PnPAddDevice_Done:

    LOGENTRY(LOG_MISC, 'addD', 0, 0, ntStatus);

    UHCD_KdPrint((2, "'exit UHCD_PnPAddDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
UHCD_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;

}


VOID
UHCD_ReadWriteConfig(
    IN  PDEVICE_OBJECT DeviceObject,
    IN  BOOLEAN Read,
    IN  PVOID Buffer,
    IN  ULONG Offset,
    IN  ULONG Length
)

/*++

Routine Description:

    This routine reads or write config space.

Arguments:

    DeviceObject        - Physical DeviceObject for this USB controller.

    Read                - TRUE if read, FALSE if write.

    Buffer              - The info to read or write.

    Offset              - The offset in config space to read or write.

    Length              - The length to transfer.

Return Value:

    None.

--*/

{
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    NTSTATUS ntStatus;
    KEVENT event;

    PAGED_CODE();

    if (Read) {
        memset(Buffer, '\0', Length);
    }

    irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);

    if (!irp) {
        UHCD_KdTrap(("failed to allocate Irp\n"));
        return;
    }

    // All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    IoSetCompletionRoutine(irp,
                           UHCD_DeferIrpCompletion,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE);


    nextStack = IoGetNextIrpStackLocation(irp);
    UHCD_ASSERT(nextStack != NULL);
    nextStack->MajorFunction= IRP_MJ_PNP;
    nextStack->MinorFunction= Read ? IRP_MN_READ_CONFIG : IRP_MN_WRITE_CONFIG;
    nextStack->Parameters.ReadWriteConfig.WhichSpace = 0;  /*PCI_WHICHSPACE_CONFIG */
    nextStack->Parameters.ReadWriteConfig.Buffer = Buffer;
    nextStack->Parameters.ReadWriteConfig.Offset = Offset;
    nextStack->Parameters.ReadWriteConfig.Length = Length;

    ntStatus = IoCallDriver(DeviceObject,
                            irp);

    UHCD_KdPrint((2, "'ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) {
       // wait for irp to complete

       TEST_TRAP(); // first time we hit this

       KeWaitForSingleObject(
            &event,
            Suspended,
            KernelMode,
            FALSE,
            NULL);
    }

    if (!NT_SUCCESS(ntStatus)) {
        // failed? this is probably a bug
        UHCD_KdTrap(("ReadWriteConfig failed, why?\n"));
    }

    IoFreeIrp(irp);
}


NTSTATUS
UHCD_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();

    // init the caps structure before calldown
    RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
    DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
    DeviceCapabilities->Version = 1;
    DeviceCapabilities->Address = -1;
    DeviceCapabilities->UINumber = -1;

    irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);

    if (!irp) {
        UHCD_KdTrap(("failed to allocate Irp\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

    nextStack = IoGetNextIrpStackLocation(irp);
    UHCD_ASSERT(nextStack != NULL);
    nextStack->MajorFunction= IRP_MJ_PNP;
    nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    IoSetCompletionRoutine(irp,
                           UHCD_DeferIrpCompletion,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE);

    nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;

    ntStatus = IoCallDriver(PdoDeviceObject,
                            irp);

    UHCD_KdPrint((2, "'ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) {
       // wait for irp to complete



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

        ntStatus = irp->IoStatus.Status;
    }

#if DBG
    if (!NT_SUCCESS(ntStatus)) {
        // failed? this is probably a bug
        UHCD_KdTrap(("QueryCapabilities failed, why?\n"));
    }
#endif

    IoFreeIrp(irp);

    return ntStatus;
}


#if 0
BOOLEAN
UHCD_StopController(
    IN PVOID Context
    )
/*++

Routine Description:

   Starts the USB host controller executing the schedule.
   Start Controller is called in by KeSynchronizeExecution.

Arguments:

   Context - DeviceData for this USB controller.

Return Value:

   TRUE

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    USHORT cmd;

    deviceExtension = Context;

    // no more interrupts
    cmd = 0;
    WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);


    return TRUE;
}
#endif



NTSTATUS
UHCD_StopDevice(
     IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    Resets the USB host controller and disconnects the interrupt.
    if a USB bios is present it is re-started.

Arguments:

    DeviceObject - DeviceObject of the controller to stop

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    LARGE_INTEGER startTime;
    USHORT cmd;
    USHORT status;
    KIRQL irql;

    UHCD_KdPrint((2, "'enter UHCD_StopDevice \n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    if (deviceExtension->HcFlags & HCFLAG_HCD_STOPPED) {
        // already stopped, bail
        goto UHCD_StopDevice_Done;
    }

    UHCD_DisableIdleCheck(deviceExtension);

    if (!(deviceExtension->HcFlags & HCFLAG_GOT_IO)) {
        // if we don't have io ports we can't stop
        // the controller
        //
        // This check is here because Win98 will send a stop
        // if we fail the start and we mail fail to start because
        // we did not get io ports
        goto UHCD_StopDevice_Done;
    }

    //
    // disable all interrupts
    //


//    KeSynchronizeExecution(deviceExtension->InterruptObject,
//                               UHCD_StopController,
//                               deviceExtension);
    cmd = 0;
    WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);

    //
    // reset the controller
    //
    cmd = UHCD_CMD_RESET;
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

    // now wait for HC to reset
    KeQuerySystemTime(&startTime);
    for (;;) {
        LARGE_INTEGER sysTime;

        cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
        UHCD_KdPrint((2, "'COMMAND = %x\n", cmd));
        if ((cmd & UHCD_CMD_RESET) == 0) {
            break;
        }

        KeQuerySystemTime(&sysTime);
        if (sysTime.QuadPart - startTime.QuadPart > 10000) {
            // timeout trying to rest
#if DBG
            UHCD_KdPrint((1, "TIMEOUT RESETTING CONTROLLER! \n"));
            UHCD_KdPrint((1, "'Port Resources @ %x Ports Available \n",
                deviceExtension->Port));
            TRAP();
#endif
            break;
        }
    }

    //
    // after reset the halt bit is set, clear status register just
    // in case.
    //
    status = 0xff;
    WRITE_PORT_USHORT(STATUS_REG(deviceExtension), status);

#if DBG
    {
    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
    UHCD_KdBreak((2, "'after reset cmd = %x stat = %x\n", cmd, status));
    }
#endif

UHCD_StopDevice_Done:

    //
    // handle no more interrupts for the HC
    //

    KeAcquireSpinLock(&deviceExtension->HcFlagSpin, &irql);
    deviceExtension->HcFlags |= HCFLAG_HCD_STOPPED;

    if (deviceExtension->InterruptObject) {
        PKINTERRUPT interruptObject;

        interruptObject = deviceExtension->InterruptObject;
        deviceExtension->InterruptObject = NULL;
        KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);

        IoDisconnectInterrupt(interruptObject);
    } else {
        KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
    }

    UHCD_KdPrint((2, "'exit UHCD_StopDevice (%x)\n", ntStatus));

    return ntStatus;
}


VOID
UHCD_CleanupDevice(
    IN  PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    Cleans up resources allocated for the host controller,
    this routine should undo anything done in START_DEVICE
    that does not require access to the HC hardware.

Arguments:

    DeviceObject - DeviceObject of the controller to stop

Return Value:

    NT status code.

--*/

{
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
    PUHCD_PAGE_LIST_ENTRY pageListEntry;
    BOOLEAN waitMore = TRUE;
    LARGE_INTEGER deltaTime;
#ifdef MAX_DEBUG
    extern LONG UHCD_CommonBufferBytes;
    extern LONG UHCD_TotalAllocatedHeapSpace;
#endif

    PAGED_CODE();
    UHCD_KdPrint((2, "'enter UHCD_CleanupDevice \n"));

    if (deviceExtension->HcFlags & HCFLAG_NEED_CLEANUP) {

        deviceExtension->HcFlags &= ~HCFLAG_NEED_CLEANUP;

   if (deviceExtension->RootHubPollTimerInitialized) {
       KeCancelTimer(&deviceExtension->RootHubPollTimer);
   }

        //
        // If someone issued a reset to one of the root
        // hub ports before the stop there may be timers
        // still outstanding, we wait here fo them to
        // expire.
        //

        while (deviceExtension->RootHubTimersActive) {

            //
            // wait 20 ms for our timers to expire
            //
            deltaTime.QuadPart = -10000 * 20;
            (VOID) KeDelayExecutionThread(KernelMode,
                                          FALSE,
                                          &deltaTime);

       waitMore = FALSE;
        }

   //
   // Wait for the polling timer to expire if we didn't wait
   // for the the root hub timers
   //

   if (waitMore && deviceExtension->RootHubPollTimerInitialized) {
       deltaTime.QuadPart = -10000 * 10;
       (VOID)KeDelayExecutionThread(KernelMode, FALSE,
                &deltaTime);
   }

        //
        // free memory allocated for frame list
        //

        if (deviceExtension->FrameListVirtualAddress != NULL) {
            UHCD_ASSERT(deviceExtension->AdapterObject != NULL);


            HalFreeCommonBuffer(deviceExtension->AdapterObject,
#if DBG
                                FRAME_LIST_LENGTH *3,
#else
                                FRAME_LIST_LENGTH *2,
#endif
                                deviceExtension->FrameListLogicalAddress,
                                deviceExtension->FrameListVirtualAddress,
                                FALSE);



            deviceExtension->FrameListVirtualAddress = NULL;

#if DBG
           {
           extern LONG UHCD_CommonBufferBytes;
           UHCD_ASSERT(UHCD_CommonBufferBytes > FRAME_LIST_LENGTH*3);
           UHCD_CommonBufferBytes -= (FRAME_LIST_LENGTH*3);
           }
#endif
        }

        // free HW descriptors used as interrupt triggers
        if (deviceExtension->PersistantQueueHead) {
            UHCD_FreeHardwareDescriptors(DeviceObject, deviceExtension->PQH_DescriptorList);
            deviceExtension->PersistantQueueHead = NULL;
        }

        //
        // unmap device registers here
        //
        if (deviceExtension->HcFlags & HCFLAG_UNMAP_REGISTERS) {
           TEST_TRAP();
        }

        if (deviceExtension->Piix4EP) {
            RETHEAP(deviceExtension->Piix4EP);
            deviceExtension->Piix4EP = NULL;
        }

        //BUGBUG
        //ASSERT that we have no active endpoints
        //and that no endpoints are on the close list


        //
        // free root hub memory
        //
        if (deviceExtension->RootHub) {
#if DBG
            if (deviceExtension->RootHub->DisabledPort) {
               RETHEAP (deviceExtension->RootHub->DisabledPort);
            }
#endif
            RETHEAP(deviceExtension->RootHub);
            deviceExtension->RootHub = NULL;
        }

        //
        // free all pages allocated with HalAllocateCommonBuffer
        //

        while (!IsListEmpty(&deviceExtension->PageList)) {
            pageListEntry = (PUHCD_PAGE_LIST_ENTRY)
                RemoveHeadList(&deviceExtension->PageList);
#ifdef MAX_DEBUG
            UHCD_CommonBufferBytes -= pageListEntry->Length;
#endif

            HalFreeCommonBuffer(deviceExtension->AdapterObject,
                                pageListEntry->Length,
                                pageListEntry->LogicalAddress,
                                pageListEntry,
                                FALSE);
        }

        //
        // NOTE: may not have an adapter object if getresources fails
        //

        if (deviceExtension->AdapterObject) {
	    KIRQL oldIrql;
	    KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
            (deviceExtension->AdapterObject->DmaOperations->PutDmaAdapter)
                (deviceExtension->AdapterObject);
	    KeLowerIrql (oldIrql);
        }
        deviceExtension->AdapterObject = NULL;
#ifdef MAX_DEBUG
        {
        extern LONG UHCD_CommonBufferBytes;
        extern LONG UHCD_TotalAllocatedHeapSpace;
        //
        // Check totalmemory allocated count
        //
        if (UHCD_TotalAllocatedHeapSpace != 0) {
            //
            // memory leak!!
            //
            TRAP();
        }

        if (UHCD_CommonBufferBytes != 0) {
            //
            // memory leak!!
            //
            TRAP();
        }
        TEST_TRAP();
        }
#endif
    }

    UHCD_KdPrint((2, "'exit UHCD_CleanupDevice (%x)\n", STATUS_SUCCESS));

    return;
}


NTSTATUS
UHCD_InitializeSchedule(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes the schedule structures for the HCD

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT Status code.

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    ULONG i, length;
    PHW_QUEUE_HEAD persistantQueueHead;
    PUHCD_HARDWARE_DESCRIPTOR_LIST pqh_DescriptorList;

    PAGED_CODE();
    UHCD_KdPrint((2, "'enter UHCD_InitializeSchedule\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // allocate a contiguous range of physical memory for the frame list --
    // we will need enough for 1024 physical addresses
    //

    length = FRAME_LIST_LENGTH;

    // 
    // Allocate a common buffer for the frame list (that is programmed into
    // the hardware later) as well as a copy of the active frame list.
    //

    deviceExtension->FrameListVirtualAddress =
        HalAllocateCommonBuffer(deviceExtension->AdapterObject,
#if DBG
                                length*3,
#else
                                length*2,
#endif
                                &deviceExtension->FrameListLogicalAddress,
                                FALSE);



#if DBG
    {
        extern LONG UHCD_CommonBufferBytes;
        UHCD_CommonBufferBytes += length*3;
    }
#endif

    //
    // Set the copy pointer into the common buffer at the end of the
    // master frame list.
    //

    deviceExtension->FrameListCopyVirtualAddress =
        ((PUCHAR) deviceExtension->FrameListVirtualAddress) + length;

    if (deviceExtension->FrameListVirtualAddress == NULL ||
        deviceExtension->FrameListCopyVirtualAddress == NULL) {
        if (deviceExtension->FrameListVirtualAddress != NULL) {

           HalFreeCommonBuffer(deviceExtension->AdapterObject,
#if DBG
                            length*3,
#else
                            length*2,
#endif
                            deviceExtension->FrameListLogicalAddress,
                            deviceExtension->FrameListVirtualAddress,
                            FALSE);

           deviceExtension->FrameListVirtualAddress = NULL;

#if DBG
	   {
	       extern LONG UHCD_CommonBufferBytes;
	       UHCD_CommonBufferBytes -= length*3;
	   }
#endif
        }
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto UHCD_InitializeScheduleExit;
    } else {
        RtlZeroMemory(deviceExtension->FrameListVirtualAddress, length);
        RtlZeroMemory(deviceExtension->FrameListCopyVirtualAddress, length);
#if DBG
        deviceExtension->IsoList =
            (PULONG)(((PUCHAR) deviceExtension->FrameListCopyVirtualAddress) + length);
        RtlZeroMemory(deviceExtension->IsoList, length);
        UHCD_KdPrint((2, "'(%d) bytes allocated for usb iso list va = %x\n", length,
                        deviceExtension->IsoList));

#endif
        //
        // this should be 1 page
        //
        UHCD_KdPrint((2, "'(%d) bytes allocated for usb frame list va = %x\n", length,
                        deviceExtension->FrameListVirtualAddress));

        UHCD_KdPrint((2, "'(%d) bytes allocated for usb frame list copy va = %x\n", length,
                        deviceExtension->FrameListCopyVirtualAddress));

    }

    //
    // Initialize the lists of Memory Descriptors
    // used to allocate TDs and packet buffers
    //

    UHCD_InitializeCommonBufferPool(DeviceObject,
                                    &deviceExtension->LargeBufferPool,
                                    UHCD_LARGE_COMMON_BUFFER_SIZE,
                                    UHCD_RESERVE_LARGE_BUFFERS);

    UHCD_InitializeCommonBufferPool(DeviceObject,
                                    &deviceExtension->MediumBufferPool,
                                    UHCD_MEDIUM_COMMON_BUFFER_SIZE,
                                    UHCD_RESERVE_MEDIUM_BUFFERS);

    UHCD_InitializeCommonBufferPool(DeviceObject,
                                    &deviceExtension->SmallBufferPool,
                                    UHCD_SMALL_COMMON_BUFFER_SIZE,
                                    UHCD_RESERVE_SMALL_BUFFERS);


    //
    // Now set up our base queues for active endpoints
    //

    InitializeListHead(&deviceExtension->EndpointList);
    InitializeListHead(&deviceExtension->EndpointLookAsideList);
    InitializeListHead(&deviceExtension->FastIsoEndpointList);
    InitializeListHead(&deviceExtension->FastIsoTransferList);

    //
    // Queue for released endpoints
    //
    InitializeListHead(&deviceExtension->ClosedEndpointList);

    //BUGBUG check for error

    //
    // TDs are allocated for use as interrupt triggers.
    // the first two are used to detect when the sign bit for
    // the frame counter changes.
    //
    // The rest are used to generate interrupts for cleanup and cancel
    //

    if (!UHCD_AllocateHardwareDescriptors(DeviceObject,
                                          &deviceExtension->PQH_DescriptorList,
                                          MAX_TDS_PER_ENDPOINT)) {


        HalFreeCommonBuffer(deviceExtension->AdapterObject,
#if DBG
                            length*3,
#else
                            length*2,
#endif
                            deviceExtension->FrameListLogicalAddress,
                            deviceExtension->FrameListVirtualAddress,
                            FALSE);


        deviceExtension->FrameListVirtualAddress = NULL;


#if DBG
    {
        extern LONG UHCD_CommonBufferBytes;
        UHCD_CommonBufferBytes -= length*3;
    }
#endif

       ntStatus = STATUS_INSUFFICIENT_RESOURCES;
       goto UHCD_InitializeScheduleExit;
    }

    pqh_DescriptorList = deviceExtension->PQH_DescriptorList;
    persistantQueueHead =
        deviceExtension->PersistantQueueHead = (PHW_QUEUE_HEAD) pqh_DescriptorList->MemoryDescriptor->VirtualAddress;

    UHCD_KdPrint((2, "'Control Queue Head va = (%x)\n", deviceExtension->PersistantQueueHead));

    //
    // This is our base queue head, it goes in every frame
    // but never has transfers associated with it.
    // Control Q heads are always added after this guy,
    // Bulk Q heads are always added in front of this guy (end of queue).
    //
    // initialize Horiz ptr to point to himself with the T-Bit set
    //
    persistantQueueHead->HW_HLink = persistantQueueHead->PhysicalAddress;
    SET_T_BIT(persistantQueueHead->HW_HLink);
    persistantQueueHead->HW_VLink = LIST_END;
    persistantQueueHead->Next =
        persistantQueueHead->Prev = persistantQueueHead;

    // Put the control 'base' queue in every frame
    for (i=0; i < FRAME_LIST_SIZE; i++)
        *( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i) ) =
            persistantQueueHead->PhysicalAddress;

    //
    // Initialize an empty interrupt schedule.
    //

    for (i=0; i < MAX_INTERVAL; i++)
        deviceExtension-> InterruptSchedule[i] =
            persistantQueueHead;


    // this is a dummy TD we use to generate an interrupt
    // when the sign bit changes on the frame counter
    deviceExtension->TriggerTDList = (PUHCD_TD_LIST) (pqh_DescriptorList->MemoryDescriptor->VirtualAddress +
                UHCD_HW_DESCRIPTOR_SIZE);

    deviceExtension->TriggerTDList->TDs[0].Active = 0;
    deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete = 1;
#ifdef VIA_HC
    // VIA Host Controller requires a valid PID even if the TD is inactive
    deviceExtension->TriggerTDList->TDs[0].PID = USB_IN_PID;
#endif /* VIA_HC */

    deviceExtension->TriggerTDList->TDs[1].Active = 0;
    deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 1;
#ifdef VIA_HC
    deviceExtension->TriggerTDList->TDs[1].PID = USB_IN_PID;
#endif /* VIA_HC */
    deviceExtension->TriggerTDList->TDs[0].HW_Link =
        deviceExtension->TriggerTDList->TDs[1].HW_Link = persistantQueueHead->PhysicalAddress;

    //
    // The PIIX3 has the following bug:
    //
    // If a frame babble occurrs an interrupt in generated with no way to
    // clear it -- the host controller will continue to generate interrupts
    // until an active TD is encountered.
    //
    // The workaround for this is to put an active TD in the schedule
    // (on the persistent queue head).  We activate this TD whenever a TD
    // completes with the babble bit set.
    //


    //
    // set up a TD for frame babble recovery
    //
    {
        PHW_TRANSFER_DESCRIPTOR transferDescriptor;

        transferDescriptor =
            &deviceExtension->TriggerTDList->TDs[2];

        transferDescriptor->Active = 1;
        transferDescriptor->Isochronous = 1;
        transferDescriptor->InterruptOnComplete = 0;
        transferDescriptor->PID = USB_OUT_PID;
        transferDescriptor->Address = 0;
        transferDescriptor->Endpoint = 1;
        transferDescriptor->ErrorCounter = 0;
        transferDescriptor->PacketBuffer = 0;
        transferDescriptor->MaxLength =
            NULL_PACKET_LENGTH;

        // point to ourself
        transferDescriptor->HW_Link =
            transferDescriptor->PhysicalAddress;
        //SET_T_BIT(transferDescriptor->HW_Link);

        // point the persistent queue head at this TD
        persistantQueueHead->HW_VLink =
            transferDescriptor->PhysicalAddress;

        deviceExtension->FrameBabbleRecoverTD =
            transferDescriptor;
    }

    // Initilaize the remainder of the trigger TDs for use by the
    // transfer code -- used to generate interrupts.
    for (i=UHCD_FIRST_TRIGGER_TD; i<MAX_TDS_PER_ENDPOINT; i++) {
        PHW_TRANSFER_DESCRIPTOR transferDescriptor;

        transferDescriptor =
            &deviceExtension->TriggerTDList->TDs[i];

        transferDescriptor->PID = USB_IN_PID;
        transferDescriptor->Frame = 0;
    }


    // Initialize internal frame counters.
    deviceExtension->FrameHighPart =
        deviceExtension->LastFrame = 0;

    // BUGBUG for now just insert trigger TDs
    *( ((PULONG) (deviceExtension->FrameListVirtualAddress)) )  =
        deviceExtension->TriggerTDList->TDs[0].PhysicalAddress;

    *( ((PULONG) (deviceExtension->FrameListVirtualAddress) + FRAME_LIST_SIZE-1) )  =
        deviceExtension->TriggerTDList->TDs[1].PhysicalAddress;

    // schedule has been set up, make copy here
    RtlCopyMemory(deviceExtension->FrameListCopyVirtualAddress,
                  deviceExtension->FrameListVirtualAddress,
                  FRAME_LIST_SIZE * sizeof(HW_DESCRIPTOR_PHYSICAL_ADDRESS));
    UHCD_KdPrint((2, "'FrameListCopy = %x FrameList= %x\n",
                  deviceExtension->FrameListCopyVirtualAddress,
                  deviceExtension->FrameListVirtualAddress));
    //
    // Initilaize Root hub variables
    //

    deviceExtension->RootHubDeviceAddress = USB_DEFAULT_DEVICE_ADDRESS;
    deviceExtension->RootHubInterruptEndpoint = NULL;

    //
    // Initialize Isoch variables
    //

    deviceExtension->LastFrameProcessed = 0;

    //
    // Initialize Misc variables
    //

    deviceExtension->EndpointListBusy = FALSE;
    deviceExtension->LastPowerUpStatus = STATUS_SUCCESS;

    UHCD_InitBandwidthTable(DeviceObject);

    KeInitializeSpinLock(&deviceExtension->EndpointListSpin);

    KeInitializeSpinLock(&deviceExtension->HcFlagSpin);
    KeInitializeSpinLock(&deviceExtension->HcDmaSpin);
    KeInitializeSpinLock(&deviceExtension->HcScheduleSpin);
    deviceExtension->HcDma = -1;

    //
    // fix PIIX4 issues.
    //
    UHCD_FixPIIX4(DeviceObject);

UHCD_InitializeScheduleExit:

    LOGENTRY(LOG_MISC, 'BASE', deviceExtension->FrameListVirtualAddress, 0, DeviceObject);

    UHCD_KdPrint((2, "'exit UHCD_InitializeSchedule (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
UHCD_StartGlobalReset(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Initializes the hardware registers in the host controller.

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT Status code.

--*/
{
    PDEVICE_EXTENSION deviceExtension;

    PAGED_CODE();
    UHCD_KdPrint((2, "'enter UHCD_StartGlobalReset\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    LOGENTRY(LOG_MISC, 'InHW', DeviceObject, deviceExtension, 0);

    UHCD_KdPrint((2, "'before init -- hardware state: Command = %x Status = %x  interrupt mask %x\nFrame Base = %x\n",
                    READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
                    READ_PORT_USHORT(STATUS_REG(deviceExtension)),
                    READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
                    READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));

    //
    // Perform global reset on the controller
    //
    // A global reset of the USB requires 20ms, we
    // begin the process here and finish later (in the
    // hub emulation code)

    UHCD_KdPrint((2, "'Begin Global Reset of Host Controller \n"));

    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),  UHCD_CMD_GLOBAL_RESET);

    UHCD_KdPrint((2, "'exit UHCD_StartGlobalReset -- (STATUS_SUCCESS)\n"));

    return STATUS_SUCCESS;
}


NTSTATUS
UHCD_CompleteGlobalReset(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Starts the USB host controller executing the schedule,
    his routine is called when the global reset for the
    controller has completed.

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT Status code.

--*/
{
    PHYSICAL_ADDRESS frameListBaseAddress;
    PDEVICE_EXTENSION deviceExtension;
    USHORT cmd;

    UHCD_KdPrint((2, "'enter CompleteGlobalReset\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // Initialization has completed
    //

    UHCD_KdPrint((2, "'Initialization Completed, starting controller\n"));

    // clear the global reset bit
    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),  cmd &= ~UHCD_CMD_GLOBAL_RESET);

    UHCD_KdPrint((2, "'after global reset -- hardware state: Command = %x Status = %x  interrupt mask %x\nFrame Base = %x\n",
                    READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
                    READ_PORT_USHORT(STATUS_REG(deviceExtension)),
                    READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
                    READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));

    //
    // do a HC reset
    //

//    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension)) | UHCD_CMD_RESET;
//    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),  cmd);

    // stall for 10 microseconds
//    KeStallExecutionProcessor(10);        //stall for 10 microseconds

//    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));

    //
    // HCReset bit should be cleared when HC completes the reset
    //

//    if (cmd &  UHCD_CMD_RESET) {
//        UHCD_KdPrint((2, "'Host Controller unable to reset!!!\n"));
//        TRAP();
//    }

    //
    // program the frame list base address
    //

    frameListBaseAddress = deviceExtension->FrameListLogicalAddress;

    WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension), frameListBaseAddress.LowPart);
    UHCD_KdPrint((2, "'Frame list base address programmed to (physical) %x.\n", frameListBaseAddress));

    UHCD_KdPrint((2, "'after init -- hardware state: Command = %x Status = %x  interrupt mask %x\nFrame Base = %x\n",
                    READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
                    READ_PORT_USHORT(STATUS_REG(deviceExtension)),
                    READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
                    READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));

    if (deviceExtension->SteppingVersion >= UHCD_B0_STEP) {
#ifdef ENABLE_B0_FEATURES
        //
        // set maxpacket for bandwidth reclimation
        //

        // TEST_TRAP();
        WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),  UHCD_CMD_MAXPKT_64);
        UHCD_KdPrint((2, "'Set MaxPacket to 64\n"));
#endif
    }

    //
    // Enable Interrupts on the controller
    //


    UHCD_KdPrint((2, "'Enable Interrupts \n"));
    cmd = UHCD_INT_MASK_IOC | UHCD_INT_MASK_TIMEOUT | UHCD_INT_MASK_RESUME;
    if (deviceExtension->SteppingVersion >= UHCD_B0_STEP) {
        // enable short packet detect
        cmd |= UHCD_INT_MASK_SHORT;
    }

    WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);

    // set the SOF modify to whatever we found before
    // the reset

    UHCD_KdPrint((1, "'Setting SOF Modify to %d\n",
            deviceExtension->SavedSofModify));

    WRITE_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension),
        deviceExtension->SavedSofModify);

    //
    // Start the controller schedule
    //

    UHCD_KdPrint((2, "'Set Run/Stop bit \n"));

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension)) | UHCD_CMD_RUN;
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

    //
    // Make sure that the controller is really running and if not, kick it
    //

    UhcdKickStartController(DeviceObject);

    UHCD_KdPrint((2, "'after start -- hardware state: Command = %x Status = %x  interrupt mask %x\nFrame Base = %x\n",
                    READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
                    READ_PORT_USHORT(STATUS_REG(deviceExtension)),
                    READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
                    READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));


    UHCD_KdPrint((2, "'exit CompleteGlobalReset\n"));

    return STATUS_SUCCESS;
}


ULONG
UHCD_GetCurrentFrame(
    IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    returns the current frame number as an unsigned 32-bit value.

Arguments:

    DeviceObject - pointer to a device object

Return Value:

    Current Frame Number.

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    ULONG currentFrame, highPart, frameNumber;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // The following algorithm compliments
    // of jfuller and kenr
    //

    // get Hcd's high part of frame number
    highPart = deviceExtension->FrameHighPart;

    // get 11-bit frame number, high 17-bits are 0
    frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));

    currentFrame = ((frameNumber & 0x0bff) | highPart) +
        ((frameNumber ^ highPart) & 0x0400);

    //UHCD_KdPrint((2, "'exit UHCD_GetCurrentFrame = %d\n", currentFrame));

    return currentFrame;

}


NTSTATUS
UHCD_SaveHCstate(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT cmd;
    PDEVICE_EXTENSION deviceExtension;

    UHCD_KdPrint((1, "'saving host controller state\n"));
    LOGENTRY(LOG_MISC, 'HCsv', 0, 0, 0);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    //
    // save some state info
    //
    deviceExtension->SavedInterruptEnable =
        READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    // save the cmd regs in the "stopped" state
    cmd &= ~UHCD_CMD_RUN;

    deviceExtension->SavedCommandReg = cmd;

    // additonal saved info needed to restore from hibernate
    deviceExtension->SavedFRNUM =
        READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));
    deviceExtension->SavedFRBASEADD =
        READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension));

    return ntStatus;
}


NTSTATUS
UHCD_RestoreHCstate(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    BOOLEAN lostPower = FALSE;
    LARGE_INTEGER deltaTime;
    BOOLEAN apm = FALSE;

    UHCD_KdPrint((1, "'restoring host controller state\n"));
    LOGENTRY(LOG_MISC, 'HCrs', 0, 0, 0);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // this check tells us that power was turned off
    // to the controller.
    //
    // IBM Aptiva rapid resume will turn off power to
    // the controller without going thru the OS
    // may APM BIOSes do this too
    //

    // see if we were in D3, if so restore additional info
    // necessary to recover from hibernate

    if (deviceExtension->HcFlags & HCFLAG_LOST_POWER) {
        UHCD_KdPrint((0, "'restoring HC from hibernate\n"));

        deviceExtension->HcFlags &= ~HCFLAG_LOST_POWER;
        // we will need to do much the same thing we do
        // in START_DEVICE

        // first restore the HC regs for the schedule
        WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension),
            deviceExtension->SavedFRNUM);
        WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
            deviceExtension->SavedFRBASEADD);

         // now do a global reset
        ntStatus = UHCD_StartGlobalReset(DeviceObject);

        if (!NT_SUCCESS(ntStatus)) {
            goto UHCD_RestoreHCstate_Done;
        }
        //
        // Everything is set, we need to wait for the
        // global reset of the Host controller to complete.
        //

        // 20 ms to reset...
        deltaTime.QuadPart = -10000 * 20;

        //
        // block here until reset is complete
        //

        (VOID) KeDelayExecutionThread(KernelMode,
                                      FALSE,
                                      &deltaTime);

        ntStatus = UHCD_CompleteGlobalReset(DeviceObject);

        lostPower = TRUE;

    } else {
        //
        // interrupt masks disabled indicates power was turned
        // off to the piix3/piix4.
        //
        lostPower =
            (BOOLEAN) (READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) == 0);
        // we get here on APM/ACPI systems
        // note that lostPower should be false on an ACPI system
        apm = TRUE;
    }

    if (lostPower)  {
        // we get here for hibernate, APM suspend or ACPI D2/D3
        // on hibernate we need to re-init the controller
        // on APM we let the BIOS do it.

        //for APM:
        //  lostPower = TRUE if APM BIOS turned off hc
        //  apm = TRUE

        //for ACPI D3/Hibernate
        //  HCFLAG_LOST_POWER flag is set
        //  lostPower TRUE

        //for ACPI D2 & APM supprted USB suspend
        //  lostPower = FALSE
        //  HCFLAG_LOST_POWER is clear
        //  apm = FALSE;

        UHCD_KdPrint((0, "'detected (APM/HIBERNATE) loss of power during suspend\n"));

        if (apm) {
            //
            // some APM BIOSes trash these registers so we'll have to put
            // them back
            //

            WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension),
                deviceExtension->SavedFRNUM);

            WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
                deviceExtension->SavedFRBASEADD);
        }

        WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension),
            deviceExtension->SavedInterruptEnable);

        WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),
            deviceExtension->SavedCommandReg);
    }

UHCD_RestoreHCstate_Done:

    return ntStatus;
}



NTSTATUS
UHCD_Suspend(
    IN PDEVICE_OBJECT DeviceObject,
    IN BOOLEAN SuspendBus
    )

/*++

Routine Description:

    This routine suspends the host controller.

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    USHORT cmd, status;
    LARGE_INTEGER finishTime, currentTime;

    PAGED_CODE();

    UHCD_KdPrint((1, "'suspending Host Controller (Root Hub)\n"));
    LOGENTRY(LOG_MISC, 'RHsu', 0, 0, 0);
#ifdef JD
    //TEST_TRAP();
#endif
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //may not be in D0 if we haven't started yet.
    //UHCD_ASSERT(deviceExtension->CurrentDevicePowerState == PowerDeviceD0);

    UHCD_DisableIdleCheck(deviceExtension);

#if DBG
    UHCD_KdPrint((2, "'HC regs before suspend\n"));
    UHCD_KdPrint((2, "'cmd register = %x\n",
        READ_PORT_USHORT(COMMAND_REG(deviceExtension)) ));
    UHCD_KdPrint((2, "'status register = %x\n",
        READ_PORT_USHORT(STATUS_REG(deviceExtension)) ));
    UHCD_KdPrint((2, "'interrupt enable register = %x\n",
        READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) ));

    UHCD_KdPrint((2, "'frame list base = %x\n",
        READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension)) ));
#endif

    //
    // save some state info
    //
    deviceExtension->SavedInterruptEnable =
        READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));

    // set the run/stop bit

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    cmd &= ~UHCD_CMD_RUN;

    deviceExtension->SavedCommandReg = cmd;

    UHCD_KdPrint((2, "'run/stop = %x\n", cmd));
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);



    KeQuerySystemTime(&finishTime); // get current time
    finishTime.QuadPart += 5000000; // figure when we quit (.5 seconds
                                    // later)

    // poll the status reg for the halt bit
    for (;;) {
        status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
        UHCD_KdPrint((2, "'STATUS = %x\n", status));
        if (status & UHCD_STATUS_HCHALT) {
            break;
        }
        KeQuerySystemTime(&currentTime);
        if (currentTime.QuadPart >= finishTime.QuadPart) {
            UHCD_KdPrint((0, "'Warning: Host contoller did not respond to halt req\n"));
#if DBG
            if (SuspendBus) {
                // this is very bad if we are suspending
                TRAP();
            }
#endif
            break;
        }
    }

    UHCD_KdPrint((2, "'UHCD Suspend RH, schedule stopped\n"));

    // reset the frame list current index
    WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension), 0);

    // re-initialize internal frame counters.
    deviceExtension->FrameHighPart =
        deviceExtension->LastFrame = 0;

    deviceExtension->XferIdleTime =
        deviceExtension->IdleTime = 0;

    // clear idle flag sinec we will be running on resume
    // note we leave the state of the rollover ints since
    // it reflects the status of the TDs not the HC schecdule

    deviceExtension->HcFlags &= ~HCFLAG_IDLE;

    //
    // we let the hub driver handle suspending the ports
    //

// BUGBUG
// not sure if we need to force resume
    if (SuspendBus) {
        LOGENTRY(LOG_MISC, 'RHew', 0, 0, 0);
        cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
        // FGR=0
        cmd &= ~UHCD_CMD_FORCE_RESUME;
        // EGSM=1
        cmd |= UHCD_CMD_SUSPEND;
        UHCD_KdPrint((2, "'enter suspend = %x\n", cmd));
        WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
    }

    UHCD_KdPrint((2, "'exit UHCD_SuspendHC 0x%x\n", ntStatus));
#ifdef MAX_DEBUG
    TEST_TRAP();
#endif

    return ntStatus;
}


NTSTATUS
UHCD_Resume(
    IN PDEVICE_OBJECT DeviceObject,
    IN BOOLEAN DoResumeSignaling
    )

/*++

Routine Description:

    This routine resumes the host controller from either the
    OFF or suspended state.

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT cmd;
    PDEVICE_EXTENSION deviceExtension;
    LARGE_INTEGER deltaTime;

    UHCD_KdPrint((1, "'resuming Host Controller (Root Hub)\n"));
    LOGENTRY(LOG_MISC, 'RHre', 0, 0, 0);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

#if DBG
    UHCD_KdPrint((2, "'HC regs after suspend\n"));
    UHCD_KdPrint((2, "'cmd register = %x\n",
        READ_PORT_USHORT(COMMAND_REG(deviceExtension)) ));
    UHCD_KdPrint((2, "'status register = %x\n",
        READ_PORT_USHORT(STATUS_REG(deviceExtension)) ));
    UHCD_KdPrint((2, "'interrupt enable register = %x\n",
        READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) ));

    UHCD_KdPrint((2, "'frame list base = %x\n",
        READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension)) ));
#endif

    // if we are resuming the controller should be in D0
    UHCD_ASSERT(deviceExtension->CurrentDevicePowerState == PowerDeviceD0);

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    if (DoResumeSignaling) {
        // force a global resume
        UHCD_KdPrint((2, "'forcing resume = %x\n", cmd));
        cmd |= UHCD_CMD_FORCE_RESUME;
        WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

        //
        // wait 20 ms for our timers to expire
        //
        deltaTime.QuadPart = -10000 * 20;
        (VOID) KeDelayExecutionThread(KernelMode,
                                      FALSE,
                                      &deltaTime);

        //done with resume
        cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
        cmd &= ~(UHCD_CMD_SUSPEND | UHCD_CMD_FORCE_RESUME);
        UHCD_KdPrint((2, "'clear suspend = %x\n", cmd));
        WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

        //
        // start schedule
        //

        // wait for FGR bit to go low
        do {
            cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
        } while (cmd & UHCD_CMD_FORCE_RESUME);

    }

    // start controller
    cmd |= UHCD_CMD_RUN;

    UHCD_KdPrint((2, "'exit resume = %x\n", cmd));
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

    //
    // Note: we let the hub driver handle resume
    // signaling
    //

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));


    //
    // Make sure that the controller is really running and if not, kick it
    //

    UhcdKickStartController(DeviceObject);

    UHCD_KdPrint((2, "'exit UHCD_Resume 0x%x\n", ntStatus));
#ifdef MAX_DEBUG
    TEST_TRAP();
#endif

    // enable the idle check routine
    deviceExtension->HcFlags &= ~HCFLAG_DISABLE_IDLE;

    UHCD_KdPrint((1, "'Host controller root hub entered D0\n"));

    return ntStatus;
}


NTSTATUS
UHCD_ExternalGetCurrentFrame(
    IN PDEVICE_OBJECT DeviceObject,
    IN PULONG CurrentFrame
    )

/*++

Routine Description:


Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    *CurrentFrame = UHCD_GetCurrentFrame(DeviceObject);

    return STATUS_SUCCESS;
}


ULONG
UHCD_ExternalGetConsumedBW(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:


Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    PDEVICE_EXTENSION deviceExtension;
    ULONG low, i;

    LOGENTRY(LOG_MISC, 'AVbw', 0, 0, 0);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    low = UHCD_TOTAL_USB_BW;

    // find the lowest value in our available bandwidth table

    for (i=0; i<MAX_INTERVAL; i++) {
        //
        // max bytes per frame - bw reaerved for bulk and control
        //
        if (deviceExtension->BwTable[i] < low) {
            low = deviceExtension->BwTable[i];
        }
    }

    // lowest available - total = consumed bw

    return UHCD_TOTAL_USB_BW-low;
}


NTSTATUS
UHCD_RootHubPower(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This function handles power messages to the root hub, note that
    we save the state of the HC here instead of when the HC itself is
    powered down.  The reason for this is for compatibility with APM
    systems that cut power to the HC during a suspend.  On these
    systems WDM never sends a power state change mesage to the HC ie
    the HC is assumed to always stay on.

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;

    UHCD_KdPrint((2, "'UHCD_RootHubPower\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    LOGENTRY(LOG_MISC, 'HCrp', irpStack->MinorFunction, 0, 0);

    switch (irpStack->MinorFunction) {
    case IRP_MN_WAIT_WAKE:
        UHCD_KdPrint((2, "'IRP_MN_WAIT_WAKE\n"));
        //
        // someone is enabling us for wakeup
        //
        LOGENTRY(LOG_MISC, 'rpWW', 0, 0, 0);

        TEST_TRAP();  // never seen this before?
        break;

    case IRP_MN_SET_POWER:

        switch (irpStack->Parameters.Power.Type) {
        case SystemPowerState:
            LOGENTRY(LOG_MISC, 'SPsp', 0, 0, 0);

            // should not get here
            UHCD_KdTrap(("RootHubPower -- SystemState\n"));
            break;

        case DevicePowerState:

            LOGENTRY(LOG_MISC, 'SPdp', 0, 0, 0);
            switch(irpStack->Parameters.Power.State.DeviceState) {
            case PowerDeviceD0:
                {
                BOOLEAN doResumeSignaling;

                if (!NT_SUCCESS(deviceExtension->LastPowerUpStatus)) {
                   ntStatus = deviceExtension->LastPowerUpStatus;
                } else {
                   doResumeSignaling = !(deviceExtension->HcFlags
                                         & HCFLAG_RH_OFF);
                   deviceExtension->HcFlags &= ~HCFLAG_RH_OFF;
                   UHCD_SetControllerD0(DeviceObject);
                   UHCD_RestoreHCstate(DeviceObject);

                   ntStatus = UHCD_Resume(DeviceObject, doResumeSignaling);
                }
                }
                break;
            case PowerDeviceD1:
            case PowerDeviceD2:
                UHCD_SaveHCstate(DeviceObject);
                ntStatus = UHCD_Suspend(DeviceObject, TRUE);
                break;
            case PowerDeviceD3:
                deviceExtension->HcFlags |= HCFLAG_RH_OFF;
                UHCD_SaveHCstate(DeviceObject);
                ntStatus = UHCD_Suspend(DeviceObject, FALSE);
                break;
            }
            break;
        }
    }

    return ntStatus;
}

VOID
UhcdKickStartController(IN PDEVICE_OBJECT PDevObj)
/*++

Routine Description:

    Best effort at fixing a hung UHCI device on power up.
    Symptom is that everything is fine and chip in run state,
    but frame counter never increments.  It was found that if
    we strobe the run/stop (RS) bit, the frame counter starts incrementing
    and the device starts working.

    We don't know the exact cause or why the fix appears to work, but the
    addition of this code was requested by MS management to help alleviate the
    problem.

Arguments:

    PDevObj        - DeviceObject for this USB controller.

Return Value:

    VOID

--*/
{
   PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
   USHORT cmd;
   USHORT counter;
   ULONG i;
   LARGE_INTEGER deltaTime;



   for (i = 0; i < UHCD_MAX_KICK_STARTS; i++) {
      //
      // Wait at least two frames (2 ms)
      //


      deltaTime.QuadPart = -10000 * 2;
      KeDelayExecutionThread(KernelMode, FALSE, &deltaTime);

      counter = READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(pDevExt));

      if (counter != 0x0000) {
         //
         // It is working splendidly
         //

         break;
      }

      //
      // The frame counter is jammed.  Kick the chip.
      //

      cmd = READ_PORT_USHORT(COMMAND_REG(pDevExt)) & ~UHCD_CMD_RUN;
      WRITE_PORT_USHORT(COMMAND_REG(pDevExt), cmd);

      //
      // Delay two frames (2ms)
      //

      deltaTime.QuadPart = -10000 * 2;
      KeDelayExecutionThread(KernelMode, FALSE, &deltaTime);

      cmd |= UHCD_CMD_RUN;

      WRITE_PORT_USHORT(COMMAND_REG(pDevExt), cmd);
   }

   LOGENTRY(LOG_MISC | LOG_IO, 'HCks', i, UHCD_MAX_KICK_STARTS, 0);
}


NTSTATUS
UHCD_FixPIIX4(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:


 PIIX4 hack

 we will need a dummy bulk queue head inserted in the schedule
    This routine resumes the host controller.

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/
{
    PUHCD_ENDPOINT endpoint;
    PHW_TRANSFER_DESCRIPTOR transferDescriptor,
        qhtransferDescriptor;
    PHW_QUEUE_HEAD queueHead;
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;


    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // create the endpoint
    //

    endpoint = GETHEAP(NonPagedPool, sizeof(UHCD_ENDPOINT));

    if (endpoint) {
        deviceExtension->Piix4EP = endpoint;
        endpoint->Type = USB_ENDPOINT_TYPE_BULK;
        endpoint->Sig = SIG_EP;
        endpoint->EndpointFlags = 0;

        // we will use two of the trigger TDs
        //
        // to set up the dummy qh with TD attached.
        //

        transferDescriptor =
                &deviceExtension->TriggerTDList->TDs[3];

        transferDescriptor->Active = 0;
        transferDescriptor->Isochronous = 1;
        transferDescriptor->InterruptOnComplete = 0;
        transferDescriptor->PID = USB_OUT_PID;
        transferDescriptor->Address = 0;
        transferDescriptor->Endpoint = 1;
        transferDescriptor->ErrorCounter = 0;
        transferDescriptor->PacketBuffer = 0;
        transferDescriptor->MaxLength =
            NULL_PACKET_LENGTH;


        // point to ourself
        transferDescriptor->HW_Link =
            transferDescriptor->PhysicalAddress;

        queueHead = (PHW_QUEUE_HEAD)
                &deviceExtension->TriggerTDList->TDs[4];

        qhtransferDescriptor =
                &deviceExtension->TriggerTDList->TDs[4];

        UHCD_InitializeHardwareQueueHeadDescriptor(
                DeviceObject,
                queueHead,
                qhtransferDescriptor->PhysicalAddress);

        UHCD_KdPrint((2, "'PIIX4 dummy endpoint, qh 0x%x\n", endpoint, queueHead));

        //link the td to the QH
        queueHead->HW_VLink = transferDescriptor->PhysicalAddress;
        queueHead->Endpoint = endpoint;

        UHCD_InsertQueueHeadInSchedule(DeviceObject,
                                       endpoint,
                                       queueHead,
                                       0);

        UHCD_BW_Reclimation(DeviceObject, FALSE);

    } else {
        // could not get memory for dummy queue head,
        // something is really broken.
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        UHCD_KdTrap(("failed to get memory for dummy qh\n"));
    }

    return ntStatus;
}

#ifdef USB_BIOS

// this is the Phoenix revised version
NTSTATUS
UHCD_StopBIOS(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This routine stops a UHCI USB BIOS if present.

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    USHORT cmd;
    USHORT legsup, status;
    PDEVICE_EXTENSION deviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    LARGE_INTEGER startTime;
    ULONG sofModifyValue = 0;
    WCHAR UHCD_SofModifyKey[] = L"SofModify";


    PAGED_CODE();
    UHCD_KdPrint((2, "'UHCD_Stopping BIOS\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    // initilialize to whatever the BIOS set it to.
    sofModifyValue =
        (ULONG) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension)));

    //
    // save current values for BIOS hand-back
    //

    deviceExtension->BiosCmd =
        READ_PORT_USHORT(COMMAND_REG(deviceExtension));

    deviceExtension->BiosIntMask =
        READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));

    deviceExtension->BiosFrameListBase =
        READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension));
    deviceExtension->BiosFrameListBase &= 0xfffff000;

    // Grab any SOF ModifyValue indicated in the registry

    //USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
    //                             &sofModifyValue,
    //                             sizeof(sofModifyValue),
    //                            UHCD_SofModifyKey,
    //                             sizeof(UHCD_SofModifyKey));

    UHCD_GetSOFRegModifyValue(DeviceObject,
                              &sofModifyValue);

    // save the SOF modify for posterity
    deviceExtension->SavedSofModify = (CHAR) sofModifyValue;
    UHCD_ASSERT(sofModifyValue <= 255);

    // stop the controller,
    // clear RUN bit but keep config flag set so BIOS won't reinit
    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    LOGENTRY(LOG_MISC, 'spBI', cmd, 0, 0);
    // BUGBUG
    // save cmd bits to set when we hand back
    cmd &= ~(UHCD_CMD_RUN | UHCD_CMD_SW_CONFIGURED);
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

    // NOTE: if no BIOS is present
    // halt bit is initially set with PIIX3
    // halt bit is initially clear with VIA

    // now wait for HC to halt
    KeQuerySystemTime(&startTime);
    for (;;) {
        LARGE_INTEGER sysTime;

        status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
        UHCD_KdPrint((2, "'STATUS = %x\n", status));
        if (status & UHCD_STATUS_HCHALT) {
            WRITE_PORT_USHORT(STATUS_REG(deviceExtension), 0xff);
            LOGENTRY(LOG_MISC, 'spBH', cmd, 0, 0);
            break;
        }

        KeQuerySystemTime(&sysTime);
        if (sysTime.QuadPart - startTime.QuadPart > 10000) {
            // time out
#if DBG
            UHCD_KdPrint((1,
                "'TIMEOUT HALTING CONTROLLER! (I hope you don't have USB L-BIOS)\n"));
            UHCD_KdPrint((1, "'THIS IS A BIOS BUG, Port Resources @ %x Ports Available \n",
                deviceExtension->Port));
            //TRAP();
#endif
            break;
        }
    }

    UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                            TRUE,
                            &legsup,
                            0xc0,     // offset of legacy bios reg
                            sizeof(legsup));


    LOGENTRY(LOG_MISC, 'legs', legsup, 0, 0);
    UHCD_KdPrint((2, "'legs = %x\n", legsup));

#ifdef JD
//    TEST_TRAP();
#endif
    // save it
    deviceExtension->LegacySupportRegister = legsup;
    if ((legsup & LEGSUP_BIOS_MODE) != 0) {


        // BUGBUG save old state
        deviceExtension->HcFlags |= HCFLAG_USBBIOS;

        UHCD_KdPrint((1, "'*** uhcd detected a USB legacy BIOS ***\n"));

        //
        // if BIOS mode bits set we have to take over
        //

        // shut off host controller SMI enable
        legsup &= ~0x10;
        UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                FALSE,
                                &legsup,
                                0xc0,     // offset of legacy bios reg
                                sizeof(legsup));

        //
        // BUGBUG
        // out 0xff to 64h if possible
        //

        //
        // take control
        //

        // read
        UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                TRUE,
                                &legsup,
                                0xc0,     // offset of legacy bios reg
                                sizeof(legsup));

        legsup = LEGSUP_HCD_MODE;
        //write
        UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                FALSE,
                                &legsup,
                                0xc0,     // offset of legacy bios reg
                                sizeof(legsup));

    }

    UHCD_KdPrint((2, "'exit UHCD_StopBIOS 0x%x\n", ntStatus));

    return ntStatus;
}


NTSTATUS
UHCD_StartBIOS(
    IN  PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This routine start a USB bios.

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    USHORT cmd, legsup;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    UHCD_KdPrint((1, "'Returning Control to USB BIOS, base port = %x\n",
        deviceExtension->DeviceRegisters[0]));

    // restore saved reg values

    WRITE_PORT_USHORT(STATUS_REG(deviceExtension), 0xff);

    WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension),
        deviceExtension->BiosIntMask);
    WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
        deviceExtension->BiosFrameListBase);

    legsup = deviceExtension->LegacySupportRegister;

    //write
    UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                         FALSE,
                         &legsup,
                         0xc0,     // offset of legacy bios reg
                         sizeof(legsup));


    // read
    UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                         TRUE,
                         &legsup,
                         0xc0,     // offset of legacy bios reg
                         sizeof(legsup));

    // enable SMI
    legsup |= 0x10;
    //write
    UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                         FALSE,
                         &legsup,
                         0xc0,     // offset of legacy bios reg
                         sizeof(legsup));

    cmd = deviceExtension->BiosCmd | UHCD_CMD_RUN;
    WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),
            cmd);

    return ntStatus;
}

#endif //USB_BIOS

NTSTATUS
UHCD_GetResources(
    IN PDEVICE_OBJECT DeviceObject,
    IN PCM_RESOURCE_LIST ResourceList,
    IN OUT PUHCD_RESOURCES Resources
    )

/*++

Routine Description:

Arguments:

    DeviceObject        - DeviceObject for this USB controller.

    ResourceList        - Resources for this controller.

Return Value:

    NT status code.

--*/

{
    ULONG i;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR interrupt;
    PHYSICAL_ADDRESS cardAddress;
    ULONG addressSpace;
    PDEVICE_EXTENSION deviceExtension;
    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
    PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDescriptor;
    BOOLEAN gotIO=FALSE, gotIRQ=FALSE;

    UHCD_KdPrint((2, "'enter UHCD_GetResources\n"));

    LOGENTRY(LOG_MISC, 'GRES', 0, 0, 0);

    if (ResourceList == NULL) {
        UHCD_KdPrint((1, "'got no resources, failing start.\n"));
        return STATUS_NONE_MAPPED;
    }

    fullResourceDescriptor = &ResourceList->List[0];
    PartialResourceList = &fullResourceDescriptor->PartialResourceList;

    deviceExtension = DeviceObject->DeviceExtension;

    //
    // Initailize our page list, do this here so that UHCD_CleanupDevice can
    // clean up if necessary.
    //

    InitializeListHead(&deviceExtension->PageList);

    deviceExtension->HcFlags |= HCFLAG_NEED_CLEANUP;

    for (i = 0; i < PartialResourceList->Count && NT_SUCCESS(ntStatus); i++) {

        switch (PartialResourceList->PartialDescriptors[i].Type) {
        case CmResourceTypePort:

            //
            // Set up AddressSpace to be of type Port I/O
            //

            UHCD_KdPrint((1, "'Port Resources Found @ %x, %d Ports Available \n",
                PartialResourceList->PartialDescriptors[i].u.Port.Start.LowPart,
                PartialResourceList->PartialDescriptors[i].u.Port.Length));

#if DBG
            deviceExtension->Port =
                PartialResourceList->PartialDescriptors[i].u.Port.Start.LowPart;
#endif

            addressSpace =
                (PartialResourceList->PartialDescriptors[i].Flags & CM_RESOURCE_PORT_IO) ==
                CM_RESOURCE_PORT_IO? 1:0;

            cardAddress=PartialResourceList->PartialDescriptors[i].u.Port.Start;

            if (!addressSpace) {
                deviceExtension->HcFlags |= HCFLAG_UNMAP_REGISTERS;
                deviceExtension->DeviceRegisters[0] =
                    MmMapIoSpace(
                    cardAddress,
                    PartialResourceList->PartialDescriptors[i].u.Port.Length,
                    FALSE);
            } else {
                deviceExtension->HcFlags &= ~HCFLAG_UNMAP_REGISTERS;
                deviceExtension->DeviceRegisters[0] =
                    (PVOID)(ULONG_PTR)cardAddress.QuadPart;
            }

            //
            // see if we successfully mapped the IO regs
            //

            if (!deviceExtension->DeviceRegisters[0]) {
                UHCD_KdPrint((2, "'Couldn't map the device registers. \n"));
                ntStatus = STATUS_NONE_MAPPED;

            } else {
                UHCD_KdPrint((2, "'Mapped device registers to 0x%x.\n",
                    deviceExtension->DeviceRegisters[0]));
                gotIO=TRUE;
                deviceExtension->HcFlags |= HCFLAG_GOT_IO;
            }

            break;

        case CmResourceTypeInterrupt:

            //
            // Get Vector, level, and affinity information for this interrupt.
            //

            UHCD_KdPrint((1, "'Interrupt Resources Found!  Level = %x Vector = %x\n",
                PartialResourceList->PartialDescriptors[i].u.Interrupt.Level,
                PartialResourceList->PartialDescriptors[i].u.Interrupt.Vector
                ));

            interrupt = &PartialResourceList->PartialDescriptors[i];
            gotIRQ=TRUE;

            //BUGBUG ??
            //KeInitializeSpinLock(&DeviceExtension->InterruptSpinLock);


            //DeviceExtension->InterruptMode = PartialResourceList->PartialDescriptors[i].Flags;

            break;

        case CmResourceTypeMemory:

            //
            // Set up AddressSpace to be of type Memory mapped I/O
            //

            UHCD_KdPrint((1, "'Memory Resources Found @ %x, Length = %x\n",
                PartialResourceList->PartialDescriptors[i].u.Memory.Start.LowPart,
                PartialResourceList->PartialDescriptors[i].u.Memory.Length
                ));

            // we should never get memory resources
            UHCD_KdTrap(("PnP gave us memory resources!!\n"));

            break;

        } /* switch */
    } /* for */

    //
    // Sanity check that we got enough resources.
    //
    if (!gotIO || !gotIRQ) {
        UHCD_KdPrint((1, "'Missing IO or IRQ: failing to start\n"));
        ntStatus = STATUS_NONE_MAPPED;
    }

    if (NT_SUCCESS(ntStatus)) {
        //
        // Note that we have all the resources we need
        // to start
        //

        //
        // Set up our interrupt.
        //

        UHCD_KdPrint((2, "'requesting interrupt vector %x level %x\n",
                                interrupt->u.Interrupt.Level,
                                interrupt->u.Interrupt.Vector));

        Resources->InterruptLevel=(KIRQL)interrupt->u.Interrupt.Level;
        Resources->InterruptVector=interrupt->u.Interrupt.Vector;
        Resources->Affinity=interrupt->u.Interrupt.Affinity;

        //
        // Initialize the interrupt object for the controller.
        //

        deviceExtension->InterruptObject = NULL;
        Resources->ShareIRQ =
            interrupt->ShareDisposition == CmResourceShareShared ? TRUE : FALSE;
        Resources->InterruptMode =
            interrupt->Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive;

#ifdef MAX_DEBUG
        UHCD_KdPrint((2, "'interrupt->ShareDisposition %x\n", interrupt->ShareDisposition));
        if (!Resources->ShareIRQ) {
            TEST_TRAP();
        }
#endif
    }


    UHCD_KdPrint((2, "'exit UHCD_GetResources (%x)\n", ntStatus));
    LOGENTRY(LOG_MISC, 'GRS1', 0, 0, ntStatus);

    return ntStatus;
}

NTSTATUS
UHCD_StartDevice(
    IN PDEVICE_OBJECT DeviceObject,
    IN PUHCD_RESOURCES Resources
    )

/*++

Routine Description:

    This routine initializes the DeviceObject for a given instance
    of a USB host controller.

Arguments:

    DeviceObject  - DeviceObject for this USB controller.

    Resources - our assigned resources

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    LARGE_INTEGER deltaTime;
    DEVICE_CAPABILITIES deviceCapabilities;
    ULONG numberOfMapRegisters = (ULONG)-1;
    DEVICE_DESCRIPTION deviceDescription;
    BOOLEAN isPIIX3or4;
    ULONG disableSelectiveSuspendValue = 0;
    WCHAR disableSelectiveSuspendKey[] = DISABLE_SELECTIVE_SUSPEND;
    WCHAR clocksPerFrameKey[] = CLOCKS_PER_FRAME;
    WCHAR recClocksPerFrameKey[] = REC_CLOCKS_PER_FRAME;
    LONG clocksPerFrame = 0;
    LONG recClocksPerFrame = 0;
    PUSBD_EXTENSION usbdExtension;

    PAGED_CODE();
    deviceExtension = DeviceObject->DeviceExtension;

    // get the debug options from the registry
#if DBG
    UHCD_GetClassGlobalDebugRegistryParameters();
#endif

    UHCD_KdPrint((2, "'enter UHCD_StartDevice\n"));

    deviceExtension->HcFlags &= ~HCFLAG_HCD_STOPPED;

    // enable the idle check routine
    deviceExtension->HcFlags &= ~HCFLAG_DISABLE_IDLE;

    LOGENTRY(LOG_MISC, 'STRT', deviceExtension->TopOfStackDeviceObject, 0, 0);

    //
    // sanity check our registers
    //
    {
    USHORT cmd;

    cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
    if (cmd == 0xffff) {
        UHCD_KdPrint((0, "'Controller Registers are bogus -- this is a bug\n"));
        UHCD_KdPrint((0, "'Controller will FAIL to start\n"));
        TRAP();
        // regs are bogus -- clear the gotio flag so we don't
        // try to access the registers any more
        deviceExtension->HcFlags &= ~HCFLAG_GOT_IO;
        ntStatus = STATUS_UNSUCCESSFUL;
    }
    }

    if (!NT_SUCCESS(ntStatus)) {
        goto UHCD_InitializeDeviceExit;
    }

    //
    // Create our adapter object
    //

    RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));

    //BUGBUG check these
    deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
    deviceDescription.Master = TRUE;
    deviceDescription.ScatterGather = TRUE;
    deviceDescription.Dma32BitAddresses = TRUE;

    deviceDescription.InterfaceType = PCIBus;
    deviceDescription.DmaWidth = Width32Bits;
    deviceDescription.DmaSpeed = Compatible;

    deviceDescription.MaximumLength = (ULONG)-1;

    deviceExtension->NumberOfMapRegisters = (ULONG)-1;
    deviceExtension->AdapterObject =
        IoGetDmaAdapter(deviceExtension->PhysicalDeviceObject,
                        &deviceDescription,
                        &deviceExtension->NumberOfMapRegisters);

    if (deviceExtension->AdapterObject == NULL) {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto UHCD_InitializeDeviceExit;
    }

    //
    // We found a host controller or a host controller found us, do the final
    // stages of initialization.
    // Setup state variables for the host controller
    //

    ntStatus = UHCD_QueryCapabilities(deviceExtension->TopOfStackDeviceObject,
                                     &deviceCapabilities);

    if (!NT_SUCCESS(ntStatus)) {
        goto UHCD_InitializeDeviceExit;
    }

    if (deviceExtension->HcFlags & HCFLAG_MAP_SX_TO_D3) {
        ULONG i;

        for (i=PowerSystemSleeping1; i< PowerSystemMaximum; i++) {

            deviceCapabilities.DeviceState[i] = PowerDeviceD3;

            UHCD_KdPrint((1, "'changing -->S%d = D%d\n", i-1,
                deviceCapabilities.DeviceState[i]-1));
        }
    }

#if DBG
    {
        ULONG i;
        UHCD_KdPrint((1, "'HCD Device Caps returned from PCI:\n"));
        UHCD_KdPrint((1, "'\tSystemWake = S%d\n", deviceCapabilities.SystemWake-1));
        UHCD_KdPrint((1, "'\tDeviceWake = D%d\n", deviceCapabilities.DeviceWake-1));
        UHCD_KdPrint((1, "'\tDevice State Map:\n"));

        for (i=0; i< PowerSystemHibernate; i++) {
             UHCD_KdPrint((1, "'\t-->S%d = D%d\n", i-1,
                 deviceCapabilities.DeviceState[i]-1));
        }
    }
#endif

    USBD_RegisterHcDeviceCapabilities(DeviceObject,
                                      &deviceCapabilities,
                                      UHCD_RootHubPower);

    //
    // Detect the Host Controller stepping version.
    //
    {
        UCHAR revisionId;
        USHORT vendorId, deviceId;

        UHCD_KdPrint((2, "'Get the stepping version\n"));

        UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                             TRUE,
                             &vendorId,
                             0,
                             sizeof(vendorId));

        UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                             TRUE,
                             &deviceId,
                             2,
                             sizeof(deviceId));

        UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                             TRUE,
                             &revisionId,
                             8,
                             sizeof(revisionId));

        UHCD_KdPrint((1, "'HC vendor = %x device = %x rev = %x\n",
                vendorId, deviceId, revisionId));

        if (vendorId == UHCD_INTEL_VENDOR_ID &&
            deviceId == UHCD_PIIX3_DEVICE_ID) {
            UHCD_KdPrint((1, "'detected PIIX3\n"));
            deviceExtension->ControllerType = UHCI_HW_VERSION_PIIX3;
        } else if (vendorId == UHCD_INTEL_VENDOR_ID &&
                   deviceId == UHCD_PIIX4_DEVICE_ID) {
            UHCD_KdPrint((1, "'detected PIIX4\n"));
            deviceExtension->ControllerType = UHCI_HW_VERSION_PIIX4;
        } else {
            UHCD_KdPrint((1, "'detected unknown host controller type\n"));
            deviceExtension->ControllerType = UHCI_HW_VERSION_UNKNOWN;
        }


        //
        // is this an A1 stepping version of the piix3?
        //
        if (revisionId == 0 &&
            vendorId == UHCD_INTEL_VENDOR_ID &&
            deviceId == UHCD_PIIX3_DEVICE_ID) {
            //yes, we will fail to load on the A1 systems
            UHCD_KdPrint((0, "'Intel USB HC stepping version A1 is not supported\n"));
            deviceExtension->SteppingVersion = UHCD_A1_STEP;
            ntStatus = STATUS_UNSUCCESSFUL;
        } else {
#ifdef ENABLE_B0_FEATURES
            deviceExtension->SteppingVersion = UHCD_B0_STEP;
#else
            deviceExtension->SteppingVersion = UHCD_A1_STEP;
#endif //ENABLE_B0_FEATURES
        }

#ifdef USB_BIOS
        if (NT_SUCCESS(ntStatus)) {
            ntStatus = UHCD_StopBIOS(DeviceObject);
        }
#endif //USB_BIOS
    }

    {
        USHORT legsup;

        UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                             TRUE,
                             &legsup,
                             0xc0,     // offset of legacy bios reg
                             sizeof(legsup));

        LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
        // set the PIRQD routing bit
        legsup |= LEGSUP_USBPIRQD_EN;

        UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                FALSE,
                                &legsup,
                                0xc0,     // offset of legacy bios reg
                                sizeof(legsup));
    }

    if (!NT_SUCCESS(ntStatus)) {
        goto UHCD_InitializeDeviceExit;
    }

    ntStatus = UHCD_InitializeSchedule(DeviceObject);

    LOGENTRY(LOG_MISC, 'INIs', 0, 0, ntStatus);
    if (!NT_SUCCESS(ntStatus)) {
        UHCD_KdPrint((0, "'InitializeSchedule Failed! %x\n", ntStatus));
        goto UHCD_InitializeDeviceExit;
    }

    //
    // initialization all done, last step is to connect the interrupt & DPCs.
    //

    KeInitializeDpc(&deviceExtension->IsrDpc,
                    UHCD_IsrDpc,
                    DeviceObject);

    UHCD_KdPrint((2, "'requesting interrupt vector %x level %x\n",
                                Resources->InterruptLevel,
                                Resources->InterruptVector));

    ntStatus = IoConnectInterrupt(
                 &(deviceExtension->InterruptObject),
                 (PKSERVICE_ROUTINE) UHCD_InterruptService,
                 (PVOID) DeviceObject,
                 (PKSPIN_LOCK)NULL,
                 Resources->InterruptVector,
                 Resources->InterruptLevel,
                 Resources->InterruptLevel,
                 Resources->InterruptMode,
                 Resources->ShareIRQ,
                 Resources->Affinity,
                 FALSE);            // BUGBUG FloatingSave, this is configurable

    LOGENTRY(LOG_MISC, 'IOCi', 0, 0, ntStatus);
    if (!NT_SUCCESS(ntStatus)) {
        UHCD_KdPrint((0, "'IoConnectInterrupt Failed! %x\n", ntStatus));
        goto UHCD_InitializeDeviceExit;
    }

    // consider ourselves 'ON'
    deviceExtension->CurrentDevicePowerState = PowerDeviceD0;

    //
    // Initialize the controller registers.
    // NOTE:
    // We don't do reset until the interrrupt is hooked because
    // the HC for some reason likes to generate an interrupt
    // (USBINT) when it is reset.
    //

    ntStatus = UHCD_StartGlobalReset(DeviceObject);

    LOGENTRY(LOG_MISC, 'GLBr', 0, 0, ntStatus);
    if (!NT_SUCCESS(ntStatus)) {
        UHCD_KdPrint((0, "'GlobalReset Failed! %x\n", ntStatus));
        goto UHCD_InitializeDeviceExit;
    }
    //
    // Everything is set, we need to wait for the
    // global reset of the Host controller to complete.
    //

    // 20 ms to reset...
    deltaTime.QuadPart = -10000 * 20;

    //
    // block here until reset is complete
    //

    (VOID) KeDelayExecutionThread(KernelMode,
                                  FALSE,
                                  &deltaTime);

    ntStatus = UHCD_CompleteGlobalReset(DeviceObject);

    // 10 ms for devices to recover
    // BUGBUG seems the Philips speakers need this
//    deltaTime.QuadPart = -10000 * 1000;
//
//    (VOID) KeDelayExecutionThread(KernelMode,
//                                  FALSE,
//                                  &deltaTime);

    if (!NT_SUCCESS(ntStatus)) {
        goto UHCD_InitializeDeviceExit;
    }

    //
    // bus reset complete, now activate the root hub emulation
    //
#ifdef ROOT_HUB
    UHCD_KdPrint((2, "'Initialize Root Hub\n"));

    //
    // BUGBUG hard coded to two ports
    //
    isPIIX3or4 = (deviceExtension->ControllerType == UHCI_HW_VERSION_PIIX3) ||
                  (deviceExtension->ControllerType == UHCI_HW_VERSION_PIIX4);

    USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
                                 &disableSelectiveSuspendValue,
                                 sizeof(disableSelectiveSuspendValue),
                                 disableSelectiveSuspendKey,
                                 sizeof(disableSelectiveSuspendKey));

    USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
                                 &recClocksPerFrame,
                                 sizeof(recClocksPerFrame),
                                 recClocksPerFrameKey,
                                 sizeof(recClocksPerFrameKey));

    deviceExtension->RegRecClocksPerFrame =
        recClocksPerFrame;

    if ((deviceExtension->RootHub =
         RootHub_Initialize(DeviceObject,
                            2,
                            (BOOLEAN)(!isPIIX3or4 &&
                                      !disableSelectiveSuspendValue))) == NULL) {

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
#if DBG
    if (!isPIIX3or4 && !disableSelectiveSuspendValue) {
        UHCD_KdPrint ((1, "'selective suspend enabled\n"));
    } else {
        UHCD_KdPrint ((1, "'selective suspend disabled\n"));
    }
#endif

    // HACK - Tell USBD if this HC is PIIX3 or PIIX4.

    if (isPIIX3or4) {
        usbdExtension = (PUSBD_EXTENSION)deviceExtension;
        usbdExtension->IsPIIX3or4 = TRUE;
    }

    // END HACK

    deviceExtension->RootHubTimersActive = 0;
#endif //ROOT_HUB

    //
    // our current power state is 'ON'
    //
    deviceExtension->CurrentDevicePowerState = PowerDeviceD0;

    // this will disable the controller if nothing
    // is initailly connected to the ports
    KeQuerySystemTime(&deviceExtension->LastIdleTime);
    deviceExtension->IdleTime = 100000000;
    deviceExtension->XferIdleTime = 0;

UHCD_InitializeDeviceExit:

    if (!NT_SUCCESS(ntStatus)) {
#ifdef MAX_DEBUG
        TEST_TRAP();
#endif
        UHCD_KdPrint((2, "'Initialization Failed, cleaning up \n"));

        //
        // The initialization failed.  Cleanup resources before exiting.
        //
        //
        // Note:  No need/way to undo the KeInitializeDpc or
        //        KeInitializeTimer calls.
        //

        UHCD_CleanupDevice(DeviceObject);

    }

    UHCD_KdPrint((2, "'exit UHCD_StartDevice (%x)\n", ntStatus));

    return ntStatus;
}


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

Routine Description:

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT Status code.

--*/
{
    NTSTATUS ntStatus;
    UHCD_RESOURCES resources;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_EXTENSION deviceExtension;
    ULONG forceLowPowerState = 0;

    PAGED_CODE();

    deviceExtension = DeviceObject->DeviceExtension;
    // initailize extension here
    deviceExtension->HcFlags = 0;

    UHCD_GetClassGlobalRegistryParameters(&forceLowPowerState);

    if (forceLowPowerState) {
        deviceExtension->HcFlags |= HCFLAG_MAP_SX_TO_D3;
    }


    irpStack = IoGetCurrentIrpStackLocation (Irp);

    ntStatus = UHCD_GetResources(DeviceObject,
                                 irpStack->Parameters.StartDevice.AllocatedResourcesTranslated,
                                 &resources);

    if (NT_SUCCESS(ntStatus)) {
        ntStatus = UHCD_StartDevice(DeviceObject,
                                    &resources);
    }

    //
    // Set the IRP status because USBD doesn't
    //

    Irp->IoStatus.Status = ntStatus;

    LOGENTRY(LOG_MISC, 'dfST', 0, 0, ntStatus);

    return ntStatus;
}


BOOLEAN
UHCD_UpdateFrameCounter(
    IN PVOID Context
    )
/*++

Routine Description:

   Starts the USB host controller executing the schedule.
   Start Controller is called in by KeSynchronizeExecution.

Arguments:

   Context - DeviceData for this USB controller.

Return Value:

   TRUE

--*/
{
    PDEVICE_EXTENSION deviceExtension;
    ULONG frameNumber;
    ULONG currentFrame, highPart;

    deviceExtension = Context;

    // This code maintains the 32-bit frame counter

    frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));

    // did the sign bit change ?
    if ((deviceExtension->LastFrame ^ frameNumber) & 0x0400) {
        // Yes
        deviceExtension->FrameHighPart += 0x0800 -
            ((frameNumber ^ deviceExtension->FrameHighPart) & 0x0400);
    }

    // remember the last frame number
    deviceExtension->LastFrame = frameNumber;

    // calc frame number and update las frame processed
    // if necseesary

    highPart = deviceExtension->FrameHighPart;

    // get 11-bit frame number, high 17-bits are 0
    //frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));

    currentFrame = ((frameNumber & 0x0bff) | highPart) +
        ((frameNumber ^ highPart) & 0x0400);

    //UHCD_KdPrint((2, "'currentFrame2 = %x\n", currentFrame));

    if (deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE) {
        deviceExtension->LastFrameProcessed = currentFrame - 1;
    }

    return TRUE;
}

VOID
UHCD_DisableIdleCheck(
    IN PDEVICE_EXTENSION DeviceExtension
    )
{
    KIRQL irql;

    KeAcquireSpinLock(&DeviceExtension->HcFlagSpin, &irql);

    DeviceExtension->HcFlags |= HCFLAG_DISABLE_IDLE;

    KeReleaseSpinLock(&DeviceExtension->HcFlagSpin, irql);
}

VOID
UHCD_WakeIdle(
    IN  PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    takes the controller out of the idle state

Arguments:

Return Value:

    None

--*/

{
    KIRQL irql;
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    LOGENTRY(LOG_MISC, 'wIDL', DeviceObject, 0, 0);

    KeRaiseIrql(DISPATCH_LEVEL, &irql);
    deviceExtension->XferIdleTime = 0;
    UHCD_CheckIdle(DeviceObject);
    KeLowerIrql(irql);
}


VOID
UHCD_CheckIdle(
    IN  PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    If the controllers hub ports are not connected this function
    stops the host controller

    If there are no iso enpoints open then this function
    disables the rollover interrupt

Arguments:

    DeviceObject - DeviceObject of the controller to stop

Return Value:

    NT status code.

--*/

{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    BOOLEAN portsIdle = TRUE;
    USHORT cmd;
    LARGE_INTEGER timeNow;
    BOOLEAN fastIsoEndpointListEmpty;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    fastIsoEndpointListEmpty = IsListEmpty(&deviceExtension->FastIsoEndpointList);

    KeAcquireSpinLockAtDpcLevel(&deviceExtension->HcFlagSpin);

    if (deviceExtension->HcFlags & HCFLAG_DISABLE_IDLE) {
        goto UHCD_CheckIdle_Done;
    }

    UHCD_ASSERT(deviceExtension->InterruptObject);
    if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
                                UHCD_UpdateFrameCounter,
                                deviceExtension)) {
        TRAP(); //something has gone terribly wrong
    }

    portsIdle = RootHub_PortsIdle(deviceExtension->RootHub) &&
                    IsListEmpty(&deviceExtension->EndpointList);

    if (portsIdle) {

        if (!(deviceExtension->HcFlags & HCFLAG_IDLE)) {
            // we are not idle,
            // see how long ports have been idle if it
            // is longer then 10 seconds stop the
            // controller
            KeQuerySystemTime(&timeNow);

            if (deviceExtension->IdleTime == 0) {
                KeQuerySystemTime(&deviceExtension->LastIdleTime);
                deviceExtension->IdleTime = 1;
            }

            deviceExtension->IdleTime +=
                (LONG) (timeNow.QuadPart -
                deviceExtension->LastIdleTime.QuadPart);
            deviceExtension->LastIdleTime = timeNow;

            // ports are idle stop the controller
            if (// 10 seconds in 100ns units
                deviceExtension->IdleTime > 100000000) {

                cmd = READ_PORT_USHORT(
                    COMMAND_REG(deviceExtension)) & ~UHCD_CMD_RUN;
                WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

                deviceExtension->HcFlags |= HCFLAG_IDLE;
                deviceExtension->IdleTime = 0;

                LOGENTRY(LOG_MISC, 'sOFF', DeviceObject, 0, 0);

                UHCD_KdPrint((2, "'HC stopped\n"));

            }
        }

    } else {
        // ports are active start the controller
        deviceExtension->IdleTime = 0;
        if (deviceExtension->HcFlags & HCFLAG_IDLE) {

            UHCD_KdPrint((2, "'ports active, break idle\n"));

            // reset the frame list current index
            WRITE_PORT_USHORT(
                FRAME_LIST_CURRENT_INDEX_REG(deviceExtension), 0);

            // re-initialize internal frame counters.
            deviceExtension->FrameHighPart =
                deviceExtension->LastFrame = 0;


            cmd = READ_PORT_USHORT(
                COMMAND_REG(deviceExtension)) | UHCD_CMD_RUN;
            WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);

            LOGENTRY(LOG_MISC, 's-ON', DeviceObject, 0, 0);

            deviceExtension->HcFlags &= ~HCFLAG_IDLE;

            UHCD_KdPrint((2, "'HC restart\n"));

        }
    }

    //
    // now deal with the HC rollover interrupt
    //

    if (!(deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE)) {

        //
        // rollover ints are on,
        // see how long it has been since the last data transfer
        //

        KeQuerySystemTime(&timeNow);

        if (deviceExtension->XferIdleTime == 0) {
            KeQuerySystemTime(&deviceExtension->LastXferIdleTime);
            deviceExtension->XferIdleTime = 1;
        }

        deviceExtension->XferIdleTime +=
                (LONG) (timeNow.QuadPart -
            deviceExtension->LastXferIdleTime.QuadPart);
        deviceExtension->LastXferIdleTime = timeNow;
    }

    if (deviceExtension->XferIdleTime > 100000000 &&
        !fastIsoEndpointListEmpty) {

        // are we currently idle

        if (!(deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE)) {
            //
            // No
            //
            // if we have seen no data transfers submitted
            // for 10 seconds disable the rollover interrupt
            //

            // turn off the interrupts for rollover
            deviceExtension->HcFlags |= HCFLAG_ROLLOVER_IDLE;

            deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete =
             deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 0;

            //UHCD_KdPrint((2, "UHCD: HC idle ints stopped\n"));
        }

    } else {
        //
        // this will turn on the rollover interrupts
        // when we see transfers
        //

        // are we currently idle

        if (deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE) {

            //
            // Yes
            //

            UHCD_KdPrint((2, "'activate rollover ints\n"));

            deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete =
              deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 1;

            deviceExtension->HcFlags &= ~HCFLAG_ROLLOVER_IDLE;

            //UHCD_KdPrint((2, "UHCD: HC idle ints started\n"));
        }
    }

UHCD_CheckIdle_Done:

    KeReleaseSpinLockFromDpcLevel(&deviceExtension->HcFlagSpin);
}


NTSTATUS
UHCD_GetSOFRegModifyValue(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PULONG SofModifyValue
    )
/*++

Routine Description:

Arguments:

    DeviceObject - DeviceObject for this USB controller.

Return Value:

    NT Status code.

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    LONG clocksPerFrame = 12000;
    LONG recClocksPerFrame;
    LONG sofModify;
    PDEVICE_EXTENSION deviceExtension;

    PAGED_CODE();

    // the default
    *SofModifyValue = 64;

    deviceExtension = DeviceObject->DeviceExtension;

    recClocksPerFrame =
        deviceExtension->RegRecClocksPerFrame;

    if (recClocksPerFrame && clocksPerFrame) {
        sofModify = recClocksPerFrame - clocksPerFrame + 64;
        *SofModifyValue = sofModify;
    }

    UHCD_KdPrint((1, "'Clocks/Frame %d Recommended Clocks/Frame %d SofModify %d\n",
        clocksPerFrame, recClocksPerFrame, *SofModifyValue));

    return ntStatus;
}


NTSTATUS
UHCD_GetConfigValue(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
/*++

Routine Description:

   This routine is a callback routine for RtlQueryRegistryValues
    It is called for each entry in the Parameters
    node to set the config values. The table is set up
    so that this function will be called with correct default
    values for keys that are not present.

Arguments:

    ValueName - The name of the value (ignored).
   ValueType - The type of the value
   ValueData - The data for the value.
   ValueLength - The length of ValueData.
   Context - A pointer to the CONFIG structure.
   EntryContext - The index in Config->Parameters to save the value.

Return Value:

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    UHCD_KdPrint((2, "'Type 0x%x, Length 0x%x\n", ValueType, ValueLength));

   switch (ValueType) {
   case REG_DWORD:
        RtlCopyMemory(EntryContext, ValueData, sizeof(ULONG));
        break;
    case REG_BINARY:
        // BUGBUG we are only set up to read a byte
        RtlCopyMemory(EntryContext, ValueData, 1);
        break;
    default:
        ntStatus = STATUS_INVALID_PARAMETER;
    }
    return ntStatus;
}



VOID
UHCD_SetControllerD0(
    IN PDEVICE_OBJECT DeviceObject
    )
 /* ++
  *
  * Description:
  *
  * Work item scheduled to do processing at passive
  * level when the controller is put in D0 by PNP/POWER.
  *
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //if (deviceExtension->CurrentDevicePowerState == PowerDeviceD0) {
    //    TEST_TRAP();
    //   return;
    //}

    LOGENTRY(LOG_MISC, 'POWC', deviceExtension->CurrentDevicePowerState,
        DeviceObject, 0);

    switch (deviceExtension->CurrentDevicePowerState) {
    case PowerDeviceD3:

        UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (OFF)\n"));
        deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
        break;

    case PowerDeviceD1:
    case PowerDeviceD2:
        UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (SUSPEND)\n"));
        break;
    case PowerDeviceD0:
        // probably a bug in the kernel/configmg or usbd
        UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (ON)\n"));
        break;
    } /* case */

#ifdef USB_BIOS
    //if (deviceExtension->HcFlags & HCFLAG_USBBIOS) {
   UHCD_StopBIOS(DeviceObject);
   //}
#endif //USB_BIOS
    {
        USHORT legsup;

        UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
                             TRUE,
                             &legsup,
                             0xc0,     // offset of legacy bios reg
                             sizeof(legsup));

        LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
        // set the PIRQD routing bit
        legsup |= LEGSUP_USBPIRQD_EN;

        UHCD_ReadWriteConfig(   deviceExtension->PhysicalDeviceObject,
                                FALSE,
                                &legsup,
                                0xc0,     // offset of legacy bios reg
                                sizeof(legsup));
    }

    deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
    UHCD_KdPrint((1, " Host Controller entered (D0)\n"));

}

#if DBG

NTSTATUS
UHCD_GetClassGlobalDebugRegistryParameters(
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus;
    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
    PWCHAR usb = L"usb";
    extern ULONG UHCD_Debug_Trace_Level;
    extern ULONG UHCD_W98_Debug_Trace;
    extern ULONG UHCD_Debug_Asserts;

    PAGED_CODE();

    //
    // Set up QueryTable to do the following:
    //

    // spew level
    QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
    QueryTable[0].Flags = 0;
    QueryTable[0].Name = DEBUG_LEVEL;
    QueryTable[0].EntryContext = &UHCD_Debug_Trace_Level;
    QueryTable[0].DefaultType = REG_DWORD;
    QueryTable[0].DefaultData = &UHCD_Debug_Trace_Level;
    QueryTable[0].DefaultLength = sizeof(UHCD_Debug_Trace_Level);

    // ntkern trace buffer
    QueryTable[1].QueryRoutine = UHCD_GetConfigValue;
    QueryTable[1].Flags = 0;
    QueryTable[1].Name = DEBUG_WIN9X;
    QueryTable[1].EntryContext = &UHCD_W98_Debug_Trace;
    QueryTable[1].DefaultType = REG_DWORD;
    QueryTable[1].DefaultData = &UHCD_W98_Debug_Trace;
    QueryTable[1].DefaultLength = sizeof(UHCD_W98_Debug_Trace);

    //
    // Stop
    //
    QueryTable[2].QueryRoutine = NULL;
    QueryTable[2].Flags = 0;
    QueryTable[2].Name = NULL;

    ntStatus = RtlQueryRegistryValues(
                RTL_REGISTRY_SERVICES,
                usb,
                QueryTable,     // QueryTable
                NULL,           // Context
                NULL);          // Environment

    if (NT_SUCCESS(ntStatus)) {
         UHCD_KdPrint((1, "'Debug Trace Level Set: (%d)\n", UHCD_Debug_Trace_Level));

        if (UHCD_W98_Debug_Trace) {
            UHCD_KdPrint((1, "'NTKERN Trace is ON\n"));
        } else {
            UHCD_KdPrint((1, "'NTKERN Trace is OFF\n"));
        }

        if (UHCD_Debug_Trace_Level > 0) {
            ULONG UHCD_Debug_Asserts = 1;
        }
    }

    if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}

#endif

NTSTATUS
UHCD_GetClassGlobalRegistryParameters(
    IN OUT PULONG ForceLowPowerState
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus;
    UCHAR toshibaLegacyFlags = 0;
    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
    PWCHAR usb = L"class\\usb";

    PAGED_CODE();

    //
    // Set up QueryTable to do the following:
    //

    // force power mapping
    QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
    QueryTable[0].Flags = 0;
    QueryTable[0].Name = L"ForceLowPowerState";
    QueryTable[0].EntryContext = ForceLowPowerState;
    QueryTable[0].DefaultType = REG_DWORD;
    QueryTable[0].DefaultData = ForceLowPowerState;
    QueryTable[0].DefaultLength = sizeof(*ForceLowPowerState);

    //
    // Stop
    //
    QueryTable[1].QueryRoutine = NULL;
    QueryTable[1].Flags = 0;
    QueryTable[1].Name = NULL;

    ntStatus = RtlQueryRegistryValues(
//                 RTL_REGISTRY_ABSOLUTE,    // RelativeTo
                RTL_REGISTRY_SERVICES,
//                 UnicodeRegistryPath->Buffer, // Path
                usb,
                QueryTable,     // QueryTable
                NULL,           // Context
                NULL);          // Environment

    if (NT_SUCCESS(ntStatus)) {
        UHCD_KdPrint(( 0, "'HC ForceLowPower = 0x%x\n",
            *ForceLowPowerState));
    }

   if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
       ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}

#if 0
// not used
NTSTATUS
UHCD_GetGlobalRegistryParameters(
    IN OUT PULONG DisableController
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus;
    UCHAR toshibaLegacyFlags = 0;
    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
    PWCHAR usb  = L"usb";

    PAGED_CODE();

    //
    // Set up QueryTable to do the following:
    //

    // legacy flag
    QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
    QueryTable[0].Flags = 0;
    QueryTable[0].Name = L"DisablePIIXUsb";
    QueryTable[0].EntryContext = DisableController;
    QueryTable[0].DefaultType = REG_BINARY;
    QueryTable[0].DefaultData = DisableController;
    QueryTable[0].DefaultLength = sizeof(*DisableController);

    //
    // Stop
    //
    QueryTable[1].QueryRoutine = NULL;
    QueryTable[1].Flags = 0;
    QueryTable[1].Name = NULL;

    ntStatus = RtlQueryRegistryValues(
//                 RTL_REGISTRY_ABSOLUTE,   // RelativeTo
                RTL_REGISTRY_SERVICES,
//                 UnicodeRegistryPath->Buffer,// Path
                usb,
                QueryTable,         // QurryTable
                NULL,               // Context
                NULL);              // Environment

    if (NT_SUCCESS(ntStatus)) {
        UHCD_KdPrint(( 0, "'HC Disable flag = 0x%x\n",
            *DisableController));
    }

   if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
       ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}
#endif