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 NTSTATUS
GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR
*** OutDescriptor
);
65 NTSTATUS
GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
);
66 VOID
HeadEndpointDescriptorModified(ULONG HeadType
);
69 NTSTATUS
GetDMA(OUT
struct IDMAMemoryManager
**m_DmaManager
);
70 NTSTATUS
GetUSBQueue(OUT
struct IUSBQueue
**OutUsbQueue
);
72 NTSTATUS
StartController();
73 NTSTATUS
StopController();
74 NTSTATUS
ResetController();
75 NTSTATUS
ResetPort(ULONG PortIndex
);
77 NTSTATUS
GetPortStatus(ULONG PortId
, OUT USHORT
*PortStatus
, OUT USHORT
*PortChange
);
78 NTSTATUS
ClearPortStatus(ULONG PortId
, ULONG Status
);
79 NTSTATUS
SetPortFeature(ULONG PortId
, ULONG Feature
);
81 VOID
SetStatusChangeEndpointCallBack(PVOID CallBack
, PVOID Context
);
83 KIRQL
AcquireDeviceLock(void);
84 VOID
ReleaseDeviceLock(KIRQL OldLevel
);
85 virtual VOID
GetCurrentFrameNumber(PULONG FrameNumber
);
87 BOOLEAN
InterruptService();
88 NTSTATUS
InitializeController();
89 NTSTATUS
AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR
*OutDescriptor
);
92 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
93 friend VOID NTAPI
OhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
94 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
95 // constructor / destructor
96 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
97 virtual ~CUSBHardwareDevice(){}
100 LONG m_Ref
; // reference count
101 PDRIVER_OBJECT m_DriverObject
; // driver object
102 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
103 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
104 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
105 KSPIN_LOCK m_Lock
; // hardware lock
106 PKINTERRUPT m_Interrupt
; // interrupt object
107 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
108 PVOID VirtualBase
; // virtual base for memory manager
109 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
110 PULONG m_Base
; // OHCI operational port base registers
111 PDMA_ADAPTER m_Adapter
; // dma adapter object
112 ULONG m_MapRegisters
; // map registers count
113 USHORT m_VendorID
; // vendor id
114 USHORT m_DeviceID
; // device id
115 PUSBQUEUE m_UsbQueue
; // usb request queue
116 POHCIHCCA m_HCCA
; // hcca virtual base
117 PHYSICAL_ADDRESS m_HCCAPhysicalAddress
; // hcca physical address
118 POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor
; // dummy control endpoint descriptor
119 POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor
; // dummy control endpoint descriptor
120 POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor
; // iso endpoint descriptor
121 POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints
[OHCI_STATIC_ENDPOINT_COUNT
]; // endpoints for interrupt / iso transfers
122 ULONG m_NumberOfPorts
; // number of ports
123 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
124 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
125 PVOID m_SCEContext
; // status change callback routine context
126 WORK_QUEUE_ITEM m_StatusChangeWorkItem
; // work item for status change callback
127 ULONG m_SyncFramePhysAddr
; // periodic frame list physical address
128 ULONG m_IntervalValue
; // periodic interval value
131 //=================================================================================================
136 CUSBHardwareDevice::QueryInterface(
140 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
142 *Output
= PVOID(PUNKNOWN(this));
143 PUNKNOWN(*Output
)->AddRef();
144 return STATUS_SUCCESS
;
147 return STATUS_UNSUCCESSFUL
;
151 CUSBHardwareDevice::Initialize(
152 PDRIVER_OBJECT DriverObject
,
153 PDEVICE_OBJECT FunctionalDeviceObject
,
154 PDEVICE_OBJECT PhysicalDeviceObject
,
155 PDEVICE_OBJECT LowerDeviceObject
)
157 BUS_INTERFACE_STANDARD BusInterface
;
158 PCI_COMMON_CONFIG PciConfig
;
162 DPRINT("CUSBHardwareDevice::Initialize\n");
165 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
167 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
168 if (!NT_SUCCESS(Status
))
170 DPRINT1("Failed to create DMAMemoryManager Object\n");
175 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
177 Status
= CreateUSBQueue(&m_UsbQueue
);
178 if (!NT_SUCCESS(Status
))
180 DPRINT1("Failed to create UsbQueue!\n");
185 // store device objects
187 m_DriverObject
= DriverObject
;
188 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
189 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
190 m_NextDeviceObject
= LowerDeviceObject
;
193 // initialize device lock
195 KeInitializeSpinLock(&m_Lock
);
198 // intialize status change work item
200 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
205 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
206 if (!NT_SUCCESS(Status
))
208 DPRINT1("Failed to get BusInteface!\n");
212 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
213 PCI_WHICHSPACE_CONFIG
,
216 PCI_COMMON_HDR_LENGTH
);
218 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
220 DPRINT1("Failed to get pci config information!\n");
221 return STATUS_SUCCESS
;
224 m_VendorID
= PciConfig
.VendorID
;
225 m_DeviceID
= PciConfig
.DeviceID
;
227 if (PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
)
232 return STATUS_SUCCESS
;
235 DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n");
237 PciConfig
.Command
|= PCI_ENABLE_BUS_MASTER
;
238 BusInterface
.SetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &PciConfig
, 0, PCI_COMMON_HDR_LENGTH
);
240 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
241 PCI_WHICHSPACE_CONFIG
,
244 PCI_COMMON_HDR_LENGTH
);
246 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
248 DPRINT1("Failed to get pci config information!\n");
250 return STATUS_SUCCESS
;
253 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
255 DPRINT1("Failed to enable master\n");
256 return STATUS_UNSUCCESSFUL
;
259 return STATUS_SUCCESS
;
263 CUSBHardwareDevice::PnpStart(
264 PCM_RESOURCE_LIST RawResources
,
265 PCM_RESOURCE_LIST TranslatedResources
)
268 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
269 DEVICE_DESCRIPTION DeviceDescription
;
274 DPRINT("CUSBHardwareDevice::PnpStart\n");
275 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
278 // get resource descriptor
280 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
282 switch(ResourceDescriptor
->Type
)
284 case CmResourceTypeInterrupt
:
286 KeInitializeDpc(&m_IntDpcObject
,
290 Status
= IoConnectInterrupt(&m_Interrupt
,
291 InterruptServiceRoutine
,
294 ResourceDescriptor
->u
.Interrupt
.Vector
,
295 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
296 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
297 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
298 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
299 ResourceDescriptor
->u
.Interrupt
.Affinity
,
302 if (!NT_SUCCESS(Status
))
305 // failed to register interrupt
307 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
312 case CmResourceTypeMemory
:
317 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
321 // failed to map registers
323 DPRINT1("MmMapIoSpace failed\n");
324 return STATUS_INSUFFICIENT_RESOURCES
;
328 // Get controllers capabilities
330 Version
= READ_REGISTER_ULONG((PULONG
)((ULONG_PTR
)ResourceBase
+ OHCI_REVISION_OFFSET
));
332 DPRINT("Version %x\n", Version
& 0xFFFF);
335 // Store Resource base
337 m_Base
= (PULONG
)ResourceBase
;
345 // zero device description
347 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
350 // initialize device description
352 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
353 DeviceDescription
.Master
= TRUE
;
354 DeviceDescription
.ScatterGather
= TRUE
;
355 DeviceDescription
.Dma32BitAddresses
= TRUE
;
356 DeviceDescription
.DmaWidth
= Width32Bits
;
357 DeviceDescription
.InterfaceType
= PCIBus
;
358 DeviceDescription
.MaximumLength
= MAXULONG
;
363 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
367 // failed to get dma adapter
369 DPRINT1("Failed to acquire dma adapter\n");
370 return STATUS_INSUFFICIENT_RESOURCES
;
374 // Create Common Buffer
376 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
382 DPRINT1("Failed to allocate a common buffer\n");
383 return STATUS_INSUFFICIENT_RESOURCES
;
387 // Initialize the DMAMemoryManager
389 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
390 if (!NT_SUCCESS(Status
))
392 DPRINT1("Failed to initialize the DMAMemoryManager\n");
397 // initializes the controller
399 Status
= InitializeController();
400 if (!NT_SUCCESS(Status
))
402 DPRINT1("Failed to Initialize the controller \n");
407 // Initialize the UsbQueue now that we have an AdapterObject.
409 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, NULL
);
410 if (!NT_SUCCESS(Status
))
412 DPRINT1("Failed to Initialize the UsbQueue\n");
418 // Stop the controller before modifying schedules
420 Status
= StopController();
421 if (!NT_SUCCESS(Status
))
423 DPRINT1("Failed to stop the controller \n");
429 // Start the controller
431 DPRINT1("Starting Controller\n");
432 Status
= StartController();
441 CUSBHardwareDevice::PnpStop(void)
444 return STATUS_NOT_IMPLEMENTED
;
448 CUSBHardwareDevice::HandlePower(
452 return STATUS_NOT_IMPLEMENTED
;
456 CUSBHardwareDevice::GetDeviceDetails(
457 OUT OPTIONAL PUSHORT VendorId
,
458 OUT OPTIONAL PUSHORT DeviceId
,
459 OUT OPTIONAL PULONG NumberOfPorts
,
460 OUT OPTIONAL PULONG Speed
)
467 *VendorId
= m_VendorID
;
475 *DeviceId
= m_DeviceID
;
481 // get number of ports
483 *NumberOfPorts
= m_NumberOfPorts
;
494 return STATUS_SUCCESS
;
497 NTSTATUS
CUSBHardwareDevice::GetDMA(
498 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
500 if (!m_MemoryManager
)
501 return STATUS_UNSUCCESSFUL
;
502 *OutDMAMemoryManager
= m_MemoryManager
;
503 return STATUS_SUCCESS
;
507 CUSBHardwareDevice::GetUSBQueue(
508 OUT
struct IUSBQueue
**OutUsbQueue
)
511 return STATUS_UNSUCCESSFUL
;
512 *OutUsbQueue
= m_UsbQueue
;
513 return STATUS_SUCCESS
;
518 CUSBHardwareDevice::StartController(void)
520 ULONG Control
, NumberOfPorts
, Index
, Descriptor
, FrameInterval
, Periodic
;
523 // lets write physical address of dummy control endpoint descriptor
525 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_HEAD_ED_OFFSET
), m_ControlEndpointDescriptor
->PhysicalAddress
.LowPart
);
528 // lets write physical address of dummy bulk endpoint descriptor
530 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_BULK_HEAD_ED_OFFSET
), m_BulkEndpointDescriptor
->PhysicalAddress
.LowPart
);
533 // get frame interval
535 FrameInterval
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
));
536 FrameInterval
= ((FrameInterval
& OHCI_FRAME_INTERVAL_TOGGLE
) ^ OHCI_FRAME_INTERVAL_TOGGLE
);
537 DPRINT1("FrameInterval %x IntervalValue %x\n", FrameInterval
, m_IntervalValue
);
538 FrameInterval
|= OHCI_FSMPS(m_IntervalValue
) | m_IntervalValue
;
539 DPRINT1("FrameInterval %x\n", FrameInterval
);
542 // write frame interval
544 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
), FrameInterval
);
547 // write address of HCCA
549 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_HCCA_OFFSET
), m_HCCAPhysicalAddress
.LowPart
);
552 // now enable the interrupts
554 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), OHCI_NORMAL_INTERRUPTS
| OHCI_MASTER_INTERRUPT_ENABLE
);
559 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_ENABLE_LIST
);
564 Periodic
= OHCI_PERIODIC(m_IntervalValue
);
565 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_PERIODIC_START_OFFSET
), Periodic
);
566 DPRINT("Periodic Start %x\n", Periodic
);
569 // start the controller
571 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_ENABLE_LIST
| OHCI_CONTROL_BULK_RATIO_1_4
| OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
);
576 KeStallExecutionProcessor(100);
579 // is the controller started
581 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
584 // assert that the controller has been started
586 ASSERT((Control
& OHCI_HC_FUNCTIONAL_STATE_MASK
) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
);
587 ASSERT((Control
& OHCI_ENABLE_LIST
) == OHCI_ENABLE_LIST
);
588 DPRINT1("Control %x\n", Control
);
593 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
596 // no over current protection
598 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
| OHCI_RH_NO_OVER_CURRENT_PROTECTION
);
601 // enable power on all ports
603 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_STATUS_OFFSET
), OHCI_RH_LOCAL_POWER_STATUS_CHANGE
);
608 KeStallExecutionProcessor(10);
613 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
);
616 // retrieve number of ports
618 for(Index
= 0; Index
< 10; Index
++)
623 KeStallExecutionProcessor(10);
628 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
631 // get number of ports
633 NumberOfPorts
= OHCI_RH_GET_PORT_COUNT(Descriptor
);
636 // check if we have received the ports
645 ASSERT(NumberOfPorts
< OHCI_MAX_PORT_COUNT
);
648 // store number of ports
650 m_NumberOfPorts
= NumberOfPorts
;
653 // print out number ports
655 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts
);
661 return STATUS_SUCCESS
;
665 CUSBHardwareDevice::AllocateEndpointDescriptor(
666 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutDescriptor
)
668 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
669 PHYSICAL_ADDRESS DescriptorAddress
;
673 // allocate descriptor
675 Status
= m_MemoryManager
->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR
), (PVOID
*)&Descriptor
, &DescriptorAddress
);
676 if (!NT_SUCCESS(Status
))
679 // failed to allocate descriptor
685 // intialize descriptor
687 Descriptor
->Flags
= OHCI_ENDPOINT_SKIP
;
688 Descriptor
->HeadPhysicalDescriptor
= 0;
689 Descriptor
->NextPhysicalEndpoint
= 0;
690 Descriptor
->TailPhysicalDescriptor
= 0;
691 Descriptor
->PhysicalAddress
.QuadPart
= DescriptorAddress
.QuadPart
;
696 *OutDescriptor
= Descriptor
;
701 return STATUS_SUCCESS
;
705 CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
706 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
708 *OutDescriptor
= m_BulkEndpointDescriptor
;
709 return STATUS_SUCCESS
;
713 CUSBHardwareDevice::GetInterruptEndpointDescriptors(
714 struct _OHCI_ENDPOINT_DESCRIPTOR
*** OutDescriptor
)
716 *OutDescriptor
= m_InterruptEndpoints
;
717 return STATUS_SUCCESS
;
721 CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor(
722 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
727 *OutDescriptor
= m_IsoEndpointDescriptor
;
728 return STATUS_SUCCESS
;
732 CUSBHardwareDevice::HeadEndpointDescriptorModified(
735 ULONG Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
737 if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
742 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_CONTROL_LIST_FILLED
);
744 else if (Type
== USB_ENDPOINT_TYPE_BULK
)
749 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_BULK_LIST_FILLED
);
754 CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
755 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
757 *OutDescriptor
= m_ControlEndpointDescriptor
;
758 return STATUS_SUCCESS
;
762 CUSBHardwareDevice::InitializeController()
765 ULONG Index
, Interval
, IntervalIndex
, InsertIndex
;
766 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
769 // first allocate the hcca area
771 Status
= m_MemoryManager
->Allocate(sizeof(OHCIHCCA
), (PVOID
*)&m_HCCA
, &m_HCCAPhysicalAddress
);
772 if (!NT_SUCCESS(Status
))
781 // now allocate an endpoint for control transfers
782 // this endpoint will never be removed
784 Status
= AllocateEndpointDescriptor(&m_ControlEndpointDescriptor
);
785 if (!NT_SUCCESS(Status
))
794 // now allocate an endpoint for bulk transfers
795 // this endpoint will never be removed
797 Status
= AllocateEndpointDescriptor(&m_BulkEndpointDescriptor
);
798 if (!NT_SUCCESS(Status
))
807 // now allocate an endpoint for iso transfers
808 // this endpoint will never be removed
810 Status
= AllocateEndpointDescriptor(&m_IsoEndpointDescriptor
);
811 if (!NT_SUCCESS(Status
))
820 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
822 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
825 // allocate endpoint descriptor
827 Status
= AllocateEndpointDescriptor(&Descriptor
);
828 if (!NT_SUCCESS(Status
))
839 m_InterruptEndpoints
[Index
] = Descriptor
;
844 // now link the descriptors, taken from Haiku
846 Interval
= OHCI_BIGGEST_INTERVAL
;
847 IntervalIndex
= OHCI_STATIC_ENDPOINT_COUNT
- 1;
850 InsertIndex
= Interval
/ 2;
851 while (InsertIndex
< OHCI_BIGGEST_INTERVAL
)
854 // assign endpoint address
856 m_HCCA
->InterruptTable
[InsertIndex
] = m_InterruptEndpoints
[IntervalIndex
]->PhysicalAddress
.LowPart
;
857 InsertIndex
+= Interval
;
865 // link all endpoint descriptors to first descriptor in array
867 m_HCCA
->InterruptTable
[0] = m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
868 for (Index
= 1; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
873 m_InterruptEndpoints
[Index
]->NextPhysicalEndpoint
= m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
877 // Now link the first endpoint to the isochronous endpoint
879 m_InterruptEndpoints
[0]->NextPhysicalEndpoint
= m_IsoEndpointDescriptor
->PhysicalAddress
.LowPart
;
882 // set iso endpoint type
884 m_IsoEndpointDescriptor
->Flags
|= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT
;
889 return STATUS_SUCCESS
;
893 CUSBHardwareDevice::StopController(void)
895 ULONG Control
, Reset
, Status
;
896 ULONG Index
, FrameInterval
;
901 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_HCCA_OFFSET
), 0xFFFFFFFF);
902 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_HCCA_OFFSET
));
903 //ASSERT((m_HCCAPhysicalAddress.QuadPart & Control) == Control);
909 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
911 if ((Control
& OHCI_INTERRUPT_ROUTING
))
914 // read command status
916 Status
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
921 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Status
| OHCI_OWNERSHIP_CHANGE_REQUEST
);
922 for(Index
= 0; Index
< 100; Index
++)
927 KeStallExecutionProcessor(100);
932 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
933 if (!(Control
& OHCI_INTERRUPT_ROUTING
))
936 // acquired ownership
943 // if the ownership is still not changed, perform reset
945 if (Control
& OHCI_INTERRUPT_ROUTING
)
947 DPRINT1("SMM not responding\n");
951 DPRINT("SMM has given up ownership\n");
957 // read contents of control register
959 Control
= (READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
)) & OHCI_HC_FUNCTIONAL_STATE_MASK
);
960 DPRINT("Controller State %x\n", Control
);
962 if (Control
!= OHCI_HC_FUNCTIONAL_STATE_RESET
)
965 // OHCI 5.1.1.3.4, no SMM, BIOS active
967 if (Control
!= OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
)
972 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESUME
);
977 // wait untill its resumed
979 KeStallExecutionProcessor(10);
982 // check control register
984 Control
= (READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
)) & OHCI_HC_FUNCTIONAL_STATE_MASK
);
985 if (Control
& OHCI_HC_FUNCTIONAL_STATE_RESUME
)
994 // check for time outs
999 DPRINT1("Failed to resume controller\n");
1008 // 5.1.1.3.5 OHCI, no SMM, no BIOS
1013 // some controllers also depend on this
1015 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
1019 // wait untill its reset
1021 KeStallExecutionProcessor(10);
1024 // check control register
1026 Control
= (READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
)) & OHCI_HC_FUNCTIONAL_STATE_MASK
);
1027 if (Control
== OHCI_HC_FUNCTIONAL_STATE_RESET
)
1036 // check for time outs
1041 DPRINT1("Failed to reset controller\n");
1050 // read from interval
1052 FrameInterval
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
));
1055 // store interval value for later
1057 m_IntervalValue
= OHCI_GET_INTERVAL_VALUE(FrameInterval
);
1059 DPRINT1("FrameInterval %x Interval %x\n", FrameInterval
, m_IntervalValue
);
1062 // now reset controller
1064 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), OHCI_HOST_CONTROLLER_RESET
);
1067 // reset time is 10ms
1069 for(Index
= 0; Index
< 10; Index
++)
1074 KeStallExecutionProcessor(10);
1077 // read command status
1079 Reset
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
1082 // was reset bit cleared
1084 if ((Reset
& OHCI_HOST_CONTROLLER_RESET
) == 0)
1087 // restore the frame interval register
1089 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
), FrameInterval
);
1092 // controller completed reset
1094 return STATUS_SUCCESS
;
1099 // failed to reset controller
1101 return STATUS_UNSUCCESSFUL
;
1105 CUSBHardwareDevice::ResetController(void)
1108 return STATUS_NOT_IMPLEMENTED
;
1112 CUSBHardwareDevice::ResetPort(
1117 return STATUS_SUCCESS
;
1121 CUSBHardwareDevice::GetPortStatus(
1123 OUT USHORT
*PortStatus
,
1124 OUT USHORT
*PortChange
)
1128 if (PortId
> m_NumberOfPorts
)
1129 return STATUS_UNSUCCESSFUL
;
1131 // init result variables
1138 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1139 DPRINT("GetPortStatus PortId %x Value %x\n", PortId
, Value
);
1142 if (Value
& OHCI_RH_PORTSTATUS_CCS
)
1143 *PortStatus
|= USB_PORT_STATUS_CONNECT
;
1145 // did a device connect?
1146 if (Value
& OHCI_RH_PORTSTATUS_CSC
)
1147 *PortChange
|= USB_PORT_STATUS_CONNECT
;
1150 if (Value
& OHCI_RH_PORTSTATUS_PES
)
1151 *PortStatus
|= USB_PORT_STATUS_ENABLE
;
1153 // port disconnect or hardware error
1154 if (Value
& OHCI_RH_PORTSTATUS_PESC
)
1155 *PortChange
|= USB_PORT_STATUS_CONNECT
;
1158 if (Value
& OHCI_RH_PORTSTATUS_PSS
)
1159 *PortStatus
|= USB_PORT_STATUS_SUSPEND
;
1162 if (Value
& OHCI_RH_PORTSTATUS_PSSC
)
1163 *PortChange
|= USB_PORT_STATUS_ENABLE
;
1165 // port reset started (change bit only set at completion)
1166 if (Value
& OHCI_RH_PORTSTATUS_PRS
)
1168 *PortStatus
|= USB_PORT_STATUS_RESET
;
1169 *PortChange
|= USB_PORT_STATUS_RESET
;
1172 // port reset ended (change bit only set at completion)
1173 if (Value
& OHCI_RH_PORTSTATUS_PRSC
)
1174 *PortChange
|= USB_PORT_STATUS_RESET
;
1177 if (Value
& OHCI_RH_PORTSTATUS_LSDA
)
1178 *PortStatus
|= USB_PORT_STATUS_LOW_SPEED
;
1180 return STATUS_SUCCESS
;
1184 CUSBHardwareDevice::ClearPortStatus(
1190 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
1192 if (PortId
> m_NumberOfPorts
)
1193 return STATUS_UNSUCCESSFUL
;
1195 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1197 if (Status
== C_PORT_RESET
)
1202 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRSC
));
1205 // clear reset bit complete
1207 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRSC
);
1212 ASSERT((Value
& OHCI_RH_PORTSTATUS_PES
));
1215 if (Status
== C_PORT_CONNECTION
|| Status
== C_PORT_ENABLE
)
1218 // clear change bits
1220 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_CSC
| OHCI_RH_PORTSTATUS_PESC
);
1223 // wait for port to stabilize
1225 if (Status
== C_PORT_CONNECTION
&& (Value
& OHCI_RH_PORTSTATUS_CCS
))
1227 LARGE_INTEGER Timeout
;
1232 Timeout
.QuadPart
= 100;
1233 DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout
.LowPart
);
1236 // convert to 100 ns units (absolute)
1238 Timeout
.QuadPart
*= -10000;
1243 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1248 // re-enable root hub change
1250 DPRINT1("Enabling status change\n");
1251 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), OHCI_ROOT_HUB_STATUS_CHANGE
);
1253 return STATUS_SUCCESS
;
1258 CUSBHardwareDevice::SetPortFeature(
1264 DPRINT("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId
, Feature
);
1269 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1272 if (Feature
== PORT_ENABLE
)
1277 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PES
);
1278 return STATUS_SUCCESS
;
1280 else if (Feature
== PORT_POWER
)
1282 LARGE_INTEGER Timeout
;
1287 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PPS
);
1290 // read descriptor A for the delay data
1292 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
1295 // compute the delay
1297 Timeout
.QuadPart
= OHCI_RH_GET_POWER_ON_TO_POWER_GOOD_TIME(Value
);
1300 // delay is multiplied by 2 ms
1302 Timeout
.QuadPart
*= 2;
1303 DPRINT1("Waiting %d milliseconds for port power up\n", Timeout
.LowPart
);
1306 // convert to 100 ns units (absolute)
1308 Timeout
.QuadPart
*= -10000;
1313 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
1315 return STATUS_SUCCESS
;
1317 else if (Feature
== PORT_SUSPEND
)
1322 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PSS
);
1323 return STATUS_SUCCESS
;
1325 else if (Feature
== PORT_RESET
)
1330 ASSERT((Value
& OHCI_RH_PORTSTATUS_CCS
));
1335 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRS
);
1342 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1344 if ((Value
& OHCI_RH_PORTSTATUS_PRS
) == 0 &&
1345 (Value
& OHCI_RH_PORTSTATUS_PRSC
) != 0)
1348 // reset is complete
1356 KeStallExecutionProcessor(100);
1359 if (m_SCECallBack
!= NULL
)
1364 m_SCECallBack(m_SCEContext
);
1366 return STATUS_SUCCESS
;
1368 return STATUS_SUCCESS
;
1374 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1378 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1379 m_SCEContext
= Context
;
1383 CUSBHardwareDevice::AcquireDeviceLock(void)
1390 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
1399 CUSBHardwareDevice::GetCurrentFrameNumber(
1406 Number
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_NUMBER_OFFSET
));
1407 DPRINT("FrameNumberInterval %x Frame %x\n", Number
, m_HCCA
->CurrentFrameNumber
);
1410 // remove reserved bits
1415 // store frame number
1417 *FrameNumber
= Number
;
1420 // is the controller started
1422 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
1423 ASSERT((Control
& OHCI_ENABLE_LIST
) == OHCI_ENABLE_LIST
);
1429 CUSBHardwareDevice::ReleaseDeviceLock(
1432 KeReleaseSpinLock(&m_Lock
, OldLevel
);
1437 InterruptServiceRoutine(
1438 IN PKINTERRUPT Interrupt
,
1439 IN PVOID ServiceContext
)
1441 CUSBHardwareDevice
*This
;
1442 ULONG DoneHead
, Status
, Acknowledge
= 0;
1447 This
= (CUSBHardwareDevice
*) ServiceContext
;
1449 DPRINT("InterruptServiceRoutine\n");
1454 DoneHead
= This
->m_HCCA
->DoneHead
;
1462 // the interrupt was not caused by DoneHead update
1463 // check if something important happened
1465 DPRINT("InterruptStatus %x InterruptEnable %x\n", READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
)),
1466 READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
)));
1467 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
);
1471 // nothing happened, appears to be shared interrupt
1479 // DoneHead update happened, check if there are other events too
1481 Status
= OHCI_WRITEBACK_DONE_HEAD
;
1484 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1486 if (DoneHead
& OHCI_DONE_INTERRUPTS
)
1491 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
));
1498 ASSERT(Status
!= 0);
1500 if (Status
& OHCI_WRITEBACK_DONE_HEAD
)
1505 Acknowledge
|= OHCI_WRITEBACK_DONE_HEAD
;
1506 This
->m_HCCA
->DoneHead
= 0;
1509 if (Status
& OHCI_RESUME_DETECTED
)
1514 DPRINT1("InterruptServiceRoutine> Resume\n");
1515 Acknowledge
|= OHCI_RESUME_DETECTED
;
1519 if (Status
& OHCI_UNRECOVERABLE_ERROR
)
1521 DPRINT1("InterruptServiceRoutine> Controller error\n");
1527 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
1530 if (Status
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1533 // disable interrupt as it will fire untill the port has been reset
1535 DPRINT1("Disabling status change interrupt\n");
1536 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_DISABLE_OFFSET
), OHCI_ROOT_HUB_STATUS_CHANGE
);
1537 Acknowledge
|= OHCI_ROOT_HUB_STATUS_CHANGE
;
1541 // is there something to acknowledge
1548 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
), Acknowledge
);
1554 DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status
, Acknowledge
, This
->m_HCCA
->CurrentFrameNumber
);
1555 KeInsertQueueDpc(&This
->m_IntDpcObject
, (PVOID
)Status
, (PVOID
)(DoneHead
& ~1));
1558 // interrupt handled
1565 OhciDefferedRoutine(
1567 IN PVOID DeferredContext
,
1568 IN PVOID SystemArgument1
,
1569 IN PVOID SystemArgument2
)
1571 CUSBHardwareDevice
*This
;
1572 ULONG CStatus
, Index
, PortStatus
;
1573 ULONG DoneHead
, QueueSCEWorkItem
;
1578 This
= (CUSBHardwareDevice
*)DeferredContext
;
1579 CStatus
= (ULONG
) SystemArgument1
;
1580 DoneHead
= (ULONG
)SystemArgument2
;
1582 DPRINT("OhciDefferedRoutine Status %x\n", CStatus
);
1584 if (CStatus
& OHCI_WRITEBACK_DONE_HEAD
)
1587 // notify queue of event
1589 This
->m_UsbQueue
->TransferDescriptorCompletionCallback(DoneHead
);
1591 if (CStatus
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1594 // device connected, lets check which port
1596 QueueSCEWorkItem
= FALSE
;
1597 for(Index
= 0; Index
< This
->m_NumberOfPorts
; Index
++)
1602 PortStatus
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)));
1605 // check if there is a status change
1607 if (PortStatus
& OHCI_RH_PORTSTATUS_CSC
)
1610 // did a device connect
1612 if (PortStatus
& OHCI_RH_PORTSTATUS_CCS
)
1617 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index
, (PortStatus
& OHCI_RH_PORTSTATUS_LSDA
));
1622 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)), OHCI_RH_PORTSTATUS_PES
);
1627 // device disconnected
1629 DPRINT1("Device disconnected at Port %x\n", Index
);
1635 QueueSCEWorkItem
= TRUE
;
1637 else if (PortStatus
& OHCI_RH_PORTSTATUS_PRSC
)
1640 // This is a port reset complete interrupt
1642 DPRINT1("Port %d completed reset\n", Index
);
1645 // Queue a work item
1647 QueueSCEWorkItem
= TRUE
;
1652 // is there a status change callback and a device connected / disconnected
1654 if (QueueSCEWorkItem
&& This
->m_SCECallBack
!= NULL
)
1657 // queue work item for processing
1659 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1666 StatusChangeWorkItemRoutine(
1670 // cast to hardware object
1672 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1675 // is there a callback
1677 if (This
->m_SCECallBack
)
1682 This
->m_SCECallBack(This
->m_SCEContext
);
1689 PUSBHARDWAREDEVICE
*OutHardware
)
1691 PUSBHARDWAREDEVICE This
;
1693 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBHardwareDevice(0);
1696 return STATUS_INSUFFICIENT_RESOURCES
;
1701 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1703 return STATUS_SUCCESS
;