2 * PROJECT: ReactOS Universal Serial Bus Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbuhci/hcd_controller.cpp
5 * PURPOSE: USB UHCI 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
);
35 IN PVOID DeferredContext
,
36 IN PVOID SystemArgument1
,
37 IN PVOID SystemArgument2
);
42 StatusChangeWorkItemRoutine(PVOID Context
);
46 class CUSBHardwareDevice
: public IUHCIHardwareDevice
49 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
51 STDMETHODIMP_(ULONG
) AddRef()
53 InterlockedIncrement(&m_Ref
);
56 STDMETHODIMP_(ULONG
) Release()
58 InterlockedDecrement(&m_Ref
);
68 IMP_IUSBHARDWAREDEVICE
69 IMP_IUHCIHARDWAREDEVICE
72 NTSTATUS
StartController();
73 NTSTATUS
StopController();
74 NTSTATUS
ResetController();
76 BOOLEAN
InterruptService();
77 NTSTATUS
InitializeController();
80 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
81 friend VOID NTAPI
UhciDefferedRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
82 friend VOID NTAPI
TimerDpcRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
83 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
84 VOID
WriteRegister8(IN ULONG Register
, IN UCHAR value
);
85 VOID
WriteRegister16(ULONG Register
, USHORT Value
);
86 VOID
WriteRegister32(ULONG Register
, ULONG Value
);
87 UCHAR
ReadRegister8(ULONG Register
);
88 USHORT
ReadRegister16(ULONG Register
);
89 ULONG
ReadRegister32(ULONG Register
);
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
; // UHCI 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 PUHCIQUEUE m_UsbQueue
; // usb request queue
112 ULONG m_NumberOfPorts
; // number of ports
113 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
114 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
115 PVOID m_SCEContext
; // status change callback routine context
116 //WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
117 ULONG m_InterruptMask
; // interrupt enabled mask
118 ULONG m_PortResetChange
; // port reset status
119 PULONG m_FrameList
; // frame list
120 PHYSICAL_ADDRESS m_FrameListPhysicalAddress
; // frame list physical address
121 PUSHORT m_FrameBandwidth
; // frame bandwidth
122 PUHCI_QUEUE_HEAD m_QueueHead
[5]; // queue heads
123 PHYSICAL_ADDRESS m_StrayDescriptorPhysicalAddress
; // physical address stray descriptor
124 PUHCI_TRANSFER_DESCRIPTOR m_StrayDescriptor
; // stray descriptor
125 KTIMER m_SCETimer
; // SCE timer
126 KDPC m_SCETimerDpc
; // timer dpc
129 //=================================================================================================
134 CUSBHardwareDevice::QueryInterface(
138 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
140 *Output
= PVOID(PUNKNOWN(this));
141 PUNKNOWN(*Output
)->AddRef();
142 return STATUS_SUCCESS
;
145 return STATUS_UNSUCCESSFUL
;
150 CUSBHardwareDevice::GetUSBType()
157 CUSBHardwareDevice::Initialize(
158 PDRIVER_OBJECT DriverObject
,
159 PDEVICE_OBJECT FunctionalDeviceObject
,
160 PDEVICE_OBJECT PhysicalDeviceObject
,
161 PDEVICE_OBJECT LowerDeviceObject
)
163 BUS_INTERFACE_STANDARD BusInterface
;
164 PCI_COMMON_CONFIG PciConfig
;
168 DPRINT1("CUSBHardwareDevice::Initialize\n");
171 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
173 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
174 if (!NT_SUCCESS(Status
))
176 DPRINT1("Failed to create DMAMemoryManager Object\n");
181 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
183 Status
= CreateUSBQueue((PUSBQUEUE
*)&m_UsbQueue
);
184 if (!NT_SUCCESS(Status
))
186 DPRINT1("Failed to create UsbQueue!\n");
191 // store device objects
193 m_DriverObject
= DriverObject
;
194 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
195 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
196 m_NextDeviceObject
= LowerDeviceObject
;
199 // initialize device lock
201 KeInitializeSpinLock(&m_Lock
);
204 // intialize status change work item
206 //ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
210 KeInitializeTimer(&m_SCETimer
);
212 // initialize timer dpc
213 KeInitializeDpc(&m_SCETimerDpc
, TimerDpcRoutine
, PVOID(this));
219 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
220 if (!NT_SUCCESS(Status
))
222 DPRINT1("Failed to get BusInteface!\n");
226 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
227 PCI_WHICHSPACE_CONFIG
,
230 PCI_COMMON_HDR_LENGTH
);
232 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
234 DPRINT1("Failed to get pci config information!\n");
235 return STATUS_SUCCESS
;
238 m_VendorID
= PciConfig
.VendorID
;
239 m_DeviceID
= PciConfig
.DeviceID
;
241 return STATUS_SUCCESS
;
245 CUSBHardwareDevice::PnpStart(
246 PCM_RESOURCE_LIST RawResources
,
247 PCM_RESOURCE_LIST TranslatedResources
)
250 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
251 DEVICE_DESCRIPTION DeviceDescription
;
254 DPRINT1("CUSBHardwareDevice::PnpStart\n");
255 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
258 // get resource descriptor
260 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
262 switch(ResourceDescriptor
->Type
)
264 case CmResourceTypeInterrupt
:
266 KeInitializeDpc(&m_IntDpcObject
,
270 Status
= IoConnectInterrupt(&m_Interrupt
,
271 InterruptServiceRoutine
,
274 ResourceDescriptor
->u
.Interrupt
.Vector
,
275 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
276 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
277 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
278 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
279 ResourceDescriptor
->u
.Interrupt
.Affinity
,
282 if (!NT_SUCCESS(Status
))
285 // failed to register interrupt
287 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
292 case CmResourceTypePort
:
295 // Store Resource base
297 m_Base
= (PULONG
)ResourceDescriptor
->u
.Port
.Start
.LowPart
; //FIXME
298 DPRINT1("UHCI Base %p Length %x\n", m_Base
, ResourceDescriptor
->u
.Port
.Length
);
307 // zero device description
309 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
312 // initialize device description
314 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
315 DeviceDescription
.Master
= TRUE
;
316 DeviceDescription
.ScatterGather
= TRUE
;
317 DeviceDescription
.Dma32BitAddresses
= TRUE
;
318 DeviceDescription
.DmaWidth
= Width32Bits
;
319 DeviceDescription
.InterfaceType
= PCIBus
;
320 DeviceDescription
.MaximumLength
= MAXULONG
;
325 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
329 // failed to get dma adapter
331 DPRINT1("Failed to acquire dma adapter\n");
332 return STATUS_INSUFFICIENT_RESOURCES
;
336 // Create Common Buffer
338 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
344 DPRINT1("Failed to allocate a common buffer\n");
345 return STATUS_INSUFFICIENT_RESOURCES
;
349 // Initialize the DMAMemoryManager
351 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
352 if (!NT_SUCCESS(Status
))
354 DPRINT1("Failed to initialize the DMAMemoryManager\n");
359 // initializes the controller
361 Status
= InitializeController();
362 if (!NT_SUCCESS(Status
))
364 DPRINT1("Failed to Initialize the controller \n");
370 // Initialize the UsbQueue now that we have an AdapterObject.
372 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, NULL
);
373 if (!NT_SUCCESS(Status
))
375 DPRINT1("Failed to Initialize the UsbQueue\n");
380 // Start the controller
382 DPRINT1("Starting Controller\n");
383 Status
= StartController();
393 CUSBHardwareDevice::PnpStop(void)
396 return STATUS_NOT_IMPLEMENTED
;
400 CUSBHardwareDevice::GetDeviceDetails(
401 OUT OPTIONAL PUSHORT VendorId
,
402 OUT OPTIONAL PUSHORT DeviceId
,
403 OUT OPTIONAL PULONG NumberOfPorts
,
404 OUT OPTIONAL PULONG Speed
)
411 *VendorId
= m_VendorID
;
419 *DeviceId
= m_DeviceID
;
425 // get number of ports
427 *NumberOfPorts
= m_NumberOfPorts
;
438 return STATUS_SUCCESS
;
441 NTSTATUS
CUSBHardwareDevice::GetDMA(
442 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
444 if (!m_MemoryManager
)
445 return STATUS_UNSUCCESSFUL
;
446 *OutDMAMemoryManager
= m_MemoryManager
;
447 return STATUS_SUCCESS
;
451 CUSBHardwareDevice::GetUSBQueue(
452 OUT
struct IUSBQueue
**OutUsbQueue
)
455 return STATUS_UNSUCCESSFUL
;
456 *OutUsbQueue
= m_UsbQueue
;
457 return STATUS_SUCCESS
;
462 CUSBHardwareDevice::StartController(void)
471 DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD
), ReadRegister16(UHCI_USBSTS
));
474 // Set the run bit in the command register
476 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_RS
);
478 for(Index
= 0; Index
< 100; Index
++)
483 KeStallExecutionProcessor(100);
486 // get controller status
488 Status
= ReadRegister16(UHCI_USBSTS
);
489 DPRINT1("[USBUHCI] Status %x\n", Status
);
491 if (!(Status
& UHCI_USBSTS_HCHALT
))
494 // controller started
500 DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD
), ReadRegister16(UHCI_USBSTS
));
503 if ((Status
& UHCI_USBSTS_HCHALT
))
506 // failed to start controller
508 DPRINT1("[USBUHCI] Failed to start controller Status %x\n", Status
);
510 return STATUS_UNSUCCESSFUL
;
514 // Set the configure bit
516 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_CF
);
518 for(Index
= 0; Index
< 2; Index
++)
523 Status
= ReadRegister16(UHCI_PORTSC1
+ Index
* 2);
526 // clear connection change and port suspend
528 WriteRegister16(UHCI_PORTSC1
+ Index
* 2, Status
& ~(UHCI_PORTSC_STATCHA
| UHCI_PORTSC_SUSPEND
));
531 DPRINT1("[USBUHCI] Controller Started\n");
532 DPRINT1("[USBUHCI] Controller Status %x\n", ReadRegister16(UHCI_USBSTS
));
533 DPRINT1("[USBUHCI] Controller Cmd Status %x\n", ReadRegister16(UHCI_USBCMD
));
534 DPRINT1("[USBUHCI] Controller Interrupt Status %x\n", ReadRegister16(UHCI_USBINTR
));
535 DPRINT1("[USBUHCI] Controller Frame %x\n", ReadRegister16(UHCI_FRNUM
));
536 DPRINT1("[USBUHCI] Controller Port Status 0 %x\n", ReadRegister16(UHCI_PORTSC1
));
537 DPRINT1("[USBUHCI] Controller Port Status 1 %x\n", ReadRegister16(UHCI_PORTSC1
+ 2));
541 LARGE_INTEGER Expires
;
542 Expires
.QuadPart
= -10 * 10000;
544 KeSetTimerEx(&m_SCETimer
, Expires
, 1000, &m_SCETimerDpc
);
549 return STATUS_SUCCESS
;
553 CUSBHardwareDevice::GlobalReset()
555 LARGE_INTEGER Timeout
;
558 // back up start of modify register
561 UCHAR sofValue
= READ_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ UHCI_SOFMOD
));
564 // perform global reset
566 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_GRESET
);
571 Timeout
.QuadPart
= 10;
572 DPRINT1("Waiting %d milliseconds for global reset\n", Timeout
.LowPart
);
575 // convert to 100 ns units (absolute)
577 Timeout
.QuadPart
*= -10000;
582 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
585 // clear command register
587 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_GRESET
);
588 KeStallExecutionProcessor(10);
592 // restore start of modify register
594 WRITE_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ UHCI_SOFMOD
), sofValue
);
598 CUSBHardwareDevice::InitializeController()
602 BUS_INTERFACE_STANDARD BusInterface
;
604 PHYSICAL_ADDRESS Address
;
606 DPRINT1("[USBUHCI] InitializeController\n");
609 // now disable all interrupts
611 WriteRegister16(UHCI_USBINTR
, 0);
615 // UHCI has two ports
622 Status
= GetBusInterface(m_PhysicalDeviceObject
, &BusInterface
);
623 if (!NT_SUCCESS(Status
))
625 DPRINT1("Failed to get BusInteface!\n");
630 // reclaim ownership from BIOS
633 BusInterface
.GetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, PCI_LEGSUP
, sizeof(USHORT
));
634 DPRINT1("[USBUHCI] LEGSUP %x\n", Value
);
636 Value
= PCI_LEGSUP_USBPIRQDEN
;
637 BusInterface
.SetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, PCI_LEGSUP
, sizeof(USHORT
));
639 DPRINT1("[USBUHCI] Acquired ownership\n");
641 BusInterface
.GetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, 0x60, sizeof(UCHAR
));
642 DPRINT1("[USBUHCI] SBRN %x\n", Value
);
645 // perform global reset
652 Status
= ResetController();
653 if (!NT_SUCCESS(Status
))
656 // failed to reset controller
658 DPRINT1("[USBUHCI] Failed to reset controller\n");
663 // allocate frame list
665 Status
= m_MemoryManager
->Allocate(NUMBER_OF_FRAMES
* sizeof(ULONG
), (PVOID
*)&m_FrameList
, &m_FrameListPhysicalAddress
);
666 if (!NT_SUCCESS(Status
))
669 // failed to allocate frame list
671 DPRINT1("[USBUHCI] Failed to allocate frame list with %x\n", Status
);
676 // Set base pointer and reset frame number
678 WriteRegister32(UHCI_FRBASEADD
, m_FrameListPhysicalAddress
.LowPart
);
679 WriteRegister16(UHCI_FRNUM
, 0);
682 // Set the max packet size for bandwidth reclamation to 64 bytes
684 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_MAXP
);
688 // 0: interrupt transfers
689 // 1: low speed control transfers
690 // 2: full speed control transfers
694 for(Index
= 0; Index
< 5; Index
++)
699 Status
= m_MemoryManager
->Allocate(sizeof(UHCI_QUEUE_HEAD
), (PVOID
*)&m_QueueHead
[Index
], &Address
);
700 if (!NT_SUCCESS(Status
))
703 // failed to allocate queue head
705 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status
, Index
);
712 m_QueueHead
[Index
]->PhysicalAddress
= Address
.LowPart
;
713 m_QueueHead
[Index
]->ElementPhysical
= QH_TERMINATE
;
714 m_QueueHead
[Index
]->LinkPhysical
= QH_TERMINATE
;
721 m_QueueHead
[Index
-1]->LinkPhysical
= m_QueueHead
[Index
]->PhysicalAddress
| QH_NEXT_IS_QH
;
722 m_QueueHead
[Index
-1]->NextLogicalDescriptor
= m_QueueHead
[Index
];
726 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
729 m_QueueHead
[0]->LinkPhysical
,
730 m_QueueHead
[0]->ElementPhysical
,
731 m_QueueHead
[0]->PhysicalAddress
,
732 m_QueueHead
[0]->Request
,
733 m_QueueHead
[0]->NextElementDescriptor
);
734 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
737 m_QueueHead
[1]->LinkPhysical
,
738 m_QueueHead
[1]->ElementPhysical
,
739 m_QueueHead
[1]->PhysicalAddress
,
740 m_QueueHead
[1]->Request
,
741 m_QueueHead
[1]->NextElementDescriptor
);
743 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
746 m_QueueHead
[2]->LinkPhysical
,
747 m_QueueHead
[2]->ElementPhysical
,
748 m_QueueHead
[2]->PhysicalAddress
,
749 m_QueueHead
[2]->Request
,
750 m_QueueHead
[2]->NextElementDescriptor
);
751 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
754 m_QueueHead
[3]->LinkPhysical
,
755 m_QueueHead
[3]->ElementPhysical
,
756 m_QueueHead
[3]->PhysicalAddress
,
757 m_QueueHead
[3]->Request
,
758 m_QueueHead
[3]->NextElementDescriptor
);
759 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
762 m_QueueHead
[4]->LinkPhysical
,
763 m_QueueHead
[4]->ElementPhysical
,
764 m_QueueHead
[4]->PhysicalAddress
,
765 m_QueueHead
[4]->Request
,
766 m_QueueHead
[4]->NextElementDescriptor
);
769 // terminate last queue head with stray descriptor
771 Status
= m_MemoryManager
->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR
), (PVOID
*)&m_StrayDescriptor
, &m_StrayDescriptorPhysicalAddress
);
772 if (!NT_SUCCESS(Status
))
775 // failed to allocate queue head
777 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status
, Index
);
782 // init stray descriptor
784 m_StrayDescriptor
->PhysicalAddress
= m_StrayDescriptorPhysicalAddress
.LowPart
;
785 m_StrayDescriptor
->LinkPhysical
= TD_TERMINATE
;
786 m_StrayDescriptor
->Token
= TD_TOKEN_NULL_DATA
| (0x7f << TD_TOKEN_DEVADDR_SHIFT
) | TD_TOKEN_IN
;
790 // link to last queue head
792 m_QueueHead
[4]->LinkPhysical
= m_StrayDescriptor
->PhysicalAddress
;
793 m_QueueHead
[4]->NextLogicalDescriptor
= m_StrayDescriptor
;
797 // allocate frame bandwidth array
799 m_FrameBandwidth
= (PUSHORT
)ExAllocatePool(NonPagedPool
, sizeof(USHORT
) * NUMBER_OF_FRAMES
);
800 if (!m_FrameBandwidth
)
805 DPRINT1("[USBUHCI] Failed to allocate memory\n");
806 return STATUS_INSUFFICIENT_RESOURCES
;
812 for (Index
= 0; Index
< NUMBER_OF_FRAMES
; Index
++)
815 // store frame list interrupt queue
817 m_FrameList
[Index
] = m_QueueHead
[UHCI_INTERRUPT_QUEUE
]->PhysicalAddress
| FRAMELIST_NEXT_IS_QH
;
818 m_FrameBandwidth
[Index
] = MAX_AVAILABLE_BANDWIDTH
;
824 // set enabled interrupt mask
826 m_InterruptMask
= UHCI_USBSTS_USBINT
| UHCI_USBSTS_ERRINT
| UHCI_USBSTS_HOSTERR
| UHCI_USBSTS_HCPRERR
| UHCI_USBSTS_HCHALT
;
829 // now enable interrupts
831 WriteRegister16(UHCI_USBINTR
, UHCI_USBINTR_CRC
| UHCI_USBINTR_IOC
| UHCI_USBINTR_SHORT
);
833 DPRINT1("[USBUHCI] Controller initialized\n");
834 return STATUS_SUCCESS
;
838 CUSBHardwareDevice::StopController(void)
842 // failed to reset controller
844 return STATUS_UNSUCCESSFUL
;
848 CUSBHardwareDevice::ResetController(void)
854 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_RS
);
856 // wait for the controller to stop
857 while((ReadRegister16(UHCI_USBSTS
) & UHCI_USBSTS_HCHALT
) == 0)
859 DPRINT1("[UHCI] Waiting for the controller to halt\n");
860 KeStallExecutionProcessor(10);
863 // clear configure bit
864 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_CF
);
869 WriteRegister16(UHCI_USBCMD
, UHCI_USBCMD_HCRESET
);
876 KeStallExecutionProcessor(100);
881 Status
= ReadRegister16(UHCI_USBCMD
);
882 if (!(Status
& UHCI_USBCMD_HCRESET
))
885 // controller reset completed
887 return STATUS_SUCCESS
;
889 }while(Count
++ < 100);
891 DPRINT1("[USBUHCI] Failed to reset controller Status %x\n", Status
);
892 return STATUS_UNSUCCESSFUL
;
896 CUSBHardwareDevice::ResetPort(
902 LARGE_INTEGER Timeout
;
904 DPRINT1("[UHCI] ResetPort Id %lu\n", PortIndex
);
909 ASSERT(PortIndex
<= 1);
912 // get register offset
914 Port
= UHCI_PORTSC1
+ PortIndex
* 2;
919 Status
= ReadRegister16(Port
);
924 // remove unwanted bits
926 Status
&= UHCI_PORTSC_DATAMASK
;
929 // now reset the port
931 WriteRegister16(Port
, Status
| UHCI_PORTSC_RESET
);
934 // delay is 20 ms for port reset
936 Timeout
.QuadPart
= 20;
937 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout
.LowPart
);
940 // convert to 100 ns units (absolute)
942 Timeout
.QuadPart
*= -10000;
947 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
952 Status
= ReadRegister16(Port
);
955 // remove unwanted bits
957 Status
&= UHCI_PORTSC_DATAMASK
;
962 WriteRegister16(Port
, (Status
& ~UHCI_PORTSC_RESET
));
968 KeStallExecutionProcessor(10);
970 for (Index
= 0; Index
< 100; Index
++)
973 Status
= ReadRegister16(Port
);
975 // remove unwanted bits
976 Status
&= UHCI_PORTSC_DATAMASK
;
979 WriteRegister16(Port
, Status
| UHCI_PORTSC_ENABLED
);
984 KeStallExecutionProcessor(50);
989 Status
= ReadRegister16(Port
);
991 if ((Status
& UHCI_PORTSC_CURSTAT
) == 0)
993 // no device connected. since we waited long enough we can assume
994 // that the port was reset and no device is connected.
998 if (Status
& (UHCI_PORTSC_STATCHA
| UHCI_PORTSC_ENABCHA
))
1000 // port enabled changed or connection status were set.
1001 // acknowledge either / both and wait again.
1002 WriteRegister16(Port
, Status
);
1006 if (Status
& UHCI_PORTSC_ENABLED
)
1008 // the port is enabled
1013 m_PortResetChange
|= (1 << PortIndex
);
1014 DPRINT1("[USBUhci] Port Index %x Status after reset %x\n", PortIndex
, ReadRegister16(Port
));
1017 // is there a callback
1024 m_SCECallBack(m_SCEContext
);
1027 return STATUS_SUCCESS
;
1031 CUSBHardwareDevice::GetPortStatus(
1033 OUT USHORT
*PortStatus
,
1034 OUT USHORT
*PortChange
)
1046 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1047 return STATUS_INVALID_PARAMETER
;
1059 Status
= ReadRegister16(UHCI_PORTSC1
+ PortId
* 2);
1060 DPRINT("[USBUHCI] PortId %x Status %x\n", PortId
, Status
);
1063 if (Status
& UHCI_PORTSC_CURSTAT
)
1065 *PortStatus
|= USB_PORT_STATUS_CONNECT
;
1068 if (Status
& UHCI_PORTSC_ENABLED
)
1070 *PortStatus
|= USB_PORT_STATUS_ENABLE
;
1073 if (Status
& UHCI_PORTSC_RESET
)
1075 *PortStatus
|= USB_PORT_STATUS_RESET
;
1078 if (Status
& UHCI_PORTSC_LOWSPEED
)
1080 *PortStatus
|= USB_PORT_STATUS_LOW_SPEED
;
1083 if (Status
& UHCI_PORTSC_STATCHA
)
1085 *PortChange
|= USB_PORT_STATUS_CONNECT
;
1088 if (Status
& UHCI_PORTSC_ENABCHA
)
1090 *PortChange
|= USB_PORT_STATUS_ENABLE
;
1093 if (m_PortResetChange
& (1 << PortId
))
1095 *PortChange
|= USB_PORT_STATUS_RESET
;
1099 // port always has power
1101 *PortStatus
|= USB_PORT_STATUS_POWER
;
1102 return STATUS_SUCCESS
;
1106 CUSBHardwareDevice::ClearPortStatus(
1113 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Feature
);
1123 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1124 return STATUS_INVALID_PARAMETER
;
1128 // read current status
1130 PortRegister
= UHCI_PORTSC1
+ PortId
* 2;
1131 PortStatus
= ReadRegister16(PortRegister
);
1132 DPRINT("[UHCI] PortStatus %x\n", PortStatus
);
1134 if (Feature
== C_PORT_RESET
)
1137 // UHCI is not supporting port reset register bit
1139 m_PortResetChange
&= ~(1 << PortId
);
1141 else if (Feature
== C_PORT_CONNECTION
|| Feature
== C_PORT_ENABLE
)
1144 // clear port status changes
1146 WriteRegister16(PortRegister
, PortStatus
);
1149 return STATUS_SUCCESS
;
1154 CUSBHardwareDevice::SetPortFeature(
1160 DPRINT1("[UHCI] SetPortFeature PortId %x Feature %x\n", PortId
, Feature
);
1170 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1171 return STATUS_INVALID_PARAMETER
;
1174 PortRegister
= UHCI_PORTSC1
+ PortId
* 2;
1176 if (Feature
== PORT_RESET
)
1181 return ResetPort(PortId
);
1183 else if (Feature
== PORT_ENABLE
)
1188 WriteRegister16(PortRegister
, ReadRegister16(PortRegister
) | UHCI_PORTSC_ENABLED
);
1190 else if (Feature
== PORT_POWER
)
1193 // port power is no op, it is always enabled
1197 return STATUS_SUCCESS
;
1203 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1207 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1208 m_SCEContext
= Context
;
1213 InterruptServiceRoutine(
1214 IN PKINTERRUPT Interrupt
,
1215 IN PVOID ServiceContext
)
1217 CUSBHardwareDevice
*This
;
1218 USHORT Status
, Acknowledge
;
1223 This
= (CUSBHardwareDevice
*) ServiceContext
;
1228 Status
= This
->ReadRegister16(UHCI_USBSTS
);
1229 DPRINT("InterruptServiceRoutine %x\n", Status
);
1232 // check if the interrupt signaled are from us
1234 if ((Status
& This
->m_InterruptMask
) == 0)
1239 // FIXME: received unexpected interrupt
1241 DPRINT1("[USBUHCI] Unexpected interrupt %x\n", Status
);
1242 This
->WriteRegister16(UHCI_USBSTS
, Status
);
1252 // check for the interrupt cause
1256 if (Status
& UHCI_USBSTS_USBINT
)
1259 // transfer finished
1261 Acknowledge
|= UHCI_USBSTS_USBINT
;
1264 if (Status
& UHCI_USBSTS_ERRINT
)
1269 Acknowledge
|= UHCI_USBSTS_ERRINT
;
1270 DPRINT1("[UHCI] Error interrupt\n");
1273 if (Status
& UHCI_USBSTS_RESDET
)
1278 DPRINT1("[UHCI] Resume detected\n");
1279 Acknowledge
|= UHCI_USBSTS_RESDET
;
1282 if (Status
& UHCI_USBSTS_HOSTERR
)
1285 // host system error
1287 DPRINT1("[UHCI] Host System Error\n");
1288 Acknowledge
|= UHCI_USBSTS_HOSTERR
;
1291 if (Status
& UHCI_USBSTS_HCPRERR
)
1296 DPRINT1("[UHCI] Process Error\n");
1297 Acknowledge
|= UHCI_USBSTS_HCPRERR
;
1300 if (Status
& UHCI_USBSTS_HCHALT
)
1303 // controller halted
1305 DPRINT1("[UHCI] Host controller halted\n");
1308 // disable interrupts
1310 This
->WriteRegister16(UHCI_USBINTR
, 0);
1311 This
->m_InterruptMask
= 0;
1315 // do we have something to acknowledge
1320 // acknowledge interrupt
1322 This
->WriteRegister16(UHCI_USBSTS
, Acknowledge
);
1327 KeInsertQueueDpc(&This
->m_IntDpcObject
, UlongToPtr(Status
), NULL
);
1331 // interrupt handled
1338 CUSBHardwareDevice::WriteRegister8(
1342 WRITE_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ Register
), Value
);
1347 CUSBHardwareDevice::WriteRegister16(
1351 WRITE_PORT_USHORT((PUSHORT
)((ULONG
)m_Base
+ Register
), Value
);
1356 CUSBHardwareDevice::WriteRegister32(
1360 WRITE_PORT_ULONG((PULONG
)((ULONG
)m_Base
+ Register
), Value
);
1365 CUSBHardwareDevice::ReadRegister8(
1368 return READ_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ Register
));
1373 CUSBHardwareDevice::ReadRegister16(
1376 return READ_PORT_USHORT((PUSHORT
)((ULONG
)m_Base
+ Register
));
1381 CUSBHardwareDevice::ReadRegister32(
1384 return READ_PORT_ULONG((PULONG
)((ULONG
)m_Base
+ Register
));
1388 CUSBHardwareDevice::GetQueueHead(
1389 IN ULONG QueueHeadIndex
,
1390 OUT PUHCI_QUEUE_HEAD
*OutQueueHead
)
1395 ASSERT(QueueHeadIndex
< 5);
1400 *OutQueueHead
= m_QueueHead
[QueueHeadIndex
];
1405 UhciDefferedRoutine(
1407 IN PVOID DeferredContext
,
1408 IN PVOID SystemArgument1
,
1409 IN PVOID SystemArgument2
)
1411 CUSBHardwareDevice
*This
;
1417 This
= (CUSBHardwareDevice
*)DeferredContext
;
1419 DPRINT("UhciDefferedRoutine\n");
1424 Status
= PtrToUlong(SystemArgument1
);
1425 if (Status
& (UHCI_USBSTS_USBINT
| UHCI_USBSTS_ERRINT
))
1428 // a transfer finished, inform the queue
1430 This
->m_UsbQueue
->TransferInterrupt(Status
& UHCI_USBSTS_USBINT
);
1437 DPRINT1("[USBUHCI] Status %x not handled\n", Status
);
1444 IN PVOID DeferredContext
,
1445 IN PVOID SystemArgument1
,
1446 IN PVOID SystemArgument2
)
1448 CUSBHardwareDevice
*This
;
1449 USHORT PortStatus
= 0;
1450 USHORT PortChange
= 0;
1453 This
= (CUSBHardwareDevice
*)DeferredContext
;
1456 This
->GetPortStatus(0, &PortStatus
, &PortChange
);
1459 // invoke status change work item routine
1460 StatusChangeWorkItemRoutine(DeferredContext
);
1465 This
->GetPortStatus(1, &PortStatus
, &PortChange
);
1468 // invoke status change work item routine
1469 StatusChangeWorkItemRoutine(DeferredContext
);
1476 StatusChangeWorkItemRoutine(
1480 // cast to hardware object
1482 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1485 // is there a callback
1487 if (This
->m_SCECallBack
)
1492 This
->m_SCECallBack(This
->m_SCEContext
);
1500 PUSBHARDWAREDEVICE
*OutHardware
)
1502 PUSBHARDWAREDEVICE This
;
1504 This
= new(NonPagedPool
, TAG_USBUHCI
) CUSBHardwareDevice(0);
1507 return STATUS_INSUFFICIENT_RESOURCES
;
1512 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1514 return STATUS_SUCCESS
;