Author: cgutman Date: Thu Mar 1 18:05:59 2012 New Revision: 55948 URL: http://svn.reactos.org/svn/reactos?rev=55948&view=rev Log: [USBOHCI] - Add a OHCI reset hack based on Linux code
Modified: trunk/reactos/drivers/usb/usbohci/hardware.cpp Modified: trunk/reactos/drivers/usb/usbohci/hardware.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbohci/hardware.cpp?rev=55948&r1=55947&r2=55948&view=diff ============================================================================== --- trunk/reactos/drivers/usb/usbohci/hardware.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/usb/usbohci/hardware.cpp [iso-8859-1] Thu Mar 1 18:05:59 2012 @@ -367,18 +367,6 @@ return Status; } - - // - // Stop the controller before modifying schedules - // - Status = StopController(); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to stop the controller \n"); - return Status; - } - - // // Start the controller // @@ -468,381 +456,18 @@ NTSTATUS CUSBHardwareDevice::StartController(void) { - ULONG Control, Descriptor, FrameInterval, Periodic, Port; - - // - // lets write physical address of dummy control endpoint descriptor - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart); - - // - // lets write physical address of dummy bulk endpoint descriptor - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart); - - // - // read descriptor A - // - Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET)); - - // - // get port count - // - m_NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor); - DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts); - ASSERT(m_NumberOfPorts < OHCI_MAX_PORT_COUNT); - - // - // no over current protection - // - Descriptor |= OHCI_RH_NO_OVER_CURRENT_PROTECTION; - - // - // power switching on - // - Descriptor &= ~OHCI_RH_NO_POWER_SWITCHING; - - // - // control each port power independently - // - Descriptor |= OHCI_RH_POWER_SWITCHING_MODE; - - // - // write the configuration back - // - DPRINT1("Descriptor A: %x\n", Descriptor); - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor); - - // - // read descriptor B - // - Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_B_OFFSET)); - - // - // set power power control for each port to use PPS - // - for (Port = 1; Port <= m_NumberOfPorts; Port++) - { - Descriptor |= (1 << (16 + Port)); - } - - // - // write the configuration back - // - DPRINT1("Descriptor B: %x\n", Descriptor); - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_B_OFFSET), Descriptor); - - // - // get frame interval - // - FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)); - m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval); - - 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); - - // - // HCCA alignment check - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), 0xFFFFFFFF); - KeStallExecutionProcessor(10); - Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET)); - ASSERT((m_HCCAPhysicalAddress.LowPart & Control) == m_HCCAPhysicalAddress.LowPart); - DPRINT1("HCCA: %x Alignment mask: %x\n", m_HCCAPhysicalAddress.LowPart, Control); - - // - // write address of HCCA - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart); - - // - // now enable the interrupts - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE); - - // - // enable all queues - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST); - - // - // 90 % periodic - // - Periodic = OHCI_PERIODIC(m_IntervalValue); - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic); - DPRINT("Periodic Start %x\n", Periodic); - - // - // start the controller - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL); - - // - // wait a bit - // - KeStallExecutionProcessor(100); - - // - // is the controller started + ULONG Control, Descriptor, FrameInterval, Periodic, Port, Reset, Index; + ULONG NewControl, WaitInMs; + LARGE_INTEGER Timeout; + BOOLEAN Again = FALSE; + + // + // check context // 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); - - // - // done - // - DPRINT1("OHCI controller is operational\n"); - return STATUS_SUCCESS; -} - -NTSTATUS -CUSBHardwareDevice::AllocateEndpointDescriptor( - OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor) -{ - POHCI_ENDPOINT_DESCRIPTOR Descriptor; - PHYSICAL_ADDRESS DescriptorAddress; - NTSTATUS Status; - - // - // allocate descriptor - // - Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress); - if (!NT_SUCCESS(Status)) - { - // - // failed to allocate descriptor - // - return Status; - } - - // - // intialize descriptor - // - Descriptor->Flags = OHCI_ENDPOINT_SKIP; - Descriptor->HeadPhysicalDescriptor = 0; - Descriptor->NextPhysicalEndpoint = 0; - Descriptor->TailPhysicalDescriptor = 0; - Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; - - // - // store result - // - *OutDescriptor = Descriptor; - - // - // done - // - return STATUS_SUCCESS; -} - -VOID -STDMETHODCALLTYPE -CUSBHardwareDevice::GetBulkHeadEndpointDescriptor( - struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) -{ - *OutDescriptor = m_BulkEndpointDescriptor; -} - -VOID -STDMETHODCALLTYPE -CUSBHardwareDevice::GetInterruptEndpointDescriptors( - struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor) -{ - *OutDescriptor = m_InterruptEndpoints; -} - -VOID -STDMETHODCALLTYPE -CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor( - struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) -{ - *OutDescriptor = m_IsoEndpointDescriptor; -} - -VOID -STDMETHODCALLTYPE -CUSBHardwareDevice::HeadEndpointDescriptorModified( - ULONG Type) -{ - if (Type == USB_ENDPOINT_TYPE_CONTROL) - { - // - // notify controller - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), 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); - } -} - -VOID -STDMETHODCALLTYPE -CUSBHardwareDevice::GetControlHeadEndpointDescriptor( - struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) -{ - *OutDescriptor = m_ControlEndpointDescriptor; -} - -NTSTATUS -CUSBHardwareDevice::InitializeController() -{ - NTSTATUS Status; - ULONG Index, Interval, IntervalIndex, InsertIndex; - POHCI_ENDPOINT_DESCRIPTOR Descriptor; - - // - // first allocate the hcca area - // - Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress); - if (!NT_SUCCESS(Status)) - { - // - // no memory - // - return Status; - } - - // - // now allocate an endpoint for control transfers - // this endpoint will never be removed - // - Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor); - if (!NT_SUCCESS(Status)) - { - // - // no memory - // - return Status; - } - - // - // now allocate an endpoint for bulk transfers - // this endpoint will never be removed - // - Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor); - if (!NT_SUCCESS(Status)) - { - // - // no memory - // - return Status; - } - - // - // now allocate an endpoint for iso transfers - // this endpoint will never be removed - // - Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor); - if (!NT_SUCCESS(Status)) - { - // - // no memory - // - return Status; - } - - // - // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32 - // - for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) - { - // - // allocate endpoint descriptor - // - Status = AllocateEndpointDescriptor(&Descriptor); - if (!NT_SUCCESS(Status)) - { - // - // no memory - // - return Status; - } - - // - // save in array - // - m_InterruptEndpoints[Index] = Descriptor; - } - - - // - // now link the descriptors, taken from Haiku - // - Interval = OHCI_BIGGEST_INTERVAL; - IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1; - while (Interval > 1) - { - InsertIndex = Interval / 2; - while (InsertIndex < OHCI_BIGGEST_INTERVAL) - { - // - // assign endpoint address - // - m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart; - InsertIndex += Interval; - } - - IntervalIndex--; - Interval /= 2; - } - - // - // link all endpoint descriptors to first descriptor in array - // - m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; - for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) - { - // - // link descriptor - // - m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; - } - - // - // Now link the first endpoint to the isochronous endpoint - // - m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart; - - // - // set iso endpoint type - // - m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; - - // - // done - // - return STATUS_SUCCESS; -} - -NTSTATUS -CUSBHardwareDevice::StopController(void) -{ - ULONG Control, Reset; - ULONG Index, FrameInterval; - - // - // check context - // - Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)); + //Save this + NewControl = Control & OHCI_REMOTE_WAKEUP_CONNECTED; if ((Control & OHCI_INTERRUPT_ROUTING)) { @@ -879,151 +504,495 @@ } else { - DPRINT("SMM has given up ownership\n"); + 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); - DPRINT("Controller State %x\n", Control); - - if (Control != OHCI_HC_FUNCTIONAL_STATE_RESET) + + // + // 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); + + switch (Control) + { + case OHCI_HC_FUNCTIONAL_STATE_RESET: + NewControl |= OHCI_HC_FUNCTIONAL_STATE_RESET; + WaitInMs = 50; + break; + + case OHCI_HC_FUNCTIONAL_STATE_SUSPEND: + case OHCI_HC_FUNCTIONAL_STATE_RESUME: + NewControl |= OHCI_HC_FUNCTIONAL_STATE_RESUME; + WaitInMs = 10; + break; + + default: + WaitInMs = 0; + break; + } + +retry: + if (WaitInMs != 0) + { + // Do the state transition + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), NewControl); + + if (!Again) { // - // 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); - } + // delay is 100 ms + // + Timeout.QuadPart = WaitInMs; + DPRINT1("Waiting %d milliseconds for controller to transition state\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + } + } + + // + // now reset controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET); + + // + // reset time is 10ms + // + for(Index = 0; Index < 100; Index++) + { + // + // wait a bit + // + KeStallExecutionProcessor(10); + + // + // read command status + // + Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET)); + + // + // was reset bit cleared + // + if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0) + { + // + // controller completed reset + // + break; + } + } + + if ((Reset & OHCI_HOST_CONTROLLER_RESET)) + { + // + // failed to reset controller + // + return STATUS_UNSUCCESSFUL; + } + + // + // get frame interval + // + FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)); + m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval); + + 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("Computed FrameInterval %x\n", FrameInterval); + + // + // write frame interval + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval); + + FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)); + DPRINT1("Read FrameInterval %x\n", FrameInterval); + + // + // 90 % periodic + // + Periodic = OHCI_PERIODIC(m_IntervalValue); + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic); + DPRINT1("Computed Periodic Start %x\n", Periodic); + + Periodic = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET)); + DPRINT1("Read Periodic Start %x\n", Periodic); + + // Linux does this hack for some bad controllers + if (!(FrameInterval & 0x3FFF0000) || + !(Periodic)) + { + if (!Again) + { + DPRINT1("Trying reset again on faulty controller\n"); + Again = TRUE; + goto retry; } 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); + DPRINT1("Second reset didn't solve the problem, failing\n"); + return STATUS_UNSUCCESSFUL; } } // - // 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 - // - WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET); - - // - // reset time is 10ms - // - for(Index = 0; Index < 100; Index++) - { - // - // wait a bit - // - KeStallExecutionProcessor(10); - - // - // read command status - // - Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET)); - - // - // was reset bit cleared - // - if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0) + // lets write physical address of dummy control endpoint descriptor + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart); + + // + // lets write physical address of dummy bulk endpoint descriptor + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart); + + // + // read descriptor A + // + Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET)); + + // + // get port count + // + m_NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor); + DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts); + ASSERT(m_NumberOfPorts < OHCI_MAX_PORT_COUNT); + + // + // no over current protection + // + Descriptor |= OHCI_RH_NO_OVER_CURRENT_PROTECTION; + + // + // power switching on + // + Descriptor &= ~OHCI_RH_NO_POWER_SWITCHING; + + // + // control each port power independently + // + Descriptor |= OHCI_RH_POWER_SWITCHING_MODE; + + // + // write the configuration back + // + DPRINT1("Descriptor A: %x\n", Descriptor); + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor); + + // + // read descriptor B + // + Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_B_OFFSET)); + + // + // set power power control for each port to use PPS + // + for (Port = 1; Port <= m_NumberOfPorts; Port++) + { + Descriptor |= (1 << (16 + Port)); + } + + // + // write the configuration back + // + DPRINT1("Descriptor B: %x\n", Descriptor); + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_B_OFFSET), Descriptor); + + // + // HCCA alignment check + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), 0xFFFFFFFF); + KeStallExecutionProcessor(10); + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET)); + ASSERT((m_HCCAPhysicalAddress.LowPart & Control) == m_HCCAPhysicalAddress.LowPart); + DPRINT1("HCCA: %x Alignment mask: %x\n", m_HCCAPhysicalAddress.LowPart, Control); + + // + // write address of HCCA + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart); + + // + // now enable the interrupts + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE); + + // + // enable all queues + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), (NewControl & OHCI_REMOTE_WAKEUP_CONNECTED) | OHCI_ENABLE_LIST); + + // + // start the controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST | + (NewControl & OHCI_REMOTE_WAKEUP_CONNECTED) | + OHCI_CONTROL_BULK_RATIO_1_4 | + OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL); + + // + // 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); + + // + // done + // + DPRINT1("OHCI controller is operational\n"); + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::AllocateEndpointDescriptor( + OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + PHYSICAL_ADDRESS DescriptorAddress; + NTSTATUS Status; + + // + // allocate descriptor + // + Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate descriptor + // + return Status; + } + + // + // intialize descriptor + // + Descriptor->Flags = OHCI_ENDPOINT_SKIP; + Descriptor->HeadPhysicalDescriptor = 0; + Descriptor->NextPhysicalEndpoint = 0; + Descriptor->TailPhysicalDescriptor = 0; + Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +STDMETHODCALLTYPE +CUSBHardwareDevice::GetBulkHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + *OutDescriptor = m_BulkEndpointDescriptor; +} + +VOID +STDMETHODCALLTYPE +CUSBHardwareDevice::GetInterruptEndpointDescriptors( + struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor) +{ + *OutDescriptor = m_InterruptEndpoints; +} + +VOID +STDMETHODCALLTYPE +CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + *OutDescriptor = m_IsoEndpointDescriptor; +} + +VOID +STDMETHODCALLTYPE +CUSBHardwareDevice::HeadEndpointDescriptorModified( + ULONG Type) +{ + if (Type == USB_ENDPOINT_TYPE_CONTROL) + { + // + // notify controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), 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); + } +} + +VOID +STDMETHODCALLTYPE +CUSBHardwareDevice::GetControlHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + *OutDescriptor = m_ControlEndpointDescriptor; +} + +NTSTATUS +CUSBHardwareDevice::InitializeController() +{ + NTSTATUS Status; + ULONG Index, Interval, IntervalIndex, InsertIndex; + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + + // + // first allocate the hcca area + // + Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for control transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for bulk transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for iso transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32 + // + for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) + { + // + // allocate endpoint descriptor + // + Status = AllocateEndpointDescriptor(&Descriptor); + if (!NT_SUCCESS(Status)) { // - // controller completed reset - // - return STATUS_SUCCESS; + // no memory + // + return Status; } - } - - // - // failed to reset controller - // + + // + // save in array + // + m_InterruptEndpoints[Index] = Descriptor; + } + + + // + // now link the descriptors, taken from Haiku + // + Interval = OHCI_BIGGEST_INTERVAL; + IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1; + while (Interval > 1) + { + InsertIndex = Interval / 2; + while (InsertIndex < OHCI_BIGGEST_INTERVAL) + { + // + // assign endpoint address + // + m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart; + InsertIndex += Interval; + } + + IntervalIndex--; + Interval /= 2; + } + + // + // link all endpoint descriptors to first descriptor in array + // + m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; + for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) + { + // + // link descriptor + // + m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; + } + + // + // Now link the first endpoint to the isochronous endpoint + // + m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart; + + // + // set iso endpoint type + // + m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::StopController(void) +{ + ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; }