[USBOHCI]
[reactos.git] / drivers / usb / usbohci / hardware.cpp
index 9f7e8cb..cc35707 100644 (file)
@@ -61,8 +61,11 @@ public:
     NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed);
     NTSTATUS GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
     NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
+    NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor);
+    NTSTATUS GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
     VOID HeadEndpointDescriptorModified(ULONG HeadType);
 
+
     NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
     NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
 
@@ -79,6 +82,7 @@ public:
 
     KIRQL AcquireDeviceLock(void);
     VOID ReleaseDeviceLock(KIRQL OldLevel);
+    virtual VOID GetCurrentFrameNumber(PULONG FrameNumber);
     // local
     BOOLEAN InterruptService();
     NTSTATUS InitializeController();
@@ -113,16 +117,15 @@ protected:
     PHYSICAL_ADDRESS m_HCCAPhysicalAddress;                                            // hcca physical address
     POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor;                             // dummy control endpoint descriptor
     POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor;                                // dummy control endpoint descriptor
-    POHCI_ENDPOINT_DESCRIPTOR  m_IsoEndpointDescriptor;                                // iso endpoint descriptor
+    POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor;                                 // iso endpoint descriptor
     POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints[OHCI_STATIC_ENDPOINT_COUNT];        // endpoints for interrupt / iso transfers
     ULONG m_NumberOfPorts;                                                             // number of ports
-    OHCI_PORT_STATUS m_PortStatus[OHCI_MAX_PORT_COUNT];                                // port change status
     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
+    ULONG m_IntervalValue;                                                             // periodic interval value
 };
 
 //=================================================================================================
@@ -489,12 +492,7 @@ CUSBHardwareDevice::GetUSBQueue(
 NTSTATUS
 CUSBHardwareDevice::StartController(void)
 {
-    ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic, IntervalValue;
-
-    //
-    // first write address of HCCA
-    //
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
+    ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic;
 
     //
     // lets write physical address of dummy control endpoint descriptor
@@ -507,54 +505,62 @@ CUSBHardwareDevice::StartController(void)
     WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart);
 
     //
-    // read control register
+    // get frame interval
     //
-    Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
+    FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
+    FrameInterval = ((FrameInterval & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE);
+    DPRINT1("FrameInterval %x IntervalValue %x\n", FrameInterval, m_IntervalValue);
+    FrameInterval |= OHCI_FSMPS(m_IntervalValue) | m_IntervalValue;
+    DPRINT1("FrameInterval %x\n", FrameInterval);
 
     //
-    // remove flags
-    // 
-    Control &= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK | OHCI_ENABLE_LIST | OHCI_HC_FUNCTIONAL_STATE_MASK | OHCI_INTERRUPT_ROUTING);
+    // write frame interval
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
 
     //
-    // set command status flags
+    // write address of HCCA
     //
-    Control |= OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL;
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
 
     //
-    // now start the controller
+    // now enable the interrupts
     //
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), Control);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE);
 
     //
-    // wait a bit
+    // enable all queues
     //
-    KeStallExecutionProcessor(100);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST);
 
     //
-    // is the controller started
+    // 90 % periodic
     //
-    Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
+    Periodic = OHCI_PERIODIC(m_IntervalValue);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic);
+    DPRINT1("Periodic Start %x\n", Periodic);
 
     //
-    // assert that the controller has been started
+    // start the controller
     //
-    ASSERT((Control & OHCI_HC_FUNCTIONAL_STATE_MASK) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
 
     //
-    // get frame interval
+    // wait a bit
     //
-    //FrameInterval = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE;
-    //FrameInterval |= OHCI_FSMPS(IntervalValue) | IntervalValue;
+    KeStallExecutionProcessor(100);
 
     //
-    // write frame interval
+    // is the controller started
     //
-    //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
-    // 90% periodic
-    //Periodic = OHCI_PERIODIC(intervalValue);
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + 0x40 /*OHCI_PERIODIC_START_OFFSET*/), 0x3E67);
+    Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
 
