[USBEHCI]
[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
739 if (PortIndex > m_Capabilities.HCSParams.PortCount)
740 return STATUS_UNSUCCESSFUL;
741
742 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
743 //
744 // check slow speed line before reset
745 //
746 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
747 {
748 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
749 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
750 return STATUS_DEVICE_NOT_CONNECTED;
751 }
752
753 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
754
755 //
756 // Reset and clean enable
757 //
758 PortStatus |= EHCI_PRT_RESET;
759 PortStatus &= ~EHCI_PRT_ENABLED;
760 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
761
762 KeStallExecutionProcessor(100);
763
764 //
765 // Clear reset
766 //
767 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
768 PortStatus &= ~EHCI_PRT_RESET;
769 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
770
771 do
772 {
773 //
774 // wait
775 //
776 KeStallExecutionProcessor(100);
777
778 //
779 // Check that the port reset
780 //
781 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
782 if (!(PortStatus & EHCI_PRT_RESET))
783 break;
784 } while (TRUE);
785
786 //
787 // check slow speed line after reset
788 //
789 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
790 {
791 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
792 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
793 return STATUS_DEVICE_NOT_CONNECTED;
794 }
795
796 //
797 // this must be enabled now
798 //
799 ASSERT(PortStatus & EHCI_PRT_ENABLED);
800
801 return STATUS_SUCCESS;
802 }
803
804 NTSTATUS
805 CUSBHardwareDevice::GetPortStatus(
806 ULONG PortId,
807 OUT USHORT *PortStatus,
808 OUT USHORT *PortChange)
809 {
810 ULONG Value;
811 USHORT Status = 0, Change = 0;
812
813 if (PortId > m_Capabilities.HCSParams.PortCount)
814 return STATUS_UNSUCCESSFUL;
815
816 //
817 // Get the value of the Port Status and Control Register
818 //
819 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
820
821 //
822 // If the PowerPortControl is 0 then host controller does not have power control switches
823 if (!m_Capabilities.HCSParams.PortPowerControl)
824 {
825 Status |= USB_PORT_STATUS_POWER;
826 }
827 else
828 {
829 // Check the value of PortPower
830 if (Value & EHCI_PRT_POWER)
831 {
832 Status |= USB_PORT_STATUS_POWER;
833 }
834 }
835
836 // Get Connected Status
837 if (Value & EHCI_PRT_CONNECTED)
838 {
839 Status |= USB_PORT_STATUS_CONNECT;
840
841 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
842 if (Value & EHCI_PRT_SLOWSPEEDLINE)
843 Status |= USB_PORT_STATUS_LOW_SPEED;
844 else
845 Status |= USB_PORT_STATUS_HIGH_SPEED;
846 }
847
848 // Get Enabled Status
849 if (Value & EHCI_PRT_ENABLED)
850 Status |= USB_PORT_STATUS_ENABLE;
851
852 // Is it suspended?
853 if (Value & EHCI_PRT_SUSPEND)
854 Status |= USB_PORT_STATUS_SUSPEND;
855
856 // a overcurrent is active?
857 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
858 Status |= USB_PORT_STATUS_OVER_CURRENT;
859
860 // In a reset state?
861 if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
862 {
863 Status |= USB_PORT_STATUS_RESET;
864 Change |= USB_PORT_STATUS_RESET;
865 }
866
867 //
868 // FIXME: Is the Change here correct?
869 //
870 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
871 Change |= USB_PORT_STATUS_CONNECT;
872
873 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
874 Change |= USB_PORT_STATUS_ENABLE;
875
876 *PortStatus = Status;
877 *PortChange = Change;
878
879 return STATUS_SUCCESS;
880 }
881
882 NTSTATUS
883 CUSBHardwareDevice::ClearPortStatus(
884 ULONG PortId,
885 ULONG Status)
886 {
887 ULONG Value;
888
889 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
890
891 if (PortId > m_Capabilities.HCSParams.PortCount)
892 return STATUS_UNSUCCESSFUL;
893
894 if (Status == C_PORT_RESET)
895 {
896 //
897 // update port status
898 //
899 m_ResetInProgress[PortId] = FALSE;
900 }
901
902 if (Status == C_PORT_CONNECTION)
903 {
904 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
905 Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
906 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
907 }
908
909 return STATUS_SUCCESS;
910 }
911
912
913 NTSTATUS
914 CUSBHardwareDevice::SetPortFeature(
915 ULONG PortId,
916 ULONG Feature)
917 {
918 ULONG Value;
919
920 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
921
922 if (PortId > m_Capabilities.HCSParams.PortCount)
923 return STATUS_UNSUCCESSFUL;
924
925 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
926
927 if (Feature == PORT_ENABLE)
928 {
929 //
930 // FIXME: EHCI Ports can only be disabled via reset
931 //
932 DPRINT1("PORT_ENABLE not supported for EHCI\n");
933 }
934
935 if (Feature == PORT_RESET)
936 {
937 ResetPort(PortId);
938
939 //
940 // update cached settings
941 //
942 m_ResetInProgress[PortId] = TRUE;
943
944 //
945 // is there a status change callback
946 //
947 if (m_SCECallBack != NULL)
948 {
949 //
950 // issue callback
951 //
952 m_SCECallBack(m_SCEContext);
953 }
954 }
955
956 if (Feature == PORT_POWER)
957 {
958 if (m_Capabilities.HCSParams.PortPowerControl)
959 {
960 //
961 // enable port power
962 //
963 ULONG Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
964 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC, Value);
965 }
966 }
967 return STATUS_SUCCESS;
968 }
969
970 VOID
971 CUSBHardwareDevice::SetAsyncListRegister(
972 ULONG PhysicalAddress)
973 {
974 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
975 }
976
977 VOID
978 CUSBHardwareDevice::SetPeriodicListRegister(
979 ULONG PhysicalAddress)
980 {
981 //
982 // store physical address
983 //
984 m_SyncFramePhysAddr = PhysicalAddress;
985 }
986
987 struct _QUEUE_HEAD *
988 CUSBHardwareDevice::GetAsyncListQueueHead()
989 {
990 return AsyncQueueHead;
991 }
992
993 ULONG CUSBHardwareDevice::GetPeriodicListRegister()
994 {
995 UNIMPLEMENTED
996 return NULL;
997 }
998
999 VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1000 PVOID CallBack,
1001 PVOID Context)
1002 {
1003 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1004 m_SCEContext = Context;
1005 }
1006
1007 KIRQL
1008 CUSBHardwareDevice::AcquireDeviceLock(void)
1009 {
1010 KIRQL OldLevel;
1011
1012 //
1013 // acquire lock
1014 //
1015 KeAcquireSpinLock(&m_Lock, &OldLevel);
1016
1017 //
1018 // return old irql
1019 //
1020 return OldLevel;
1021 }
1022
1023
1024 VOID
1025 CUSBHardwareDevice::ReleaseDeviceLock(
1026 KIRQL OldLevel)
1027 {
1028 KeReleaseSpinLock(&m_Lock, OldLevel);
1029 }
1030
1031 BOOLEAN
1032 NTAPI
1033 InterruptServiceRoutine(
1034 IN PKINTERRUPT Interrupt,
1035 IN PVOID ServiceContext)
1036 {
1037 CUSBHardwareDevice *This;
1038 ULONG CStatus;
1039
1040 This = (CUSBHardwareDevice*) ServiceContext;
1041 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
1042
1043 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
1044 //
1045 // Check that it belongs to EHCI
1046 //
1047 if (!CStatus)
1048 return FALSE;
1049
1050 //
1051 // Clear the Status
1052 //
1053 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
1054
1055 if (CStatus & EHCI_STS_FATAL)
1056 {
1057 This->StopController();
1058 DPRINT1("EHCI: Host System Error!\n");
1059 return TRUE;
1060 }
1061
1062 if (CStatus & EHCI_ERROR_INT)
1063 {
1064 DPRINT1("EHCI Status = 0x%x\n", CStatus);
1065 }
1066
1067 if (CStatus & EHCI_STS_HALT)
1068 {
1069 DPRINT1("Host Error Unexpected Halt\n");
1070 // FIXME: Reset controller\n");
1071 return TRUE;
1072 }
1073
1074 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
1075 return TRUE;
1076 }
1077
1078 VOID NTAPI
1079 EhciDefferedRoutine(
1080 IN PKDPC Dpc,
1081 IN PVOID DeferredContext,
1082 IN PVOID SystemArgument1,
1083 IN PVOID SystemArgument2)
1084 {
1085 CUSBHardwareDevice *This;
1086 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
1087 NTSTATUS Status = STATUS_SUCCESS;
1088 EHCI_USBCMD_CONTENT UsbCmd;
1089
1090 This = (CUSBHardwareDevice*) SystemArgument1;
1091 CStatus = (ULONG) SystemArgument2;
1092
1093 DPRINT("CStatus %x\n", CStatus);
1094
1095 //
1096 // check for completion of async schedule
1097 //
1098 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
1099 {
1100 //
1101 // check if there is a door bell ring in progress
1102 //
1103 if (This->m_DoorBellRingInProgress == FALSE)
1104 {
1105 if (CStatus & EHCI_ERROR_INT)
1106 {
1107 //
1108 // controller reported error
1109 //
1110 DPRINT1("CStatus %x\n", CStatus);
1111 Status = STATUS_UNSUCCESSFUL;
1112 PC_ASSERT(FALSE);
1113 return;
1114 }
1115
1116 //
1117 // inform IUSBQueue of a completed queue head
1118 //
1119 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1120
1121 //
1122 // was a queue head completed?
1123 //
1124 if (ShouldRingDoorBell)
1125 {
1126 //
1127 // set door ring bell in progress status flag
1128 //
1129 This->m_DoorBellRingInProgress = TRUE;
1130
1131 //
1132 // get command register
1133 //
1134 This->GetCommandRegister(&UsbCmd);
1135
1136 //
1137 // set door rang bell bit
1138 //
1139 UsbCmd.DoorBell = TRUE;
1140
1141 //
1142 // update command status
1143 //
1144 This->SetCommandRegister(&UsbCmd);
1145 }
1146 }
1147 }
1148
1149 //
1150 // check if the controller has acknowledged the door bell
1151 //
1152 if (CStatus & EHCI_STS_IAA)
1153 {
1154 //
1155 // controller has acknowledged, assert we rang the bell
1156 //
1157 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1158
1159 //
1160 // now notify IUSBQueue that it can free completed requests
1161 //
1162 This->m_UsbQueue->CompleteAsyncRequests();
1163
1164 //
1165 // door ring bell completed
1166 //
1167 This->m_DoorBellRingInProgress = FALSE;
1168 }
1169
1170 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1171 if (CStatus & EHCI_STS_PCD)
1172 {
1173 for (i = 0; i < PortCount; i++)
1174 {
1175 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1176
1177 //
1178 // Device connected or removed
1179 //
1180 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1181 {
1182 if (PortStatus & EHCI_PRT_CONNECTED)
1183 {
1184 DPRINT1("Device connected on port %d\n", i);
1185
1186 //
1187 //FIXME: Determine device speed
1188 //
1189 if (This->m_Capabilities.HCSParams.CHCCount)
1190 {
1191 if (PortStatus & EHCI_PRT_ENABLED)
1192 {
1193 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1194 }
1195
1196 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
1197 {
1198 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1199 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
1200 continue;
1201 }
1202 }
1203 }
1204 else
1205 {
1206 DPRINT1("Device disconnected on port %d\n", i);
1207 }
1208
1209 //
1210 // is there a status change callback
1211 //
1212 if (This->m_SCECallBack != NULL)
1213 {
1214 //
1215 // queue work item for processing
1216 //
1217 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1218 }
1219
1220 //
1221 // FIXME: This needs to be saved somewhere
1222 //
1223 }
1224 }
1225 }
1226 return;
1227 }
1228
1229 VOID
1230 NTAPI
1231 StatusChangeWorkItemRoutine(
1232 PVOID Context)
1233 {
1234 //
1235 // cast to hardware object
1236 //
1237 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1238
1239 //
1240 // is there a callback
1241 //
1242 if (This->m_SCECallBack)
1243 {
1244 //
1245 // issue callback
1246 //
1247 This->m_SCECallBack(This->m_SCEContext);
1248 }
1249
1250 }
1251
1252 NTSTATUS
1253 CreateUSBHardware(
1254 PUSBHARDWAREDEVICE *OutHardware)
1255 {
1256 PUSBHARDWAREDEVICE This;
1257
1258 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1259
1260 if (!This)
1261 return STATUS_INSUFFICIENT_RESOURCES;
1262
1263 This->AddRef();
1264
1265 // return result
1266 *OutHardware = (PUSBHARDWAREDEVICE)This;
1267
1268 return STATUS_SUCCESS;
1269 }