[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 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 // lets write physical address of dummy control endpoint descriptor
500 //
501 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart);
502
503 //
504 // lets write physical address of dummy bulk endpoint descriptor
505 //
506 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart);
507
508 //
509 // get frame interval
510 //
511 FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
512 FrameInterval = ((FrameInterval & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE);
513 DPRINT1("FrameInterval %x IntervalValue %x\n", FrameInterval, m_IntervalValue);
514 FrameInterval |= OHCI_FSMPS(m_IntervalValue) | m_IntervalValue;
515 DPRINT1("FrameInterval %x\n", FrameInterval);
516
517 //
518 // write frame interval
519 //
520 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
521
522 //
523 // write address of HCCA
524 //
525 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart);
526
527 //
528 // now enable the interrupts
529 //
530 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE);
531
532 //
533 // enable all queues
534 //
535 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST);
536
537 //
538 // 90 % periodic
539 //
540 Periodic = OHCI_PERIODIC(m_IntervalValue);
541 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic);
542 DPRINT1("Periodic Start %x\n", Periodic);
543
544 //
545 // start the controller
546 //
547 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
548
549 //
550 // wait a bit
551 //
552 KeStallExecutionProcessor(100);
553
554 //
555 // is the controller started
556 //
557 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
558
559 //
560 // assert that the controller has been started
561 //
562 ASSERT((Control & OHCI_HC_FUNCTIONAL_STATE_MASK) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL);
563 ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
564 DPRINT1("Control %x\n", Control);
565
566 //
567 // read descriptor
568 //
569 Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
570
571 //
572 // no over current protection
573 //
574 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor | OHCI_RH_NO_OVER_CURRENT_PROTECTION);
575
576 //
577 // enable power on all ports
578 //
579 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_STATUS_OFFSET), OHCI_RH_LOCAL_POWER_STATUS_CHANGE);
580
581 //
582 // wait a bit
583 //
584 KeStallExecutionProcessor(10);
585
586 //
587 // write descriptor
588 //
589 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor);
590
591 //
592 // retrieve number of ports
593 //
594 for(Index = 0; Index < 10; Index++)
595 {
596 //
597 // wait a bit
598 //
599 KeStallExecutionProcessor(10);
600
601 //
602 // read descriptor
603 //
604 Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET));
605
606 //
607 // get number of ports
608 //
609 NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor);
610
611 //
612 // check if we have received the ports
613 //
614 if (NumberOfPorts)
615 break;
616 }
617
618 //
619 // sanity check
620 //
621 ASSERT(NumberOfPorts < OHCI_MAX_PORT_COUNT);
622
623 //
624 // store number of ports
625 //
626 m_NumberOfPorts = NumberOfPorts;
627
628 //
629 // print out number ports
630 //
631 DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts);
632
633
634 //
635 // done
636 //
637 return STATUS_SUCCESS;
638 }
639
640 NTSTATUS
641 CUSBHardwareDevice::AllocateEndpointDescriptor(
642 OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor)
643 {
644 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
645 PHYSICAL_ADDRESS DescriptorAddress;
646 NTSTATUS Status;
647
648 //
649 // allocate descriptor
650 //
651 Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress);
652 if (!NT_SUCCESS(Status))
653 {
654 //
655 // failed to allocate descriptor
656 //
657 return Status;
658 }
659
660 //
661 // intialize descriptor
662 //
663 Descriptor->Flags = OHCI_ENDPOINT_SKIP;
664 Descriptor->HeadPhysicalDescriptor = 0;
665 Descriptor->NextPhysicalEndpoint = 0;
666 Descriptor->TailPhysicalDescriptor = 0;
667 Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
668
669 //
670 // store result
671 //
672 *OutDescriptor = Descriptor;
673
674 //
675 // done
676 //
677 return STATUS_SUCCESS;
678 }
679
680 NTSTATUS
681 CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
682 struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
683 {
684 *OutDescriptor = m_BulkEndpointDescriptor;
685 return STATUS_SUCCESS;
686 }
687
688 NTSTATUS
689 CUSBHardwareDevice::GetInterruptEndpointDescriptors(
690 struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor)
691 {
692 *OutDescriptor = m_InterruptEndpoints;
693 return STATUS_SUCCESS;
694 }
695
696 NTSTATUS
697 CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor(
698 struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
699 {
700 //
701 // get descriptor
702 //
703 *OutDescriptor = m_IsoEndpointDescriptor;
704 return STATUS_SUCCESS;
705 }
706
707 VOID
708 CUSBHardwareDevice::HeadEndpointDescriptorModified(
709 ULONG Type)
710 {
711 ULONG Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
712
713 if (Type == USB_ENDPOINT_TYPE_CONTROL)
714 {
715 //
716 // notify controller
717 //
718 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_CONTROL_LIST_FILLED);
719 }
720 else if (Type == USB_ENDPOINT_TYPE_BULK)
721 {
722 //
723 // notify controller
724 //
725 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_BULK_LIST_FILLED);
726 }
727 }
728
729 NTSTATUS
730 CUSBHardwareDevice::GetControlHeadEndpointDescriptor(
731 struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
732 {
733 *OutDescriptor = m_ControlEndpointDescriptor;
734 return STATUS_SUCCESS;
735 }
736
737 NTSTATUS
738 CUSBHardwareDevice::InitializeController()
739 {
740 NTSTATUS Status;
741 ULONG Index, Interval, IntervalIndex, InsertIndex;
742 POHCI_ENDPOINT_DESCRIPTOR Descriptor;
743
744 //
745 // first allocate the hcca area
746 //
747 Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress);
748 if (!NT_SUCCESS(Status))
749 {
750 //
751 // no memory
752 //
753 return Status;
754 }
755
756 //
757 // now allocate an endpoint for control transfers
758 // this endpoint will never be removed
759 //
760 Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor);
761 if (!NT_SUCCESS(Status))
762 {
763 //
764 // no memory
765 //
766 return Status;
767 }
768
769 //
770 // now allocate an endpoint for bulk transfers
771 // this endpoint will never be removed
772 //
773 Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor);
774 if (!NT_SUCCESS(Status))
775 {
776 //
777 // no memory
778 //
779 return Status;
780 }
781
782 //
783 // now allocate an endpoint for iso transfers
784 // this endpoint will never be removed
785 //
786 Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor);
787 if (!NT_SUCCESS(Status))
788 {
789 //
790 // no memory
791 //
792 return Status;
793 }
794
795 //
796 // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32
797 //
798 for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
799 {
800 //
801 // allocate endpoint descriptor
802 //
803 Status = AllocateEndpointDescriptor(&Descriptor);
804 if (!NT_SUCCESS(Status))
805 {
806 //
807 // no memory
808 //
809 return Status;
810 }
811
812 //
813 // save in array
814 //
815 m_InterruptEndpoints[Index] = Descriptor;
816 }
817
818
819 //
820 // now link the descriptors, taken from Haiku
821 //
822 Interval = OHCI_BIGGEST_INTERVAL;
823 IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1;
824 while (Interval > 1)
825 {
826 InsertIndex = Interval / 2;
827 while (InsertIndex < OHCI_BIGGEST_INTERVAL)
828 {
829 //
830 // assign endpoint address
831 //
832 m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart;
833 InsertIndex += Interval;
834 }
835
836 IntervalIndex--;
837 Interval /= 2;
838 }
839
840 //
841 // link all endpoint descriptors to first descriptor in array
842 //
843 m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
844 for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
845 {
846 //
847 // link descriptor
848 //
849 m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart;
850 }
851
852 //
853 // Now link the first endpoint to the isochronous endpoint
854 //
855 m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
856
857 //
858 // set iso endpoint type
859 //
860 m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
861
862 //
863 // done
864 //
865 return STATUS_SUCCESS;
866 }
867
868 NTSTATUS
869 CUSBHardwareDevice::StopController(void)
870 {
871 ULONG Control, Reset, Status;
872 ULONG Index, FrameInterval;
873
874 //
875 // alignment check
876 //
877 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), 0xFFFFFFFF);
878 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET));
879 //ASSERT((m_HCCAPhysicalAddress.QuadPart & Control) == Control);
880
881
882 //
883 // check context
884 //
885 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
886
887 if ((Control & OHCI_INTERRUPT_ROUTING))
888 {
889 //
890 // read command status
891 //
892 Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
893
894 //
895 // change ownership
896 //
897 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Status | OHCI_OWNERSHIP_CHANGE_REQUEST);
898 for(Index = 0; Index < 100; Index++)
899 {
900 //
901 // wait a bit
902 //
903 KeStallExecutionProcessor(100);
904
905 //
906 // check control
907 //
908 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
909 if (!(Control & OHCI_INTERRUPT_ROUTING))
910 {
911 //
912 // acquired ownership
913 //
914 break;
915 }
916 }
917
918 //
919 // if the ownership is still not changed, perform reset
920 //
921 if (Control & OHCI_INTERRUPT_ROUTING)
922 {
923 DPRINT1("SMM not responding\n");
924 }
925 else
926 {
927 DPRINT1("SMM has given up ownership\n");
928 }
929 }
930 else
931 {
932 //
933 // read contents of control register
934 //
935 Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
936 DPRINT1("Controller State %x\n", Control);
937
938 if (Control != OHCI_HC_FUNCTIONAL_STATE_RESET)
939 {
940 //
941 // OHCI 5.1.1.3.4, no SMM, BIOS active
942 //
943 if (Control != OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL)
944 {
945 //
946 // lets resume
947 //
948 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESUME);
949 Index = 0;
950 do
951 {
952 //
953 // wait untill its resumed
954 //
955 KeStallExecutionProcessor(10);
956
957 //
958 // check control register
959 //
960 Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
961 if (Control & OHCI_HC_FUNCTIONAL_STATE_RESUME)
962 {
963 //
964 // it has resumed
965 //
966 break;
967 }
968
969 //
970 // check for time outs
971 //
972 Index++;
973 if(Index > 100)
974 {
975 DPRINT1("Failed to resume controller\n");
976 break;
977 }
978 }while(TRUE);
979 }
980 }
981 else
982 {
983 //
984 // 5.1.1.3.5 OHCI, no SMM, no BIOS
985 //
986 Index = 0;
987
988 //
989 // some controllers also depend on this
990 //
991 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
992 do
993 {
994 //
995 // wait untill its reset
996 //
997 KeStallExecutionProcessor(10);
998
999 //
1000 // check control register
1001 //
1002 Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK);
1003 if (Control == OHCI_HC_FUNCTIONAL_STATE_RESET)
1004 {
1005 //
1006 // it has reset
1007 //
1008 break;
1009 }
1010
1011 //
1012 // check for time outs
1013 //
1014 Index++;
1015 if(Index > 100)
1016 {
1017 DPRINT1("Failed to reset controller\n");
1018 break;
1019 }
1020
1021 }while(TRUE);
1022 }
1023 }
1024
1025 //
1026 // read from interval
1027 //
1028 FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
1029
1030 //
1031 // store interval value for later
1032 //
1033 m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval);
1034
1035 DPRINT1("FrameInterval %x Interval %x\n", FrameInterval, m_IntervalValue);
1036
1037 //
1038 // now reset controller
1039 //
1040 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET);
1041
1042 //
1043 // reset time is 10ms
1044 //
1045 for(Index = 0; Index < 10; Index++)
1046 {
1047 //
1048 // wait a bit
1049 //
1050 KeStallExecutionProcessor(10);
1051
1052 //
1053 // read command status
1054 //
1055 Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET));
1056
1057 //
1058 // was reset bit cleared
1059 //
1060 if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0)
1061 {
1062 //
1063 // restore the frame interval register
1064 //
1065 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
1066
1067 //
1068 // controller completed reset
1069 //
1070 return STATUS_SUCCESS;
1071 }
1072 }
1073
1074 //
1075 // failed to reset controller
1076 //
1077 return STATUS_UNSUCCESSFUL;
1078 }
1079
1080 NTSTATUS
1081 CUSBHardwareDevice::ResetController(void)
1082 {
1083 UNIMPLEMENTED
1084 return STATUS_NOT_IMPLEMENTED;
1085 }
1086
1087 NTSTATUS
1088 CUSBHardwareDevice::ResetPort(
1089 IN ULONG PortIndex)
1090 {
1091 ASSERT(FALSE);
1092
1093 return STATUS_SUCCESS;
1094 }
1095
1096 NTSTATUS
1097 CUSBHardwareDevice::GetPortStatus(
1098 ULONG PortId,
1099 OUT USHORT *PortStatus,
1100 OUT USHORT *PortChange)
1101 {
1102 #if 0
1103 ULONG Value;
1104
1105 if (PortId > m_NumberOfPorts)
1106 return STATUS_UNSUCCESSFUL;
1107
1108 // init result variables
1109 *PortStatus = 0;
1110 *PortChange = 0;
1111
1112 //
1113 // read port status
1114 //
1115 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1116 DPRINT("GetPortStatus PortId %x Value %x\n", PortId, Value);
1117
1118
1119 // connected
1120 if (Value & OHCI_RH_PORTSTATUS_CCS)
1121 *PortStatus |= USB_PORT_STATUS_CONNECT;
1122
1123 // did a device connect?
1124 if (Value & OHCI_RH_PORTSTATUS_CSC)
1125 *PortChange |= USB_PORT_STATUS_CONNECT;
1126
1127 // port enabled
1128 if (Value & OHCI_RH_PORTSTATUS_PES)
1129 *PortStatus |= USB_PORT_STATUS_ENABLE;
1130
1131 // port enabled
1132 if (Value & OHCI_RH_PORTSTATUS_PESC)
1133 *PortChange |= USB_PORT_STATUS_ENABLE;
1134
1135 // port suspend
1136 if (Value & OHCI_RH_PORTSTATUS_PSS)
1137 *PortStatus |= USB_PORT_STATUS_SUSPEND;
1138
1139 // port suspend
1140 if (Value & OHCI_RH_PORTSTATUS_PSSC)
1141 *PortChange |= USB_PORT_STATUS_ENABLE;
1142
1143 // port reset
1144 if (Value & OHCI_RH_PORTSTATUS_PSS)
1145 *PortStatus |= USB_PORT_STATUS_RESET;
1146
1147 // port reset
1148 if (Value & OHCI_RH_PORTSTATUS_PRSC)
1149 *PortChange |= USB_PORT_STATUS_RESET;
1150
1151 #else
1152 *PortStatus = m_PortStatus[PortId].PortStatus;
1153 *PortChange = m_PortStatus[PortId].PortChange;
1154 #endif
1155
1156 return STATUS_SUCCESS;
1157 }
1158
1159 NTSTATUS
1160 CUSBHardwareDevice::ClearPortStatus(
1161 ULONG PortId,
1162 ULONG Status)
1163 {
1164 ULONG Value;
1165
1166 DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status);
1167
1168 if (PortId > m_NumberOfPorts)
1169 return STATUS_UNSUCCESSFUL;
1170
1171 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1172 KeStallExecutionProcessor(100);
1173
1174 if (Status == C_PORT_RESET)
1175 {
1176 do
1177 {
1178 //
1179 // read port status
1180 //
1181 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1182
1183 if ((Value & OHCI_RH_PORTSTATUS_PRS) == 0)
1184 {
1185 //
1186 // reset is complete
1187 //
1188 break;
1189 }
1190
1191 //
1192 // wait a bit
1193 //
1194 KeStallExecutionProcessor(100);
1195
1196 //DPRINT1("Value %x Index %lu\n", Value, Index);
1197
1198 }while(TRUE);
1199
1200 //
1201 // check if reset bit is still set
1202 //
1203 if (Value & OHCI_RH_PORTSTATUS_PRS)
1204 {
1205 //
1206 // reset failed
1207 //
1208 DPRINT1("PortId %lu Reset failed\n", PortId);
1209 return STATUS_UNSUCCESSFUL;
1210 }
1211
1212 //
1213 // sanity checks
1214 //
1215 ASSERT((Value & OHCI_RH_PORTSTATUS_PRS) == 0);
1216 ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC));
1217
1218 //
1219 // clear reset bit complete
1220 //
1221 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRSC);
1222
1223 //
1224 // read status register
1225 //
1226 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1227
1228 //
1229 // reset complete bit should be cleared
1230 //
1231 ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC) == 0);
1232
1233 //
1234 // update port status
1235 //
1236 m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_RESET;
1237
1238 //
1239 // sanity check
1240 //
1241 ASSERT((Value & OHCI_RH_PORTSTATUS_PES));
1242
1243 //
1244 // port is enabled
1245 //
1246 m_PortStatus[PortId].PortStatus |= USB_PORT_STATUS_ENABLE;
1247
1248 //
1249 // re-enable root hub change
1250 //
1251 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET));
1252 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), Value | OHCI_ROOT_HUB_STATUS_CHANGE);
1253
1254 }
1255
1256 if (Status == C_PORT_CONNECTION)
1257 {
1258 //
1259 // clear bit
1260 //
1261 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC);
1262 m_PortStatus[PortId].PortChange &= ~USB_PORT_STATUS_CONNECT;
1263 }
1264
1265
1266
1267 return STATUS_SUCCESS;
1268 }
1269
1270
1271 NTSTATUS
1272 CUSBHardwareDevice::SetPortFeature(
1273 ULONG PortId,
1274 ULONG Feature)
1275 {
1276 ULONG Value;
1277
1278 DPRINT1("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId, Feature);
1279
1280 //
1281 // read port status
1282 //
1283 Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)));
1284
1285
1286 if (Feature == PORT_ENABLE)
1287 {
1288 //
1289 // enable port
1290 //
1291 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PES);
1292 return STATUS_SUCCESS;
1293 }
1294 else if (Feature == PORT_POWER)
1295 {
1296 //
1297 // enable power
1298 //
1299 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS);
1300 return STATUS_SUCCESS;
1301 }
1302 else if (Feature == PORT_SUSPEND)
1303 {
1304 //
1305 // enable port
1306 //
1307 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PSS);
1308 return STATUS_SUCCESS;
1309 }
1310 else if (Feature == PORT_RESET)
1311 {
1312 //
1313 // assert
1314 //
1315 ASSERT((Value & OHCI_RH_PORTSTATUS_CCS));
1316
1317 //
1318 // reset port
1319 //
1320 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS);
1321
1322 //
1323 // wait
1324 //
1325 KeStallExecutionProcessor(100);
1326
1327 //
1328 // update cached settings
1329 //
1330 m_PortStatus[PortId].PortChange |= USB_PORT_STATUS_RESET;
1331 m_PortStatus[PortId].PortStatus &= ~USB_PORT_STATUS_ENABLE;
1332
1333 //
1334 // is there a status change callback
1335 //
1336 if (m_SCECallBack != NULL)
1337 {
1338 //
1339 // issue callback
1340 //
1341 m_SCECallBack(m_SCEContext);
1342 }
1343 }
1344 return STATUS_SUCCESS;
1345 }
1346
1347
1348
1349 VOID
1350 CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
1351 PVOID CallBack,
1352 PVOID Context)
1353 {
1354 m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
1355 m_SCEContext = Context;
1356 }
1357
1358 KIRQL
1359 CUSBHardwareDevice::AcquireDeviceLock(void)
1360 {
1361 KIRQL OldLevel;
1362
1363 //
1364 // acquire lock
1365 //
1366 KeAcquireSpinLock(&m_Lock, &OldLevel);
1367
1368 //
1369 // return old irql
1370 //
1371 return OldLevel;
1372 }
1373
1374 VOID
1375 CUSBHardwareDevice::GetCurrentFrameNumber(
1376 PULONG FrameNumber)
1377 {
1378 ULONG Control;
1379 ULONG Number;
1380
1381
1382 Number = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_NUMBER_OFFSET));
1383 DPRINT1("FrameNumberInterval %x Frame %x\n", Number, m_HCCA->CurrentFrameNumber);
1384
1385 //
1386 // remove reserved bits
1387 //
1388 Number &= 0xFFFF;
1389
1390 //
1391 // store frame number
1392 //
1393 *FrameNumber = Number;
1394
1395 //
1396 // is the controller started
1397 //
1398 Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET));
1399 ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST);
1400
1401
1402 }
1403
1404 VOID
1405 CUSBHardwareDevice::ReleaseDeviceLock(
1406 KIRQL OldLevel)
1407 {
1408 KeReleaseSpinLock(&m_Lock, OldLevel);
1409 }
1410
1411 BOOLEAN
1412 NTAPI
1413 InterruptServiceRoutine(
1414 IN PKINTERRUPT Interrupt,
1415 IN PVOID ServiceContext)
1416 {
1417 CUSBHardwareDevice *This;
1418 ULONG DoneHead, Status, Acknowledge = 0;
1419
1420 //
1421 // get context
1422 //
1423 This = (CUSBHardwareDevice*) ServiceContext;
1424
1425 DPRINT("InterruptServiceRoutine\n");
1426
1427 //
1428 // get done head
1429 //
1430 DoneHead = This->m_HCCA->DoneHead;
1431
1432 //
1433 // check if zero
1434 //
1435 if (DoneHead == 0)
1436 {
1437 //
1438 // the interrupt was not caused by DoneHead update
1439 // check if something important happened
1440 //
1441 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);
1442 if (Status == 0)
1443 {
1444 //
1445 // nothing happened, appears to be shared interrupt
1446 //
1447 return FALSE;
1448 }
1449 }
1450 else
1451 {
1452 //
1453 // DoneHead update happened, check if there are other events too
1454 //
1455 Status = OHCI_WRITEBACK_DONE_HEAD;
1456
1457 //
1458 // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests
1459 //
1460 if (DoneHead & OHCI_DONE_INTERRUPTS)
1461 {
1462 //
1463 // get other events
1464 //
1465 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));
1466 }
1467 }
1468
1469 //
1470 // sanity check
1471 //
1472 ASSERT(Status != 0);
1473
1474 if (Status & OHCI_WRITEBACK_DONE_HEAD)
1475 {
1476 //
1477 // head completed
1478 //
1479 Acknowledge |= OHCI_WRITEBACK_DONE_HEAD;
1480 This->m_HCCA->DoneHead = 0;
1481 }
1482
1483 if (Status & OHCI_RESUME_DETECTED)
1484 {
1485 //
1486 // resume
1487 //
1488 DPRINT1("InterruptServiceRoutine> Resume\n");
1489 Acknowledge |= OHCI_RESUME_DETECTED;
1490 }
1491
1492
1493 if (Status & OHCI_UNRECOVERABLE_ERROR)
1494 {
1495 DPRINT1("InterruptServiceRoutine> Controller error\n");
1496
1497 //
1498 // halt controller
1499 //
1500 ASSERT(FALSE);
1501 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET);
1502 }
1503
1504 if (Status & OHCI_ROOT_HUB_STATUS_CHANGE)
1505 {
1506 //
1507 // new device has arrived
1508 //
1509
1510 //
1511 // disable interrupt as it will fire untill the port has been reset
1512 //
1513 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE);
1514 Acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE;
1515 }
1516
1517 //
1518 // is there something to acknowledge
1519 //
1520 if (Acknowledge)
1521 {
1522 //
1523 // ack change
1524 //
1525 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET), Acknowledge);
1526 }
1527
1528 //
1529 // defer processing
1530 //
1531 DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status, Acknowledge, This->m_HCCA->CurrentFrameNumber);
1532 KeInsertQueueDpc(&This->m_IntDpcObject, (PVOID)Status, (PVOID)(DoneHead & ~1));
1533
1534 //
1535 // interrupt handled
1536 //
1537 return TRUE;
1538 }
1539
1540 VOID
1541 NTAPI
1542 OhciDefferedRoutine(
1543 IN PKDPC Dpc,
1544 IN PVOID DeferredContext,
1545 IN PVOID SystemArgument1,
1546 IN PVOID SystemArgument2)
1547 {
1548 CUSBHardwareDevice *This;
1549 ULONG CStatus, Index, PortStatus;
1550 ULONG DoneHead;
1551
1552 //
1553 // get parameters
1554 //
1555 This = (CUSBHardwareDevice*)DeferredContext;
1556 CStatus = (ULONG) SystemArgument1;
1557 DoneHead = (ULONG)SystemArgument2;
1558
1559 DPRINT("OhciDefferedRoutine Status %x\n", CStatus);
1560
1561 if (CStatus & OHCI_WRITEBACK_DONE_HEAD)
1562 {
1563 //
1564 // notify queue of event
1565 //
1566 This->m_UsbQueue->TransferDescriptorCompletionCallback(DoneHead);
1567 }
1568 if (CStatus & OHCI_ROOT_HUB_STATUS_CHANGE)
1569 {
1570 //
1571 // device connected, lets check which port
1572 //
1573 for(Index = 0; Index < This->m_NumberOfPorts; Index++)
1574 {
1575 //
1576 // read port status
1577 //
1578 PortStatus = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)));
1579
1580 //
1581 // check if there is a status change
1582 //
1583 if (PortStatus & OHCI_RH_PORTSTATUS_CSC)
1584 {
1585 //
1586 // did a device connect
1587 //
1588 if (PortStatus & OHCI_RH_PORTSTATUS_CCS)
1589 {
1590 //
1591 // device connected
1592 //
1593 DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index, (PortStatus & OHCI_RH_PORTSTATUS_LSDA));
1594
1595 //
1596 // enable port
1597 //
1598 WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)), OHCI_RH_PORTSTATUS_PES);
1599
1600
1601 //
1602 // store change
1603 //
1604 This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_CONNECT;
1605 This->m_PortStatus[Index].PortChange |= USB_PORT_STATUS_CONNECT;
1606
1607 if ((PortStatus & OHCI_RH_PORTSTATUS_LSDA))
1608 {
1609 //
1610 // low speed device connected
1611 //
1612 This->m_PortStatus[Index].PortStatus |= USB_PORT_STATUS_LOW_SPEED;
1613 }
1614 }
1615 else
1616 {
1617 //
1618 // device disconnected
1619 //
1620 DPRINT1("Device disconnected at Port %x\n", Index);
1621
1622 //
1623 // update port status flags
1624 //
1625 This->m_PortStatus[Index].PortStatus &= ~USB_PORT_STATUS_LOW_SPEED;
1626 This->m_PortStatus[Index].PortStatus &= ~USB_PORT_STATUS_CONNECT;
1627 This->m_PortStatus[Index].PortChange |= USB_PORT_STATUS_CONNECT;
1628 }
1629
1630 //
1631 // is there a status change callback
1632 //
1633 if (This->m_SCECallBack != NULL)
1634 {
1635 //
1636 // queue work item for processing
1637 //
1638 ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue);
1639 }
1640 }
1641 }
1642 }
1643
1644
1645 }
1646
1647 VOID
1648 NTAPI
1649 StatusChangeWorkItemRoutine(
1650 PVOID Context)
1651 {
1652 //
1653 // cast to hardware object
1654 //
1655 CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
1656
1657 //
1658 // is there a callback
1659 //
1660 if (This->m_SCECallBack)
1661 {
1662 //
1663 // issue callback
1664 //
1665 This->m_SCECallBack(This->m_SCEContext);
1666 }
1667
1668 }
1669
1670 NTSTATUS
1671 CreateUSBHardware(
1672 PUSBHARDWAREDEVICE *OutHardware)
1673 {
1674 PUSBHARDWAREDEVICE This;
1675
1676 This = new(NonPagedPool, TAG_USBOHCI) CUSBHardwareDevice(0);
1677
1678 if (!This)
1679 return STATUS_INSUFFICIENT_RESOURCES;
1680
1681 This->AddRef();
1682
1683 // return result
1684 *OutHardware = (PUSBHARDWAREDEVICE)This;
1685
1686 return STATUS_SUCCESS;
1687 }