[EHCI]
[reactos.git] / drivers / usb / usbehci_new / hardware.cpp
index 3dfece4..9830762 100644 (file)
 #include "usbehci.h"
 #include "hardware.h"
 
+typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
+
 BOOLEAN
 NTAPI
 InterruptServiceRoutine(
     IN PKINTERRUPT  Interrupt,
     IN PVOID  ServiceContext);
 
-VOID NTAPI
+VOID
+NTAPI
 EhciDefferedRoutine(
     IN PKDPC Dpc,
     IN PVOID DeferredContext,
     IN PVOID SystemArgument1,
     IN PVOID SystemArgument2);
 
+VOID
+NTAPI
+StatusChangeWorkItemRoutine(PVOID Context);
+
 class CUSBHardwareDevice : public IUSBHardwareDevice
 {
 public:
@@ -66,44 +73,63 @@ public:
 
     VOID SetAsyncListRegister(ULONG PhysicalAddress);
     VOID SetPeriodicListRegister(ULONG PhysicalAddress);
+    struct _QUEUE_HEAD * GetAsyncListQueueHead();
+    ULONG GetPeriodicListRegister();
+
+    VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context);
 
     KIRQL AcquireDeviceLock(void);
     VOID ReleaseDeviceLock(KIRQL OldLevel);
+    // set command
+    VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
+
+    // get command
+    VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
+
+
     // local
     BOOLEAN InterruptService();
 
     // friend function
     friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT  Interrupt, IN PVOID  ServiceContext);
     friend VOID NTAPI EhciDefferedRoutine(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;
-    PDRIVER_OBJECT m_DriverObject;
-    PDEVICE_OBJECT m_PhysicalDeviceObject;
-    PDEVICE_OBJECT m_FunctionalDeviceObject;
-    PDEVICE_OBJECT m_NextDeviceObject;
-    KSPIN_LOCK m_Lock;
-    PKINTERRUPT m_Interrupt;
-    KDPC m_IntDpcObject;
-    PVOID VirtualBase;
-    PHYSICAL_ADDRESS PhysicalAddress;
-    PULONG m_Base;
-    PDMA_ADAPTER m_Adapter;
-    ULONG m_MapRegisters;
-    EHCI_CAPS m_Capabilities;
-    USHORT m_VendorID;
-    USHORT m_DeviceID;
-    PQUEUE_HEAD AsyncQueueHead;
-    PUSBQUEUE m_UsbQueue;
-    PDMAMEMORYMANAGER m_MemoryManager;
-
-    VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
-    VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
+    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
+    PUSBQUEUE 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
+    ULONG m_SyncFramePhysAddr;                                                         // periodic frame list physical address
+    BOOLEAN m_ResetInProgress[16];                                                     // set when a reset is in progress
+    BUS_INTERFACE_STANDARD m_BusInterface;                                             // pci bus interface
+
+    // read register
     ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
+
+    // write register
     VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
 };
 
@@ -133,7 +159,6 @@ CUSBHardwareDevice::Initialize(
     PDEVICE_OBJECT PhysicalDeviceObject,
     PDEVICE_OBJECT LowerDeviceObject)
 {
-    BUS_INTERFACE_STANDARD BusInterface;
     PCI_COMMON_CONFIG PciConfig;
     NTSTATUS Status;
     ULONG BytesRead;
@@ -173,17 +198,22 @@ CUSBHardwareDevice::Initialize(
     //
     KeInitializeSpinLock(&m_Lock);
 
+    //
+    // intialize status change work item
+    //
+    ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
+
     m_VendorID = 0;
     m_DeviceID = 0;
 
-    Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
+    Status = GetBusInterface(PhysicalDeviceObject, &m_BusInterface);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("Failed to get BusInteface!\n");
         return Status;
     }
 
-    BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
+    BytesRead = (*m_BusInterface.GetBusData)(m_BusInterface.Context,
                                            PCI_WHICHSPACE_CONFIG,
                                            &PciConfig,
                                            0,
@@ -195,14 +225,42 @@ CUSBHardwareDevice::Initialize(
         return STATUS_SUCCESS;
     }
 
-    if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER))
+    m_VendorID = PciConfig.VendorID;
+    m_DeviceID = PciConfig.DeviceID;
+
+
+    if (PciConfig.Command & PCI_ENABLE_BUS_MASTER)
     {
-        DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
+        //
+        // master is enabled
+        //
+        return STATUS_SUCCESS;
     }
 
