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/usbohci/hcd_controller.cpp
5 * PURPOSE: USB OHCI 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
GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
);
63 NTSTATUS
GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
);
64 VOID
HeadEndpointDescriptorModified(ULONG HeadType
);
66 NTSTATUS
GetDMA(OUT
struct IDMAMemoryManager
**m_DmaManager
);
67 NTSTATUS
GetUSBQueue(OUT
struct IUSBQueue
**OutUsbQueue
);
69 NTSTATUS
StartController();
70 NTSTATUS
StopController();
71 NTSTATUS
ResetController();
72 NTSTATUS
ResetPort(ULONG PortIndex
);
74 NTSTATUS
GetPortStatus(ULONG PortId
, OUT USHORT
*PortStatus
, OUT USHORT
*PortChange
);
75 NTSTATUS
ClearPortStatus(ULONG PortId
, ULONG Status
);
76 NTSTATUS
SetPortFeature(ULONG PortId
, ULONG Feature
);
78 VOID
SetStatusChangeEndpointCallBack(PVOID CallBack
, PVOID Context
);
80 KIRQL
AcquireDeviceLock(void);
81 VOID
ReleaseDeviceLock(KIRQL OldLevel
);
83 BOOLEAN
InterruptService();
84 NTSTATUS
InitializeController();
85 NTSTATUS
AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR
*OutDescriptor
);
88 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
89 friend VOID NTAPI
OhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
90 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
91 // constructor / destructor
92 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
93 virtual ~CUSBHardwareDevice(){}
96 LONG m_Ref
; // reference count
97 PDRIVER_OBJECT m_DriverObject
; // driver object
98 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
99 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
100 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
101 KSPIN_LOCK m_Lock
; // hardware lock
102 PKINTERRUPT m_Interrupt
; // interrupt object
103 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
104 PVOID VirtualBase
; // virtual base for memory manager
105 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
106 PULONG m_Base
; // OHCI operational port base registers
107 PDMA_ADAPTER m_Adapter
; // dma adapter object
108 ULONG m_MapRegisters
; // map registers count
109 USHORT m_VendorID
; // vendor id
110 USHORT m_DeviceID
; // device id
111 PUSBQUEUE m_UsbQueue
; // usb request queue
112 POHCIHCCA m_HCCA
; // hcca virtual base
113 PHYSICAL_ADDRESS m_HCCAPhysicalAddress
; // hcca physical address
114 POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor
; // dummy control endpoint descriptor
115 POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor
; // dummy control endpoint descriptor
116 POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor
; // iso endpoint descriptor
117 POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints
[OHCI_STATIC_ENDPOINT_COUNT
]; // endpoints for interrupt / iso transfers
118 ULONG m_NumberOfPorts
; // number of ports
119 OHCI_PORT_STATUS m_PortStatus
[OHCI_MAX_PORT_COUNT
]; // port change status
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
128 //=================================================================================================
133 CUSBHardwareDevice::QueryInterface(
137 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
139 *Output
= PVOID(PUNKNOWN(this));
140 PUNKNOWN(*Output
)->AddRef();
141 return STATUS_SUCCESS
;
144 return STATUS_UNSUCCESSFUL
;
148 CUSBHardwareDevice::Initialize(
149 PDRIVER_OBJECT DriverObject
,
150 PDEVICE_OBJECT FunctionalDeviceObject
,
151 PDEVICE_OBJECT PhysicalDeviceObject
,
152 PDEVICE_OBJECT LowerDeviceObject
)
154 BUS_INTERFACE_STANDARD BusInterface
;
155 PCI_COMMON_CONFIG PciConfig
;
159 DPRINT1("CUSBHardwareDevice::Initialize\n");
162 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
164 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
165 if (!NT_SUCCESS(Status
))
167 DPRINT1("Failed to create DMAMemoryManager Object\n");
172 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
174 Status
= CreateUSBQueue(&m_UsbQueue
);
175 if (!NT_SUCCESS(Status
))
177 DPRINT1("Failed to create UsbQueue!\n");
182 // store device objects
184 m_DriverObject
= DriverObject
;
185 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
186 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
187 m_NextDeviceObject
= LowerDeviceObject
;
190 // initialize device lock
192 KeInitializeSpinLock(&m_Lock
);
195 // intialize status change work item
197 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
202 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
203 if (!NT_SUCCESS(Status
))
205 DPRINT1("Failed to get BusInteface!\n");
209 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
210 PCI_WHICHSPACE_CONFIG
,
213 PCI_COMMON_HDR_LENGTH
);
215 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
217 DPRINT1("Failed to get pci config information!\n");
218 return STATUS_SUCCESS
;
221 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
223 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
226 m_VendorID
= PciConfig
.VendorID
;
227 m_DeviceID
= PciConfig
.DeviceID
;
229 return STATUS_SUCCESS
;
233 CUSBHardwareDevice::PnpStart(
234 PCM_RESOURCE_LIST RawResources
,
235 PCM_RESOURCE_LIST TranslatedResources
)
238 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
239 DEVICE_DESCRIPTION DeviceDescription
;
244 DPRINT1("CUSBHardwareDevice::PnpStart\n");
245 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
248 // get resource descriptor
250 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
252 switch(ResourceDescriptor
->Type
)
254 case CmResourceTypeInterrupt
:
256 KeInitializeDpc(&m_IntDpcObject
,
260 Status
= IoConnectInterrupt(&m_Interrupt
,
261 InterruptServiceRoutine
,
264 ResourceDescriptor
->u
.Interrupt
.Vector
,
265 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
266 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
267 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
268 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
269 ResourceDescriptor
->u
.Interrupt
.Affinity
,
272 if (!NT_SUCCESS(Status
))
275 // failed to register interrupt
277 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
282 case CmResourceTypeMemory
:
287 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
291 // failed to map registers
293 DPRINT1("MmMapIoSpace failed\n");
294 return STATUS_INSUFFICIENT_RESOURCES
;
298 // Get controllers capabilities
300 Version
= READ_REGISTER_ULONG((PULONG
)((ULONG_PTR
)ResourceBase
+ OHCI_REVISION_OFFSET
));
302 DPRINT1("Version %x\n", Version
);
305 // Store Resource base
307 m_Base
= (PULONG
)ResourceBase
;
315 // zero device description
317 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
320 // initialize device description
322 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
323 DeviceDescription
.Master
= TRUE
;
324 DeviceDescription
.ScatterGather
= TRUE
;
325 DeviceDescription
.Dma32BitAddresses
= TRUE
;
326 DeviceDescription
.DmaWidth
= Width32Bits
;
327 DeviceDescription
.InterfaceType
= PCIBus
;
328 DeviceDescription
.MaximumLength
= MAXULONG
;
333 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
337 // failed to get dma adapter
339 DPRINT1("Failed to acquire dma adapter\n");
340 return STATUS_INSUFFICIENT_RESOURCES
;
344 // Create Common Buffer
346 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
352 DPRINT1("Failed to allocate a common buffer\n");
353 return STATUS_INSUFFICIENT_RESOURCES
;
357 // Initialize the DMAMemoryManager
359 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
360 if (!NT_SUCCESS(Status
))
362 DPRINT1("Failed to initialize the DMAMemoryManager\n");
367 // initializes the controller
369 Status
= InitializeController();
370 if (!NT_SUCCESS(Status
))
372 DPRINT1("Failed to Initialize the controller \n");
378 // Initialize the UsbQueue now that we have an AdapterObject.
380 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, NULL
);
381 if (!NT_SUCCESS(Status
))
383 DPRINT1("Failed to Initialize the UsbQueue\n");
389 // Stop the controller before modifying schedules
391 Status
= StopController();
392 if (!NT_SUCCESS(Status
))
394 DPRINT1("Failed to stop the controller \n");
401 // Start the controller
403 DPRINT1("Starting Controller\n");
404 Status
= StartController();
413 CUSBHardwareDevice::PnpStop(void)
416 return STATUS_NOT_IMPLEMENTED
;
420 CUSBHardwareDevice::HandlePower(
424 return STATUS_NOT_IMPLEMENTED
;
428 CUSBHardwareDevice::GetDeviceDetails(
429 OUT OPTIONAL PUSHORT VendorId
,
430 OUT OPTIONAL PUSHORT DeviceId
,
431 OUT OPTIONAL PULONG NumberOfPorts
,
432 OUT OPTIONAL PULONG Speed
)
439 *VendorId
= m_VendorID
;
447 *DeviceId
= m_DeviceID
;
453 // get number of ports
455 *NumberOfPorts
= m_NumberOfPorts
;
466 return STATUS_SUCCESS
;
469 NTSTATUS
CUSBHardwareDevice::GetDMA(
470 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
472 if (!m_MemoryManager
)
473 return STATUS_UNSUCCESSFUL
;
474 *OutDMAMemoryManager
= m_MemoryManager
;
475 return STATUS_SUCCESS
;
479 CUSBHardwareDevice::GetUSBQueue(
480 OUT
struct IUSBQueue
**OutUsbQueue
)
483 return STATUS_UNSUCCESSFUL
;
484 *OutUsbQueue
= m_UsbQueue
;
485 return STATUS_SUCCESS
;
490 CUSBHardwareDevice::StartController(void)
492 ULONG Control
, NumberOfPorts
, Index
, Descriptor
, FrameInterval
, Periodic
, IntervalValue
;
495 // first write address of HCCA
497 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_HCCA_OFFSET
), m_HCCAPhysicalAddress
.LowPart
);
500 // lets write physical address of dummy control endpoint descriptor
502 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_HEAD_ED_OFFSET
), m_ControlEndpointDescriptor
->PhysicalAddress
.LowPart
);
505 // lets write physical address of dummy bulk endpoint descriptor
507 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_BULK_HEAD_ED_OFFSET
), m_BulkEndpointDescriptor
->PhysicalAddress
.LowPart
);
510 // read control register
512 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
517 Control
&= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK
| OHCI_ENABLE_LIST
| OHCI_HC_FUNCTIONAL_STATE_MASK
| OHCI_INTERRUPT_ROUTING
);
520 // set command status flags
522 Control
|= OHCI_ENABLE_LIST
| OHCI_CONTROL_BULK_RATIO_1_4
| OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
;
525 // now start the controller
527 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), Control
);
532 KeStallExecutionProcessor(100);
535 // is the controller started
537 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
540 // assert that the controller has been started
542 ASSERT((Control
& OHCI_HC_FUNCTIONAL_STATE_MASK
) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
);
543 ASSERT((Control
& OHCI_ENABLE_LIST
) == OHCI_ENABLE_LIST
);
546 // get frame interval
548 //FrameInterval = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE;
549 //FrameInterval |= OHCI_FSMPS(IntervalValue) | IntervalValue;
552 // write frame interval
554 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
556 //Periodic = OHCI_PERIODIC(intervalValue);
557 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ 0x40 /*OHCI_PERIODIC_START_OFFSET*/), 0x3E67);
563 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
566 // no over current protection
568 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
| OHCI_RH_NO_OVER_CURRENT_PROTECTION
);
571 // enable power on all ports
573 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_STATUS_OFFSET
), OHCI_RH_LOCAL_POWER_STATUS_CHANGE
);
578 KeStallExecutionProcessor(10);
583 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
);
588 // retrieve number of ports
590 for(Index
= 0; Index
< 10; Index
++)
595 KeStallExecutionProcessor(10);
600 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
603 // get number of ports
605 NumberOfPorts
= OHCI_RH_GET_PORT_COUNT(Descriptor
);
608 // check if we have received the ports
617 ASSERT(NumberOfPorts
< OHCI_MAX_PORT_COUNT
);
620 // store number of ports
622 m_NumberOfPorts
= NumberOfPorts
;
625 // print out number ports
627 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts
);
631 // now enable the interrupts
633 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), OHCI_NORMAL_INTERRUPTS
| OHCI_MASTER_INTERRUPT_ENABLE
);
638 return STATUS_SUCCESS
;
642 CUSBHardwareDevice::AllocateEndpointDescriptor(
643 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutDescriptor
)
645 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
646 PHYSICAL_ADDRESS DescriptorAddress
;
650 // allocate descriptor
652 Status
= m_MemoryManager
->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR
), (PVOID
*)&Descriptor
, &DescriptorAddress
);
653 if (!NT_SUCCESS(Status
))
656 // failed to allocate descriptor
662 // intialize descriptor
664 Descriptor
->Flags
= OHCI_ENDPOINT_SKIP
;
665 Descriptor
->HeadPhysicalDescriptor
= 0;
666 Descriptor
->NextPhysicalEndpoint
= 0;
667 Descriptor
->TailPhysicalDescriptor
= 0;
668 Descriptor
->PhysicalAddress
.QuadPart
= DescriptorAddress
.QuadPart
;
673 *OutDescriptor
= Descriptor
;
678 return STATUS_SUCCESS
;
682 CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
683 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
685 *OutDescriptor
= m_BulkEndpointDescriptor
;
686 return STATUS_SUCCESS
;
690 CUSBHardwareDevice::HeadEndpointDescriptorModified(
693 ULONG Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
695 if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
700 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->NextPhysicalEndpoint);
701 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_CURRENT_ED_OFFSET), m_ControlEndpointDescriptor->NextPhysicalEndpoint);
702 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_CONTROL_LIST_FILLED
);
704 else if (Type
== USB_ENDPOINT_TYPE_BULK
)
709 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_BULK_LIST_FILLED
);
712 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
715 DPRINT1("HeadEndpointDescriptorModified Value %x Type %x\n", Value
, Type
);
720 CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
721 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
723 *OutDescriptor
= m_ControlEndpointDescriptor
;
724 return STATUS_SUCCESS
;
728 CUSBHardwareDevice::InitializeController()
731 ULONG Index
, Interval
, IntervalIndex
, InsertIndex
;
732 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
735 // first allocate the hcca area
737 Status
= m_MemoryManager
->Allocate(sizeof(OHCIHCCA
), (PVOID
*)&m_HCCA
, &m_HCCAPhysicalAddress
);
738 if (!NT_SUCCESS(Status
))
747 // now allocate an endpoint for control transfers
748 // this endpoint will never be removed
750 Status
= AllocateEndpointDescriptor(&m_ControlEndpointDescriptor
);
751 if (!NT_SUCCESS(Status
))
760 // now allocate an endpoint for bulk transfers
761 // this endpoint will never be removed
763 Status
= AllocateEndpointDescriptor(&m_BulkEndpointDescriptor
);
764 if (!NT_SUCCESS(Status
))
773 // now allocate an endpoint for iso transfers
774 // this endpoint will never be removed
776 Status
= AllocateEndpointDescriptor(&m_IsoEndpointDescriptor
);
777 if (!NT_SUCCESS(Status
))
786 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
788 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
791 // allocate endpoint descriptor
793 Status
= AllocateEndpointDescriptor(&Descriptor
);
794 if (!NT_SUCCESS(Status
))
805 m_InterruptEndpoints
[Index
] = Descriptor
;
810 // now link the descriptors, taken from Haiku
812 Interval
= OHCI_BIGGEST_INTERVAL
;
813 IntervalIndex
= OHCI_STATIC_ENDPOINT_COUNT
- 1;
816 InsertIndex
= Interval
/ 2;
817 while (InsertIndex
< OHCI_BIGGEST_INTERVAL
)
820 // assign endpoint address
822 m_HCCA
->InterruptTable
[InsertIndex
] = m_InterruptEndpoints
[IntervalIndex
]->PhysicalAddress
.LowPart
;
823 InsertIndex
+= Interval
;
831 // link all endpoint descriptors to first descriptor in array
833 m_HCCA
->InterruptTable
[0] = m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
834 for (Index
= 1; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
839 m_InterruptEndpoints
[Index
]->NextPhysicalEndpoint
= m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
843 // Now link the first endpoint to the isochronous endpoint
845 m_InterruptEndpoints
[0]->NextPhysicalEndpoint
= m_IsoEndpointDescriptor
->PhysicalAddress
.LowPart
;
850 return STATUS_SUCCESS
;
854 CUSBHardwareDevice::StopController(void)
856 ULONG Control
, Reset
;
860 // first turn off all interrupts
862 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_DISABLE_OFFSET
), OHCI_ALL_INTERRUPTS
);
867 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
870 // FIXME: support routing
872 ASSERT((Control
& OHCI_INTERRUPT_ROUTING
) == 0);
877 KeStallExecutionProcessor(100);
880 // some controllers also depend on this
882 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
887 KeStallExecutionProcessor(100);
890 // now reset controller
892 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), OHCI_HOST_CONTROLLER_RESET
);
895 // reset time is 10ms
897 for(Index
= 0; Index
< 10; Index
++)
902 KeStallExecutionProcessor(10);
905 // read command status
907 Reset
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
910 // was reset bit cleared
912 if ((Reset
& OHCI_HOST_CONTROLLER_RESET
) == 0)
915 // controller completed reset
917 return STATUS_SUCCESS
;
922 // failed to reset controller
924 return STATUS_UNSUCCESSFUL
;
928 CUSBHardwareDevice::ResetController(void)
931 return STATUS_NOT_IMPLEMENTED
;
935 CUSBHardwareDevice::ResetPort(
940 return STATUS_SUCCESS
;
944 CUSBHardwareDevice::GetPortStatus(
946 OUT USHORT
*PortStatus
,
947 OUT USHORT
*PortChange
)
950 // FIXME: should read status from hardware
952 *PortStatus
= m_PortStatus
[PortId
].PortStatus
;
953 *PortChange
= m_PortStatus
[PortId
].PortChange
;
954 return STATUS_SUCCESS
;
958 CUSBHardwareDevice::ClearPortStatus(
962 ULONG Value
, Index
= 0;
964 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
966 if (PortId
> m_NumberOfPorts
)
967 return STATUS_UNSUCCESSFUL
;
969 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
970 KeStallExecutionProcessor(100);
972 if (Status
== C_PORT_RESET
)
979 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
981 if ((Value
& OHCI_RH_PORTSTATUS_PRS
) == 0)
992 KeStallExecutionProcessor(100);
994 //DPRINT1("Value %x Index %lu\n", Value, Index);
999 // check if reset bit is still set
1001 if (Value
& OHCI_RH_PORTSTATUS_PRS
)
1006 DPRINT1("PortId %lu Reset failed\n", PortId
);
1007 return STATUS_UNSUCCESSFUL
;
1013 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRS
) == 0);
1014 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRSC
));
1017 // clear reset bit complete
1019 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRSC
);
1022 // read status register
1024 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1027 // reset complete bit should be cleared
1029 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRSC
) == 0);
1032 // update port status
1034 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_RESET
;
1039 ASSERT((Value
& OHCI_RH_PORTSTATUS_PES
));
1044 m_PortStatus
[PortId
].PortStatus
|= USB_PORT_STATUS_ENABLE
;
1047 // re-enable root hub change
1049 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
));
1050 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), Value
| OHCI_ROOT_HUB_STATUS_CHANGE
);
1054 if (Status
== C_PORT_CONNECTION
)
1059 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_CSC
);
1060 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_CONNECT
;
1065 return STATUS_SUCCESS
;
1070 CUSBHardwareDevice::SetPortFeature(
1076 DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId
, Feature
);
1081 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1084 if (Feature
== PORT_ENABLE
)
1089 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PES
);
1090 return STATUS_SUCCESS
;
1092 else if (Feature
== PORT_POWER
)
1097 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PPS
);
1098 return STATUS_SUCCESS
;
1100 else if (Feature
== PORT_SUSPEND
)
1105 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PSS
);
1106 return STATUS_SUCCESS
;
1108 else if (Feature
== PORT_RESET
)
1113 ASSERT((Value
& OHCI_RH_PORTSTATUS_CCS
));
1118 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRS
);
1123 KeStallExecutionProcessor(100);
1126 // update cached settings
1128 m_PortStatus
[PortId
].PortChange
|= USB_PORT_STATUS_RESET
;
1129 m_PortStatus
[PortId
].PortStatus
&= ~USB_PORT_STATUS_ENABLE
;
1132 // is there a status change callback
1134 if (m_SCECallBack
!= NULL
)
1139 m_SCECallBack(m_SCEContext
);
1142 return STATUS_SUCCESS
;
1148 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1152 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1153 m_SCEContext
= Context
;
1157 CUSBHardwareDevice::AcquireDeviceLock(void)
1164 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
1174 CUSBHardwareDevice::ReleaseDeviceLock(
1177 KeReleaseSpinLock(&m_Lock
, OldLevel
);
1182 InterruptServiceRoutine(
1183 IN PKINTERRUPT Interrupt
,
1184 IN PVOID ServiceContext
)
1186 CUSBHardwareDevice
*This
;
1187 ULONG DoneHead
, Status
, Acknowledge
= 0;
1192 This
= (CUSBHardwareDevice
*) ServiceContext
;
1194 DPRINT1("InterruptServiceRoutine\n");
1199 DoneHead
= This
->m_HCCA
->DoneHead
;
1207 // the interrupt was not caused by DoneHead update
1208 // check if something important happened
1210 Status
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
)) & READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
)) & (~OHCI_WRITEBACK_DONE_HEAD
);
1214 // nothing happened, appears to be shared interrupt
1222 // DoneHead update happened, check if there are other events too
1224 Status
= OHCI_WRITEBACK_DONE_HEAD
;
1227 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1229 if (DoneHead
& OHCI_DONE_INTERRUPTS
)
1234 Status
|= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
)) & READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
));
1241 ASSERT(Status
!= 0);
1243 if (Status
& OHCI_WRITEBACK_DONE_HEAD
)
1248 Acknowledge
|= OHCI_WRITEBACK_DONE_HEAD
;
1251 if (Status
& OHCI_RESUME_DETECTED
)
1256 DPRINT1("InterruptServiceRoutine> Resume\n");
1257 Acknowledge
|= OHCI_RESUME_DETECTED
;
1261 if (Status
& OHCI_UNRECOVERABLE_ERROR
)
1263 DPRINT1("InterruptServiceRoutine> Controller error\n");
1269 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
1272 if (Status
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1275 // new device has arrived
1279 // disable interrupt as it will fire untill the port has been reset
1281 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_DISABLE_OFFSET
), OHCI_ROOT_HUB_STATUS_CHANGE
);
1282 Acknowledge
|= OHCI_ROOT_HUB_STATUS_CHANGE
;
1286 // is there something to acknowledge
1293 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
), Acknowledge
);
1299 DPRINT1("Status %x\n", Status
);
1300 KeInsertQueueDpc(&This
->m_IntDpcObject
, This
, (PVOID
)Status
);
1303 // interrupt handled
1310 OhciDefferedRoutine(
1312 IN PVOID DeferredContext
,
1313 IN PVOID SystemArgument1
,
1314 IN PVOID SystemArgument2
)
1316 CUSBHardwareDevice
*This
;
1317 ULONG CStatus
, Index
, PortStatus
;
1318 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
;
1324 This
= (CUSBHardwareDevice
*) SystemArgument1
;
1325 CStatus
= (ULONG
) SystemArgument2
;
1327 DPRINT1("OhciDefferedRoutine Status %x\n", CStatus
);
1329 if (CStatus
& OHCI_WRITEBACK_DONE_HEAD
)
1332 // descriptor completion, get done head
1334 DoneHead
= This
->m_HCCA
->DoneHead
;
1337 // clear out lower bits, ed are 16 byte aligned
1342 // notify queue of event
1344 This
->m_UsbQueue
->TransferDescriptorCompletionCallback(DoneHead
);
1346 if (CStatus
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1349 // device connected, lets check which port
1351 for(Index
= 0; Index
< This
->m_NumberOfPorts
; Index
++)
1356 PortStatus
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)));
1359 // check if there is a status change
1361 if (PortStatus
& OHCI_RH_PORTSTATUS_CSC
)
1364 // did a device connect
1366 if (PortStatus
& OHCI_RH_PORTSTATUS_CCS
)
1371 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index
, (PortStatus
& OHCI_RH_PORTSTATUS_LSDA
));
1376 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)), OHCI_RH_PORTSTATUS_PES
);
1382 This
->m_PortStatus
[Index
].PortStatus
|= USB_PORT_STATUS_CONNECT
;
1383 This
->m_PortStatus
[Index
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1385 if ((PortStatus
& OHCI_RH_PORTSTATUS_LSDA
))
1388 // low speed device connected
1390 This
->m_PortStatus
[Index
].PortStatus
|= USB_PORT_STATUS_LOW_SPEED
;
1394 // is there a status change callback
1396 if (This
->m_SCECallBack
!= NULL
)
1399 // queue work item for processing
1401 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1407 // device disconnected
1409 DPRINT1("Device disconnected at Port %x\n", Index
);
1420 StatusChangeWorkItemRoutine(
1424 // cast to hardware object
1426 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1429 // is there a callback
1431 if (This
->m_SCECallBack
)
1436 This
->m_SCECallBack(This
->m_SCEContext
);
1443 PUSBHARDWAREDEVICE
*OutHardware
)
1445 PUSBHARDWAREDEVICE This
;
1447 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBHardwareDevice(0);
1450 return STATUS_INSUFFICIENT_RESOURCES
;
1455 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1457 return STATUS_SUCCESS
;