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 BOOLEAN
InterruptService();
87 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
88 friend VOID NTAPI
EhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
89 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
90 // constructor / destructor
91 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
92 virtual ~CUSBHardwareDevice(){}
95 LONG m_Ref
; // reference count
96 PDRIVER_OBJECT m_DriverObject
; // driver object
97 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
98 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
99 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
100 KSPIN_LOCK m_Lock
; // hardware lock
101 PKINTERRUPT m_Interrupt
; // interrupt object
102 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
103 PVOID VirtualBase
; // virtual base for memory manager
104 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
105 PULONG m_Base
; // EHCI operational port base registers
106 PDMA_ADAPTER m_Adapter
; // dma adapter object
107 ULONG m_MapRegisters
; // map registers count
108 EHCI_CAPS m_Capabilities
; // EHCI caps
109 USHORT m_VendorID
; // vendor id
110 USHORT m_DeviceID
; // device id
111 PQUEUE_HEAD AsyncQueueHead
; // async queue head terminator
112 PUSBQUEUE m_UsbQueue
; // usb request queue
113 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
114 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
115 PVOID m_SCEContext
; // status change callback routine context
116 BOOLEAN m_DoorBellRingInProgress
; // door bell ring in progress
117 EHCI_PORT_STATUS m_PortStatus
[16]; // port status
118 WORK_QUEUE_ITEM m_StatusChangeWorkItem
; // work item for status change callback
119 ULONG m_SyncFramePhysAddr
; // periodic frame list physical address
122 VOID
SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
125 VOID
GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
);
128 ULONG
EHCI_READ_REGISTER_ULONG(ULONG Offset
);
131 VOID
EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
);
134 //=================================================================================================
139 CUSBHardwareDevice::QueryInterface(
143 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
145 *Output
= PVOID(PUNKNOWN(this));
146 PUNKNOWN(*Output
)->AddRef();
147 return STATUS_SUCCESS
;
150 return STATUS_UNSUCCESSFUL
;
154 CUSBHardwareDevice::Initialize(
155 PDRIVER_OBJECT DriverObject
,
156 PDEVICE_OBJECT FunctionalDeviceObject
,
157 PDEVICE_OBJECT PhysicalDeviceObject
,
158 PDEVICE_OBJECT LowerDeviceObject
)
160 BUS_INTERFACE_STANDARD BusInterface
;
161 PCI_COMMON_CONFIG PciConfig
;
165 DPRINT1("CUSBHardwareDevice::Initialize\n");
168 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
170 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
171 if (!NT_SUCCESS(Status
))
173 DPRINT1("Failed to create DMAMemoryManager Object\n");
178 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
180 Status
= CreateUSBQueue(&m_UsbQueue
);
181 if (!NT_SUCCESS(Status
))
183 DPRINT1("Failed to create UsbQueue!\n");
188 // store device objects
190 m_DriverObject
= DriverObject
;
191 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
192 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
193 m_NextDeviceObject
= LowerDeviceObject
;
196 // initialize device lock
198 KeInitializeSpinLock(&m_Lock
);
201 // intialize status change work item
203 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
208 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
209 if (!NT_SUCCESS(Status
))
211 DPRINT1("Failed to get BusInteface!\n");
215 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
216 PCI_WHICHSPACE_CONFIG
,
219 PCI_COMMON_HDR_LENGTH
);
221 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
223 DPRINT1("Failed to get pci config information!\n");
224 return STATUS_SUCCESS
;
227 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
229 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
232 m_VendorID
= PciConfig
.VendorID
;
233 m_DeviceID
= PciConfig
.DeviceID
;
235 return STATUS_SUCCESS
;
239 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
242 Register
= (PULONG
)UsbCmd
;
243 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
), *Register
);
247 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd
)
250 Register
= (PULONG
)UsbCmd
;
251 *Register
= READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ EHCI_USBCMD
));
255 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset
)
257 return READ_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
));
261 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset
, ULONG Value
)
263 WRITE_REGISTER_ULONG((PULONG
)((ULONG
)m_Base
+ Offset
), Value
);
267 CUSBHardwareDevice::PnpStart(
268 PCM_RESOURCE_LIST RawResources
,
269 PCM_RESOURCE_LIST TranslatedResources
)
272 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
273 DEVICE_DESCRIPTION DeviceDescription
;
274 PHYSICAL_ADDRESS AsyncPhysicalAddress
;
278 DPRINT1("CUSBHardwareDevice::PnpStart\n");
279 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
282 // get resource descriptor
284 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
286 switch(ResourceDescriptor
->Type
)
288 case CmResourceTypeInterrupt
:
290 KeInitializeDpc(&m_IntDpcObject
,
294 Status
= IoConnectInterrupt(&m_Interrupt
,
295 InterruptServiceRoutine
,
298 ResourceDescriptor
->u
.Interrupt
.Vector
,
299 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
300 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
301 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
302 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
303 ResourceDescriptor
->u
.Interrupt
.Affinity
,
306 if (!NT_SUCCESS(Status
))
309 // failed to register interrupt
311 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
316 case CmResourceTypeMemory
:
321 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
325 // failed to map registers
327 DPRINT1("MmMapIoSpace failed\n");
328 return STATUS_INSUFFICIENT_RESOURCES
;
332 // Get controllers capabilities
334 m_Capabilities
.Length
= READ_REGISTER_UCHAR((PUCHAR
)ResourceBase
);
335 m_Capabilities
.HCIVersion
= READ_REGISTER_USHORT((PUSHORT
)((ULONG
)ResourceBase
+ 2));
336 m_Capabilities
.HCSParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ 4));
337 m_Capabilities
.HCCParamsLong
= READ_REGISTER_ULONG((PULONG
)((ULONG
)ResourceBase
+ 8));
339 DPRINT1("Controller has %d Ports\n", m_Capabilities
.HCSParams
.PortCount
);
340 DPRINT1("Controller EHCI Version %x\n", m_Capabilities
.HCIVersion
);
341 if (m_Capabilities
.HCSParams
.PortRouteRules
)
343 for (Count
= 0; Count
< m_Capabilities
.HCSParams
.PortCount
; Count
++)
345 m_Capabilities
.PortRoute
[Count
] = READ_REGISTER_UCHAR((PUCHAR
)(ULONG
)ResourceBase
+ 12 + Count
);
350 // Set m_Base to the address of Operational Register Space
352 m_Base
= (PULONG
)((ULONG
)ResourceBase
+ m_Capabilities
.Length
);
360 // zero device description
362 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
365 // initialize device description
367 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
368 DeviceDescription
.Master
= TRUE
;
369 DeviceDescription
.ScatterGather
= TRUE
;
370 DeviceDescription
.Dma32BitAddresses
= TRUE
;
371 DeviceDescription
.DmaWidth
= Width32Bits
;
372 DeviceDescription
.InterfaceType
= PCIBus
;
373 DeviceDescription
.MaximumLength
= MAXULONG
;
378 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
382 // failed to get dma adapter
384 DPRINT1("Failed to acquire dma adapter\n");
385 return STATUS_INSUFFICIENT_RESOURCES
;
389 // Create Common Buffer
391 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
397 DPRINT1("Failed to allocate a common buffer\n");
398 return STATUS_INSUFFICIENT_RESOURCES
;
402 // Stop the controller before modifying schedules
404 Status
= StopController();
405 if (!NT_SUCCESS(Status
))
409 // Initialize the DMAMemoryManager
411 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
412 if (!NT_SUCCESS(Status
))
414 DPRINT1("Failed to initialize the DMAMemoryManager\n");
419 // Create a queuehead for the Async Register
421 m_MemoryManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&AsyncQueueHead
, &AsyncPhysicalAddress
);
423 AsyncQueueHead
->AlternateNextPointer
= TERMINATE_POINTER
;
424 AsyncQueueHead
->NextPointer
= TERMINATE_POINTER
;
425 AsyncQueueHead
->PhysicalAddr
= AsyncPhysicalAddress
.LowPart
;
426 AsyncQueueHead
->HorizontalLinkPointer
= AsyncQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
427 AsyncQueueHead
->EndPointCharacteristics
.QEDTDataToggleControl
= FALSE
;
428 AsyncQueueHead
->Token
.Bits
.InterruptOnComplete
= FALSE
;
429 AsyncQueueHead
->EndPointCharacteristics
.HeadOfReclamation
= TRUE
;
430 AsyncQueueHead
->Token
.Bits
.Halted
= TRUE
;
431 AsyncQueueHead
->EndPointCharacteristics
.MaximumPacketLength
= 64;
432 AsyncQueueHead
->EndPointCharacteristics
.NakCountReload
= 0;
433 AsyncQueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
434 AsyncQueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x03;
436 InitializeListHead(&AsyncQueueHead
->LinkedQueueHeads
);
439 // Initialize the UsbQueue now that we have an AdapterObject.
441 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, &m_Lock
);
442 if (!NT_SUCCESS(Status
))
444 DPRINT1("Failed to Initialize the UsbQueue\n");
449 // Start the controller
451 DPRINT1("Starting Controller\n");
452 Status
= StartController();
461 CUSBHardwareDevice::PnpStop(void)
464 return STATUS_NOT_IMPLEMENTED
;
468 CUSBHardwareDevice::HandlePower(
472 return STATUS_NOT_IMPLEMENTED
;
476 CUSBHardwareDevice::GetDeviceDetails(
477 OUT OPTIONAL PUSHORT VendorId
,
478 OUT OPTIONAL PUSHORT DeviceId
,
479 OUT OPTIONAL PULONG NumberOfPorts
,
480 OUT OPTIONAL PULONG Speed
)
483 *VendorId
= m_VendorID
;
485 *DeviceId
= m_DeviceID
;
487 *NumberOfPorts
= m_Capabilities
.HCSParams
.PortCount
;
488 //FIXME: What to returned here?
491 return STATUS_SUCCESS
;
494 NTSTATUS
CUSBHardwareDevice::GetDMA(
495 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
497 if (!m_MemoryManager
)
498 return STATUS_UNSUCCESSFUL
;
499 *OutDMAMemoryManager
= m_MemoryManager
;
500 return STATUS_SUCCESS
;
504 CUSBHardwareDevice::GetUSBQueue(
505 OUT
struct IUSBQueue
**OutUsbQueue
)
508 return STATUS_UNSUCCESSFUL
;
509 *OutUsbQueue
= m_UsbQueue
;
510 return STATUS_SUCCESS
;
515 CUSBHardwareDevice::StartController(void)
517 EHCI_USBCMD_CONTENT UsbCmd
;
518 ULONG UsbSts
, FailSafe
;
521 // Stop the controller if its running
523 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
524 if (!(UsbSts
& EHCI_STS_HALT
))
528 // Reset the device. Bit is set to 0 on completion.
530 GetCommandRegister(&UsbCmd
);
531 UsbCmd
.HCReset
= TRUE
;
532 SetCommandRegister(&UsbCmd
);
535 // Check that the controller reset
537 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
539 KeStallExecutionProcessor(10);
540 GetCommandRegister(&UsbCmd
);
548 // If the controller did not reset then fail
552 DPRINT1("EHCI ERROR: Controller failed to reset. Hardware problem!\n");
553 return STATUS_UNSUCCESSFUL
;
557 // Disable Interrupts and clear status
559 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, 0);
560 EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, 0x0000001f);
563 // Assign the AsyncList Register
565 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, AsyncQueueHead
->PhysicalAddr
);
568 // Assign the SyncList Register
570 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE
, m_SyncFramePhysAddr
);
573 // Set Schedules to Enable and Interrupt Threshold to 1ms.
575 GetCommandRegister(&UsbCmd
);
576 UsbCmd
.PeriodicEnable
= TRUE
;
577 UsbCmd
.AsyncEnable
= TRUE
; //FIXME: Need USB Memory Manager
579 UsbCmd
.IntThreshold
= 1;
580 // FIXME: Set framelistsize when periodic is implemented.
581 SetCommandRegister(&UsbCmd
);
584 // Enable Interrupts and start execution
586 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR
, EHCI_USBINTR_INTE
| EHCI_USBINTR_ERR
| EHCI_USBINTR_ASYNC
| EHCI_USBINTR_HSERR
587 /*| EHCI_USBINTR_FLROVR*/ | EHCI_USBINTR_PC
);
590 SetCommandRegister(&UsbCmd
);
593 // Wait for execution to start
595 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
597 KeStallExecutionProcessor(10);
598 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
600 if (!(UsbSts
& EHCI_STS_HALT
))
606 if (UsbSts
& EHCI_STS_HALT
)
608 DPRINT1("Could not start execution on the controller\n");
609 return STATUS_UNSUCCESSFUL
;
613 // Set port routing to EHCI controller
615 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG
, 1);
617 DPRINT1("EHCI Started!\n");
618 return STATUS_SUCCESS
;
622 CUSBHardwareDevice::StopController(void)
624 EHCI_USBCMD_CONTENT UsbCmd
;
625 ULONG UsbSts
, FailSafe
;
628 // Disable Interrupts and stop execution
630 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR
, 0);
632 GetCommandRegister(&UsbCmd
);
634 SetCommandRegister(&UsbCmd
);
636 for (FailSafe
= 100; FailSafe
> 1; FailSafe
--)
638 KeStallExecutionProcessor(10);
639 UsbSts
= EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
640 if (UsbSts
& EHCI_STS_HALT
)
646 if (!(UsbSts
& EHCI_STS_HALT
))
648 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
649 return STATUS_UNSUCCESSFUL
;
652 return STATUS_SUCCESS
;
656 CUSBHardwareDevice::ResetController(void)
659 return STATUS_NOT_IMPLEMENTED
;
663 CUSBHardwareDevice::ResetPort(
668 if (PortIndex
> m_Capabilities
.HCSParams
.PortCount
)
669 return STATUS_UNSUCCESSFUL
;
671 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
672 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
674 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
675 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), EHCI_PRT_RELEASEOWNERSHIP
);
676 return STATUS_DEVICE_NOT_CONNECTED
;
680 // Reset and clean enable
682 PortStatus
|= EHCI_PRT_RESET
;
683 PortStatus
&= ~EHCI_PRT_ENABLED
;
684 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
686 KeStallExecutionProcessor(100);
691 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
692 PortStatus
&= ~EHCI_PRT_RESET
;
693 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
), PortStatus
);
695 KeStallExecutionProcessor(100);
698 // Check that the port reset
700 PortStatus
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortIndex
));
701 if (PortStatus
& EHCI_PRT_RESET
)
703 DPRINT1("Port did not reset\n");
707 return STATUS_SUCCESS
;
711 CUSBHardwareDevice::GetPortStatus(
713 OUT USHORT
*PortStatus
,
714 OUT USHORT
*PortChange
)
718 USHORT Status
= 0, Change
= 0;
720 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
721 return STATUS_UNSUCCESSFUL
;
724 // Get the value of the Port Status and Control Register
726 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
729 // If the PowerPortControl is 0 then host controller does not have power control switches
730 if (!m_Capabilities
.HCSParams
.PortPowerControl
)
732 Status
|= USB_PORT_STATUS_POWER
;
736 // Check the value of PortPower
737 if (Value
& EHCI_PRT_POWER
)
739 Status
|= USB_PORT_STATUS_POWER
;
743 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
744 if (Value
& EHCI_PRT_SLOWSPEEDLINE
)
745 Status
|= USB_PORT_STATUS_LOW_SPEED
;
747 Status
|= USB_PORT_STATUS_HIGH_SPEED
;
749 // Get Connected Status
750 if (Value
& EHCI_PRT_CONNECTED
)
751 Status
|= USB_PORT_STATUS_CONNECT
;
753 // Get Enabled Status
754 if (Value
& EHCI_PRT_ENABLED
)
755 Status
|= USB_PORT_STATUS_ENABLE
;
758 if (Value
& EHCI_PRT_SUSPEND
)
759 Status
|= USB_PORT_STATUS_SUSPEND
;
761 // a overcurrent is active?
762 if (Value
& EHCI_PRT_OVERCURRENTACTIVE
)
763 Status
|= USB_PORT_STATUS_OVER_CURRENT
;
766 if (Value
& EHCI_PRT_RESET
)
767 Status
|= USB_PORT_STATUS_RESET
;
770 // FIXME: Is the Change here correct?
772 if (Value
& EHCI_PRT_CONNECTSTATUSCHANGE
)
773 Change
|= USB_PORT_STATUS_CONNECT
;
775 if (Value
& EHCI_PRT_ENABLEDSTATUSCHANGE
)
776 Change
|= USB_PORT_STATUS_ENABLE
;
778 *PortStatus
= Status
;
779 *PortChange
= Change
;
781 *PortStatus
= m_PortStatus
[PortId
].PortStatus
;
782 *PortChange
= m_PortStatus
[PortId
].PortChange
;
784 return STATUS_SUCCESS
;
788 CUSBHardwareDevice::ClearPortStatus(
794 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
796 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
797 return STATUS_UNSUCCESSFUL
;
799 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
801 if (Status
== C_PORT_RESET
)
803 if (Value
& EHCI_PRT_RESET
)
805 Value
&= ~EHCI_PRT_RESET
;
806 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
807 KeStallExecutionProcessor(100);
810 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
812 // update port status
814 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_RESET
;
815 if (Value
& EHCI_PRT_ENABLED
)
816 m_PortStatus
[PortId
].PortStatus
|= USB_PORT_STATUS_ENABLE
;
819 DPRINT1("Port is not enabled.\n");
823 if (Status
== C_PORT_CONNECTION
)
825 // FIXME: Make sure its the Connection and Enable Change status.
826 Value
|= EHCI_PRT_CONNECTSTATUSCHANGE
;
827 Value
|= EHCI_PRT_ENABLEDSTATUSCHANGE
;
828 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
), Value
);
830 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_CONNECT
;
833 return STATUS_SUCCESS
;
838 CUSBHardwareDevice::SetPortFeature(
844 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
846 if (PortId
> m_Capabilities
.HCSParams
.PortCount
)
847 return STATUS_UNSUCCESSFUL
;
849 Value
= EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * PortId
));
851 if (Feature
== PORT_ENABLE
)
854 // FIXME: EHCI Ports can only be disabled via reset
856 DPRINT1("PORT_ENABLE not supported for EHCI\n");
859 if (Feature
== PORT_RESET
)
861 if (Value
& EHCI_PRT_SLOWSPEEDLINE
)
863 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
869 // update cached settings
871 m_PortStatus
[PortId
].PortChange
|= USB_PORT_STATUS_RESET
;
872 m_PortStatus
[PortId
].PortStatus
&= ~USB_PORT_STATUS_ENABLE
;
875 // is there a status change callback
877 if (m_SCECallBack
!= NULL
)
882 m_SCECallBack(m_SCEContext
);
886 if (Feature
== PORT_POWER
)
887 DPRINT1("PORT_POWER Not implemented\n");
889 return STATUS_SUCCESS
;
893 CUSBHardwareDevice::SetAsyncListRegister(
894 ULONG PhysicalAddress
)
896 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE
, PhysicalAddress
);
900 CUSBHardwareDevice::SetPeriodicListRegister(
901 ULONG PhysicalAddress
)
904 // store physical address
906 m_SyncFramePhysAddr
= PhysicalAddress
;
910 CUSBHardwareDevice::GetAsyncListQueueHead()
912 return AsyncQueueHead
;
915 ULONG
CUSBHardwareDevice::GetPeriodicListRegister()
921 VOID
CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
925 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
926 m_SCEContext
= Context
;
930 CUSBHardwareDevice::AcquireDeviceLock(void)
937 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
947 CUSBHardwareDevice::ReleaseDeviceLock(
950 KeReleaseSpinLock(&m_Lock
, OldLevel
);
955 InterruptServiceRoutine(
956 IN PKINTERRUPT Interrupt
,
957 IN PVOID ServiceContext
)
959 CUSBHardwareDevice
*This
;
962 This
= (CUSBHardwareDevice
*) ServiceContext
;
963 CStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS
);
965 CStatus
&= (EHCI_ERROR_INT
| EHCI_STS_INT
| EHCI_STS_IAA
| EHCI_STS_PCD
| EHCI_STS_FLR
);
967 // Check that it belongs to EHCI
975 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS
, CStatus
);
977 if (CStatus
& EHCI_STS_FATAL
)
979 This
->StopController();
980 DPRINT1("EHCI: Host System Error!\n");
984 if (CStatus
& EHCI_ERROR_INT
)
986 DPRINT1("EHCI Status = 0x%x\n", CStatus
);
989 if (CStatus
& EHCI_STS_HALT
)
991 DPRINT1("Host Error Unexpected Halt\n");
992 // FIXME: Reset controller\n");
996 KeInsertQueueDpc(&This
->m_IntDpcObject
, This
, (PVOID
)CStatus
);
1001 EhciDefferedRoutine(
1003 IN PVOID DeferredContext
,
1004 IN PVOID SystemArgument1
,
1005 IN PVOID SystemArgument2
)
1007 CUSBHardwareDevice
*This
;
1008 ULONG CStatus
, PortStatus
, PortCount
, i
, ShouldRingDoorBell
;
1009 NTSTATUS Status
= STATUS_SUCCESS
;
1010 EHCI_USBCMD_CONTENT UsbCmd
;
1012 This
= (CUSBHardwareDevice
*) SystemArgument1
;
1013 CStatus
= (ULONG
) SystemArgument2
;
1017 // check for completion of async schedule
1019 if (CStatus
& (EHCI_STS_RECL
| EHCI_STS_INT
| EHCI_ERROR_INT
))
1022 // check if there is a door bell ring in progress
1024 if (This
->m_DoorBellRingInProgress
== FALSE
)
1026 if (CStatus
& EHCI_ERROR_INT
)
1029 // controller reported error
1031 Status
= STATUS_UNSUCCESSFUL
;
1036 // inform IUSBQueue of a completed queue head
1038 This
->m_UsbQueue
->InterruptCallback(Status
, &ShouldRingDoorBell
);
1041 // was a queue head completed?
1043 if (ShouldRingDoorBell
)
1046 // set door ring bell in progress status flag
1048 This
->m_DoorBellRingInProgress
= TRUE
;
1051 // get command register
1053 This
->GetCommandRegister(&UsbCmd
);
1056 // set door rang bell bit
1058 UsbCmd
.DoorBell
= TRUE
;
1061 // update command status
1063 This
->SetCommandRegister(&UsbCmd
);
1069 // check if the controller has acknowledged the door bell
1071 if (CStatus
& EHCI_STS_IAA
)
1074 // controller has acknowledged, assert we rang the bell
1076 PC_ASSERT(This
->m_DoorBellRingInProgress
== TRUE
);
1079 // now notify IUSBQueue that it can free completed requests
1081 This
->m_UsbQueue
->CompleteAsyncRequests();
1084 // door ring bell completed
1086 This
->m_DoorBellRingInProgress
= FALSE
;
1089 This
->GetDeviceDetails(NULL
, NULL
, &PortCount
, NULL
);
1090 if (CStatus
& EHCI_STS_PCD
)
1092 for (i
= 0; i
< PortCount
; i
++)
1094 PortStatus
= This
->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
));
1097 // Device connected or removed
1099 if (PortStatus
& EHCI_PRT_CONNECTSTATUSCHANGE
)
1102 // Clear the port change status
1104 //This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_CONNECTSTATUSCHANGE);
1106 if (PortStatus
& EHCI_PRT_CONNECTED
)
1108 DPRINT1("Device connected on port %d\n", i
);
1111 //FIXME: Determine device speed
1113 if (This
->m_Capabilities
.HCSParams
.CHCCount
)
1115 if (PortStatus
& EHCI_PRT_ENABLED
)
1117 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1120 if (PortStatus
& EHCI_PRT_SLOWSPEEDLINE
)
1122 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1123 This
->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC
+ (4 * i
), EHCI_PRT_RELEASEOWNERSHIP
);
1129 // update port status flags
1131 This
->m_PortStatus
[i
].PortStatus
|= USB_PORT_STATUS_HIGH_SPEED
;
1132 This
->m_PortStatus
[i
].PortStatus
|= USB_PORT_STATUS_CONNECT
;
1133 This
->m_PortStatus
[i
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1137 DPRINT1("Device disconnected on port %d\n", i
);
1140 // update port status flags
1142 This
->m_PortStatus
[i
].PortStatus
&= ~USB_PORT_STATUS_HIGH_SPEED
;
1143 This
->m_PortStatus
[i
].PortStatus
&= ~USB_PORT_STATUS_CONNECT
;
1144 This
->m_PortStatus
[i
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1148 // is there a status change callback
1150 if (This
->m_SCECallBack
!= NULL
)
1153 // queue work item for processing
1155 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1159 // FIXME: This needs to be saved somewhere
1169 StatusChangeWorkItemRoutine(
1173 // cast to hardware object
1175 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1178 // is there a callback
1180 if (This
->m_SCECallBack
)
1185 This
->m_SCECallBack(This
->m_SCEContext
);
1192 PUSBHARDWAREDEVICE
*OutHardware
)
1194 PUSBHARDWAREDEVICE This
;
1196 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBHardwareDevice(0);
1199 return STATUS_INSUFFICIENT_RESOURCES
;
1204 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1206 return STATUS_SUCCESS
;