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 OHCI_PORT_STATUS m_PortStatus
[OHCI_MAX_PORT_COUNT
]; // port change status
124 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
125 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
126 PVOID m_SCEContext
; // status change callback routine context
127 WORK_QUEUE_ITEM m_StatusChangeWorkItem
; // work item for status change callback
128 ULONG m_SyncFramePhysAddr
; // periodic frame list physical address
129 ULONG m_IntervalValue
; // periodic interval value
132 //=================================================================================================
137 CUSBHardwareDevice::QueryInterface(
141 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
143 *Output
= PVOID(PUNKNOWN(this));
144 PUNKNOWN(*Output
)->AddRef();
145 return STATUS_SUCCESS
;
148 return STATUS_UNSUCCESSFUL
;
152 CUSBHardwareDevice::Initialize(
153 PDRIVER_OBJECT DriverObject
,
154 PDEVICE_OBJECT FunctionalDeviceObject
,
155 PDEVICE_OBJECT PhysicalDeviceObject
,
156 PDEVICE_OBJECT LowerDeviceObject
)
158 BUS_INTERFACE_STANDARD BusInterface
;
159 PCI_COMMON_CONFIG PciConfig
;
163 DPRINT1("CUSBHardwareDevice::Initialize\n");
166 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
168 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
169 if (!NT_SUCCESS(Status
))
171 DPRINT1("Failed to create DMAMemoryManager Object\n");
176 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
178 Status
= CreateUSBQueue(&m_UsbQueue
);
179 if (!NT_SUCCESS(Status
))
181 DPRINT1("Failed to create UsbQueue!\n");
186 // store device objects
188 m_DriverObject
= DriverObject
;
189 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
190 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
191 m_NextDeviceObject
= LowerDeviceObject
;
194 // initialize device lock
196 KeInitializeSpinLock(&m_Lock
);
199 // intialize status change work item
201 ExInitializeWorkItem(&m_StatusChangeWorkItem
, StatusChangeWorkItemRoutine
, PVOID(this));
206 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
207 if (!NT_SUCCESS(Status
))
209 DPRINT1("Failed to get BusInteface!\n");
213 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
214 PCI_WHICHSPACE_CONFIG
,
217 PCI_COMMON_HDR_LENGTH
);
219 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
221 DPRINT1("Failed to get pci config information!\n");
222 return STATUS_SUCCESS
;
225 if (!(PciConfig
.Command
& PCI_ENABLE_BUS_MASTER
))
227 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
230 m_VendorID
= PciConfig
.VendorID
;
231 m_DeviceID
= PciConfig
.DeviceID
;
233 return STATUS_SUCCESS
;
237 CUSBHardwareDevice::PnpStart(
238 PCM_RESOURCE_LIST RawResources
,
239 PCM_RESOURCE_LIST TranslatedResources
)
242 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
243 DEVICE_DESCRIPTION DeviceDescription
;
248 DPRINT1("CUSBHardwareDevice::PnpStart\n");
249 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
252 // get resource descriptor
254 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
256 switch(ResourceDescriptor
->Type
)
258 case CmResourceTypeInterrupt
:
260 KeInitializeDpc(&m_IntDpcObject
,
264 Status
= IoConnectInterrupt(&m_Interrupt
,
265 InterruptServiceRoutine
,
268 ResourceDescriptor
->u
.Interrupt
.Vector
,
269 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
270 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
271 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
272 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
273 ResourceDescriptor
->u
.Interrupt
.Affinity
,
276 if (!NT_SUCCESS(Status
))
279 // failed to register interrupt
281 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
286 case CmResourceTypeMemory
:
291 ResourceBase
= MmMapIoSpace(ResourceDescriptor
->u
.Memory
.Start
, ResourceDescriptor
->u
.Memory
.Length
, MmNonCached
);
295 // failed to map registers
297 DPRINT1("MmMapIoSpace failed\n");
298 return STATUS_INSUFFICIENT_RESOURCES
;
302 // Get controllers capabilities
304 Version
= READ_REGISTER_ULONG((PULONG
)((ULONG_PTR
)ResourceBase
+ OHCI_REVISION_OFFSET
));
306 DPRINT1("Version %x\n", Version
);
309 // Store Resource base
311 m_Base
= (PULONG
)ResourceBase
;
319 // zero device description
321 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
324 // initialize device description
326 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
327 DeviceDescription
.Master
= TRUE
;
328 DeviceDescription
.ScatterGather
= TRUE
;
329 DeviceDescription
.Dma32BitAddresses
= TRUE
;
330 DeviceDescription
.DmaWidth
= Width32Bits
;
331 DeviceDescription
.InterfaceType
= PCIBus
;
332 DeviceDescription
.MaximumLength
= MAXULONG
;
337 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
341 // failed to get dma adapter
343 DPRINT1("Failed to acquire dma adapter\n");
344 return STATUS_INSUFFICIENT_RESOURCES
;
348 // Create Common Buffer
350 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
356 DPRINT1("Failed to allocate a common buffer\n");
357 return STATUS_INSUFFICIENT_RESOURCES
;
361 // Initialize the DMAMemoryManager
363 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
364 if (!NT_SUCCESS(Status
))
366 DPRINT1("Failed to initialize the DMAMemoryManager\n");
371 // initializes the controller
373 Status
= InitializeController();
374 if (!NT_SUCCESS(Status
))
376 DPRINT1("Failed to Initialize the controller \n");
382 // Initialize the UsbQueue now that we have an AdapterObject.
384 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, NULL
);
385 if (!NT_SUCCESS(Status
))
387 DPRINT1("Failed to Initialize the UsbQueue\n");
393 // Stop the controller before modifying schedules
395 Status
= StopController();
396 if (!NT_SUCCESS(Status
))
398 DPRINT1("Failed to stop the controller \n");
405 // Start the controller
407 DPRINT1("Starting Controller\n");
408 Status
= StartController();
417 CUSBHardwareDevice::PnpStop(void)
420 return STATUS_NOT_IMPLEMENTED
;
424 CUSBHardwareDevice::HandlePower(
428 return STATUS_NOT_IMPLEMENTED
;
432 CUSBHardwareDevice::GetDeviceDetails(
433 OUT OPTIONAL PUSHORT VendorId
,
434 OUT OPTIONAL PUSHORT DeviceId
,
435 OUT OPTIONAL PULONG NumberOfPorts
,
436 OUT OPTIONAL PULONG Speed
)
443 *VendorId
= m_VendorID
;
451 *DeviceId
= m_DeviceID
;
457 // get number of ports
459 *NumberOfPorts
= m_NumberOfPorts
;
470 return STATUS_SUCCESS
;
473 NTSTATUS
CUSBHardwareDevice::GetDMA(
474 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
476 if (!m_MemoryManager
)
477 return STATUS_UNSUCCESSFUL
;
478 *OutDMAMemoryManager
= m_MemoryManager
;
479 return STATUS_SUCCESS
;
483 CUSBHardwareDevice::GetUSBQueue(
484 OUT
struct IUSBQueue
**OutUsbQueue
)
487 return STATUS_UNSUCCESSFUL
;
488 *OutUsbQueue
= m_UsbQueue
;
489 return STATUS_SUCCESS
;
494 CUSBHardwareDevice::StartController(void)
496 ULONG Control
, NumberOfPorts
, Index
, Descriptor
, FrameInterval
, Periodic
;
499 // first write address of HCCA
501 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_HCCA_OFFSET
), m_HCCAPhysicalAddress
.LowPart
);
504 // lets write physical address of dummy control endpoint descriptor
506 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_HEAD_ED_OFFSET
), m_ControlEndpointDescriptor
->PhysicalAddress
.LowPart
);
509 // lets write physical address of dummy bulk endpoint descriptor
511 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_BULK_HEAD_ED_OFFSET
), m_BulkEndpointDescriptor
->PhysicalAddress
.LowPart
);
514 // read control register
516 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
521 Control
&= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK
| OHCI_ENABLE_LIST
| OHCI_HC_FUNCTIONAL_STATE_MASK
| OHCI_INTERRUPT_ROUTING
);
524 // set command status flags
526 Control
|= OHCI_ENABLE_LIST
| OHCI_CONTROL_BULK_RATIO_1_4
| OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
;
529 // now start the controller
531 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), Control
);
536 KeStallExecutionProcessor(100);
539 // is the controller started
541 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
544 // assert that the controller has been started
546 ASSERT((Control
& OHCI_HC_FUNCTIONAL_STATE_MASK
) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL
);
547 ASSERT((Control
& OHCI_ENABLE_LIST
) == OHCI_ENABLE_LIST
);
548 DPRINT1("Control %x\n", Control
);
551 // get frame interval
553 FrameInterval
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
));
554 FrameInterval
= ((FrameInterval
& OHCI_FRAME_INTERVAL_TOGGLE
) ^ OHCI_FRAME_INTERVAL_TOGGLE
);
555 DPRINT1("FrameInterval %x IntervalValue %x\n", FrameInterval
, m_IntervalValue
);
556 FrameInterval
|= OHCI_FSMPS(m_IntervalValue
) | m_IntervalValue
;
557 DPRINT1("FrameInterval %x\n", FrameInterval
);
560 // write frame interval
562 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
), FrameInterval
);
567 Periodic
= OHCI_PERIODIC(m_IntervalValue
);
568 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_PERIODIC_START_OFFSET
), Periodic
);
569 DPRINT1("Periodic Start %x\n", Periodic
);
574 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
577 // no over current protection
579 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
| OHCI_RH_NO_OVER_CURRENT_PROTECTION
);
582 // enable power on all ports
584 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_STATUS_OFFSET
), OHCI_RH_LOCAL_POWER_STATUS_CHANGE
);
589 KeStallExecutionProcessor(10);
594 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
), Descriptor
);
599 // retrieve number of ports
601 for(Index
= 0; Index
< 10; Index
++)
606 KeStallExecutionProcessor(10);
611 Descriptor
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_DESCRIPTOR_A_OFFSET
));
614 // get number of ports
616 NumberOfPorts
= OHCI_RH_GET_PORT_COUNT(Descriptor
);
619 // check if we have received the ports
628 ASSERT(NumberOfPorts
< OHCI_MAX_PORT_COUNT
);
631 // store number of ports
633 m_NumberOfPorts
= NumberOfPorts
;
636 // print out number ports
638 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts
);
642 // now enable the interrupts
644 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), OHCI_NORMAL_INTERRUPTS
| OHCI_MASTER_INTERRUPT_ENABLE
);
649 return STATUS_SUCCESS
;
653 CUSBHardwareDevice::AllocateEndpointDescriptor(
654 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutDescriptor
)
656 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
657 PHYSICAL_ADDRESS DescriptorAddress
;
661 // allocate descriptor
663 Status
= m_MemoryManager
->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR
), (PVOID
*)&Descriptor
, &DescriptorAddress
);
664 if (!NT_SUCCESS(Status
))
667 // failed to allocate descriptor
673 // intialize descriptor
675 Descriptor
->Flags
= OHCI_ENDPOINT_SKIP
;
676 Descriptor
->HeadPhysicalDescriptor
= 0;
677 Descriptor
->NextPhysicalEndpoint
= 0;
678 Descriptor
->TailPhysicalDescriptor
= 0;
679 Descriptor
->PhysicalAddress
.QuadPart
= DescriptorAddress
.QuadPart
;
684 *OutDescriptor
= Descriptor
;
689 return STATUS_SUCCESS
;
693 CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
694 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
696 *OutDescriptor
= m_BulkEndpointDescriptor
;
697 return STATUS_SUCCESS
;
701 CUSBHardwareDevice::GetInterruptEndpointDescriptors(
702 struct _OHCI_ENDPOINT_DESCRIPTOR
*** OutDescriptor
)
704 *OutDescriptor
= m_InterruptEndpoints
;
705 return STATUS_SUCCESS
;
709 CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor(
710 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
715 *OutDescriptor
= m_IsoEndpointDescriptor
;
716 return STATUS_SUCCESS
;
720 CUSBHardwareDevice::HeadEndpointDescriptorModified(
723 ULONG Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
725 if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
730 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_CONTROL_LIST_FILLED
);
732 else if (Type
== USB_ENDPOINT_TYPE_BULK
)
737 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Value
| OHCI_BULK_LIST_FILLED
);
742 CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
743 struct _OHCI_ENDPOINT_DESCRIPTOR
** OutDescriptor
)
745 *OutDescriptor
= m_ControlEndpointDescriptor
;
746 return STATUS_SUCCESS
;
750 CUSBHardwareDevice::InitializeController()
753 ULONG Index
, Interval
, IntervalIndex
, InsertIndex
;
754 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
757 // first allocate the hcca area
759 Status
= m_MemoryManager
->Allocate(sizeof(OHCIHCCA
), (PVOID
*)&m_HCCA
, &m_HCCAPhysicalAddress
);
760 if (!NT_SUCCESS(Status
))
769 // now allocate an endpoint for control transfers
770 // this endpoint will never be removed
772 Status
= AllocateEndpointDescriptor(&m_ControlEndpointDescriptor
);
773 if (!NT_SUCCESS(Status
))
782 // now allocate an endpoint for bulk transfers
783 // this endpoint will never be removed
785 Status
= AllocateEndpointDescriptor(&m_BulkEndpointDescriptor
);
786 if (!NT_SUCCESS(Status
))
795 // now allocate an endpoint for iso transfers
796 // this endpoint will never be removed
798 Status
= AllocateEndpointDescriptor(&m_IsoEndpointDescriptor
);
799 if (!NT_SUCCESS(Status
))
808 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
810 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
813 // allocate endpoint descriptor
815 Status
= AllocateEndpointDescriptor(&Descriptor
);
816 if (!NT_SUCCESS(Status
))
827 m_InterruptEndpoints
[Index
] = Descriptor
;
832 // now link the descriptors, taken from Haiku
834 Interval
= OHCI_BIGGEST_INTERVAL
;
835 IntervalIndex
= OHCI_STATIC_ENDPOINT_COUNT
- 1;
838 InsertIndex
= Interval
/ 2;
839 while (InsertIndex
< OHCI_BIGGEST_INTERVAL
)
842 // assign endpoint address
844 m_HCCA
->InterruptTable
[InsertIndex
] = m_InterruptEndpoints
[IntervalIndex
]->PhysicalAddress
.LowPart
;
845 InsertIndex
+= Interval
;
853 // link all endpoint descriptors to first descriptor in array
855 m_HCCA
->InterruptTable
[0] = m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
856 for (Index
= 1; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
861 m_InterruptEndpoints
[Index
]->NextPhysicalEndpoint
= m_InterruptEndpoints
[0]->PhysicalAddress
.LowPart
;
865 // Now link the first endpoint to the isochronous endpoint
867 m_InterruptEndpoints
[0]->NextPhysicalEndpoint
= m_IsoEndpointDescriptor
->PhysicalAddress
.LowPart
;
870 // set iso endpoint type
872 m_IsoEndpointDescriptor
->Flags
|= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT
;
877 return STATUS_SUCCESS
;
881 CUSBHardwareDevice::StopController(void)
883 ULONG Control
, Reset
, Status
;
884 ULONG Index
, FrameInterval
;
887 // first turn off all interrupts
889 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_DISABLE_OFFSET
), OHCI_ALL_INTERRUPTS
);
894 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
897 if ((Control
& OHCI_INTERRUPT_ROUTING
))
900 // read command status
902 Status
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
907 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), Status
| OHCI_OWNERSHIP_CHANGE_REQUEST
);
908 for(Index
= 0; Index
< 100; Index
++)
913 KeStallExecutionProcessor(100);
918 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
919 if (!(Control
& OHCI_INTERRUPT_ROUTING
))
922 // acquired ownership
929 // if the ownership is still not changed, perform reset
931 if (Control
& OHCI_INTERRUPT_ROUTING
)
933 DPRINT1("SMM not responding\n");
935 // some controllers also depend on this
937 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
942 KeStallExecutionProcessor(100);
950 KeStallExecutionProcessor(100);
953 // some controllers also depend on this
955 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
960 KeStallExecutionProcessor(100);
963 // read from interval
965 FrameInterval
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
));
968 // store interval value for later
970 m_IntervalValue
= OHCI_GET_INTERVAL_VALUE(FrameInterval
);
972 DPRINT1("FrameInterval %x Interval %x\n", FrameInterval
, m_IntervalValue
);
975 // now reset controller
977 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
), OHCI_HOST_CONTROLLER_RESET
);
980 // reset time is 10ms
982 for(Index
= 0; Index
< 10; Index
++)
987 KeStallExecutionProcessor(10);
990 // read command status
992 Reset
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_COMMAND_STATUS_OFFSET
));
995 // was reset bit cleared
997 if ((Reset
& OHCI_HOST_CONTROLLER_RESET
) == 0)
1000 // restore the frame interval register
1002 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_OFFSET
), FrameInterval
);
1005 // controller completed reset
1007 return STATUS_SUCCESS
;
1012 // failed to reset controller
1014 return STATUS_UNSUCCESSFUL
;
1018 CUSBHardwareDevice::ResetController(void)
1021 return STATUS_NOT_IMPLEMENTED
;
1025 CUSBHardwareDevice::ResetPort(
1030 return STATUS_SUCCESS
;
1034 CUSBHardwareDevice::GetPortStatus(
1036 OUT USHORT
*PortStatus
,
1037 OUT USHORT
*PortChange
)
1041 if (PortId
> m_NumberOfPorts
)
1042 return STATUS_UNSUCCESSFUL
;
1044 // init result variables
1051 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1052 DPRINT("GetPortStatus PortId %x Value %x\n", PortId
, Value
);
1056 if (Value
& OHCI_RH_PORTSTATUS_CCS
)
1057 *PortStatus
|= USB_PORT_STATUS_CONNECT
;
1059 // did a device connect?
1060 if (Value
& OHCI_RH_PORTSTATUS_CSC
)
1061 *PortChange
|= USB_PORT_STATUS_CONNECT
;
1064 if (Value
& OHCI_RH_PORTSTATUS_PES
)
1065 *PortStatus
|= USB_PORT_STATUS_ENABLE
;
1068 if (Value
& OHCI_RH_PORTSTATUS_PESC
)
1069 *PortChange
|= USB_PORT_STATUS_ENABLE
;
1072 if (Value
& OHCI_RH_PORTSTATUS_PSS
)
1073 *PortStatus
|= USB_PORT_STATUS_SUSPEND
;
1076 if (Value
& OHCI_RH_PORTSTATUS_PSSC
)
1077 *PortChange
|= USB_PORT_STATUS_ENABLE
;
1080 if (Value
& OHCI_RH_PORTSTATUS_PSS
)
1081 *PortStatus
|= USB_PORT_STATUS_RESET
;
1084 if (Value
& OHCI_RH_PORTSTATUS_PRSC
)
1085 *PortChange
|= USB_PORT_STATUS_RESET
;
1087 return STATUS_SUCCESS
;
1091 CUSBHardwareDevice::ClearPortStatus(
1097 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Status
);
1099 if (PortId
> m_NumberOfPorts
)
1100 return STATUS_UNSUCCESSFUL
;
1102 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1103 KeStallExecutionProcessor(100);
1105 if (Status
== C_PORT_RESET
)
1112 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1114 if ((Value
& OHCI_RH_PORTSTATUS_PRS
) == 0)
1117 // reset is complete
1125 KeStallExecutionProcessor(100);
1127 //DPRINT1("Value %x Index %lu\n", Value, Index);
1132 // check if reset bit is still set
1134 if (Value
& OHCI_RH_PORTSTATUS_PRS
)
1139 DPRINT1("PortId %lu Reset failed\n", PortId
);
1140 return STATUS_UNSUCCESSFUL
;
1146 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRS
) == 0);
1147 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRSC
));
1150 // clear reset bit complete
1152 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRSC
);
1155 // read status register
1157 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1160 // reset complete bit should be cleared
1162 ASSERT((Value
& OHCI_RH_PORTSTATUS_PRSC
) == 0);
1165 // update port status
1167 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_RESET
;
1172 ASSERT((Value
& OHCI_RH_PORTSTATUS_PES
));
1177 m_PortStatus
[PortId
].PortStatus
|= USB_PORT_STATUS_ENABLE
;
1180 // re-enable root hub change
1182 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
));
1183 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_INTERRUPT_ENABLE_OFFSET
), Value
| OHCI_ROOT_HUB_STATUS_CHANGE
);
1187 if (Status
== C_PORT_CONNECTION
)
1192 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_CSC
);
1193 m_PortStatus
[PortId
].PortChange
&= ~USB_PORT_STATUS_CONNECT
;
1198 return STATUS_SUCCESS
;
1203 CUSBHardwareDevice::SetPortFeature(
1209 DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId
, Feature
);
1214 Value
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)));
1217 if (Feature
== PORT_ENABLE
)
1222 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PES
);
1223 return STATUS_SUCCESS
;
1225 else if (Feature
== PORT_POWER
)
1230 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PPS
);
1231 return STATUS_SUCCESS
;
1233 else if (Feature
== PORT_SUSPEND
)
1238 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PSS
);
1239 return STATUS_SUCCESS
;
1241 else if (Feature
== PORT_RESET
)
1246 ASSERT((Value
& OHCI_RH_PORTSTATUS_CCS
));
1251 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_RH_PORT_STATUS(PortId
)), OHCI_RH_PORTSTATUS_PRS
);
1256 KeStallExecutionProcessor(100);
1259 // update cached settings
1261 m_PortStatus
[PortId
].PortChange
|= USB_PORT_STATUS_RESET
;
1262 m_PortStatus
[PortId
].PortStatus
&= ~USB_PORT_STATUS_ENABLE
;
1265 // is there a status change callback
1267 if (m_SCECallBack
!= NULL
)
1272 m_SCECallBack(m_SCEContext
);
1275 return STATUS_SUCCESS
;
1281 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1285 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1286 m_SCEContext
= Context
;
1290 CUSBHardwareDevice::AcquireDeviceLock(void)
1297 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
1306 CUSBHardwareDevice::GetCurrentFrameNumber(
1313 Number
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_FRAME_INTERVAL_NUMBER_OFFSET
));
1314 DPRINT1("FrameNumberInterval %x Frame %x\n", Number
, m_HCCA
->CurrentFrameNumber
);
1317 // remove reserved bits
1322 // store frame number
1324 *FrameNumber
= Number
;
1327 // is the controller started
1329 Control
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)m_Base
+ OHCI_CONTROL_OFFSET
));
1330 ASSERT((Control
& OHCI_ENABLE_LIST
) == OHCI_ENABLE_LIST
);
1336 CUSBHardwareDevice::ReleaseDeviceLock(
1339 KeReleaseSpinLock(&m_Lock
, OldLevel
);
1344 InterruptServiceRoutine(
1345 IN PKINTERRUPT Interrupt
,
1346 IN PVOID ServiceContext
)
1348 CUSBHardwareDevice
*This
;
1349 ULONG DoneHead
, Status
, Acknowledge
= 0;
1354 This
= (CUSBHardwareDevice
*) ServiceContext
;
1356 DPRINT("InterruptServiceRoutine\n");
1361 DoneHead
= This
->m_HCCA
->DoneHead
;
1369 // the interrupt was not caused by DoneHead update
1370 // check if something important happened
1372 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
);
1376 // nothing happened, appears to be shared interrupt
1384 // DoneHead update happened, check if there are other events too
1386 Status
= OHCI_WRITEBACK_DONE_HEAD
;
1389 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1391 if (DoneHead
& OHCI_DONE_INTERRUPTS
)
1396 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
));
1403 ASSERT(Status
!= 0);
1405 if (Status
& OHCI_WRITEBACK_DONE_HEAD
)
1410 Acknowledge
|= OHCI_WRITEBACK_DONE_HEAD
;
1411 This
->m_HCCA
->DoneHead
= 0;
1414 if (Status
& OHCI_RESUME_DETECTED
)
1419 DPRINT1("InterruptServiceRoutine> Resume\n");
1420 Acknowledge
|= OHCI_RESUME_DETECTED
;
1424 if (Status
& OHCI_UNRECOVERABLE_ERROR
)
1426 DPRINT1("InterruptServiceRoutine> Controller error\n");
1432 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_CONTROL_OFFSET
), OHCI_HC_FUNCTIONAL_STATE_RESET
);
1435 if (Status
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1438 // new device has arrived
1442 // disable interrupt as it will fire untill the port has been reset
1444 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_DISABLE_OFFSET
), OHCI_ROOT_HUB_STATUS_CHANGE
);
1445 Acknowledge
|= OHCI_ROOT_HUB_STATUS_CHANGE
;
1449 // is there something to acknowledge
1456 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_INTERRUPT_STATUS_OFFSET
), Acknowledge
);
1462 DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status
, Acknowledge
, This
->m_HCCA
->CurrentFrameNumber
);
1463 KeInsertQueueDpc(&This
->m_IntDpcObject
, (PVOID
)Status
, (PVOID
)(DoneHead
& ~1));
1466 // interrupt handled
1473 OhciDefferedRoutine(
1475 IN PVOID DeferredContext
,
1476 IN PVOID SystemArgument1
,
1477 IN PVOID SystemArgument2
)
1479 CUSBHardwareDevice
*This
;
1480 ULONG CStatus
, Index
, PortStatus
;
1486 This
= (CUSBHardwareDevice
*)DeferredContext
;
1487 CStatus
= (ULONG
) SystemArgument1
;
1488 DoneHead
= (ULONG
)SystemArgument2
;
1490 DPRINT("OhciDefferedRoutine Status %x\n", CStatus
);
1492 if (CStatus
& OHCI_WRITEBACK_DONE_HEAD
)
1495 // notify queue of event
1497 This
->m_UsbQueue
->TransferDescriptorCompletionCallback(DoneHead
);
1499 if (CStatus
& OHCI_ROOT_HUB_STATUS_CHANGE
)
1502 // device connected, lets check which port
1504 for(Index
= 0; Index
< This
->m_NumberOfPorts
; Index
++)
1509 PortStatus
= READ_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)));
1512 // check if there is a status change
1514 if (PortStatus
& OHCI_RH_PORTSTATUS_CSC
)
1517 // did a device connect
1519 if (PortStatus
& OHCI_RH_PORTSTATUS_CCS
)
1524 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index
, (PortStatus
& OHCI_RH_PORTSTATUS_LSDA
));
1529 WRITE_REGISTER_ULONG((PULONG
)((PUCHAR
)This
->m_Base
+ OHCI_RH_PORT_STATUS(Index
)), OHCI_RH_PORTSTATUS_PES
);
1535 This
->m_PortStatus
[Index
].PortStatus
|= USB_PORT_STATUS_CONNECT
;
1536 This
->m_PortStatus
[Index
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1538 if ((PortStatus
& OHCI_RH_PORTSTATUS_LSDA
))
1541 // low speed device connected
1543 This
->m_PortStatus
[Index
].PortStatus
|= USB_PORT_STATUS_LOW_SPEED
;
1549 // device disconnected
1551 DPRINT1("Device disconnected at Port %x\n", Index
);
1554 // update port status flags
1556 This
->m_PortStatus
[Index
].PortStatus
&= ~USB_PORT_STATUS_LOW_SPEED
;
1557 This
->m_PortStatus
[Index
].PortStatus
&= ~USB_PORT_STATUS_CONNECT
;
1558 This
->m_PortStatus
[Index
].PortChange
|= USB_PORT_STATUS_CONNECT
;
1562 // is there a status change callback
1564 if (This
->m_SCECallBack
!= NULL
)
1567 // queue work item for processing
1569 ExQueueWorkItem(&This
->m_StatusChangeWorkItem
, DelayedWorkQueue
);
1580 StatusChangeWorkItemRoutine(
1584 // cast to hardware object
1586 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1589 // is there a callback
1591 if (This
->m_SCECallBack
)
1596 This
->m_SCECallBack(This
->m_SCEContext
);
1603 PUSBHARDWAREDEVICE
*OutHardware
)
1605 PUSBHARDWAREDEVICE This
;
1607 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBHardwareDevice(0);
1610 return STATUS_INSUFFICIENT_RESOURCES
;
1615 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1617 return STATUS_SUCCESS
;