[USB-BRINGUP-TRUNK]
[reactos.git] / drivers / usb / usbohci / hardware.cpp
index 40536af..e2e0cdb 100644 (file)
@@ -22,7 +22,7 @@ InterruptServiceRoutine(
 
 VOID
 NTAPI
-EhciDefferedRoutine(
+OhciDefferedRoutine(
     IN PKDPC Dpc,
     IN PVOID DeferredContext,
     IN PVOID SystemArgument1,
@@ -59,6 +59,13 @@ public:
     NTSTATUS PnpStop(void);
     NTSTATUS HandlePower(PIRP Irp);
     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);
 
@@ -75,6 +82,7 @@ public:
 
     KIRQL AcquireDeviceLock(void);
     VOID ReleaseDeviceLock(KIRQL OldLevel);
+    virtual VOID GetCurrentFrameNumber(PULONG FrameNumber);
     // local
     BOOLEAN InterruptService();
     NTSTATUS InitializeController();
@@ -82,7 +90,7 @@ public:
 
     // 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 OhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
     friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
     // constructor / destructor
     CUSBHardwareDevice(IUnknown *OuterUnknown){}
@@ -109,15 +117,16 @@ 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
 };
 
 //=================================================================================================
@@ -249,7 +258,7 @@ CUSBHardwareDevice::PnpStart(
             case CmResourceTypeInterrupt:
             {
                 KeInitializeDpc(&m_IntDpcObject,
-                                EhciDefferedRoutine,
+                                OhciDefferedRoutine,
                                 this);
 
                 Status = IoConnectInterrupt(&m_Interrupt,
@@ -359,23 +368,23 @@ CUSBHardwareDevice::PnpStart(
     }
 
     //
-    // Initialize the UsbQueue now that we have an AdapterObject.
+    // initializes the controller
     //
-    Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
+    Status = InitializeController();
     if (!NT_SUCCESS(Status))
     {
-        DPRINT1("Failed to Initialize the UsbQueue\n");
+        DPRINT1("Failed to Initialize the controller \n");
+        ASSERT(FALSE);
         return Status;
     }
 
     //
-    // initializes the controller
+    // Initialize the UsbQueue now that we have an AdapterObject.
     //
-    Status = InitializeController();
+    Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
     if (!NT_SUCCESS(Status))
     {
-        DPRINT1("Failed to Initialize the controller \n");
-        ASSERT(FALSE);
+        DPRINT1("Failed to Initialize the UsbQueue\n");
         return Status;
     }
 
@@ -484,7 +493,7 @@ CUSBHardwareDevice::GetUSBQueue(
 NTSTATUS
 CUSBHardwareDevice::StartController(void)
 {
-    ULONG Control, NumberOfPorts, Index, Descriptor;
+    ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic;
 
     //
     // first write address of HCCA
@@ -521,6 +530,71 @@ CUSBHardwareDevice::StartController(void)
     //
     WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), Control);
 
+    //
+    // wait a bit
+    //
+    KeStallExecutionProcessor(100);
+
+    //
+    // is the controller started
+    //
+    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);
+
+    //
+    // get frame interval
+    //
+    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);
+
+    //
+    // write frame interval
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
+
+    //
+    // 90 % periodic
+    //
+    Periodic = OHCI_PERIODIC(m_IntervalValue);
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic);
+    DPRINT1("Periodic Start %x\n", Periodic);
+
+    //
+    // read descriptor
+    //
+    Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
+
+    //
+    // no over current protection
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor | OHCI_RH_NO_OVER_CURRENT_PROTECTION);
+
+    //
+    // enable power on all ports
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_STATUS_OFFSET), OHCI_RH_LOCAL_POWER_STATUS_CHANGE);
+
+    //
+    // wait a bit
+    //
+    KeStallExecutionProcessor(10);
+
+    //
+    // write descriptor
+    //
+    WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor);
+
+
+
     //
     // retrieve number of ports
     //
@@ -563,6 +637,12 @@ 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
     //
