5724a1cabe9df44d7c05183c7f890ae06c728b39
[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 UCHAR Value;
308 UCHAR PortCount;
309
310 DPRINT1("CUSBHardwareDevice::PnpStart\n");
311 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
312 {
313 //
314 // get resource descriptor
315 //
316 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
317
318 switch(ResourceDescriptor->Type)
319 {
320 case CmResourceTypeInterrupt:
321 {
322 KeInitializeDpc(&m_IntDpcObject,
323 EhciDefferedRoutine,
324 this);
325
326 Status = IoConnectInterrupt(&m_Interrupt,
327 InterruptServiceRoutine,
328 (PVOID)this,
329 NULL,
330 ResourceDescriptor->u.Interrupt.Vector,
331 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
332 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
333 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
334 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
335 ResourceDescriptor->u.Interrupt.Affinity,
336 FALSE);
337
338 if (!NT_SUCCESS(Status))
339 {
340 //
341 // failed to register interrupt
342 //
343 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
344 return Status;
345 }
346 break;
347 }
348 case CmResourceTypeMemory:
349 {
350 //
351 // get resource base
352 //
353 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
354 if (!ResourceBase)
355 {
356 //
357 // failed to map registers
358 //
359 DPRINT1("MmMapIoSpace failed\n");
360 return STATUS_INSUFFICIENT_RESOURCES;
361 }
362
363 //
364 // Get controllers capabilities
365 //
366 m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase + EHCI_CAPLENGTH);
367 m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + EHCI_HCIVERSION));
368 m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCSPARAMS));
369 m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCCPARAMS));
370
371 DPRINT1("Controller has %d Length\n", m_Capabilities.Length);
372 DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount);
373 DPRINT1("Controller EHCI Version %x\n", m_Capabilities.HCIVersion);
374 DPRINT1("Controler EHCI Caps HCSParamsLong %x\n", m_Capabilities.HCSParamsLong);
375 DPRINT1("Controler EHCI Caps HCCParamsLong %x\n", m_Capabilities.HCCParamsLong);
376 DPRINT1("Controler EHCI Caps PowerControl %x\n", m_Capabilities.HCSParams.PortPowerControl);
377
378
379
380
381 if (m_Capabilities.HCSParams.PortRouteRules)
382 {
383 Count = 0;
384 PortCount = max(m_Capabilities.HCSParams.PortCount/2, (m_Capabilities.HCSParams.PortCount+1)/2);
385 do
386 {
387 //
388 // each entry is a 4 bit field EHCI 2.2.5
389 //
390 Value = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + EHCI_HCSP_PORTROUTE + Count);
391 m_Capabilities.PortRoute[Count*2] = (Value & 0xF0);
392
393 if ((Count*2) + 1 < m_Capabilities.HCSParams.PortCount)
394 m_Capabilities.PortRoute[(Count*2)+1] = (Value & 0x0F);
395
396 Count++;
397 }while(Count < PortCount);
398 }
399
400 //
401 // Set m_Base to the address of Operational Register Space
402 //
403 m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
404 break;
405 }
406 }
407 }
408
409
410 //
411 // zero device description
412 //
413 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
414
415 //
416 // initialize device description
417 //
418 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
419 DeviceDescription.Master = TRUE;
420 DeviceDescription.ScatterGather = TRUE;
421 DeviceDescription.Dma32BitAddresses = TRUE;
422 DeviceDescription.DmaWidth = Width32Bits;
423 DeviceDescription.InterfaceType = PCIBus;
424 DeviceDescription.MaximumLength = MAXULONG;
425
426 //
427 // get dma adapter
428 //
429 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
430 if (!m_Adapter)
431 {
432 //
433 // failed to get dma adapter
434 //
435 DPRINT1("Failed to acquire dma adapter\n");
436 return STATUS_INSUFFICIENT_RESOURCES;
437 }
438
439 //
440 // Create Common Buffer
441 //
442 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
443 PAGE_SIZE * 4,
444 &PhysicalAddress,
445 FALSE);
446 if (!VirtualBase)
447 {
448 DPRINT1("Failed to allocate a common buffer\n");
449 return STATUS_INSUFFICIENT_RESOURCES;
450 }
451
452 //
453 // Stop the controller before modifying schedules
454 //
455 Status = StopController();
456 if (!NT_SUCCESS(Status))
457 return Status;
458
459 //
460 // Initialize the DMAMemoryManager
461 //
462 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
463 if (!NT_SUCCESS(Status))
464 {
465 DPRINT1("Failed to initialize the DMAMemoryManager\n");
466 return Status;
467 }
468
469 //
470 // Create a queuehead for the Async Register
471 //
472 m_MemoryManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&AsyncQueueHead, &AsyncPhysicalAddress);
473
474 AsyncQueueHead->AlternateNextPointer = TERMINATE_POINTER;
475 AsyncQueueHead->NextPointer = TERMINATE_POINTER;
476 AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
477 AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
478 AsyncQueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
479 AsyncQueueHead->Token.Bits.InterruptOnComplete = FALSE;
480 AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
481 AsyncQueueHead->Token.Bits.Halted = TRUE;
482 AsyncQueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
483 AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0;
484 AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
485 AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
486
487 InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
488
489 //
490 // Initialize the UsbQueue now that we have an AdapterObject.
491 //
492 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
493 if (!NT_SUCCESS(Status))
494 {
495 DPRINT1("Failed to Initialize the UsbQueue\n");
496 return Status;
497 }
498
499 //
500 // Start the controller
501 //
502 DPRINT1("Starting Controller\n");
503 Status = StartController();
504
505 //
506 // done
507 //
508 return Status;
509 }
510
511 NTSTATUS
512 CUSBHardwareDevice::PnpStop(void)
513 {
514 UNIMPLEMENTED
515 return STATUS_NOT_IMPLEMENTED;
516 }
517
518 NTSTATUS
519 CUSBHardwareDevice::HandlePower(
520 PIRP Irp)
521 {
522 UNIMPLEMENTED
523 return STATUS_NOT_IMPLEMENTED;
524 }
525
526 NTSTATUS
527 CUSBHardwareDevice::GetDeviceDetails(
528 OUT OPTIONAL PUSHORT VendorId,
529 OUT OPTIONAL PUSHORT DeviceId,
530 OUT OPTIONAL PULONG NumberOfPorts,
531 OUT OPTIONAL PULONG Speed)
532 {
533 if (VendorId)
534 *VendorId = m_VendorID;
535 if (DeviceId)
536 *DeviceId = m_DeviceID;
537 if (NumberOfPorts)
538 *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
539 //FIXME: What to returned here?
540 if (Speed)
541 *Speed = 0x200;
542 return STATUS_SUCCESS;
543 }
544
545 NTSTATUS CUSBHardwareDevice::GetDMA(
546 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
547 {
548 if (!m_MemoryManager)
549 return STATUS_UNSUCCESSFUL;
550 *OutDMAMemoryManager = m_MemoryManager;
551 return STATUS_SUCCESS;
552 }
553
554 NTSTATUS
555 CUSBHardwareDevice::GetUSBQueue(
556 OUT struct IUSBQueue **OutUsbQueue)
557 {
558 if (!m_UsbQueue)
559 return STATUS_UNSUCCESSFUL;
560 *OutUsbQueue = m_UsbQueue;
561 return STATUS_SUCCESS;
562 }
563
564
565 NTSTATUS
566 CUSBHardwareDevice::StartController(void)
567 {
568 EHCI_USBCMD_CONTENT UsbCmd;
569 ULONG UsbSts, FailSafe;
570
571 //
572 // check caps
573 //
574 if (m_Capabilities.HCCParams.CurAddrBits)
575 {
576 //
577 // disable 64-bit addressing
578 //
579 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
580 }
581
582
583 #if 1
584 //
585 // Stop the controller if its running
586 //
587 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
588 if (!(UsbSts & EHCI_STS_HALT))
589 {
590 DPRINT1("Stopping Controller %x\n", UsbSts);
591 StopController();
592 }
593 #endif
594
595 //
596 // Enable Interrupts and start execution
597 //
598 ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC;
599 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask);
600
601 KeStallExecutionProcessor(10);
602
603 ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR);
604
605 DPRINT1("Interrupt Mask %x\n", Status);
606 ASSERT((Status & Mask) == Mask);
607
608
609 //
610 // Assign the SyncList Register
611 //
612 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
613
614 //
615 // Set Schedules to Enable and Interrupt Threshold to 1ms.
616 //
617 RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT));
618
619 UsbCmd.PeriodicEnable = TRUE;
620 UsbCmd.IntThreshold = 0x8; //1ms
621 UsbCmd.Run = TRUE;
622 UsbCmd.FrameListSize = 0x0; //1024
623 SetCommandRegister(&UsbCmd);
624
625 //
626 // Wait for execution to start
627 //
628 for (FailSafe = 100; FailSafe > 1; FailSafe--)
629 {
630 KeStallExecutionProcessor(10);
631 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
632
633 if (!(UsbSts & EHCI_STS_HALT))
634 {
635 break;
636 }
637 }
638
639
640 if (UsbSts & EHCI_STS_HALT)
641 {
642 DPRINT1("Could not start execution on the controller\n");
643 return STATUS_UNSUCCESSFUL;
644 }
645
646 //
647 // Assign the AsyncList Register
648 //
649 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
650
651 //
652 // get command register
653 //
654 GetCommandRegister(&UsbCmd);
655
656 //
657 // preserve bits
658 //
659 UsbCmd.AsyncEnable = TRUE;
660
661 //
662 // enable async
663 //
664 SetCommandRegister(&UsbCmd);
665
666 //
667 // Wait for execution to start
668 //
669 for (FailSafe = 100; FailSafe > 1; FailSafe--)
670 {
671 KeStallExecutionProcessor(10);
672 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
673
674 if ((UsbSts & EHCI_STS_ASS))
675 {
676 break;
677 }
678 }
679
680 if (!(UsbSts & EHCI_STS_ASS))
681 {
682 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts);
683 ASSERT(FALSE);
684 return STATUS_UNSUCCESSFUL;
685 }
686
687 DPRINT1("UsbSts %x\n", UsbSts);
688 GetCommandRegister(&UsbCmd);
689
690 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable);
691 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable);
692 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold);
693 DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run);
694 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize);
695
696 //
697 // Set port routing to EHCI controller
698 //
699 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
700
701
702
703
704 DPRINT1("EHCI Started!\n");
705 return STATUS_SUCCESS;
706 }
707
708 NTSTATUS
709 CUSBHardwareDevice::StopController(void)
710 {
711 EHCI_USBCMD_CONTENT UsbCmd;
712 ULONG UsbSts, FailSafe;
713
714 //
715 // Disable Interrupts and stop execution
716 //
717 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
718
719 GetCommandRegister(&UsbCmd);
720 UsbCmd.Run = FALSE;
721 SetCommandRegister(&UsbCmd);
722
723 for (FailSafe = 100; FailSafe > 1; FailSafe--)
724 {
725 KeStallExecutionProcessor(10);
726 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
727 if (UsbSts & EHCI_STS_HALT)
728 {
729 break;
730 }
731 }
732
733 if (!(UsbSts & EHCI_STS_HALT))
734 {
735 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
736 return STATUS_UNSUCCESSFUL;
737 }
738
739 return STATUS_SUCCESS;
740 }
741
742 NTSTATUS
743 CUSBHardwareDevice::ResetController(void)
744 {
745 UNIMPLEMENTED
746 return STATUS_NOT_IMPLEMENTED;
747 }
748
749 NTSTATUS
750 CUSBHardwareDevice::ResetPort(
751 IN ULONG PortIndex)
752 {
753 ULONG PortStatus;
754 LARGE_INTEGER Timeout;
755
756 if (PortIndex > m_Capabilities.HCSParams.PortCount)
757 return STATUS_UNSUCCESSFUL;
758
759 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
760 //
761 // check slow speed line before reset
762 //
763 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
764 {
765 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
766 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
767 return STATUS_DEVICE_NOT_CONNECTED;
768 }
769
770 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
771
772 //
773 // Reset and clean enable
774 //
775 PortStatus |= EHCI_PRT_RESET;
776 PortStatus &= ~EHCI_PRT_ENABLED;
777 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
778
779 //
780 // delay is 20 ms for port reset as per USB 2.0 spec
781 //
782 Timeout.QuadPart = 20;
783 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
784
785 //
786 // convert to 100 ns units (absolute)
787 //
788 Timeout.QuadPart *= -10000;
789
790 //
791 // perform the wait
792 //
793 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
794
795 //
796 // Clear reset
797 //
798 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
799 PortStatus &= ~EHCI_PRT_RESET;
800 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
801
802 do
803 {
804 //
805 // wait
806 //
807 KeStallExecutionProcessor(100);
808
809 //
810 // Check that the port reset
811 //
812 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
813 if (!(PortStatus & EHCI_PRT_RESET))
814 break;
815 } while (TRUE);
816
817 //
818 // delay is 10 ms
819 //
820 Timeout.QuadPart = 10;
821 DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart);
822
823 //
824 // convert to 100 ns units (absolute)
825 //
826 Timeout.QuadPart *= -10000;
827
828 //
829 // perform the wait
830 //
831 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
832
833 //
834 // check slow speed line after reset
835 //
836 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
837 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
838 {
839 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
840 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
841 return STATUS_DEVICE_NOT_CONNECTED;
842 }
843
844 //
845 // this must be enabled now
846 //
847 ASSERT(PortStatus & EHCI_PRT_ENABLED);
848
849 return STATUS_SUCCESS;
850 }
851
852 NTSTATUS
853 CUSBHardwareDevice::GetPortStatus(
854 ULONG PortId,
855 OUT USHORT *PortStatus,
856 OUT USHORT *PortChange)
857 {
858 ULONG Value;
859 USHORT Status = 0, Change = 0;
860
861 if (PortId > m_Capabilities.HCSParams.PortCount)
862 return STATUS_UNSUCCESSFUL;
863
864 //
865 // Get the value of the Port Status and Control Register
866 //
867 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
868
869 //
870 // If the PowerPortControl is 0 then host controller does not have power control switches
871 if (!m_Capabilities.HCSParams.PortPowerControl)
872 {
873 Status |= USB_PORT_STATUS_POWER;
874 }
875 else
876 {
877 // Check the value of PortPower
878 if (Value & EHCI_PRT_POWER)
879 {
880 Status |= USB_PORT_STATUS_POWER;
881 }
882 }
883
884 // Get Connected Status
885 if (Value & EHCI_PRT_CONNECTED)
886 {
887 Status |= USB_PORT_STATUS_CONNECT;
888
889 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
890 if (Value & EHCI_PRT_SLOWSPEEDLINE)
891 Status |= USB_PORT_STATUS_LOW_SPEED;
892 else
893 Status |= USB_PORT_STATUS_HIGH_SPEED;
894 }
895
896 // Get Enabled Status
897 if (Value & EHCI_PRT_ENABLED)
898 Status |= USB_PORT_STATUS_ENABLE;
899
900 // Is it suspended?
901 if (Value & EHCI_PRT_SUSPEND)
902 Status |= USB_PORT_STATUS_SUSPEND;
903
904 // a overcurrent is active?
905 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
906 Status |= USB_PORT_STATUS_OVER_CURRENT;
907
908 // In a reset state?
909 if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
910 {
911 Status |= USB_PORT_STATUS_RESET;
912 Change |= USB_PORT_STATUS_RESET;
913 }
914
915 //
916 // FIXME: Is the Change here correct?
917 //
918 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
919 Change |= USB_PORT_STATUS_CONNECT;
920
921 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
922 Change |= USB_PORT_STATUS_ENABLE;
923
924 *PortStatus = Status;
925 *PortChange = Change;
926
927 return STATUS_SUCCESS;
928 }
929
930 NTSTATUS
931 CUSBHardwareDevice::ClearPortStatus(
932 ULONG PortId,
933 ULONG Status)
934 {
935 ULONG Value;
936
937 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
938
939 if (PortId > m_Capabilities.HCSParams.PortCount)
940 return STATUS_UNSUCCESSFUL;
941
942 if (Status == C_PORT_RESET)
943 {
944 //
945 // update port status
946 //
947 m_ResetInProgress[PortId] = FALSE;
948 }
949
950 if (Status == C_PORT_CONNECTION)
951 {
952 LARGE_INTEGER Timeout;
953
954 //
955 // reset status change bits
956 //
957 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
958 Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
959 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
960
961 //
962 // delay is 100 ms
963 //
964 Timeout.QuadPart = 100;
965 DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart);
966
967 //
968 // convert to 100 ns units (absolute)
969 //
970 Timeout.QuadPart *= -10000;
971
972 //
973 // perform the wait
974 //
975 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
976 }
977
978 return STATUS_SUCCESS;
979 }
980
981
982 NTSTATUS
983 CUSBHardwareDevice::SetPortFeature(
984 ULONG PortId,
985 ULONG Feature)
986 {
987 ULONG Value;
988
989 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
990
991 if (PortId > m_Capabilities.HCSParams.PortCount)
992 return STATUS_UNSUCCESSFUL;
993
994 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
995
996 if (Feature == PORT_ENABLE)
997 {
998 //
999 // FIXME: EHCI Ports can only be disabled via reset
1000 //
1001 DPRINT1("PORT_ENABLE not supported for EHCI\n");
1002 }
1003
1004 if (Feature == PORT_RESET)
1005 {
1006 ResetPort(PortId);
1007
1008 //
1009 // update cached settings
1010 //
1011 m_ResetInProgress[PortId] = TRUE;
1012
1013 //
1014 // is there a status change callback
1015 //
1016 if (m_SCECallBack != NULL)
1017 {
1018 //
1019 // issue callback
1020 //
1021 m_SCECallBack(m_SCEContext);
1022 }
1023 }
1024
1025 if (Feature == PORT_POWER)
1026 {
1027 if (m_Capabilities.HCSParams.PortPowerControl)
1028 {
1029 ULONG Value;
1030 LARGE_INTEGER Timeout;
1031
1032 //
1033 // enable port power
1034 //
1035 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
1036 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC, Value);
1037
1038 //
1039 // delay is 20 ms
1040 //
1041 Timeout.QuadPart = 20;
1042 DPRINT1("Waiting %d milliseconds for port power up\n", Timeout.LowPart);
1043
1044 //
1045 // convert to 100 ns units (absolute)
1046 //
1047 Timeout.QuadPart *= -10000;
1048
1049 //
1050 // perform the wait
1051 //
1052 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1053 }
1054 }
1055 return STATUS_SUCCESS;
1056 }
1057
1058 VOID
1059 CUSBHardwareDevice::SetAsyncListRegister(
1060 ULONG PhysicalAddress)
1061 {
1062 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
1063 }
1064
1065 VOID
1066 CUSBHardwareDevice::SetPeriodicListRegister(
1067 ULONG PhysicalAddress)
1068 {
1069 //
1070 // store physical address
1071 //
1072 m_SyncFramePhysAddr = PhysicalAddress;
1073 }
1074
1075 struct _QUEUE_HEAD *
1076 CUSBHardwareDevice::GetAsyncListQueueHead()
1077 {
1078 return AsyncQueueHead;
1079 }
1080
1081 ULONG CUSBHardwareDevice::GetPeriodicListRegister()
1082 {
1083 UNIMPLEMENTED
1084 return NULL;
1085 }
1086
1087 VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1088 PVOID CallBack,
1089 PVOID Context)
1090 {
1091 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1092 m_SCEContext = Context;
1093 }
1094
1095 KIRQL
1096 CUSBHardwareDevice::AcquireDeviceLock(void)
1097 {
1098 KIRQL OldLevel;
1099
1100 //
1101 // acquire lock
1102 //
1103 KeAcquireSpinLock(&m_Lock, &OldLevel);
1104
1105 //
1106 // return old irql
1107 //
1108 return OldLevel;
1109 }
1110
1111
1112 VOID
1113 CUSBHardwareDevice::ReleaseDeviceLock(
1114 KIRQL OldLevel)
1115 {
1116 KeReleaseSpinLock(&m_Lock, OldLevel);
1117 }
1118
1119 BOOLEAN
1120 NTAPI
1121 InterruptServiceRoutine(
1122 IN PKINTERRUPT Interrupt,
1123 IN PVOID ServiceContext)
1124 {
1125 CUSBHardwareDevice *This;
1126 ULONG CStatus;
1127
1128 This = (CUSBHardwareDevice*) ServiceContext;
1129 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
1130
1131 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
1132 //
1133 // Check that it belongs to EHCI
1134 //
1135 if (!CStatus)
1136 return FALSE;
1137
1138 //
1139 // Clear the Status
1140 //
1141 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
1142
1143 if (CStatus & EHCI_STS_FATAL)
1144 {
1145 This->StopController();
1146 DPRINT1("EHCI: Host System Error!\n");
1147 return TRUE;
1148 }
1149
1150 if (CStatus & EHCI_ERROR_INT)
1151 {
1152 DPRINT1("EHCI Status = 0x%x\n", CStatus);
1153 }
1154
1155 if (CStatus & EHCI_STS_HALT)
1156 {
1157 DPRINT1("Host Error Unexpected Halt\n");
1158 // FIXME: Reset controller\n");
1159 return TRUE;
1160 }
1161
1162 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
1163 return TRUE;
1164 }
1165
1166 VOID NTAPI
1167 EhciDefferedRoutine(
1168 IN PKDPC Dpc,
1169 IN PVOID DeferredContext,
1170 IN PVOID SystemArgument1,
1171 IN PVOID SystemArgument2)
1172 {
1173 CUSBHardwareDevice *This;
1174 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
1175 NTSTATUS Status = STATUS_SUCCESS;
1176 EHCI_USBCMD_CONTENT UsbCmd;
1177
1178 This = (CUSBHardwareDevice*) SystemArgument1;
1179 CStatus = (ULONG) SystemArgument2;
1180
1181 DPRINT("CStatus %x\n", CStatus);
1182
1183 //
1184 // check for completion of async schedule
1185 //
1186 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
1187 {
1188 //
1189 // check if there is a door bell ring in progress
1190 //
1191 if (This->m_DoorBellRingInProgress == FALSE)
1192 {
1193 if (CStatus & EHCI_ERROR_INT)
1194 {
1195 //
1196 // controller reported error
1197 //
1198 DPRINT1("CStatus %x\n", CStatus);
1199 Status = STATUS_UNSUCCESSFUL;
1200 PC_ASSERT(FALSE);
1201 return;
1202 }
1203
1204 //
1205 // inform IUSBQueue of a completed queue head
1206 //
1207 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1208
1209 //
1210 // was a queue head completed?
1211 //
1212 if (ShouldRingDoorBell)
1213 {
1214 //
1215 // set door ring bell in progress status flag
1216 //
1217 This->m_DoorBellRingInProgress = TRUE;
1218
1219 //
1220 // get command register
1221 //
1222 This->GetCommandRegister(&UsbCmd);
1223
1224 //
1225 // set door rang bell bit
1226 //
1227 UsbCmd.DoorBell = TRUE;
1228
1229 //
1230 // update command status
1231 //
1232 This->SetCommandRegister(&UsbCmd);
1233 }
1234 }
1235 }
1236
1237 //
1238 // check if the controller has acknowledged the door bell
1239 //
1240 if (CStatus & EHCI_STS_IAA)
1241 {
1242 //
1243 // controller has acknowledged, assert we rang the bell
1244 //
1245 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1246
1247 //
1248 // now notify IUSBQueue that it can free completed requests
1249 //
1250 This->m_UsbQueue->CompleteAsyncRequests();
1251
1252 //
1253 // door ring bell completed
1254 //
1255 This->m_DoorBellRingInProgress = FALSE;
1256 }
1257
1258 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1259 if (CStatus & EHCI_STS_PCD)
1260 {
1261 for (i = 0; i < PortCount; i++)
1262 {
1263 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1264
1265 //
1266 // Device connected or removed
1267 //
1268 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1269 {
1270 if (PortStatus & EHCI_PRT_CONNECTED)
1271 {
1272 DPRINT1("Device connected on port %d\n", i);
1273
1274 //
1275 //FIXME: Determine device speed
1276 //
1277 if (This->m_Capabilities.HCSParams.CHCCount)
1278 {
1279 if (PortStatus & EHCI_PRT_ENABLED)
1280 {
1281 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1282 }
1283
1284 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
1285 {
1286 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1287 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
1288 continue;
1289 }
1290 }
1291 }
1292 else
1293 {
1294 DPRINT1("Device disconnected on port %d\n", i);
1295 }
1296
1297 //
1298 // is there a status change callback
1299 //
1300 if (This->m_SCECallBack != NULL)
1301 {
1302 //
1303 // queue work item for processing
1304 //
1305 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1306 }
1307
1308 //
1309 // FIXME: This needs to be saved somewhere
1310 //
1311 }
1312 }
1313 }
1314 return;
1315 }
1316
1317 VOID
1318 NTAPI
1319 StatusChangeWorkItemRoutine(
1320 PVOID Context)
1321 {
1322 //
1323 // cast to hardware object
1324 //
1325 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1326
1327 //
1328 // is there a callback
1329 //
1330 if (This->m_SCECallBack)
1331 {
1332 //
1333 // issue callback
1334 //
1335 This->m_SCECallBack(This->m_SCEContext);
1336 }
1337
1338 }
1339
1340 NTSTATUS
1341 CreateUSBHardware(
1342 PUSBHARDWAREDEVICE *OutHardware)
1343 {
1344 PUSBHARDWAREDEVICE This;
1345
1346 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1347
1348 if (!This)
1349 return STATUS_INSUFFICIENT_RESOURCES;
1350
1351 This->AddRef();
1352
1353 // return result
1354 *OutHardware = (PUSBHARDWAREDEVICE)This;
1355
1356 return STATUS_SUCCESS;
1357 }