Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / drivers / usb / usbehci / hardware.cpp
diff --git a/drivers/usb/usbehci/hardware.cpp b/drivers/usb/usbehci/hardware.cpp
new file mode 100644 (file)
index 0000000..52478f9
--- /dev/null
@@ -0,0 +1,1490 @@
+/*
+ * PROJECT:     ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        drivers/usb/usbehci/hcd_controller.cpp
+ * PURPOSE:     USB EHCI device driver.
+ * PROGRAMMERS:
+ *              Michael Martin (michael.martin@reactos.org)
+ *              Johannes Anderwald (johannes.anderwald@reactos.org)
+ */
+
+#include "usbehci.h"
+
+#define NDEBUG
+#include <debug.h>
+
+typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
+
+BOOLEAN
+NTAPI
+InterruptServiceRoutine(
+    IN PKINTERRUPT  Interrupt,
+    IN PVOID  ServiceContext);
+
+VOID
+NTAPI
+EhciDeferredRoutine(
+    IN PKDPC Dpc,
+    IN PVOID DeferredContext,
+    IN PVOID SystemArgument1,
+    IN PVOID SystemArgument2);
+
+VOID
+NTAPI
+StatusChangeWorkItemRoutine(PVOID Context);
+
+class CUSBHardwareDevice : public IEHCIHardwareDevice
+{
+public:
+    STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
+
+    STDMETHODIMP_(ULONG) AddRef()
+    {
+        InterlockedIncrement(&m_Ref);
+        return m_Ref;
+    }
+    STDMETHODIMP_(ULONG) Release()
+    {
+        InterlockedDecrement(&m_Ref);
+
+        if (!m_Ref)
+        {
+            delete this;
+            return 0;
+        }
+        return m_Ref;
+    }
+    // com
+    IMP_IUSBHARDWAREDEVICE
+    IMP_IUSBEHCIHARDWARE
+
+    // local
+    BOOLEAN InterruptService();
+    VOID PrintCapabilities();
+    NTSTATUS StartController();
+    NTSTATUS StopController();
+    NTSTATUS ResetController();
+
+    // friend function
+    friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT  Interrupt, IN PVOID  ServiceContext);
+    friend VOID NTAPI EhciDeferredRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
+    friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
+    // constructor / destructor
+    CUSBHardwareDevice(IUnknown *OuterUnknown){}
+    virtual ~CUSBHardwareDevice(){}
+
+protected:
+    LONG m_Ref;                                                                        // reference count
+    PDRIVER_OBJECT m_DriverObject;                                                     // driver object
+    PDEVICE_OBJECT m_PhysicalDeviceObject;                                             // pdo
+    PDEVICE_OBJECT m_FunctionalDeviceObject;                                           // fdo (hcd controller)
+    PDEVICE_OBJECT m_NextDeviceObject;                                                 // lower device object
+    KSPIN_LOCK m_Lock;                                                                 // hardware lock
+    PKINTERRUPT m_Interrupt;                                                           // interrupt object
+    KDPC m_IntDpcObject;                                                               // dpc object for deferred isr processing
+    PVOID VirtualBase;                                                                 // virtual base for memory manager
+    PHYSICAL_ADDRESS PhysicalAddress;                                                  // physical base for memory manager
+    PULONG m_Base;                                                                     // EHCI operational port base registers
+    PDMA_ADAPTER m_Adapter;                                                            // dma adapter object
+    ULONG m_MapRegisters;                                                              // map registers count
+    EHCI_CAPS m_Capabilities;                                                          // EHCI caps
+    USHORT m_VendorID;                                                                 // vendor id
+    USHORT m_DeviceID;                                                                 // device id
+    PQUEUE_HEAD AsyncQueueHead;                                                        // async queue head terminator
+    PEHCIQUEUE m_UsbQueue;                                                              // usb request queue
+    PDMAMEMORYMANAGER m_MemoryManager;                                                 // memory manager
+    HD_INIT_CALLBACK* m_SCECallBack;                                                   // status change callback routine
+    PVOID m_SCEContext;                                                                // status change callback routine context
+    BOOLEAN m_DoorBellRingInProgress;                                                  // door bell ring in progress
+    WORK_QUEUE_ITEM m_StatusChangeWorkItem;                                            // work item for status change callback
+    volatile LONG m_StatusChangeWorkItemStatus;                                        // work item status
+    ULONG m_SyncFramePhysAddr;                                                         // periodic frame list physical address
+    BUS_INTERFACE_STANDARD m_BusInterface;                                             // pci bus interface
+    BOOLEAN m_PortResetInProgress[0xF];                                                // stores reset in progress (vbox hack)
+
+    // read register
+    ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
+
+    // write register
+    VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
+};
+
+//=================================================================================================
+// COM
+//
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::QueryInterface(
+    IN  REFIID refiid,
+    OUT PVOID* Output)
+{
+    if (IsEqualGUIDAligned(refiid, IID_IUnknown))
+    {
+        *Output = PVOID(PUNKNOWN(this));
+        PUNKNOWN(*Output)->AddRef();
+        return STATUS_SUCCESS;
+    }
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+LPCSTR
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetUSBType()
+{
+    return "USBEHCI";
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::Initialize(
+    PDRIVER_OBJECT DriverObject,
+    PDEVICE_OBJECT FunctionalDeviceObject,
+    PDEVICE_OBJECT PhysicalDeviceObject,
+    PDEVICE_OBJECT LowerDeviceObject)
+{
+    PCI_COMMON_CONFIG PciConfig;
+    NTSTATUS Status;
+    ULONG BytesRead;
+
+    DPRINT("CUSBHardwareDevice::Initialize\n");
+
+    //
+    // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
+    //
+    Status =  CreateDMAMemoryManager(&m_MemoryManager);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create DMAMemoryManager Object\n");
+        return Status;
+    }
+
+    //
+    // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
+    //
+    Status = CreateUSBQueue((PUSBQUEUE*)&m_UsbQueue);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create UsbQueue!\n");
+        return Status;
+    }
+
+    //
+    // store device objects
+    //
+    m_DriverObject = DriverObject;
+    m_FunctionalDeviceObject = FunctionalDeviceObject;
+    m_PhysicalDeviceObject = PhysicalDeviceObject;
+    m_NextDeviceObject = LowerDeviceObject;
+
+    //
+    // initialize device lock
+    //
+    KeInitializeSpinLock(&m_Lock);
+
+    //
+    // initialize status change work item
+    //
+    ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
+
+    m_VendorID = 0;
+    m_DeviceID = 0;
+
+    Status = GetBusInterface(PhysicalDeviceObject, &m_BusInterface);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to get BusInterface!\n");
+        return Status;
+    }
+
+    BytesRead = (*m_BusInterface.GetBusData)(m_BusInterface.Context,
+                                           PCI_WHICHSPACE_CONFIG,
+                                           &PciConfig,
+                                           0,
+                                           PCI_COMMON_HDR_LENGTH);
+
+    if (BytesRead != PCI_COMMON_HDR_LENGTH)
+    {
+        DPRINT1("Failed to get pci config information!\n");
+        return STATUS_SUCCESS;
+    }
+
+    m_VendorID = PciConfig.VendorID;
+    m_DeviceID = PciConfig.DeviceID;
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+STDMETHODCALLTYPE
+CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
+{
+    PULONG Register;
+    Register = (PULONG)UsbCmd;
+    WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register);
+}
+
+VOID
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
+{
+    PULONG Register;
+    Register = (PULONG)UsbCmd;
+    *Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD));
+}
+
+ULONG
+CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset)
+{
+    return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset));
+}
+
+VOID
+CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value)
+{
+    WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value);
+}
+
+VOID
+CUSBHardwareDevice::PrintCapabilities()
+{
+    if (m_Capabilities.HCSParams.PortPowerControl)
+    {
+        DPRINT1("Controller EHCI has Port Power Control\n");
+    }
+
+    DPRINT1("Controller Port Routing Rules %lu\n", m_Capabilities.HCSParams.PortRouteRules);
+    DPRINT1("Number of Ports per Companion Controller %lu\n", m_Capabilities.HCSParams.PortPerCHC);
+    DPRINT1("Number of Companion Controller %lu\n", m_Capabilities.HCSParams.CHCCount);
+
+    if (m_Capabilities.HCSParams.PortIndicator)
+    {
+        DPRINT1("Controller has Port Indicators Support\n");
+    }
+
+    if (m_Capabilities.HCSParams.DbgPortNum)
+    {
+        DPRINT1("Controller has Debug Port Support At Port %x\n", m_Capabilities.HCSParams.DbgPortNum);
+    }
+
+    if (m_Capabilities.HCCParams.EECPCapable)
+    {
+        DPRINT1("Controller has Extended Capabilities Support\n");
+    }
+
+    if (m_Capabilities.HCCParams.ParkMode)
+    {
+        DPRINT1("Controller supports Asynchronous Schedule Park\n");
+    }
+
+    if (m_Capabilities.HCCParams.VarFrameList)
+    {
+        DPRINT1("Controller supports Programmable Frame List Size\n");
+    }
+
+    if (m_Capabilities.HCCParams.CurAddrBits)
+    {
+        DPRINT1("Controller uses 64-Bit Addressing\n");
+    }
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::PnpStart(
+    PCM_RESOURCE_LIST RawResources,
+    PCM_RESOURCE_LIST TranslatedResources)
+{
+    ULONG Index, Count;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
+    DEVICE_DESCRIPTION DeviceDescription;
+    PHYSICAL_ADDRESS AsyncPhysicalAddress;
+    PVOID ResourceBase;
+    NTSTATUS Status;
+    UCHAR Value;
+    UCHAR PortCount;
+
+    DPRINT("CUSBHardwareDevice::PnpStart\n");
+    for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
+    {
+        //
+        // get resource descriptor
+        //
+        ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
+
+        switch(ResourceDescriptor->Type)
+        {
+            case CmResourceTypeInterrupt:
+            {
+                KeInitializeDpc(&m_IntDpcObject,
+                                EhciDeferredRoutine,
+                                this);
+
+                Status = IoConnectInterrupt(&m_Interrupt,
+                                            InterruptServiceRoutine,
+                                            (PVOID)this,
+                                            NULL,
+                                            ResourceDescriptor->u.Interrupt.Vector,
+                                            (KIRQL)ResourceDescriptor->u.Interrupt.Level,
+                                            (KIRQL)ResourceDescriptor->u.Interrupt.Level,
+                                            (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
+                                            (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
+                                            ResourceDescriptor->u.Interrupt.Affinity,
+                                            FALSE);
+
+                if (!NT_SUCCESS(Status))
+                {
+                    //
+                    // failed to register interrupt
+                    //
+                    DPRINT1("IoConnect Interrupt failed with %x\n", Status);
+                    return Status;
+                }
+                break;
+            }
+            case CmResourceTypeMemory:
+            {
+                //
+                // get resource base
+                //
+                ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
+                if (!ResourceBase)
+                {
+                    //
+                    // failed to map registers
+                    //
+                    DPRINT1("MmMapIoSpace failed\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+
+                //
+                // Get controllers capabilities
+                //
+                m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase + EHCI_CAPLENGTH);
+                m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG_PTR)ResourceBase + EHCI_HCIVERSION));
+                m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + EHCI_HCSPARAMS));
+                m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + EHCI_HCCPARAMS));
+
+                DPRINT1("Controller Capabilities Length 0x%x\n", m_Capabilities.Length);
+                DPRINT1("Controller EHCI Version 0x%x\n", m_Capabilities.HCIVersion);
+                DPRINT1("Controller EHCI Caps HCSParamsLong 0x%lx\n", m_Capabilities.HCSParamsLong);
+                DPRINT1("Controller EHCI Caps HCCParamsLong 0x%lx\n", m_Capabilities.HCCParamsLong);
+                DPRINT1("Controller has %lu Ports\n", m_Capabilities.HCSParams.PortCount);
+
+                //
+                // print capabilities
+                //
+                PrintCapabilities();
+
+                if (m_Capabilities.HCSParams.PortRouteRules)
+                {
+                    Count = 0;
+                    PortCount = max(m_Capabilities.HCSParams.PortCount/2, (m_Capabilities.HCSParams.PortCount+1)/2);
+                    do
+                    {
+                        //
+                        // each entry is a 4 bit field EHCI 2.2.5
+                        //
+                        Value = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + EHCI_HCSP_PORTROUTE + Count);
+                        m_Capabilities.PortRoute[Count*2] = (Value & 0xF0);
+
+                        if ((Count*2) + 1 < m_Capabilities.HCSParams.PortCount)
+                            m_Capabilities.PortRoute[(Count*2)+1] = (Value & 0x0F);
+
+                        Count++;
+                    } while(Count < PortCount);
+                }
+
+                //
+                // Set m_Base to the address of Operational Register Space
+                //
+                m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
+                break;
+            }
+        }
+    }
+
+
+    //
+    // zero device description
+    //
+    RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
+
+    //
+    // initialize device description
+    //
+    DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
+    DeviceDescription.Master = TRUE;
+    DeviceDescription.ScatterGather = TRUE;
+    DeviceDescription.Dma32BitAddresses = TRUE;
+    DeviceDescription.DmaWidth = Width32Bits;
+    DeviceDescription.InterfaceType = PCIBus;
+    DeviceDescription.MaximumLength = MAXULONG;
+
+    //
+    // get dma adapter
+    //
+    m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
+    if (!m_Adapter)
+    {
+        //
+        // failed to get dma adapter
+        //
+        DPRINT1("Failed to acquire dma adapter\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // Create Common Buffer
+    //
+    VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
+                                                                 PAGE_SIZE * 4,
+                                                                 &PhysicalAddress,
+                                                                 FALSE);
+    if (!VirtualBase)
+    {
+        DPRINT1("Failed to allocate a common buffer\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // Initialize the DMAMemoryManager
+    //
+    Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to initialize the DMAMemoryManager\n");
+        return Status;
+    }
+
+    //
+    // Create a queuehead for the Async Register
+    //
+    m_MemoryManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&AsyncQueueHead, &AsyncPhysicalAddress);
+
+    AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
+    AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
+    AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
+    AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
+    AsyncQueueHead->Token.Bits.Halted = TRUE;
+
+    AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01;
+    AsyncQueueHead->NextPointer = TERMINATE_POINTER;
+    AsyncQueueHead->CurrentLinkPointer = TERMINATE_POINTER;
+
+    InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
+
+    //
+    // Initialize the UsbQueue now that we have an AdapterObject.
+    //
+    Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to Initialize the UsbQueue\n");
+        return Status;
+    }
+
+    //
+    // Start the controller
+    //
+    DPRINT1("Starting Controller\n");
+    Status = StartController();
+
+    //
+    // done
+    //
+    return Status;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::PnpStop(void)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetDeviceDetails(
+    OUT OPTIONAL PUSHORT VendorId,
+    OUT OPTIONAL PUSHORT DeviceId,
+    OUT OPTIONAL PULONG NumberOfPorts,
+    OUT OPTIONAL PULONG Speed)
+{
+    if (VendorId)
+        *VendorId = m_VendorID;
+    if (DeviceId)
+        *DeviceId = m_DeviceID;
+    if (NumberOfPorts)
+        *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
+    //FIXME: What to returned here?
+    if (Speed)
+        *Speed = 0x200;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetDMA(
+    OUT struct IDMAMemoryManager **OutDMAMemoryManager)
+{
+    if (!m_MemoryManager)
+        return STATUS_UNSUCCESSFUL;
+    *OutDMAMemoryManager = m_MemoryManager;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetUSBQueue(
+    OUT struct IUSBQueue **OutUsbQueue)
+{
+    if (!m_UsbQueue)
+        return STATUS_UNSUCCESSFUL;
+    *OutUsbQueue = m_UsbQueue;
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CUSBHardwareDevice::StartController(void)
+{
+    EHCI_USBCMD_CONTENT UsbCmd;
+    ULONG UsbSts, FailSafe, ExtendedCapsSupport, Caps, Index;
+    UCHAR Value;
+    LARGE_INTEGER Timeout;
+
+    //
+    // are extended caps supported
+    //
+    ExtendedCapsSupport = (m_Capabilities.HCCParamsLong >> EHCI_ECP_SHIFT) & EHCI_ECP_MASK;
+    if (ExtendedCapsSupport)
+    {
+        DPRINT1("[EHCI] Extended Caps Support detected!\n");
+
+        //
+        // sanity check
+        //
+        ASSERT(ExtendedCapsSupport >= PCI_COMMON_HDR_LENGTH);
+        m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
+
+        //
+        // OS Handoff Synchronization support capability. EHCI 5.1
+        //
+        if ((Caps & EHCI_LEGSUP_CAPID_MASK) == EHCI_LEGSUP_CAPID)
+        {
+            //
+            // is it bios owned
+            //
+            if ((Caps & EHCI_LEGSUP_BIOSOWNED))
+            {
+                DPRINT1("[EHCI] Controller is BIOS owned, acquiring control\n");
+
+                //
+                // acquire ownership
+                //
+                Value = 1;
+                m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+3, sizeof(UCHAR));
+
+                for(Index = 0; Index < 20; Index++)
+                {
+                    //
+                    // get status
+                    //
+                    m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
+                    if ((Caps & EHCI_LEGSUP_BIOSOWNED))
+                    {
+                        //
+                        // lets wait a bit
+                        //
+                        Timeout.QuadPart = 50;
+                        DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout.LowPart);
+
+                        //
+                        // convert to 100 ns units (absolute)
+                        //
+                        Timeout.QuadPart *= -10000;
+
+                        //
+                        // perform the wait
+                        //
+                        KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+                    }
+                }
+                if ((Caps & EHCI_LEGSUP_BIOSOWNED))
+                {
+                    //
+                    // failed to acquire ownership
+                    //
+                    DPRINT1("[EHCI] failed to acquire ownership\n");
+                }
+                else if ((Caps & EHCI_LEGSUP_OSOWNED))
+                {
+                    //
+                    // HC OS Owned Semaphore EHCI 2.1.7
+                    //
+                    DPRINT1("[EHCI] acquired ownership\n");
+                }
+#if 0
+                //
+                // explicitly clear the bios owned flag 2.1.7
+                //
+                Value = 0;
+                m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+2, sizeof(UCHAR));
+
+                //
+                // clear SMI interrupt EHCI 2.1.8
+                //
+                Caps = 4;
+                m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport+4, sizeof(ULONG));
+#endif
+            }
+        }
+    }
+
+    //
+    // get command register
+    //
+    GetCommandRegister(&UsbCmd);
+
+    //
+    // disable running schedules
+    //
+    UsbCmd.PeriodicEnable = FALSE;
+    UsbCmd.AsyncEnable = FALSE;
+    SetCommandRegister(&UsbCmd);
+
+    //
+    // Wait for execution to start
+    //
+    for (FailSafe = 100; FailSafe > 1; FailSafe--)
+    {
+        KeStallExecutionProcessor(100);
+        UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+
+        if (!(UsbSts & EHCI_STS_PSS) && (UsbSts & EHCI_STS_ASS))
+        {
+            break;
+        }
+    }
+
+    if ((UsbSts & (EHCI_STS_PSS | EHCI_STS_ASS)))
+    {
+        DPRINT1("Failed to stop running schedules %x\n", UsbSts);
+        //ASSERT(FALSE);
+    }
+
+
+    //
+    // Stop the controller if its running
+    //
+    UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+    if (!(UsbSts & EHCI_STS_HALT))
+    {
+        DPRINT1("Stopping Controller %x\n", UsbSts);
+        StopController();
+    }
+
+    //
+    // Reset the controller
+    //
+    ResetController();
+
+    //
+    // check caps
+    //
+    if (m_Capabilities.HCCParams.CurAddrBits)
+    {
+        //
+        // disable 64-bit addressing
+        //
+        EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
+    }
+
+    //
+    // Enable Interrupts and start execution
+    //
+    ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC;
+    EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask);
+
+    KeStallExecutionProcessor(10);
+
+    ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR);
+
+    DPRINT1("Interrupt Mask %x\n", Status);
+    ASSERT((Status & Mask) == Mask);
+
+    //
+    // Assign the SyncList Register
+    //
+    EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
+
+    //
+    // Set Schedules to Enable and Interrupt Threshold to 1ms.
+    //
+    RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT));
+
+    UsbCmd.PeriodicEnable = TRUE;
+    UsbCmd.IntThreshold = 0x8; //1ms
+    UsbCmd.Run = TRUE;
+    UsbCmd.FrameListSize = 0x0; //1024
+
+    if (m_Capabilities.HCCParams.ParkMode)
+    {
+        //
+        // enable async park mode
+        //
+        UsbCmd.AsyncParkEnable = TRUE;
+        UsbCmd.AsyncParkCount = 3;
+    }
+
+    SetCommandRegister(&UsbCmd);
+
+
+    //
+    // Wait for execution to start
+    //
+    for (FailSafe = 100; FailSafe > 1; FailSafe--)
+    {
+        KeStallExecutionProcessor(100);
+        UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+
+        if (!(UsbSts & EHCI_STS_HALT) && (UsbSts & EHCI_STS_PSS))
+        {
+            break;
+        }
+    }
+
+    if (UsbSts & EHCI_STS_HALT)
+    {
+        DPRINT1("Could not start execution on the controller\n");
+        //ASSERT(FALSE);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    if (!(UsbSts & EHCI_STS_PSS))
+    {
+        DPRINT1("Could not enable periodic scheduling\n");
+        //ASSERT(FALSE);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    //
+    // Assign the AsyncList Register
+    //
+    EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
+
+    //
+    // get command register
+    //
+    GetCommandRegister(&UsbCmd);
+
+    //
+    // preserve bits
+    //
+    UsbCmd.AsyncEnable = TRUE;
+
+    //
+    // enable async
+    //
+    SetCommandRegister(&UsbCmd);
+
+    //
+    // Wait for execution to start
+    //
+    for (FailSafe = 100; FailSafe > 1; FailSafe--)
+    {
+        KeStallExecutionProcessor(100);
+        UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+
+        if ((UsbSts & EHCI_STS_ASS))
+        {
+            break;
+        }
+    }
+
+    if (!(UsbSts & EHCI_STS_ASS))
+    {
+        DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts);
+        //ASSERT(FALSE);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    DPRINT1("UsbSts %x\n", UsbSts);
+    GetCommandRegister(&UsbCmd);
+
+    DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable);
+    DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable);
+    DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold);
+    DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run);
+    DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize);
+
+    //
+    // Set port routing to EHCI controller
+    //
+    EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
+
+    DPRINT1("EHCI Started!\n");
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CUSBHardwareDevice::StopController(void)
+{
+    EHCI_USBCMD_CONTENT UsbCmd;
+    ULONG UsbSts, FailSafe;
+
+    //
+    // Disable Interrupts and stop execution
+    //
+    EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
+
+    GetCommandRegister(&UsbCmd);
+    UsbCmd.Run = FALSE;
+    SetCommandRegister(&UsbCmd);
+
+    for (FailSafe = 100; FailSafe > 1; FailSafe--)
+    {
+        KeStallExecutionProcessor(10);
+        UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+        if (UsbSts & EHCI_STS_HALT)
+        {
+            break;
+        }
+    }
+
+    if (!(UsbSts & EHCI_STS_HALT))
+    {
+        DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CUSBHardwareDevice::ResetController(void)
+{
+    EHCI_USBCMD_CONTENT UsbCmd;
+    ULONG FailSafe;
+
+    GetCommandRegister(&UsbCmd);
+    UsbCmd.HCReset = TRUE;
+    SetCommandRegister(&UsbCmd);
+
+    for (FailSafe = 100; FailSafe > 1; FailSafe--)
+    {
+        KeStallExecutionProcessor(100);
+        GetCommandRegister(&UsbCmd);
+        if (!UsbCmd.HCReset)
+            break;
+    }
+
+    if (UsbCmd.HCReset)
+    {
+        DPRINT1("EHCI ERROR: Controller is not responding to reset request!\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::ResetPort(
+    IN ULONG PortIndex)
+{
+    ULONG PortStatus;
+    LARGE_INTEGER Timeout;
+
+    if (PortIndex > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
+
+    ASSERT(!EHCI_IS_LOW_SPEED(PortStatus));
+    ASSERT(PortStatus & EHCI_PRT_CONNECTED);
+
+    //
+    // Reset and clean enable
+    //
+    PortStatus |= EHCI_PRT_RESET;
+    PortStatus &= EHCI_PORTSC_DATAMASK;
+    EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
+
+    //
+    // delay is 50 ms for port reset as per USB 2.0 spec
+    //
+    Timeout.QuadPart = 50;
+    DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout.LowPart);
+
+    //
+    // convert to 100 ns units (absolute)
+    //
+    Timeout.QuadPart *= -10000;
+
+    //
+    // perform the wait
+    //
+    KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetPortStatus(
+    ULONG PortId,
+    OUT USHORT *PortStatus,
+    OUT USHORT *PortChange)
+{
+    ULONG Value;
+    USHORT Status = 0, Change = 0;
+
+    if (PortId > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    //
+    // Get the value of the Port Status and Control Register
+    //
+    Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+
+    //
+    // If the PowerPortControl is 0 then host controller does not have power control switches
+    if (!m_Capabilities.HCSParams.PortPowerControl)
+    {
+        Status |= USB_PORT_STATUS_POWER;
+    }
+    else
+    {
+        // Check the value of PortPower
+        if (Value & EHCI_PRT_POWER)
+        {
+            Status |= USB_PORT_STATUS_POWER;
+        }
+    }
+
+    // Get Connected Status
+    if (Value & EHCI_PRT_CONNECTED)
+    {
+        Status |= USB_PORT_STATUS_CONNECT;
+
+        // EHCI only supports high speed
+        Status |= USB_PORT_STATUS_HIGH_SPEED;
+    }
+
+    // Get Enabled Status
+    if (Value & EHCI_PRT_ENABLED)
+        Status |= USB_PORT_STATUS_ENABLE;
+
+    // Is it suspended?
+    if (Value & EHCI_PRT_SUSPEND)
+        Status |= USB_PORT_STATUS_SUSPEND;
+
+    // a overcurrent is active?
+    if (Value & EHCI_PRT_OVERCURRENTACTIVE)
+        Status |= USB_PORT_STATUS_OVER_CURRENT;
+
+    // In a reset state?
+    if ((Value & EHCI_PRT_RESET) || m_PortResetInProgress[PortId])
+    {
+        Status |= USB_PORT_STATUS_RESET;
+        Change |= USB_PORT_STATUS_RESET;
+    }
+
+    // This indicates a connect or disconnect
+    if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
+        Change |= USB_PORT_STATUS_CONNECT;
+
+    // This is set to indicate a critical port error
+    if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
+        Change |= USB_PORT_STATUS_ENABLE;
+
+    *PortStatus = Status;
+    *PortChange = Change;
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::ClearPortStatus(
+    ULONG PortId,
+    ULONG Status)
+{
+    ULONG Value;
+    LARGE_INTEGER Timeout;
+
+    DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
+
+    if (PortId > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    if (Status == C_PORT_RESET)
+    {
+        // reset done
+        m_PortResetInProgress[PortId] = FALSE;
+
+        // Clear reset
+        Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+        Value &= (EHCI_PORTSC_DATAMASK | EHCI_PRT_ENABLED);
+        Value &= ~EHCI_PRT_RESET;
+        EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
+
+        //
+        // wait for reset bit to clear
+        //
+        do
+        {
+            Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+
+            if (!(Value & EHCI_PRT_RESET))
+                break;
+
+            KeStallExecutionProcessor(20);
+        } while (TRUE);
+
+        //
+        // delay is 50 ms
+        //
+        Timeout.QuadPart = 50;
+        DPRINT1("Waiting %lu milliseconds for port to recover after reset\n", Timeout.LowPart);
+
+        //
+        // convert to 100 ns units (absolute)
+        //
+        Timeout.QuadPart *= -10000;
+
+        //
+        // perform the wait
+        //
+        KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+
+        //
+        // check the port status after reset
+        //
+        Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+        if (!(Value & EHCI_PRT_CONNECTED))
+        {
+            DPRINT1("No device is here after reset. Bad controller/device?\n");
+            return STATUS_UNSUCCESSFUL;
+        }
+        else if (EHCI_IS_LOW_SPEED(Value))
+        {
+            DPRINT1("Low speed device connected. Releasing ownership\n");
+            EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value | EHCI_PRT_RELEASEOWNERSHIP);
+            return STATUS_DEVICE_NOT_CONNECTED;
+        }
+        else if (!(Value & EHCI_PRT_ENABLED))
+        {
+            DPRINT1("Full speed device connected. Releasing ownership\n");
+            EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value | EHCI_PRT_RELEASEOWNERSHIP);
+            return STATUS_DEVICE_NOT_CONNECTED;
+        }
+        else
+        {
+            DPRINT1("High speed device connected\n");
+            return STATUS_SUCCESS;
+        }
+    }
+    else if (Status == C_PORT_CONNECTION)
+    {
+        //
+        // reset status change bits
+        //
+        Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+        EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
+
+        if (Value & EHCI_PRT_CONNECTED)
+        {
+            //
+            // delay is 100 ms
+            //
+            Timeout.QuadPart = 100;
+            DPRINT1("Waiting %lu milliseconds for port to stabilize after connection\n", Timeout.LowPart);
+
+            //
+            // convert to 100 ns units (absolute)
+            //
+            Timeout.QuadPart *= -10000;
+
+            //
+            // perform the wait
+            //
+            KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+STDMETHODCALLTYPE
+CUSBHardwareDevice::SetPortFeature(
+    ULONG PortId,
+    ULONG Feature)
+{
+    DPRINT("CUSBHardwareDevice::SetPortFeature\n");
+
+    if (PortId > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    if (Feature == PORT_ENABLE)
+    {
+        //
+        // FIXME: EHCI Ports can only be disabled via reset
+        //
+        DPRINT1("PORT_ENABLE not supported for EHCI\n");
+    }
+
+    if (Feature == PORT_RESET)
+    {
+        //
+        // call the helper
+        //
+        ResetPort(PortId);
+
+        // reset in progress
+        m_PortResetInProgress[PortId] = TRUE;
+
+        //
+        // is there a status change callback
+        //
+        if (m_SCECallBack != NULL)
+        {
+            //
+            // issue callback
+            //
+            m_SCECallBack(m_SCEContext);
+        }
+    }
+
+    if (Feature == PORT_POWER)
+    {
+        if (m_Capabilities.HCSParams.PortPowerControl)
+        {
+            ULONG Value;
+            LARGE_INTEGER Timeout;
+
+            //
+            // enable port power
+            //
+            Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
+            EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
+
+            //
+            // delay is 20 ms
+            //
+            Timeout.QuadPart = 20;
+            DPRINT1("Waiting %lu milliseconds for port power up\n", Timeout.LowPart);
+
+            //
+            // convert to 100 ns units (absolute)
+            //
+            Timeout.QuadPart *= -10000;
+
+            //
+            // perform the wait
+            //
+            KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+        }
+    }
+    return STATUS_SUCCESS;
+}
+
+VOID
+STDMETHODCALLTYPE
+CUSBHardwareDevice::SetAsyncListRegister(
+    ULONG PhysicalAddress)
+{
+    EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
+}
+
+VOID
+STDMETHODCALLTYPE
+CUSBHardwareDevice::SetPeriodicListRegister(
+    ULONG PhysicalAddress)
+{
+    //
+    // store physical address
+    //
+    m_SyncFramePhysAddr = PhysicalAddress;
+}
+
+struct _QUEUE_HEAD *
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetAsyncListQueueHead()
+{
+    return AsyncQueueHead;
+}
+
+ULONG
+STDMETHODCALLTYPE
+CUSBHardwareDevice::GetPeriodicListRegister()
+{
+    UNIMPLEMENTED;
+    return NULL;
+}
+
+VOID
+STDMETHODCALLTYPE
+CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
+    PVOID CallBack,
+    PVOID Context)
+{
+    m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
+    m_SCEContext = Context;
+}
+
+BOOLEAN
+NTAPI
+InterruptServiceRoutine(
+    IN PKINTERRUPT  Interrupt,
+    IN PVOID  ServiceContext)
+{
+    CUSBHardwareDevice *This;
+    ULONG CStatus;
+
+    This = (CUSBHardwareDevice*) ServiceContext;
+    CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+
+    CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
+    DPRINT("InterruptServiceRoutine CStatus %lx\n", CStatus);
+
+    //
+    // Check that it belongs to EHCI
+    //
+    if (!CStatus)
+        return FALSE;
+
+    //
+    // Clear the Status
+    //
+    This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
+
+    if (CStatus & EHCI_STS_FATAL)
+    {
+        This->StopController();
+        DPRINT1("EHCI: Host System Error!\n");
+        return TRUE;
+    }
+
+    if (CStatus & EHCI_ERROR_INT)
+    {
+        DPRINT1("EHCI Status = 0x%x\n", CStatus);
+    }
+
+    if (CStatus & EHCI_STS_HALT)
+    {
+        DPRINT1("Host Error Unexpected Halt\n");
+        // FIXME: Reset controller\n");
+        return TRUE;
+    }
+
+    KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
+    return TRUE;
+}
+
+VOID NTAPI
+EhciDeferredRoutine(
+    IN PKDPC Dpc,
+    IN PVOID DeferredContext,
+    IN PVOID SystemArgument1,
+    IN PVOID SystemArgument2)
+{
+    CUSBHardwareDevice *This;
+    ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell, QueueSCEWorkItem;
+    NTSTATUS Status = STATUS_SUCCESS;
+    EHCI_USBCMD_CONTENT UsbCmd;
+
+    This = (CUSBHardwareDevice*) SystemArgument1;
+    CStatus = (ULONG) SystemArgument2;
+
+    DPRINT("EhciDeferredRoutine CStatus %lx\n", CStatus);
+
+    //
+    // check for completion of async schedule
+    //
+    if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
+    {
+        //
+        // check if there is a door bell ring in progress
+        //
+        if (This->m_DoorBellRingInProgress == FALSE)
+        {
+            if (CStatus & EHCI_ERROR_INT)
+            {
+                //
+                // controller reported error
+                //
+                DPRINT1("CStatus %lx\n", CStatus);
+                //ASSERT(FALSE);
+            }
+
+            //
+            // inform IUSBQueue of a completed queue head
+            //
+            This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
+
+            //
+            // was a queue head completed?
+            //
+            if (ShouldRingDoorBell)
+            {
+                //
+                // set door ring bell in progress status flag
+                //
+                This->m_DoorBellRingInProgress = TRUE;
+
+                //
+                // get command register
+                //
+                This->GetCommandRegister(&UsbCmd);
+
+                //
+                // set door rang bell bit
+                //
+                UsbCmd.DoorBell = TRUE;
+
+                //
+                // update command status
+                //
+                This->SetCommandRegister(&UsbCmd);
+            }
+        }
+    }
+
+    //
+    // check if the controller has acknowledged the door bell
+    //
+    if (CStatus & EHCI_STS_IAA)
+    {
+        //
+        // controller has acknowledged, assert we rang the bell
+        //
+        PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
+
+        //
+        // now notify IUSBQueue that it can free completed requests
+        //
+        This->m_UsbQueue->CompleteAsyncRequests();
+
+        //
+        // door ring bell completed
+        //
+        This->m_DoorBellRingInProgress = FALSE;
+    }
+
+    This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
+    if (CStatus & EHCI_STS_PCD)
+    {
+        QueueSCEWorkItem = FALSE;
+        for (i = 0; i < PortCount; i++)
+        {
+            PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
+
+            //
+            // Device connected or removed
+            //
+            if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
+            {
+                if (PortStatus & EHCI_PRT_CONNECTED)
+                {
+                    DPRINT1("Device connected on port %lu\n", i);
+
+                    if (This->m_Capabilities.HCSParams.CHCCount)
+                    {
+                        if (PortStatus & EHCI_PRT_ENABLED)
+                        {
+                            DPRINT1("Misbehaving controller. Port should be disabled at this point\n");
+                        }
+
+                        if (EHCI_IS_LOW_SPEED(PortStatus))
+                        {
+                            DPRINT1("Low speed device connected. Releasing ownership\n");
+                            This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_RELEASEOWNERSHIP);
+                            continue;
+                        }
+                    }
+
+                    //
+                    // work to do
+                    //
+                    QueueSCEWorkItem = TRUE;
+                }
+                else
+                {
+                    DPRINT1("Device disconnected on port %lu\n", i);
+
+                    //
+                    // work to do
+                    //
+                    QueueSCEWorkItem = TRUE;
+                }
+            }
+        }
+
+        //
+        // is there a status change callback and a high speed device connected / disconnected
+        //
+        if (QueueSCEWorkItem && This->m_SCECallBack != NULL)
+        {
+            if (InterlockedCompareExchange(&This->m_StatusChangeWorkItemStatus, 1, 0) == 0)
+            {
+                //
+                // queue work item for processing
+                //
+                ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
+            }
+        }
+    }
+    return;
+}
+
+VOID
+NTAPI
+StatusChangeWorkItemRoutine(
+    PVOID Context)
+{
+    //
+    // cast to hardware object
+    //
+    CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
+
+    //
+    // is there a callback
+    //
+    if (This->m_SCECallBack)
+    {
+        //
+        // issue callback
+        //
+        This->m_SCECallBack(This->m_SCEContext);
+    }
+
+    //
+    // reset active status
+    //
+    InterlockedDecrement(&This->m_StatusChangeWorkItemStatus);
+}
+
+NTSTATUS
+NTAPI
+CreateUSBHardware(
+    PUSBHARDWAREDEVICE *OutHardware)
+{
+    PUSBHARDWAREDEVICE This;
+
+    This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
+
+    if (!This)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    This->AddRef();
+
+    // return result
+    *OutHardware = (PUSBHARDWAREDEVICE)This;
+
+    return STATUS_SUCCESS;
+}