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)
16 typedef VOID __stdcall
HD_INIT_CALLBACK(IN PVOID CallBackContext
);
20 InterruptServiceRoutine(
21 IN PKINTERRUPT Interrupt
,
22 IN PVOID ServiceContext
);
28 IN PVOID DeferredContext
,
29 IN PVOID SystemArgument1
,
30 IN PVOID SystemArgument2
);
36 IN PVOID DeferredContext
,
37 IN PVOID SystemArgument1
,
38 IN PVOID SystemArgument2
);
43 StatusChangeWorkItemRoutine(PVOID Context
);
47 class CUSBHardwareDevice
: public IUHCIHardwareDevice
50 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
52 STDMETHODIMP_(ULONG
) AddRef()
54 InterlockedIncrement(&m_Ref
);
57 STDMETHODIMP_(ULONG
) Release()
59 InterlockedDecrement(&m_Ref
);
69 IMP_IUSBHARDWAREDEVICE
70 IMP_IUHCIHARDWAREDEVICE
73 NTSTATUS
StartController();
74 NTSTATUS
StopController();
75 NTSTATUS
ResetController();
77 BOOLEAN
InterruptService();
78 NTSTATUS
InitializeController();
81 friend BOOLEAN NTAPI
InterruptServiceRoutine(IN PKINTERRUPT Interrupt
, IN PVOID ServiceContext
);
82 friend VOID NTAPI
UhciDeferredRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
83 friend VOID NTAPI
TimerDpcRoutine(IN PKDPC Dpc
, IN PVOID DeferredContext
, IN PVOID SystemArgument1
, IN PVOID SystemArgument2
);
84 friend VOID NTAPI
StatusChangeWorkItemRoutine(PVOID Context
);
85 VOID
WriteRegister8(IN ULONG Register
, IN UCHAR value
);
86 VOID
WriteRegister16(ULONG Register
, USHORT Value
);
87 VOID
WriteRegister32(ULONG Register
, ULONG Value
);
88 UCHAR
ReadRegister8(ULONG Register
);
89 USHORT
ReadRegister16(ULONG Register
);
90 ULONG
ReadRegister32(ULONG Register
);
92 // constructor / destructor
93 CUSBHardwareDevice(IUnknown
*OuterUnknown
){}
94 virtual ~CUSBHardwareDevice(){}
97 LONG m_Ref
; // reference count
98 PDRIVER_OBJECT m_DriverObject
; // driver object
99 PDEVICE_OBJECT m_PhysicalDeviceObject
; // pdo
100 PDEVICE_OBJECT m_FunctionalDeviceObject
; // fdo (hcd controller)
101 PDEVICE_OBJECT m_NextDeviceObject
; // lower device object
102 KSPIN_LOCK m_Lock
; // hardware lock
103 PKINTERRUPT m_Interrupt
; // interrupt object
104 KDPC m_IntDpcObject
; // dpc object for deferred isr processing
105 PVOID VirtualBase
; // virtual base for memory manager
106 PHYSICAL_ADDRESS PhysicalAddress
; // physical base for memory manager
107 PULONG m_Base
; // UHCI operational port base registers
108 PDMA_ADAPTER m_Adapter
; // dma adapter object
109 ULONG m_MapRegisters
; // map registers count
110 USHORT m_VendorID
; // vendor id
111 USHORT m_DeviceID
; // device id
112 PUHCIQUEUE m_UsbQueue
; // usb request queue
113 ULONG m_NumberOfPorts
; // number of ports
114 PDMAMEMORYMANAGER m_MemoryManager
; // memory manager
115 HD_INIT_CALLBACK
* m_SCECallBack
; // status change callback routine
116 PVOID m_SCEContext
; // status change callback routine context
117 //WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
118 ULONG m_InterruptMask
; // interrupt enabled mask
119 ULONG m_PortResetChange
; // port reset status
120 PULONG m_FrameList
; // frame list
121 PHYSICAL_ADDRESS m_FrameListPhysicalAddress
; // frame list physical address
122 PUSHORT m_FrameBandwidth
; // frame bandwidth
123 PUHCI_QUEUE_HEAD m_QueueHead
[5]; // queue heads
124 PHYSICAL_ADDRESS m_StrayDescriptorPhysicalAddress
; // physical address stray descriptor
125 PUHCI_TRANSFER_DESCRIPTOR m_StrayDescriptor
; // stray descriptor
126 KTIMER m_SCETimer
; // SCE timer
127 KDPC m_SCETimerDpc
; // timer dpc
130 //=================================================================================================
135 CUSBHardwareDevice::QueryInterface(
139 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
141 *Output
= PVOID(PUNKNOWN(this));
142 PUNKNOWN(*Output
)->AddRef();
143 return STATUS_SUCCESS
;
146 return STATUS_UNSUCCESSFUL
;
151 CUSBHardwareDevice::GetUSBType()
158 CUSBHardwareDevice::Initialize(
159 PDRIVER_OBJECT DriverObject
,
160 PDEVICE_OBJECT FunctionalDeviceObject
,
161 PDEVICE_OBJECT PhysicalDeviceObject
,
162 PDEVICE_OBJECT LowerDeviceObject
)
164 BUS_INTERFACE_STANDARD BusInterface
;
165 PCI_COMMON_CONFIG PciConfig
;
169 DPRINT("CUSBHardwareDevice::Initialize\n");
172 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
174 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
175 if (!NT_SUCCESS(Status
))
177 DPRINT1("Failed to create DMAMemoryManager Object\n");
182 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
184 Status
= CreateUSBQueue((PUSBQUEUE
*)&m_UsbQueue
);
185 if (!NT_SUCCESS(Status
))
187 DPRINT1("Failed to create UsbQueue!\n");
192 // store device objects
194 m_DriverObject
= DriverObject
;
195 m_FunctionalDeviceObject
= FunctionalDeviceObject
;
196 m_PhysicalDeviceObject
= PhysicalDeviceObject
;
197 m_NextDeviceObject
= LowerDeviceObject
;
200 // initialize device lock
202 KeInitializeSpinLock(&m_Lock
);
205 // initialize status change work item
207 //ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
211 KeInitializeTimer(&m_SCETimer
);
213 // initialize timer dpc
214 KeInitializeDpc(&m_SCETimerDpc
, TimerDpcRoutine
, PVOID(this));
220 Status
= GetBusInterface(PhysicalDeviceObject
, &BusInterface
);
221 if (!NT_SUCCESS(Status
))
223 DPRINT1("Failed to get BusInterface!\n");
227 BytesRead
= (*BusInterface
.GetBusData
)(BusInterface
.Context
,
228 PCI_WHICHSPACE_CONFIG
,
231 PCI_COMMON_HDR_LENGTH
);
233 if (BytesRead
!= PCI_COMMON_HDR_LENGTH
)
235 DPRINT1("Failed to get pci config information!\n");
236 return STATUS_SUCCESS
;
239 m_VendorID
= PciConfig
.VendorID
;
240 m_DeviceID
= PciConfig
.DeviceID
;
242 return STATUS_SUCCESS
;
246 CUSBHardwareDevice::PnpStart(
247 PCM_RESOURCE_LIST RawResources
,
248 PCM_RESOURCE_LIST TranslatedResources
)
251 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor
;
252 DEVICE_DESCRIPTION DeviceDescription
;
255 DPRINT("CUSBHardwareDevice::PnpStart\n");
256 for(Index
= 0; Index
< TranslatedResources
->List
[0].PartialResourceList
.Count
; Index
++)
259 // get resource descriptor
261 ResourceDescriptor
= &TranslatedResources
->List
[0].PartialResourceList
.PartialDescriptors
[Index
];
263 switch(ResourceDescriptor
->Type
)
265 case CmResourceTypeInterrupt
:
267 KeInitializeDpc(&m_IntDpcObject
,
271 Status
= IoConnectInterrupt(&m_Interrupt
,
272 InterruptServiceRoutine
,
275 ResourceDescriptor
->u
.Interrupt
.Vector
,
276 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
277 (KIRQL
)ResourceDescriptor
->u
.Interrupt
.Level
,
278 (KINTERRUPT_MODE
)(ResourceDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
),
279 (ResourceDescriptor
->ShareDisposition
!= CmResourceShareDeviceExclusive
),
280 ResourceDescriptor
->u
.Interrupt
.Affinity
,
283 if (!NT_SUCCESS(Status
))
286 // failed to register interrupt
288 DPRINT1("IoConnect Interrupt failed with %x\n", Status
);
293 case CmResourceTypePort
:
296 // Store Resource base
298 m_Base
= (PULONG
)ResourceDescriptor
->u
.Port
.Start
.LowPart
; //FIXME
299 DPRINT("UHCI Base %p Length %x\n", m_Base
, ResourceDescriptor
->u
.Port
.Length
);
308 // zero device description
310 RtlZeroMemory(&DeviceDescription
, sizeof(DEVICE_DESCRIPTION
));
313 // initialize device description
315 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
316 DeviceDescription
.Master
= TRUE
;
317 DeviceDescription
.ScatterGather
= TRUE
;
318 DeviceDescription
.Dma32BitAddresses
= TRUE
;
319 DeviceDescription
.DmaWidth
= Width32Bits
;
320 DeviceDescription
.InterfaceType
= PCIBus
;
321 DeviceDescription
.MaximumLength
= MAXULONG
;
326 m_Adapter
= IoGetDmaAdapter(m_PhysicalDeviceObject
, &DeviceDescription
, &m_MapRegisters
);
330 // failed to get dma adapter
332 DPRINT1("Failed to acquire dma adapter\n");
333 return STATUS_INSUFFICIENT_RESOURCES
;
337 // Create Common Buffer
339 VirtualBase
= m_Adapter
->DmaOperations
->AllocateCommonBuffer(m_Adapter
,
345 DPRINT1("Failed to allocate a common buffer\n");
346 return STATUS_INSUFFICIENT_RESOURCES
;
350 // Initialize the DMAMemoryManager
352 Status
= m_MemoryManager
->Initialize(this, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
353 if (!NT_SUCCESS(Status
))
355 DPRINT1("Failed to initialize the DMAMemoryManager\n");
360 // initializes the controller
362 Status
= InitializeController();
363 if (!NT_SUCCESS(Status
))
365 DPRINT1("Failed to Initialize the controller \n");
371 // Initialize the UsbQueue now that we have an AdapterObject.
373 Status
= m_UsbQueue
->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter
, m_MemoryManager
, NULL
);
374 if (!NT_SUCCESS(Status
))
376 DPRINT1("Failed to Initialize the UsbQueue\n");
381 // Start the controller
383 DPRINT("Starting Controller\n");
384 Status
= StartController();
394 CUSBHardwareDevice::PnpStop(void)
397 return STATUS_NOT_IMPLEMENTED
;
401 CUSBHardwareDevice::GetDeviceDetails(
402 OUT OPTIONAL PUSHORT VendorId
,
403 OUT OPTIONAL PUSHORT DeviceId
,
404 OUT OPTIONAL PULONG NumberOfPorts
,
405 OUT OPTIONAL PULONG Speed
)
412 *VendorId
= m_VendorID
;
420 *DeviceId
= m_DeviceID
;
426 // get number of ports
428 *NumberOfPorts
= m_NumberOfPorts
;
439 return STATUS_SUCCESS
;
442 NTSTATUS
CUSBHardwareDevice::GetDMA(
443 OUT
struct IDMAMemoryManager
**OutDMAMemoryManager
)
445 if (!m_MemoryManager
)
446 return STATUS_UNSUCCESSFUL
;
447 *OutDMAMemoryManager
= m_MemoryManager
;
448 return STATUS_SUCCESS
;
452 CUSBHardwareDevice::GetUSBQueue(
453 OUT
struct IUSBQueue
**OutUsbQueue
)
456 return STATUS_UNSUCCESSFUL
;
457 *OutUsbQueue
= m_UsbQueue
;
458 return STATUS_SUCCESS
;
463 CUSBHardwareDevice::StartController(void)
472 DPRINT("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD
), ReadRegister16(UHCI_USBSTS
));
475 // Set the run bit in the command register
477 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_RS
);
479 for(Index
= 0; Index
< 100; Index
++)
484 KeStallExecutionProcessor(100);
487 // get controller status
489 Status
= ReadRegister16(UHCI_USBSTS
);
490 DPRINT("[USBUHCI] Status %x\n", Status
);
492 if (!(Status
& UHCI_USBSTS_HCHALT
))
495 // controller started
501 DPRINT("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD
), ReadRegister16(UHCI_USBSTS
));
504 if ((Status
& UHCI_USBSTS_HCHALT
))
507 // failed to start controller
509 DPRINT1("[USBUHCI] Failed to start controller Status %x\n", Status
);
511 return STATUS_UNSUCCESSFUL
;
515 // Set the configure bit
517 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_CF
);
519 for(Index
= 0; Index
< 2; Index
++)
524 Status
= ReadRegister16(UHCI_PORTSC1
+ Index
* 2);
527 // clear connection change and port suspend
529 WriteRegister16(UHCI_PORTSC1
+ Index
* 2, Status
& ~(UHCI_PORTSC_STATCHA
| UHCI_PORTSC_SUSPEND
));
532 DPRINT("[USBUHCI] Controller Started\n");
533 DPRINT("[USBUHCI] Controller Status %x\n", ReadRegister16(UHCI_USBSTS
));
534 DPRINT("[USBUHCI] Controller Cmd Status %x\n", ReadRegister16(UHCI_USBCMD
));
535 DPRINT("[USBUHCI] Controller Interrupt Status %x\n", ReadRegister16(UHCI_USBINTR
));
536 DPRINT("[USBUHCI] Controller Frame %x\n", ReadRegister16(UHCI_FRNUM
));
537 DPRINT("[USBUHCI] Controller Port Status 0 %x\n", ReadRegister16(UHCI_PORTSC1
));
538 DPRINT("[USBUHCI] Controller Port Status 1 %x\n", ReadRegister16(UHCI_PORTSC1
+ 2));
542 LARGE_INTEGER Expires
;
543 Expires
.QuadPart
= -10 * 10000;
545 KeSetTimerEx(&m_SCETimer
, Expires
, 1000, &m_SCETimerDpc
);
550 return STATUS_SUCCESS
;
554 CUSBHardwareDevice::GlobalReset()
556 LARGE_INTEGER Timeout
;
559 // back up start of modify register
562 UCHAR sofValue
= READ_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ UHCI_SOFMOD
));
565 // perform global reset
567 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_GRESET
);
572 Timeout
.QuadPart
= 10;
573 DPRINT("Waiting %lu milliseconds for global reset\n", Timeout
.LowPart
);
576 // convert to 100 ns units (absolute)
578 Timeout
.QuadPart
*= -10000;
583 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
586 // clear command register
588 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_GRESET
);
589 KeStallExecutionProcessor(10);
593 // restore start of modify register
595 WRITE_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ UHCI_SOFMOD
), sofValue
);
599 CUSBHardwareDevice::InitializeController()
603 BUS_INTERFACE_STANDARD BusInterface
;
605 PHYSICAL_ADDRESS Address
;
607 DPRINT("[USBUHCI] InitializeController\n");
610 // now disable all interrupts
612 WriteRegister16(UHCI_USBINTR
, 0);
616 // UHCI has two ports
623 Status
= GetBusInterface(m_PhysicalDeviceObject
, &BusInterface
);
624 if (!NT_SUCCESS(Status
))
626 DPRINT1("Failed to get BusInterface!\n");
631 // reclaim ownership from BIOS
634 BusInterface
.GetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, PCI_LEGSUP
, sizeof(USHORT
));
635 DPRINT("[USBUHCI] LEGSUP %x\n", Value
);
637 Value
= PCI_LEGSUP_USBPIRQDEN
;
638 BusInterface
.SetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, PCI_LEGSUP
, sizeof(USHORT
));
640 DPRINT("[USBUHCI] Acquired ownership\n");
642 BusInterface
.GetBusData(BusInterface
.Context
, PCI_WHICHSPACE_CONFIG
, &Value
, 0x60, sizeof(UCHAR
));
643 DPRINT("[USBUHCI] SBRN %x\n", Value
);
646 // perform global reset
653 Status
= ResetController();
654 if (!NT_SUCCESS(Status
))
657 // failed to reset controller
659 DPRINT1("[USBUHCI] Failed to reset controller\n");
664 // allocate frame list
666 Status
= m_MemoryManager
->Allocate(NUMBER_OF_FRAMES
* sizeof(ULONG
), (PVOID
*)&m_FrameList
, &m_FrameListPhysicalAddress
);
667 if (!NT_SUCCESS(Status
))
670 // failed to allocate frame list
672 DPRINT1("[USBUHCI] Failed to allocate frame list with %x\n", Status
);
677 // Set base pointer and reset frame number
679 WriteRegister32(UHCI_FRBASEADD
, m_FrameListPhysicalAddress
.LowPart
);
680 WriteRegister16(UHCI_FRNUM
, 0);
683 // Set the max packet size for bandwidth reclamation to 64 bytes
685 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) | UHCI_USBCMD_MAXP
);
689 // 0: interrupt transfers
690 // 1: low speed control transfers
691 // 2: full speed control transfers
695 for(Index
= 0; Index
< 5; Index
++)
700 Status
= m_MemoryManager
->Allocate(sizeof(UHCI_QUEUE_HEAD
), (PVOID
*)&m_QueueHead
[Index
], &Address
);
701 if (!NT_SUCCESS(Status
))
704 // failed to allocate queue head
706 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status
, Index
);
713 m_QueueHead
[Index
]->PhysicalAddress
= Address
.LowPart
;
714 m_QueueHead
[Index
]->ElementPhysical
= QH_TERMINATE
;
715 m_QueueHead
[Index
]->LinkPhysical
= QH_TERMINATE
;
722 m_QueueHead
[Index
-1]->LinkPhysical
= m_QueueHead
[Index
]->PhysicalAddress
| QH_NEXT_IS_QH
;
723 m_QueueHead
[Index
-1]->NextLogicalDescriptor
= m_QueueHead
[Index
];
727 DPRINT("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
730 m_QueueHead
[0]->LinkPhysical
,
731 m_QueueHead
[0]->ElementPhysical
,
732 m_QueueHead
[0]->PhysicalAddress
,
733 m_QueueHead
[0]->Request
,
734 m_QueueHead
[0]->NextElementDescriptor
);
735 DPRINT("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
738 m_QueueHead
[1]->LinkPhysical
,
739 m_QueueHead
[1]->ElementPhysical
,
740 m_QueueHead
[1]->PhysicalAddress
,
741 m_QueueHead
[1]->Request
,
742 m_QueueHead
[1]->NextElementDescriptor
);
744 DPRINT("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
747 m_QueueHead
[2]->LinkPhysical
,
748 m_QueueHead
[2]->ElementPhysical
,
749 m_QueueHead
[2]->PhysicalAddress
,
750 m_QueueHead
[2]->Request
,
751 m_QueueHead
[2]->NextElementDescriptor
);
752 DPRINT("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
755 m_QueueHead
[3]->LinkPhysical
,
756 m_QueueHead
[3]->ElementPhysical
,
757 m_QueueHead
[3]->PhysicalAddress
,
758 m_QueueHead
[3]->Request
,
759 m_QueueHead
[3]->NextElementDescriptor
);
760 DPRINT("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
763 m_QueueHead
[4]->LinkPhysical
,
764 m_QueueHead
[4]->ElementPhysical
,
765 m_QueueHead
[4]->PhysicalAddress
,
766 m_QueueHead
[4]->Request
,
767 m_QueueHead
[4]->NextElementDescriptor
);
770 // terminate last queue head with stray descriptor
772 Status
= m_MemoryManager
->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR
), (PVOID
*)&m_StrayDescriptor
, &m_StrayDescriptorPhysicalAddress
);
773 if (!NT_SUCCESS(Status
))
776 // failed to allocate queue head
778 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status
, Index
);
783 // init stray descriptor
785 m_StrayDescriptor
->PhysicalAddress
= m_StrayDescriptorPhysicalAddress
.LowPart
;
786 m_StrayDescriptor
->LinkPhysical
= TD_TERMINATE
;
787 m_StrayDescriptor
->Token
= TD_TOKEN_NULL_DATA
| (0x7f << TD_TOKEN_DEVADDR_SHIFT
) | TD_TOKEN_IN
;
791 // link to last queue head
793 m_QueueHead
[4]->LinkPhysical
= m_StrayDescriptor
->PhysicalAddress
;
794 m_QueueHead
[4]->NextLogicalDescriptor
= m_StrayDescriptor
;
798 // allocate frame bandwidth array
800 m_FrameBandwidth
= (PUSHORT
)ExAllocatePool(NonPagedPool
, sizeof(USHORT
) * NUMBER_OF_FRAMES
);
801 if (!m_FrameBandwidth
)
806 DPRINT1("[USBUHCI] Failed to allocate memory\n");
807 return STATUS_INSUFFICIENT_RESOURCES
;
813 for (Index
= 0; Index
< NUMBER_OF_FRAMES
; Index
++)
816 // store frame list interrupt queue
818 m_FrameList
[Index
] = m_QueueHead
[UHCI_INTERRUPT_QUEUE
]->PhysicalAddress
| FRAMELIST_NEXT_IS_QH
;
819 m_FrameBandwidth
[Index
] = MAX_AVAILABLE_BANDWIDTH
;
825 // set enabled interrupt mask
827 m_InterruptMask
= UHCI_USBSTS_USBINT
| UHCI_USBSTS_ERRINT
| UHCI_USBSTS_HOSTERR
| UHCI_USBSTS_HCPRERR
| UHCI_USBSTS_HCHALT
;
830 // now enable interrupts
832 WriteRegister16(UHCI_USBINTR
, UHCI_USBINTR_CRC
| UHCI_USBINTR_IOC
| UHCI_USBINTR_SHORT
);
834 DPRINT("[USBUHCI] Controller initialized\n");
835 return STATUS_SUCCESS
;
839 CUSBHardwareDevice::StopController(void)
843 // failed to reset controller
845 return STATUS_UNSUCCESSFUL
;
849 CUSBHardwareDevice::ResetController(void)
855 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_RS
);
857 // wait for the controller to stop
858 while((ReadRegister16(UHCI_USBSTS
) & UHCI_USBSTS_HCHALT
) == 0)
860 DPRINT("[UHCI] Waiting for the controller to halt\n");
861 KeStallExecutionProcessor(10);
864 // clear configure bit
865 WriteRegister16(UHCI_USBCMD
, ReadRegister16(UHCI_USBCMD
) & ~UHCI_USBCMD_CF
);
870 WriteRegister16(UHCI_USBCMD
, UHCI_USBCMD_HCRESET
);
877 KeStallExecutionProcessor(100);
882 Status
= ReadRegister16(UHCI_USBCMD
);
883 if (!(Status
& UHCI_USBCMD_HCRESET
))
886 // controller reset completed
888 return STATUS_SUCCESS
;
890 }while(Count
++ < 100);
892 DPRINT1("[USBUHCI] Failed to reset controller Status %x\n", Status
);
893 return STATUS_UNSUCCESSFUL
;
897 CUSBHardwareDevice::ResetPort(
903 LARGE_INTEGER Timeout
;
905 DPRINT("[UHCI] ResetPort Id %lu\n", PortIndex
);
910 ASSERT(PortIndex
<= 1);
913 // get register offset
915 Port
= UHCI_PORTSC1
+ PortIndex
* 2;
920 Status
= ReadRegister16(Port
);
925 // remove unwanted bits
927 Status
&= UHCI_PORTSC_DATAMASK
;
930 // now reset the port
932 WriteRegister16(Port
, Status
| UHCI_PORTSC_RESET
);
935 // delay is 20 ms for port reset
937 Timeout
.QuadPart
= 20;
938 DPRINT("Waiting %lu milliseconds for port reset\n", Timeout
.LowPart
);
941 // convert to 100 ns units (absolute)
943 Timeout
.QuadPart
*= -10000;
948 KeDelayExecutionThread(KernelMode
, FALSE
, &Timeout
);
953 Status
= ReadRegister16(Port
);
956 // remove unwanted bits
958 Status
&= UHCI_PORTSC_DATAMASK
;
963 WriteRegister16(Port
, (Status
& ~UHCI_PORTSC_RESET
));
969 KeStallExecutionProcessor(10);
971 for (Index
= 0; Index
< 100; Index
++)
974 Status
= ReadRegister16(Port
);
976 // remove unwanted bits
977 Status
&= UHCI_PORTSC_DATAMASK
;
980 WriteRegister16(Port
, Status
| UHCI_PORTSC_ENABLED
);
985 KeStallExecutionProcessor(50);
990 Status
= ReadRegister16(Port
);
992 if ((Status
& UHCI_PORTSC_CURSTAT
) == 0)
994 // no device connected. since we waited long enough we can assume
995 // that the port was reset and no device is connected.
999 if (Status
& (UHCI_PORTSC_STATCHA
| UHCI_PORTSC_ENABCHA
))
1001 // port enabled changed or connection status were set.
1002 // acknowledge either / both and wait again.
1003 WriteRegister16(Port
, Status
);
1007 if (Status
& UHCI_PORTSC_ENABLED
)
1009 // the port is enabled
1014 m_PortResetChange
|= (1 << PortIndex
);
1015 DPRINT("[USBUHCI] Port Index %x Status after reset %x\n", PortIndex
, ReadRegister16(Port
));
1018 // is there a callback
1025 m_SCECallBack(m_SCEContext
);
1028 return STATUS_SUCCESS
;
1032 CUSBHardwareDevice::GetPortStatus(
1034 OUT USHORT
*PortStatus
,
1035 OUT USHORT
*PortChange
)
1047 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1048 return STATUS_INVALID_PARAMETER
;
1060 Status
= ReadRegister16(UHCI_PORTSC1
+ PortId
* 2);
1061 DPRINT("[USBUHCI] PortId %x Status %x\n", PortId
, Status
);
1064 if (Status
& UHCI_PORTSC_CURSTAT
)
1066 *PortStatus
|= USB_PORT_STATUS_CONNECT
;
1069 if (Status
& UHCI_PORTSC_ENABLED
)
1071 *PortStatus
|= USB_PORT_STATUS_ENABLE
;
1074 if (Status
& UHCI_PORTSC_RESET
)
1076 *PortStatus
|= USB_PORT_STATUS_RESET
;
1079 if (Status
& UHCI_PORTSC_LOWSPEED
)
1081 *PortStatus
|= USB_PORT_STATUS_LOW_SPEED
;
1084 if (Status
& UHCI_PORTSC_STATCHA
)
1086 *PortChange
|= USB_PORT_STATUS_CONNECT
;
1089 if (Status
& UHCI_PORTSC_ENABCHA
)
1091 *PortChange
|= USB_PORT_STATUS_ENABLE
;
1094 if (m_PortResetChange
& (1 << PortId
))
1096 *PortChange
|= USB_PORT_STATUS_RESET
;
1100 // port always has power
1102 *PortStatus
|= USB_PORT_STATUS_POWER
;
1103 return STATUS_SUCCESS
;
1107 CUSBHardwareDevice::ClearPortStatus(
1114 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId
, Feature
);
1124 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1125 return STATUS_INVALID_PARAMETER
;
1129 // read current status
1131 PortRegister
= UHCI_PORTSC1
+ PortId
* 2;
1132 PortStatus
= ReadRegister16(PortRegister
);
1133 DPRINT("[UHCI] PortStatus %x\n", PortStatus
);
1135 if (Feature
== C_PORT_RESET
)
1138 // UHCI is not supporting port reset register bit
1140 m_PortResetChange
&= ~(1 << PortId
);
1142 else if (Feature
== C_PORT_CONNECTION
|| Feature
== C_PORT_ENABLE
)
1145 // clear port status changes
1147 WriteRegister16(PortRegister
, PortStatus
);
1150 return STATUS_SUCCESS
;
1155 CUSBHardwareDevice::SetPortFeature(
1161 DPRINT("[UHCI] SetPortFeature PortId %x Feature %x\n", PortId
, Feature
);
1171 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId
);
1172 return STATUS_INVALID_PARAMETER
;
1175 PortRegister
= UHCI_PORTSC1
+ PortId
* 2;
1177 if (Feature
== PORT_RESET
)
1182 return ResetPort(PortId
);
1184 else if (Feature
== PORT_ENABLE
)
1189 WriteRegister16(PortRegister
, ReadRegister16(PortRegister
) | UHCI_PORTSC_ENABLED
);
1191 else if (Feature
== PORT_POWER
)
1194 // port power is no op, it is always enabled
1198 return STATUS_SUCCESS
;
1204 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1208 m_SCECallBack
= (HD_INIT_CALLBACK
*)CallBack
;
1209 m_SCEContext
= Context
;
1214 InterruptServiceRoutine(
1215 IN PKINTERRUPT Interrupt
,
1216 IN PVOID ServiceContext
)
1218 CUSBHardwareDevice
*This
;
1219 USHORT Status
, Acknowledge
;
1224 This
= (CUSBHardwareDevice
*) ServiceContext
;
1229 Status
= This
->ReadRegister16(UHCI_USBSTS
);
1230 DPRINT("InterruptServiceRoutine %x\n", Status
);
1233 // check if the interrupt signaled are from us
1235 if ((Status
& This
->m_InterruptMask
) == 0)
1240 // FIXME: received unexpected interrupt
1242 DPRINT1("[USBUHCI] Unexpected interrupt %x\n", Status
);
1243 This
->WriteRegister16(UHCI_USBSTS
, Status
);
1253 // check for the interrupt cause
1257 if (Status
& UHCI_USBSTS_USBINT
)
1260 // transfer finished
1262 Acknowledge
|= UHCI_USBSTS_USBINT
;
1265 if (Status
& UHCI_USBSTS_ERRINT
)
1270 Acknowledge
|= UHCI_USBSTS_ERRINT
;
1271 DPRINT("[UHCI] Error interrupt\n");
1274 if (Status
& UHCI_USBSTS_RESDET
)
1279 DPRINT("[UHCI] Resume detected\n");
1280 Acknowledge
|= UHCI_USBSTS_RESDET
;
1283 if (Status
& UHCI_USBSTS_HOSTERR
)
1286 // host system error
1288 DPRINT("[UHCI] Host System Error\n");
1289 Acknowledge
|= UHCI_USBSTS_HOSTERR
;
1292 if (Status
& UHCI_USBSTS_HCPRERR
)
1297 DPRINT("[UHCI] Process Error\n");
1298 Acknowledge
|= UHCI_USBSTS_HCPRERR
;
1301 if (Status
& UHCI_USBSTS_HCHALT
)
1304 // controller halted
1306 DPRINT("[UHCI] Host controller halted\n");
1309 // disable interrupts
1311 This
->WriteRegister16(UHCI_USBINTR
, 0);
1312 This
->m_InterruptMask
= 0;
1316 // do we have something to acknowledge
1321 // acknowledge interrupt
1323 This
->WriteRegister16(UHCI_USBSTS
, Acknowledge
);
1328 KeInsertQueueDpc(&This
->m_IntDpcObject
, UlongToPtr(Status
), NULL
);
1332 // interrupt handled
1339 CUSBHardwareDevice::WriteRegister8(
1343 WRITE_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ Register
), Value
);
1348 CUSBHardwareDevice::WriteRegister16(
1352 WRITE_PORT_USHORT((PUSHORT
)((ULONG
)m_Base
+ Register
), Value
);
1357 CUSBHardwareDevice::WriteRegister32(
1361 WRITE_PORT_ULONG((PULONG
)((ULONG
)m_Base
+ Register
), Value
);
1366 CUSBHardwareDevice::ReadRegister8(
1369 return READ_PORT_UCHAR((PUCHAR
)((ULONG
)m_Base
+ Register
));
1374 CUSBHardwareDevice::ReadRegister16(
1377 return READ_PORT_USHORT((PUSHORT
)((ULONG
)m_Base
+ Register
));
1382 CUSBHardwareDevice::ReadRegister32(
1385 return READ_PORT_ULONG((PULONG
)((ULONG
)m_Base
+ Register
));
1389 CUSBHardwareDevice::GetQueueHead(
1390 IN ULONG QueueHeadIndex
,
1391 OUT PUHCI_QUEUE_HEAD
*OutQueueHead
)
1396 ASSERT(QueueHeadIndex
< 5);
1401 *OutQueueHead
= m_QueueHead
[QueueHeadIndex
];
1406 UhciDeferredRoutine(
1408 IN PVOID DeferredContext
,
1409 IN PVOID SystemArgument1
,
1410 IN PVOID SystemArgument2
)
1412 CUSBHardwareDevice
*This
;
1418 This
= (CUSBHardwareDevice
*)DeferredContext
;
1420 DPRINT("UhciDeferredRoutine\n");
1425 Status
= PtrToUlong(SystemArgument1
);
1426 if (Status
& (UHCI_USBSTS_USBINT
| UHCI_USBSTS_ERRINT
))
1429 // a transfer finished, inform the queue
1431 This
->m_UsbQueue
->TransferInterrupt(Status
& UHCI_USBSTS_USBINT
);
1438 DPRINT1("[USBUHCI] Status %x not handled\n", Status
);
1445 IN PVOID DeferredContext
,
1446 IN PVOID SystemArgument1
,
1447 IN PVOID SystemArgument2
)
1449 CUSBHardwareDevice
*This
;
1450 USHORT PortStatus
= 0;
1451 USHORT PortChange
= 0;
1454 This
= (CUSBHardwareDevice
*)DeferredContext
;
1457 This
->GetPortStatus(0, &PortStatus
, &PortChange
);
1460 // invoke status change work item routine
1461 StatusChangeWorkItemRoutine(DeferredContext
);
1466 This
->GetPortStatus(1, &PortStatus
, &PortChange
);
1469 // invoke status change work item routine
1470 StatusChangeWorkItemRoutine(DeferredContext
);
1477 StatusChangeWorkItemRoutine(
1481 // cast to hardware object
1483 CUSBHardwareDevice
* This
= (CUSBHardwareDevice
*)Context
;
1486 // is there a callback
1488 if (This
->m_SCECallBack
)
1493 This
->m_SCECallBack(This
->m_SCEContext
);
1501 PUSBHARDWAREDEVICE
*OutHardware
)
1503 PUSBHARDWAREDEVICE This
;
1505 This
= new(NonPagedPool
, TAG_USBUHCI
) CUSBHardwareDevice(0);
1508 return STATUS_INSUFFICIENT_RESOURCES
;
1513 *OutHardware
= (PUSBHARDWAREDEVICE
)This
;
1515 return STATUS_SUCCESS
;