[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 WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
118 ULONG m_SyncFramePhysAddr; // periodic frame list physical address
119 BOOLEAN m_ResetInProgress[16]; // set when a reset is in progress
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 // Enable Interrupts and start execution
529 //
530 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR
531 /*| EHCI_USBINTR_FLROVR*/ | EHCI_USBINTR_PC);
532
533 //
534 // Assign the AsyncList Register
535 //
536 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
537
538 //
539 // Assign the SyncList Register
540 //
541 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
542
543 //
544 // Set Schedules to Enable and Interrupt Threshold to 1ms.
545 //
546 GetCommandRegister(&UsbCmd);
547 UsbCmd.PeriodicEnable = TRUE;
548 UsbCmd.IntThreshold = 1;
549 SetCommandRegister(&UsbCmd);
550
551 GetCommandRegister(&UsbCmd);
552 UsbCmd.Run = TRUE;
553 SetCommandRegister(&UsbCmd);
554
555 //
556 // Wait for execution to start
557 //
558 for (FailSafe = 100; FailSafe > 1; FailSafe--)
559 {
560 KeStallExecutionProcessor(10);
561 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
562
563 if (!(UsbSts & EHCI_STS_HALT))
564 {
565 break;
566 }
567 }
568
569 if (UsbSts & EHCI_STS_HALT)
570 {
571 DPRINT1("Could not start execution on the controller\n");
572 return STATUS_UNSUCCESSFUL;
573 }
574
575 //
576 // Set port routing to EHCI controller
577 //
578 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
579
580 //
581 // Enable async
582 //
583 GetCommandRegister(&UsbCmd);
584 UsbCmd.AsyncEnable = TRUE; //FIXME: Need USB Memory Manager
585 // FIXME: Set framelistsize when periodic is implemented.
586 SetCommandRegister(&UsbCmd);
587
588 DPRINT1("EHCI Started!\n");
589 return STATUS_SUCCESS;
590 }
591
592 NTSTATUS
593 CUSBHardwareDevice::StopController(void)
594 {
595 EHCI_USBCMD_CONTENT UsbCmd;
596 ULONG UsbSts, FailSafe;
597
598 //
599 // Disable Interrupts and stop execution
600 //
601 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
602
603 GetCommandRegister(&UsbCmd);
604 UsbCmd.Run = FALSE;
605 SetCommandRegister(&UsbCmd);
606
607 for (FailSafe = 100; FailSafe > 1; FailSafe--)
608 {
609 KeStallExecutionProcessor(10);
610 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
611 if (UsbSts & EHCI_STS_HALT)
612 {
613 break;
614 }
615 }
616
617 if (!(UsbSts & EHCI_STS_HALT))
618 {
619 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
620 return STATUS_UNSUCCESSFUL;
621 }
622
623 return STATUS_SUCCESS;
624 }
625
626 NTSTATUS
627 CUSBHardwareDevice::ResetController(void)
628 {
629 UNIMPLEMENTED
630 return STATUS_NOT_IMPLEMENTED;
631 }
632
633 NTSTATUS
634 CUSBHardwareDevice::ResetPort(
635 IN ULONG PortIndex)
636 {
637 ULONG PortStatus;
638
639 if (PortIndex > m_Capabilities.HCSParams.PortCount)
640 return STATUS_UNSUCCESSFUL;
641
642 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
643 //
644 // check slow speed line before reset
645 //
646 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
647 {
648 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
649 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
650 return STATUS_DEVICE_NOT_CONNECTED;
651 }
652
653 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
654
655 //
656 // Reset and clean enable
657 //
658 PortStatus |= EHCI_PRT_RESET;
659 PortStatus &= ~EHCI_PRT_ENABLED;
660 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
661
662 KeStallExecutionProcessor(100);
663
664 //
665 // Clear reset
666 //
667 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
668 PortStatus &= ~EHCI_PRT_RESET;
669 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
670
671 do
672 {
673 //
674 // wait
675 //
676 KeStallExecutionProcessor(100);
677
678 //
679 // Check that the port reset
680 //
681 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
682 if (!(PortStatus & EHCI_PRT_RESET))
683 break;
684 } while (TRUE);
685
686 //
687 // check slow speed line after reset
688 //
689 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
690 {
691 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
692 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
693 return STATUS_DEVICE_NOT_CONNECTED;
694 }
695
696 //
697 // this must be enabled now
698 //
699 ASSERT(PortStatus & EHCI_PRT_ENABLED);
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 ULONG Value;
711 USHORT Status = 0, Change = 0;
712
713 if (PortId > m_Capabilities.HCSParams.PortCount)
714 return STATUS_UNSUCCESSFUL;
715
716 //
717 // Get the value of the Port Status and Control Register
718 //
719 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
720
721 //
722 // If the PowerPortControl is 0 then host controller does not have power control switches
723 if (!m_Capabilities.HCSParams.PortPowerControl)
724 {
725 Status |= USB_PORT_STATUS_POWER;
726 }
727 else
728 {
729 // Check the value of PortPower
730 if (Value & EHCI_PRT_POWER)
731 {
732 Status |= USB_PORT_STATUS_POWER;
733 }
734 }
735
736 // Get Connected Status
737 if (Value & EHCI_PRT_CONNECTED)
738 {
739 Status |= USB_PORT_STATUS_CONNECT;
740
741 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
742 if (Value & EHCI_PRT_SLOWSPEEDLINE)
743 Status |= USB_PORT_STATUS_LOW_SPEED;
744 else
745 Status |= USB_PORT_STATUS_HIGH_SPEED;
746 }
747
748 // Get Enabled Status
749 if (Value & EHCI_PRT_ENABLED)
750 Status |= USB_PORT_STATUS_ENABLE;
751
752 // Is it suspended?
753 if (Value & EHCI_PRT_SUSPEND)
754 Status |= USB_PORT_STATUS_SUSPEND;
755
756 // a overcurrent is active?
757 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
758 Status |= USB_PORT_STATUS_OVER_CURRENT;
759
760 // In a reset state?
761 if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
762 {
763 Status |= USB_PORT_STATUS_RESET;
764 Change |= USB_PORT_STATUS_RESET;
765 }
766
767 //
768 // FIXME: Is the Change here correct?
769 //
770 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
771 Change |= USB_PORT_STATUS_CONNECT;
772
773 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
774 Change |= USB_PORT_STATUS_ENABLE;
775
776 *PortStatus = Status;
777 *PortChange = Change;
778
779 return STATUS_SUCCESS;
780 }
781
782 NTSTATUS
783 CUSBHardwareDevice::ClearPortStatus(
784 ULONG PortId,
785 ULONG Status)
786 {
787 ULONG Value;
788
789 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
790
791 if (PortId > m_Capabilities.HCSParams.PortCount)
792 return STATUS_UNSUCCESSFUL;
793
794 if (Status == C_PORT_RESET)
795 {
796 //
797 // update port status
798 //
799 m_ResetInProgress[PortId] = FALSE;
800 }
801
802 if (Status == C_PORT_CONNECTION)
803 {
804 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
805 Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
806 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
807 }
808
809 return STATUS_SUCCESS;
810 }
811
812
813 NTSTATUS
814 CUSBHardwareDevice::SetPortFeature(
815 ULONG PortId,
816 ULONG Feature)
817 {
818 ULONG Value;
819
820 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
821
822 if (PortId > m_Capabilities.HCSParams.PortCount)
823 return STATUS_UNSUCCESSFUL;
824
825 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
826
827 if (Feature == PORT_ENABLE)
828 {
829 //
830 // FIXME: EHCI Ports can only be disabled via reset
831 //
832 DPRINT1("PORT_ENABLE not supported for EHCI\n");
833 }
834
835 if (Feature == PORT_RESET)
836 {
837 ResetPort(PortId);
838
839 //
840 // update cached settings
841 //
842 m_ResetInProgress[PortId] = TRUE;
843
844 //
845 // is there a status change callback
846 //
847 if (m_SCECallBack != NULL)
848 {
849 //
850 // issue callback
851 //
852 m_SCECallBack(m_SCEContext);
853 }
854 }
855
856 if (Feature == PORT_POWER)
857 DPRINT1("PORT_POWER Not implemented\n");
858
859 return STATUS_SUCCESS;
860 }
861
862 VOID
863 CUSBHardwareDevice::SetAsyncListRegister(
864 ULONG PhysicalAddress)
865 {
866 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
867 }
868
869 VOID
870 CUSBHardwareDevice::SetPeriodicListRegister(
871 ULONG PhysicalAddress)
872 {
873 //
874 // store physical address
875 //
876 m_SyncFramePhysAddr = PhysicalAddress;
877 }
878
879 struct _QUEUE_HEAD *
880 CUSBHardwareDevice::GetAsyncListQueueHead()
881 {
882 return AsyncQueueHead;
883 }
884
885 ULONG CUSBHardwareDevice::GetPeriodicListRegister()
886 {
887 UNIMPLEMENTED
888 return NULL;
889 }
890
891 VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
892 PVOID CallBack,
893 PVOID Context)
894 {
895 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
896 m_SCEContext = Context;
897 }
898
899 KIRQL
900 CUSBHardwareDevice::AcquireDeviceLock(void)
901 {
902 KIRQL OldLevel;
903
904 //
905 // acquire lock
906 //
907 KeAcquireSpinLock(&m_Lock, &OldLevel);
908
909 //
910 // return old irql
911 //
912 return OldLevel;
913 }
914
915
916 VOID
917 CUSBHardwareDevice::ReleaseDeviceLock(
918 KIRQL OldLevel)
919 {
920 KeReleaseSpinLock(&m_Lock, OldLevel);
921 }
922
923 BOOLEAN
924 NTAPI
925 InterruptServiceRoutine(
926 IN PKINTERRUPT Interrupt,
927 IN PVOID ServiceContext)
928 {
929 CUSBHardwareDevice *This;
930 ULONG CStatus;
931
932 This = (CUSBHardwareDevice*) ServiceContext;
933 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
934
935 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
936 //
937 // Check that it belongs to EHCI
938 //
939 if (!CStatus)
940 return FALSE;
941
942 //
943 // Clear the Status
944 //
945 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
946
947 if (CStatus & EHCI_STS_FATAL)
948 {
949 This->StopController();
950 DPRINT1("EHCI: Host System Error!\n");
951 return TRUE;
952 }
953
954 if (CStatus & EHCI_ERROR_INT)
955 {
956 DPRINT1("EHCI Status = 0x%x\n", CStatus);
957 }
958
959 if (CStatus & EHCI_STS_HALT)
960 {
961 DPRINT1("Host Error Unexpected Halt\n");
962 // FIXME: Reset controller\n");
963 return TRUE;
964 }
965
966 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
967 return TRUE;
968 }
969
970 VOID NTAPI
971 EhciDefferedRoutine(
972 IN PKDPC Dpc,
973 IN PVOID DeferredContext,
974 IN PVOID SystemArgument1,
975 IN PVOID SystemArgument2)
976 {
977 CUSBHardwareDevice *This;
978 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
979 NTSTATUS Status = STATUS_SUCCESS;
980 EHCI_USBCMD_CONTENT UsbCmd;
981
982 This = (CUSBHardwareDevice*) SystemArgument1;
983 CStatus = (ULONG) SystemArgument2;
984
985
986 //
987 // check for completion of async schedule
988 //
989 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
990 {
991 //
992 // check if there is a door bell ring in progress
993 //
994 if (This->m_DoorBellRingInProgress == FALSE)
995 {
996 if (CStatus & EHCI_ERROR_INT)
997 {
998 //
999 // controller reported error
1000 //
1001 DPRINT1("CStatus %x\n", CStatus);
1002 Status = STATUS_UNSUCCESSFUL;
1003 PC_ASSERT(FALSE);
1004 return;
1005 }
1006
1007 //
1008 // inform IUSBQueue of a completed queue head
1009 //
1010 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1011
1012 //
1013 // was a queue head completed?
1014 //
1015 if (ShouldRingDoorBell)
1016 {
1017 //
1018 // set door ring bell in progress status flag
1019 //
1020 This->m_DoorBellRingInProgress = TRUE;
1021
1022 //
1023 // get command register
1024 //
1025 This->GetCommandRegister(&UsbCmd);
1026
1027 //
1028 // set door rang bell bit
1029 //
1030 UsbCmd.DoorBell = TRUE;
1031
1032 //
1033 // update command status
1034 //
1035 This->SetCommandRegister(&UsbCmd);
1036 }
1037 }
1038 }
1039
1040 //
1041 // check if the controller has acknowledged the door bell
1042 //
1043 if (CStatus & EHCI_STS_IAA)
1044 {
1045 //
1046 // controller has acknowledged, assert we rang the bell
1047 //
1048 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1049
1050 //
1051 // now notify IUSBQueue that it can free completed requests
1052 //
1053 This->m_UsbQueue->CompleteAsyncRequests();
1054
1055 //
1056 // door ring bell completed
1057 //
1058 This->m_DoorBellRingInProgress = FALSE;
1059 }
1060
1061 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1062 if (CStatus & EHCI_STS_PCD)
1063 {
1064 for (i = 0; i < PortCount; i++)
1065 {
1066 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1067
1068 //
1069 // Device connected or removed
1070 //
1071 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1072 {
1073 if (PortStatus & EHCI_PRT_CONNECTED)
1074 {
1075 DPRINT1("Device connected on port %d\n", i);
1076
1077 //
1078 //FIXME: Determine device speed
1079 //
1080 if (This->m_Capabilities.HCSParams.CHCCount)
1081 {
1082 if (PortStatus & EHCI_PRT_ENABLED)
1083 {
1084 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1085 }
1086
1087 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
1088 {
1089 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1090 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
1091 continue;
1092 }
1093 }
1094 }
1095 else
1096 {
1097 DPRINT1("Device disconnected on port %d\n", i);
1098 }
1099
1100 //
1101 // is there a status change callback
1102 //
1103 if (This->m_SCECallBack != NULL)
1104 {
1105 //
1106 // queue work item for processing
1107 //
1108 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1109 }
1110
1111 //
1112 // FIXME: This needs to be saved somewhere
1113 //
1114 }
1115 }
1116 }
1117 return;
1118 }
1119
1120 VOID
1121 NTAPI
1122 StatusChangeWorkItemRoutine(
1123 PVOID Context)
1124 {
1125 //
1126 // cast to hardware object
1127 //
1128 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1129
1130 //
1131 // is there a callback
1132 //
1133 if (This->m_SCECallBack)
1134 {
1135 //
1136 // issue callback
1137 //
1138 This->m_SCECallBack(This->m_SCEContext);
1139 }
1140
1141 }
1142
1143 NTSTATUS
1144 CreateUSBHardware(
1145 PUSBHARDWAREDEVICE *OutHardware)
1146 {
1147 PUSBHARDWAREDEVICE This;
1148
1149 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1150
1151 if (!This)
1152 return STATUS_INSUFFICIENT_RESOURCES;
1153
1154 This->AddRef();
1155
1156 // return result
1157 *OutHardware = (PUSBHARDWAREDEVICE)This;
1158
1159 return STATUS_SUCCESS;
1160 }