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