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