-    m_VendorID = PciConfig.VendorID;
-    m_DeviceID = PciConfig.DeviceID;
+     DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n");
+
+     PciConfig.Command |= PCI_ENABLE_BUS_MASTER;
+     m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &PciConfig, 0, PCI_COMMON_HDR_LENGTH);
+
+     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");
+        ASSERT(FALSE);
+        return STATUS_SUCCESS;
+    }
+
+    if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER))
+    {
+        PciConfig.Command |= PCI_ENABLE_BUS_MASTER;
+        DPRINT1("Failed to enable master\n");
+        return STATUS_UNSUCCESSFUL;
+    }
     return STATUS_SUCCESS;
 }
 
@@ -245,6 +303,8 @@ CUSBHardwareDevice::PnpStart(
     PHYSICAL_ADDRESS AsyncPhysicalAddress;
     PVOID ResourceBase;
     NTSTATUS Status;
+    UCHAR Value;
+    UCHAR PortCount;
 
     DPRINT1("CUSBHardwareDevice::PnpStart\n");
     for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
@@ -302,18 +362,35 @@ CUSBHardwareDevice::PnpStart(
                 //
                 // Get controllers capabilities 
                 //
-                m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase);
-                m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + 2));
-                m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 4));
-                m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 8));
+                m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase + EHCI_CAPLENGTH);
+                m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + EHCI_HCIVERSION));
+                m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCSPARAMS));
+                m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCCPARAMS));
 
+                DPRINT1("Controller has %d Length\n", m_Capabilities.Length);
                 DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount);
+                DPRINT1("Controller EHCI Version %x\n", m_Capabilities.HCIVersion);
+                DPRINT1("Controler EHCI Caps HCSParamsLong %x\n", m_Capabilities.HCSParamsLong);
+                DPRINT1("Controler EHCI Caps HCCParamsLong %x\n", m_Capabilities.HCCParamsLong);
+                DPRINT1("Controler EHCI Caps PowerControl %x\n", m_Capabilities.HCSParams.PortPowerControl);
+
                 if (m_Capabilities.HCSParams.PortRouteRules)
                 {
-                    for (Count = 0; Count < m_Capabilities.HCSParams.PortCount; Count++)
+                    Count = 0;
+                    PortCount = max(m_Capabilities.HCSParams.PortCount/2, (m_Capabilities.HCSParams.PortCount+1)/2);
+                    do
                     {
-                        m_Capabilities.PortRoute[Count] = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + 12 + Count);
-                    }
+                        //
+                        // 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);
                 }
 
                 //
@@ -385,16 +462,6 @@ CUSBHardwareDevice::PnpStart(
         return Status;
     }
 
-    //
-    // Initialize the UsbQueue now that we have an AdapterObject.
-    //
-    Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("Failed to Initialize the UsbQueue\n");
-        return Status;
-    }
-
     // 
     // Create a queuehead for the Async Register
     //
@@ -409,17 +476,32 @@ CUSBHardwareDevice::PnpStart(
     AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
     AsyncQueueHead->Token.Bits.Halted = TRUE;
     AsyncQueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
-    AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0xF;
+    AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0;
     AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
     AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
 
-    SetAsyncListRegister(AsyncQueueHead->PhysicalAddr);
+    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");
-    return StartController();
+    Status = StartController();
+
+    //
+    // done
+    //
+    return Status;
 }
 
 NTSTATUS
@@ -480,72 +562,193 @@ NTSTATUS
 CUSBHardwareDevice::StartController(void)
 {
     EHCI_USBCMD_CONTENT UsbCmd;
-    ULONG UsbSts, FailSafe;
+    ULONG UsbSts, FailSafe, ExtendedCapsSupport, Caps, Index;
+    UCHAR Value;
+    LARGE_INTEGER Timeout;
 
+    //
+    // check caps
+    //
+    if (m_Capabilities.HCCParams.CurAddrBits)
+    {
+        //
+        // disable 64-bit addressing
+        //
+        EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
+    }
+
+    //
+    // 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, acquring 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 %d 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 aquire 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");
+                }
+
+                //
+                // explictly 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));
+
+
+            }
+        }
+    }
+
+
+
+#if 1
     //
     // 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();
+    }
+#endif
 
     //
