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
);
26 IN PVOID DeferredContext
,
27 IN PVOID SystemArgument1
,
28 IN PVOID SystemArgument2
);
30 class CUSBHardwareDevice
: public IUSBHardwareDevice
33 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
35 STDMETHODIMP_(ULONG
) AddRef()
37 InterlockedIncrement(&m_Ref
);
40 STDMETHODIMP_(ULONG
) Release()
42 InterlockedDecrement(&m_Ref
);
52 NTSTATUS
Initialize(PDRIVER_OBJECT DriverObject
, PDEVICE_OBJECT FunctionalDeviceObject
, PDEVICE_OBJECT PhysicalDeviceObject
, PDEVICE_OBJECT LowerDeviceObject
);
53 NTSTATUS
PnpStart(PCM_RESOURCE_LIST RawResources
, PCM_RESOURCE_LIST TranslatedResources
);
54 NTSTATUS
PnpStop(void);
55 NTSTATUS
HandlePower(PIRP Irp
);
56 NTSTATUS
GetDeviceDetails(PUSHORT VendorId
, PUSHORT DeviceId
, PULONG NumberOfPorts
, PULONG Speed
);
57 NTSTATUS
GetDMA(OUT
struct IDMAMemoryManager
**m_DmaManager
);
58 NTSTATUS
GetUSBQueue(OUT
struct IUSBQueue
**OutUsbQueue
);
60 NTSTATUS
StartController();
61 NTSTATUS
StopController();
62 NTSTATUS
ResetController();
63 NTSTATUS
ResetPort(ULONG PortIndex
);
65 NTSTATUS
GetPortStatus(ULONG PortId
, OUT USHORT
*PortStatus
, OUT USHORT
*PortChange
);
66 NTSTATUS
ClearPortStatus(ULONG PortId
, ULONG Status
);
67 NTSTATUS
SetPortFeature(ULONG PortId
, ULONG Feature
);
69 VOID
SetAsyncListRegister(ULONG PhysicalAddress
);
70 VOID
SetPeriodicListRegister(ULONG PhysicalAddress
);
71 struct _QUEUE_HEAD
* GetAsyncListQueueHead();
72 ULONG
GetPeriodicListRegister();
74 VOID
SetStatusChangeEndpointCallBack(PVOID CallBack
, PVOID Context
);
76 KIRQL
AcquireDeviceLock(void);
77 VOID
ReleaseDeviceLock(KIRQL OldLevel
);
79 BOOLEAN
InterruptService();
82 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
83 friend VOID NTAPI
EhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
85 // constructor / destructor
86 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
87 virtual ~CUSBHardwareDevice(){}
90 LONG m_Ref
; // reference count
91 PDRIVER_OBJECT m_DriverObject
; // driver object
92 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
93 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
94 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
95 KSPIN_LOCK m_Lock
; // hardware lock
96 PKINTERRUPT m_Interrupt
; // interrupt object
97 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
98 PVOID VirtualBase
; // virtual base for memory manager
99 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
100 PULONG m_Base
; // EHCI operational port base registers
101 PDMA_ADAPTER m_Adapter
; // dma adapter object
102 ULONG m_MapRegisters
; // map registers count
103 EHCI_CAPS m_Capabilities
; // EHCI caps
104 USHORT m_VendorID
; // vendor id
105 USHORT m_DeviceID
; // device id
106 PQUEUE_HEAD AsyncQueueHead
; // async queue head terminator
107 PUSBQUEUE m_UsbQueue
; // usb request queue
108 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
109 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
110 PVOID m_SCEContext
; // status change callback routine context
111 BOOLEAN m_DoorBellRingInProgress
; // door bell ring in progress
112 EHCI_PORT_STATUS m_PortStatus
[16]; // port status
115 VOID
SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
118 VOID
GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
121 ULONG
EHCI_READ_REGISTER_ULONG(ULONG Offset
);
124 VOID
EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
);
127 //=================================================================================================
132 CUSBHardwareDevice::QueryInterface(
136 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
138 *Output
= PVOID(PUNKNOWN(this));
139 PUNKNOWN(*Output
)->AddRef();
140 return STATUS_SUCCESS
;
143 return STATUS_UNSUCCESSFUL
;
147 CUSBHardwareDevice::Initialize(
148 PDRIVER_OBJECT DriverObject
,
149 PDEVICE_OBJECT FunctionalDeviceObject
,
150 PDEVICE_OBJECT PhysicalDeviceObject
,
151 PDEVICE_OBJECT LowerDeviceObject
)
153 BUS_INTERFACE_STANDARD BusInterface
;
154 PCI_COMMON_CONFIG PciConfig
;
158 DPRINT1("CUSBHardwareDevice::Initialize\n");
161 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
163 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
164 if (!NT_SUCCESS(Status
))
166 DPRINT1("Failed to create DMAMemoryManager Object\n");
171 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
173 Status
= CreateUSBQueue(&m_UsbQueue
);
174 if (!NT_SUCCESS(Status
))
176 DPRINT1("Failed to create UsbQueue!\n");
181 // store device objects
183 m_DriverObject
= DriverObject
;
184 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
185 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
186 m_NextDeviceObject
= LowerDeviceObject
;
189 // initialize device lock
191 KeInitializeSpinLock(&m_Lock
);
196 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
197 if (!NT_SUCCESS(Status
))
199 DPRINT1("Failed to get BusInteface!\n");
203 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
204 PCI_WHICHSPACE_CONFIG
,
207 PCI_COMMON_HDR_LENGTH
);
209 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
211 DPRINT1("Failed to get pci config information!\n");
212 return STATUS_SUCCESS
;
215 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
217 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
220 m_VendorID
= PciConfig
.VendorID
;
221 m_DeviceID
= PciConfig
.DeviceID
;
223 return STATUS_SUCCESS
;
227 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
230 Register
= (PULONG
)UsbCmd
;
231 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
), *Register
);
235 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
238 Register
= (PULONG
)UsbCmd
;
239 *Register
= READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
));
243 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset
)
245 return READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
));
249 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
)
251 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
), Value
);
255 CUSBHardwareDevice::PnpStart(
256 PCM_RESOURCE_LIST RawResources
,
257 PCM_RESOURCE_LIST TranslatedResources
)
260 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
261 DEVICE_DESCRIPTION DeviceDescription
;
262 PHYSICAL_ADDRESS AsyncPhysicalAddress
;
266 DPRINT1("CUSBHardwareDevice::PnpStart\n");
267 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
270 // get resource descriptor
272 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
274 switch(ResourceDescriptor
->Type
)
276 case CmResourceTypeInterrupt
:
278 KeInitializeDpc(&m_IntDpcObject
,
282 Status
= IoConnectInterrupt(&m_Interrupt
,
283 InterruptServiceRoutine
,
286 ResourceDescriptor
->u
.Interrupt
.Vector
,
287 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
288 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
289 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
290 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
291 ResourceDescriptor
->u
.Interrupt
.Affinity
,
294 if (!NT_SUCCESS(Status
))
297 // failed to register interrupt
299 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
304 case CmResourceTypeMemory
:
309 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
313 // failed to map registers
315 DPRINT1("MmMapIoSpace failed\n");
316 return STATUS_INSUFFICIENT_RESOURCES
;
320 // Get controllers capabilities
322 m_Capabilities
.Length
= READ_REGISTER_UCHAR((PUCHAR
)ResourceBase
);
323 m_Capabilities
.HCIVersion
= READ_REGISTER_USHORT((PUSHORT
)((ULONG
)ResourceBase
+ 2));
324 m_Capabilities
.HCSParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ 4));
325 m_Capabilities
.HCCParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ 8));
327 DPRINT1("Controller has %d Ports\n", m_Capabilities
.HCSParams
.PortCount
);
328 DPRINT1("Controller EHCI Version %x\n", m_Capabilities
.HCIVersion
);
329 if (m_Capabilities
.HCSParams
.PortRouteRules
)
331 for (Count
= 0; Count
< m_Capabilities
.HCSParams
.PortCount
; Count
++)
333 m_Capabilities
.PortRoute
[Count
] = READ_REGISTER_UCHAR((PUCHAR
)(ULONG
)ResourceBase
+ 12 + Count
);
338 // Set m_Base to the address of Operational Register Space
340 m_Base
= (PULONG
)((ULONG
)ResourceBase
+ m_Capabilities
.Length
);
348 // zero device description
350 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
353 // initialize device description
355 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
356 DeviceDescription
.Master
= TRUE
;
357 DeviceDescription
.ScatterGather
= TRUE
;
358 DeviceDescription
.Dma32BitAddresses
= TRUE
;
359 DeviceDescription
.DmaWidth
= Width32Bits
;
360 DeviceDescription
.InterfaceType
= PCIBus
;
361 DeviceDescription
.MaximumLength
= MAXULONG
;
366 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
370 // failed to get dma adapter
372 DPRINT1("Failed to acquire dma adapter\n");
373 return STATUS_INSUFFICIENT_RESOURCES
;
377 // Create Common Buffer
379 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
385 DPRINT1("Failed to allocate a common buffer\n");
386 return STATUS_INSUFFICIENT_RESOURCES
;
390 // Stop the controller before modifying schedules
392 Status
= StopController();
393 if (!NT_SUCCESS(Status
))
397 // Initialize the DMAMemoryManager
399 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
400 if (!NT_SUCCESS(Status
))
402 DPRINT1("Failed to initialize the DMAMemoryManager\n");
407 // Create a queuehead for the Async Register
409 m_MemoryManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&AsyncQueueHead
, &AsyncPhysicalAddress
);
411 AsyncQueueHead
->AlternateNextPointer
= TERMINATE_POINTER
;
412 AsyncQueueHead
->NextPointer
= TERMINATE_POINTER
;
413 AsyncQueueHead
->PhysicalAddr
= AsyncPhysicalAddress
.LowPart
;
414 AsyncQueueHead
->HorizontalLinkPointer
= AsyncQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
415 AsyncQueueHead
->EndPointCharacteristics
.QEDTDataToggleControl
= FALSE
;
416 AsyncQueueHead
->Token
.Bits
.InterruptOnComplete
= FALSE
;
417 AsyncQueueHead
->EndPointCharacteristics
.HeadOfReclamation
= TRUE
;
418 AsyncQueueHead
->Token
.Bits
.Halted
= TRUE
;
419 AsyncQueueHead
->EndPointCharacteristics
.MaximumPacketLength
= 64;
420 AsyncQueueHead
->EndPointCharacteristics
.NakCountReload
= 0;
421 AsyncQueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
422 AsyncQueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x03;
424 InitializeListHead(&AsyncQueueHead
->LinkedQueueHeads
);
427 // Initialize the UsbQueue now that we have an AdapterObject.
429 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, NULL
);
430 if (!NT_SUCCESS(Status
))
432 DPRINT1("Failed to Initialize the UsbQueue\n");
437 // Start the controller
439 DPRINT1("Starting Controller\n");
440 Status
= StartController();
449 CUSBHardwareDevice::PnpStop(void)
452 return STATUS_NOT_IMPLEMENTED
;
456 CUSBHardwareDevice::HandlePower(
460 return STATUS_NOT_IMPLEMENTED
;
464 CUSBHardwareDevice::GetDeviceDetails(
465 OUT OPTIONAL PUSHORT VendorId
,
466 OUT OPTIONAL PUSHORT DeviceId
,
467 OUT OPTIONAL PULONG NumberOfPorts
,
468 OUT OPTIONAL PULONG Speed
)
471 *VendorId
= m_VendorID
;
473 *DeviceId
= m_DeviceID
;
475 *NumberOfPorts
= m_Capabilities
.HCSParams
.PortCount
;
476 //FIXME: What to returned here?
479 return STATUS_SUCCESS
;
482 NTSTATUS
CUSBHardwareDevice::GetDMA(
483 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
485 if (!m_MemoryManager
)
486 return STATUS_UNSUCCESSFUL
;
487 *OutDMAMemoryManager
= m_MemoryManager
;
488 return STATUS_SUCCESS
;
492 CUSBHardwareDevice::GetUSBQueue(
493 OUT
struct IUSBQueue
**OutUsbQueue
)
496 return STATUS_UNSUCCESSFUL
;
497 *OutUsbQueue
= m_UsbQueue
;
498 return STATUS_SUCCESS
;
503 CUSBHardwareDevice::StartController(void)
505 EHCI_USBCMD_CONTENT UsbCmd
;
506 ULONG UsbSts
, FailSafe
;
509 // Stop the controller if its running
511 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
512 if (!(UsbSts
& EHCI_STS_HALT
))
516 // Reset the device. Bit is set to 0 on completion.
518 GetCommandRegister(&UsbCmd
);
519 UsbCmd
.HCReset
= TRUE
;
520 SetCommandRegister(&UsbCmd
);
523 // Check that the controller reset
525 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
527 KeStallExecutionProcessor(10);
528 GetCommandRegister(&UsbCmd
);
536 // If the controller did not reset then fail
540 DPRINT1("EHCI ERROR: Controller failed to reset. Hardware problem!\n");
541 return STATUS_UNSUCCESSFUL
;
545 // Disable Interrupts and clear status
547 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, 0);
548 EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, 0x0000001f);
551 // Assign the AsyncList Register
553 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, AsyncQueueHead
->PhysicalAddr
);
556 // Set Schedules to Enable and Interrupt Threshold to 1ms.
558 GetCommandRegister(&UsbCmd
);
559 UsbCmd
.PeriodicEnable
= FALSE
;
560 UsbCmd
.AsyncEnable
= TRUE
; //FIXME: Need USB Memory Manager
562 UsbCmd
.IntThreshold
= 1;
563 // FIXME: Set framelistsize when periodic is implemented.
564 SetCommandRegister(&UsbCmd
);
567 // Enable Interrupts and start execution
569 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, EHCI_USBINTR_INTE
| EHCI_USBINTR_ERR
| EHCI_USBINTR_ASYNC
| EHCI_USBINTR_HSERR
570 /*| EHCI_USBINTR_FLROVR*/ | EHCI_USBINTR_PC
);
573 SetCommandRegister(&UsbCmd
);
576 // Wait for execution to start
578 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
580 KeStallExecutionProcessor(10);
581 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
583 if (!(UsbSts
& EHCI_STS_HALT
))
589 if (UsbSts
& EHCI_STS_HALT
)
591 DPRINT1("Could not start execution on the controller\n");
592 return STATUS_UNSUCCESSFUL
;
596 // Set port routing to EHCI controller
598 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG
, 1);
600 DPRINT1("EHCI Started!\n");
601 return STATUS_SUCCESS
;
605 CUSBHardwareDevice::StopController(void)
607 EHCI_USBCMD_CONTENT UsbCmd
;
608 ULONG UsbSts
, FailSafe
;
611 // Disable Interrupts and stop execution
613 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR
, 0);
615 GetCommandRegister(&UsbCmd
);
617 SetCommandRegister(&UsbCmd
);
619 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
621 KeStallExecutionProcessor(10);
622 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
623 if (UsbSts
& EHCI_STS_HALT
)
629 if (!(UsbSts
& EHCI_STS_HALT
))
631 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
632 return STATUS_UNSUCCESSFUL
;
635 return STATUS_SUCCESS
;
639 CUSBHardwareDevice::ResetController(void)
642 return STATUS_NOT_IMPLEMENTED
;
646 CUSBHardwareDevice::ResetPort(
651 if (PortIndex
> m_Capabilities
.HCSParams
.PortCount
)
652 return STATUS_UNSUCCESSFUL
;
654 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
655 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
657 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
658 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), EHCI_PRT_RELEASEOWNERSHIP
);
659 return STATUS_DEVICE_NOT_CONNECTED
;
663 // Reset and clean enable
665 PortStatus
|= EHCI_PRT_RESET
;
666 PortStatus
&= ~EHCI_PRT_ENABLED
;
667 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
669 KeStallExecutionProcessor(100);
674 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
675 PortStatus
&= ~EHCI_PRT_RESET
;
676 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
678 KeStallExecutionProcessor(100);
681 // Check that the port reset
683 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
684 if (PortStatus
& EHCI_PRT_RESET
)
686 DPRINT1("Port did not reset\n");
690 return STATUS_SUCCESS
;
694 CUSBHardwareDevice::GetPortStatus(
696 OUT USHORT
*PortStatus
,
697 OUT USHORT
*PortChange
)
701 USHORT Status
= 0, Change
= 0;
703 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
704 return STATUS_UNSUCCESSFUL
;
707 // Get the value of the Port Status and Control Register
709 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
712 // If the PowerPortControl is 0 then host controller does not have power control switches
713 if (!m_Capabilities
.HCSParams
.PortPowerControl
)
715 Status
|= USB_PORT_STATUS_POWER
;
719 // Check the value of PortPower
720 if (Value
& EHCI_PRT_POWER
)
722 Status
|= USB_PORT_STATUS_POWER
;
726 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
727 if (Value
& EHCI_PRT_SLOWSPEEDLINE
)
728 Status
|= USB_PORT_STATUS_LOW_SPEED
;
730 Status
|= USB_PORT_STATUS_HIGH_SPEED
;
732 // Get Connected Status
733 if (Value
& EHCI_PRT_CONNECTED
)
734 Status
|= USB_PORT_STATUS_CONNECT
;
736 // Get Enabled Status
737 if (Value
& EHCI_PRT_ENABLED
)
738 Status
|= USB_PORT_STATUS_ENABLE
;
741 if (Value
& EHCI_PRT_SUSPEND
)
742 Status
|= USB_PORT_STATUS_SUSPEND
;
744 // a overcurrent is active?
745 if (Value
& EHCI_PRT_OVERCURRENTACTIVE
)
746 Status
|= USB_PORT_STATUS_OVER_CURRENT
;
749 if (Value
& EHCI_PRT_RESET
)
750 Status
|= USB_PORT_STATUS_RESET
;
753 // FIXME: Is the Change here correct?
755 if (Value
& EHCI_PRT_CONNECTSTATUSCHANGE
)
756 Change
|= USB_PORT_STATUS_CONNECT
;
758 if (Value
& EHCI_PRT_ENABLEDSTATUSCHANGE
)
759 Change
|= USB_PORT_STATUS_ENABLE
;
761 *PortStatus
= Status
;
762 *PortChange
= Change
;
764 *PortStatus
= m_PortStatus
[PortId
].PortStatus
;
765 *PortChange
= m_PortStatus
[PortId
].PortChange
;
767 return STATUS_SUCCESS
;
771 CUSBHardwareDevice::ClearPortStatus(
777 DPRINT("CUSBHardwareDevice::ClearPortStatus\n");
779 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
780 return STATUS_UNSUCCESSFUL
;
782 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
784 if (Status
== C_PORT_RESET
)
786 if (Value
& EHCI_PRT_RESET
)
788 Value
&= ~EHCI_PRT_RESET
;
789 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
790 KeStallExecutionProcessor(100);
793 // update port status
795 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_RESET
;
796 m_PortStatus
[PortId
].PortStatus
|= USB_PORT_STATUS_ENABLE
;
800 if (Status
== C_PORT_CONNECTION
)
802 // FIXME: Make sure its the Connection and Enable Change status.
803 Value
|= EHCI_PRT_CONNECTSTATUSCHANGE
;
804 Value
|= EHCI_PRT_ENABLEDSTATUSCHANGE
;
805 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
807 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_CONNECT
;
810 return STATUS_SUCCESS
;
815 CUSBHardwareDevice::SetPortFeature(
821 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
823 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
824 return STATUS_UNSUCCESSFUL
;
826 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
828 if (Feature
== PORT_ENABLE
)
831 // FIXME: EHCI Ports can only be disabled via reset
833 DPRINT1("PORT_ENABLE not supported for EHCI\n");
836 if (Feature
== PORT_RESET
)
838 if (Value
& EHCI_PRT_SLOWSPEEDLINE
)
840 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
846 // update cached settings
848 m_PortStatus
[PortId
].PortChange
|= USB_PORT_STATUS_RESET
;
849 m_PortStatus
[PortId
].PortStatus
&= ~USB_PORT_STATUS_ENABLE
;
852 // is there a status change callback
854 if (m_SCECallBack
!= NULL
)
859 m_SCECallBack(m_SCEContext
);
863 if (Feature
== PORT_POWER
)
864 DPRINT1("PORT_POWER Not implemented\n");
866 return STATUS_SUCCESS
;
870 CUSBHardwareDevice::SetAsyncListRegister(
871 ULONG PhysicalAddress
)
873 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, PhysicalAddress
);
877 CUSBHardwareDevice::SetPeriodicListRegister(
878 ULONG PhysicalAddress
)
880 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE
, PhysicalAddress
);
884 CUSBHardwareDevice::GetAsyncListQueueHead()
886 return AsyncQueueHead
;
889 ULONG
CUSBHardwareDevice::GetPeriodicListRegister()
895 VOID
CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
899 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
900 m_SCEContext
= Context
;
904 CUSBHardwareDevice::AcquireDeviceLock(void)
911 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
921 CUSBHardwareDevice::ReleaseDeviceLock(
924 KeReleaseSpinLock(&m_Lock
, OldLevel
);
929 InterruptServiceRoutine(
930 IN PKINTERRUPT Interrupt
,
931 IN PVOID ServiceContext
)
933 CUSBHardwareDevice
*This
;
936 This
= (CUSBHardwareDevice
*) ServiceContext
;
937 CStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
939 CStatus
&= (EHCI_ERROR_INT
| EHCI_STS_INT
| EHCI_STS_IAA
| EHCI_STS_PCD
| EHCI_STS_FLR
);
941 // Check that it belongs to EHCI
949 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, CStatus
);
951 if (CStatus
& EHCI_STS_FATAL
)
953 This
->StopController();
954 DPRINT1("EHCI: Host System Error!\n");
958 if (CStatus
& EHCI_ERROR_INT
)
960 DPRINT1("EHCI Status = 0x%x\n", CStatus
);
963 if (CStatus
& EHCI_STS_HALT
)
965 DPRINT1("Host Error Unexpected Halt\n");
966 // FIXME: Reset controller\n");
970 KeInsertQueueDpc(&This
->m_IntDpcObject
, This
, (PVOID
)CStatus
);
977 IN PVOID DeferredContext
,
978 IN PVOID SystemArgument1
,
979 IN PVOID SystemArgument2
)
981 CUSBHardwareDevice
*This
;
982 ULONG CStatus
, PortStatus
, PortCount
, i
, ShouldRingDoorBell
;
983 NTSTATUS Status
= STATUS_SUCCESS
;
984 EHCI_USBCMD_CONTENT UsbCmd
;
986 This
= (CUSBHardwareDevice
*) SystemArgument1
;
987 CStatus
= (ULONG
) SystemArgument2
;
991 // check for completion of async schedule
993 if (CStatus
& (EHCI_STS_RECL
| EHCI_STS_INT
| EHCI_ERROR_INT
))
996 // check if there is a door bell ring in progress
998 if (This
->m_DoorBellRingInProgress
== FALSE
)
1000 if (CStatus
& EHCI_ERROR_INT
)
1003 // controller reported error
1005 Status
= STATUS_UNSUCCESSFUL
;
1010 // inform IUSBQueue of a completed queue head
1012 This
->m_UsbQueue
->InterruptCallback(Status
, &ShouldRingDoorBell
);
1015 // was a queue head completed?
1017 if (ShouldRingDoorBell
)
1020 // set door ring bell in progress status flag
1022 This
->m_DoorBellRingInProgress
= TRUE
;
1025 // get command register
1027 This
->GetCommandRegister(&UsbCmd
);
1030 // set door rang bell bit
1032 UsbCmd
.DoorBell
= TRUE
;
1035 // update command status
1037 This
->SetCommandRegister(&UsbCmd
);
1043 // check if the controller has acknowledged the door bell
1045 if (CStatus
& EHCI_STS_IAA
)
1048 // controller has acknowledged, assert we rang the bell
1050 PC_ASSERT(This
->m_DoorBellRingInProgress
== TRUE
);
1053 // now notify IUSBQueue that it can free completed requests
1055 This
->m_UsbQueue
->CompleteAsyncRequests();
1058 // door ring bell completed
1060 This
->m_DoorBellRingInProgress
= FALSE
;
1063 This
->GetDeviceDetails(NULL
, NULL
, &PortCount
, NULL
);
1064 if (CStatus
& EHCI_STS_PCD
)
1066 for (i
= 0; i
< PortCount
; i
++)
1068 PortStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
));
1071 // Device connected or removed
1073 if (PortStatus
& EHCI_PRT_CONNECTSTATUSCHANGE
)
1076 // Clear the port change status
1078 //This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_CONNECTSTATUSCHANGE);
1080 if (PortStatus
& EHCI_PRT_CONNECTED
)
1082 DPRINT1("Device connected on port %d\n", i
);
1085 //FIXME: Determine device speed
1087 if (This
->m_Capabilities
.HCSParams
.CHCCount
)
1089 if (PortStatus
& EHCI_PRT_ENABLED
)
1091 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1094 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
1096 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1097 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
), EHCI_PRT_RELEASEOWNERSHIP
);
1103 // update port status flags
1105 This
->m_PortStatus
[i
].PortStatus
|= USB_PORT_STATUS_HIGH_SPEED
;
1106 This
->m_PortStatus
[i
].PortStatus
|= USB_PORT_STATUS_CONNECT
;
1107 This
->m_PortStatus
[i
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1110 // is there a status change callback
1112 if (This
->m_SCECallBack
!= NULL
)
1117 This
->m_SCECallBack(This
->m_SCEContext
);
1122 DPRINT1("Device disconnected on port %d\n", i
);
1126 // FIXME: This needs to be saved somewhere
1136 PUSBHARDWAREDEVICE
*OutHardware
)
1138 PUSBHARDWAREDEVICE This
;
1140 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBHardwareDevice(0);
1143 return STATUS_INSUFFICIENT_RESOURCES
;
1148 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1150 return STATUS_SUCCESS
;