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