-    // Reset the device. Bit is set to 0 on completion.
+    // Enable Interrupts and start execution
     //
-    GetCommandRegister(&UsbCmd);
-    UsbCmd.HCReset = TRUE;
+    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
     SetCommandRegister(&UsbCmd);
 
     //
-    // Check that the controller reset
+    // Wait for execution to start
     //
     for (FailSafe = 100; FailSafe > 1; FailSafe--)
     {
         KeStallExecutionProcessor(10);
-        GetCommandRegister(&UsbCmd);
-        if (!UsbCmd.HCReset)
+        UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
+
+        if (!(UsbSts & EHCI_STS_HALT))
         {
             break;
         }
     }
 
-    //
-    // If the controller did not reset then fail
-    //
-    if (UsbCmd.HCReset)
+
+    if (UsbSts & EHCI_STS_HALT)
     {
-        DPRINT1("EHCI ERROR: Controller failed to reset. Hardware problem!\n");
+        DPRINT1("Could not start execution on the controller\n");
         return STATUS_UNSUCCESSFUL;
     }
 
     //
-    // Disable Interrupts and clear status
+    // Assign the AsyncList Register
     //
-    EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, 0);
-    EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, 0x0000001f);
+    EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
 
     //
-    // FIXME: Assign the AsyncList Register
+    // get command register
     //
+    GetCommandRegister(&UsbCmd);
 
     //
-    // Set Schedules to Enable and Interrupt Threshold to 1ms.
+    // preserve bits
     //
-    GetCommandRegister(&UsbCmd);
-    UsbCmd.PeriodicEnable = FALSE;
-    UsbCmd.AsyncEnable = FALSE;  //FIXME: Need USB Memory Manager
-
-    UsbCmd.IntThreshold = 1;
-    // FIXME: Set framelistsize when periodic is implemented.
-    SetCommandRegister(&UsbCmd);
+    UsbCmd.AsyncEnable = TRUE;
 
     //
-    // Enable Interrupts and start execution
+    // enable async
     //
-    EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR
-        /*| EHCI_USBINTR_FLROVR*/  | EHCI_USBINTR_PC);
-
-    UsbCmd.Run = TRUE;
     SetCommandRegister(&UsbCmd);
 
     //
@@ -556,18 +759,28 @@ CUSBHardwareDevice::StartController(void)
         KeStallExecutionProcessor(10);
         UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
 
-        if (!(UsbSts & EHCI_STS_HALT))
+        if ((UsbSts & EHCI_STS_ASS))
         {
             break;
         }
     }
 