@@ -609,6 +689,63 @@ CUSBHardwareDevice::AllocateEndpointDescriptor(
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
+    struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
+{
+    *OutDescriptor = m_BulkEndpointDescriptor;
+    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), 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), Value | OHCI_BULK_LIST_FILLED);
+    }
+}
+
+NTSTATUS
+CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
+    struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
+{
+    *OutDescriptor = m_ControlEndpointDescriptor;
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS
 CUSBHardwareDevice::InitializeController()
 {
@@ -729,6 +866,11 @@ CUSBHardwareDevice::InitializeController()
     //
     m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
 
+    //
+    // set iso endpoint type
+    //
+    m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
+
     //
     // done
     //
@@ -738,8 +880,8 @@ CUSBHardwareDevice::InitializeController()
 NTSTATUS
 CUSBHardwareDevice::StopController(void)
 {
-    ULONG Control, Reset;
-    ULONG Index;
+    ULONG Control, Reset, Status;
+    ULONG Index, FrameInterval;
 
     //
     // first turn off all interrupts
@@ -751,10 +893,56 @@ CUSBHardwareDevice::StopController(void)
     //
     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));
+
+        //
+        // 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");
+            //
+            // some controllers also depend on this
+            //
+            WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
+
+            //
+            // wait a bit
+            //
+            KeStallExecutionProcessor(100);
+        }
+    }
+
 
     //
     // have a break
@@ -771,6 +959,18 @@ CUSBHardwareDevice::StopController(void)
     //
     KeStallExecutionProcessor(100);
 
+    //
+    // read from interval
+    //
+    FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
+
+    //
+    // store interval value for later
+    //
+    m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval);
+
+    DPRINT1("FrameInterval %x Interval %x\n", FrameInterval, m_IntervalValue);
+
     //
     // now reset controller
     //
@@ -796,6 +996,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
             //
@@ -831,9 +1036,11 @@ CUSBHardwareDevice::GetPortStatus(
     OUT USHORT *PortStatus,
     OUT USHORT *PortChange)
 {
-    UNIMPLEMENTED
-    *PortStatus = 0;
-    *PortChange = 0;
+    //
+    // FIXME: should read status from hardware
+    //
+    *PortStatus = m_PortStatus[PortId].PortStatus;
+    *PortChange = m_PortStatus[PortId].PortChange;
     return STATUS_SUCCESS;
 }
 
@@ -842,7 +1049,109 @@ CUSBHardwareDevice::ClearPortStatus(
     ULONG PortId,
     ULONG Status)
 {
-    UNIMPLEMENTED
+    ULONG Value;
+
+    DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
+
+    if (PortId > m_NumberOfPorts)
+        return STATUS_UNSUCCESSFUL;
+
+    Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
+    KeStallExecutionProcessor(100);
+
+    if (Status == C_PORT_RESET)
+    {
+        do
+        {
+           //
+           // read port status
+           //
+           Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
+
+           if ((Value & OHCI_RH_PORTSTATUS_PRS)  == 0)
+           {
+               //
+               // reset is complete
+               //
+               break;
+           }
+
+           //
+           // wait a bit
+           //
+           KeStallExecutionProcessor(100);
+
+           //DPRINT1("Value %x Index %lu\n", Value, Index);
+
+        }while(TRUE);
+
+        //
+        // check if reset bit is still set
+        //
+        if (Value & OHCI_RH_PORTSTATUS_PRS)
+        {
+            //
+            // reset failed
+            //
+            DPRINT1("PortId %lu Reset failed\n", PortId);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        //
+        // sanity checks
+        //
+        ASSERT((Value & OHCI_RH_PORTSTATUS_PRS) == 0);
+        ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC));
+
+        //
+        // clear reset bit complete
+        //
+        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
+        //
+        Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), Value | OHCI_ROOT_HUB_STATUS_CHANGE);
+
+    }
+
+    if (Status == C_PORT_CONNECTION)
+    {
+        //
+        // clear bit
+        //
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC);
+        m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_CONNECT;
+    }
+
+
+
     return STATUS_SUCCESS;
 }
 
