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