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