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