-    if (UsbSts & EHCI_STS_HALT)
+    if (!(UsbSts & EHCI_STS_ASS))
     {
-        DPRINT1("Could not start execution on the controller\n");
+        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
     //
@@ -623,11 +836,15 @@ 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));
+    //
+    // check slow speed line before reset
+    //
     if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
     {
         DPRINT1("Non HighSpeed device. Releasing Ownership\n");
@@ -635,6 +852,8 @@ CUSBHardwareDevice::ResetPort(
         return STATUS_DEVICE_NOT_CONNECTED;
     }
 
+    ASSERT(PortStatus & EHCI_PRT_CONNECTED);
+
     //
     // Reset and clean enable
     //
@@ -642,7 +861,21 @@ CUSBHardwareDevice::ResetPort(
     PortStatus &= ~EHCI_PRT_ENABLED;
     EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
 
-    KeStallExecutionProcessor(100);
+    //
+    // delay is 20 ms for port reset as per USB 2.0 spec
+    //
+    Timeout.QuadPart = 20;
+    DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
+
+    //
+    // convert to 100 ns units (absolute)
+    //
+    Timeout.QuadPart *= -10000;
+
+    //
+    // perform the wait
+    //
+    KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
 
     //
     // Clear reset
@@ -651,17 +884,53 @@ CUSBHardwareDevice::ResetPort(
     PortStatus &= ~EHCI_PRT_RESET;
     EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
 
-    KeStallExecutionProcessor(100);
+    do
+    {
+        //
+        // wait
+        //
+        KeStallExecutionProcessor(100);
+
+        //
+        // Check that the port reset
+        //
+        PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
+        if (!(PortStatus & EHCI_PRT_RESET))
+            break;
+    } while (TRUE);
 
     //
-    // Check that the port reset
+    // delay is 10 ms
+    //
+    Timeout.QuadPart = 10;
+    DPRINT1("Waiting %d 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 slow speed line after reset
     //
     PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
-    if (PortStatus & EHCI_PRT_RESET)
+    if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
     {
-        DPRINT1("Port did not reset\n");
-        return STATUS_RETRY;
+        DPRINT1("Non HighSpeed device. Releasing Ownership\n");
+        EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
+        return STATUS_DEVICE_NOT_CONNECTED;
     }
+
+    //
+    // this must be enabled now
+    //
+    ASSERT(PortStatus & EHCI_PRT_ENABLED);
+
     return STATUS_SUCCESS;
 }
 
@@ -674,11 +943,9 @@ CUSBHardwareDevice::GetPortStatus(
     ULONG Value;
     USHORT Status = 0, Change = 0;
 
-    DPRINT1("CUSBHardwareDevice::GetPortStatus\n");
-
     if (PortId > m_Capabilities.HCSParams.PortCount)
         return STATUS_UNSUCCESSFUL;
-    
+
     //
     // Get the value of the Port Status and Control Register
     //
@@ -699,16 +966,18 @@ CUSBHardwareDevice::GetPortStatus(
         }
     }
 
-    // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
-    if (Value & EHCI_PRT_SLOWSPEEDLINE)
-        Status |= USB_PORT_STATUS_LOW_SPEED;
-    else
-        Status |= USB_PORT_STATUS_HIGH_SPEED;
-
     // Get Connected Status
     if (Value & EHCI_PRT_CONNECTED)
+    {
         Status |= USB_PORT_STATUS_CONNECT;
 
+        // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
+        if (Value & EHCI_PRT_SLOWSPEEDLINE)
+            Status |= USB_PORT_STATUS_LOW_SPEED;
+        else
+            Status |= USB_PORT_STATUS_HIGH_SPEED;
+    }
+
     // Get Enabled Status
     if (Value & EHCI_PRT_ENABLED)
         Status |= USB_PORT_STATUS_ENABLE;
@@ -722,8 +991,11 @@ CUSBHardwareDevice::GetPortStatus(
         Status |= USB_PORT_STATUS_OVER_CURRENT;
 
     // In a reset state?
-    if (Value & EHCI_PRT_RESET)
+    if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
+    {
         Status |= USB_PORT_STATUS_RESET;
+        Change |= USB_PORT_STATUS_RESET;
+    }
 
     //
     // FIXME: Is the Change here correct?
@@ -737,10 +1009,6 @@ CUSBHardwareDevice::GetPortStatus(
     *PortStatus = Status;
     *PortChange = Change;
 
-    //HACK: Maybe
-    if (Status == (USB_PORT_STATUS_HIGH_SPEED | USB_PORT_STATUS_CONNECT | USB_PORT_STATUS_POWER))
-        *PortChange = USB_PORT_STATUS_CONNECT;
-    
     return STATUS_SUCCESS;
 }
 
@@ -751,29 +1019,45 @@ CUSBHardwareDevice::ClearPortStatus(
 {
     ULONG Value;
 
-    DPRINT1("CUSBHardwareDevice::ClearPortStatus\n");
+    DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
 
     if (PortId > m_Capabilities.HCSParams.PortCount)
         return STATUS_UNSUCCESSFUL;
 
-    Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
-
     if (Status == C_PORT_RESET)
     {
-        if (Value & EHCI_PRT_RESET)
-        {
-            Value &= ~EHCI_PRT_RESET;
-            EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
-            KeStallExecutionProcessor(100);
-        }
+        //
+        // update port status
+        //
+        m_ResetInProgress[PortId] = FALSE;
     }
 
     if (Status == C_PORT_CONNECTION)
     {
-        // FIXME: Make sure its the Connection and Enable Change status.
-        Value |= EHCI_PRT_CONNECTSTATUSCHANGE;
-        Value |= EHCI_PRT_ENABLEDSTATUSCHANGE;
+        LARGE_INTEGER Timeout;
+
+        //
+        // reset status change bits
+        //
+        Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+        Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
         EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
+
+        //
+        // delay is 100 ms
+        //
+        Timeout.QuadPart = 100;
+        DPRINT1("Waiting %d 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;
@@ -787,7 +1071,7 @@ CUSBHardwareDevice::SetPortFeature(
 {
     ULONG Value;
 
-    DPRINT1("CUSBHardwareDevice::SetPortFeature\n");
+    DPRINT("CUSBHardwareDevice::SetPortFeature\n");
 
     if (PortId > m_Capabilities.HCSParams.PortCount)
         return STATUS_UNSUCCESSFUL;
@@ -801,26 +1085,58 @@ CUSBHardwareDevice::SetPortFeature(
         //
         DPRINT1("PORT_ENABLE not supported for EHCI\n");
     }
-    
+
     if (Feature == PORT_RESET)
     {
-        if (Value & EHCI_PRT_SLOWSPEEDLINE)
-        {
-            DPRINT1("Non HighSpeed device. Releasing Ownership\n");
-        }
+        ResetPort(PortId);
+
         //
-        // Reset and clean enable
+        // update cached settings
         //
-        Value |= EHCI_PRT_RESET;
-        Value &= ~EHCI_PRT_ENABLED;
-        EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
+        m_ResetInProgress[PortId] = TRUE;
 
-        KeStallExecutionProcessor(100);
+        //
+        // is there a status change callback
+        //
+        if (m_SCECallBack != NULL)
+        {
+            //
+            // issue callback
+            //
+            m_SCECallBack(m_SCEContext);
+        }
     }
-    
+
     if (Feature == PORT_POWER)
-        DPRINT1("PORT_POWER Not implemented\n");
+    {
+        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, Value);
 
+            //
+            // delay is 20 ms
+            //
+            Timeout.QuadPart = 20;
+            DPRINT1("Waiting %d 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;
 }
 
@@ -835,7 +1151,30 @@ VOID
 CUSBHardwareDevice::SetPeriodicListRegister(
     ULONG PhysicalAddress)
 {
-    EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, PhysicalAddress);
+    //
+    // store physical address
+    //
+    m_SyncFramePhysAddr = PhysicalAddress;
+}
+
+struct _QUEUE_HEAD *
+CUSBHardwareDevice::GetAsyncListQueueHead()
+{
+    return AsyncQueueHead;
+}
+
+ULONG CUSBHardwareDevice::GetPeriodicListRegister()
+{
+    UNIMPLEMENTED
+    return NULL;
+}
+
+VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
+    PVOID CallBack,
+    PVOID Context)
+{
+    m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
+    m_SCEContext = Context;
 }
 
 KIRQL
@@ -917,11 +1256,90 @@ EhciDefferedRoutine(
     IN PVOID SystemArgument2)
 {
     CUSBHardwareDevice *This;
-    ULONG CStatus, PortStatus, PortCount, i;
+    ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
+    NTSTATUS Status = STATUS_SUCCESS;
+    EHCI_USBCMD_CONTENT UsbCmd;
 
     This = (CUSBHardwareDevice*) SystemArgument1;
     CStatus = (ULONG) SystemArgument2;
 
+       DPRINT("CStatus %x\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 %x\n", CStatus);
+                Status = STATUS_UNSUCCESSFUL;
+                PC_ASSERT(FALSE);
+                return;
+            }
+
+            //
+            // 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)
     {
@@ -934,11 +1352,6 @@ EhciDefferedRoutine(
             //
             if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
             {
-                //
-                // Clear the port change status
-                //
-                This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_CONNECTSTATUSCHANGE);
-
                 if (PortStatus & EHCI_PRT_CONNECTED)
                 {
                     DPRINT1("Device connected on port %d\n", i);
@@ -955,14 +1368,10 @@ EhciDefferedRoutine(
 
                         if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
                         {
-                            DPRINT1("Non HighSeped device connected. Release ownership\n");
+                            DPRINT1("Non HighSpeed device connected. Release ownership\n");
                             This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
                             continue;
                         }
-
-                        //
-                        // FIXME: Is a port reset needed, or does hub driver request this?
-                        //
                     }
                 }
                 else
@@ -970,6 +1379,17 @@ EhciDefferedRoutine(
                     DPRINT1("Device disconnected on port %d\n", i);
                 }
 
+                //
+                // is there a status change callback
+                //
+                if (This->m_SCECallBack != NULL)
+                {
+                    //
+                    // queue work item for processing
+                    //
+                    ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
+                }
+
                 //
                 // FIXME: This needs to be saved somewhere
                 //
@@ -979,6 +1399,29 @@ EhciDefferedRoutine(
     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);
+    }
+
+}
+
 NTSTATUS
 CreateUSBHardware(
     PUSBHARDWAREDEVICE *OutHardware)