cdc2bb917905cfc9c076dba985682434fa9be601
[reactos.git] / drivers / usb / usbehci_new / hardware.cpp
1 /*
2 * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbehci/hcd_controller.cpp
5 * PURPOSE: USB EHCI 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 "usbehci.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 EhciDefferedRoutine(
26 IN PKDPC Dpc,
27 IN PVOID DeferredContext,
28 IN PVOID SystemArgument1,
29 IN PVOID SystemArgument2);
30
31 VOID
32 NTAPI
33 StatusChangeWorkItemRoutine(PVOID Context);
34
35 class CUSBHardwareDevice : public IUSBHardwareDevice
36 {
37 public:
38 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
39
40 STDMETHODIMP_(ULONG) AddRef()
41 {
42 InterlockedIncrement(&m_Ref);
43 return m_Ref;
44 }
45 STDMETHODIMP_(ULONG) Release()
46 {
47 InterlockedDecrement(&m_Ref);
48
49 if (!m_Ref)
50 {
51 delete this;
52 return 0;
53 }
54 return m_Ref;
55 }
56 // com
57 NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject);
58 NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources);
59 NTSTATUS PnpStop(void);
60 NTSTATUS HandlePower(PIRP Irp);
61 NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed);
62 NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
63 NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
64
65 NTSTATUS StartController();
66 NTSTATUS StopController();
67 NTSTATUS ResetController();
68 NTSTATUS ResetPort(ULONG PortIndex);
69
70 NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange);
71 NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status);
72 NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature);
73
74 VOID SetAsyncListRegister(ULONG PhysicalAddress);
75 VOID SetPeriodicListRegister(ULONG PhysicalAddress);
76 struct _QUEUE_HEAD * GetAsyncListQueueHead();
77 ULONG GetPeriodicListRegister();
78
79 VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context);
80
81 KIRQL AcquireDeviceLock(void);
82 VOID ReleaseDeviceLock(KIRQL OldLevel);
83 // set command
84 VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
85
86 // get command
87 VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
88
89
90 // local
91 BOOLEAN InterruptService();
92
93 // friend function
94 friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
95 friend VOID NTAPI EhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
96 friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
97 // constructor / destructor
98 CUSBHardwareDevice(IUnknown *OuterUnknown){}
99 virtual ~CUSBHardwareDevice(){}
100
101 protected:
102 LONG m_Ref; // reference count
103 PDRIVER_OBJECT m_DriverObject; // driver object
104 PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo
105 PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller)
106 PDEVICE_OBJECT m_NextDeviceObject; // lower device object
107 KSPIN_LOCK m_Lock; // hardware lock
108 PKINTERRUPT m_Interrupt; // interrupt object
109 KDPC m_IntDpcObject; // dpc object for deferred isr processing
110 PVOID VirtualBase; // virtual base for memory manager
111 PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager
112 PULONG m_Base; // EHCI operational port base registers
113 PDMA_ADAPTER m_Adapter; // dma adapter object
114 ULONG m_MapRegisters; // map registers count
115 EHCI_CAPS m_Capabilities; // EHCI caps
116 USHORT m_VendorID; // vendor id
117 USHORT m_DeviceID; // device id
118 PQUEUE_HEAD AsyncQueueHead; // async queue head terminator
119 PUSBQUEUE m_UsbQueue; // usb request queue
120 PDMAMEMORYMANAGER m_MemoryManager; // memory manager
121 HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine
122 PVOID m_SCEContext; // status change callback routine context
123 BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress
124 WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
125 ULONG m_SyncFramePhysAddr; // periodic frame list physical address
126 BOOLEAN m_ResetInProgress[16]; // set when a reset is in progress
127
128
129 // read register
130 ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
131
132 // write register
133 VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
134 };
135
136 //=================================================================================================
137 // COM
138 //
139 NTSTATUS
140 STDMETHODCALLTYPE
141 CUSBHardwareDevice::QueryInterface(
142 IN REFIID refiid,
143 OUT PVOID* Output)
144 {
145 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
146 {
147 *Output = PVOID(PUNKNOWN(this));
148 PUNKNOWN(*Output)->AddRef();
149 return STATUS_SUCCESS;
150 }
151
152 return STATUS_UNSUCCESSFUL;
153 }
154
155 NTSTATUS
156 CUSBHardwareDevice::Initialize(
157 PDRIVER_OBJECT DriverObject,
158 PDEVICE_OBJECT FunctionalDeviceObject,
159 PDEVICE_OBJECT PhysicalDeviceObject,
160 PDEVICE_OBJECT LowerDeviceObject)
161 {
162 BUS_INTERFACE_STANDARD BusInterface;
163 PCI_COMMON_CONFIG PciConfig;
164 NTSTATUS Status;
165 ULONG BytesRead;
166
167 DPRINT1("CUSBHardwareDevice::Initialize\n");
168
169 //
170 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
171 //
172 Status = CreateDMAMemoryManager(&m_MemoryManager);
173 if (!NT_SUCCESS(Status))
174 {
175 DPRINT1("Failed to create DMAMemoryManager Object\n");
176 return Status;
177 }
178
179 //
180 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
181 //
182 Status = CreateUSBQueue(&m_UsbQueue);
183 if (!NT_SUCCESS(Status))
184 {
185 DPRINT1("Failed to create UsbQueue!\n");
186 return Status;
187 }
188
189 //
190 // store device objects
191 //
192 m_DriverObject = DriverObject;
193 m_FunctionalDeviceObject = FunctionalDeviceObject;
194 m_PhysicalDeviceObject = PhysicalDeviceObject;
195 m_NextDeviceObject = LowerDeviceObject;
196
197 //
198 // initialize device lock
199 //
200 KeInitializeSpinLock(&m_Lock);
201
202 //
203 // intialize status change work item
204 //
205 ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
206
207 m_VendorID = 0;
208 m_DeviceID = 0;
209
210 Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
211 if (!NT_SUCCESS(Status))
212 {
213 DPRINT1("Failed to get BusInteface!\n");
214 return Status;
215 }
216
217 BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
218 PCI_WHICHSPACE_CONFIG,
219 &PciConfig,
220 0,
221 PCI_COMMON_HDR_LENGTH);
222
223 if (BytesRead != PCI_COMMON_HDR_LENGTH)
224 {
225 DPRINT1("Failed to get pci config information!\n");
226 return STATUS_SUCCESS;
227 }
228
229 m_VendorID = PciConfig.VendorID;
230 m_DeviceID = PciConfig.DeviceID;
231
232
233 if (PciConfig.Command & PCI_ENABLE_BUS_MASTER)
234 {
235 //
236 // master is enabled
237 //
238 return STATUS_SUCCESS;
239 }
240
241 DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n");
242
243 PciConfig.Command |= PCI_ENABLE_BUS_MASTER;
244 BusInterface.SetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &PciConfig, 0, PCI_COMMON_HDR_LENGTH);
245
246 BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
247 PCI_WHICHSPACE_CONFIG,
248 &PciConfig,
249 0,
250 PCI_COMMON_HDR_LENGTH);
251
252 if (BytesRead != PCI_COMMON_HDR_LENGTH)
253 {
254 DPRINT1("Failed to get pci config information!\n");
255 ASSERT(FALSE);
256 return STATUS_SUCCESS;
257 }
258
259 if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER))
260 {
261 PciConfig.Command |= PCI_ENABLE_BUS_MASTER;
262 DPRINT1("Failed to enable master\n");
263 return STATUS_UNSUCCESSFUL;
264 }
265 return STATUS_SUCCESS;
266 }
267
268 VOID
269 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
270 {
271 PULONG Register;
272 Register = (PULONG)UsbCmd;
273 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register);
274 }
275
276 VOID
277 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
278 {
279 PULONG Register;
280 Register = (PULONG)UsbCmd;
281 *Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD));
282 }
283
284 ULONG
285 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset)
286 {
287 return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset));
288 }
289
290 VOID
291 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value)
292 {
293 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value);
294 }
295
296 NTSTATUS
297 CUSBHardwareDevice::PnpStart(
298 PCM_RESOURCE_LIST RawResources,
299 PCM_RESOURCE_LIST TranslatedResources)
300 {
301 ULONG Index, Count;
302 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
303 DEVICE_DESCRIPTION DeviceDescription;
304 PHYSICAL_ADDRESS AsyncPhysicalAddress;
305 PVOID ResourceBase;
306 NTSTATUS Status;
307
308 DPRINT1("CUSBHardwareDevice::PnpStart\n");
309 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
310 {
311 //
312 // get resource descriptor
313 //
314 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
315
316 switch(ResourceDescriptor->Type)
317 {
318 case CmResourceTypeInterrupt:
319 {
320 KeInitializeDpc(&m_IntDpcObject,
321 EhciDefferedRoutine,
322 this);
323
324 Status = IoConnectInterrupt(&m_Interrupt,
325 InterruptServiceRoutine,
326 (PVOID)this,
327 NULL,
328 ResourceDescriptor->u.Interrupt.Vector,
329 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
330 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
331 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
332 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
333 ResourceDescriptor->u.Interrupt.Affinity,
334 FALSE);
335
336 if (!NT_SUCCESS(Status))
337 {
338 //
339 // failed to register interrupt
340 //
341 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
342 return Status;
343 }
344 break;
345 }
346 case CmResourceTypeMemory:
347 {
348 //
349 // get resource base
350 //
351 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
352 if (!ResourceBase)
353 {
354 //
355 // failed to map registers
356 //
357 DPRINT1("MmMapIoSpace failed\n");
358 return STATUS_INSUFFICIENT_RESOURCES;
359 }
360
361 //
362 // Get controllers capabilities
363 //
364 m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase);
365 m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + 2));
366 m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 4));
367 m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 8));
368
369 DPRINT1("Controller has %d Length\n", m_Capabilities.Length);
370 DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount);
371 DPRINT1("Controller EHCI Version %x\n", m_Capabilities.HCIVersion);
372 DPRINT1("Controler EHCI Caps HCSParamsLong %x\n", m_Capabilities.HCSParamsLong);
373 DPRINT1("Controler EHCI Caps HCCParamsLong %x\n", m_Capabilities.HCCParamsLong);
374
375 DPRINT1("Controler EHCI Caps PowerControl %x\n", m_Capabilities.HCSParams.PortPowerControl);
376 if (m_Capabilities.HCSParams.PortRouteRules)
377 {
378 for (Count = 0; Count < m_Capabilities.HCSParams.PortCount; Count++)
379 {
380 m_Capabilities.PortRoute[Count] = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + 12 + Count);
381 }
382 }
383
384 //
385 // Set m_Base to the address of Operational Register Space
386 //
387 m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
388 break;
389 }
390 }
391 }
392
393
394 //
395 // zero device description
396 //
397 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
398
399 //
400 // initialize device description
401 //
402 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
403 DeviceDescription.Master = TRUE;
404 DeviceDescription.ScatterGather = TRUE;
405 DeviceDescription.Dma32BitAddresses = TRUE;
406 DeviceDescription.DmaWidth = Width32Bits;
407 DeviceDescription.InterfaceType = PCIBus;
408 DeviceDescription.MaximumLength = MAXULONG;
409
410 //
411 // get dma adapter
412 //
413 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
414 if (!m_Adapter)
415 {
416 //
417 // failed to get dma adapter
418 //
419 DPRINT1("Failed to acquire dma adapter\n");
420 return STATUS_INSUFFICIENT_RESOURCES;
421 }
422
423 //
424 // Create Common Buffer
425 //
426 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
427 PAGE_SIZE * 4,
428 &PhysicalAddress,
429 FALSE);
430 if (!VirtualBase)
431 {
432 DPRINT1("Failed to allocate a common buffer\n");
433 return STATUS_INSUFFICIENT_RESOURCES;
434 }
435
436 //
437 // Stop the controller before modifying schedules
438 //
439 Status = StopController();
440 if (!NT_SUCCESS(Status))
441 return Status;
442
443 //
444 // Initialize the DMAMemoryManager
445 //
446 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
447 if (!NT_SUCCESS(Status))
448 {
449 DPRINT1("Failed to initialize the DMAMemoryManager\n");
450 return Status;
451 }
452
453 //
454 // Create a queuehead for the Async Register
455 //
456 m_MemoryManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&AsyncQueueHead, &AsyncPhysicalAddress);
457
458 AsyncQueueHead->AlternateNextPointer = TERMINATE_POINTER;
459 AsyncQueueHead->NextPointer = TERMINATE_POINTER;
460 AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
461 AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
462 AsyncQueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
463 AsyncQueueHead->Token.Bits.InterruptOnComplete = FALSE;
464 AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
465 AsyncQueueHead->Token.Bits.Halted = TRUE;
466 AsyncQueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
467 AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0;
468 AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
469 AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
470
471 InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
472
473 //
474 // Initialize the UsbQueue now that we have an AdapterObject.
475 //
476 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
477 if (!NT_SUCCESS(Status))
478 {
479 DPRINT1("Failed to Initialize the UsbQueue\n");
480 return Status;
481 }
482
483 //
484 // Start the controller
485 //
486 DPRINT1("Starting Controller\n");
487 Status = StartController();
488
489 //
490 // done
491 //
492 return Status;
493 }
494
495 NTSTATUS
496 CUSBHardwareDevice::PnpStop(void)
497 {
498 UNIMPLEMENTED
499 return STATUS_NOT_IMPLEMENTED;
500 }
501
502 NTSTATUS
503 CUSBHardwareDevice::HandlePower(
504 PIRP Irp)
505 {
506 UNIMPLEMENTED
507 return STATUS_NOT_IMPLEMENTED;
508 }
509
510 NTSTATUS
511 CUSBHardwareDevice::GetDeviceDetails(
512 OUT OPTIONAL PUSHORT VendorId,
513 OUT OPTIONAL PUSHORT DeviceId,
514 OUT OPTIONAL PULONG NumberOfPorts,
515 OUT OPTIONAL PULONG Speed)
516 {
517 if (VendorId)
518 *VendorId = m_VendorID;
519 if (DeviceId)
520 *DeviceId = m_DeviceID;
521 if (NumberOfPorts)
522 *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
523 //FIXME: What to returned here?
524 if (Speed)
525 *Speed = 0x200;
526 return STATUS_SUCCESS;
527 }
528
529 NTSTATUS CUSBHardwareDevice::GetDMA(
530 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
531 {
532 if (!m_MemoryManager)
533 return STATUS_UNSUCCESSFUL;
534 *OutDMAMemoryManager = m_MemoryManager;
535 return STATUS_SUCCESS;
536 }
537
538 NTSTATUS
539 CUSBHardwareDevice::GetUSBQueue(
540 OUT struct IUSBQueue **OutUsbQueue)
541 {
542 if (!m_UsbQueue)
543 return STATUS_UNSUCCESSFUL;
544 *OutUsbQueue = m_UsbQueue;
545 return STATUS_SUCCESS;
546 }
547
548
549 NTSTATUS
550 CUSBHardwareDevice::StartController(void)
551 {
552 EHCI_USBCMD_CONTENT UsbCmd;
553 ULONG UsbSts, FailSafe;
554
555 //
556 // check caps
557 //
558 if (m_Capabilities.HCCParams.CurAddrBits)
559 {
560 //
561 // disable 64-bit addressing
562 //
563 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
564 }
565
566
567 #if 1
568 //
569 // Stop the controller if its running
570 //
571 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
572 if (!(UsbSts & EHCI_STS_HALT))
573 {
574 DPRINT1("Stopping Controller %x\n", UsbSts);
575 StopController();
576 }
577 #endif
578
579 //
580 // Enable Interrupts and start execution
581 //
582 ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC;
583 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask);
584
585 KeStallExecutionProcessor(10);
586
587 ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR);
588
589 DPRINT1("Interrupt Mask %x\n", Status);
590 ASSERT((Status & Mask) == Mask);
591
592
593 //
594 // Assign the SyncList Register
595 //
596 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
597
598 //
599 // Set Schedules to Enable and Interrupt Threshold to 1ms.
600 //
601 RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT));
602
603 UsbCmd.PeriodicEnable = TRUE;
604 UsbCmd.IntThreshold = 0x8; //1ms
605 UsbCmd.Run = TRUE;
606 UsbCmd.FrameListSize = 0x0; //1024
607 SetCommandRegister(&UsbCmd);
608
609 //
610 // Wait for execution to start
611 //
612 for (FailSafe = 100; FailSafe > 1; FailSafe--)
613 {
614 KeStallExecutionProcessor(10);
615 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
616
617 if (!(UsbSts & EHCI_STS_HALT))
618 {
619 break;
620 }
621 }
622
623
624 if (UsbSts & EHCI_STS_HALT)
625 {
626 DPRINT1("Could not start execution on the controller\n");
627 return STATUS_UNSUCCESSFUL;
628 }
629
630 //
631 // Assign the AsyncList Register
632 //
633 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
634
635 //
636 // get command register
637 //
638 GetCommandRegister(&UsbCmd);
639
640 //
641 // preserve bits
642 //
643 UsbCmd.AsyncEnable = TRUE;
644
645 //
646 // enable async
647 //
648 SetCommandRegister(&UsbCmd);
649
650 //
651 // Wait for execution to start
652 //
653 for (FailSafe = 100; FailSafe > 1; FailSafe--)
654 {
655 KeStallExecutionProcessor(10);
656 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
657
658 if ((UsbSts & EHCI_STS_ASS))
659 {
660 break;
661 }
662 }
663
664 if (!(UsbSts & EHCI_STS_ASS))
665 {
666 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts);
667 ASSERT(FALSE);
668 return STATUS_UNSUCCESSFUL;
669 }
670
671 DPRINT1("UsbSts %x\n", UsbSts);
672 GetCommandRegister(&UsbCmd);
673
674 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable);
675 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable);
676 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold);
677 DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run);
678 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize);
679
680 //
681 // Set port routing to EHCI controller
682 //
683 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
684
685
686
687
688 DPRINT1("EHCI Started!\n");
689 return STATUS_SUCCESS;
690 }
691
692 NTSTATUS
693 CUSBHardwareDevice::StopController(void)
694 {
695 EHCI_USBCMD_CONTENT UsbCmd;
696 ULONG UsbSts, FailSafe;
697
698 //
699 // Disable Interrupts and stop execution
700 //
701 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
702
703 GetCommandRegister(&UsbCmd);
704 UsbCmd.Run = FALSE;
705 SetCommandRegister(&UsbCmd);
706
707 for (FailSafe = 100; FailSafe > 1; FailSafe--)
708 {
709 KeStallExecutionProcessor(10);
710 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
711 if (UsbSts & EHCI_STS_HALT)
712 {
713 break;
714 }
715 }
716
717 if (!(UsbSts & EHCI_STS_HALT))
718 {
719 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
720 return STATUS_UNSUCCESSFUL;
721 }
722
723 return STATUS_SUCCESS;
724 }
725
726 NTSTATUS
727 CUSBHardwareDevice::ResetController(void)
728 {
729 UNIMPLEMENTED
730 return STATUS_NOT_IMPLEMENTED;
731 }
732
733 NTSTATUS
734 CUSBHardwareDevice::ResetPort(
735 IN ULONG PortIndex)
736 {
737 ULONG PortStatus;
738 LARGE_INTEGER Timeout;
739
740 if (PortIndex > m_Capabilities.HCSParams.PortCount)
741 return STATUS_UNSUCCESSFUL;
742
743 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
744 //
745 // check slow speed line before reset
746 //
747 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
748 {
749 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
750 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
751 return STATUS_DEVICE_NOT_CONNECTED;
752 }
753
754 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
755
756 //
757 // Reset and clean enable
758 //
759 PortStatus |= EHCI_PRT_RESET;
760 PortStatus &= ~EHCI_PRT_ENABLED;
761 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
762
763 //
764 // delay is 20 ms for port reset as per USB 2.0 spec
765 //
766 Timeout.QuadPart = 20;
767 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
768
769 //
770 // convert to 100 ns units (absolute)
771 //
772 Timeout.QuadPart *= -10000;
773
774 //
775 // perform the wait
776 //
777 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
778
779 //
780 // Clear reset
781 //
782 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
783 PortStatus &= ~EHCI_PRT_RESET;
784 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
785
786 do
787 {
788 //
789 // wait
790 //
791 KeStallExecutionProcessor(100);
792
793 //
794 // Check that the port reset
795 //
796 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
797 if (!(PortStatus & EHCI_PRT_RESET))
798 break;
799 } while (TRUE);
800
801 //
802 // delay is 10 ms
803 //
804 Timeout.QuadPart = 10;
805 DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart);
806
807 //
808 // convert to 100 ns units (absolute)
809 //
810 Timeout.QuadPart *= -10000;
811
812 //
813 // perform the wait
814 //
815 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
816
817 //
818 // check slow speed line after reset
819 //
820 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
821 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
822 {
823 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
824 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
825 return STATUS_DEVICE_NOT_CONNECTED;
826 }
827
828 //
829 // this must be enabled now
830 //
831 ASSERT(PortStatus & EHCI_PRT_ENABLED);
832
833 return STATUS_SUCCESS;
834 }
835
836 NTSTATUS
837 CUSBHardwareDevice::GetPortStatus(
838 ULONG PortId,
839 OUT USHORT *PortStatus,
840 OUT USHORT *PortChange)
841 {
842 ULONG Value;
843 USHORT Status = 0, Change = 0;
844
845 if (PortId > m_Capabilities.HCSParams.PortCount)
846 return STATUS_UNSUCCESSFUL;
847
848 //
849 // Get the value of the Port Status and Control Register
850 //
851 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
852
853 //
854 // If the PowerPortControl is 0 then host controller does not have power control switches
855 if (!m_Capabilities.HCSParams.PortPowerControl)
856 {
857 Status |= USB_PORT_STATUS_POWER;
858 }
859 else
860 {
861 // Check the value of PortPower
862 if (Value & EHCI_PRT_POWER)
863 {
864 Status |= USB_PORT_STATUS_POWER;
865 }
866 }
867
868 // Get Connected Status
869 if (Value & EHCI_PRT_CONNECTED)
870 {
871 Status |= USB_PORT_STATUS_CONNECT;
872
873 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
874 if (Value & EHCI_PRT_SLOWSPEEDLINE)
875 Status |= USB_PORT_STATUS_LOW_SPEED;
876 else
877 Status |= USB_PORT_STATUS_HIGH_SPEED;
878 }
879
880 // Get Enabled Status
881 if (Value & EHCI_PRT_ENABLED)
882 Status |= USB_PORT_STATUS_ENABLE;
883
884 // Is it suspended?
885 if (Value & EHCI_PRT_SUSPEND)
886 Status |= USB_PORT_STATUS_SUSPEND;
887
888 // a overcurrent is active?
889 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
890 Status |= USB_PORT_STATUS_OVER_CURRENT;
891
892 // In a reset state?
893 if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
894 {
895 Status |= USB_PORT_STATUS_RESET;
896 Change |= USB_PORT_STATUS_RESET;
897 }
898
899 //
900 // FIXME: Is the Change here correct?
901 //
902 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
903 Change |= USB_PORT_STATUS_CONNECT;
904
905 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
906 Change |= USB_PORT_STATUS_ENABLE;
907
908 *PortStatus = Status;
909 *PortChange = Change;
910
911 return STATUS_SUCCESS;
912 }
913
914 NTSTATUS
915 CUSBHardwareDevice::ClearPortStatus(
916 ULONG PortId,
917 ULONG Status)
918 {
919 ULONG Value;
920
921 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
922
923 if (PortId > m_Capabilities.HCSParams.PortCount)
924 return STATUS_UNSUCCESSFUL;
925
926 if (Status == C_PORT_RESET)
927 {
928 //
929 // update port status
930 //
931 m_ResetInProgress[PortId] = FALSE;
932 }
933
934 if (Status == C_PORT_CONNECTION)
935 {
936 LARGE_INTEGER Timeout;
937
938 //
939 // reset status change bits
940 //
941 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
942 Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
943 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
944
945 //
946 // delay is 100 ms
947 //
948 Timeout.QuadPart = 100;
949 DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart);
950
951 //
952 // convert to 100 ns units (absolute)
953 //
954 Timeout.QuadPart *= -10000;
955
956 //
957 // perform the wait
958 //
959 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
960 }
961
962 return STATUS_SUCCESS;
963 }
964
965
966 NTSTATUS
967 CUSBHardwareDevice::SetPortFeature(
968 ULONG PortId,
969 ULONG Feature)
970 {
971 ULONG Value;
972
973 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
974
975 if (PortId > m_Capabilities.HCSParams.PortCount)
976 return STATUS_UNSUCCESSFUL;
977
978 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
979
980 if (Feature == PORT_ENABLE)
981 {
982 //
983 // FIXME: EHCI Ports can only be disabled via reset
984 //
985 DPRINT1("PORT_ENABLE not supported for EHCI\n");
986 }
987
988 if (Feature == PORT_RESET)
989 {
990 ResetPort(PortId);
991
992 //
993 // update cached settings
994 //
995 m_ResetInProgress[PortId] = TRUE;
996
997 //
998 // is there a status change callback
999 //
1000 if (m_SCECallBack != NULL)
1001 {
1002 //
1003 // issue callback
1004 //
1005 m_SCECallBack(m_SCEContext);
1006 }
1007 }
1008
1009 if (Feature == PORT_POWER)
1010 {
1011 if (m_Capabilities.HCSParams.PortPowerControl)
1012 {
1013 ULONG Value;
1014 LARGE_INTEGER Timeout;
1015
1016 //
1017 // enable port power
1018 //
1019 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
1020 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC, Value);
1021
1022 //
1023 // delay is 20 ms
1024 //
1025 Timeout.QuadPart = 20;
1026 DPRINT1("Waiting %d milliseconds for port power up\n", Timeout.LowPart);
1027
1028 //
1029 // convert to 100 ns units (absolute)
1030 //
1031 Timeout.QuadPart *= -10000;
1032
1033 //
1034 // perform the wait
1035 //
1036 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1037 }
1038 }
1039 return STATUS_SUCCESS;
1040 }
1041
1042 VOID
1043 CUSBHardwareDevice::SetAsyncListRegister(
1044 ULONG PhysicalAddress)
1045 {
1046 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
1047 }
1048
1049 VOID
1050 CUSBHardwareDevice::SetPeriodicListRegister(
1051 ULONG PhysicalAddress)
1052 {
1053 //
1054 // store physical address
1055 //
1056 m_SyncFramePhysAddr = PhysicalAddress;
1057 }
1058
1059 struct _QUEUE_HEAD *
1060 CUSBHardwareDevice::GetAsyncListQueueHead()
1061 {
1062 return AsyncQueueHead;
1063 }
1064
1065 ULONG CUSBHardwareDevice::GetPeriodicListRegister()
1066 {
1067 UNIMPLEMENTED
1068 return NULL;
1069 }
1070
1071 VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1072 PVOID CallBack,
1073 PVOID Context)
1074 {
1075 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1076 m_SCEContext = Context;
1077 }
1078
1079 KIRQL
1080 CUSBHardwareDevice::AcquireDeviceLock(void)
1081 {
1082 KIRQL OldLevel;
1083
1084 //
1085 // acquire lock
1086 //
1087 KeAcquireSpinLock(&m_Lock, &OldLevel);
1088
1089 //
1090 // return old irql
1091 //
1092 return OldLevel;
1093 }
1094
1095
1096 VOID
1097 CUSBHardwareDevice::ReleaseDeviceLock(
1098 KIRQL OldLevel)
1099 {
1100 KeReleaseSpinLock(&m_Lock, OldLevel);
1101 }
1102
1103 BOOLEAN
1104 NTAPI
1105 InterruptServiceRoutine(
1106 IN PKINTERRUPT Interrupt,
1107 IN PVOID ServiceContext)
1108 {
1109 CUSBHardwareDevice *This;
1110 ULONG CStatus;
1111
1112 This = (CUSBHardwareDevice*) ServiceContext;
1113 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
1114
1115 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
1116 //
1117 // Check that it belongs to EHCI
1118 //
1119 if (!CStatus)
1120 return FALSE;
1121
1122 //
1123 // Clear the Status
1124 //
1125 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
1126
1127 if (CStatus & EHCI_STS_FATAL)
1128 {
1129 This->StopController();
1130 DPRINT1("EHCI: Host System Error!\n");
1131 return TRUE;
1132 }
1133
1134 if (CStatus & EHCI_ERROR_INT)
1135 {
1136 DPRINT1("EHCI Status = 0x%x\n", CStatus);
1137 }
1138
1139 if (CStatus & EHCI_STS_HALT)
1140 {
1141 DPRINT1("Host Error Unexpected Halt\n");
1142 // FIXME: Reset controller\n");
1143 return TRUE;
1144 }
1145
1146 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
1147 return TRUE;
1148 }
1149
1150 VOID NTAPI
1151 EhciDefferedRoutine(
1152 IN PKDPC Dpc,
1153 IN PVOID DeferredContext,
1154 IN PVOID SystemArgument1,
1155 IN PVOID SystemArgument2)
1156 {
1157 CUSBHardwareDevice *This;
1158 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
1159 NTSTATUS Status = STATUS_SUCCESS;
1160 EHCI_USBCMD_CONTENT UsbCmd;
1161
1162 This = (CUSBHardwareDevice*) SystemArgument1;
1163 CStatus = (ULONG) SystemArgument2;
1164
1165 DPRINT("CStatus %x\n", CStatus);
1166
1167 //
1168 // check for completion of async schedule
1169 //
1170 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
1171 {
1172 //
1173 // check if there is a door bell ring in progress
1174 //
1175 if (This->m_DoorBellRingInProgress == FALSE)
1176 {
1177 if (CStatus & EHCI_ERROR_INT)
1178 {
1179 //
1180 // controller reported error
1181 //
1182 DPRINT1("CStatus %x\n", CStatus);
1183 Status = STATUS_UNSUCCESSFUL;
1184 PC_ASSERT(FALSE);
1185 return;
1186 }
1187
1188 //
1189 // inform IUSBQueue of a completed queue head
1190 //
1191 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1192
1193 //
1194 // was a queue head completed?
1195 //
1196 if (ShouldRingDoorBell)
1197 {
1198 //
1199 // set door ring bell in progress status flag
1200 //
1201 This->m_DoorBellRingInProgress = TRUE;
1202
1203 //
1204 // get command register
1205 //
1206 This->GetCommandRegister(&UsbCmd);
1207
1208 //
1209 // set door rang bell bit
1210 //
1211 UsbCmd.DoorBell = TRUE;
1212
1213 //
1214 // update command status
1215 //
1216 This->SetCommandRegister(&UsbCmd);
1217 }
1218 }
1219 }
1220
1221 //
1222 // check if the controller has acknowledged the door bell
1223 //
1224 if (CStatus & EHCI_STS_IAA)
1225 {
1226 //
1227 // controller has acknowledged, assert we rang the bell
1228 //
1229 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1230
1231 //
1232 // now notify IUSBQueue that it can free completed requests
1233 //
1234 This->m_UsbQueue->CompleteAsyncRequests();
1235
1236 //
1237 // door ring bell completed
1238 //
1239 This->m_DoorBellRingInProgress = FALSE;
1240 }
1241
1242 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1243 if (CStatus & EHCI_STS_PCD)
1244 {
1245 for (i = 0; i < PortCount; i++)
1246 {
1247 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1248
1249 //
1250 // Device connected or removed
1251 //
1252 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1253 {
1254 if (PortStatus & EHCI_PRT_CONNECTED)
1255 {
1256 DPRINT1("Device connected on port %d\n", i);
1257
1258 //
1259 //FIXME: Determine device speed
1260 //
1261 if (This->m_Capabilities.HCSParams.CHCCount)
1262 {
1263 if (PortStatus & EHCI_PRT_ENABLED)
1264 {
1265 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1266 }
1267
1268 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
1269 {
1270 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1271 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
1272 continue;
1273 }
1274 }
1275 }
1276 else
1277 {
1278 DPRINT1("Device disconnected on port %d\n", i);
1279 }
1280
1281 //
1282 // is there a status change callback
1283 //
1284 if (This->m_SCECallBack != NULL)
1285 {
1286 //
1287 // queue work item for processing
1288 //
1289 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1290 }
1291
1292 //
1293 // FIXME: This needs to be saved somewhere
1294 //
1295 }
1296 }
1297 }
1298 return;
1299 }
1300
1301 VOID
1302 NTAPI
1303 StatusChangeWorkItemRoutine(
1304 PVOID Context)
1305 {
1306 //
1307 // cast to hardware object
1308 //
1309 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1310
1311 //
1312 // is there a callback
1313 //
1314 if (This->m_SCECallBack)
1315 {
1316 //
1317 // issue callback
1318 //
1319 This->m_SCECallBack(This->m_SCEContext);
1320 }
1321
1322 }
1323
1324 NTSTATUS
1325 CreateUSBHardware(
1326 PUSBHARDWAREDEVICE *OutHardware)
1327 {
1328 PUSBHARDWAREDEVICE This;
1329
1330 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1331
1332 if (!This)
1333 return STATUS_INSUFFICIENT_RESOURCES;
1334
1335 This->AddRef();
1336
1337 // return result
1338 *OutHardware = (PUSBHARDWAREDEVICE)This;
1339
1340 return STATUS_SUCCESS;
1341 }