+    //
+    // assert that the controller has been started
+    //
+    ASSERT((Control & OHCI_HC_FUNCTIONAL_STATE_MASK) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
+    ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
+    DPRINT1("Control %x\n", Control);
 
     //
     // read descriptor
@@ -581,8 +587,6 @@ CUSBHardwareDevice::StartController(void)
     //
     WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor);
 
-
-
     //
     // retrieve number of ports
     //
@@ -626,11 +630,6 @@ CUSBHardwareDevice::StartController(void)
     DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts);
 
 
-    //
-    // now enable the interrupts
-    //
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE);
-
     //
     // done
     //
@@ -685,23 +684,44 @@ CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+CUSBHardwareDevice::GetInterruptEndpointDescriptors(
+    struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor)
+{
+    *OutDescriptor = m_InterruptEndpoints;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor(
+    struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
+{
+    //
+    // get descriptor
+    //
+    *OutDescriptor = m_IsoEndpointDescriptor;
+    return STATUS_SUCCESS;
+}
+
 VOID
 CUSBHardwareDevice::HeadEndpointDescriptorModified(
     ULONG Type)
 {
+    ULONG Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
+
     if (Type == USB_ENDPOINT_TYPE_CONTROL)
     {
         //
         // notify controller
         //
-        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_CONTROL_LIST_FILLED);
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_CONTROL_LIST_FILLED);
     }
     else if (Type == USB_ENDPOINT_TYPE_BULK)
     {
         //
         // notify controller
         //
-        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_BULK_LIST_FILLED);
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_BULK_LIST_FILLED);
     }
 }
 
@@ -833,6 +853,11 @@ CUSBHardwareDevice::InitializeController()
     //
     m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
 
+    //
+    // set iso endpoint type
+    //
+    m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
+
     //
     // done
     //
@@ -842,38 +867,171 @@ CUSBHardwareDevice::InitializeController()
 NTSTATUS
 CUSBHardwareDevice::StopController(void)
 {
-    ULONG Control, Reset;
-    ULONG Index;
+    ULONG Control, Reset, Status;
+    ULONG Index, FrameInterval;
 
     //
-    // first turn off all interrupts
+    // alignment check
     //
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ALL_INTERRUPTS);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), 0xFFFFFFFF);
+    Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET));
+    //ASSERT((m_HCCAPhysicalAddress.QuadPart & Control) == Control);
+
 
     //
     // check context
     //
     Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
 
