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