[EHCI]
[reactos.git] / drivers / usb / usbehci_new / hardware.cpp
index 5922b12..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:
@@ -52,45 +59,77 @@ public:
     NTSTATUS PnpStop(void);
     NTSTATUS HandlePower(PIRP Irp);
     NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed);
-    NTSTATUS GetDmaMemoryManager(OUT struct IDMAMemoryManager **OutMemoryManager);
+    NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
     NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
+
     NTSTATUS StartController();
     NTSTATUS StopController();
     NTSTATUS ResetController();
     NTSTATUS ResetPort(ULONG PortIndex);
+
+    NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange);
+    NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status);
+    NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature);
+
+    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;
-    PULONG m_Base;
-    PDMA_ADAPTER m_Adapter;
-    ULONG m_MapRegisters;
-    PQUEUE_HEAD m_AsyncListQueueHead;
-    EHCI_CAPS m_Capabilities;
-    USHORT m_VendorID;
-    USHORT m_DeviceID;
-
-    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);
 };
 
@@ -120,13 +159,32 @@ CUSBHardwareDevice::Initialize(
     PDEVICE_OBJECT PhysicalDeviceObject,
     PDEVICE_OBJECT LowerDeviceObject)
 {
-    BUS_INTERFACE_STANDARD BusInterface;
     PCI_COMMON_CONFIG PciConfig;
     NTSTATUS Status;
     ULONG BytesRead;
 
     DPRINT1("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(&m_UsbQueue);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create UsbQueue!\n");
+        return Status;
+    }
+
     //
     // store device objects
     // 
@@ -140,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,
@@ -162,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;
 }
 
@@ -209,8 +300,11 @@ CUSBHardwareDevice::PnpStart(
     ULONG Index, Count;
     PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
     DEVICE_DESCRIPTION DeviceDescription;
+    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++)
@@ -268,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);
                 }
 
                 //
@@ -322,15 +433,75 @@ CUSBHardwareDevice::PnpStart(
     }
 
     //
-    // FIXME: Create a QueueHead that will always be the address of the AsyncList
+    // 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;
+    }
+
+    //
+    // Stop the controller before modifying schedules
+    //
+    Status = StopController();
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    //
+    // 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->AlternateNextPointer = TERMINATE_POINTER;
+    AsyncQueueHead->NextPointer = TERMINATE_POINTER;
+    AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
+    AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
+    AsyncQueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
+    AsyncQueueHead->Token.Bits.InterruptOnComplete = FALSE;
+    AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
+    AsyncQueueHead->Token.Bits.Halted = TRUE;
+    AsyncQueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
+    AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0;
+    AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
+    AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
+
+    InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
+
+    //
+    // Initialize the UsbQueue now that we have an AdapterObject.
     //
-    m_AsyncListQueueHead = NULL;
+    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
@@ -355,100 +526,229 @@ CUSBHardwareDevice::GetDeviceDetails(
     OUT OPTIONAL PULONG NumberOfPorts,
     OUT OPTIONAL PULONG Speed)
 {
-    *VendorId = m_VendorID;
-    *DeviceId = m_DeviceID;
-    *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
+    if (VendorId)
+        *VendorId = m_VendorID;
+    if (DeviceId)
+        *DeviceId = m_DeviceID;
+    if (NumberOfPorts)
+        *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
     //FIXME: What to returned here?
-    *Speed = 0;
+    if (Speed)
+        *Speed = 0x200;
     return STATUS_SUCCESS;
 }
 
-NTSTATUS
-CUSBHardwareDevice::GetDmaMemoryManager(
-    OUT struct IDMAMemoryManager **OutMemoryManager)
+NTSTATUS CUSBHardwareDevice::GetDMA(
+    OUT struct IDMAMemoryManager **OutDMAMemoryManager)
 {
-    UNIMPLEMENTED
-    return STATUS_NOT_IMPLEMENTED;
+    if (!m_MemoryManager)
+        return STATUS_UNSUCCESSFUL;
+    *OutDMAMemoryManager = m_MemoryManager;
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
 CUSBHardwareDevice::GetUSBQueue(
     OUT struct IUSBQueue **OutUsbQueue)
 {
-    UNIMPLEMENTED
-    return STATUS_NOT_IMPLEMENTED;
+    if (!m_UsbQueue)
+        return STATUS_UNSUCCESSFUL;
+    *OutUsbQueue = m_UsbQueue;
+    return STATUS_SUCCESS;
 }
 
+
 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);
 
     //
