2 * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbehci/hcd_controller.cpp
5 * PURPOSE: USB EHCI device driver.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
16 typedef VOID __stdcall
HD_INIT_CALLBACK(IN PVOID CallBackContext
);
20 InterruptServiceRoutine(
21 IN PKINTERRUPT Interrupt
,
22 IN PVOID ServiceContext
);
28 IN PVOID DeferredContext
,
29 IN PVOID SystemArgument1
,
30 IN PVOID SystemArgument2
);
34 StatusChangeWorkItemRoutine(PVOID Context
);
36 class CUSBHardwareDevice
: public IEHCIHardwareDevice
39 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
41 STDMETHODIMP_(ULONG
) AddRef()
43 InterlockedIncrement(&m_Ref
);
46 STDMETHODIMP_(ULONG
) Release()
48 InterlockedDecrement(&m_Ref
);
58 IMP_IUSBHARDWAREDEVICE
62 BOOLEAN
InterruptService();
63 VOID
PrintCapabilities();
64 NTSTATUS
StartController();
65 NTSTATUS
StopController();
66 NTSTATUS
ResetController();
69 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
70 friend VOID NTAPI
EhciDeferredRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
71 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
72 // constructor / destructor
73 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
74 virtual ~CUSBHardwareDevice(){}
77 LONG m_Ref
; // reference count
78 PDRIVER_OBJECT m_DriverObject
; // driver object
79 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
80 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
81 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
82 KSPIN_LOCK m_Lock
; // hardware lock
83 PKINTERRUPT m_Interrupt
; // interrupt object
84 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
85 PVOID VirtualBase
; // virtual base for memory manager
86 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
87 PULONG m_Base
; // EHCI operational port base registers
88 PDMA_ADAPTER m_Adapter
; // dma adapter object
89 ULONG m_MapRegisters
; // map registers count
90 EHCI_CAPS m_Capabilities
; // EHCI caps
91 USHORT m_VendorID
; // vendor id
92 USHORT m_DeviceID
; // device id
93 PQUEUE_HEAD AsyncQueueHead
; // async queue head terminator
94 PEHCIQUEUE m_UsbQueue
; // usb request queue
95 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
96 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
97 PVOID m_SCEContext
; // status change callback routine context
98 BOOLEAN m_DoorBellRingInProgress
; // door bell ring in progress
99 WORK_QUEUE_ITEM m_StatusChangeWorkItem
; // work item for status change callback
100 volatile LONG m_StatusChangeWorkItemStatus
; // work item status
101 ULONG m_SyncFramePhysAddr
; // periodic frame list physical address
102 BUS_INTERFACE_STANDARD m_BusInterface
; // pci bus interface
103 BOOLEAN m_PortResetInProgress
[0xF]; // stores reset in progress (vbox hack)
106 ULONG
EHCI_READ_REGISTER_ULONG(ULONG Offset
);
109 VOID
EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
);
112 //=================================================================================================
117 CUSBHardwareDevice::QueryInterface(
121 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
123 *Output
= PVOID(PUNKNOWN(this));
124 PUNKNOWN(*Output
)->AddRef();
125 return STATUS_SUCCESS
;
128 return STATUS_UNSUCCESSFUL
;
133 CUSBHardwareDevice::GetUSBType()
140 CUSBHardwareDevice::Initialize(
141 PDRIVER_OBJECT DriverObject
,
142 PDEVICE_OBJECT FunctionalDeviceObject
,
143 PDEVICE_OBJECT PhysicalDeviceObject
,
144 PDEVICE_OBJECT LowerDeviceObject
)
146 PCI_COMMON_CONFIG PciConfig
;
150 DPRINT("CUSBHardwareDevice::Initialize\n");
153 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
155 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
156 if (!NT_SUCCESS(Status
))
158 DPRINT1("Failed to create DMAMemoryManager Object\n");
163 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
165 Status
= CreateUSBQueue((PUSBQUEUE
*)&m_UsbQueue
);
166 if (!NT_SUCCESS(Status
))
168 DPRINT1("Failed to create UsbQueue!\n");
173 // store device objects
175 m_DriverObject
= DriverObject
;
176 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
177 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
178 m_NextDeviceObject
= LowerDeviceObject
;
181 // initialize device lock
183 KeInitializeSpinLock(&m_Lock
);
186 // initialize status change work item
188 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
193 Status
= GetBusInterface(PhysicalDeviceObject
, &m_BusInterface
);
194 if (!NT_SUCCESS(Status
))
196 DPRINT1("Failed to get BusInterface!\n");
200 BytesRead
= (*m_BusInterface
.GetBusData
)(m_BusInterface
.Context
,
201 PCI_WHICHSPACE_CONFIG
,
204 PCI_COMMON_HDR_LENGTH
);
206 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
208 DPRINT1("Failed to get pci config information!\n");
209 return STATUS_SUCCESS
;
212 m_VendorID
= PciConfig
.VendorID
;
213 m_DeviceID
= PciConfig
.DeviceID
;
215 return STATUS_SUCCESS
;
220 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
223 Register
= (PULONG
)UsbCmd
;
224 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
), *Register
);
229 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
232 Register
= (PULONG
)UsbCmd
;
233 *Register
= READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
));
237 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset
)
239 return READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
));
243 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
)
245 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
), Value
);
249 CUSBHardwareDevice::PrintCapabilities()
251 if (m_Capabilities
.HCSParams
.PortPowerControl
)
253 DPRINT1("Controller EHCI has Port Power Control\n");
256 DPRINT1("Controller Port Routing Rules %lu\n", m_Capabilities
.HCSParams
.PortRouteRules
);
257 DPRINT1("Number of Ports per Companion Controller %lu\n", m_Capabilities
.HCSParams
.PortPerCHC
);
258 DPRINT1("Number of Companion Controller %lu\n", m_Capabilities
.HCSParams
.CHCCount
);
260 if (m_Capabilities
.HCSParams
.PortIndicator
)
262 DPRINT1("Controller has Port Indicators Support\n");
265 if (m_Capabilities
.HCSParams
.DbgPortNum
)
267 DPRINT1("Controller has Debug Port Support At Port %x\n", m_Capabilities
.HCSParams
.DbgPortNum
);
270 if (m_Capabilities
.HCCParams
.EECPCapable
)
272 DPRINT1("Controller has Extended Capabilities Support\n");
275 if (m_Capabilities
.HCCParams
.ParkMode
)
277 DPRINT1("Controller supports Asynchronous Schedule Park\n");
280 if (m_Capabilities
.HCCParams
.VarFrameList
)
282 DPRINT1("Controller supports Programmable Frame List Size\n");
285 if (m_Capabilities
.HCCParams
.CurAddrBits
)
287 DPRINT1("Controller uses 64-Bit Addressing\n");
293 CUSBHardwareDevice::PnpStart(
294 PCM_RESOURCE_LIST RawResources
,
295 PCM_RESOURCE_LIST TranslatedResources
)
298 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
299 DEVICE_DESCRIPTION DeviceDescription
;
300 PHYSICAL_ADDRESS AsyncPhysicalAddress
;
306 DPRINT("CUSBHardwareDevice::PnpStart\n");
307 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
310 // get resource descriptor
312 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
314 switch(ResourceDescriptor
->Type
)
316 case CmResourceTypeInterrupt
:
318 KeInitializeDpc(&m_IntDpcObject
,
322 Status
= IoConnectInterrupt(&m_Interrupt
,
323 InterruptServiceRoutine
,
326 ResourceDescriptor
->u
.Interrupt
.Vector
,
327 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
328 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
329 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
330 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
331 ResourceDescriptor
->u
.Interrupt
.Affinity
,
334 if (!NT_SUCCESS(Status
))
337 // failed to register interrupt
339 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
344 case CmResourceTypeMemory
:
349 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
353 // failed to map registers
355 DPRINT1("MmMapIoSpace failed\n");
356 return STATUS_INSUFFICIENT_RESOURCES
;
360 // Get controllers capabilities
362 m_Capabilities
.Length
= READ_REGISTER_UCHAR((PUCHAR
)ResourceBase
+ EHCI_CAPLENGTH
);
363 m_Capabilities
.HCIVersion
= READ_REGISTER_USHORT((PUSHORT
)((ULONG_PTR
)ResourceBase
+ EHCI_HCIVERSION
));
364 m_Capabilities
.HCSParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG_PTR
)ResourceBase
+ EHCI_HCSPARAMS
));
365 m_Capabilities
.HCCParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG_PTR
)ResourceBase
+ EHCI_HCCPARAMS
));
367 DPRINT1("Controller Capabilities Length 0x%x\n", m_Capabilities
.Length
);
368 DPRINT1("Controller EHCI Version 0x%x\n", m_Capabilities
.HCIVersion
);
369 DPRINT1("Controller EHCI Caps HCSParamsLong 0x%lx\n", m_Capabilities
.HCSParamsLong
);
370 DPRINT1("Controller EHCI Caps HCCParamsLong 0x%lx\n", m_Capabilities
.HCCParamsLong
);
371 DPRINT1("Controller has %lu Ports\n", m_Capabilities
.HCSParams
.PortCount
);
374 // print capabilities
378 if (m_Capabilities
.HCSParams
.PortRouteRules
)
381 PortCount
= max(m_Capabilities
.HCSParams
.PortCount
/2, (m_Capabilities
.HCSParams
.PortCount
+1)/2);
385 // each entry is a 4 bit field EHCI 2.2.5
387 Value
= READ_REGISTER_UCHAR((PUCHAR
)(ULONG
)ResourceBase
+ EHCI_HCSP_PORTROUTE
+ Count
);
388 m_Capabilities
.PortRoute
[Count
*2] = (Value
& 0xF0);
390 if ((Count
*2) + 1 < m_Capabilities
.HCSParams
.PortCount
)
391 m_Capabilities
.PortRoute
[(Count
*2)+1] = (Value
& 0x0F);
394 } while(Count
< PortCount
);
398 // Set m_Base to the address of Operational Register Space
400 m_Base
= (PULONG
)((ULONG
)ResourceBase
+ m_Capabilities
.Length
);
408 // zero device description
410 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
413 // initialize device description
415 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
416 DeviceDescription
.Master
= TRUE
;
417 DeviceDescription
.ScatterGather
= TRUE
;
418 DeviceDescription
.Dma32BitAddresses
= TRUE
;
419 DeviceDescription
.DmaWidth
= Width32Bits
;
420 DeviceDescription
.InterfaceType
= PCIBus
;
421 DeviceDescription
.MaximumLength
= MAXULONG
;
426 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
430 // failed to get dma adapter
432 DPRINT1("Failed to acquire dma adapter\n");
433 return STATUS_INSUFFICIENT_RESOURCES
;
437 // Create Common Buffer
439 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
445 DPRINT1("Failed to allocate a common buffer\n");
446 return STATUS_INSUFFICIENT_RESOURCES
;
450 // Initialize the DMAMemoryManager
452 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
453 if (!NT_SUCCESS(Status
))
455 DPRINT1("Failed to initialize the DMAMemoryManager\n");
460 // Create a queuehead for the Async Register
462 m_MemoryManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&AsyncQueueHead
, &AsyncPhysicalAddress
);
464 AsyncQueueHead
->PhysicalAddr
= AsyncPhysicalAddress
.LowPart
;
465 AsyncQueueHead
->HorizontalLinkPointer
= AsyncQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
466 AsyncQueueHead
->EndPointCharacteristics
.HeadOfReclamation
= TRUE
;
467 AsyncQueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
468 AsyncQueueHead
->Token
.Bits
.Halted
= TRUE
;
470 AsyncQueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x01;
471 AsyncQueueHead
->NextPointer
= TERMINATE_POINTER
;
472 AsyncQueueHead
->CurrentLinkPointer
= TERMINATE_POINTER
;
474 InitializeListHead(&AsyncQueueHead
->LinkedQueueHeads
);
477 // Initialize the UsbQueue now that we have an AdapterObject.
479 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, &m_Lock
);
480 if (!NT_SUCCESS(Status
))
482 DPRINT1("Failed to Initialize the UsbQueue\n");
487 // Start the controller
489 DPRINT1("Starting Controller\n");
490 Status
= StartController();
500 CUSBHardwareDevice::PnpStop(void)
503 return STATUS_NOT_IMPLEMENTED
;
508 CUSBHardwareDevice::GetDeviceDetails(
509 OUT OPTIONAL PUSHORT VendorId
,
510 OUT OPTIONAL PUSHORT DeviceId
,
511 OUT OPTIONAL PULONG NumberOfPorts
,
512 OUT OPTIONAL PULONG Speed
)
515 *VendorId
= m_VendorID
;
517 *DeviceId
= m_DeviceID
;
519 *NumberOfPorts
= m_Capabilities
.HCSParams
.PortCount
;
520 //FIXME: What to returned here?
523 return STATUS_SUCCESS
;
528 CUSBHardwareDevice::GetDMA(
529 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
531 if (!m_MemoryManager
)
532 return STATUS_UNSUCCESSFUL
;
533 *OutDMAMemoryManager
= m_MemoryManager
;
534 return STATUS_SUCCESS
;
539 CUSBHardwareDevice::GetUSBQueue(
540 OUT
struct IUSBQueue
**OutUsbQueue
)
543 return STATUS_UNSUCCESSFUL
;
544 *OutUsbQueue
= m_UsbQueue
;
545 return STATUS_SUCCESS
;
550 CUSBHardwareDevice::StartController(void)
552 EHCI_USBCMD_CONTENT UsbCmd
;
553 ULONG UsbSts
, FailSafe
, ExtendedCapsSupport
, Caps
, Index
;
555 LARGE_INTEGER Timeout
;
558 // are extended caps supported
560 ExtendedCapsSupport
= (m_Capabilities
.HCCParamsLong
>> EHCI_ECP_SHIFT
) & EHCI_ECP_MASK
;
561 if (ExtendedCapsSupport
)
563 DPRINT1("[EHCI] Extended Caps Support detected!\n");
568 ASSERT(ExtendedCapsSupport
>= PCI_COMMON_HDR_LENGTH
);
569 m_BusInterface
.GetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
, sizeof(ULONG
));
572 // OS Handoff Synchronization support capability. EHCI 5.1
574 if ((Caps
& EHCI_LEGSUP_CAPID_MASK
) == EHCI_LEGSUP_CAPID
)
579 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
581 DPRINT1("[EHCI] Controller is BIOS owned, acquiring control\n");
587 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, ExtendedCapsSupport
+3, sizeof(UCHAR
));
589 for(Index
= 0; Index
< 20; Index
++)
594 m_BusInterface
.GetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
, sizeof(ULONG
));
595 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
600 Timeout
.QuadPart
= 50;
601 DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout
.LowPart
);
604 // convert to 100 ns units (absolute)
606 Timeout
.QuadPart
*= -10000;
611 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
614 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
617 // failed to acquire ownership
619 DPRINT1("[EHCI] failed to acquire ownership\n");
621 else if ((Caps
& EHCI_LEGSUP_OSOWNED
))
624 // HC OS Owned Semaphore EHCI 2.1.7
626 DPRINT1("[EHCI] acquired ownership\n");
630 // explicitly clear the bios owned flag 2.1.7
633 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, ExtendedCapsSupport
+2, sizeof(UCHAR
));
636 // clear SMI interrupt EHCI 2.1.8
639 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
+4, sizeof(ULONG
));
646 // get command register
648 GetCommandRegister(&UsbCmd
);
651 // disable running schedules
653 UsbCmd
.PeriodicEnable
= FALSE
;
654 UsbCmd
.AsyncEnable
= FALSE
;
655 SetCommandRegister(&UsbCmd
);
658 // Wait for execution to start
660 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
662 KeStallExecutionProcessor(100);
663 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
665 if (!(UsbSts
& EHCI_STS_PSS
) && (UsbSts
& EHCI_STS_ASS
))
671 if ((UsbSts
& (EHCI_STS_PSS
| EHCI_STS_ASS
)))
673 DPRINT1("Failed to stop running schedules %x\n", UsbSts
);
679 // Stop the controller if its running
681 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
682 if (!(UsbSts
& EHCI_STS_HALT
))
684 DPRINT1("Stopping Controller %x\n", UsbSts
);
689 // Reset the controller
696 if (m_Capabilities
.HCCParams
.CurAddrBits
)
699 // disable 64-bit addressing
701 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT
, 0x0);
705 // Enable Interrupts and start execution
707 ULONG Mask
= EHCI_USBINTR_INTE
| EHCI_USBINTR_ERR
| EHCI_USBINTR_ASYNC
| EHCI_USBINTR_HSERR
| EHCI_USBINTR_PC
;
708 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, Mask
);
710 KeStallExecutionProcessor(10);
712 ULONG Status
= EHCI_READ_REGISTER_ULONG(EHCI_USBINTR
);
714 DPRINT1("Interrupt Mask %x\n", Status
);
715 ASSERT((Status
& Mask
) == Mask
);
718 // Assign the SyncList Register
720 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE
, m_SyncFramePhysAddr
);
723 // Set Schedules to Enable and Interrupt Threshold to 1ms.
725 RtlZeroMemory(&UsbCmd
, sizeof(EHCI_USBCMD_CONTENT
));
727 UsbCmd
.PeriodicEnable
= TRUE
;
728 UsbCmd
.IntThreshold
= 0x8; //1ms
730 UsbCmd
.FrameListSize
= 0x0; //1024
732 if (m_Capabilities
.HCCParams
.ParkMode
)
735 // enable async park mode
737 UsbCmd
.AsyncParkEnable
= TRUE
;
738 UsbCmd
.AsyncParkCount
= 3;
741 SetCommandRegister(&UsbCmd
);
745 // Wait for execution to start
747 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
749 KeStallExecutionProcessor(100);
750 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
752 if (!(UsbSts
& EHCI_STS_HALT
) && (UsbSts
& EHCI_STS_PSS
))
758 if (UsbSts
& EHCI_STS_HALT
)
760 DPRINT1("Could not start execution on the controller\n");
762 return STATUS_UNSUCCESSFUL
;
765 if (!(UsbSts
& EHCI_STS_PSS
))
767 DPRINT1("Could not enable periodic scheduling\n");
769 return STATUS_UNSUCCESSFUL
;
773 // Assign the AsyncList Register
775 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, AsyncQueueHead
->PhysicalAddr
);
778 // get command register
780 GetCommandRegister(&UsbCmd
);
785 UsbCmd
.AsyncEnable
= TRUE
;
790 SetCommandRegister(&UsbCmd
);
793 // Wait for execution to start
795 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
797 KeStallExecutionProcessor(100);
798 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
800 if ((UsbSts
& EHCI_STS_ASS
))
806 if (!(UsbSts
& EHCI_STS_ASS
))
808 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts
);
810 return STATUS_UNSUCCESSFUL
;
813 DPRINT1("UsbSts %x\n", UsbSts
);
814 GetCommandRegister(&UsbCmd
);
816 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd
.PeriodicEnable
);
817 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd
.AsyncEnable
);
818 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd
.IntThreshold
);
819 DPRINT1("UsbCmd.Run %x\n", UsbCmd
.Run
);
820 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd
.FrameListSize
);
823 // Set port routing to EHCI controller
825 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG
, 1);
827 DPRINT1("EHCI Started!\n");
828 return STATUS_SUCCESS
;
832 CUSBHardwareDevice::StopController(void)
834 EHCI_USBCMD_CONTENT UsbCmd
;
835 ULONG UsbSts
, FailSafe
;
838 // Disable Interrupts and stop execution
840 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR
, 0);
842 GetCommandRegister(&UsbCmd
);
844 SetCommandRegister(&UsbCmd
);
846 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
848 KeStallExecutionProcessor(10);
849 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
850 if (UsbSts
& EHCI_STS_HALT
)
856 if (!(UsbSts
& EHCI_STS_HALT
))
858 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
859 return STATUS_UNSUCCESSFUL
;
862 return STATUS_SUCCESS
;
866 CUSBHardwareDevice::ResetController(void)
868 EHCI_USBCMD_CONTENT UsbCmd
;
871 GetCommandRegister(&UsbCmd
);
872 UsbCmd
.HCReset
= TRUE
;
873 SetCommandRegister(&UsbCmd
);
875 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
877 KeStallExecutionProcessor(100);
878 GetCommandRegister(&UsbCmd
);
885 DPRINT1("EHCI ERROR: Controller is not responding to reset request!\n");
886 return STATUS_UNSUCCESSFUL
;
889 return STATUS_SUCCESS
;
894 CUSBHardwareDevice::ResetPort(
898 LARGE_INTEGER Timeout
;
900 if (PortIndex
> m_Capabilities
.HCSParams
.PortCount
)
901 return STATUS_UNSUCCESSFUL
;
903 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
905 ASSERT(!EHCI_IS_LOW_SPEED(PortStatus
));
906 ASSERT(PortStatus
& EHCI_PRT_CONNECTED
);
909 // Reset and clean enable
911 PortStatus
|= EHCI_PRT_RESET
;
912 PortStatus
&= EHCI_PORTSC_DATAMASK
;
913 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
916 // delay is 50 ms for port reset as per USB 2.0 spec
918 Timeout
.QuadPart
= 50;
919 DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout
.LowPart
);
922 // convert to 100 ns units (absolute)
924 Timeout
.QuadPart
*= -10000;
929 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
931 return STATUS_SUCCESS
;
936 CUSBHardwareDevice::GetPortStatus(
938 OUT USHORT
*PortStatus
,
939 OUT USHORT
*PortChange
)
942 USHORT Status
= 0, Change
= 0;
944 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
945 return STATUS_UNSUCCESSFUL
;
948 // Get the value of the Port Status and Control Register
950 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
953 // If the PowerPortControl is 0 then host controller does not have power control switches
954 if (!m_Capabilities
.HCSParams
.PortPowerControl
)
956 Status
|= USB_PORT_STATUS_POWER
;
960 // Check the value of PortPower
961 if (Value
& EHCI_PRT_POWER
)
963 Status
|= USB_PORT_STATUS_POWER
;
967 // Get Connected Status
968 if (Value
& EHCI_PRT_CONNECTED
)
970 Status
|= USB_PORT_STATUS_CONNECT
;
972 // EHCI only supports high speed
973 Status
|= USB_PORT_STATUS_HIGH_SPEED
;
976 // Get Enabled Status
977 if (Value
& EHCI_PRT_ENABLED
)
978 Status
|= USB_PORT_STATUS_ENABLE
;
981 if (Value
& EHCI_PRT_SUSPEND
)
982 Status
|= USB_PORT_STATUS_SUSPEND
;
984 // a overcurrent is active?
985 if (Value
& EHCI_PRT_OVERCURRENTACTIVE
)
986 Status
|= USB_PORT_STATUS_OVER_CURRENT
;
989 if ((Value
& EHCI_PRT_RESET
) || m_PortResetInProgress
[PortId
])
991 Status
|= USB_PORT_STATUS_RESET
;
992 Change
|= USB_PORT_STATUS_RESET
;
995 // This indicates a connect or disconnect
996 if (Value
& EHCI_PRT_CONNECTSTATUSCHANGE
)
997 Change
|= USB_PORT_STATUS_CONNECT
;
999 // This is set to indicate a critical port error
1000 if (Value
& EHCI_PRT_ENABLEDSTATUSCHANGE
)
1001 Change
|= USB_PORT_STATUS_ENABLE
;
1003 *PortStatus
= Status
;
1004 *PortChange
= Change
;
1006 return STATUS_SUCCESS
;
1011 CUSBHardwareDevice::ClearPortStatus(
1016 LARGE_INTEGER Timeout
;
1018 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
1020 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
1021 return STATUS_UNSUCCESSFUL
;
1023 if (Status
== C_PORT_RESET
)
1026 m_PortResetInProgress
[PortId
] = FALSE
;
1029 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1030 Value
&= (EHCI_PORTSC_DATAMASK
| EHCI_PRT_ENABLED
);
1031 Value
&= ~EHCI_PRT_RESET
;
1032 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
1035 // wait for reset bit to clear
1039 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1041 if (!(Value
& EHCI_PRT_RESET
))
1044 KeStallExecutionProcessor(20);
1050 Timeout
.QuadPart
= 50;
1051 DPRINT1("Waiting %lu milliseconds for port to recover after reset\n", Timeout
.LowPart
);
1054 // convert to 100 ns units (absolute)
1056 Timeout
.QuadPart
*= -10000;
1061 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1064 // check the port status after reset
1066 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1067 if (!(Value
& EHCI_PRT_CONNECTED
))
1069 DPRINT1("No device is here after reset. Bad controller/device?\n");
1070 return STATUS_UNSUCCESSFUL
;
1072 else if (EHCI_IS_LOW_SPEED(Value
))
1074 DPRINT1("Low speed device connected. Releasing ownership\n");
1075 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
| EHCI_PRT_RELEASEOWNERSHIP
);
1076 return STATUS_DEVICE_NOT_CONNECTED
;
1078 else if (!(Value
& EHCI_PRT_ENABLED
))
1080 DPRINT1("Full speed device connected. Releasing ownership\n");
1081 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
| EHCI_PRT_RELEASEOWNERSHIP
);
1082 return STATUS_DEVICE_NOT_CONNECTED
;
1086 DPRINT1("High speed device connected\n");
1087 return STATUS_SUCCESS
;
1090 else if (Status
== C_PORT_CONNECTION
)
1093 // reset status change bits
1095 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1096 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
1098 if (Value
& EHCI_PRT_CONNECTED
)
1103 Timeout
.QuadPart
= 100;
1104 DPRINT1("Waiting %lu milliseconds for port to stabilize after connection\n", Timeout
.LowPart
);
1107 // convert to 100 ns units (absolute)
1109 Timeout
.QuadPart
*= -10000;
1114 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1118 return STATUS_SUCCESS
;
1124 CUSBHardwareDevice::SetPortFeature(
1128 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
1130 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
1131 return STATUS_UNSUCCESSFUL
;
1133 if (Feature
== PORT_ENABLE
)
1136 // FIXME: EHCI Ports can only be disabled via reset
1138 DPRINT1("PORT_ENABLE not supported for EHCI\n");
1141 if (Feature
== PORT_RESET
)
1148 // reset in progress
1149 m_PortResetInProgress
[PortId
] = TRUE
;
1152 // is there a status change callback
1154 if (m_SCECallBack
!= NULL
)
1159 m_SCECallBack(m_SCEContext
);
1163 if (Feature
== PORT_POWER
)
1165 if (m_Capabilities
.HCSParams
.PortPowerControl
)
1168 LARGE_INTEGER Timeout
;
1171 // enable port power
1173 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
)) | EHCI_PRT_POWER
;
1174 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
1179 Timeout
.QuadPart
= 20;
1180 DPRINT1("Waiting %lu milliseconds for port power up\n", Timeout
.LowPart
);
1183 // convert to 100 ns units (absolute)
1185 Timeout
.QuadPart
*= -10000;
1190 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1193 return STATUS_SUCCESS
;
1198 CUSBHardwareDevice::SetAsyncListRegister(
1199 ULONG PhysicalAddress
)
1201 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, PhysicalAddress
);
1206 CUSBHardwareDevice::SetPeriodicListRegister(
1207 ULONG PhysicalAddress
)
1210 // store physical address
1212 m_SyncFramePhysAddr
= PhysicalAddress
;
1215 struct _QUEUE_HEAD
*
1217 CUSBHardwareDevice::GetAsyncListQueueHead()
1219 return AsyncQueueHead
;
1224 CUSBHardwareDevice::GetPeriodicListRegister()
1232 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1236 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1237 m_SCEContext
= Context
;
1242 InterruptServiceRoutine(
1243 IN PKINTERRUPT Interrupt
,
1244 IN PVOID ServiceContext
)
1246 CUSBHardwareDevice
*This
;
1249 This
= (CUSBHardwareDevice
*) ServiceContext
;
1250 CStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
1252 CStatus
&= (EHCI_ERROR_INT
| EHCI_STS_INT
| EHCI_STS_IAA
| EHCI_STS_PCD
| EHCI_STS_FLR
);
1253 DPRINT("InterruptServiceRoutine CStatus %lx\n", CStatus
);
1256 // Check that it belongs to EHCI
1264 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, CStatus
);
1266 if (CStatus
& EHCI_STS_FATAL
)
1268 This
->StopController();
1269 DPRINT1("EHCI: Host System Error!\n");
1273 if (CStatus
& EHCI_ERROR_INT
)
1275 DPRINT1("EHCI Status = 0x%x\n", CStatus
);
1278 if (CStatus
& EHCI_STS_HALT
)
1280 DPRINT1("Host Error Unexpected Halt\n");
1281 // FIXME: Reset controller\n");
1285 KeInsertQueueDpc(&This
->m_IntDpcObject
, This
, (PVOID
)CStatus
);
1290 EhciDeferredRoutine(
1292 IN PVOID DeferredContext
,
1293 IN PVOID SystemArgument1
,
1294 IN PVOID SystemArgument2
)
1296 CUSBHardwareDevice
*This
;
1297 ULONG CStatus
, PortStatus
, PortCount
, i
, ShouldRingDoorBell
, QueueSCEWorkItem
;
1298 NTSTATUS Status
= STATUS_SUCCESS
;
1299 EHCI_USBCMD_CONTENT UsbCmd
;
1301 This
= (CUSBHardwareDevice
*) SystemArgument1
;
1302 CStatus
= (ULONG
) SystemArgument2
;
1304 DPRINT("EhciDeferredRoutine CStatus %lx\n", CStatus
);
1307 // check for completion of async schedule
1309 if (CStatus
& (EHCI_STS_RECL
| EHCI_STS_INT
| EHCI_ERROR_INT
))
1312 // check if there is a door bell ring in progress
1314 if (This
->m_DoorBellRingInProgress
== FALSE
)
1316 if (CStatus
& EHCI_ERROR_INT
)
1319 // controller reported error
1321 DPRINT1("CStatus %lx\n", CStatus
);
1326 // inform IUSBQueue of a completed queue head
1328 This
->m_UsbQueue
->InterruptCallback(Status
, &ShouldRingDoorBell
);
1331 // was a queue head completed?
1333 if (ShouldRingDoorBell
)
1336 // set door ring bell in progress status flag
1338 This
->m_DoorBellRingInProgress
= TRUE
;
1341 // get command register
1343 This
->GetCommandRegister(&UsbCmd
);
1346 // set door rang bell bit
1348 UsbCmd
.DoorBell
= TRUE
;
1351 // update command status
1353 This
->SetCommandRegister(&UsbCmd
);
1359 // check if the controller has acknowledged the door bell
1361 if (CStatus
& EHCI_STS_IAA
)
1364 // controller has acknowledged, assert we rang the bell
1366 PC_ASSERT(This
->m_DoorBellRingInProgress
== TRUE
);
1369 // now notify IUSBQueue that it can free completed requests
1371 This
->m_UsbQueue
->CompleteAsyncRequests();
1374 // door ring bell completed
1376 This
->m_DoorBellRingInProgress
= FALSE
;
1379 This
->GetDeviceDetails(NULL
, NULL
, &PortCount
, NULL
);
1380 if (CStatus
& EHCI_STS_PCD
)
1382 QueueSCEWorkItem
= FALSE
;
1383 for (i
= 0; i
< PortCount
; i
++)
1385 PortStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
));
1388 // Device connected or removed
1390 if (PortStatus
& EHCI_PRT_CONNECTSTATUSCHANGE
)
1392 if (PortStatus
& EHCI_PRT_CONNECTED
)
1394 DPRINT1("Device connected on port %lu\n", i
);
1396 if (This
->m_Capabilities
.HCSParams
.CHCCount
)
1398 if (PortStatus
& EHCI_PRT_ENABLED
)
1400 DPRINT1("Misbehaving controller. Port should be disabled at this point\n");
1403 if (EHCI_IS_LOW_SPEED(PortStatus
))
1405 DPRINT1("Low speed device connected. Releasing ownership\n");
1406 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
), PortStatus
| EHCI_PRT_RELEASEOWNERSHIP
);
1414 QueueSCEWorkItem
= TRUE
;
1418 DPRINT1("Device disconnected on port %lu\n", i
);
1423 QueueSCEWorkItem
= TRUE
;
1429 // is there a status change callback and a high speed device connected / disconnected
1431 if (QueueSCEWorkItem
&& This
->m_SCECallBack
!= NULL
)
1433 if (InterlockedCompareExchange(&This
->m_StatusChangeWorkItemStatus
, 1, 0) == 0)
1436 // queue work item for processing
1438 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1447 StatusChangeWorkItemRoutine(
1451 // cast to hardware object
1453 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1456 // is there a callback
1458 if (This
->m_SCECallBack
)
1463 This
->m_SCECallBack(This
->m_SCEContext
);
1467 // reset active status
1469 InterlockedDecrement(&This
->m_StatusChangeWorkItemStatus
);
1475 PUSBHARDWAREDEVICE
*OutHardware
)
1477 PUSBHARDWAREDEVICE This
;
1479 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBHardwareDevice(0);
1482 return STATUS_INSUFFICIENT_RESOURCES
;
1487 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1489 return STATUS_SUCCESS
;