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)
15 typedef VOID __stdcall
HD_INIT_CALLBACK(IN PVOID CallBackContext
);
19 InterruptServiceRoutine(
20 IN PKINTERRUPT Interrupt
,
21 IN PVOID ServiceContext
);
27 IN PVOID DeferredContext
,
28 IN PVOID SystemArgument1
,
29 IN PVOID SystemArgument2
);
33 StatusChangeWorkItemRoutine(PVOID Context
);
35 class CUSBHardwareDevice
: public IUSBHardwareDevice
38 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
40 STDMETHODIMP_(ULONG
) AddRef()
42 InterlockedIncrement(&m_Ref
);
45 STDMETHODIMP_(ULONG
) Release()
47 InterlockedDecrement(&m_Ref
);
57 NTSTATUS
Initialize(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT FunctionalDeviceObject
, PDEVICE_OBJECT PhysicalDeviceObject
, PDEVICE_OBJECT LowerDeviceObject
);
58 NTSTATUS
PnpStart(PCM_RESOURCE_LIST RawResources
, PCM_RESOURCE_LIST TranslatedResources
);
59 NTSTATUS
PnpStop(void);
60 NTSTATUS
HandlePower(PIRP Irp
);
61 NTSTATUS
GetDeviceDetails(PUSHORT VendorId
, PUSHORT DeviceId
, PULONG NumberOfPorts
, PULONG Speed
);
62 NTSTATUS
GetDMA(OUT
struct IDMAMemoryManager
**m_DmaManager
);
63 NTSTATUS
GetUSBQueue(OUT
struct IUSBQueue
**OutUsbQueue
);
65 NTSTATUS
StartController();
66 NTSTATUS
StopController();
67 NTSTATUS
ResetController();
68 NTSTATUS
ResetPort(ULONG PortIndex
);
70 NTSTATUS
GetPortStatus(ULONG PortId
, OUT USHORT
*PortStatus
, OUT USHORT
*PortChange
);
71 NTSTATUS
ClearPortStatus(ULONG PortId
, ULONG Status
);
72 NTSTATUS
SetPortFeature(ULONG PortId
, ULONG Feature
);
74 VOID
SetAsyncListRegister(ULONG PhysicalAddress
);
75 VOID
SetPeriodicListRegister(ULONG PhysicalAddress
);
76 struct _QUEUE_HEAD
* GetAsyncListQueueHead();
77 ULONG
GetPeriodicListRegister();
79 VOID
SetStatusChangeEndpointCallBack(PVOID CallBack
, PVOID Context
);
81 KIRQL
AcquireDeviceLock(void);
82 VOID
ReleaseDeviceLock(KIRQL OldLevel
);
84 VOID
SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
87 VOID
GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
91 BOOLEAN
InterruptService();
94 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
95 friend VOID NTAPI
EhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
96 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
97 // constructor / destructor
98 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
99 virtual ~CUSBHardwareDevice(){}
102 LONG m_Ref
; // reference count
103 PDRIVER_OBJECT m_DriverObject
; // driver object
104 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
105 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
106 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
107 KSPIN_LOCK m_Lock
; // hardware lock
108 PKINTERRUPT m_Interrupt
; // interrupt object
109 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
110 PVOID VirtualBase
; // virtual base for memory manager
111 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
112 PULONG m_Base
; // EHCI operational port base registers
113 PDMA_ADAPTER m_Adapter
; // dma adapter object
114 ULONG m_MapRegisters
; // map registers count
115 EHCI_CAPS m_Capabilities
; // EHCI caps
116 USHORT m_VendorID
; // vendor id
117 USHORT m_DeviceID
; // device id
118 PQUEUE_HEAD AsyncQueueHead
; // async queue head terminator
119 PUSBQUEUE m_UsbQueue
; // usb request queue
120 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
121 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
122 PVOID m_SCEContext
; // status change callback routine context
123 BOOLEAN m_DoorBellRingInProgress
; // door bell ring in progress
124 WORK_QUEUE_ITEM m_StatusChangeWorkItem
; // work item for status change callback
125 ULONG m_SyncFramePhysAddr
; // periodic frame list physical address
126 BOOLEAN m_ResetInProgress
[16]; // set when a reset is in progress
127 BUS_INTERFACE_STANDARD m_BusInterface
; // pci bus interface
130 ULONG
EHCI_READ_REGISTER_ULONG(ULONG Offset
);
133 VOID
EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
);
136 //=================================================================================================
141 CUSBHardwareDevice::QueryInterface(
145 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
147 *Output
= PVOID(PUNKNOWN(this));
148 PUNKNOWN(*Output
)->AddRef();
149 return STATUS_SUCCESS
;
152 return STATUS_UNSUCCESSFUL
;
156 CUSBHardwareDevice::Initialize(
157 PDRIVER_OBJECT DriverObject
,
158 PDEVICE_OBJECT FunctionalDeviceObject
,
159 PDEVICE_OBJECT PhysicalDeviceObject
,
160 PDEVICE_OBJECT LowerDeviceObject
)
162 PCI_COMMON_CONFIG PciConfig
;
166 DPRINT1("CUSBHardwareDevice::Initialize\n");
169 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
171 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
172 if (!NT_SUCCESS(Status
))
174 DPRINT1("Failed to create DMAMemoryManager Object\n");
179 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
181 Status
= CreateUSBQueue(&m_UsbQueue
);
182 if (!NT_SUCCESS(Status
))
184 DPRINT1("Failed to create UsbQueue!\n");
189 // store device objects
191 m_DriverObject
= DriverObject
;
192 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
193 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
194 m_NextDeviceObject
= LowerDeviceObject
;
197 // initialize device lock
199 KeInitializeSpinLock(&m_Lock
);
202 // intialize status change work item
204 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
209 Status
= GetBusInterface(PhysicalDeviceObject
, &m_BusInterface
);
210 if (!NT_SUCCESS(Status
))
212 DPRINT1("Failed to get BusInteface!\n");
216 BytesRead
= (*m_BusInterface
.GetBusData
)(m_BusInterface
.Context
,
217 PCI_WHICHSPACE_CONFIG
,
220 PCI_COMMON_HDR_LENGTH
);
222 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
224 DPRINT1("Failed to get pci config information!\n");
225 return STATUS_SUCCESS
;
228 m_VendorID
= PciConfig
.VendorID
;
229 m_DeviceID
= PciConfig
.DeviceID
;
232 if (PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
)
237 return STATUS_SUCCESS
;
240 DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n");
242 PciConfig
.Command
|= PCI_ENABLE_BUS_MASTER
;
243 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &PciConfig
, 0, PCI_COMMON_HDR_LENGTH
);
245 BytesRead
= (*m_BusInterface
.GetBusData
)(m_BusInterface
.Context
,
246 PCI_WHICHSPACE_CONFIG
,
249 PCI_COMMON_HDR_LENGTH
);
251 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
253 DPRINT1("Failed to get pci config information!\n");
255 return STATUS_SUCCESS
;
258 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
260 PciConfig
.Command
|= PCI_ENABLE_BUS_MASTER
;
261 DPRINT1("Failed to enable master\n");
262 return STATUS_UNSUCCESSFUL
;
264 return STATUS_SUCCESS
;
268 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
271 Register
= (PULONG
)UsbCmd
;
272 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
), *Register
);
276 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
279 Register
= (PULONG
)UsbCmd
;
280 *Register
= READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
));
284 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset
)
286 return READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
));
290 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
)
292 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
), Value
);
296 CUSBHardwareDevice::PnpStart(
297 PCM_RESOURCE_LIST RawResources
,
298 PCM_RESOURCE_LIST TranslatedResources
)
301 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
302 DEVICE_DESCRIPTION DeviceDescription
;
303 PHYSICAL_ADDRESS AsyncPhysicalAddress
;
309 DPRINT1("CUSBHardwareDevice::PnpStart\n");
310 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
313 // get resource descriptor
315 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
317 switch(ResourceDescriptor
->Type
)
319 case CmResourceTypeInterrupt
:
321 KeInitializeDpc(&m_IntDpcObject
,
325 Status
= IoConnectInterrupt(&m_Interrupt
,
326 InterruptServiceRoutine
,
329 ResourceDescriptor
->u
.Interrupt
.Vector
,
330 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
331 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
332 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
333 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
334 ResourceDescriptor
->u
.Interrupt
.Affinity
,
337 if (!NT_SUCCESS(Status
))
340 // failed to register interrupt
342 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
347 case CmResourceTypeMemory
:
352 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
356 // failed to map registers
358 DPRINT1("MmMapIoSpace failed\n");
359 return STATUS_INSUFFICIENT_RESOURCES
;
363 // Get controllers capabilities
365 m_Capabilities
.Length
= READ_REGISTER_UCHAR((PUCHAR
)ResourceBase
+ EHCI_CAPLENGTH
);
366 m_Capabilities
.HCIVersion
= READ_REGISTER_USHORT((PUSHORT
)((ULONG
)ResourceBase
+ EHCI_HCIVERSION
));
367 m_Capabilities
.HCSParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ EHCI_HCSPARAMS
));
368 m_Capabilities
.HCCParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ EHCI_HCCPARAMS
));
370 DPRINT1("Controller has %d Length\n", m_Capabilities
.Length
);
371 DPRINT1("Controller has %d Ports\n", m_Capabilities
.HCSParams
.PortCount
);
372 DPRINT1("Controller EHCI Version %x\n", m_Capabilities
.HCIVersion
);
373 DPRINT1("Controler EHCI Caps HCSParamsLong %x\n", m_Capabilities
.HCSParamsLong
);
374 DPRINT1("Controler EHCI Caps HCCParamsLong %x\n", m_Capabilities
.HCCParamsLong
);
375 DPRINT1("Controler EHCI Caps PowerControl %x\n", m_Capabilities
.HCSParams
.PortPowerControl
);
377 if (m_Capabilities
.HCSParams
.PortRouteRules
)
380 PortCount
= max(m_Capabilities
.HCSParams
.PortCount
/2, (m_Capabilities
.HCSParams
.PortCount
+1)/2);
384 // each entry is a 4 bit field EHCI 2.2.5
386 Value
= READ_REGISTER_UCHAR((PUCHAR
)(ULONG
)ResourceBase
+ EHCI_HCSP_PORTROUTE
+ Count
);
387 m_Capabilities
.PortRoute
[Count
*2] = (Value
& 0xF0);
389 if ((Count
*2) + 1 < m_Capabilities
.HCSParams
.PortCount
)
390 m_Capabilities
.PortRoute
[(Count
*2)+1] = (Value
& 0x0F);
393 }while(Count
< PortCount
);
397 // Set m_Base to the address of Operational Register Space
399 m_Base
= (PULONG
)((ULONG
)ResourceBase
+ m_Capabilities
.Length
);
407 // zero device description
409 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
412 // initialize device description
414 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
415 DeviceDescription
.Master
= TRUE
;
416 DeviceDescription
.ScatterGather
= TRUE
;
417 DeviceDescription
.Dma32BitAddresses
= TRUE
;
418 DeviceDescription
.DmaWidth
= Width32Bits
;
419 DeviceDescription
.InterfaceType
= PCIBus
;
420 DeviceDescription
.MaximumLength
= MAXULONG
;
425 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
429 // failed to get dma adapter
431 DPRINT1("Failed to acquire dma adapter\n");
432 return STATUS_INSUFFICIENT_RESOURCES
;
436 // Create Common Buffer
438 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
444 DPRINT1("Failed to allocate a common buffer\n");
445 return STATUS_INSUFFICIENT_RESOURCES
;
449 // Stop the controller before modifying schedules
451 Status
= StopController();
452 if (!NT_SUCCESS(Status
))
456 // Initialize the DMAMemoryManager
458 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
459 if (!NT_SUCCESS(Status
))
461 DPRINT1("Failed to initialize the DMAMemoryManager\n");
466 // Create a queuehead for the Async Register
468 m_MemoryManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&AsyncQueueHead
, &AsyncPhysicalAddress
);
470 AsyncQueueHead
->PhysicalAddr
= AsyncPhysicalAddress
.LowPart
;
471 AsyncQueueHead
->HorizontalLinkPointer
= AsyncQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
472 AsyncQueueHead
->EndPointCharacteristics
.HeadOfReclamation
= TRUE
;
473 AsyncQueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
474 AsyncQueueHead
->Token
.Bits
.Halted
= TRUE
;
476 AsyncQueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x01;
477 AsyncQueueHead
->NextPointer
= TERMINATE_POINTER
;
478 AsyncQueueHead
->CurrentLinkPointer
= TERMINATE_POINTER
;
480 InitializeListHead(&AsyncQueueHead
->LinkedQueueHeads
);
483 // Initialize the UsbQueue now that we have an AdapterObject.
485 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, &m_Lock
);
486 if (!NT_SUCCESS(Status
))
488 DPRINT1("Failed to Initialize the UsbQueue\n");
493 // Start the controller
495 DPRINT1("Starting Controller\n");
496 Status
= StartController();
505 CUSBHardwareDevice::PnpStop(void)
508 return STATUS_NOT_IMPLEMENTED
;
512 CUSBHardwareDevice::HandlePower(
516 return STATUS_NOT_IMPLEMENTED
;
520 CUSBHardwareDevice::GetDeviceDetails(
521 OUT OPTIONAL PUSHORT VendorId
,
522 OUT OPTIONAL PUSHORT DeviceId
,
523 OUT OPTIONAL PULONG NumberOfPorts
,
524 OUT OPTIONAL PULONG Speed
)
527 *VendorId
= m_VendorID
;
529 *DeviceId
= m_DeviceID
;
531 *NumberOfPorts
= m_Capabilities
.HCSParams
.PortCount
;
532 //FIXME: What to returned here?
535 return STATUS_SUCCESS
;
538 NTSTATUS
CUSBHardwareDevice::GetDMA(
539 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
541 if (!m_MemoryManager
)
542 return STATUS_UNSUCCESSFUL
;
543 *OutDMAMemoryManager
= m_MemoryManager
;
544 return STATUS_SUCCESS
;
548 CUSBHardwareDevice::GetUSBQueue(
549 OUT
struct IUSBQueue
**OutUsbQueue
)
552 return STATUS_UNSUCCESSFUL
;
553 *OutUsbQueue
= m_UsbQueue
;
554 return STATUS_SUCCESS
;
559 CUSBHardwareDevice::StartController(void)
561 EHCI_USBCMD_CONTENT UsbCmd
;
562 ULONG UsbSts
, FailSafe
, ExtendedCapsSupport
, Caps
, Index
;
564 LARGE_INTEGER Timeout
;
567 // are extended caps supported
569 ExtendedCapsSupport
= (m_Capabilities
.HCCParamsLong
>> EHCI_ECP_SHIFT
) & EHCI_ECP_MASK
;
570 if (ExtendedCapsSupport
)
572 DPRINT1("[EHCI] Extended Caps Support detected!\n");
577 ASSERT(ExtendedCapsSupport
>= PCI_COMMON_HDR_LENGTH
);
578 m_BusInterface
.GetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
, sizeof(ULONG
));
581 // OS Handoff Synchronization support capability. EHCI 5.1
583 if ((Caps
& EHCI_LEGSUP_CAPID_MASK
) == EHCI_LEGSUP_CAPID
)
588 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
590 DPRINT1("[EHCI] Controller is BIOS owned, acquring control\n");
596 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, ExtendedCapsSupport
+3, sizeof(UCHAR
));
598 for(Index
= 0; Index
< 20; Index
++)
603 m_BusInterface
.GetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
, sizeof(ULONG
));
604 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
609 Timeout
.QuadPart
= 50;
610 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout
.LowPart
);
613 // convert to 100 ns units (absolute)
615 Timeout
.QuadPart
*= -10000;
620 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
623 if ((Caps
& EHCI_LEGSUP_BIOSOWNED
))
626 // failed to aquire ownership
628 DPRINT1("[EHCI] failed to acquire ownership\n");
630 else if ((Caps
& EHCI_LEGSUP_OSOWNED
))
633 // HC OS Owned Semaphore EHCI 2.1.7
635 DPRINT1("[EHCI] acquired ownership\n");
639 // explictly clear the bios owned flag 2.1.7
642 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, ExtendedCapsSupport
+2, sizeof(UCHAR
));
645 // clear SMI interrupt EHCI 2.1.8
648 m_BusInterface
.SetBusData(m_BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Caps
, ExtendedCapsSupport
+4, sizeof(ULONG
));
655 // get command register
657 GetCommandRegister(&UsbCmd
);
660 // disable running schedules
662 UsbCmd
.PeriodicEnable
= FALSE
;
663 UsbCmd
.AsyncEnable
= FALSE
;
664 SetCommandRegister(&UsbCmd
);
667 // Wait for execution to start
669 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
671 KeStallExecutionProcessor(100);
672 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
674 if (!(UsbSts
& EHCI_STS_PSS
) && (UsbSts
& EHCI_STS_ASS
))
680 if ((UsbSts
& (EHCI_STS_PSS
| EHCI_STS_ASS
)))
682 DPRINT1("Failed to stop running schedules %x\n", UsbSts
);
688 // Stop the controller if its running
690 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
691 if (!(UsbSts
& EHCI_STS_HALT
))
693 DPRINT1("Stopping Controller %x\n", UsbSts
);
698 // Reset the controller
705 if (m_Capabilities
.HCCParams
.CurAddrBits
)
708 // disable 64-bit addressing
710 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT
, 0x0);
714 // Enable Interrupts and start execution
716 ULONG Mask
= EHCI_USBINTR_INTE
| EHCI_USBINTR_ERR
| EHCI_USBINTR_ASYNC
| EHCI_USBINTR_HSERR
| EHCI_USBINTR_PC
;
717 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, Mask
);
719 KeStallExecutionProcessor(10);
721 ULONG Status
= EHCI_READ_REGISTER_ULONG(EHCI_USBINTR
);
723 DPRINT1("Interrupt Mask %x\n", Status
);
724 ASSERT((Status
& Mask
) == Mask
);
727 // Assign the SyncList Register
729 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE
, m_SyncFramePhysAddr
);
732 // Set Schedules to Enable and Interrupt Threshold to 1ms.
734 RtlZeroMemory(&UsbCmd
, sizeof(EHCI_USBCMD_CONTENT
));
736 UsbCmd
.PeriodicEnable
= TRUE
;
737 UsbCmd
.IntThreshold
= 0x8; //1ms
739 UsbCmd
.FrameListSize
= 0x0; //1024
741 if (m_Capabilities
.HCCParams
.ParkMode
)
744 // enable async park mode
746 UsbCmd
.AsyncParkEnable
= TRUE
;
747 UsbCmd
.AsyncParkCount
= 3;
750 SetCommandRegister(&UsbCmd
);
754 // Wait for execution to start
756 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
758 KeStallExecutionProcessor(100);
759 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
761 if (!(UsbSts
& EHCI_STS_HALT
) && (UsbSts
& EHCI_STS_PSS
))
767 if (UsbSts
& EHCI_STS_HALT
)
769 DPRINT1("Could not start execution on the controller\n");
771 return STATUS_UNSUCCESSFUL
;
774 if (!(UsbSts
& EHCI_STS_PSS
))
776 DPRINT1("Could not enable periodic scheduling\n");
778 return STATUS_UNSUCCESSFUL
;
782 // Assign the AsyncList Register
784 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, AsyncQueueHead
->PhysicalAddr
);
787 // get command register
789 GetCommandRegister(&UsbCmd
);
794 UsbCmd
.AsyncEnable
= TRUE
;
799 SetCommandRegister(&UsbCmd
);
802 // Wait for execution to start
804 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
806 KeStallExecutionProcessor(100);
807 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
809 if ((UsbSts
& EHCI_STS_ASS
))
815 if (!(UsbSts
& EHCI_STS_ASS
))
817 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts
);
819 return STATUS_UNSUCCESSFUL
;
822 DPRINT1("UsbSts %x\n", UsbSts
);
823 GetCommandRegister(&UsbCmd
);
825 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd
.PeriodicEnable
);
826 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd
.AsyncEnable
);
827 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd
.IntThreshold
);
828 DPRINT1("UsbCmd.Run %x\n", UsbCmd
.Run
);
829 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd
.FrameListSize
);
832 // Set port routing to EHCI controller
834 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG
, 1);
836 DPRINT1("EHCI Started!\n");
837 return STATUS_SUCCESS
;
841 CUSBHardwareDevice::StopController(void)
843 EHCI_USBCMD_CONTENT UsbCmd
;
844 ULONG UsbSts
, FailSafe
;
847 // Disable Interrupts and stop execution
849 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR
, 0);
851 GetCommandRegister(&UsbCmd
);
853 SetCommandRegister(&UsbCmd
);
855 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
857 KeStallExecutionProcessor(10);
858 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
859 if (UsbSts
& EHCI_STS_HALT
)
865 if (!(UsbSts
& EHCI_STS_HALT
))
867 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
868 return STATUS_UNSUCCESSFUL
;
871 return STATUS_SUCCESS
;
875 CUSBHardwareDevice::ResetController(void)
877 EHCI_USBCMD_CONTENT UsbCmd
;
880 GetCommandRegister(&UsbCmd
);
881 UsbCmd
.HCReset
= TRUE
;
882 SetCommandRegister(&UsbCmd
);
884 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
886 KeStallExecutionProcessor(100);
887 GetCommandRegister(&UsbCmd
);
894 DPRINT1("EHCI ERROR: Controller is not responding to reset request!\n");
895 return STATUS_UNSUCCESSFUL
;
898 return STATUS_SUCCESS
;
902 CUSBHardwareDevice::ResetPort(
906 LARGE_INTEGER Timeout
;
908 if (PortIndex
> m_Capabilities
.HCSParams
.PortCount
)
909 return STATUS_UNSUCCESSFUL
;
911 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
913 // check slow speed line before reset
915 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
917 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
918 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), EHCI_PRT_RELEASEOWNERSHIP
);
919 return STATUS_DEVICE_NOT_CONNECTED
;
922 ASSERT(PortStatus
& EHCI_PRT_CONNECTED
);
925 // Reset and clean enable
927 PortStatus
|= EHCI_PRT_RESET
;
928 PortStatus
&= ~EHCI_PRT_ENABLED
;
929 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
932 // delay is 20 ms for port reset as per USB 2.0 spec
934 Timeout
.QuadPart
= 20;
935 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout
.LowPart
);
938 // convert to 100 ns units (absolute)
940 Timeout
.QuadPart
*= -10000;
945 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
950 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
951 PortStatus
&= ~EHCI_PRT_RESET
;
952 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
959 KeStallExecutionProcessor(100);
962 // Check that the port reset
964 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
965 if (!(PortStatus
& EHCI_PRT_RESET
))
972 Timeout
.QuadPart
= 10;
973 DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout
.LowPart
);
976 // convert to 100 ns units (absolute)
978 Timeout
.QuadPart
*= -10000;
983 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
986 // check slow speed line after reset
988 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
989 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
991 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
992 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), EHCI_PRT_RELEASEOWNERSHIP
);
993 return STATUS_DEVICE_NOT_CONNECTED
;
997 // this must be enabled now
999 ASSERT(PortStatus
& EHCI_PRT_ENABLED
);
1001 return STATUS_SUCCESS
;
1005 CUSBHardwareDevice::GetPortStatus(
1007 OUT USHORT
*PortStatus
,
1008 OUT USHORT
*PortChange
)
1011 USHORT Status
= 0, Change
= 0;
1013 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
1014 return STATUS_UNSUCCESSFUL
;
1017 // Get the value of the Port Status and Control Register
1019 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1022 // If the PowerPortControl is 0 then host controller does not have power control switches
1023 if (!m_Capabilities
.HCSParams
.PortPowerControl
)
1025 Status
|= USB_PORT_STATUS_POWER
;
1029 // Check the value of PortPower
1030 if (Value
& EHCI_PRT_POWER
)
1032 Status
|= USB_PORT_STATUS_POWER
;
1036 // Get Connected Status
1037 if (Value
& EHCI_PRT_CONNECTED
)
1039 Status
|= USB_PORT_STATUS_CONNECT
;
1041 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
1042 if (Value
& EHCI_PRT_SLOWSPEEDLINE
)
1043 Status
|= USB_PORT_STATUS_LOW_SPEED
;
1045 Status
|= USB_PORT_STATUS_HIGH_SPEED
;
1048 // Get Enabled Status
1049 if (Value
& EHCI_PRT_ENABLED
)
1050 Status
|= USB_PORT_STATUS_ENABLE
;
1053 if (Value
& EHCI_PRT_SUSPEND
)
1054 Status
|= USB_PORT_STATUS_SUSPEND
;
1056 // a overcurrent is active?
1057 if (Value
& EHCI_PRT_OVERCURRENTACTIVE
)
1058 Status
|= USB_PORT_STATUS_OVER_CURRENT
;
1060 // In a reset state?
1061 if ((Value
& EHCI_PRT_RESET
) || m_ResetInProgress
[PortId
])
1063 Status
|= USB_PORT_STATUS_RESET
;
1064 Change
|= USB_PORT_STATUS_RESET
;
1068 // FIXME: Is the Change here correct?
1070 if (Value
& EHCI_PRT_CONNECTSTATUSCHANGE
)
1071 Change
|= USB_PORT_STATUS_CONNECT
;
1073 if (Value
& EHCI_PRT_ENABLEDSTATUSCHANGE
)
1074 Change
|= USB_PORT_STATUS_ENABLE
;
1076 *PortStatus
= Status
;
1077 *PortChange
= Change
;
1079 return STATUS_SUCCESS
;
1083 CUSBHardwareDevice::ClearPortStatus(
1089 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
1091 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
1092 return STATUS_UNSUCCESSFUL
;
1094 if (Status
== C_PORT_RESET
)
1097 // update port status
1099 m_ResetInProgress
[PortId
] = FALSE
;
1102 if (Status
== C_PORT_CONNECTION
)
1104 LARGE_INTEGER Timeout
;
1107 // reset status change bits
1109 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1110 Value
|= EHCI_PRT_CONNECTSTATUSCHANGE
| EHCI_PRT_ENABLEDSTATUSCHANGE
;
1111 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
1116 Timeout
.QuadPart
= 100;
1117 DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout
.LowPart
);
1120 // convert to 100 ns units (absolute)
1122 Timeout
.QuadPart
*= -10000;
1127 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1130 return STATUS_SUCCESS
;
1135 CUSBHardwareDevice::SetPortFeature(
1141 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
1143 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
1144 return STATUS_UNSUCCESSFUL
;
1146 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
1148 if (Feature
== PORT_ENABLE
)
1151 // FIXME: EHCI Ports can only be disabled via reset
1153 DPRINT1("PORT_ENABLE not supported for EHCI\n");
1156 if (Feature
== PORT_RESET
)
1161 // update cached settings
1163 m_ResetInProgress
[PortId
] = TRUE
;
1166 // is there a status change callback
1168 if (m_SCECallBack
!= NULL
)
1173 m_SCECallBack(m_SCEContext
);
1177 if (Feature
== PORT_POWER
)
1179 if (m_Capabilities
.HCSParams
.PortPowerControl
)
1182 LARGE_INTEGER Timeout
;
1185 // enable port power
1187 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
)) | EHCI_PRT_POWER
;
1188 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
, Value
);
1193 Timeout
.QuadPart
= 20;
1194 DPRINT1("Waiting %d milliseconds for port power up\n", Timeout
.LowPart
);
1197 // convert to 100 ns units (absolute)
1199 Timeout
.QuadPart
*= -10000;
1204 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1207 return STATUS_SUCCESS
;
1211 CUSBHardwareDevice::SetAsyncListRegister(
1212 ULONG PhysicalAddress
)
1214 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, PhysicalAddress
);
1218 CUSBHardwareDevice::SetPeriodicListRegister(
1219 ULONG PhysicalAddress
)
1222 // store physical address
1224 m_SyncFramePhysAddr
= PhysicalAddress
;
1227 struct _QUEUE_HEAD
*
1228 CUSBHardwareDevice::GetAsyncListQueueHead()
1230 return AsyncQueueHead
;
1233 ULONG
CUSBHardwareDevice::GetPeriodicListRegister()
1239 VOID
CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1243 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1244 m_SCEContext
= Context
;
1248 CUSBHardwareDevice::AcquireDeviceLock(void)
1255 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
1265 CUSBHardwareDevice::ReleaseDeviceLock(
1268 KeReleaseSpinLock(&m_Lock
, OldLevel
);
1273 InterruptServiceRoutine(
1274 IN PKINTERRUPT Interrupt
,
1275 IN PVOID ServiceContext
)
1277 CUSBHardwareDevice
*This
;
1280 This
= (CUSBHardwareDevice
*) ServiceContext
;
1281 CStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
1283 CStatus
&= (EHCI_ERROR_INT
| EHCI_STS_INT
| EHCI_STS_IAA
| EHCI_STS_PCD
| EHCI_STS_FLR
);
1284 DPRINT1("CStatus %x\n", CStatus
);
1287 // Check that it belongs to EHCI
1295 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, CStatus
);
1297 if (CStatus
& EHCI_STS_FATAL
)
1299 This
->StopController();
1300 DPRINT1("EHCI: Host System Error!\n");
1304 if (CStatus
& EHCI_ERROR_INT
)
1306 DPRINT1("EHCI Status = 0x%x\n", CStatus
);
1309 if (CStatus
& EHCI_STS_HALT
)
1311 DPRINT1("Host Error Unexpected Halt\n");
1312 // FIXME: Reset controller\n");
1316 KeInsertQueueDpc(&This
->m_IntDpcObject
, This
, (PVOID
)CStatus
);
1321 EhciDefferedRoutine(
1323 IN PVOID DeferredContext
,
1324 IN PVOID SystemArgument1
,
1325 IN PVOID SystemArgument2
)
1327 CUSBHardwareDevice
*This
;
1328 ULONG CStatus
, PortStatus
, PortCount
, i
, ShouldRingDoorBell
;
1329 NTSTATUS Status
= STATUS_SUCCESS
;
1330 EHCI_USBCMD_CONTENT UsbCmd
;
1332 This
= (CUSBHardwareDevice
*) SystemArgument1
;
1333 CStatus
= (ULONG
) SystemArgument2
;
1335 DPRINT("CStatus %x\n", CStatus
);
1338 // check for completion of async schedule
1340 if (CStatus
& (EHCI_STS_RECL
| EHCI_STS_INT
| EHCI_ERROR_INT
))
1343 // check if there is a door bell ring in progress
1345 if (This
->m_DoorBellRingInProgress
== FALSE
)
1347 if (CStatus
& EHCI_ERROR_INT
)
1350 // controller reported error
1352 DPRINT1("CStatus %x\n", CStatus
);
1357 // inform IUSBQueue of a completed queue head
1359 This
->m_UsbQueue
->InterruptCallback(Status
, &ShouldRingDoorBell
);
1362 // was a queue head completed?
1364 if (ShouldRingDoorBell
)
1367 // set door ring bell in progress status flag
1369 This
->m_DoorBellRingInProgress
= TRUE
;
1372 // get command register
1374 This
->GetCommandRegister(&UsbCmd
);
1377 // set door rang bell bit
1379 UsbCmd
.DoorBell
= TRUE
;
1382 // update command status
1384 This
->SetCommandRegister(&UsbCmd
);
1390 // check if the controller has acknowledged the door bell
1392 if (CStatus
& EHCI_STS_IAA
)
1395 // controller has acknowledged, assert we rang the bell
1397 PC_ASSERT(This
->m_DoorBellRingInProgress
== TRUE
);
1400 // now notify IUSBQueue that it can free completed requests
1402 This
->m_UsbQueue
->CompleteAsyncRequests();
1405 // door ring bell completed
1407 This
->m_DoorBellRingInProgress
= FALSE
;
1410 This
->GetDeviceDetails(NULL
, NULL
, &PortCount
, NULL
);
1411 if (CStatus
& EHCI_STS_PCD
)
1413 for (i
= 0; i
< PortCount
; i
++)
1415 PortStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
));
1418 // Device connected or removed
1420 if (PortStatus
& EHCI_PRT_CONNECTSTATUSCHANGE
)
1422 if (PortStatus
& EHCI_PRT_CONNECTED
)
1424 DPRINT1("Device connected on port %d\n", i
);
1427 //FIXME: Determine device speed
1429 if (This
->m_Capabilities
.HCSParams
.CHCCount
)
1431 if (PortStatus
& EHCI_PRT_ENABLED
)
1433 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1436 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
1438 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1439 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
), EHCI_PRT_RELEASEOWNERSHIP
);
1446 DPRINT1("Device disconnected on port %d\n", i
);
1450 // is there a status change callback
1452 if (This
->m_SCECallBack
!= NULL
)
1455 // queue work item for processing
1457 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1461 // FIXME: This needs to be saved somewhere
1471 StatusChangeWorkItemRoutine(
1475 // cast to hardware object
1477 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1480 // is there a callback
1482 if (This
->m_SCECallBack
)
1487 This
->m_SCECallBack(This
->m_SCEContext
);
1494 PUSBHARDWAREDEVICE
*OutHardware
)
1496 PUSBHARDWAREDEVICE This
;
1498 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBHardwareDevice(0);
1501 return STATUS_INSUFFICIENT_RESOURCES
;
1506 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1508 return STATUS_SUCCESS
;