[USBOHCI]
[reactos.git] / drivers / usb / usbohci / 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/usbohci/hcd_controller.cpp
5 * PURPOSE: USB OHCI 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 "usbohci.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 OhciDefferedRoutine(
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 GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
63 NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
64 VOID HeadEndpointDescriptorModified(ULONG HeadType);
65
66 NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
67 NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
68
69 NTSTATUS StartController();
70 NTSTATUS StopController();
71 NTSTATUS ResetController();
72 NTSTATUS ResetPort(ULONG PortIndex);
73
74 NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange);
75 NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status);
76 NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature);
77
78 VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context);
79
80 KIRQL AcquireDeviceLock(void);
81 VOID ReleaseDeviceLock(KIRQL OldLevel);
82 // local
83 BOOLEAN InterruptService();
84 NTSTATUS InitializeController();
85 NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor);
86
87 // friend function
88 friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
89 friend VOID NTAPI OhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
90 friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
91 // constructor / destructor
92 CUSBHardwareDevice(IUnknown *OuterUnknown){}
93 virtual ~CUSBHardwareDevice(){}
94
95 protected:
96 LONG m_Ref; // reference count
97 PDRIVER_OBJECT m_DriverObject; // driver object
98 PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo
99 PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller)
100 PDEVICE_OBJECT m_NextDeviceObject; // lower device object
101 KSPIN_LOCK m_Lock; // hardware lock
102 PKINTERRUPT m_Interrupt; // interrupt object
103 KDPC m_IntDpcObject; // dpc object for deferred isr processing
104 PVOID VirtualBase; // virtual base for memory manager
105 PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager
106 PULONG m_Base; // OHCI operational port base registers
107 PDMA_ADAPTER m_Adapter; // dma adapter object
108 ULONG m_MapRegisters; // map registers count
109 USHORT m_VendorID; // vendor id
110 USHORT m_DeviceID; // device id
111 PUSBQUEUE m_UsbQueue; // usb request queue
112 POHCIHCCA m_HCCA; // hcca virtual base
113 PHYSICAL_ADDRESS m_HCCAPhysicalAddress; // hcca physical address
114 POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor; // dummy control endpoint descriptor
115 POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor; // dummy control endpoint descriptor
116 POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor; // iso endpoint descriptor
117 POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints[OHCI_STATIC_ENDPOINT_COUNT]; // endpoints for interrupt / iso transfers
118 ULONG m_NumberOfPorts; // number of ports
119 OHCI_PORT_STATUS m_PortStatus[OHCI_MAX_PORT_COUNT]; // port change status
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 };
127
128 //=================================================================================================
129 // COM
130 //
131 NTSTATUS
132 STDMETHODCALLTYPE
133 CUSBHardwareDevice::QueryInterface(
134 IN REFIID refiid,
135 OUT PVOID* Output)
136 {
137 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
138 {
139 *Output = PVOID(PUNKNOWN(this));
140 PUNKNOWN(*Output)->AddRef();
141 return STATUS_SUCCESS;
142 }
143
144 return STATUS_UNSUCCESSFUL;
145 }
146
147 NTSTATUS
148 CUSBHardwareDevice::Initialize(
149 PDRIVER_OBJECT DriverObject,
150 PDEVICE_OBJECT FunctionalDeviceObject,
151 PDEVICE_OBJECT PhysicalDeviceObject,
152 PDEVICE_OBJECT LowerDeviceObject)
153 {
154 BUS_INTERFACE_STANDARD BusInterface;
155 PCI_COMMON_CONFIG PciConfig;
156 NTSTATUS Status;
157 ULONG BytesRead;
158
159 DPRINT1("CUSBHardwareDevice::Initialize\n");
160
161 //
162 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
163 //
164 Status = CreateDMAMemoryManager(&m_MemoryManager);
165 if (!NT_SUCCESS(Status))
166 {
167 DPRINT1("Failed to create DMAMemoryManager Object\n");
168 return Status;
169 }
170
171 //
172 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
173 //
174 Status = CreateUSBQueue(&m_UsbQueue);
175 if (!NT_SUCCESS(Status))
176 {
177 DPRINT1("Failed to create UsbQueue!\n");
178 return Status;
179 }
180
181 //
182 // store device objects
183 //
184 m_DriverObject = DriverObject;
185 m_FunctionalDeviceObject = FunctionalDeviceObject;
186 m_PhysicalDeviceObject = PhysicalDeviceObject;
187 m_NextDeviceObject = LowerDeviceObject;
188
189 //
190 // initialize device lock
191 //
192 KeInitializeSpinLock(&m_Lock);
193
194 //
195 // intialize status change work item
196 //
197 ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
198
199 m_VendorID = 0;
200 m_DeviceID = 0;
201
202 Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
203 if (!NT_SUCCESS(Status))
204 {
205 DPRINT1("Failed to get BusInteface!\n");
206 return Status;
207 }
208
209 BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
210 PCI_WHICHSPACE_CONFIG,
211 &PciConfig,
212 0,
213 PCI_COMMON_HDR_LENGTH);
214
215 if (BytesRead != PCI_COMMON_HDR_LENGTH)
216 {
217 DPRINT1("Failed to get pci config information!\n");
218 return STATUS_SUCCESS;
219 }
220
221 if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER))
222 {
223 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
224 }
225
226 m_VendorID = PciConfig.VendorID;
227 m_DeviceID = PciConfig.DeviceID;
228
229 return STATUS_SUCCESS;
230 }
231
232 NTSTATUS
233 CUSBHardwareDevice::PnpStart(
234 PCM_RESOURCE_LIST RawResources,
235 PCM_RESOURCE_LIST TranslatedResources)
236 {
237 ULONG Index;
238 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
239 DEVICE_DESCRIPTION DeviceDescription;
240 PVOID ResourceBase;
241 NTSTATUS Status;
242 ULONG Version;
243
244 DPRINT1("CUSBHardwareDevice::PnpStart\n");
245 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
246 {
247 //
248 // get resource descriptor
249 //
250 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
251
252 switch(ResourceDescriptor->Type)
253 {
254 case CmResourceTypeInterrupt:
255 {
256 KeInitializeDpc(&m_IntDpcObject,
257 OhciDefferedRoutine,
258 this);
259
260 Status = IoConnectInterrupt(&m_Interrupt,
261 InterruptServiceRoutine,
262 (PVOID)this,
263 NULL,
264 ResourceDescriptor->u.Interrupt.Vector,
265 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
266 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
267 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
268 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
269 ResourceDescriptor->u.Interrupt.Affinity,
270 FALSE);
271
272 if (!NT_SUCCESS(Status))
273 {
274 //
275 // failed to register interrupt
276 //
277 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
278 return Status;
279 }
280 break;
281 }
282 case CmResourceTypeMemory:
283 {
284 //
285 // get resource base
286 //
287 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
288 if (!ResourceBase)
289 {
290 //
291 // failed to map registers
292 //
293 DPRINT1("MmMapIoSpace failed\n");
294 return STATUS_INSUFFICIENT_RESOURCES;
295 }
296
297 //
298 // Get controllers capabilities
299 //
300 Version = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + OHCI_REVISION_OFFSET));
301
302 DPRINT1("Version %x\n", Version);
303
304 //
305 // Store Resource base
306 //
307 m_Base = (PULONG)ResourceBase;
308 break;
309 }
310 }
311 }
312
313
314 //
315 // zero device description
316 //
317 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
318
319 //
320 // initialize device description
321 //
322 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
323 DeviceDescription.Master = TRUE;
324 DeviceDescription.ScatterGather = TRUE;
325 DeviceDescription.Dma32BitAddresses = TRUE;
326 DeviceDescription.DmaWidth = Width32Bits;
327 DeviceDescription.InterfaceType = PCIBus;
328 DeviceDescription.MaximumLength = MAXULONG;
329
330 //
331 // get dma adapter
332 //
333 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
334 if (!m_Adapter)
335 {
336 //
337 // failed to get dma adapter
338 //
339 DPRINT1("Failed to acquire dma adapter\n");
340 return STATUS_INSUFFICIENT_RESOURCES;
341 }
342
343 //
344 // Create Common Buffer
345 //
346 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
347 PAGE_SIZE * 4,
348 &PhysicalAddress,
349 FALSE);
350 if (!VirtualBase)
351 {
352 DPRINT1("Failed to allocate a common buffer\n");
353 return STATUS_INSUFFICIENT_RESOURCES;
354 }
355
356 //
357 // Initialize the DMAMemoryManager
358 //
359 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
360 if (!NT_SUCCESS(Status))
361 {
362 DPRINT1("Failed to initialize the DMAMemoryManager\n");
363 return Status;
364 }
365
366 //
367 // initializes the controller
368 //
369 Status = InitializeController();
370 if (!NT_SUCCESS(Status))
371 {
372 DPRINT1("Failed to Initialize the controller \n");
373 ASSERT(FALSE);
374 return Status;
375 }
376
377 //
378 // Initialize the UsbQueue now that we have an AdapterObject.
379 //
380 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
381 if (!NT_SUCCESS(Status))
382 {
383 DPRINT1("Failed to Initialize the UsbQueue\n");
384 return Status;
385 }
386
387
388 //
389 // Stop the controller before modifying schedules
390 //
391 Status = StopController();
392 if (!NT_SUCCESS(Status))
393 {
394 DPRINT1("Failed to stop the controller \n");
395 ASSERT(FALSE);
396 return Status;
397 }
398
399
400 //
401 // Start the controller
402 //
403 DPRINT1("Starting Controller\n");
404 Status = StartController();
405
406 //
407 // done
408 //
409 return Status;
410 }
411
412 NTSTATUS
413 CUSBHardwareDevice::PnpStop(void)
414 {
415 UNIMPLEMENTED
416 return STATUS_NOT_IMPLEMENTED;
417 }
418
419 NTSTATUS
420 CUSBHardwareDevice::HandlePower(
421 PIRP Irp)
422 {
423 UNIMPLEMENTED
424 return STATUS_NOT_IMPLEMENTED;
425 }
426
427 NTSTATUS
428 CUSBHardwareDevice::GetDeviceDetails(
429 OUT OPTIONAL PUSHORT VendorId,
430 OUT OPTIONAL PUSHORT DeviceId,
431 OUT OPTIONAL PULONG NumberOfPorts,
432 OUT OPTIONAL PULONG Speed)
433 {
434 if (VendorId)
435 {
436 //
437 // get vendor
438 //
439 *VendorId = m_VendorID;
440 }
441
442 if (DeviceId)
443 {
444 //
445 // get device id
446 //
447 *DeviceId = m_DeviceID;
448 }
449
450 if (NumberOfPorts)
451 {
452 //
453 // get number of ports
454 //
455 *NumberOfPorts = m_NumberOfPorts;
456 }
457
458 if (Speed)
459 {
460 //
461 // speed is 0x100
462 //
463 *Speed = 0x100;
464 }
465
466 return STATUS_SUCCESS;
467 }
468
469 NTSTATUS CUSBHardwareDevice::GetDMA(
470 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
471 {
472 if (!m_MemoryManager)
473 return STATUS_UNSUCCESSFUL;
474 *OutDMAMemoryManager = m_MemoryManager;
475 return STATUS_SUCCESS;
476 }
477
478 NTSTATUS
479 CUSBHardwareDevice::GetUSBQueue(
480 OUT struct IUSBQueue **OutUsbQueue)
481 {
482 if (!m_UsbQueue)
483 return STATUS_UNSUCCESSFUL;
484 *OutUsbQueue = m_UsbQueue;
485 return STATUS_SUCCESS;
486 }
487
488
489 NTSTATUS
490 CUSBHardwareDevice::StartController(void)
491 {
492 ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic, IntervalValue;
493
494 //
495 // first write address of HCCA
496 //
497 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
498
499 //
500 // lets write physical address of dummy control endpoint descriptor
501 //
502 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart);
503
504 //
505 // lets write physical address of dummy bulk endpoint descriptor
506 //
507 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart);
508
509 //
510 // read control register
511 //
512 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
513
514 //
515 // remove flags
516 //
517 Control &= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK | OHCI_ENABLE_LIST | OHCI_HC_FUNCTIONAL_STATE_MASK | OHCI_INTERRUPT_ROUTING);
518
519 //
520 // set command status flags
521 //
522 Control |= OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL;
523
524 //
525 // now start the controller
526 //
527 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), Control);
528
529 //
530 // wait a bit
531 //
532 KeStallExecutionProcessor(100);
533
534 //
535 // is the controller started
536 //
537 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
538
539 //
540 // assert that the controller has been started
541 //
542 ASSERT((Control & OHCI_HC_FUNCTIONAL_STATE_MASK) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
543 ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
544
545 //
546 // get frame interval
547 //
548 //FrameInterval = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE;
549 //FrameInterval |= OHCI_FSMPS(IntervalValue) | IntervalValue;
550
551 //
552 // write frame interval
553 //
554 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
555 // 90% periodic
556 //Periodic = OHCI_PERIODIC(intervalValue);
557 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + 0x40 /*OHCI_PERIODIC_START_OFFSET*/), 0x3E67);
558
559
560 //
561 // read descriptor
562 //
563 Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
564
565 //
566 // no over current protection
567 //
568 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor | OHCI_RH_NO_OVER_CURRENT_PROTECTION);
569
570 //
571 // enable power on all ports
572 //
573 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_STATUS_OFFSET), OHCI_RH_LOCAL_POWER_STATUS_CHANGE);
574
575 //
576 // wait a bit
577 //
578 KeStallExecutionProcessor(10);
579
580 //
581 // write descriptor
582 //
583 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor);
584
585
586
587 //
588 // retrieve number of ports
589 //
590 for(Index = 0; Index < 10; Index++)
591 {
592 //
593 // wait a bit
594 //
595 KeStallExecutionProcessor(10);
596
597 //
598 // read descriptor
599 //
600 Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
601
602 //
603 // get number of ports
604 //
605 NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor);
606
607 //
608 // check if we have received the ports
609 //
610 if (NumberOfPorts)
611 break;
612 }
613
614 //
615 // sanity check
616 //
617 ASSERT(NumberOfPorts < OHCI_MAX_PORT_COUNT);
618
619 //
620 // store number of ports
621 //
622 m_NumberOfPorts = NumberOfPorts;
623
624 //
625 // print out number ports
626 //
627 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts);
628
629
630 //
631 // now enable the interrupts
632 //
633 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE);
634
635 //
636 // done
637 //
638 return STATUS_SUCCESS;
639 }
640
641 NTSTATUS
642 CUSBHardwareDevice::AllocateEndpointDescriptor(
643 OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor)
644 {
645 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
646 PHYSICAL_ADDRESS DescriptorAddress;
647 NTSTATUS Status;
648
649 //
650 // allocate descriptor
651 //
652 Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress);
653 if (!NT_SUCCESS(Status))
654 {
655 //
656 // failed to allocate descriptor
657 //
658 return Status;
659 }
660
661 //
662 // intialize descriptor
663 //
664 Descriptor->Flags = OHCI_ENDPOINT_SKIP;
665 Descriptor->HeadPhysicalDescriptor = 0;
666 Descriptor->NextPhysicalEndpoint = 0;
667 Descriptor->TailPhysicalDescriptor = 0;
668 Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
669
670 //
671 // store result
672 //
673 *OutDescriptor = Descriptor;
674
675 //
676 // done
677 //
678 return STATUS_SUCCESS;
679 }
680
681 NTSTATUS
682 CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
683 struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
684 {
685 *OutDescriptor = m_BulkEndpointDescriptor;
686 return STATUS_SUCCESS;
687 }
688
689 VOID
690 CUSBHardwareDevice::HeadEndpointDescriptorModified(
691 ULONG Type)
692 {
693 ULONG Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
694
695 if (Type == USB_ENDPOINT_TYPE_CONTROL)
696 {
697 //
698 // notify controller
699 //
700 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->NextPhysicalEndpoint);
701 //WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_CURRENT_ED_OFFSET), m_ControlEndpointDescriptor->NextPhysicalEndpoint);
702 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_CONTROL_LIST_FILLED);
703 }
704 else if (Type == USB_ENDPOINT_TYPE_BULK)
705 {
706 //
707 // notify controller
708 //
709 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_BULK_LIST_FILLED);
710 }
711
712 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
713
714
715 DPRINT1("HeadEndpointDescriptorModified Value %x Type %x\n", Value, Type);
716
717 }
718
719 NTSTATUS
720 CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
721 struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
722 {
723 *OutDescriptor = m_ControlEndpointDescriptor;
724 return STATUS_SUCCESS;
725 }
726
727 NTSTATUS
728 CUSBHardwareDevice::InitializeController()
729 {
730 NTSTATUS Status;
731 ULONG Index, Interval, IntervalIndex, InsertIndex;
732 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
733
734 //
735 // first allocate the hcca area
736 //
737 Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress);
738 if (!NT_SUCCESS(Status))
739 {
740 //
741 // no memory
742 //
743 return Status;
744 }
745
746 //
747 // now allocate an endpoint for control transfers
748 // this endpoint will never be removed
749 //
750 Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor);
751 if (!NT_SUCCESS(Status))
752 {
753 //
754 // no memory
755 //
756 return Status;
757 }
758
759 //
760 // now allocate an endpoint for bulk transfers
761 // this endpoint will never be removed
762 //
763 Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor);
764 if (!NT_SUCCESS(Status))
765 {
766 //
767 // no memory
768 //
769 return Status;
770 }
771
772 //
773 // now allocate an endpoint for iso transfers
774 // this endpoint will never be removed
775 //
776 Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor);
777 if (!NT_SUCCESS(Status))
778 {
779 //
780 // no memory
781 //
782 return Status;
783 }
784
785 //
786 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
787 //
788 for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
789 {
790 //
791 // allocate endpoint descriptor
792 //
793 Status = AllocateEndpointDescriptor(&Descriptor);
794 if (!NT_SUCCESS(Status))
795 {
796 //
797 // no memory
798 //
799 return Status;
800 }
801
802 //
803 // save in array
804 //
805 m_InterruptEndpoints[Index] = Descriptor;
806 }
807
808
809 //
810 // now link the descriptors, taken from Haiku
811 //
812 Interval = OHCI_BIGGEST_INTERVAL;
813 IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1;
814 while (Interval > 1)
815 {
816 InsertIndex = Interval / 2;
817 while (InsertIndex < OHCI_BIGGEST_INTERVAL)
818 {
819 //
820 // assign endpoint address
821 //
822 m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart;
823 InsertIndex += Interval;
824 }
825
826 IntervalIndex--;
827 Interval /= 2;
828 }
829
830 //
831 // link all endpoint descriptors to first descriptor in array
832 //
833 m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
834 for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
835 {
836 //
837 // link descriptor
838 //
839 m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
840 }
841
842 //
843 // Now link the first endpoint to the isochronous endpoint
844 //
845 m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
846
847 //
848 // done
849 //
850 return STATUS_SUCCESS;
851 }
852
853 NTSTATUS
854 CUSBHardwareDevice::StopController(void)
855 {
856 ULONG Control, Reset;
857 ULONG Index;
858
859 //
860 // first turn off all interrupts
861 //
862 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ALL_INTERRUPTS);
863
864 //
865 // check context
866 //
867 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
868
869 //
870 // FIXME: support routing
871 //
872 ASSERT((Control & OHCI_INTERRUPT_ROUTING) == 0);
873
874 //
875 // have a break
876 //
877 KeStallExecutionProcessor(100);
878
879 //
880 // some controllers also depend on this
881 //
882 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
883
884 //
885 // wait a bit
886 //
887 KeStallExecutionProcessor(100);
888
889 //
890 // now reset controller
891 //
892 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET);
893
894 //
895 // reset time is 10ms
896 //
897 for(Index = 0; Index < 10; Index++)
898 {
899 //
900 // wait a bit
901 //
902 KeStallExecutionProcessor(10);
903
904 //
905 // read command status
906 //
907 Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
908
909 //
910 // was reset bit cleared
911 //
912 if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0)
913 {
914 //
915 // controller completed reset
916 //
917 return STATUS_SUCCESS;
918 }
919 }
920
921 //
922 // failed to reset controller
923 //
924 return STATUS_UNSUCCESSFUL;
925 }
926
927 NTSTATUS
928 CUSBHardwareDevice::ResetController(void)
929 {
930 UNIMPLEMENTED
931 return STATUS_NOT_IMPLEMENTED;
932 }
933
934 NTSTATUS
935 CUSBHardwareDevice::ResetPort(
936 IN ULONG PortIndex)
937 {
938 ASSERT(FALSE);
939
940 return STATUS_SUCCESS;
941 }
942
943 NTSTATUS
944 CUSBHardwareDevice::GetPortStatus(
945 ULONG PortId,
946 OUT USHORT *PortStatus,
947 OUT USHORT *PortChange)
948 {
949 //
950 // FIXME: should read status from hardware
951 //
952 *PortStatus = m_PortStatus[PortId].PortStatus;
953 *PortChange = m_PortStatus[PortId].PortChange;
954 return STATUS_SUCCESS;
955 }
956
957 NTSTATUS
958 CUSBHardwareDevice::ClearPortStatus(
959 ULONG PortId,
960 ULONG Status)
961 {
962 ULONG Value, Index = 0;
963
964 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
965
966 if (PortId > m_NumberOfPorts)
967 return STATUS_UNSUCCESSFUL;
968
969 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
970 KeStallExecutionProcessor(100);
971
972 if (Status == C_PORT_RESET)
973 {
974 do
975 {
976 //
977 // read port status
978 //
979 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
980
981 if ((Value & OHCI_RH_PORTSTATUS_PRS) == 0)
982 {
983 //
984 // reset is complete
985 //
986 break;
987 }
988
989 //
990 // wait a bit
991 //
992 KeStallExecutionProcessor(100);
993
994 //DPRINT1("Value %x Index %lu\n", Value, Index);
995
996 }while(TRUE);
997
998 //
999 // check if reset bit is still set
1000 //
1001 if (Value & OHCI_RH_PORTSTATUS_PRS)
1002 {
1003 //
1004 // reset failed
1005 //
1006 DPRINT1("PortId %lu Reset failed\n", PortId);
1007 return STATUS_UNSUCCESSFUL;
1008 }
1009
1010 //
1011 // sanity checks
1012 //
1013 ASSERT((Value & OHCI_RH_PORTSTATUS_PRS) == 0);
1014 ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC));
1015
1016 //
1017 // clear reset bit complete
1018 //
1019 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRSC);
1020
1021 //
1022 // read status register
1023 //
1024 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1025
1026 //
1027 // reset complete bit should be cleared
1028 //
1029 ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC) == 0);
1030
1031 //
1032 // update port status
1033 //
1034 m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_RESET;
1035
1036 //
1037 // sanity check
1038 //
1039 ASSERT((Value & OHCI_RH_PORTSTATUS_PES));
1040
1041 //
1042 // port is enabled
1043 //
1044 m_PortStatus[PortId].PortStatus |= USB_PORT_STATUS_ENABLE;
1045
1046 //
1047 // re-enable root hub change
1048 //
1049 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
1050 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), Value | OHCI_ROOT_HUB_STATUS_CHANGE);
1051
1052 }
1053
1054 if (Status == C_PORT_CONNECTION)
1055 {
1056 //
1057 // clear bit
1058 //
1059 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC);
1060 m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_CONNECT;
1061 }
1062
1063
1064
1065 return STATUS_SUCCESS;
1066 }
1067
1068
1069 NTSTATUS
1070 CUSBHardwareDevice::SetPortFeature(
1071 ULONG PortId,
1072 ULONG Feature)
1073 {
1074 ULONG Value;
1075
1076 DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId, Feature);
1077
1078 //
1079 // read port status
1080 //
1081 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1082
1083
1084 if (Feature == PORT_ENABLE)
1085 {
1086 //
1087 // enable port
1088 //
1089 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PES);
1090 return STATUS_SUCCESS;
1091 }
1092 else if (Feature == PORT_POWER)
1093 {
1094 //
1095 // enable power
1096 //
1097 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS);
1098 return STATUS_SUCCESS;
1099 }
1100 else if (Feature == PORT_SUSPEND)
1101 {
1102 //
1103 // enable port
1104 //
1105 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PSS);
1106 return STATUS_SUCCESS;
1107 }
1108 else if (Feature == PORT_RESET)
1109 {
1110 //
1111 // assert
1112 //
1113 ASSERT((Value & OHCI_RH_PORTSTATUS_CCS));
1114
1115 //
1116 // reset port
1117 //
1118 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
1119
1120 //
1121 // wait
1122 //
1123 KeStallExecutionProcessor(100);
1124
1125 //
1126 // update cached settings
1127 //
1128 m_PortStatus[PortId].PortChange |= USB_PORT_STATUS_RESET;
1129 m_PortStatus[PortId].PortStatus &= ~USB_PORT_STATUS_ENABLE;
1130
1131 //
1132 // is there a status change callback
1133 //
1134 if (m_SCECallBack != NULL)
1135 {
1136 //
1137 // issue callback
1138 //
1139 m_SCECallBack(m_SCEContext);
1140 }
1141 }
1142 return STATUS_SUCCESS;
1143 }
1144
1145
1146
1147 VOID
1148 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1149 PVOID CallBack,
1150 PVOID Context)
1151 {
1152 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1153 m_SCEContext = Context;
1154 }
1155
1156 KIRQL
1157 CUSBHardwareDevice::AcquireDeviceLock(void)
1158 {
1159 KIRQL OldLevel;
1160
1161 //
1162 // acquire lock
1163 //
1164 KeAcquireSpinLock(&m_Lock, &OldLevel);
1165
1166 //
1167 // return old irql
1168 //
1169 return OldLevel;
1170 }
1171
1172
1173 VOID
1174 CUSBHardwareDevice::ReleaseDeviceLock(
1175 KIRQL OldLevel)
1176 {
1177 KeReleaseSpinLock(&m_Lock, OldLevel);
1178 }
1179
1180 BOOLEAN
1181 NTAPI
1182 InterruptServiceRoutine(
1183 IN PKINTERRUPT Interrupt,
1184 IN PVOID ServiceContext)
1185 {
1186 CUSBHardwareDevice *This;
1187 ULONG DoneHead, Status, Acknowledge = 0;
1188
1189 //
1190 // get context
1191 //
1192 This = (CUSBHardwareDevice*) ServiceContext;
1193
1194 DPRINT1("InterruptServiceRoutine\n");
1195
1196 //
1197 // get done head
1198 //
1199 DoneHead = This->m_HCCA->DoneHead;
1200
1201 //
1202 // check if zero
1203 //
1204 if (DoneHead == 0)
1205 {
1206 //
1207 // the interrupt was not caused by DoneHead update
1208 // check if something important happened
1209 //
1210 Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET)) & (~OHCI_WRITEBACK_DONE_HEAD);
1211 if (Status == 0)
1212 {
1213 //
1214 // nothing happened, appears to be shared interrupt
1215 //
1216 return FALSE;
1217 }
1218 }
1219 else
1220 {
1221 //
1222 // DoneHead update happened, check if there are other events too
1223 //
1224 Status = OHCI_WRITEBACK_DONE_HEAD;
1225
1226 //
1227 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1228 //
1229 if (DoneHead & OHCI_DONE_INTERRUPTS)
1230 {
1231 //
1232 // get other events
1233 //
1234 Status |= READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
1235 }
1236 }
1237
1238 //
1239 // sanity check
1240 //
1241 ASSERT(Status != 0);
1242
1243 if (Status & OHCI_WRITEBACK_DONE_HEAD)
1244 {
1245 //
1246 // head completed
1247 //
1248 Acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
1249 }
1250
1251 if (Status & OHCI_RESUME_DETECTED)
1252 {
1253 //
1254 // resume
1255 //
1256 DPRINT1("InterruptServiceRoutine> Resume\n");
1257 Acknowledge |= OHCI_RESUME_DETECTED;
1258 }
1259
1260
1261 if (Status & OHCI_UNRECOVERABLE_ERROR)
1262 {
1263 DPRINT1("InterruptServiceRoutine> Controller error\n");
1264
1265 //
1266 // halt controller
1267 //
1268 ASSERT(FALSE);
1269 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
1270 }
1271
1272 if (Status & OHCI_ROOT_HUB_STATUS_CHANGE)
1273 {
1274 //
1275 // new device has arrived
1276 //
1277
1278 //
1279 // disable interrupt as it will fire untill the port has been reset
1280 //
1281 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
1282 Acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE;
1283 }
1284
1285 //
1286 // is there something to acknowledge
1287 //
1288 if (Acknowledge)
1289 {
1290 //
1291 // ack change
1292 //
1293 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET), Acknowledge);
1294 }
1295
1296 //
1297 // defer processing
1298 //
1299 DPRINT1("Status %x\n", Status);
1300 KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)Status);
1301
1302 //
1303 // interrupt handled
1304 //
1305 return TRUE;
1306 }
1307
1308 VOID
1309 NTAPI
1310 OhciDefferedRoutine(
1311 IN PKDPC Dpc,
1312 IN PVOID DeferredContext,
1313 IN PVOID SystemArgument1,
1314 IN PVOID SystemArgument2)
1315 {
1316 CUSBHardwareDevice *This;
1317 ULONG CStatus, Index, PortStatus;
1318 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
1319 ULONG DoneHead;
1320
1321 //
1322 // get parameters
1323 //
1324 This = (CUSBHardwareDevice*) SystemArgument1;
1325 CStatus = (ULONG) SystemArgument2;
1326
1327 DPRINT1("OhciDefferedRoutine Status %x\n", CStatus);
1328
1329 if (CStatus & OHCI_WRITEBACK_DONE_HEAD)
1330 {
1331 //
1332 // descriptor completion, get done head
1333 //
1334 DoneHead = This->m_HCCA->DoneHead;
1335
1336 //
1337 // clear out lower bits, ed are 16 byte aligned
1338 //
1339 DoneHead &= ~0xF;
1340
1341 //
1342 // notify queue of event
1343 //
1344 This->m_UsbQueue->TransferDescriptorCompletionCallback(DoneHead);
1345 }
1346 if (CStatus & OHCI_ROOT_HUB_STATUS_CHANGE)
1347 {
1348 //
1349 // device connected, lets check which port
1350 //
1351 for(Index = 0; Index < This->m_NumberOfPorts; Index++)
1352 {
1353 //
1354 // read port status
1355 //
1356 PortStatus = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)));
1357
1358 //
1359 // check if there is a status change
1360 //
1361 if (PortStatus & OHCI_RH_PORTSTATUS_CSC)
1362 {
1363 //
1364 // did a device connect
1365 //
1366 if (PortStatus & OHCI_RH_PORTSTATUS_CCS)
1367 {
1368 //
1369 // device connected
1370 //
1371 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index, (PortStatus & OHCI_RH_PORTSTATUS_LSDA));
1372
1373 //
1374 // enable port
1375 //
1376 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)), OHCI_RH_PORTSTATUS_PES);
1377
1378
1379 //
1380 // store change
1381 //
1382 This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_CONNECT;
1383 This->m_PortStatus[Index].PortChange |= USB_PORT_STATUS_CONNECT;
1384
1385 if ((PortStatus & OHCI_RH_PORTSTATUS_LSDA))
1386 {
1387 //
1388 // low speed device connected
1389 //
1390 This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_LOW_SPEED;
1391 }
1392
1393 //
1394 // is there a status change callback
1395 //
1396 if (This->m_SCECallBack != NULL)
1397 {
1398 //
1399 // queue work item for processing
1400 //
1401 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1402 }
1403 }
1404 else
1405 {
1406 //
1407 // device disconnected
1408 //
1409 DPRINT1("Device disconnected at Port %x\n", Index);
1410 }
1411 }
1412 }
1413 }
1414
1415
1416 }
1417
1418 VOID
1419 NTAPI
1420 StatusChangeWorkItemRoutine(
1421 PVOID Context)
1422 {
1423 //
1424 // cast to hardware object
1425 //
1426 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1427
1428 //
1429 // is there a callback
1430 //
1431 if (This->m_SCECallBack)
1432 {
1433 //
1434 // issue callback
1435 //
1436 This->m_SCECallBack(This->m_SCEContext);
1437 }
1438
1439 }
1440
1441 NTSTATUS
1442 CreateUSBHardware(
1443 PUSBHARDWAREDEVICE *OutHardware)
1444 {
1445 PUSBHARDWAREDEVICE This;
1446
1447 This = new(NonPagedPool, TAG_USBOHCI) CUSBHardwareDevice(0);
1448
1449 if (!This)
1450 return STATUS_INSUFFICIENT_RESOURCES;
1451
1452 This->AddRef();
1453
1454 // return result
1455 *OutHardware = (PUSBHARDWAREDEVICE)This;
1456
1457 return STATUS_SUCCESS;
1458 }