@@ -459,22 +759,33 @@ 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
     //
     EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
+
     DPRINT1("EHCI Started!\n");
     return STATUS_SUCCESS;
 }
@@ -487,6 +798,7 @@ CUSBHardwareDevice::StopController(void)
 
     //
     // Disable Interrupts and stop execution
+    //
     EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
 
     GetCommandRegister(&UsbCmd);
@@ -524,8 +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");
@@ -533,6 +852,8 @@ CUSBHardwareDevice::ResetPort(
         return STATUS_DEVICE_NOT_CONNECTED;
     }
 
+    ASSERT(PortStatus & EHCI_PRT_CONNECTED);
+
     //
     // Reset and clean enable
     //
@@ -540,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
@@ -549,20 +884,299 @@ 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);
+
+    //
+    // 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;
 
     //
-    // Check that the port reset
+    // 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;
 }
 
+NTSTATUS
+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;
+
+        // 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;
+
+    // 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_ResetInProgress[PortId])
+    {
+        Status |= USB_PORT_STATUS_RESET;
+        Change |= USB_PORT_STATUS_RESET;
+    }
+
+    //
+    // FIXME: Is the Change here correct?
+    //
+    if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
+        Change |= USB_PORT_STATUS_CONNECT;
+
+    if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
+        Change |= USB_PORT_STATUS_ENABLE;
+
+    *PortStatus = Status;
+    *PortChange = Change;
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CUSBHardwareDevice::ClearPortStatus(
+    ULONG PortId,
+    ULONG Status)
+{
+    ULONG Value;
+
+    DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
+
+    if (PortId > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    if (Status == C_PORT_RESET)
+    {
+        //
+        // update port status
+        //
+        m_ResetInProgress[PortId] = FALSE;
+    }
+
+    if (Status == C_PORT_CONNECTION)
+    {
+        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;
+}
+
+
+NTSTATUS
+CUSBHardwareDevice::SetPortFeature(
+    ULONG PortId,
+    ULONG Feature)
+{
+    ULONG Value;
+
+    DPRINT("CUSBHardwareDevice::SetPortFeature\n");
+
+    if (PortId > m_Capabilities.HCSParams.PortCount)
+        return STATUS_UNSUCCESSFUL;
+
+    Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
+
+    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)
+    {
+        ResetPort(PortId);
+
+        //
+        // update cached settings
+        //
+        m_ResetInProgress[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, 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;
+}
+
+VOID
+CUSBHardwareDevice::SetAsyncListRegister(
+    ULONG PhysicalAddress)
+{
+    EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
+}
+
+VOID
+CUSBHardwareDevice::SetPeriodicListRegister(
+    ULONG 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
 CUSBHardwareDevice::AcquireDeviceLock(void)
 {
@@ -642,27 +1256,102 @@ EhciDefferedRoutine(
     IN PVOID SystemArgument2)
 {
     CUSBHardwareDevice *This;
-    ULONG CStatus, PortStatus, 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)
     {
-        for (i = 0; i < This->m_Capabilities.HCSParams.PortCount; i++)
+        for (i = 0; i < PortCount; i++)
         {
             PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
 
             //
             // Device connected or removed
             //
-            if (PortStatus & EHCI_PRT_CONNECTSTATUSCHAGE)
+            if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
             {
-                //
-                // Clear the port change status
-                //
-                This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus & EHCI_PRT_CONNECTSTATUSCHAGE);
-
                 if (PortStatus & EHCI_PRT_CONNECTED)
                 {
                     DPRINT1("Device connected on port %d\n", i);
@@ -679,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
@@ -694,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
                 //
@@ -703,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)