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