@@ -852,6 +1161,16 @@ CUSBHardwareDevice::SetPortFeature(
     ULONG PortId,
     ULONG Feature)
 {
+    ULONG Value;
+
+    DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId, Feature);
+
+    //
+    // read port status
+    //
+    Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
+
+
     if (Feature == PORT_ENABLE)
     {
         //
@@ -878,11 +1197,27 @@ CUSBHardwareDevice::SetPortFeature(
     }
     else if (Feature == PORT_RESET)
     {
+        //
+        // assert
+        //
+        ASSERT((Value & OHCI_RH_PORTSTATUS_CCS));
+
         //
         // reset port
         //
         WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
 
+        //
+        // wait 
+        //
+        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
         //
@@ -924,6 +1259,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(
@@ -938,19 +1302,234 @@ InterruptServiceRoutine(
     IN PKINTERRUPT  Interrupt,
     IN PVOID  ServiceContext)
 {
-    ASSERT(FALSE);
+    CUSBHardwareDevice *This;
+    ULONG DoneHead, Status, Acknowledge = 0;
+
+    //
+    // get context
+    //
+    This = (CUSBHardwareDevice*) ServiceContext;
+
+    DPRINT("InterruptServiceRoutine\n");
+
+    //
+    // get done head
+    //
+    DoneHead = This->m_HCCA->DoneHead;
+
+    //
+    // check if zero
+    //
+    if (DoneHead == 0)
+    {
+        //
+        // the interrupt was not caused by DoneHead update
+        // check if something important happened
+        //
+        Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET)) & (~OHCI_WRITEBACK_DONE_HEAD); 
+        if (Status == 0)
+        {
+            //
+            // nothing happened, appears to be shared interrupt
+            //
+            return FALSE;
+        }
+    }
+    else
+    {
+        //
+        // DoneHead update happened, check if there are other events too
+        //
+        Status = OHCI_WRITEBACK_DONE_HEAD;
+
+        //
+        // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
+        //
+        if (DoneHead & OHCI_DONE_INTERRUPTS)
+        {
+            //
+            // get other events
+            //
+            Status |= READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
+        }
+    }
+
+    //
+    // sanity check
+    //
+    ASSERT(Status != 0);
+
+     if (Status & OHCI_WRITEBACK_DONE_HEAD)
+     {
+         //
+         // head completed
+         //
+         Acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
+         This->m_HCCA->DoneHead = 0;
+    }
+
+    if (Status & OHCI_RESUME_DETECTED)
+    {
+        //
+        // resume
+        //
+        DPRINT1("InterruptServiceRoutine> Resume\n");
+        Acknowledge |= OHCI_RESUME_DETECTED;
+    }
+
+
+    if (Status & OHCI_UNRECOVERABLE_ERROR)
+    {
+        DPRINT1("InterruptServiceRoutine> Controller error\n");
+
+        //
+        // halt controller
+        //
+        ASSERT(FALSE);
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
+    }
+
+    if (Status & OHCI_ROOT_HUB_STATUS_CHANGE) 
+    {
+        //
+        // new device has arrived
+        //
+
+        //
+        // disable interrupt as it will fire untill the port has been reset
+        //
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
+        Acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE;
+    }
+
+    //
+    // is there something to acknowledge
+    //
+    if (Acknowledge)
+    {
+        //
+        // ack change
+        //
+        WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET), Acknowledge);
+    }
+
+    //
+    // defer processing
+    //
+    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
+    //
     return TRUE;
 }
 
-VOID NTAPI
-EhciDefferedRoutine(
+VOID
+NTAPI
+OhciDefferedRoutine(
     IN PKDPC Dpc,
     IN PVOID DeferredContext,
     IN PVOID SystemArgument1,
     IN PVOID SystemArgument2)
 {
-    ASSERT(FALSE);
-    return;
+    CUSBHardwareDevice *This;
+    ULONG CStatus, Index, PortStatus;
+    ULONG DoneHead;
+
+    //
+    // get parameters
+    //
+    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)
+    {
+        //
+        // device connected, lets check which port
+        //
+        for(Index = 0; Index < This->m_NumberOfPorts; Index++)
+        {
+            //
+            // read port status
+            //
+            PortStatus = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)));
+
+            //
+            // check if there is a status change
+            //
+            if (PortStatus & OHCI_RH_PORTSTATUS_CSC)
+            {
+                //
+                // did a device connect
+                //
+                if (PortStatus & OHCI_RH_PORTSTATUS_CCS)
+                {
+                    //
+                    // device connected
+                    //
+                    DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index, (PortStatus & OHCI_RH_PORTSTATUS_LSDA));
+
+                    //
+                    // 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;
+                    }
+                }
+                else
+                {
+                    //
+                    // device disconnected
+                    //
+                    DPRINT1("Device disconnected at Port %x\n", Index);
+
+                    //
+                    // update port status flags
+                    //
+                    This->m_PortStatus[Index].PortStatus &= ~USB_PORT_STATUS_LOW_SPEED;
+                    This->m_PortStatus[Index].PortStatus &= ~USB_PORT_STATUS_CONNECT;
+                    This->m_PortStatus[Index].PortChange |= USB_PORT_STATUS_CONNECT;
+                }
+
+                //
+                // is there a status change callback
+                //
+                if (This->m_SCECallBack != NULL)
+                {
+                    //
+                    // queue work item for processing
+                    //
+                    ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
+                }
+            }
+        }
+    }
+
+
 }
 
 VOID