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