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