-    //
-    // FIXME: support routing
-    //
-    ASSERT((Control & OHCI_INTERRUPT_ROUTING) == 0);
+    if ((Control & OHCI_INTERRUPT_ROUTING))
+    {
+        //
+        // read command status
+        //
+        Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
 
-    //
-    // have a break
-    //
-    KeStallExecutionProcessor(100);
+        //
+        // change ownership
+        //
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Status | OHCI_OWNERSHIP_CHANGE_REQUEST);
+        for(Index = 0; Index < 100; Index++)
+        {
+            //
+            // wait a bit
+            //
+            KeStallExecutionProcessor(100);
+
+            //
+            // check control
+            //
+            Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
+            if (!(Control & OHCI_INTERRUPT_ROUTING))
+            {
+                //
+                // acquired ownership
+                //
+                break;
+            }
+        }    
+
+        //
+        // if the ownership is still not changed, perform reset
+        //
+        if (Control & OHCI_INTERRUPT_ROUTING)
+        {
+            DPRINT1("SMM not responding\n");
+        }
+        else
+        {
+            DPRINT1("SMM has given up ownership\n");
+        }
+    }
+    else
+    {
+        //
+        // read contents of control register
+        //
+        Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
+        DPRINT1("Controller State %x\n", Control);
+
+        if (Control != OHCI_HC_FUNCTIONAL_STATE_RESET)
+        {
+            //
+            // OHCI 5.1.1.3.4, no SMM, BIOS active
+            //
+            if (Control != OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL)
+            {
+                //
+                // lets resume
+                //
+                WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESUME);
+                Index = 0;
+                do
+                {
+                    //
+                    // wait untill its resumed
+                    //
+                    KeStallExecutionProcessor(10);
+
+                    //
+                    // check control register
+                    //
+                    Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
+                    if (Control & OHCI_HC_FUNCTIONAL_STATE_RESUME)
+                    {
+                        //
+                        // it has resumed
+                        //
+                        break;
+                    }
+
+                    //
+                    // check for time outs
+                    //
+                    Index++;
+                    if(Index > 100)
+                    {
+                        DPRINT1("Failed to resume controller\n");
+                        break;
+                    }
+                }while(TRUE);
+            }
+        }
+        else
+        {
+            //
+            // 5.1.1.3.5 OHCI, no SMM, no BIOS
+            //
+            Index = 0;
+
+            //
+            // some controllers also depend on this
+            //
+            WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
+            do
+            {
+                 //
+                 // wait untill its reset
+                 //
+                 KeStallExecutionProcessor(10);
+
+                 //
+                 // check control register
+                 //
+                 Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
+                 if (Control == OHCI_HC_FUNCTIONAL_STATE_RESET)
+                 {
+                     //
+                     // it has reset
+                     //
+                     break;
+                 }
+
+                 //
+                 // check for time outs
+                 //
+                 Index++;
+                 if(Index > 100)
+                 {
+                    DPRINT1("Failed to reset controller\n");
+                    break;
+                 }
+
+            }while(TRUE);
+        }
+    }
 
     //
-    // some controllers also depend on this
+    // read from interval
     //
-    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
+    FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
 
     //
-    // wait a bit
+    // store interval value for later
     //
-    KeStallExecutionProcessor(100);
+    m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval);
+
+    DPRINT1("FrameInterval %x Interval %x\n", FrameInterval, m_IntervalValue);
 
     //
     // now reset controller
@@ -900,6 +1058,11 @@ CUSBHardwareDevice::StopController(void)
         //
         if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0)
         {
+            //
+            // restore the frame interval register
+            //
+            WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
+
             //
             // controller completed reset
             //
@@ -935,11 +1098,60 @@ CUSBHardwareDevice::GetPortStatus(
     OUT USHORT *PortStatus,
     OUT USHORT *PortChange)
 {
+    ULONG Value;
+
+    if (PortId > m_NumberOfPorts)
+        return STATUS_UNSUCCESSFUL;
+
+    // init result variables
+    *PortStatus = 0;
+    *PortChange = 0;
+
     //
-    // FIXME: should read status from hardware
+    // read port status
     //
-    *PortStatus = m_PortStatus[PortId].PortStatus;
-    *PortChange = m_PortStatus[PortId].PortChange;
+    Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
+    DPRINT("GetPortStatus PortId %x Value %x\n", PortId, Value);
+
+    // connected
+    if (Value & OHCI_RH_PORTSTATUS_CCS)
+        *PortStatus |= USB_PORT_STATUS_CONNECT;
+
+    // did a device connect?
+    if (Value & OHCI_RH_PORTSTATUS_CSC)
+        *PortChange |= USB_PORT_STATUS_CONNECT;
+
+    // port enabled
+    if (Value & OHCI_RH_PORTSTATUS_PES)
+        *PortStatus |= USB_PORT_STATUS_ENABLE;
+
+    // port disconnect or hardware error
+    if (Value & OHCI_RH_PORTSTATUS_PESC)
+        *PortChange |= USB_PORT_STATUS_CONNECT;
+
+    // port suspend
+    if (Value & OHCI_RH_PORTSTATUS_PSS)
+        *PortStatus |= USB_PORT_STATUS_SUSPEND;
+
+    // port suspend
+    if (Value & OHCI_RH_PORTSTATUS_PSSC)
+        *PortChange |= USB_PORT_STATUS_ENABLE;
+
+    // port reset started (change bit only set at completion)
+    if (Value & OHCI_RH_PORTSTATUS_PRS)
+    {
+        *PortStatus |= USB_PORT_STATUS_RESET;
+        *PortChange |= USB_PORT_STATUS_RESET;
+    }
+
+    // port reset ended (change bit only set at completion)
+    if (Value & OHCI_RH_PORTSTATUS_PRSC)
+        *PortChange |= USB_PORT_STATUS_RESET;
+
+    // low speed device
+    if (Value & OHCI_RH_PORTSTATUS_LSDA)
+        *PortStatus |= USB_PORT_STATUS_LOW_SPEED;
+
     return STATUS_SUCCESS;
 }
 
@@ -948,7 +1160,7 @@ CUSBHardwareDevice::ClearPortStatus(
     ULONG PortId,
     ULONG Status)
 {
-    ULONG Value, Index = 0;
+    ULONG Value;
 
     DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
 
@@ -999,7 +1211,6 @@ CUSBHardwareDevice::ClearPortStatus(
         //
         // sanity checks
         //
-        ASSERT((Value & OHCI_RH_PORTSTATUS_PRS) == 0);
         ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC));
 
         //
@@ -1007,47 +1218,24 @@ CUSBHardwareDevice::ClearPortStatus(
         //
         WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRSC);
 
-        //
-        // read status register
-        //
-        Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
-
-        //
-        // reset complete bit should be cleared
-        //
-        ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC) == 0);
-
-        //
-        // update port status
-        //
-        m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_RESET;
-
         //
         // sanity check
         //
         ASSERT((Value & OHCI_RH_PORTSTATUS_PES));
