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