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