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