Merge in 58534, 58627, and 58628 from trunk. After this, the 0.3.15 code is frozen.
[reactos.git] / drivers / usb / usbuhci / hardware.cpp
1 /*
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.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #define INITGUID
12 #include "usbuhci.h"
13 #include "hardware.h"
14
15 typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
16
17 BOOLEAN
18 NTAPI
19 InterruptServiceRoutine(
20 IN PKINTERRUPT Interrupt,
21 IN PVOID ServiceContext);
22
23 VOID
24 NTAPI
25 UhciDefferedRoutine(
26 IN PKDPC Dpc,
27 IN PVOID DeferredContext,
28 IN PVOID SystemArgument1,
29 IN PVOID SystemArgument2);
30
31 VOID
32 NTAPI
33 TimerDpcRoutine(
34 IN PKDPC Dpc,
35 IN PVOID DeferredContext,
36 IN PVOID SystemArgument1,
37 IN PVOID SystemArgument2);
38
39
40 VOID
41 NTAPI
42 StatusChangeWorkItemRoutine(PVOID Context);
43
44
45
46 class CUSBHardwareDevice : public IUHCIHardwareDevice
47 {
48 public:
49 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
50
51 STDMETHODIMP_(ULONG) AddRef()
52 {
53 InterlockedIncrement(&m_Ref);
54 return m_Ref;
55 }
56 STDMETHODIMP_(ULONG) Release()
57 {
58 InterlockedDecrement(&m_Ref);
59
60 if (!m_Ref)
61 {
62 delete this;
63 return 0;
64 }
65 return m_Ref;
66 }
67 // com
68 IMP_IUSBHARDWAREDEVICE
69 IMP_IUHCIHARDWAREDEVICE
70
71 // local
72 NTSTATUS StartController();
73 NTSTATUS StopController();
74 NTSTATUS ResetController();
75 VOID GlobalReset();
76 BOOLEAN InterruptService();
77 NTSTATUS InitializeController();
78
79 // friend function
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);
90
91 // constructor / destructor
92 CUSBHardwareDevice(IUnknown *OuterUnknown){}
93 virtual ~CUSBHardwareDevice(){}
94
95 protected:
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
127 };
128
129 //=================================================================================================
130 // COM
131 //
132 NTSTATUS
133 STDMETHODCALLTYPE
134 CUSBHardwareDevice::QueryInterface(
135 IN REFIID refiid,
136 OUT PVOID* Output)
137 {
138 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
139 {
140 *Output = PVOID(PUNKNOWN(this));
141 PUNKNOWN(*Output)->AddRef();
142 return STATUS_SUCCESS;
143 }
144
145 return STATUS_UNSUCCESSFUL;
146 }
147
148 LPCSTR
149 STDMETHODCALLTYPE
150 CUSBHardwareDevice::GetUSBType()
151 {
152 return "USBUHCI";
153 }
154
155
156 NTSTATUS
157 CUSBHardwareDevice::Initialize(
158 PDRIVER_OBJECT DriverObject,
159 PDEVICE_OBJECT FunctionalDeviceObject,
160 PDEVICE_OBJECT PhysicalDeviceObject,
161 PDEVICE_OBJECT LowerDeviceObject)
162 {
163 BUS_INTERFACE_STANDARD BusInterface;
164 PCI_COMMON_CONFIG PciConfig;
165 NTSTATUS Status;
166 ULONG BytesRead;
167
168 DPRINT1("CUSBHardwareDevice::Initialize\n");
169
170 //
171 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
172 //
173 Status = CreateDMAMemoryManager(&m_MemoryManager);
174 if (!NT_SUCCESS(Status))
175 {
176 DPRINT1("Failed to create DMAMemoryManager Object\n");
177 return Status;
178 }
179
180 //
181 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
182 //
183 Status = CreateUSBQueue((PUSBQUEUE*)&m_UsbQueue);
184 if (!NT_SUCCESS(Status))
185 {
186 DPRINT1("Failed to create UsbQueue!\n");
187 return Status;
188 }
189
190 //
191 // store device objects
192 //
193 m_DriverObject = DriverObject;
194 m_FunctionalDeviceObject = FunctionalDeviceObject;
195 m_PhysicalDeviceObject = PhysicalDeviceObject;
196 m_NextDeviceObject = LowerDeviceObject;
197
198 //
199 // initialize device lock
200 //
201 KeInitializeSpinLock(&m_Lock);
202
203 //
204 // intialize status change work item
205 //
206 //ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
207
208
209 // initialize timer
210 KeInitializeTimer(&m_SCETimer);
211
212 // initialize timer dpc
213 KeInitializeDpc(&m_SCETimerDpc, TimerDpcRoutine, PVOID(this));
214
215
216 m_VendorID = 0;
217 m_DeviceID = 0;
218
219 Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
220 if (!NT_SUCCESS(Status))
221 {
222 DPRINT1("Failed to get BusInteface!\n");
223 return Status;
224 }
225
226 BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
227 PCI_WHICHSPACE_CONFIG,
228 &PciConfig,
229 0,
230 PCI_COMMON_HDR_LENGTH);
231
232 if (BytesRead != PCI_COMMON_HDR_LENGTH)
233 {
234 DPRINT1("Failed to get pci config information!\n");
235 return STATUS_SUCCESS;
236 }
237
238 m_VendorID = PciConfig.VendorID;
239 m_DeviceID = PciConfig.DeviceID;
240
241 return STATUS_SUCCESS;
242 }
243
244 NTSTATUS
245 CUSBHardwareDevice::PnpStart(
246 PCM_RESOURCE_LIST RawResources,
247 PCM_RESOURCE_LIST TranslatedResources)
248 {
249 ULONG Index;
250 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
251 DEVICE_DESCRIPTION DeviceDescription;
252 NTSTATUS Status;
253
254 DPRINT1("CUSBHardwareDevice::PnpStart\n");
255 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
256 {
257 //
258 // get resource descriptor
259 //
260 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
261
262 switch(ResourceDescriptor->Type)
263 {
264 case CmResourceTypeInterrupt:
265 {
266 KeInitializeDpc(&m_IntDpcObject,
267 UhciDefferedRoutine,
268 this);
269
270 Status = IoConnectInterrupt(&m_Interrupt,
271 InterruptServiceRoutine,
272 (PVOID)this,
273 NULL,
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,
280 FALSE);
281
282 if (!NT_SUCCESS(Status))
283 {
284 //
285 // failed to register interrupt
286 //
287 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
288 return Status;
289 }
290 break;
291 }
292 case CmResourceTypePort:
293 {
294 //
295 // Store Resource base
296 //
297 m_Base = (PULONG)ResourceDescriptor->u.Port.Start.LowPart; //FIXME
298 DPRINT1("UHCI Base %p Length %x\n", m_Base, ResourceDescriptor->u.Port.Length);
299 break;
300 }
301 }
302 }
303
304 ASSERT(m_Base);
305
306 //
307 // zero device description
308 //
309 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
310
311 //
312 // initialize device description
313 //
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;
321
322 //
323 // get dma adapter
324 //
325 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
326 if (!m_Adapter)
327 {
328 //
329 // failed to get dma adapter
330 //
331 DPRINT1("Failed to acquire dma adapter\n");
332 return STATUS_INSUFFICIENT_RESOURCES;
333 }
334
335 //
336 // Create Common Buffer
337 //
338 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
339 PAGE_SIZE * 4,
340 &PhysicalAddress,
341 FALSE);
342 if (!VirtualBase)
343 {
344 DPRINT1("Failed to allocate a common buffer\n");
345 return STATUS_INSUFFICIENT_RESOURCES;
346 }
347
348 //
349 // Initialize the DMAMemoryManager
350 //
351 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
352 if (!NT_SUCCESS(Status))
353 {
354 DPRINT1("Failed to initialize the DMAMemoryManager\n");
355 return Status;
356 }
357
358 //
359 // initializes the controller
360 //
361 Status = InitializeController();
362 if (!NT_SUCCESS(Status))
363 {
364 DPRINT1("Failed to Initialize the controller \n");
365 ASSERT(FALSE);
366 return Status;
367 }
368
369 //
370 // Initialize the UsbQueue now that we have an AdapterObject.
371 //
372 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
373 if (!NT_SUCCESS(Status))
374 {
375 DPRINT1("Failed to Initialize the UsbQueue\n");
376 return Status;
377 }
378
379 //
380 // Start the controller
381 //
382 DPRINT1("Starting Controller\n");
383 Status = StartController();
384
385
386 //
387 // done
388 //
389 return Status;
390 }
391
392 NTSTATUS
393 CUSBHardwareDevice::PnpStop(void)
394 {
395 UNIMPLEMENTED
396 return STATUS_NOT_IMPLEMENTED;
397 }
398
399 NTSTATUS
400 CUSBHardwareDevice::GetDeviceDetails(
401 OUT OPTIONAL PUSHORT VendorId,
402 OUT OPTIONAL PUSHORT DeviceId,
403 OUT OPTIONAL PULONG NumberOfPorts,
404 OUT OPTIONAL PULONG Speed)
405 {
406 if (VendorId)
407 {
408 //
409 // get vendor
410 //
411 *VendorId = m_VendorID;
412 }
413
414 if (DeviceId)
415 {
416 //
417 // get device id
418 //
419 *DeviceId = m_DeviceID;
420 }
421
422 if (NumberOfPorts)
423 {
424 //
425 // get number of ports
426 //
427 *NumberOfPorts = m_NumberOfPorts;
428 }
429
430 if (Speed)
431 {
432 //
433 // speed is 0x100
434 //
435 *Speed = 0x100;
436 }
437
438 return STATUS_SUCCESS;
439 }
440
441 NTSTATUS CUSBHardwareDevice::GetDMA(
442 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
443 {
444 if (!m_MemoryManager)
445 return STATUS_UNSUCCESSFUL;
446 *OutDMAMemoryManager = m_MemoryManager;
447 return STATUS_SUCCESS;
448 }
449
450 NTSTATUS
451 CUSBHardwareDevice::GetUSBQueue(
452 OUT struct IUSBQueue **OutUsbQueue)
453 {
454 if (!m_UsbQueue)
455 return STATUS_UNSUCCESSFUL;
456 *OutUsbQueue = m_UsbQueue;
457 return STATUS_SUCCESS;
458 }
459
460
461 NTSTATUS
462 CUSBHardwareDevice::StartController(void)
463 {
464 ULONG Index;
465 USHORT Status;
466
467
468 //
469 // debug info
470 //
471 DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD), ReadRegister16(UHCI_USBSTS));
472
473 //
474 // Set the run bit in the command register
475 //
476 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_RS);
477
478 for(Index = 0; Index < 100; Index++)
479 {
480 //
481 // wait a bit
482 //
483 KeStallExecutionProcessor(100);
484
485 //
486 // get controller status
487 //
488 Status = ReadRegister16(UHCI_USBSTS);
489 DPRINT1("[USBUHCI] Status %x\n", Status);
490
491 if (!(Status & UHCI_USBSTS_HCHALT))
492 {
493 //
494 // controller started
495 //
496 break;
497 }
498 }
499
500 DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD), ReadRegister16(UHCI_USBSTS));
501
502
503 if ((Status & UHCI_USBSTS_HCHALT))
504 {
505 //
506 // failed to start controller
507 //
508 DPRINT1("[USBUHCI] Failed to start controller Status %x\n", Status);
509 ASSERT(FALSE);
510 return STATUS_UNSUCCESSFUL;
511 }
512
513 //
514 // Set the configure bit
515 //
516 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_CF);
517
518 for(Index = 0; Index < 2; Index++)
519 {
520 //
521 // get port status
522 //
523 Status = ReadRegister16(UHCI_PORTSC1 + Index * 2);
524
525 //
526 // clear connection change and port suspend
527 //
528 WriteRegister16(UHCI_PORTSC1 + Index * 2, Status & ~(UHCI_PORTSC_STATCHA | UHCI_PORTSC_SUSPEND));
529 }
530
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));
538
539
540 // queue timer
541 LARGE_INTEGER Expires;
542 Expires.QuadPart = -10 * 10000;
543
544 KeSetTimerEx(&m_SCETimer, Expires, 1000, &m_SCETimerDpc);
545
546 //
547 // done
548 //
549 return STATUS_SUCCESS;
550 }
551
552 VOID
553 CUSBHardwareDevice::GlobalReset()
554 {
555 LARGE_INTEGER Timeout;
556
557 //
558 // back up start of modify register
559 //
560 ASSERT(m_Base);
561 UCHAR sofValue = READ_PORT_UCHAR((PUCHAR)((ULONG)m_Base + UHCI_SOFMOD));
562
563 //
564 // perform global reset
565 //
566 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_GRESET);
567
568 //
569 // delay is 10 ms
570 //
571 Timeout.QuadPart = 10;
572 DPRINT1("Waiting %d milliseconds for global reset\n", Timeout.LowPart);
573
574 //
575 // convert to 100 ns units (absolute)
576 //
577 Timeout.QuadPart *= -10000;
578
579 //
580 // perform the wait
581 //
582 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
583
584 //
585 // clear command register
586 //
587 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_GRESET);
588 KeStallExecutionProcessor(10);
589
590
591 //
592 // restore start of modify register
593 //
594 WRITE_PORT_UCHAR((PUCHAR)((ULONG)m_Base + UHCI_SOFMOD), sofValue);
595 }
596
597 NTSTATUS
598 CUSBHardwareDevice::InitializeController()
599 {
600 NTSTATUS Status;
601 ULONG Index;
602 BUS_INTERFACE_STANDARD BusInterface;
603 USHORT Value;
604 PHYSICAL_ADDRESS Address;
605
606 DPRINT1("[USBUHCI] InitializeController\n");
607
608 //
609 // now disable all interrupts
610 //
611 WriteRegister16(UHCI_USBINTR, 0);
612
613
614 //
615 // UHCI has two ports
616 //
617 m_NumberOfPorts = 2;
618
619 //
620 // get bus interface
621 //
622 Status = GetBusInterface(m_PhysicalDeviceObject, &BusInterface);
623 if (!NT_SUCCESS(Status))
624 {
625 DPRINT1("Failed to get BusInteface!\n");
626 return Status;
627 }
628
629 //
630 // reclaim ownership from BIOS
631 //
632 Value = 0;
633 BusInterface.GetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, PCI_LEGSUP, sizeof(USHORT));
634 DPRINT1("[USBUHCI] LEGSUP %x\n", Value);
635
636 Value = PCI_LEGSUP_USBPIRQDEN;
637 BusInterface.SetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, PCI_LEGSUP, sizeof(USHORT));
638
639 DPRINT1("[USBUHCI] Acquired ownership\n");
640 Value = 0;
641 BusInterface.GetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, 0x60, sizeof(UCHAR));
642 DPRINT1("[USBUHCI] SBRN %x\n", Value);
643
644 //
645 // perform global reset
646 //
647 GlobalReset();
648
649 //
650 // reset controller
651 //
652 Status = ResetController();
653 if (!NT_SUCCESS(Status))
654 {
655 //
656 // failed to reset controller
657 //
658 DPRINT1("[USBUHCI] Failed to reset controller\n");
659 return Status;
660 }
661
662 //
663 // allocate frame list
664 //
665 Status = m_MemoryManager->Allocate(NUMBER_OF_FRAMES * sizeof(ULONG), (PVOID*)&m_FrameList, &m_FrameListPhysicalAddress);
666 if (!NT_SUCCESS(Status))
667 {
668 //
669 // failed to allocate frame list
670 //
671 DPRINT1("[USBUHCI] Failed to allocate frame list with %x\n", Status);
672 return Status;
673 }
674
675 //
676 // Set base pointer and reset frame number
677 //
678 WriteRegister32(UHCI_FRBASEADD, m_FrameListPhysicalAddress.LowPart);
679 WriteRegister16(UHCI_FRNUM, 0);
680
681 //
682 // Set the max packet size for bandwidth reclamation to 64 bytes
683 //
684 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_MAXP);
685
686 //
687 // now create queues
688 // 0: interrupt transfers
689 // 1: low speed control transfers
690 // 2: full speed control transfers
691 // 3: bulk transfers
692 // 4: debug queue
693 //
694 for(Index = 0; Index < 5; Index++)
695 {
696 //
697 // create queue head
698 //
699 Status = m_MemoryManager->Allocate(sizeof(UHCI_QUEUE_HEAD), (PVOID*)&m_QueueHead[Index], &Address);
700 if (!NT_SUCCESS(Status))
701 {
702 //
703 // failed to allocate queue head
704 //
705 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index);
706 return Status;
707 }
708
709 //
710 // store queue head
711 //
712 m_QueueHead[Index]->PhysicalAddress = Address.LowPart;
713 m_QueueHead[Index]->ElementPhysical = QH_TERMINATE;
714 m_QueueHead[Index]->LinkPhysical = QH_TERMINATE;
715
716 if (Index > 0)
717 {
718 //
719 // link queue heads
720 //
721 m_QueueHead[Index-1]->LinkPhysical = m_QueueHead[Index]->PhysicalAddress | QH_NEXT_IS_QH;
722 m_QueueHead[Index-1]->NextLogicalDescriptor = m_QueueHead[Index];
723 }
724 }
725
726 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
727 0,
728 m_QueueHead[0],
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",
735 1,
736 m_QueueHead[1],
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);
742
743 DPRINT1("Index %lu QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %x NextElementDescriptor %x\n",
744 2,
745 m_QueueHead[2],
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",
752 3,
753 m_QueueHead[3],
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",
760 4,
761 m_QueueHead[4],
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);
767
768 //
769 // terminate last queue head with stray descriptor
770 //
771 Status = m_MemoryManager->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR), (PVOID*)&m_StrayDescriptor, &m_StrayDescriptorPhysicalAddress);
772 if (!NT_SUCCESS(Status))
773 {
774 //
775 // failed to allocate queue head
776 //
777 DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index);
778 return Status;
779 }
780 #if 0
781 //
782 // init stray descriptor
783 //
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;
787
788
789 //
790 // link to last queue head
791 //
792 m_QueueHead[4]->LinkPhysical = m_StrayDescriptor->PhysicalAddress;
793 m_QueueHead[4]->NextLogicalDescriptor = m_StrayDescriptor;
794 #endif
795
796 //
797 // allocate frame bandwidth array
798 //
799 m_FrameBandwidth = (PUSHORT)ExAllocatePool(NonPagedPool, sizeof(USHORT) * NUMBER_OF_FRAMES);
800 if (!m_FrameBandwidth)
801 {
802 //
803 // no memory
804 //
805 DPRINT1("[USBUHCI] Failed to allocate memory\n");
806 return STATUS_INSUFFICIENT_RESOURCES;
807 }
808
809 //
810 // init frame list
811 //
812 for (Index = 0; Index < NUMBER_OF_FRAMES; Index++)
813 {
814 //
815 // store frame list interrupt queue
816 //
817 m_FrameList[Index] = m_QueueHead[UHCI_INTERRUPT_QUEUE]->PhysicalAddress | FRAMELIST_NEXT_IS_QH;
818 m_FrameBandwidth[Index] = MAX_AVAILABLE_BANDWIDTH;
819
820
821 }
822
823 //
824 // set enabled interrupt mask
825 //
826 m_InterruptMask = UHCI_USBSTS_USBINT | UHCI_USBSTS_ERRINT | UHCI_USBSTS_HOSTERR | UHCI_USBSTS_HCPRERR | UHCI_USBSTS_HCHALT;
827
828 //
829 // now enable interrupts
830 //
831 WriteRegister16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_IOC| UHCI_USBINTR_SHORT);
832
833 DPRINT1("[USBUHCI] Controller initialized\n");
834 return STATUS_SUCCESS;
835 }
836
837 NTSTATUS
838 CUSBHardwareDevice::StopController(void)
839 {
840 ASSERT(FALSE);
841 //
842 // failed to reset controller
843 //
844 return STATUS_UNSUCCESSFUL;
845 }
846
847 NTSTATUS
848 CUSBHardwareDevice::ResetController(void)
849 {
850 ULONG Count = 0;
851 USHORT Status;
852
853 // clear run bit
854 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_RS);
855
856 // wait for the controller to stop
857 while((ReadRegister16(UHCI_USBSTS) & UHCI_USBSTS_HCHALT) == 0)
858 {
859 DPRINT1("[UHCI] Waiting for the controller to halt\n");
860 KeStallExecutionProcessor(10);
861 }
862
863 // clear configure bit
864 WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_CF);
865
866 //
867 // reset controller
868 //
869 WriteRegister16(UHCI_USBCMD, UHCI_USBCMD_HCRESET);
870
871 do
872 {
873 //
874 // wait a bit
875 //
876 KeStallExecutionProcessor(100);
877
878 //
879 // get status
880 //
881 Status = ReadRegister16(UHCI_USBCMD);
882 if (!(Status & UHCI_USBCMD_HCRESET))
883 {
884 //
885 // controller reset completed
886 //
887 return STATUS_SUCCESS;
888 }
889 }while(Count++ < 100);
890
891 DPRINT1("[USBUHCI] Failed to reset controller Status %x\n", Status);
892 return STATUS_UNSUCCESSFUL;
893 }
894
895 NTSTATUS
896 CUSBHardwareDevice::ResetPort(
897 IN ULONG PortIndex)
898 {
899 ULONG Port;
900 USHORT Status;
901 ULONG Index;
902 LARGE_INTEGER Timeout;
903
904 DPRINT1("[UHCI] ResetPort Id %lu\n", PortIndex);
905
906 //
907 // sanity check
908 //
909 ASSERT(PortIndex <= 1);
910
911 //
912 // get register offset
913 //
914 Port = UHCI_PORTSC1 + PortIndex * 2;
915
916 //
917 // read port status
918 //
919 Status = ReadRegister16(Port);
920
921
922
923 //
924 // remove unwanted bits
925 //
926 Status &= UHCI_PORTSC_DATAMASK;
927
928 //
929 // now reset the port
930 //
931 WriteRegister16(Port, Status | UHCI_PORTSC_RESET);
932
933 //
934 // delay is 20 ms for port reset
935 //
936 Timeout.QuadPart = 20;
937 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
938
939 //
940 // convert to 100 ns units (absolute)
941 //
942 Timeout.QuadPart *= -10000;
943
944 //
945 // perform the wait
946 //
947 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
948
949 //
950 // re-read status
951 //
952 Status = ReadRegister16(Port);
953
954 //
955 // remove unwanted bits
956 //
957 Status &= UHCI_PORTSC_DATAMASK;
958
959 //
960 // clear reset port
961 //
962 WriteRegister16(Port, (Status & ~UHCI_PORTSC_RESET));
963
964
965 //
966 // now wait a bit
967 //
968 KeStallExecutionProcessor(10);
969
970 for (Index = 0; Index < 100; Index++)
971 {
972 // read port status
973 Status = ReadRegister16(Port);
974
975 // remove unwanted bits
976 Status &= UHCI_PORTSC_DATAMASK;
977
978 // enable port
979 WriteRegister16(Port, Status | UHCI_PORTSC_ENABLED);
980
981 //
982 // wait a bit
983 //
984 KeStallExecutionProcessor(50);
985
986 //
987 // re-read port
988 //
989 Status = ReadRegister16(Port);
990
991 if ((Status & UHCI_PORTSC_CURSTAT) == 0)
992 {
993 // no device connected. since we waited long enough we can assume
994 // that the port was reset and no device is connected.
995 break;
996 }
997
998 if (Status & (UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA))
999 {
1000 // port enabled changed or connection status were set.
1001 // acknowledge either / both and wait again.
1002 WriteRegister16(Port, Status);
1003 continue;
1004 }
1005
1006 if (Status & UHCI_PORTSC_ENABLED)
1007 {
1008 // the port is enabled
1009 break;
1010 }
1011 }
1012
1013 m_PortResetChange |= (1 << PortIndex);
1014 DPRINT1("[USBUhci] Port Index %x Status after reset %x\n", PortIndex, ReadRegister16(Port));
1015
1016 //
1017 // is there a callback
1018 //
1019 if (m_SCECallBack)
1020 {
1021 //
1022 // issue callback
1023 //
1024 m_SCECallBack(m_SCEContext);
1025 }
1026
1027 return STATUS_SUCCESS;
1028 }
1029
1030 NTSTATUS
1031 CUSBHardwareDevice::GetPortStatus(
1032 ULONG PortId,
1033 OUT USHORT *PortStatus,
1034 OUT USHORT *PortChange)
1035 {
1036 USHORT Status;
1037
1038 //
1039 // sanity check
1040 //
1041 if (PortId > 1)
1042 {
1043 //
1044 // invalid index
1045 //
1046 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
1047 return STATUS_INVALID_PARAMETER;
1048 }
1049
1050 //
1051 // init status
1052 //
1053 *PortStatus = 0;
1054 *PortChange = 0;
1055
1056 //
1057 // read port status
1058 //
1059 Status = ReadRegister16(UHCI_PORTSC1 + PortId * 2);
1060 DPRINT("[USBUHCI] PortId %x Status %x\n", PortId, Status);
1061
1062 // build the status
1063 if (Status & UHCI_PORTSC_CURSTAT)
1064 {
1065 *PortStatus |= USB_PORT_STATUS_CONNECT;
1066 }
1067
1068 if (Status & UHCI_PORTSC_ENABLED)
1069 {
1070 *PortStatus |= USB_PORT_STATUS_ENABLE;
1071 }
1072
1073 if (Status & UHCI_PORTSC_RESET)
1074 {
1075 *PortStatus |= USB_PORT_STATUS_RESET;
1076 }
1077
1078 if (Status & UHCI_PORTSC_LOWSPEED)
1079 {
1080 *PortStatus |= USB_PORT_STATUS_LOW_SPEED;
1081 }
1082
1083 if (Status & UHCI_PORTSC_STATCHA)
1084 {
1085 *PortChange |= USB_PORT_STATUS_CONNECT;
1086 }
1087
1088 if (Status & UHCI_PORTSC_ENABCHA)
1089 {
1090 *PortChange |= USB_PORT_STATUS_ENABLE;
1091 }
1092
1093 if (m_PortResetChange & (1 << PortId))
1094 {
1095 *PortChange |= USB_PORT_STATUS_RESET;
1096 }
1097
1098 //
1099 // port always has power
1100 //
1101 *PortStatus |= USB_PORT_STATUS_POWER;
1102 return STATUS_SUCCESS;
1103 }
1104
1105 NTSTATUS
1106 CUSBHardwareDevice::ClearPortStatus(
1107 ULONG PortId,
1108 ULONG Feature)
1109 {
1110 ULONG PortRegister;
1111 USHORT PortStatus;
1112
1113 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Feature);
1114
1115 //
1116 // sanity check
1117 //
1118 if (PortId > 1)
1119 {
1120 //
1121 // invalid index
1122 //
1123 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
1124 return STATUS_INVALID_PARAMETER;
1125 }
1126
1127 //
1128 // read current status
1129 //
1130 PortRegister = UHCI_PORTSC1 + PortId * 2;
1131 PortStatus = ReadRegister16(PortRegister);
1132 DPRINT("[UHCI] PortStatus %x\n", PortStatus);
1133
1134 if (Feature == C_PORT_RESET)
1135 {
1136 //
1137 // UHCI is not supporting port reset register bit
1138 //
1139 m_PortResetChange &= ~(1 << PortId);
1140 }
1141 else if (Feature == C_PORT_CONNECTION || Feature == C_PORT_ENABLE)
1142 {
1143 //
1144 // clear port status changes
1145 //
1146 WriteRegister16(PortRegister, PortStatus);
1147 }
1148
1149 return STATUS_SUCCESS;
1150 }
1151
1152
1153 NTSTATUS
1154 CUSBHardwareDevice::SetPortFeature(
1155 ULONG PortId,
1156 ULONG Feature)
1157 {
1158 ULONG PortRegister;
1159
1160 DPRINT1("[UHCI] SetPortFeature PortId %x Feature %x\n", PortId, Feature);
1161
1162 //
1163 // sanity check
1164 //
1165 if (PortId > 1)
1166 {
1167 //
1168 // invalid index
1169 //
1170 DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
1171 return STATUS_INVALID_PARAMETER;
1172 }
1173
1174 PortRegister = UHCI_PORTSC1 + PortId * 2;
1175
1176 if (Feature == PORT_RESET)
1177 {
1178 //
1179 // reset port
1180 //
1181 return ResetPort(PortId);
1182 }
1183 else if (Feature == PORT_ENABLE)
1184 {
1185 //
1186 // reset port
1187 //
1188 WriteRegister16(PortRegister, ReadRegister16(PortRegister) | UHCI_PORTSC_ENABLED);
1189 }
1190 else if (Feature == PORT_POWER)
1191 {
1192 //
1193 // port power is no op, it is always enabled
1194 //
1195 }
1196
1197 return STATUS_SUCCESS;
1198 }
1199
1200
1201
1202 VOID
1203 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1204 PVOID CallBack,
1205 PVOID Context)
1206 {
1207 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1208 m_SCEContext = Context;
1209 }
1210
1211 BOOLEAN
1212 NTAPI
1213 InterruptServiceRoutine(
1214 IN PKINTERRUPT Interrupt,
1215 IN PVOID ServiceContext)
1216 {
1217 CUSBHardwareDevice *This;
1218 USHORT Status, Acknowledge;
1219
1220 //
1221 // get context
1222 //
1223 This = (CUSBHardwareDevice*) ServiceContext;
1224
1225 //
1226 // read register
1227 //
1228 Status = This->ReadRegister16(UHCI_USBSTS);
1229 DPRINT("InterruptServiceRoutine %x\n", Status);
1230
1231 //
1232 // check if the interrupt signaled are from us
1233 //
1234 if ((Status & This->m_InterruptMask) == 0)
1235 {
1236 if (Status != 0)
1237 {
1238 //
1239 // FIXME: received unexpected interrupt
1240 //
1241 DPRINT1("[USBUHCI] Unexpected interrupt %x\n", Status);
1242 This->WriteRegister16(UHCI_USBSTS, Status);
1243 }
1244
1245 //
1246 // shared interrupt
1247 //
1248 return FALSE;
1249 }
1250
1251 //
1252 // check for the interrupt cause
1253 //
1254 Acknowledge = 0;
1255
1256 if (Status & UHCI_USBSTS_USBINT)
1257 {
1258 //
1259 // transfer finished
1260 //
1261 Acknowledge |= UHCI_USBSTS_USBINT;
1262 }
1263
1264 if (Status & UHCI_USBSTS_ERRINT)
1265 {
1266 //
1267 // error interrupt
1268 //
1269 Acknowledge |= UHCI_USBSTS_ERRINT;
1270 DPRINT1("[UHCI] Error interrupt\n");
1271 }
1272
1273 if (Status & UHCI_USBSTS_RESDET)
1274 {
1275 //
1276 // resume detected
1277 //
1278 DPRINT1("[UHCI] Resume detected\n");
1279 Acknowledge |= UHCI_USBSTS_RESDET;
1280 }
1281
1282 if (Status & UHCI_USBSTS_HOSTERR)
1283 {
1284 //
1285 // host system error
1286 //
1287 DPRINT1("[UHCI] Host System Error\n");
1288 Acknowledge |= UHCI_USBSTS_HOSTERR;
1289 }
1290
1291 if (Status & UHCI_USBSTS_HCPRERR)
1292 {
1293 //
1294 // processing error
1295 //
1296 DPRINT1("[UHCI] Process Error\n");
1297 Acknowledge |= UHCI_USBSTS_HCPRERR;
1298 }
1299
1300 if (Status & UHCI_USBSTS_HCHALT)
1301 {
1302 //
1303 // controller halted
1304 //
1305 DPRINT1("[UHCI] Host controller halted\n");
1306
1307 //
1308 // disable interrupts
1309 //
1310 This->WriteRegister16(UHCI_USBINTR, 0);
1311 This->m_InterruptMask = 0;
1312 }
1313
1314 //
1315 // do we have something to acknowledge
1316 //
1317 if (Acknowledge)
1318 {
1319 //
1320 // acknowledge interrupt
1321 //
1322 This->WriteRegister16(UHCI_USBSTS, Acknowledge);
1323
1324 //
1325 // queue dpc
1326 //
1327 KeInsertQueueDpc(&This->m_IntDpcObject, UlongToPtr(Status), NULL);
1328 }
1329
1330 //
1331 // interrupt handled
1332 //
1333 return TRUE;
1334 }
1335
1336
1337 VOID
1338 CUSBHardwareDevice::WriteRegister8(
1339 IN ULONG Register,
1340 IN UCHAR Value)
1341 {
1342 WRITE_PORT_UCHAR((PUCHAR)((ULONG)m_Base + Register), Value);
1343 }
1344
1345
1346 VOID
1347 CUSBHardwareDevice::WriteRegister16(
1348 ULONG Register,
1349 USHORT Value)
1350 {
1351 WRITE_PORT_USHORT((PUSHORT)((ULONG)m_Base + Register), Value);
1352 }
1353
1354
1355 VOID
1356 CUSBHardwareDevice::WriteRegister32(
1357 ULONG Register,
1358 ULONG Value)
1359 {
1360 WRITE_PORT_ULONG((PULONG)((ULONG)m_Base + Register), Value);
1361 }
1362
1363
1364 UCHAR
1365 CUSBHardwareDevice::ReadRegister8(
1366 ULONG Register)
1367 {
1368 return READ_PORT_UCHAR((PUCHAR)((ULONG)m_Base + Register));
1369 }
1370
1371
1372 USHORT
1373 CUSBHardwareDevice::ReadRegister16(
1374 ULONG Register)
1375 {
1376 return READ_PORT_USHORT((PUSHORT)((ULONG)m_Base + Register));
1377 }
1378
1379
1380 ULONG
1381 CUSBHardwareDevice::ReadRegister32(
1382 ULONG Register)
1383 {
1384 return READ_PORT_ULONG((PULONG)((ULONG)m_Base + Register));
1385 }
1386
1387 VOID
1388 CUSBHardwareDevice::GetQueueHead(
1389 IN ULONG QueueHeadIndex,
1390 OUT PUHCI_QUEUE_HEAD *OutQueueHead)
1391 {
1392 //
1393 // sanity check
1394 //
1395 ASSERT(QueueHeadIndex < 5);
1396
1397 //
1398 // store queue head
1399 //
1400 *OutQueueHead = m_QueueHead[QueueHeadIndex];
1401 }
1402
1403 VOID
1404 NTAPI
1405 UhciDefferedRoutine(
1406 IN PKDPC Dpc,
1407 IN PVOID DeferredContext,
1408 IN PVOID SystemArgument1,
1409 IN PVOID SystemArgument2)
1410 {
1411 CUSBHardwareDevice *This;
1412 ULONG Status;
1413
1414 //
1415 // get parameters
1416 //
1417 This = (CUSBHardwareDevice*)DeferredContext;
1418
1419 DPRINT("UhciDefferedRoutine\n");
1420
1421 //
1422 // get status
1423 //
1424 Status = PtrToUlong(SystemArgument1);
1425 if (Status & (UHCI_USBSTS_USBINT | UHCI_USBSTS_ERRINT))
1426 {
1427 //
1428 // a transfer finished, inform the queue
1429 //
1430 This->m_UsbQueue->TransferInterrupt(Status & UHCI_USBSTS_USBINT);
1431 return;
1432 }
1433
1434 //
1435 // other event
1436 //
1437 DPRINT1("[USBUHCI] Status %x not handled\n", Status);
1438 }
1439
1440 VOID
1441 NTAPI
1442 TimerDpcRoutine(
1443 IN PKDPC Dpc,
1444 IN PVOID DeferredContext,
1445 IN PVOID SystemArgument1,
1446 IN PVOID SystemArgument2)
1447 {
1448 CUSBHardwareDevice *This;
1449 USHORT PortStatus = 0;
1450 USHORT PortChange = 0;
1451
1452 // get parameters
1453 This = (CUSBHardwareDevice*)DeferredContext;
1454
1455 // check port 0
1456 This->GetPortStatus(0, &PortStatus, &PortChange);
1457 if (PortChange)
1458 {
1459 // invoke status change work item routine
1460 StatusChangeWorkItemRoutine(DeferredContext);
1461 return;
1462 }
1463
1464 // check port 1
1465 This->GetPortStatus(1, &PortStatus, &PortChange);
1466 if (PortChange)
1467 {
1468 // invoke status change work item routine
1469 StatusChangeWorkItemRoutine(DeferredContext);
1470 }
1471 }
1472
1473
1474 VOID
1475 NTAPI
1476 StatusChangeWorkItemRoutine(
1477 PVOID Context)
1478 {
1479 //
1480 // cast to hardware object
1481 //
1482 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1483
1484 //
1485 // is there a callback
1486 //
1487 if (This->m_SCECallBack)
1488 {
1489 //
1490 // issue callback
1491 //
1492 This->m_SCECallBack(This->m_SCEContext);
1493 }
1494
1495 }
1496
1497 NTSTATUS
1498 NTAPI
1499 CreateUSBHardware(
1500 PUSBHARDWAREDEVICE *OutHardware)
1501 {
1502 PUSBHARDWAREDEVICE This;
1503
1504 This = new(NonPagedPool, TAG_USBUHCI) CUSBHardwareDevice(0);
1505
1506 if (!This)
1507 return STATUS_INSUFFICIENT_RESOURCES;
1508
1509 This->AddRef();
1510
1511 // return result
1512 *OutHardware = (PUSBHARDWAREDEVICE)This;
1513
1514 return STATUS_SUCCESS;
1515 }