[USBOHCI]
[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 EhciDefferedRoutine(
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 GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
63 NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
64
65 NTSTATUS StartController();
66 NTSTATUS StopController();
67 NTSTATUS ResetController();
68 NTSTATUS ResetPort(ULONG PortIndex);
69
70 NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange);
71 NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status);
72 NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature);
73
74 VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context);
75
76 KIRQL AcquireDeviceLock(void);
77 VOID ReleaseDeviceLock(KIRQL OldLevel);
78 // local
79 BOOLEAN InterruptService();
80 NTSTATUS InitializeController();
81 NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor);
82
83 // friend function
84 friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
85 friend VOID NTAPI EhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
86 friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
87 // constructor / destructor
88 CUSBHardwareDevice(IUnknown *OuterUnknown){}
89 virtual ~CUSBHardwareDevice(){}
90
91 protected:
92 LONG m_Ref; // reference count
93 PDRIVER_OBJECT m_DriverObject; // driver object
94 PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo
95 PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller)
96 PDEVICE_OBJECT m_NextDeviceObject; // lower device object
97 KSPIN_LOCK m_Lock; // hardware lock
98 PKINTERRUPT m_Interrupt; // interrupt object
99 KDPC m_IntDpcObject; // dpc object for deferred isr processing
100 PVOID VirtualBase; // virtual base for memory manager
101 PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager
102 PULONG m_Base; // OHCI operational port base registers
103 PDMA_ADAPTER m_Adapter; // dma adapter object
104 ULONG m_MapRegisters; // map registers count
105 USHORT m_VendorID; // vendor id
106 USHORT m_DeviceID; // device id
107 PUSBQUEUE m_UsbQueue; // usb request queue
108 POHCIHCCA m_HCCA; // hcca virtual base
109 PHYSICAL_ADDRESS m_HCCAPhysicalAddress; // hcca physical address
110 POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor; // dummy control endpoint descriptor
111 POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor; // dummy control endpoint descriptor
112 POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor; // iso endpoint descriptor
113 POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints[OHCI_STATIC_ENDPOINT_COUNT]; // endpoints for interrupt / iso transfers
114 ULONG m_NumberOfPorts; // number of ports
115 PDMAMEMORYMANAGER m_MemoryManager; // memory manager
116 HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine
117 PVOID m_SCEContext; // status change callback routine context
118 BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress
119 WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
120 ULONG m_SyncFramePhysAddr; // periodic frame list physical address
121 };
122
123 //=================================================================================================
124 // COM
125 //
126 NTSTATUS
127 STDMETHODCALLTYPE
128 CUSBHardwareDevice::QueryInterface(
129 IN REFIID refiid,
130 OUT PVOID* Output)
131 {
132 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
133 {
134 *Output = PVOID(PUNKNOWN(this));
135 PUNKNOWN(*Output)->AddRef();
136 return STATUS_SUCCESS;
137 }
138
139 return STATUS_UNSUCCESSFUL;
140 }
141
142 NTSTATUS
143 CUSBHardwareDevice::Initialize(
144 PDRIVER_OBJECT DriverObject,
145 PDEVICE_OBJECT FunctionalDeviceObject,
146 PDEVICE_OBJECT PhysicalDeviceObject,
147 PDEVICE_OBJECT LowerDeviceObject)
148 {
149 BUS_INTERFACE_STANDARD BusInterface;
150 PCI_COMMON_CONFIG PciConfig;
151 NTSTATUS Status;
152 ULONG BytesRead;
153
154 DPRINT1("CUSBHardwareDevice::Initialize\n");
155
156 //
157 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
158 //
159 Status = CreateDMAMemoryManager(&m_MemoryManager);
160 if (!NT_SUCCESS(Status))
161 {
162 DPRINT1("Failed to create DMAMemoryManager Object\n");
163 return Status;
164 }
165
166 //
167 // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
168 //
169 Status = CreateUSBQueue(&m_UsbQueue);
170 if (!NT_SUCCESS(Status))
171 {
172 DPRINT1("Failed to create UsbQueue!\n");
173 return Status;
174 }
175
176 //
177 // store device objects
178 //
179 m_DriverObject = DriverObject;
180 m_FunctionalDeviceObject = FunctionalDeviceObject;
181 m_PhysicalDeviceObject = PhysicalDeviceObject;
182 m_NextDeviceObject = LowerDeviceObject;
183
184 //
185 // initialize device lock
186 //
187 KeInitializeSpinLock(&m_Lock);
188
189 //
190 // intialize status change work item
191 //
192 ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
193
194 m_VendorID = 0;
195 m_DeviceID = 0;
196
197 Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
198 if (!NT_SUCCESS(Status))
199 {
200 DPRINT1("Failed to get BusInteface!\n");
201 return Status;
202 }
203
204 BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
205 PCI_WHICHSPACE_CONFIG,
206 &PciConfig,
207 0,
208 PCI_COMMON_HDR_LENGTH);
209
210 if (BytesRead != PCI_COMMON_HDR_LENGTH)
211 {
212 DPRINT1("Failed to get pci config information!\n");
213 return STATUS_SUCCESS;
214 }
215
216 if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER))
217 {
218 DPRINT1("PCI Configuration shows this as a non Bus Mastering device!\n");
219 }
220
221 m_VendorID = PciConfig.VendorID;
222 m_DeviceID = PciConfig.DeviceID;
223
224 return STATUS_SUCCESS;
225 }
226
227 NTSTATUS
228 CUSBHardwareDevice::PnpStart(
229 PCM_RESOURCE_LIST RawResources,
230 PCM_RESOURCE_LIST TranslatedResources)
231 {
232 ULONG Index;
233 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
234 DEVICE_DESCRIPTION DeviceDescription;
235 PVOID ResourceBase;
236 NTSTATUS Status;
237 ULONG Version;
238
239 DPRINT1("CUSBHardwareDevice::PnpStart\n");
240 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
241 {
242 //
243 // get resource descriptor
244 //
245 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
246
247 switch(ResourceDescriptor->Type)
248 {
249 case CmResourceTypeInterrupt:
250 {
251 KeInitializeDpc(&m_IntDpcObject,
252 EhciDefferedRoutine,
253 this);
254
255 Status = IoConnectInterrupt(&m_Interrupt,
256 InterruptServiceRoutine,
257 (PVOID)this,
258 NULL,
259 ResourceDescriptor->u.Interrupt.Vector,
260 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
261 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
262 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
263 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
264 ResourceDescriptor->u.Interrupt.Affinity,
265 FALSE);
266
267 if (!NT_SUCCESS(Status))
268 {
269 //
270 // failed to register interrupt
271 //
272 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
273 return Status;
274 }
275 break;
276 }
277 case CmResourceTypeMemory:
278 {
279 //
280 // get resource base
281 //
282 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
283 if (!ResourceBase)
284 {
285 //
286 // failed to map registers
287 //
288 DPRINT1("MmMapIoSpace failed\n");
289 return STATUS_INSUFFICIENT_RESOURCES;
290 }
291
292 //
293 // Get controllers capabilities
294 //
295 Version = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + OHCI_REVISION_OFFSET));
296
297 DPRINT1("Version %x\n", Version);
298
299 //
300 // Store Resource base
301 //
302 m_Base = (PULONG)ResourceBase;
303 break;
304 }
305 }
306 }
307
308
309 //
310 // zero device description
311 //
312 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
313
314 //
315 // initialize device description
316 //
317 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
318 DeviceDescription.Master = TRUE;
319 DeviceDescription.ScatterGather = TRUE;
320 DeviceDescription.Dma32BitAddresses = TRUE;
321 DeviceDescription.DmaWidth = Width32Bits;
322 DeviceDescription.InterfaceType = PCIBus;
323 DeviceDescription.MaximumLength = MAXULONG;
324
325 //
326 // get dma adapter
327 //
328 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
329 if (!m_Adapter)
330 {
331 //
332 // failed to get dma adapter
333 //
334 DPRINT1("Failed to acquire dma adapter\n");
335 return STATUS_INSUFFICIENT_RESOURCES;
336 }
337
338 //
339 // Create Common Buffer
340 //
341 VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
342 PAGE_SIZE * 4,
343 &PhysicalAddress,
344 FALSE);
345 if (!VirtualBase)
346 {
347 DPRINT1("Failed to allocate a common buffer\n");
348 return STATUS_INSUFFICIENT_RESOURCES;
349 }
350
351 //
352 // Initialize the DMAMemoryManager
353 //
354 Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
355 if (!NT_SUCCESS(Status))
356 {
357 DPRINT1("Failed to initialize the DMAMemoryManager\n");
358 return Status;
359 }
360
361 //
362 // Initialize the UsbQueue now that we have an AdapterObject.
363 //
364 Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
365 if (!NT_SUCCESS(Status))
366 {
367 DPRINT1("Failed to Initialize the UsbQueue\n");
368 return Status;
369 }
370
371 //
372 // initializes the controller
373 //
374 Status = InitializeController();
375 if (!NT_SUCCESS(Status))
376 {
377 DPRINT1("Failed to Initialize the controller \n");
378 ASSERT(FALSE);
379 return Status;
380 }
381
382
383 //
384 // Stop the controller before modifying schedules
385 //
386 Status = StopController();
387 if (!NT_SUCCESS(Status))
388 {
389 DPRINT1("Failed to stop the controller \n");
390 ASSERT(FALSE);
391 return Status;
392 }
393
394
395 //
396 // Start the controller
397 //
398 DPRINT1("Starting Controller\n");
399 Status = StartController();
400
401 //
402 // done
403 //
404 return Status;
405 }
406
407 NTSTATUS
408 CUSBHardwareDevice::PnpStop(void)
409 {
410 UNIMPLEMENTED
411 return STATUS_NOT_IMPLEMENTED;
412 }
413
414 NTSTATUS
415 CUSBHardwareDevice::HandlePower(
416 PIRP Irp)
417 {
418 UNIMPLEMENTED
419 return STATUS_NOT_IMPLEMENTED;
420 }
421
422 NTSTATUS
423 CUSBHardwareDevice::GetDeviceDetails(
424 OUT OPTIONAL PUSHORT VendorId,
425 OUT OPTIONAL PUSHORT DeviceId,
426 OUT OPTIONAL PULONG NumberOfPorts,
427 OUT OPTIONAL PULONG Speed)
428 {
429 if (VendorId)
430 {
431 //
432 // get vendor
433 //
434 *VendorId = m_VendorID;
435 }
436
437 if (DeviceId)
438 {
439 //
440 // get device id
441 //
442 *DeviceId = m_DeviceID;
443 }
444
445 if (NumberOfPorts)
446 {
447 //
448 // get number of ports
449 //
450 *NumberOfPorts = m_NumberOfPorts;
451 }
452
453 if (Speed)
454 {
455 //
456 // speed is 0x100
457 //
458 *Speed = 0x100;
459 }
460
461 return STATUS_SUCCESS;
462 }
463
464 NTSTATUS CUSBHardwareDevice::GetDMA(
465 OUT struct IDMAMemoryManager **OutDMAMemoryManager)
466 {
467 if (!m_MemoryManager)
468 return STATUS_UNSUCCESSFUL;
469 *OutDMAMemoryManager = m_MemoryManager;
470 return STATUS_SUCCESS;
471 }
472
473 NTSTATUS
474 CUSBHardwareDevice::GetUSBQueue(
475 OUT struct IUSBQueue **OutUsbQueue)
476 {
477 if (!m_UsbQueue)
478 return STATUS_UNSUCCESSFUL;
479 *OutUsbQueue = m_UsbQueue;
480 return STATUS_SUCCESS;
481 }
482
483
484 NTSTATUS
485 CUSBHardwareDevice::StartController(void)
486 {
487 ULONG Control, NumberOfPorts, Index, Descriptor;
488
489 //
490 // first write address of HCCA
491 //
492 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
493
494 //
495 // lets write physical address of dummy control endpoint descriptor
496 //
497 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart);
498
499 //
500 // lets write physical address of dummy bulk endpoint descriptor
501 //
502 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart);
503
504 //
505 // read control register
506 //
507 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
508
509 //
510 // remove flags
511 //
512 Control &= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK | OHCI_ENABLE_LIST | OHCI_HC_FUNCTIONAL_STATE_MASK | OHCI_INTERRUPT_ROUTING);
513
514 //
515 // set command status flags
516 //
517 Control |= OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL;
518
519 //
520 // now start the controller
521 //
522 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), Control);
523
524 //
525 // retrieve number of ports
526 //
527 for(Index = 0; Index < 10; Index++)
528 {
529 //
530 // wait a bit
531 //
532 KeStallExecutionProcessor(10);
533
534 //
535 // read descriptor
536 //
537 Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
538
539 //
540 // get number of ports
541 //
542 NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor);
543
544 //
545 // check if we have received the ports
546 //
547 if (NumberOfPorts)
548 break;
549 }
550
551 //
552 // sanity check
553 //
554 ASSERT(NumberOfPorts < OHCI_MAX_PORT_COUNT);
555
556 //
557 // store number of ports
558 //
559 m_NumberOfPorts = NumberOfPorts;
560
561 //
562 // print out number ports
563 //
564 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts);
565
566 //
567 // done
568 //
569 return STATUS_SUCCESS;
570 }
571
572 NTSTATUS
573 CUSBHardwareDevice::AllocateEndpointDescriptor(
574 OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor)
575 {
576 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
577 PHYSICAL_ADDRESS DescriptorAddress;
578 NTSTATUS Status;
579
580 //
581 // allocate descriptor
582 //
583 Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress);
584 if (!NT_SUCCESS(Status))
585 {
586 //
587 // failed to allocate descriptor
588 //
589 return Status;
590 }
591
592 //
593 // intialize descriptor
594 //
595 Descriptor->Flags = OHCI_ENDPOINT_SKIP;
596 Descriptor->HeadPhysicalDescriptor = 0;
597 Descriptor->NextPhysicalEndpoint = 0;
598 Descriptor->TailPhysicalDescriptor = 0;
599 Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
600
601 //
602 // store result
603 //
604 *OutDescriptor = Descriptor;
605
606 //
607 // done
608 //
609 return STATUS_SUCCESS;
610 }
611
612 NTSTATUS
613 CUSBHardwareDevice::InitializeController()
614 {
615 NTSTATUS Status;
616 ULONG Index, Interval, IntervalIndex, InsertIndex;
617 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
618
619 //
620 // first allocate the hcca area
621 //
622 Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress);
623 if (!NT_SUCCESS(Status))
624 {
625 //
626 // no memory
627 //
628 return Status;
629 }
630
631 //
632 // now allocate an endpoint for control transfers
633 // this endpoint will never be removed
634 //
635 Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor);
636 if (!NT_SUCCESS(Status))
637 {
638 //
639 // no memory
640 //
641 return Status;
642 }
643
644 //
645 // now allocate an endpoint for bulk transfers
646 // this endpoint will never be removed
647 //
648 Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor);
649 if (!NT_SUCCESS(Status))
650 {
651 //
652 // no memory
653 //
654 return Status;
655 }
656
657 //
658 // now allocate an endpoint for iso transfers
659 // this endpoint will never be removed
660 //
661 Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor);
662 if (!NT_SUCCESS(Status))
663 {
664 //
665 // no memory
666 //
667 return Status;
668 }
669
670 //
671 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
672 //
673 for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
674 {
675 //
676 // allocate endpoint descriptor
677 //
678 Status = AllocateEndpointDescriptor(&Descriptor);
679 if (!NT_SUCCESS(Status))
680 {
681 //
682 // no memory
683 //
684 return Status;
685 }
686
687 //
688 // save in array
689 //
690 m_InterruptEndpoints[Index] = Descriptor;
691 }
692
693
694 //
695 // now link the descriptors, taken from Haiku
696 //
697 Interval = OHCI_BIGGEST_INTERVAL;
698 IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1;
699 while (Interval > 1)
700 {
701 InsertIndex = Interval / 2;
702 while (InsertIndex < OHCI_BIGGEST_INTERVAL)
703 {
704 //
705 // assign endpoint address
706 //
707 m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart;
708 InsertIndex += Interval;
709 }
710
711 IntervalIndex--;
712 Interval /= 2;
713 }
714
715 //
716 // link all endpoint descriptors to first descriptor in array
717 //
718 m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
719 for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
720 {
721 //
722 // link descriptor
723 //
724 m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
725 }
726
727 //
728 // Now link the first endpoint to the isochronous endpoint
729 //
730 m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
731
732 //
733 // done
734 //
735 return STATUS_SUCCESS;
736 }
737
738 NTSTATUS
739 CUSBHardwareDevice::StopController(void)
740 {
741 ULONG Control, Reset;
742 ULONG Index;
743
744 //
745 // first turn off all interrupts
746 //
747 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ALL_INTERRUPTS);
748
749 //
750 // check context
751 //
752 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
753
754 //
755 // FIXME: support routing
756 //
757 ASSERT((Control & OHCI_INTERRUPT_ROUTING) == 0);
758
759 //
760 // have a break
761 //
762 KeStallExecutionProcessor(100);
763
764 //
765 // some controllers also depend on this
766 //
767 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
768
769 //
770 // wait a bit
771 //
772 KeStallExecutionProcessor(100);
773
774 //
775 // now reset controller
776 //
777 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET);
778
779 //
780 // reset time is 10ms
781 //
782 for(Index = 0; Index < 10; Index++)
783 {
784 //
785 // wait a bit
786 //
787 KeStallExecutionProcessor(10);
788
789 //
790 // read command status
791 //
792 Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
793
794 //
795 // was reset bit cleared
796 //
797 if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0)
798 {
799 //
800 // controller completed reset
801 //
802 return STATUS_SUCCESS;
803 }
804 }
805
806 //
807 // failed to reset controller
808 //
809 return STATUS_UNSUCCESSFUL;
810 }
811
812 NTSTATUS
813 CUSBHardwareDevice::ResetController(void)
814 {
815 UNIMPLEMENTED
816 return STATUS_NOT_IMPLEMENTED;
817 }
818
819 NTSTATUS
820 CUSBHardwareDevice::ResetPort(
821 IN ULONG PortIndex)
822 {
823 ASSERT(FALSE);
824
825 return STATUS_SUCCESS;
826 }
827
828 NTSTATUS
829 CUSBHardwareDevice::GetPortStatus(
830 ULONG PortId,
831 OUT USHORT *PortStatus,
832 OUT USHORT *PortChange)
833 {
834 UNIMPLEMENTED
835 *PortStatus = 0;
836 *PortChange = 0;
837 return STATUS_SUCCESS;
838 }
839
840 NTSTATUS
841 CUSBHardwareDevice::ClearPortStatus(
842 ULONG PortId,
843 ULONG Status)
844 {
845 UNIMPLEMENTED
846 return STATUS_SUCCESS;
847 }
848
849
850 NTSTATUS
851 CUSBHardwareDevice::SetPortFeature(
852 ULONG PortId,
853 ULONG Feature)
854 {
855 if (Feature == PORT_ENABLE)
856 {
857 //
858 // enable port
859 //
860 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PES);
861 return STATUS_SUCCESS;
862 }
863 else if (Feature == PORT_POWER)
864 {
865 //
866 // enable power
867 //
868 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS);
869 return STATUS_SUCCESS;
870 }
871 else if (Feature == PORT_SUSPEND)
872 {
873 //
874 // enable port
875 //
876 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PSS);
877 return STATUS_SUCCESS;
878 }
879 else if (Feature == PORT_RESET)
880 {
881 //
882 // reset port
883 //
884 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
885
886 //
887 // is there a status change callback
888 //
889 if (m_SCECallBack != NULL)
890 {
891 //
892 // issue callback
893 //
894 m_SCECallBack(m_SCEContext);
895 }
896 }
897 return STATUS_SUCCESS;
898 }
899
900
901
902 VOID
903 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
904 PVOID CallBack,
905 PVOID Context)
906 {
907 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
908 m_SCEContext = Context;
909 }
910
911 KIRQL
912 CUSBHardwareDevice::AcquireDeviceLock(void)
913 {
914 KIRQL OldLevel;
915
916 //
917 // acquire lock
918 //
919 KeAcquireSpinLock(&m_Lock, &OldLevel);
920
921 //
922 // return old irql
923 //
924 return OldLevel;
925 }
926
927
928 VOID
929 CUSBHardwareDevice::ReleaseDeviceLock(
930 KIRQL OldLevel)
931 {
932 KeReleaseSpinLock(&m_Lock, OldLevel);
933 }
934
935 BOOLEAN
936 NTAPI
937 InterruptServiceRoutine(
938 IN PKINTERRUPT Interrupt,
939 IN PVOID ServiceContext)
940 {
941 ASSERT(FALSE);
942 return TRUE;
943 }
944
945 VOID NTAPI
946 EhciDefferedRoutine(
947 IN PKDPC Dpc,
948 IN PVOID DeferredContext,
949 IN PVOID SystemArgument1,
950 IN PVOID SystemArgument2)
951 {
952 ASSERT(FALSE);
953 return;
954 }
955
956 VOID
957 NTAPI
958 StatusChangeWorkItemRoutine(
959 PVOID Context)
960 {
961 //
962 // cast to hardware object
963 //
964 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
965
966 //
967 // is there a callback
968 //
969 if (This->m_SCECallBack)
970 {
971 //
972 // issue callback
973 //
974 This->m_SCECallBack(This->m_SCEContext);
975 }
976
977 }
978
979 NTSTATUS
980 CreateUSBHardware(
981 PUSBHARDWAREDEVICE *OutHardware)
982 {
983 PUSBHARDWAREDEVICE This;
984
985 This = new(NonPagedPool, TAG_USBOHCI) CUSBHardwareDevice(0);
986
987 if (!This)
988 return STATUS_INSUFFICIENT_RESOURCES;
989
990 This->AddRef();
991
992 // return result
993 *OutHardware = (PUSBHARDWAREDEVICE)This;
994
995 return STATUS_SUCCESS;
996 }