-
-        //
-        // port is enabled
-        //
-        m_PortStatus[PortId].PortStatus |= USB_PORT_STATUS_ENABLE;
-
-        //
-        // re-enable root hub change
-        //
-        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
     }
 
-    if (Status == C_PORT_CONNECTION)
+    if (Status == C_PORT_CONNECTION || Status == C_PORT_ENABLE)
     {
         //
-        // clear bit
+        // clear bits
         //
-        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC);
-        m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_CONNECT;
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC | OHCI_RH_PORTSTATUS_PESC);
     }
 
-
+    //
+    // re-enable root hub change
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
 
     return STATUS_SUCCESS;
 }
@@ -1078,10 +1266,39 @@ CUSBHardwareDevice::SetPortFeature(
     }
     else if (Feature == PORT_POWER)
     {
+        LARGE_INTEGER Timeout;
+
         //
         // enable power
         //
         WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS);
+
+        //
+        // read descriptor A for the delay data
+        //
+        Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
+
+        //
+        // compute the delay
+        //
+        Timeout.QuadPart = OHCI_RH_GET_POWER_ON_TO_POWER_GOOD_TIME(Value);
+
+        //
+        // delay is multiplied by 2 ms
+        //
+        Timeout.QuadPart *= 2;
+        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;
     }
     else if (Feature == PORT_SUSPEND)
@@ -1109,12 +1326,6 @@ CUSBHardwareDevice::SetPortFeature(
         //
         KeStallExecutionProcessor(100);
 
-        //
-        // update cached settings
-        //
-        m_PortStatus[PortId].PortChange |= USB_PORT_STATUS_RESET;
-        m_PortStatus[PortId].PortStatus &= ~USB_PORT_STATUS_ENABLE;
-
         //
         // is there a status change callback
         //
@@ -1156,6 +1367,35 @@ CUSBHardwareDevice::AcquireDeviceLock(void)
     return OldLevel;
 }
 
