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