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:
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(){}
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
- EHCI_PORT_STATUS m_PortStatus[16]; // port status
-
- // set command
- VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
-
- // get command
- VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
+ 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);
PDEVICE_OBJECT PhysicalDeviceObject,
PDEVICE_OBJECT LowerDeviceObject)
{
- BUS_INTERFACE_STANDARD BusInterface;
PCI_COMMON_CONFIG PciConfig;
NTSTATUS Status;
ULONG BytesRead;
//
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,
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;
}
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++)
//
// 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);
}
//
//
// Initialize the UsbQueue now that we have an AdapterObject.
//
- Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, NULL);
+ Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to Initialize the UsbQueue\n");
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
- //
- EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, 0);
- EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, 0x0000001f);
-
//
// Assign the AsyncList Register
//
EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
//
- // Set Schedules to Enable and Interrupt Threshold to 1ms.
+ // get command register
//
GetCommandRegister(&UsbCmd);
- UsbCmd.PeriodicEnable = FALSE;
- UsbCmd.AsyncEnable = TRUE; //FIXME: Need USB Memory Manager
-
- UsbCmd.IntThreshold = 1;
- // FIXME: Set framelistsize when periodic is implemented.
- SetCommandRegister(&UsbCmd);
//
- // Enable Interrupts and start execution
+ // preserve bits
//
- 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.AsyncEnable = TRUE;
- UsbCmd.Run = TRUE;
+ //
+ // enable async
+ //
SetCommandRegister(&UsbCmd);
//
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
//
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");
return STATUS_DEVICE_NOT_CONNECTED;
}
+ ASSERT(PortStatus & EHCI_PRT_CONNECTED);
+
//
// Reset and clean enable
//
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
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;
}
OUT USHORT *PortStatus,
OUT USHORT *PortChange)
{
-#if 0
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
//
}
}
- // 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 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;
Status |= USB_PORT_STATUS_OVER_CURRENT;
// In a reset state?
- if (Value & EHCI_PRT_RESET)
+ if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
+ {
Status |= USB_PORT_STATUS_RESET;
+ Change |= USB_PORT_STATUS_RESET;
+ }
//
// FIXME: Is the Change here correct?
*PortStatus = Status;
*PortChange = Change;
-#else
- *PortStatus = m_PortStatus[PortId].PortStatus;
- *PortChange = m_PortStatus[PortId].PortChange;
-#endif
+
return STATUS_SUCCESS;
}
{
ULONG Value;
- DPRINT1("CUSBHardwareDevice::ClearPortStatus\n");
+ DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
if (PortId > m_Capabilities.HCSParams.PortCount)
return STATUS_UNSUCCESSFUL;
- Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
-
if (Status == C_PORT_RESET)
{
- if (Value & EHCI_PRT_RESET)
- {
- Value &= ~EHCI_PRT_RESET;
- EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
- KeStallExecutionProcessor(100);
-
- //
- // update port status
- //
- m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_RESET;
- m_PortStatus[PortId].PortStatus |= USB_PORT_STATUS_ENABLE;
- }
+ //
+ // update port status
+ //
+ m_ResetInProgress[PortId] = FALSE;
}
if (Status == C_PORT_CONNECTION)
{
- // FIXME: Make sure its the Connection and Enable Change status.
- Value |= EHCI_PRT_CONNECTSTATUSCHANGE;
- Value |= EHCI_PRT_ENABLEDSTATUSCHANGE;
+ 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);
- m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_CONNECT;
+ //
+ // 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;
{
ULONG Value;
- DPRINT1("CUSBHardwareDevice::SetPortFeature\n");
+ DPRINT("CUSBHardwareDevice::SetPortFeature\n");
if (PortId > m_Capabilities.HCSParams.PortCount)
return STATUS_UNSUCCESSFUL;
//
DPRINT1("PORT_ENABLE not supported for EHCI\n");
}
-
+
if (Feature == PORT_RESET)
{
- if (Value & EHCI_PRT_SLOWSPEEDLINE)
- {
- DPRINT1("Non HighSpeed device. Releasing Ownership\n");
- }
-
ResetPort(PortId);
//
// update cached settings
//
- m_PortStatus[PortId].PortChange |= USB_PORT_STATUS_RESET;
- m_PortStatus[PortId].PortStatus &= ~USB_PORT_STATUS_ENABLE;
+ m_ResetInProgress[PortId] = TRUE;
//
// is there a status change callback
m_SCECallBack(m_SCEContext);
}
}
-
+
if (Feature == PORT_POWER)
- DPRINT1("PORT_POWER Not implemented\n");
+ {
+ 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;
}
CUSBHardwareDevice::SetPeriodicListRegister(
ULONG PhysicalAddress)
{
- EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, PhysicalAddress);
+ //
+ // store physical address
+ //
+ m_SyncFramePhysAddr = PhysicalAddress;
}
struct _QUEUE_HEAD *
This = (CUSBHardwareDevice*) SystemArgument1;
CStatus = (ULONG) SystemArgument2;
+ DPRINT("CStatus %x\n", CStatus);
//
// check for completion of async schedule
//
// controller reported error
//
+ DPRINT1("CStatus %x\n", CStatus);
Status = STATUS_UNSUCCESSFUL;
PC_ASSERT(FALSE);
+ return;
}
//
//
if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
{
- //
- // Clear the port change status
- //
- //This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_CONNECTSTATUSCHANGE);
-
if (PortStatus & EHCI_PRT_CONNECTED)
{
DPRINT1("Device connected on port %d\n", i);
continue;
}
}
-
- //
- // update port status flags
- //
- This->m_PortStatus[i].PortStatus |= USB_PORT_STATUS_HIGH_SPEED;
- This->m_PortStatus[i].PortStatus |= USB_PORT_STATUS_CONNECT;
- This->m_PortStatus[i].PortChange |= USB_PORT_STATUS_CONNECT;
-
- //
- // is there a status change callback
- //
- if (This->m_SCECallBack != NULL)
- {
- //
- // issue callback
- //
- This->m_SCECallBack(This->m_SCEContext);
- }
}
else
{
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
//
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)