+VOID
+CUSBHardwareDevice::GetCurrentFrameNumber(
+    PULONG FrameNumber)
+{
+    ULONG Control;
+    ULONG Number;
+
+
+    Number = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_NUMBER_OFFSET));
+    DPRINT1("FrameNumberInterval %x Frame %x\n", Number, m_HCCA->CurrentFrameNumber);
+
+    //
+    // remove reserved bits
+    //
+    Number &= 0xFFFF;
+
+    //
+    // store frame number
+    //
+    *FrameNumber = Number;
+
+    //
+    // is the controller started
+    //
+    Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
+    ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
+
+
+}
 
 VOID
 CUSBHardwareDevice::ReleaseDeviceLock(
@@ -1178,7 +1418,7 @@ InterruptServiceRoutine(
     //
     This = (CUSBHardwareDevice*) ServiceContext;
 
-    DPRINT1("InterruptServiceRoutine\n");
+    DPRINT("InterruptServiceRoutine\n");
 
     //
     // get done head
@@ -1232,12 +1472,8 @@ InterruptServiceRoutine(
          //
          // head completed
          //
-         DPRINT1("InterruptServiceRoutine> Done Head completion\n");
-         ASSERT(FALSE);
-         //
-         // FIXME: handle event
-         //
          Acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
+         This->m_HCCA->DoneHead = 0;
     }
 
     if (Status & OHCI_RESUME_DETECTED)
@@ -1263,10 +1499,6 @@ InterruptServiceRoutine(
 
     if (Status & OHCI_ROOT_HUB_STATUS_CHANGE) 
     {
-        //
-        // new device has arrived
-        //
-
         //
         // disable interrupt as it will fire untill the port has been reset
         //
@@ -1288,8 +1520,8 @@ InterruptServiceRoutine(
     //
     // defer processing
     //
-    DPRINT1("Status %x\n", Status);
-    KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)Status);
+    DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status, Acknowledge, This->m_HCCA->CurrentFrameNumber);
+    KeInsertQueueDpc(&This->m_IntDpcObject, (PVOID)Status, (PVOID)(DoneHead & ~1));
 
     //
     // interrupt handled
@@ -1307,13 +1539,24 @@ OhciDefferedRoutine(
 {
     CUSBHardwareDevice *This;
     ULONG CStatus, Index, PortStatus;
+    ULONG DoneHead;
 
     //
     // get parameters
     //
-    This = (CUSBHardwareDevice*) SystemArgument1;
-    CStatus = (ULONG) SystemArgument2;
+    This = (CUSBHardwareDevice*)DeferredContext;
+    CStatus = (ULONG) SystemArgument1;
+    DoneHead = (ULONG)SystemArgument2;
+
+    DPRINT("OhciDefferedRoutine Status %x\n", CStatus);
 
+    if (CStatus & OHCI_WRITEBACK_DONE_HEAD)
+    {
+        //
+        // notify queue of event
+        //
+        This->m_UsbQueue->TransferDescriptorCompletionCallback(DoneHead);
+    }
     if (CStatus & OHCI_ROOT_HUB_STATUS_CHANGE)
     {
         //
@@ -1345,32 +1588,6 @@ OhciDefferedRoutine(
                     // enable port
                     //
                     WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)), OHCI_RH_PORTSTATUS_PES);
-
-
-                    //
-                    // store change
-                    //
-                    This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_CONNECT;
-                    This->m_PortStatus[Index].PortChange |= USB_PORT_STATUS_CONNECT;
-
-                    if ((PortStatus & OHCI_RH_PORTSTATUS_LSDA))
-                    {
-                        //
-                        // low speed device connected
-                        //
-                        This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_LOW_SPEED;
-                    }
-
-                    //
-                    // is there a status change callback
-                    //
-                    if (This->m_SCECallBack != NULL)
-                    {
-                        //
-                        // queue work item for processing
-                        //
-                        ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
-                    }
                 }
                 else
                 {
@@ -1379,6 +1596,17 @@ OhciDefferedRoutine(
                     //
                     DPRINT1("Device disconnected at Port %x\n", Index);
                 }
+
+                //
+                // is there a status change callback
+                //
+                if (This->m_SCECallBack != NULL)
+                {
+                    //
+                    // queue work item for processing
+                    //
+                    ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
+                }
             }
         }
     }