[NTFS]
[reactos.git] / drivers / usb / usbehci / 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 #include "usbehci.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
17
18 BOOLEAN
19 NTAPI
20 InterruptServiceRoutine(
21 IN PKINTERRUPT Interrupt,
22 IN PVOID ServiceContext);
23
24 VOID
25 NTAPI
26 EhciDeferredRoutine(
27 IN PKDPC Dpc,
28 IN PVOID DeferredContext,
29 IN PVOID SystemArgument1,
30 IN PVOID SystemArgument2);
31
32 VOID
33 NTAPI
34 StatusChangeWorkItemRoutine(PVOID Context);
35
36 class CUSBHardwareDevice : public IEHCIHardwareDevice
37 {
38 public:
39 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
40
41 STDMETHODIMP_(ULONG) AddRef()
42 {
43 InterlockedIncrement(&m_Ref);
44 return m_Ref;
45 }
46 STDMETHODIMP_(ULONG) Release()
47 {
48 InterlockedDecrement(&m_Ref);
49
50 if (!m_Ref)
51 {
52 delete this;
53 return 0;
54 }
55 return m_Ref;
56 }
57 // com
58 IMP_IUSBHARDWAREDEVICE
59 IMP_IUSBEHCIHARDWARE
60
61 // local
62 BOOLEAN InterruptService();
63 VOID PrintCapabilities();
64 NTSTATUS StartController();
65 NTSTATUS StopController();
66 NTSTATUS ResetController();
67
68 // friend function
69 friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
70 friend VOID NTAPI EhciDeferredRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
71 friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
72 // constructor / destructor
73 CUSBHardwareDevice(IUnknown *OuterUnknown){}
74 virtual ~CUSBHardwareDevice(){}
75
76 protected:
77 LONG m_Ref; // reference count
78 PDRIVER_OBJECT m_DriverObject; // driver object
79 PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo
80 PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller)
81 PDEVICE_OBJECT m_NextDeviceObject; // lower device object
82 KSPIN_LOCK m_Lock; // hardware lock
83 PKINTERRUPT m_Interrupt; // interrupt object
84 KDPC m_IntDpcObject; // dpc object for deferred isr processing
85 PVOID VirtualBase; // virtual base for memory manager
86 PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager
87 PULONG m_Base; // EHCI operational port base registers
88 PDMA_ADAPTER m_Adapter; // dma adapter object
89 ULONG m_MapRegisters; // map registers count
90 EHCI_CAPS m_Capabilities; // EHCI caps
91 USHORT m_VendorID; // vendor id
92 USHORT m_DeviceID; // device id
93 PQUEUE_HEAD AsyncQueueHead; // async queue head terminator
94 PEHCIQUEUE m_UsbQueue; // usb request queue
95 PDMAMEMORYMANAGER m_MemoryManager; // memory manager
96 HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine
97 PVOID m_SCEContext; // status change callback routine context
98 BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress
99 WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
100 volatile LONG m_StatusChangeWorkItemStatus; // work item status
101 ULONG m_SyncFramePhysAddr; // periodic frame list physical address
102 BUS_INTERFACE_STANDARD m_BusInterface; // pci bus interface
103 BOOLEAN m_PortResetInProgress[0xF]; // stores reset in progress (vbox hack)
104
105 // read register
106 ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
107
108 // write register
109 VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
110 };
111
112 //=================================================================================================
113 // COM
114 //
115 NTSTATUS
116 STDMETHODCALLTYPE
117 CUSBHardwareDevice::QueryInterface(
118 IN REFIID refiid,
119 OUT PVOID* Output)
120 {
121 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
122 {
123 *Output = PVOID(PUNKNOWN(this));
124 PUNKNOWN(*Output)->AddRef();
125 return STATUS_SUCCESS;
126 }
127
128 return STATUS_UNSUCCESSFUL;
129 }
130
131 LPCSTR
132 STDMETHODCALLTYPE
133 CUSBHardwareDevice::GetUSBType()
134 {
135 return "USBEHCI";
136 }
137
138 NTSTATUS
139 STDMETHODCALLTYPE
140 CUSBHardwareDevice::Initialize(
141 PDRIVER_OBJECT DriverObject,
142 PDEVICE_OBJECT FunctionalDeviceObject,
143 PDEVICE_OBJECT PhysicalDeviceObject,
144 PDEVICE_OBJECT LowerDeviceObject)
145 {
146 PCI_COMMON_CONFIG PciConfig;
147 NTSTATUS Status;
148 ULONG BytesRead;
149
150 DPRINT("CUSBHardwareDevice::Initialize\n");
151
152 //
153 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
154 //
155 Status = CreateDMAMemoryManager(&m_MemoryManager);
156 if (!NT_SUCCESS(Status))
157 {
158 DPRINT1("Failed to create DMAMemoryManager Object\n");
159 return Status;
160 }
161
162 //
163 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
164 //
165 Status = CreateUSBQueue((PUSBQUEUE*)&m_UsbQueue);
166 if (!NT_SUCCESS(Status))
167 {
168 DPRINT1("Failed to create UsbQueue!\n");
169 return Status;
170 }
171
172 //
173 // store device objects
174 //
175 m_DriverObject = DriverObject;
176 m_FunctionalDeviceObject = FunctionalDeviceObject;
177 m_PhysicalDeviceObject = PhysicalDeviceObject;
178 m_NextDeviceObject = LowerDeviceObject;
179
180 //
181 // initialize device lock
182 //
183 KeInitializeSpinLock(&m_Lock);
184
185 //
186 // initialize status change work item
187 //
188 ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
189
190 m_VendorID = 0;
191 m_DeviceID = 0;
192
193 Status = GetBusInterface(PhysicalDeviceObject, &m_BusInterface);
194 if (!NT_SUCCESS(Status))
195 {
196 DPRINT1("Failed to get BusInterface!\n");
197 return Status;
198 }
199
200 BytesRead = (*m_BusInterface.GetBusData)(m_BusInterface.Context,
201 PCI_WHICHSPACE_CONFIG,
202 &PciConfig,
203 0,
204 PCI_COMMON_HDR_LENGTH);
205
206 if (BytesRead != PCI_COMMON_HDR_LENGTH)
207 {
208 DPRINT1("Failed to get pci config information!\n");
209 return STATUS_SUCCESS;
210 }
211
212 m_VendorID = PciConfig.VendorID;
213 m_DeviceID = PciConfig.DeviceID;
214
215 return STATUS_SUCCESS;
216 }
217
218 VOID
219 STDMETHODCALLTYPE
220 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
221 {
222 PULONG Register;
223 Register = (PULONG)UsbCmd;
224 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register);
225 }
226
227 VOID
228 STDMETHODCALLTYPE
229 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
230 {
231 PULONG Register;
232 Register = (PULONG)UsbCmd;
233 *Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD));
234 }
235
236 ULONG
237 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset)
238 {
239 return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset));
240 }
241
242 VOID
243 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value)
244 {
245 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value);
246 }
247
248 VOID
249 CUSBHardwareDevice::PrintCapabilities()
250 {
251 if (m_Capabilities.HCSParams.PortPowerControl)
252 {
253 DPRINT1("Controller EHCI has Port Power Control\n");
254 }
255
256 DPRINT1("Controller Port Routing Rules %lu\n", m_Capabilities.HCSParams.PortRouteRules);
257 DPRINT1("Number of Ports per Companion Controller %lu\n", m_Capabilities.HCSParams.PortPerCHC);
258 DPRINT1("Number of Companion Controller %lu\n", m_Capabilities.HCSParams.CHCCount);
259
260 if (m_Capabilities.HCSParams.PortIndicator)
261 {
262 DPRINT1("Controller has Port Indicators Support\n");
263 }
264
265 if (m_Capabilities.HCSParams.DbgPortNum)
266 {
267 DPRINT1("Controller has Debug Port Support At Port %x\n", m_Capabilities.HCSParams.DbgPortNum);
268 }
269
270 if (m_Capabilities.HCCParams.EECPCapable)
271 {
272 DPRINT1("Controller has Extended Capabilities Support\n");
273 }
274
275 if (m_Capabilities.HCCParams.ParkMode)
276 {
277 DPRINT1("Controller supports Asynchronous Schedule Park\n");
278 }
279
280 if (m_Capabilities.HCCParams.VarFrameList)
281 {
282 DPRINT1("Controller supports Programmable Frame List Size\n");
283 }
284
285 if (m_Capabilities.HCCParams.CurAddrBits)
286 {
287 DPRINT1("Controller uses 64-Bit Addressing\n");
288 }
289 }
290
291 NTSTATUS
292 STDMETHODCALLTYPE
293 CUSBHardwareDevice::PnpStart(
294 PCM_RESOURCE_LIST RawResources,
295 PCM_RESOURCE_LIST TranslatedResources)
296 {
297 ULONG Index, Count;
298 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
299 DEVICE_DESCRIPTION DeviceDescription;
300 PHYSICAL_ADDRESS AsyncPhysicalAddress;
301 PVOID ResourceBase;
302 NTSTATUS Status;
303 UCHAR Value;
304 UCHAR PortCount;
305
306 DPRINT("CUSBHardwareDevice::PnpStart\n");
307 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
308 {
309 //
310 // get resource descriptor
311 //
312 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
313
314 switch(ResourceDescriptor->Type)
315 {
316 case CmResourceTypeInterrupt:
317 {
318 KeInitializeDpc(&m_IntDpcObject,
319 EhciDeferredRoutine,
320 this);
321
322 Status = IoConnectInterrupt(&m_Interrupt,
323 InterruptServiceRoutine,
324 (PVOID)this,
325 NULL,
326 ResourceDescriptor->u.Interrupt.Vector,
327 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
328 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
329 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
330 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
331 ResourceDescriptor->u.Interrupt.Affinity,
332 FALSE);
333
334 if (!NT_SUCCESS(Status))
335 {
336 //
337 // failed to register interrupt
338 //
339 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
340 return Status;
341 }
342 break;
343 }
344 case CmResourceTypeMemory:
345 {
346 //
347 // get resource base
348 //
349 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
350 if (!ResourceBase)
351 {
352 //
353 // failed to map registers
354 //
355 DPRINT1("MmMapIoSpace failed\n");
356 return STATUS_INSUFFICIENT_RESOURCES;
357 }
358
359 //
360 // Get controllers capabilities
361 //
362 m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase + EHCI_CAPLENGTH);
363 m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG_PTR)ResourceBase + EHCI_HCIVERSION));
364 m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + EHCI_HCSPARAMS));
365 m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + EHCI_HCCPARAMS));
366
367 DPRINT1("Controller Capabilities Length 0x%x\n", m_Capabilities.Length);
368 DPRINT1("Controller EHCI Version 0x%x\n", m_Capabilities.HCIVersion);
369 DPRINT1("Controller EHCI Caps HCSParamsLong 0x%lx\n", m_Capabilities.HCSParamsLong);
370 DPRINT1("Controller EHCI Caps HCCParamsLong 0x%lx\n", m_Capabilities.HCCParamsLong);
371 DPRINT1("Controller has %lu Ports\n", m_Capabilities.HCSParams.PortCount);
372
373 //
374 // print capabilities
375 //
376 PrintCapabilities();
377
378 if (m_Capabilities.HCSParams.PortRouteRules)
379 {
380 Count = 0;
381 PortCount = max(m_Capabilities.HCSParams.PortCount/2, (m_Capabilities.HCSParams.PortCount+1)/2);
382 do
383 {
384 //
385 // each entry is a 4 bit field EHCI 2.2.5
386 //
387 Value = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + EHCI_HCSP_PORTROUTE + Count);
388 m_Capabilities.PortRoute[Count*2] = (Value & 0xF0);
389
390 if ((Count*2) + 1 < m_Capabilities.HCSParams.PortCount)
391 m_Capabilities.PortRoute[(Count*2)+1] = (Value & 0x0F);
392
393 Count++;
394 } while(Count < PortCount);
395 }
396
397 //
398 // Set m_Base to the address of Operational Register Space
399 //
400 m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
401 break;
402 }
403 }
404 }
405
406
407 //
408 // zero device description
409 //
410 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
411
412 //
413 // initialize device description
414 //
415 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
416 DeviceDescription.Master = TRUE;
417 DeviceDescription.ScatterGather = TRUE;
418 DeviceDescription.Dma32BitAddresses = TRUE;
419 DeviceDescription.DmaWidth = Width32Bits;
420 DeviceDescription.InterfaceType = PCIBus;
421 DeviceDescription.MaximumLength = MAXULONG;
422
423 //
424 // get dma adapter
425 //
426 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
427 if (!m_Adapter)
428 {
429 //
430 // failed to get dma adapter
431 //
432 DPRINT1("Failed to acquire dma adapter\n");
433 return STATUS_INSUFFICIENT_RESOURCES;
434 }
435
436 //
437 // Create Common Buffer
438 //
439 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
440 PAGE_SIZE * 4,
441 &PhysicalAddress,
442 FALSE);
443 if (!VirtualBase)
444 {
445 DPRINT1("Failed to allocate a common buffer\n");
446 return STATUS_INSUFFICIENT_RESOURCES;
447 }
448
449 //
450 // Initialize the DMAMemoryManager
451 //
452 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
453 if (!NT_SUCCESS(Status))
454 {
455 DPRINT1("Failed to initialize the DMAMemoryManager\n");
456 return Status;
457 }
458
459 //
460 // Create a queuehead for the Async Register
461 //
462 m_MemoryManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&AsyncQueueHead, &AsyncPhysicalAddress);
463
464 AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
465 AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
466 AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
467 AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
468 AsyncQueueHead->Token.Bits.Halted = TRUE;
469
470 AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01;
471 AsyncQueueHead->NextPointer = TERMINATE_POINTER;
472 AsyncQueueHead->CurrentLinkPointer = TERMINATE_POINTER;
473
474 InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
475
476 //
477 // Initialize the UsbQueue now that we have an AdapterObject.
478 //
479 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
480 if (!NT_SUCCESS(Status))
481 {
482 DPRINT1("Failed to Initialize the UsbQueue\n");
483 return Status;
484 }
485
486 //
487 // Start the controller
488 //
489 DPRINT1("Starting Controller\n");
490 Status = StartController();
491
492 //
493 // done
494 //
495 return Status;
496 }
497
498 NTSTATUS
499 STDMETHODCALLTYPE
500 CUSBHardwareDevice::PnpStop(void)
501 {
502 UNIMPLEMENTED;
503 return STATUS_NOT_IMPLEMENTED;
504 }
505
506 NTSTATUS
507 STDMETHODCALLTYPE
508 CUSBHardwareDevice::GetDeviceDetails(
509 OUT OPTIONAL PUSHORT VendorId,
510 OUT OPTIONAL PUSHORT DeviceId,
511 OUT OPTIONAL PULONG NumberOfPorts,
512 OUT OPTIONAL PULONG Speed)
513 {
514 if (VendorId)
515 *VendorId = m_VendorID;
516 if (DeviceId)
517 *DeviceId = m_DeviceID;
518 if (NumberOfPorts)
519 *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
520 //FIXME: What to returned here?
521 if (Speed)
522 *Speed = 0x200;
523 return STATUS_SUCCESS;
524 }
525
526 NTSTATUS
527 STDMETHODCALLTYPE
528 CUSBHardwareDevice::GetDMA(
529 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
530 {
531 if (!m_MemoryManager)
532 return STATUS_UNSUCCESSFUL;
533 *OutDMAMemoryManager = m_MemoryManager;
534 return STATUS_SUCCESS;
535 }
536
537 NTSTATUS
538 STDMETHODCALLTYPE
539 CUSBHardwareDevice::GetUSBQueue(
540 OUT struct IUSBQueue **OutUsbQueue)
541 {
542 if (!m_UsbQueue)
543 return STATUS_UNSUCCESSFUL;
544 *OutUsbQueue = m_UsbQueue;
545 return STATUS_SUCCESS;
546 }
547
548
549 NTSTATUS
550 CUSBHardwareDevice::StartController(void)
551 {
552 EHCI_USBCMD_CONTENT UsbCmd;
553 ULONG UsbSts, FailSafe, ExtendedCapsSupport, Caps, Index;
554 UCHAR Value;
555 LARGE_INTEGER Timeout;
556
557 //
558 // are extended caps supported
559 //
560 ExtendedCapsSupport = (m_Capabilities.HCCParamsLong >> EHCI_ECP_SHIFT) & EHCI_ECP_MASK;
561 if (ExtendedCapsSupport)
562 {
563 DPRINT1("[EHCI] Extended Caps Support detected!\n");
564
565 //
566 // sanity check
567 //
568 ASSERT(ExtendedCapsSupport >= PCI_COMMON_HDR_LENGTH);
569 m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
570
571 //
572 // OS Handoff Synchronization support capability. EHCI 5.1
573 //
574 if ((Caps & EHCI_LEGSUP_CAPID_MASK) == EHCI_LEGSUP_CAPID)
575 {
576 //
577 // is it bios owned
578 //
579 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
580 {
581 DPRINT1("[EHCI] Controller is BIOS owned, acquiring control\n");
582
583 //
584 // acquire ownership
585 //
586 Value = 1;
587 m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+3, sizeof(UCHAR));
588
589 for(Index = 0; Index < 20; Index++)
590 {
591 //
592 // get status
593 //
594 m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
595 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
596 {
597 //
598 // lets wait a bit
599 //
600 Timeout.QuadPart = 50;
601 DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout.LowPart);
602
603 //
604 // convert to 100 ns units (absolute)
605 //
606 Timeout.QuadPart *= -10000;
607
608 //
609 // perform the wait
610 //
611 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
612 }
613 }
614 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
615 {
616 //
617 // failed to acquire ownership
618 //
619 DPRINT1("[EHCI] failed to acquire ownership\n");
620 }
621 else if ((Caps & EHCI_LEGSUP_OSOWNED))
622 {
623 //
624 // HC OS Owned Semaphore EHCI 2.1.7
625 //
626 DPRINT1("[EHCI] acquired ownership\n");
627 }
628 #if 0
629 //
630 // explicitly clear the bios owned flag 2.1.7
631 //
632 Value = 0;
633 m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+2, sizeof(UCHAR));
634
635 //
636 // clear SMI interrupt EHCI 2.1.8
637 //
638 Caps = 4;
639 m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport+4, sizeof(ULONG));
640 #endif
641 }
642 }
643 }
644
645 //
646 // get command register
647 //
648 GetCommandRegister(&UsbCmd);
649
650 //
651 // disable running schedules
652 //
653 UsbCmd.PeriodicEnable = FALSE;
654 UsbCmd.AsyncEnable = FALSE;
655 SetCommandRegister(&UsbCmd);
656
657 //
658 // Wait for execution to start
659 //
660 for (FailSafe = 100; FailSafe > 1; FailSafe--)
661 {
662 KeStallExecutionProcessor(100);
663 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
664
665 if (!(UsbSts & EHCI_STS_PSS) && (UsbSts & EHCI_STS_ASS))
666 {
667 break;
668 }
669 }
670
671 if ((UsbSts & (EHCI_STS_PSS | EHCI_STS_ASS)))
672 {
673 DPRINT1("Failed to stop running schedules %x\n", UsbSts);
674 //ASSERT(FALSE);
675 }
676
677
678 //
679 // Stop the controller if its running
680 //
681 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
682 if (!(UsbSts & EHCI_STS_HALT))
683 {
684 DPRINT1("Stopping Controller %x\n", UsbSts);
685 StopController();
686 }
687
688 //
689 // Reset the controller
690 //
691 ResetController();
692
693 //
694 // check caps
695 //
696 if (m_Capabilities.HCCParams.CurAddrBits)
697 {
698 //
699 // disable 64-bit addressing
700 //
701 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
702 }
703
704 //
705 // Enable Interrupts and start execution
706 //
707 ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC;
708 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask);
709
710 KeStallExecutionProcessor(10);
711
712 ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR);
713
714 DPRINT1("Interrupt Mask %x\n", Status);
715 ASSERT((Status & Mask) == Mask);
716
717 //
718 // Assign the SyncList Register
719 //
720 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
721
722 //
723 // Set Schedules to Enable and Interrupt Threshold to 1ms.
724 //
725 RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT));
726
727 UsbCmd.PeriodicEnable = TRUE;
728 UsbCmd.IntThreshold = 0x8; //1ms
729 UsbCmd.Run = TRUE;
730 UsbCmd.FrameListSize = 0x0; //1024
731
732 if (m_Capabilities.HCCParams.ParkMode)
733 {
734 //
735 // enable async park mode
736 //
737 UsbCmd.AsyncParkEnable = TRUE;
738 UsbCmd.AsyncParkCount = 3;
739 }
740
741 SetCommandRegister(&UsbCmd);
742
743
744 //
745 // Wait for execution to start
746 //
747 for (FailSafe = 100; FailSafe > 1; FailSafe--)
748 {
749 KeStallExecutionProcessor(100);
750 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
751
752 if (!(UsbSts & EHCI_STS_HALT) && (UsbSts & EHCI_STS_PSS))
753 {
754 break;
755 }
756 }
757
758 if (UsbSts & EHCI_STS_HALT)
759 {
760 DPRINT1("Could not start execution on the controller\n");
761 //ASSERT(FALSE);
762 return STATUS_UNSUCCESSFUL;
763 }
764
765 if (!(UsbSts & EHCI_STS_PSS))
766 {
767 DPRINT1("Could not enable periodic scheduling\n");
768 //ASSERT(FALSE);
769 return STATUS_UNSUCCESSFUL;
770 }
771
772 //
773 // Assign the AsyncList Register
774 //
775 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
776
777 //
778 // get command register
779 //
780 GetCommandRegister(&UsbCmd);
781
782 //
783 // preserve bits
784 //
785 UsbCmd.AsyncEnable = TRUE;
786
787 //
788 // enable async
789 //
790 SetCommandRegister(&UsbCmd);
791
792 //
793 // Wait for execution to start
794 //
795 for (FailSafe = 100; FailSafe > 1; FailSafe--)
796 {
797 KeStallExecutionProcessor(100);
798 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
799
800 if ((UsbSts & EHCI_STS_ASS))
801 {
802 break;
803 }
804 }
805
806 if (!(UsbSts & EHCI_STS_ASS))
807 {
808 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts);
809 //ASSERT(FALSE);
810 return STATUS_UNSUCCESSFUL;
811 }
812
813 DPRINT1("UsbSts %x\n", UsbSts);
814 GetCommandRegister(&UsbCmd);
815
816 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable);
817 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable);
818 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold);
819 DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run);
820 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize);
821
822 //
823 // Set port routing to EHCI controller
824 //
825 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
826
827 DPRINT1("EHCI Started!\n");
828 return STATUS_SUCCESS;
829 }
830
831 NTSTATUS
832 CUSBHardwareDevice::StopController(void)
833 {
834 EHCI_USBCMD_CONTENT UsbCmd;
835 ULONG UsbSts, FailSafe;
836
837 //
838 // Disable Interrupts and stop execution
839 //
840 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
841
842 GetCommandRegister(&UsbCmd);
843 UsbCmd.Run = FALSE;
844 SetCommandRegister(&UsbCmd);
845
846 for (FailSafe = 100; FailSafe > 1; FailSafe--)
847 {
848 KeStallExecutionProcessor(10);
849 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
850 if (UsbSts & EHCI_STS_HALT)
851 {
852 break;
853 }
854 }
855
856 if (!(UsbSts & EHCI_STS_HALT))
857 {
858 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
859 return STATUS_UNSUCCESSFUL;
860 }
861
862 return STATUS_SUCCESS;
863 }
864
865 NTSTATUS
866 CUSBHardwareDevice::ResetController(void)
867 {
868 EHCI_USBCMD_CONTENT UsbCmd;
869 ULONG FailSafe;
870
871 GetCommandRegister(&UsbCmd);
872 UsbCmd.HCReset = TRUE;
873 SetCommandRegister(&UsbCmd);
874
875 for (FailSafe = 100; FailSafe > 1; FailSafe--)
876 {
877 KeStallExecutionProcessor(100);
878 GetCommandRegister(&UsbCmd);
879 if (!UsbCmd.HCReset)
880 break;
881 }
882
883 if (UsbCmd.HCReset)
884 {
885 DPRINT1("EHCI ERROR: Controller is not responding to reset request!\n");
886 return STATUS_UNSUCCESSFUL;
887 }
888
889 return STATUS_SUCCESS;
890 }
891
892 NTSTATUS
893 STDMETHODCALLTYPE
894 CUSBHardwareDevice::ResetPort(
895 IN ULONG PortIndex)
896 {
897 ULONG PortStatus;
898 LARGE_INTEGER Timeout;
899
900 if (PortIndex > m_Capabilities.HCSParams.PortCount)
901 return STATUS_UNSUCCESSFUL;
902
903 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
904
905 ASSERT(!EHCI_IS_LOW_SPEED(PortStatus));
906 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
907
908 //
909 // Reset and clean enable
910 //
911 PortStatus |= EHCI_PRT_RESET;
912 PortStatus &= EHCI_PORTSC_DATAMASK;
913 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
914
915 //
916 // delay is 50 ms for port reset as per USB 2.0 spec
917 //
918 Timeout.QuadPart = 50;
919 DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout.LowPart);
920
921 //
922 // convert to 100 ns units (absolute)
923 //
924 Timeout.QuadPart *= -10000;
925
926 //
927 // perform the wait
928 //
929 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
930
931 return STATUS_SUCCESS;
932 }
933
934 NTSTATUS
935 STDMETHODCALLTYPE
936 CUSBHardwareDevice::GetPortStatus(
937 ULONG PortId,
938 OUT USHORT *PortStatus,
939 OUT USHORT *PortChange)
940 {
941 ULONG Value;
942 USHORT Status = 0, Change = 0;
943
944 if (PortId > m_Capabilities.HCSParams.PortCount)
945 return STATUS_UNSUCCESSFUL;
946
947 //
948 // Get the value of the Port Status and Control Register
949 //
950 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
951
952 //
953 // If the PowerPortControl is 0 then host controller does not have power control switches
954 if (!m_Capabilities.HCSParams.PortPowerControl)
955 {
956 Status |= USB_PORT_STATUS_POWER;
957 }
958 else
959 {
960 // Check the value of PortPower
961 if (Value & EHCI_PRT_POWER)
962 {
963 Status |= USB_PORT_STATUS_POWER;
964 }
965 }
966
967 // Get Connected Status
968 if (Value & EHCI_PRT_CONNECTED)
969 {
970 Status |= USB_PORT_STATUS_CONNECT;
971
972 // EHCI only supports high speed
973 Status |= USB_PORT_STATUS_HIGH_SPEED;
974 }
975
976 // Get Enabled Status
977 if (Value & EHCI_PRT_ENABLED)
978 Status |= USB_PORT_STATUS_ENABLE;
979
980 // Is it suspended?
981 if (Value & EHCI_PRT_SUSPEND)
982 Status |= USB_PORT_STATUS_SUSPEND;
983
984 // a overcurrent is active?
985 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
986 Status |= USB_PORT_STATUS_OVER_CURRENT;
987
988 // In a reset state?
989 if ((Value & EHCI_PRT_RESET) || m_PortResetInProgress[PortId])
990 {
991 Status |= USB_PORT_STATUS_RESET;
992 Change |= USB_PORT_STATUS_RESET;
993 }
994
995 // This indicates a connect or disconnect
996 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
997 Change |= USB_PORT_STATUS_CONNECT;
998
999 // This is set to indicate a critical port error
1000 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
1001 Change |= USB_PORT_STATUS_ENABLE;
1002
1003 *PortStatus = Status;
1004 *PortChange = Change;
1005
1006 return STATUS_SUCCESS;
1007 }
1008
1009 NTSTATUS
1010 STDMETHODCALLTYPE
1011 CUSBHardwareDevice::ClearPortStatus(
1012 ULONG PortId,
1013 ULONG Status)
1014 {
1015 ULONG Value;
1016 LARGE_INTEGER Timeout;
1017
1018 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
1019
1020 if (PortId > m_Capabilities.HCSParams.PortCount)
1021 return STATUS_UNSUCCESSFUL;
1022
1023 if (Status == C_PORT_RESET)
1024 {
1025 // reset done
1026 m_PortResetInProgress[PortId] = FALSE;
1027
1028 // Clear reset
1029 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1030 Value &= (EHCI_PORTSC_DATAMASK | EHCI_PRT_ENABLED);
1031 Value &= ~EHCI_PRT_RESET;
1032 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
1033
1034 //
1035 // wait for reset bit to clear
1036 //
1037 do
1038 {
1039 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1040
1041 if (!(Value & EHCI_PRT_RESET))
1042 break;
1043
1044 KeStallExecutionProcessor(20);
1045 } while (TRUE);
1046
1047 //
1048 // delay is 50 ms
1049 //
1050 Timeout.QuadPart = 50;
1051 DPRINT1("Waiting %lu milliseconds for port to recover after reset\n", Timeout.LowPart);
1052
1053 //
1054 // convert to 100 ns units (absolute)
1055 //
1056 Timeout.QuadPart *= -10000;
1057
1058 //
1059 // perform the wait
1060 //
1061 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1062
1063 //
1064 // check the port status after reset
1065 //
1066 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1067 if (!(Value & EHCI_PRT_CONNECTED))
1068 {
1069 DPRINT1("No device is here after reset. Bad controller/device?\n");
1070 return STATUS_UNSUCCESSFUL;
1071 }
1072 else if (EHCI_IS_LOW_SPEED(Value))
1073 {
1074 DPRINT1("Low speed device connected. Releasing ownership\n");
1075 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value | EHCI_PRT_RELEASEOWNERSHIP);
1076 return STATUS_DEVICE_NOT_CONNECTED;
1077 }
1078 else if (!(Value & EHCI_PRT_ENABLED))
1079 {
1080 DPRINT1("Full speed device connected. Releasing ownership\n");
1081 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value | EHCI_PRT_RELEASEOWNERSHIP);
1082 return STATUS_DEVICE_NOT_CONNECTED;
1083 }
1084 else
1085 {
1086 DPRINT1("High speed device connected\n");
1087 return STATUS_SUCCESS;
1088 }
1089 }
1090 else if (Status == C_PORT_CONNECTION)
1091 {
1092 //
1093 // reset status change bits
1094 //
1095 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1096 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
1097
1098 if (Value & EHCI_PRT_CONNECTED)
1099 {
1100 //
1101 // delay is 100 ms
1102 //
1103 Timeout.QuadPart = 100;
1104 DPRINT1("Waiting %lu milliseconds for port to stabilize after connection\n", Timeout.LowPart);
1105
1106 //
1107 // convert to 100 ns units (absolute)
1108 //
1109 Timeout.QuadPart *= -10000;
1110
1111 //
1112 // perform the wait
1113 //
1114 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1115 }
1116 }
1117
1118 return STATUS_SUCCESS;
1119 }
1120
1121
1122 NTSTATUS
1123 STDMETHODCALLTYPE
1124 CUSBHardwareDevice::SetPortFeature(
1125 ULONG PortId,
1126 ULONG Feature)
1127 {
1128 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
1129
1130 if (PortId > m_Capabilities.HCSParams.PortCount)
1131 return STATUS_UNSUCCESSFUL;
1132
1133 if (Feature == PORT_ENABLE)
1134 {
1135 //
1136 // FIXME: EHCI Ports can only be disabled via reset
1137 //
1138 DPRINT1("PORT_ENABLE not supported for EHCI\n");
1139 }
1140
1141 if (Feature == PORT_RESET)
1142 {
1143 //
1144 // call the helper
1145 //
1146 ResetPort(PortId);
1147
1148 // reset in progress
1149 m_PortResetInProgress[PortId] = TRUE;
1150
1151 //
1152 // is there a status change callback
1153 //
1154 if (m_SCECallBack != NULL)
1155 {
1156 //
1157 // issue callback
1158 //
1159 m_SCECallBack(m_SCEContext);
1160 }
1161 }
1162
1163 if (Feature == PORT_POWER)
1164 {
1165 if (m_Capabilities.HCSParams.PortPowerControl)
1166 {
1167 ULONG Value;
1168 LARGE_INTEGER Timeout;
1169
1170 //
1171 // enable port power
1172 //
1173 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
1174 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
1175
1176 //
1177 // delay is 20 ms
1178 //
1179 Timeout.QuadPart = 20;
1180 DPRINT1("Waiting %lu milliseconds for port power up\n", Timeout.LowPart);
1181
1182 //
1183 // convert to 100 ns units (absolute)
1184 //
1185 Timeout.QuadPart *= -10000;
1186
1187 //
1188 // perform the wait
1189 //
1190 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1191 }
1192 }
1193 return STATUS_SUCCESS;
1194 }
1195
1196 VOID
1197 STDMETHODCALLTYPE
1198 CUSBHardwareDevice::SetAsyncListRegister(
1199 ULONG PhysicalAddress)
1200 {
1201 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
1202 }
1203
1204 VOID
1205 STDMETHODCALLTYPE
1206 CUSBHardwareDevice::SetPeriodicListRegister(
1207 ULONG PhysicalAddress)
1208 {
1209 //
1210 // store physical address
1211 //
1212 m_SyncFramePhysAddr = PhysicalAddress;
1213 }
1214
1215 struct _QUEUE_HEAD *
1216 STDMETHODCALLTYPE
1217 CUSBHardwareDevice::GetAsyncListQueueHead()
1218 {
1219 return AsyncQueueHead;
1220 }
1221
1222 ULONG
1223 STDMETHODCALLTYPE
1224 CUSBHardwareDevice::GetPeriodicListRegister()
1225 {
1226 UNIMPLEMENTED;
1227 return NULL;
1228 }
1229
1230 VOID
1231 STDMETHODCALLTYPE
1232 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1233 PVOID CallBack,
1234 PVOID Context)
1235 {
1236 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1237 m_SCEContext = Context;
1238 }
1239
1240 BOOLEAN
1241 NTAPI
1242 InterruptServiceRoutine(
1243 IN PKINTERRUPT Interrupt,
1244 IN PVOID ServiceContext)
1245 {
1246 CUSBHardwareDevice *This;
1247 ULONG CStatus;
1248
1249 This = (CUSBHardwareDevice*) ServiceContext;
1250 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
1251
1252 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
1253 DPRINT("InterruptServiceRoutine CStatus %lx\n", CStatus);
1254
1255 //
1256 // Check that it belongs to EHCI
1257 //
1258 if (!CStatus)
1259 return FALSE;
1260
1261 //
1262 // Clear the Status
1263 //
1264 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
1265
1266 if (CStatus & EHCI_STS_FATAL)
1267 {
1268 This->StopController();
1269 DPRINT1("EHCI: Host System Error!\n");
1270 return TRUE;
1271 }
1272
1273 if (CStatus & EHCI_ERROR_INT)
1274 {
1275 DPRINT1("EHCI Status = 0x%x\n", CStatus);
1276 }
1277
1278 if (CStatus & EHCI_STS_HALT)
1279 {
1280 DPRINT1("Host Error Unexpected Halt\n");
1281 // FIXME: Reset controller\n");
1282 return TRUE;
1283 }
1284
1285 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
1286 return TRUE;
1287 }
1288
1289 VOID NTAPI
1290 EhciDeferredRoutine(
1291 IN PKDPC Dpc,
1292 IN PVOID DeferredContext,
1293 IN PVOID SystemArgument1,
1294 IN PVOID SystemArgument2)
1295 {
1296 CUSBHardwareDevice *This;
1297 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell, QueueSCEWorkItem;
1298 NTSTATUS Status = STATUS_SUCCESS;
1299 EHCI_USBCMD_CONTENT UsbCmd;
1300
1301 This = (CUSBHardwareDevice*) SystemArgument1;
1302 CStatus = (ULONG) SystemArgument2;
1303
1304 DPRINT("EhciDeferredRoutine CStatus %lx\n", CStatus);
1305
1306 //
1307 // check for completion of async schedule
1308 //
1309 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
1310 {
1311 //
1312 // check if there is a door bell ring in progress
1313 //
1314 if (This->m_DoorBellRingInProgress == FALSE)
1315 {
1316 if (CStatus & EHCI_ERROR_INT)
1317 {
1318 //
1319 // controller reported error
1320 //
1321 DPRINT1("CStatus %lx\n", CStatus);
1322 //ASSERT(FALSE);
1323 }
1324
1325 //
1326 // inform IUSBQueue of a completed queue head
1327 //
1328 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1329
1330 //
1331 // was a queue head completed?
1332 //
1333 if (ShouldRingDoorBell)
1334 {
1335 //
1336 // set door ring bell in progress status flag
1337 //
1338 This->m_DoorBellRingInProgress = TRUE;
1339
1340 //
1341 // get command register
1342 //
1343 This->GetCommandRegister(&UsbCmd);
1344
1345 //
1346 // set door rang bell bit
1347 //
1348 UsbCmd.DoorBell = TRUE;
1349
1350 //
1351 // update command status
1352 //
1353 This->SetCommandRegister(&UsbCmd);
1354 }
1355 }
1356 }
1357
1358 //
1359 // check if the controller has acknowledged the door bell
1360 //
1361 if (CStatus & EHCI_STS_IAA)
1362 {
1363 //
1364 // controller has acknowledged, assert we rang the bell
1365 //
1366 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1367
1368 //
1369 // now notify IUSBQueue that it can free completed requests
1370 //
1371 This->m_UsbQueue->CompleteAsyncRequests();
1372
1373 //
1374 // door ring bell completed
1375 //
1376 This->m_DoorBellRingInProgress = FALSE;
1377 }
1378
1379 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1380 if (CStatus & EHCI_STS_PCD)
1381 {
1382 QueueSCEWorkItem = FALSE;
1383 for (i = 0; i < PortCount; i++)
1384 {
1385 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1386
1387 //
1388 // Device connected or removed
1389 //
1390 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1391 {
1392 if (PortStatus & EHCI_PRT_CONNECTED)
1393 {
1394 DPRINT1("Device connected on port %lu\n", i);
1395
1396 if (This->m_Capabilities.HCSParams.CHCCount)
1397 {
1398 if (PortStatus & EHCI_PRT_ENABLED)
1399 {
1400 DPRINT1("Misbehaving controller. Port should be disabled at this point\n");
1401 }
1402
1403 if (EHCI_IS_LOW_SPEED(PortStatus))
1404 {
1405 DPRINT1("Low speed device connected. Releasing ownership\n");
1406 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), PortStatus | EHCI_PRT_RELEASEOWNERSHIP);
1407 continue;
1408 }
1409 }
1410
1411 //
1412 // work to do
1413 //
1414 QueueSCEWorkItem = TRUE;
1415 }
1416 else
1417 {
1418 DPRINT1("Device disconnected on port %lu\n", i);
1419
1420 //
1421 // work to do
1422 //
1423 QueueSCEWorkItem = TRUE;
1424 }
1425 }
1426 }
1427
1428 //
1429 // is there a status change callback and a high speed device connected / disconnected
1430 //
1431 if (QueueSCEWorkItem && This->m_SCECallBack != NULL)
1432 {
1433 if (InterlockedCompareExchange(&This->m_StatusChangeWorkItemStatus, 1, 0) == 0)
1434 {
1435 //
1436 // queue work item for processing
1437 //
1438 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1439 }
1440 }
1441 }
1442 return;
1443 }
1444
1445 VOID
1446 NTAPI
1447 StatusChangeWorkItemRoutine(
1448 PVOID Context)
1449 {
1450 //
1451 // cast to hardware object
1452 //
1453 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1454
1455 //
1456 // is there a callback
1457 //
1458 if (This->m_SCECallBack)
1459 {
1460 //
1461 // issue callback
1462 //
1463 This->m_SCECallBack(This->m_SCEContext);
1464 }
1465
1466 //
1467 // reset active status
1468 //
1469 InterlockedDecrement(&This->m_StatusChangeWorkItemStatus);
1470 }
1471
1472 NTSTATUS
1473 NTAPI
1474 CreateUSBHardware(
1475 PUSBHARDWAREDEVICE *OutHardware)
1476 {
1477 PUSBHARDWAREDEVICE This;
1478
1479 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1480
1481 if (!This)
1482 return STATUS_INSUFFICIENT_RESOURCES;
1483
1484 This->AddRef();
1485
1486 // return result
1487 *OutHardware = (PUSBHARDWAREDEVICE)This;
1488
1489 return STATUS_SUCCESS;
1490 }