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