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
{
ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic;
- //
- // first write address of HCCA
- //
- WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
-
//
// lets write physical address of dummy control endpoint descriptor
//
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);
- ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
- DPRINT1("Control %x\n", Control);
+ 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));
- 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);
+ KeStallExecutionProcessor(100);
//
- // write frame interval
+ // is the controller started
//
- WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
+ Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
//
- // 90 % periodic
+ // assert that the controller has been started
//
- Periodic = OHCI_PERIODIC(m_IntervalValue);
- WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic);
- DPRINT1("Periodic Start %x\n", Periodic);
+ 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
//
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor);
-
-
//
// retrieve number of ports
//
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
//
ULONG Control, Reset, Status;
ULONG Index, FrameInterval;
+ //
+ // alignment check
+ //
+ 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
//
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;
if (Value & OHCI_RH_PORTSTATUS_PES)
*PortStatus |= USB_PORT_STATUS_ENABLE;
- // port enabled
+ // port disconnect or hardware error
if (Value & OHCI_RH_PORTSTATUS_PESC)
- *PortChange |= USB_PORT_STATUS_ENABLE;
+ *PortChange |= USB_PORT_STATUS_CONNECT;
// port suspend
if (Value & OHCI_RH_PORTSTATUS_PSS)
if (Value & OHCI_RH_PORTSTATUS_PSSC)
*PortChange |= USB_PORT_STATUS_ENABLE;
- // port reset
- if (Value & OHCI_RH_PORTSTATUS_PSS)
+ // 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
+ // 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;
}
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));
//
//
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));
+ }
+ if (Status == C_PORT_CONNECTION || Status == C_PORT_ENABLE)
+ {
//
- // port is enabled
+ // clear change bits
//
- m_PortStatus[PortId].PortStatus |= USB_PORT_STATUS_ENABLE;
+ 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
+ // wait for port to stabilize
//
- 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)
+ {
+ LARGE_INTEGER Timeout;
- }
+ //
+ // delay is 100 ms
+ //
+ Timeout.QuadPart = 100;
+ DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart);
- 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;
- }
+ //
+ // convert to 100 ns units (absolute)
+ //
+ Timeout.QuadPart *= -10000;
+ //
+ // perform the wait
+ //
+ KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
+ }
+ }
+ //
+ // re-enable root hub change
+ //
+ WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
return STATUS_SUCCESS;
}
}
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)
}
else if (Feature == PORT_RESET)
{
+ LARGE_INTEGER Timeout;
+
//
// assert
//
//
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
+ 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);
+ }while(TRUE);
+
//
- // wait
+ // delay is 10 ms
//
- KeStallExecutionProcessor(100);
-
+ Timeout.QuadPart = 10;
+ DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart);
+
+ //
+ // convert to 100 ns units (absolute)
//
- // update cached settings
+ Timeout.QuadPart *= -10000;
+
//
- m_PortStatus[PortId].PortChange |= USB_PORT_STATUS_RESET;
- m_PortStatus[PortId].PortStatus &= ~USB_PORT_STATUS_ENABLE;
+ // perform the wait
+ //
+ KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
//
// is there a status change callback
//
m_SCECallBack(m_SCEContext);
}
+ return STATUS_SUCCESS;
}
return STATUS_SUCCESS;
}
// the interrupt was not caused by DoneHead update
// check if something important happened
//
+ DPRINT1("InterruptStatus %x InterruptEnable %x\n", READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)),
+ READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET)));
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)
{
if (Status & OHCI_ROOT_HUB_STATUS_CHANGE)
{
- //
- // new device has arrived
- //
-
//
// disable interrupt as it will fire untill the port has been reset
//
// 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;
}
//