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