[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 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 enabled
1129 if (Value & OHCI_RH_PORTSTATUS_PESC)
1130 *PortChange |= USB_PORT_STATUS_ENABLE;
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_PRS) == 0);
1215 ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC));
1216
1217 //
1218 // clear reset bit complete
1219 //
1220 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRSC);
1221
1222
1223 //
1224 // sanity check
1225 //
1226 ASSERT((Value & OHCI_RH_PORTSTATUS_PES));
1227
1228 //
1229 // re-enable root hub change
1230 //
1231 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
1232 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), Value | OHCI_ROOT_HUB_STATUS_CHANGE);
1233
1234 }
1235
1236 if (Status == C_PORT_CONNECTION)
1237 {
1238 //
1239 // clear bit
1240 //
1241 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC);
1242 }
1243
1244 return STATUS_SUCCESS;
1245 }
1246
1247
1248 NTSTATUS
1249 CUSBHardwareDevice::SetPortFeature(
1250 ULONG PortId,
1251 ULONG Feature)
1252 {
1253 ULONG Value;
1254
1255 DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId, Feature);
1256
1257 //
1258 // read port status
1259 //
1260 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1261
1262
1263 if (Feature == PORT_ENABLE)
1264 {
1265 //
1266 // enable port
1267 //
1268 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PES);
1269 return STATUS_SUCCESS;
1270 }
1271 else if (Feature == PORT_POWER)
1272 {
1273 //
1274 // enable power
1275 //
1276 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS);
1277 return STATUS_SUCCESS;
1278 }
1279 else if (Feature == PORT_SUSPEND)
1280 {
1281 //
1282 // enable port
1283 //
1284 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PSS);
1285 return STATUS_SUCCESS;
1286 }
1287 else if (Feature == PORT_RESET)
1288 {
1289 //
1290 // assert
1291 //
1292 ASSERT((Value & OHCI_RH_PORTSTATUS_CCS));
1293
1294 //
1295 // reset port
1296 //
1297 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
1298
1299 //
1300 // wait
1301 //
1302 KeStallExecutionProcessor(100);
1303
1304 //
1305 // is there a status change callback
1306 //
1307 if (m_SCECallBack != NULL)
1308 {
1309 //
1310 // issue callback
1311 //
1312 m_SCECallBack(m_SCEContext);
1313 }
1314 }
1315 return STATUS_SUCCESS;
1316 }
1317
1318
1319
1320 VOID
1321 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1322 PVOID CallBack,
1323 PVOID Context)
1324 {
1325 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1326 m_SCEContext = Context;
1327 }
1328
1329 KIRQL
1330 CUSBHardwareDevice::AcquireDeviceLock(void)
1331 {
1332 KIRQL OldLevel;
1333
1334 //
1335 // acquire lock
1336 //
1337 KeAcquireSpinLock(&m_Lock, &OldLevel);
1338
1339 //
1340 // return old irql
1341 //
1342 return OldLevel;
1343 }
1344
1345 VOID
1346 CUSBHardwareDevice::GetCurrentFrameNumber(
1347 PULONG FrameNumber)
1348 {
1349 ULONG Control;
1350 ULONG Number;
1351
1352
1353 Number = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_NUMBER_OFFSET));
1354 DPRINT1("FrameNumberInterval %x Frame %x\n", Number, m_HCCA->CurrentFrameNumber);
1355
1356 //
1357 // remove reserved bits
1358 //
1359 Number &= 0xFFFF;
1360
1361 //
1362 // store frame number
1363 //
1364 *FrameNumber = Number;
1365
1366 //
1367 // is the controller started
1368 //
1369 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
1370 ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
1371
1372
1373 }
1374
1375 VOID
1376 CUSBHardwareDevice::ReleaseDeviceLock(
1377 KIRQL OldLevel)
1378 {
1379 KeReleaseSpinLock(&m_Lock, OldLevel);
1380 }
1381
1382 BOOLEAN
1383 NTAPI
1384 InterruptServiceRoutine(
1385 IN PKINTERRUPT Interrupt,
1386 IN PVOID ServiceContext)
1387 {
1388 CUSBHardwareDevice *This;
1389 ULONG DoneHead, Status, Acknowledge = 0;
1390
1391 //
1392 // get context
1393 //
1394 This = (CUSBHardwareDevice*) ServiceContext;
1395
1396 DPRINT("InterruptServiceRoutine\n");
1397
1398 //
1399 // get done head
1400 //
1401 DoneHead = This->m_HCCA->DoneHead;
1402
1403 //
1404 // check if zero
1405 //
1406 if (DoneHead == 0)
1407 {
1408 //
1409 // the interrupt was not caused by DoneHead update
1410 // check if something important happened
1411 //
1412 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);
1413 if (Status == 0)
1414 {
1415 //
1416 // nothing happened, appears to be shared interrupt
1417 //
1418 return FALSE;
1419 }
1420 }
1421 else
1422 {
1423 //
1424 // DoneHead update happened, check if there are other events too
1425 //
1426 Status = OHCI_WRITEBACK_DONE_HEAD;
1427
1428 //
1429 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1430 //
1431 if (DoneHead & OHCI_DONE_INTERRUPTS)
1432 {
1433 //
1434 // get other events
1435 //
1436 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));
1437 }
1438 }
1439
1440 //
1441 // sanity check
1442 //
1443 ASSERT(Status != 0);
1444
1445 if (Status & OHCI_WRITEBACK_DONE_HEAD)
1446 {
1447 //
1448 // head completed
1449 //
1450 Acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
1451 This->m_HCCA->DoneHead = 0;
1452 }
1453
1454 if (Status & OHCI_RESUME_DETECTED)
1455 {
1456 //
1457 // resume
1458 //
1459 DPRINT1("InterruptServiceRoutine> Resume\n");
1460 Acknowledge |= OHCI_RESUME_DETECTED;
1461 }
1462
1463
1464 if (Status & OHCI_UNRECOVERABLE_ERROR)
1465 {
1466 DPRINT1("InterruptServiceRoutine> Controller error\n");
1467
1468 //
1469 // halt controller
1470 //
1471 ASSERT(FALSE);
1472 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
1473 }
1474
1475 if (Status & OHCI_ROOT_HUB_STATUS_CHANGE)
1476 {
1477 //
1478 // new device has arrived
1479 //
1480
1481 //
1482 // disable interrupt as it will fire untill the port has been reset
1483 //
1484 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
1485 Acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE;
1486 }
1487
1488 //
1489 // is there something to acknowledge
1490 //
1491 if (Acknowledge)
1492 {
1493 //
1494 // ack change
1495 //
1496 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET), Acknowledge);
1497 }
1498
1499 //
1500 // defer processing
1501 //
1502 DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status, Acknowledge, This->m_HCCA->CurrentFrameNumber);
1503 KeInsertQueueDpc(&This->m_IntDpcObject, (PVOID)Status, (PVOID)(DoneHead & ~1));
1504
1505 //
1506 // interrupt handled
1507 //
1508 return TRUE;
1509 }
1510
1511 VOID
1512 NTAPI
1513 OhciDefferedRoutine(
1514 IN PKDPC Dpc,
1515 IN PVOID DeferredContext,
1516 IN PVOID SystemArgument1,
1517 IN PVOID SystemArgument2)
1518 {
1519 CUSBHardwareDevice *This;
1520 ULONG CStatus, Index, PortStatus;
1521 ULONG DoneHead;
1522
1523 //
1524 // get parameters
1525 //
1526 This = (CUSBHardwareDevice*)DeferredContext;
1527 CStatus = (ULONG) SystemArgument1;
1528 DoneHead = (ULONG)SystemArgument2;
1529
1530 DPRINT("OhciDefferedRoutine Status %x\n", CStatus);
1531
1532 if (CStatus & OHCI_WRITEBACK_DONE_HEAD)
1533 {
1534 //
1535 // notify queue of event
1536 //
1537 This->m_UsbQueue->TransferDescriptorCompletionCallback(DoneHead);
1538 }
1539 if (CStatus & OHCI_ROOT_HUB_STATUS_CHANGE)
1540 {
1541 //
1542 // device connected, lets check which port
1543 //
1544 for(Index = 0; Index < This->m_NumberOfPorts; Index++)
1545 {
1546 //
1547 // read port status
1548 //
1549 PortStatus = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)));
1550
1551 //
1552 // check if there is a status change
1553 //
1554 if (PortStatus & OHCI_RH_PORTSTATUS_CSC)
1555 {
1556 //
1557 // did a device connect
1558 //
1559 if (PortStatus & OHCI_RH_PORTSTATUS_CCS)
1560 {
1561 //
1562 // device connected
1563 //
1564 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index, (PortStatus & OHCI_RH_PORTSTATUS_LSDA));
1565
1566 //
1567 // enable port
1568 //
1569 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)), OHCI_RH_PORTSTATUS_PES);
1570 }
1571 else
1572 {
1573 //
1574 // device disconnected
1575 //
1576 DPRINT1("Device disconnected at Port %x\n", Index);
1577 }
1578
1579 //
1580 // is there a status change callback
1581 //
1582 if (This->m_SCECallBack != NULL)
1583 {
1584 //
1585 // queue work item for processing
1586 //
1587 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1588 }
1589 }
1590 }
1591 }
1592
1593
1594 }
1595
1596 VOID
1597 NTAPI
1598 StatusChangeWorkItemRoutine(
1599 PVOID Context)
1600 {
1601 //
1602 // cast to hardware object
1603 //
1604 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1605
1606 //
1607 // is there a callback
1608 //
1609 if (This->m_SCECallBack)
1610 {
1611 //
1612 // issue callback
1613 //
1614 This->m_SCECallBack(This->m_SCEContext);
1615 }
1616
1617 }
1618
1619 NTSTATUS
1620 CreateUSBHardware(
1621 PUSBHARDWAREDEVICE *OutHardware)
1622 {
1623 PUSBHARDWAREDEVICE This;
1624
1625 This = new(NonPagedPool, TAG_USBOHCI) CUSBHardwareDevice(0);
1626
1627 if (!This)
1628 return STATUS_INSUFFICIENT_RESOURCES;
1629
1630 This->AddRef();
1631
1632 // return result
1633 *OutHardware = (PUSBHARDWAREDEVICE)This;
1634
1635 return STATUS_SUCCESS;
1636 }