9ae3e9f6082b8e2afdc45f74a50ad16482e3cad6
[reactos.git] / drivers / usb / usbehci_new / hardware.cpp
1 /*
2 * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbehci/hcd_controller.cpp
5 * PURPOSE: USB EHCI device driver.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #define INITGUID
12 #include "usbehci.h"
13 #include "hardware.h"
14
15 typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
16
17 BOOLEAN
18 NTAPI
19 InterruptServiceRoutine(
20 IN PKINTERRUPT Interrupt,
21 IN PVOID ServiceContext);
22
23 VOID
24 NTAPI
25 EhciDefferedRoutine(
26 IN PKDPC Dpc,
27 IN PVOID DeferredContext,
28 IN PVOID SystemArgument1,
29 IN PVOID SystemArgument2);
30
31 VOID
32 NTAPI
33 StatusChangeWorkItemRoutine(PVOID Context);
34
35 class CUSBHardwareDevice : public IUSBHardwareDevice
36 {
37 public:
38 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
39
40 STDMETHODIMP_(ULONG) AddRef()
41 {
42 InterlockedIncrement(&m_Ref);
43 return m_Ref;
44 }
45 STDMETHODIMP_(ULONG) Release()
46 {
47 InterlockedDecrement(&m_Ref);
48
49 if (!m_Ref)
50 {
51 delete this;
52 return 0;
53 }
54 return m_Ref;
55 }
56 // com
57 NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject);
58 NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources);
59 NTSTATUS PnpStop(void);
60 NTSTATUS HandlePower(PIRP Irp);
61 NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed);
62 NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
63 NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
64
65 NTSTATUS StartController();
66 NTSTATUS StopController();
67 NTSTATUS ResetController();
68 NTSTATUS ResetPort(ULONG PortIndex);
69
70 NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange);
71 NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status);
72 NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature);
73
74 VOID SetAsyncListRegister(ULONG PhysicalAddress);
75 VOID SetPeriodicListRegister(ULONG PhysicalAddress);
76 struct _QUEUE_HEAD * GetAsyncListQueueHead();
77 ULONG GetPeriodicListRegister();
78
79 VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context);
80
81 KIRQL AcquireDeviceLock(void);
82 VOID ReleaseDeviceLock(KIRQL OldLevel);
83 // 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 DPRINT1("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 DPRINT1("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->AlternateNextPointer = TERMINATE_POINTER;
471 AsyncQueueHead->NextPointer = TERMINATE_POINTER;
472 AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart;
473 AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH;
474 AsyncQueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
475 AsyncQueueHead->Token.Bits.InterruptOnComplete = FALSE;
476 AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE;
477 AsyncQueueHead->Token.Bits.Halted = TRUE;
478 AsyncQueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
479 AsyncQueueHead->EndPointCharacteristics.NakCountReload = 0;
480 AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
481 AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
482
483 InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
484
485 //
486 // Initialize the UsbQueue now that we have an AdapterObject.
487 //
488 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock);
489 if (!NT_SUCCESS(Status))
490 {
491 DPRINT1("Failed to Initialize the UsbQueue\n");
492 return Status;
493 }
494
495 //
496 // Start the controller
497 //
498 DPRINT1("Starting Controller\n");
499 Status = StartController();
500
501 //
502 // done
503 //
504 return Status;
505 }
506
507 NTSTATUS
508 CUSBHardwareDevice::PnpStop(void)
509 {
510 UNIMPLEMENTED
511 return STATUS_NOT_IMPLEMENTED;
512 }
513
514 NTSTATUS
515 CUSBHardwareDevice::HandlePower(
516 PIRP Irp)
517 {
518 UNIMPLEMENTED
519 return STATUS_NOT_IMPLEMENTED;
520 }
521
522 NTSTATUS
523 CUSBHardwareDevice::GetDeviceDetails(
524 OUT OPTIONAL PUSHORT VendorId,
525 OUT OPTIONAL PUSHORT DeviceId,
526 OUT OPTIONAL PULONG NumberOfPorts,
527 OUT OPTIONAL PULONG Speed)
528 {
529 if (VendorId)
530 *VendorId = m_VendorID;
531 if (DeviceId)
532 *DeviceId = m_DeviceID;
533 if (NumberOfPorts)
534 *NumberOfPorts = m_Capabilities.HCSParams.PortCount;
535 //FIXME: What to returned here?
536 if (Speed)
537 *Speed = 0x200;
538 return STATUS_SUCCESS;
539 }
540
541 NTSTATUS CUSBHardwareDevice::GetDMA(
542 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
543 {
544 if (!m_MemoryManager)
545 return STATUS_UNSUCCESSFUL;
546 *OutDMAMemoryManager = m_MemoryManager;
547 return STATUS_SUCCESS;
548 }
549
550 NTSTATUS
551 CUSBHardwareDevice::GetUSBQueue(
552 OUT struct IUSBQueue **OutUsbQueue)
553 {
554 if (!m_UsbQueue)
555 return STATUS_UNSUCCESSFUL;
556 *OutUsbQueue = m_UsbQueue;
557 return STATUS_SUCCESS;
558 }
559
560
561 NTSTATUS
562 CUSBHardwareDevice::StartController(void)
563 {
564 EHCI_USBCMD_CONTENT UsbCmd;
565 ULONG UsbSts, FailSafe, ExtendedCapsSupport, Caps, Index;
566 UCHAR Value;
567 LARGE_INTEGER Timeout;
568
569 //
570 // check caps
571 //
572 if (m_Capabilities.HCCParams.CurAddrBits)
573 {
574 //
575 // disable 64-bit addressing
576 //
577 EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0);
578 }
579
580 //
581 // are extended caps supported
582 //
583 ExtendedCapsSupport = (m_Capabilities.HCCParamsLong >> EHCI_ECP_SHIFT) & EHCI_ECP_MASK;
584 if (ExtendedCapsSupport)
585 {
586 DPRINT1("[EHCI] Extended Caps Support detected!\n");
587
588 //
589 // sanity check
590 //
591 ASSERT(ExtendedCapsSupport >= PCI_COMMON_HDR_LENGTH);
592 m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
593
594 //
595 // OS Handoff Synchronization support capability. EHCI 5.1
596 //
597 if ((Caps & EHCI_LEGSUP_CAPID_MASK) == EHCI_LEGSUP_CAPID)
598 {
599 //
600 // is it bios owned
601 //
602 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
603 {
604 DPRINT1("[EHCI] Controller is BIOS owned, acquring control\n");
605
606 //
607 // acquire ownership
608 //
609 Value = 1;
610 m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+3, sizeof(UCHAR));
611
612 for(Index = 0; Index < 20; Index++)
613 {
614 //
615 // get status
616 //
617 m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG));
618 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
619 {
620 //
621 // lets wait a bit
622 //
623 Timeout.QuadPart = 50;
624 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
625
626 //
627 // convert to 100 ns units (absolute)
628 //
629 Timeout.QuadPart *= -10000;
630
631 //
632 // perform the wait
633 //
634 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
635 }
636 }
637 if ((Caps & EHCI_LEGSUP_BIOSOWNED))
638 {
639 //
640 // failed to aquire ownership
641 //
642 DPRINT1("[EHCI] failed to acquire ownership\n");
643 }
644 else if ((Caps & EHCI_LEGSUP_OSOWNED))
645 {
646 //
647 // HC OS Owned Semaphore EHCI 2.1.8
648 //
649 DPRINT1("[EHCI] acquired ownership\n");
650 }
651 }
652 }
653 }
654
655
656
657 #if 1
658 //
659 // Stop the controller if its running
660 //
661 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
662 if (!(UsbSts & EHCI_STS_HALT))
663 {
664 DPRINT1("Stopping Controller %x\n", UsbSts);
665 StopController();
666 }
667 #endif
668
669 //
670 // Enable Interrupts and start execution
671 //
672 ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC;
673 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask);
674
675 KeStallExecutionProcessor(10);
676
677 ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR);
678
679 DPRINT1("Interrupt Mask %x\n", Status);
680 ASSERT((Status & Mask) == Mask);
681
682
683 //
684 // Assign the SyncList Register
685 //
686 EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr);
687
688 //
689 // Set Schedules to Enable and Interrupt Threshold to 1ms.
690 //
691 RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT));
692
693 UsbCmd.PeriodicEnable = TRUE;
694 UsbCmd.IntThreshold = 0x8; //1ms
695 UsbCmd.Run = TRUE;
696 UsbCmd.FrameListSize = 0x0; //1024
697 SetCommandRegister(&UsbCmd);
698
699 //
700 // Wait for execution to start
701 //
702 for (FailSafe = 100; FailSafe > 1; FailSafe--)
703 {
704 KeStallExecutionProcessor(10);
705 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
706
707 if (!(UsbSts & EHCI_STS_HALT))
708 {
709 break;
710 }
711 }
712
713
714 if (UsbSts & EHCI_STS_HALT)
715 {
716 DPRINT1("Could not start execution on the controller\n");
717 return STATUS_UNSUCCESSFUL;
718 }
719
720 //
721 // Assign the AsyncList Register
722 //
723 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr);
724
725 //
726 // get command register
727 //
728 GetCommandRegister(&UsbCmd);
729
730 //
731 // preserve bits
732 //
733 UsbCmd.AsyncEnable = TRUE;
734
735 //
736 // enable async
737 //
738 SetCommandRegister(&UsbCmd);
739
740 //
741 // Wait for execution to start
742 //
743 for (FailSafe = 100; FailSafe > 1; FailSafe--)
744 {
745 KeStallExecutionProcessor(10);
746 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
747
748 if ((UsbSts & EHCI_STS_ASS))
749 {
750 break;
751 }
752 }
753
754 if (!(UsbSts & EHCI_STS_ASS))
755 {
756 DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts);
757 ASSERT(FALSE);
758 return STATUS_UNSUCCESSFUL;
759 }
760
761 DPRINT1("UsbSts %x\n", UsbSts);
762 GetCommandRegister(&UsbCmd);
763
764 DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable);
765 DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable);
766 DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold);
767 DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run);
768 DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize);
769
770 //
771 // Set port routing to EHCI controller
772 //
773 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
774
775 DPRINT1("EHCI Started!\n");
776 return STATUS_SUCCESS;
777 }
778
779 NTSTATUS
780 CUSBHardwareDevice::StopController(void)
781 {
782 EHCI_USBCMD_CONTENT UsbCmd;
783 ULONG UsbSts, FailSafe;
784
785 //
786 // Disable Interrupts and stop execution
787 //
788 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
789
790 GetCommandRegister(&UsbCmd);
791 UsbCmd.Run = FALSE;
792 SetCommandRegister(&UsbCmd);
793
794 for (FailSafe = 100; FailSafe > 1; FailSafe--)
795 {
796 KeStallExecutionProcessor(10);
797 UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
798 if (UsbSts & EHCI_STS_HALT)
799 {
800 break;
801 }
802 }
803
804 if (!(UsbSts & EHCI_STS_HALT))
805 {
806 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
807 return STATUS_UNSUCCESSFUL;
808 }
809
810 return STATUS_SUCCESS;
811 }
812
813 NTSTATUS
814 CUSBHardwareDevice::ResetController(void)
815 {
816 UNIMPLEMENTED
817 return STATUS_NOT_IMPLEMENTED;
818 }
819
820 NTSTATUS
821 CUSBHardwareDevice::ResetPort(
822 IN ULONG PortIndex)
823 {
824 ULONG PortStatus;
825 LARGE_INTEGER Timeout;
826
827 if (PortIndex > m_Capabilities.HCSParams.PortCount)
828 return STATUS_UNSUCCESSFUL;
829
830 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
831 //
832 // check slow speed line before reset
833 //
834 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
835 {
836 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
837 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
838 return STATUS_DEVICE_NOT_CONNECTED;
839 }
840
841 ASSERT(PortStatus & EHCI_PRT_CONNECTED);
842
843 //
844 // Reset and clean enable
845 //
846 PortStatus |= EHCI_PRT_RESET;
847 PortStatus &= ~EHCI_PRT_ENABLED;
848 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
849
850 //
851 // delay is 20 ms for port reset as per USB 2.0 spec
852 //
853 Timeout.QuadPart = 20;
854 DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart);
855
856 //
857 // convert to 100 ns units (absolute)
858 //
859 Timeout.QuadPart *= -10000;
860
861 //
862 // perform the wait
863 //
864 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
865
866 //
867 // Clear reset
868 //
869 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
870 PortStatus &= ~EHCI_PRT_RESET;
871 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus);
872
873 do
874 {
875 //
876 // wait
877 //
878 KeStallExecutionProcessor(100);
879
880 //
881 // Check that the port reset
882 //
883 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
884 if (!(PortStatus & EHCI_PRT_RESET))
885 break;
886 } while (TRUE);
887
888 //
889 // delay is 10 ms
890 //
891 Timeout.QuadPart = 10;
892 DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart);
893
894 //
895 // convert to 100 ns units (absolute)
896 //
897 Timeout.QuadPart *= -10000;
898
899 //
900 // perform the wait
901 //
902 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
903
904 //
905 // check slow speed line after reset
906 //
907 PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex));
908 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
909 {
910 DPRINT1("Non HighSpeed device. Releasing Ownership\n");
911 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP);
912 return STATUS_DEVICE_NOT_CONNECTED;
913 }
914
915 //
916 // this must be enabled now
917 //
918 ASSERT(PortStatus & EHCI_PRT_ENABLED);
919
920 return STATUS_SUCCESS;
921 }
922
923 NTSTATUS
924 CUSBHardwareDevice::GetPortStatus(
925 ULONG PortId,
926 OUT USHORT *PortStatus,
927 OUT USHORT *PortChange)
928 {
929 ULONG Value;
930 USHORT Status = 0, Change = 0;
931
932 if (PortId > m_Capabilities.HCSParams.PortCount)
933 return STATUS_UNSUCCESSFUL;
934
935 //
936 // Get the value of the Port Status and Control Register
937 //
938 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
939
940 //
941 // If the PowerPortControl is 0 then host controller does not have power control switches
942 if (!m_Capabilities.HCSParams.PortPowerControl)
943 {
944 Status |= USB_PORT_STATUS_POWER;
945 }
946 else
947 {
948 // Check the value of PortPower
949 if (Value & EHCI_PRT_POWER)
950 {
951 Status |= USB_PORT_STATUS_POWER;
952 }
953 }
954
955 // Get Connected Status
956 if (Value & EHCI_PRT_CONNECTED)
957 {
958 Status |= USB_PORT_STATUS_CONNECT;
959
960 // Get Speed. If SlowSpeedLine flag is there then its a slow speed device
961 if (Value & EHCI_PRT_SLOWSPEEDLINE)
962 Status |= USB_PORT_STATUS_LOW_SPEED;
963 else
964 Status |= USB_PORT_STATUS_HIGH_SPEED;
965 }
966
967 // Get Enabled Status
968 if (Value & EHCI_PRT_ENABLED)
969 Status |= USB_PORT_STATUS_ENABLE;
970
971 // Is it suspended?
972 if (Value & EHCI_PRT_SUSPEND)
973 Status |= USB_PORT_STATUS_SUSPEND;
974
975 // a overcurrent is active?
976 if (Value & EHCI_PRT_OVERCURRENTACTIVE)
977 Status |= USB_PORT_STATUS_OVER_CURRENT;
978
979 // In a reset state?
980 if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId])
981 {
982 Status |= USB_PORT_STATUS_RESET;
983 Change |= USB_PORT_STATUS_RESET;
984 }
985
986 //
987 // FIXME: Is the Change here correct?
988 //
989 if (Value & EHCI_PRT_CONNECTSTATUSCHANGE)
990 Change |= USB_PORT_STATUS_CONNECT;
991
992 if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE)
993 Change |= USB_PORT_STATUS_ENABLE;
994
995 *PortStatus = Status;
996 *PortChange = Change;
997
998 return STATUS_SUCCESS;
999 }
1000
1001 NTSTATUS
1002 CUSBHardwareDevice::ClearPortStatus(
1003 ULONG PortId,
1004 ULONG Status)
1005 {
1006 ULONG Value;
1007
1008 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
1009
1010 if (PortId > m_Capabilities.HCSParams.PortCount)
1011 return STATUS_UNSUCCESSFUL;
1012
1013 if (Status == C_PORT_RESET)
1014 {
1015 //
1016 // update port status
1017 //
1018 m_ResetInProgress[PortId] = FALSE;
1019 }
1020
1021 if (Status == C_PORT_CONNECTION)
1022 {
1023 LARGE_INTEGER Timeout;
1024
1025 //
1026 // reset status change bits
1027 //
1028 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1029 Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE;
1030 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value);
1031
1032 //
1033 // delay is 100 ms
1034 //
1035 Timeout.QuadPart = 100;
1036 DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart);
1037
1038 //
1039 // convert to 100 ns units (absolute)
1040 //
1041 Timeout.QuadPart *= -10000;
1042
1043 //
1044 // perform the wait
1045 //
1046 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1047 }
1048
1049 return STATUS_SUCCESS;
1050 }
1051
1052
1053 NTSTATUS
1054 CUSBHardwareDevice::SetPortFeature(
1055 ULONG PortId,
1056 ULONG Feature)
1057 {
1058 ULONG Value;
1059
1060 DPRINT("CUSBHardwareDevice::SetPortFeature\n");
1061
1062 if (PortId > m_Capabilities.HCSParams.PortCount)
1063 return STATUS_UNSUCCESSFUL;
1064
1065 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId));
1066
1067 if (Feature == PORT_ENABLE)
1068 {
1069 //
1070 // FIXME: EHCI Ports can only be disabled via reset
1071 //
1072 DPRINT1("PORT_ENABLE not supported for EHCI\n");
1073 }
1074
1075 if (Feature == PORT_RESET)
1076 {
1077 ResetPort(PortId);
1078
1079 //
1080 // update cached settings
1081 //
1082 m_ResetInProgress[PortId] = TRUE;
1083
1084 //
1085 // is there a status change callback
1086 //
1087 if (m_SCECallBack != NULL)
1088 {
1089 //
1090 // issue callback
1091 //
1092 m_SCECallBack(m_SCEContext);
1093 }
1094 }
1095
1096 if (Feature == PORT_POWER)
1097 {
1098 if (m_Capabilities.HCSParams.PortPowerControl)
1099 {
1100 ULONG Value;
1101 LARGE_INTEGER Timeout;
1102
1103 //
1104 // enable port power
1105 //
1106 Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER;
1107 EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC, Value);
1108
1109 //
1110 // delay is 20 ms
1111 //
1112 Timeout.QuadPart = 20;
1113 DPRINT1("Waiting %d milliseconds for port power up\n", Timeout.LowPart);
1114
1115 //
1116 // convert to 100 ns units (absolute)
1117 //
1118 Timeout.QuadPart *= -10000;
1119
1120 //
1121 // perform the wait
1122 //
1123 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
1124 }
1125 }
1126 return STATUS_SUCCESS;
1127 }
1128
1129 VOID
1130 CUSBHardwareDevice::SetAsyncListRegister(
1131 ULONG PhysicalAddress)
1132 {
1133 EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress);
1134 }
1135
1136 VOID
1137 CUSBHardwareDevice::SetPeriodicListRegister(
1138 ULONG PhysicalAddress)
1139 {
1140 //
1141 // store physical address
1142 //
1143 m_SyncFramePhysAddr = PhysicalAddress;
1144 }
1145
1146 struct _QUEUE_HEAD *
1147 CUSBHardwareDevice::GetAsyncListQueueHead()
1148 {
1149 return AsyncQueueHead;
1150 }
1151
1152 ULONG CUSBHardwareDevice::GetPeriodicListRegister()
1153 {
1154 UNIMPLEMENTED
1155 return NULL;
1156 }
1157
1158 VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1159 PVOID CallBack,
1160 PVOID Context)
1161 {
1162 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1163 m_SCEContext = Context;
1164 }
1165
1166 KIRQL
1167 CUSBHardwareDevice::AcquireDeviceLock(void)
1168 {
1169 KIRQL OldLevel;
1170
1171 //
1172 // acquire lock
1173 //
1174 KeAcquireSpinLock(&m_Lock, &OldLevel);
1175
1176 //
1177 // return old irql
1178 //
1179 return OldLevel;
1180 }
1181
1182
1183 VOID
1184 CUSBHardwareDevice::ReleaseDeviceLock(
1185 KIRQL OldLevel)
1186 {
1187 KeReleaseSpinLock(&m_Lock, OldLevel);
1188 }
1189
1190 BOOLEAN
1191 NTAPI
1192 InterruptServiceRoutine(
1193 IN PKINTERRUPT Interrupt,
1194 IN PVOID ServiceContext)
1195 {
1196 CUSBHardwareDevice *This;
1197 ULONG CStatus;
1198
1199 This = (CUSBHardwareDevice*) ServiceContext;
1200 CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS);
1201
1202 CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
1203 //
1204 // Check that it belongs to EHCI
1205 //
1206 if (!CStatus)
1207 return FALSE;
1208
1209 //
1210 // Clear the Status
1211 //
1212 This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus);
1213
1214 if (CStatus & EHCI_STS_FATAL)
1215 {
1216 This->StopController();
1217 DPRINT1("EHCI: Host System Error!\n");
1218 return TRUE;
1219 }
1220
1221 if (CStatus & EHCI_ERROR_INT)
1222 {
1223 DPRINT1("EHCI Status = 0x%x\n", CStatus);
1224 }
1225
1226 if (CStatus & EHCI_STS_HALT)
1227 {
1228 DPRINT1("Host Error Unexpected Halt\n");
1229 // FIXME: Reset controller\n");
1230 return TRUE;
1231 }
1232
1233 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus);
1234 return TRUE;
1235 }
1236
1237 VOID NTAPI
1238 EhciDefferedRoutine(
1239 IN PKDPC Dpc,
1240 IN PVOID DeferredContext,
1241 IN PVOID SystemArgument1,
1242 IN PVOID SystemArgument2)
1243 {
1244 CUSBHardwareDevice *This;
1245 ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell;
1246 NTSTATUS Status = STATUS_SUCCESS;
1247 EHCI_USBCMD_CONTENT UsbCmd;
1248
1249 This = (CUSBHardwareDevice*) SystemArgument1;
1250 CStatus = (ULONG) SystemArgument2;
1251
1252 DPRINT("CStatus %x\n", CStatus);
1253
1254 //
1255 // check for completion of async schedule
1256 //
1257 if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT))
1258 {
1259 //
1260 // check if there is a door bell ring in progress
1261 //
1262 if (This->m_DoorBellRingInProgress == FALSE)
1263 {
1264 if (CStatus & EHCI_ERROR_INT)
1265 {
1266 //
1267 // controller reported error
1268 //
1269 DPRINT1("CStatus %x\n", CStatus);
1270 Status = STATUS_UNSUCCESSFUL;
1271 PC_ASSERT(FALSE);
1272 return;
1273 }
1274
1275 //
1276 // inform IUSBQueue of a completed queue head
1277 //
1278 This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell);
1279
1280 //
1281 // was a queue head completed?
1282 //
1283 if (ShouldRingDoorBell)
1284 {
1285 //
1286 // set door ring bell in progress status flag
1287 //
1288 This->m_DoorBellRingInProgress = TRUE;
1289
1290 //
1291 // get command register
1292 //
1293 This->GetCommandRegister(&UsbCmd);
1294
1295 //
1296 // set door rang bell bit
1297 //
1298 UsbCmd.DoorBell = TRUE;
1299
1300 //
1301 // update command status
1302 //
1303 This->SetCommandRegister(&UsbCmd);
1304 }
1305 }
1306 }
1307
1308 //
1309 // check if the controller has acknowledged the door bell
1310 //
1311 if (CStatus & EHCI_STS_IAA)
1312 {
1313 //
1314 // controller has acknowledged, assert we rang the bell
1315 //
1316 PC_ASSERT(This->m_DoorBellRingInProgress == TRUE);
1317
1318 //
1319 // now notify IUSBQueue that it can free completed requests
1320 //
1321 This->m_UsbQueue->CompleteAsyncRequests();
1322
1323 //
1324 // door ring bell completed
1325 //
1326 This->m_DoorBellRingInProgress = FALSE;
1327 }
1328
1329 This->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
1330 if (CStatus & EHCI_STS_PCD)
1331 {
1332 for (i = 0; i < PortCount; i++)
1333 {
1334 PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i));
1335
1336 //
1337 // Device connected or removed
1338 //
1339 if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE)
1340 {
1341 if (PortStatus & EHCI_PRT_CONNECTED)
1342 {
1343 DPRINT1("Device connected on port %d\n", i);
1344
1345 //
1346 //FIXME: Determine device speed
1347 //
1348 if (This->m_Capabilities.HCSParams.CHCCount)
1349 {
1350 if (PortStatus & EHCI_PRT_ENABLED)
1351 {
1352 DPRINT1("Misbeaving controller. Port should be disabled at this point\n");
1353 }
1354
1355 if (PortStatus & EHCI_PRT_SLOWSPEEDLINE)
1356 {
1357 DPRINT1("Non HighSpeed device connected. Release ownership\n");
1358 This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP);
1359 continue;
1360 }
1361 }
1362 }
1363 else
1364 {
1365 DPRINT1("Device disconnected on port %d\n", i);
1366 }
1367
1368 //
1369 // is there a status change callback
1370 //
1371 if (This->m_SCECallBack != NULL)
1372 {
1373 //
1374 // queue work item for processing
1375 //
1376 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1377 }
1378
1379 //
1380 // FIXME: This needs to be saved somewhere
1381 //
1382 }
1383 }
1384 }
1385 return;
1386 }
1387
1388 VOID
1389 NTAPI
1390 StatusChangeWorkItemRoutine(
1391 PVOID Context)
1392 {
1393 //
1394 // cast to hardware object
1395 //
1396 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1397
1398 //
1399 // is there a callback
1400 //
1401 if (This->m_SCECallBack)
1402 {
1403 //
1404 // issue callback
1405 //
1406 This->m_SCECallBack(This->m_SCEContext);
1407 }
1408
1409 }
1410
1411 NTSTATUS
1412 CreateUSBHardware(
1413 PUSBHARDWAREDEVICE *OutHardware)
1414 {
1415 PUSBHARDWAREDEVICE This;
1416
1417 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
1418
1419 if (!This)
1420 return STATUS_INSUFFICIENT_RESOURCES;
1421
1422 This->AddRef();
1423
1424 // return result
1425 *OutHardware = (PUSBHARDWAREDEVICE)This;
1426
1427 return STATUS_SUCCESS;
1428 }