[USBEHCI_NEW]
[reactos.git] / drivers / usb / usbehci_new / 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/usbehci/hcd_controller.cpp
5 * PURPOSE: USB EHCI 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 "usbehci.h"
13 #include "hardware.h"
14
15 BOOLEAN
16 NTAPI
17 InterruptServiceRoutine(
18 IN PKINTERRUPT Interrupt,
19 IN PVOID ServiceContext);
20
21 class CUSBHardwareDevice : public IUSBHardwareDevice
22 {
23 public:
24 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
25
26 STDMETHODIMP_(ULONG) AddRef()
27 {
28 InterlockedIncrement(&m_Ref);
29 return m_Ref;
30 }
31 STDMETHODIMP_(ULONG) Release()
32 {
33 InterlockedDecrement(&m_Ref);
34
35 if (!m_Ref)
36 {
37 delete this;
38 return 0;
39 }
40 return m_Ref;
41 }
42 // com
43 NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject);
44 NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources);
45 NTSTATUS PnpStop(void);
46 NTSTATUS HandlePower(PIRP Irp);
47 NTSTATUS GetDeviceDetails(PULONG VendorId, PULONG DeviceId, PULONG NumberOfPorts, PULONG Speed);
48 NTSTATUS GetDmaMemoryManager(OUT struct IDMAMemoryManager **OutMemoryManager);
49 NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
50 NTSTATUS StartController();
51 NTSTATUS StopController();
52 NTSTATUS ResetController();
53 NTSTATUS ResetPort(ULONG PortIndex);
54 KIRQL AcquireDeviceLock(void);
55 VOID ReleaseDeviceLock(KIRQL OldLevel);
56 // local
57 BOOLEAN InterruptService();
58
59 // friend function
60 friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
61
62 // constructor / destructor
63 CUSBHardwareDevice(IUnknown *OuterUnknown){}
64 virtual ~CUSBHardwareDevice(){}
65
66 protected:
67 LONG m_Ref;
68 PDRIVER_OBJECT m_DriverObject;
69 PDEVICE_OBJECT m_PhysicalDeviceObject;
70 PDEVICE_OBJECT m_FunctionalDeviceObject;
71 PDEVICE_OBJECT m_NextDeviceObject;
72 KSPIN_LOCK m_Lock;
73 PKINTERRUPT m_Interrupt;
74 PULONG m_Base;
75 PDMA_ADAPTER m_Adapter;
76 ULONG m_MapRegisters;
77 PQUEUE_HEAD AsyncListQueueHead;
78 EHCI_CAPS m_Capabilities;
79
80 VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
81 VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
82 VOID SetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts);
83 VOID GetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts);
84 //VOID SetPortRegister(PEHCI_USBPORTSC_CONTENT UsbPort);
85 //VOID GetPortRegister(PEHCI_USBPORTSC_CONTENT UsbPort);
86 ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
87 ULONG EHCI_READ_REGISTER_USHORT(ULONG Offset);
88 ULONG EHCI_READ_REGISTER_UCHAR(ULONG Offset);
89 VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
90 VOID EHCI_WRITE_REGISTER_USHORT(ULONG Offset, ULONG Value);
91 VOID EHCI_WRITE_REGISTER_UCHAR(ULONG Offset, ULONG Value);
92 };
93
94 //=================================================================================================
95 // COM
96 //
97 NTSTATUS
98 STDMETHODCALLTYPE
99 CUSBHardwareDevice::QueryInterface(
100 IN REFIID refiid,
101 OUT PVOID* Output)
102 {
103 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
104 {
105 *Output = PVOID(PUNKNOWN(this));
106 PUNKNOWN(*Output)->AddRef();
107 return STATUS_SUCCESS;
108 }
109
110 return STATUS_UNSUCCESSFUL;
111 }
112
113 NTSTATUS
114 CUSBHardwareDevice::Initialize(
115 PDRIVER_OBJECT DriverObject,
116 PDEVICE_OBJECT FunctionalDeviceObject,
117 PDEVICE_OBJECT PhysicalDeviceObject,
118 PDEVICE_OBJECT LowerDeviceObject)
119 {
120
121 DPRINT1("CUSBHardwareDevice::Initialize\n");
122
123 //
124 // store device objects
125 //
126 m_DriverObject = DriverObject;
127 m_FunctionalDeviceObject = FunctionalDeviceObject;
128 m_PhysicalDeviceObject = PhysicalDeviceObject;
129 m_NextDeviceObject = LowerDeviceObject;
130
131 //
132 // initialize device lock
133 //
134 KeInitializeSpinLock(&m_Lock);
135
136 return STATUS_SUCCESS;
137 }
138
139 VOID
140 CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
141 {
142 PULONG Register;
143 Register = (PULONG)UsbCmd;
144 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register);
145 }
146
147 VOID
148 CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
149 {
150 PULONG Register;
151 Register = (PULONG)UsbCmd;
152 *Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD));
153 }
154
155
156 VOID
157 CUSBHardwareDevice::SetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts)
158 {
159 PULONG Register;
160 Register = (PULONG)UsbSts;
161 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBSTS), *Register);
162 }
163
164 VOID
165 CUSBHardwareDevice::GetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts)
166 {
167 PULONG CmdRegister;
168 CmdRegister = (PULONG)UsbSts;
169 *CmdRegister = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBSTS));
170 }
171
172 ULONG
173 CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset)
174 {
175 return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset));
176 }
177
178 ULONG
179 CUSBHardwareDevice::EHCI_READ_REGISTER_USHORT(ULONG Offset)
180 {
181 return READ_REGISTER_USHORT((PUSHORT)((ULONG)m_Base + Offset));
182 }
183
184 ULONG
185 CUSBHardwareDevice::EHCI_READ_REGISTER_UCHAR(ULONG Offset)
186 {
187 return READ_REGISTER_UCHAR((PUCHAR)((ULONG)m_Base + Offset));
188 }
189
190 VOID
191 CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value)
192 {
193 WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value);
194 }
195
196 VOID
197 CUSBHardwareDevice::EHCI_WRITE_REGISTER_USHORT(ULONG Offset, ULONG Value)
198 {
199 WRITE_REGISTER_USHORT((PUSHORT)((ULONG)m_Base + Offset), Value);
200 }
201
202 VOID
203 CUSBHardwareDevice::EHCI_WRITE_REGISTER_UCHAR(ULONG Offset, ULONG Value)
204 {
205 WRITE_REGISTER_UCHAR((PUCHAR)((ULONG)m_Base + Offset), Value);
206 }
207
208 NTSTATUS
209 CUSBHardwareDevice::PnpStart(
210 PCM_RESOURCE_LIST RawResources,
211 PCM_RESOURCE_LIST TranslatedResources)
212 {
213 ULONG Index, Count;
214 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
215 DEVICE_DESCRIPTION DeviceDescription;
216 PVOID ResourceBase;
217 NTSTATUS Status;
218
219 DPRINT1("CUSBHardwareDevice::PnpStart\n");
220 for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
221 {
222 //
223 // get resource descriptor
224 //
225 ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
226
227 switch(ResourceDescriptor->Type)
228 {
229 case CmResourceTypeInterrupt:
230 {
231 Status = IoConnectInterrupt(&m_Interrupt,
232 InterruptServiceRoutine,
233 (PVOID)this,
234 NULL,
235 ResourceDescriptor->u.Interrupt.Vector,
236 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
237 (KIRQL)ResourceDescriptor->u.Interrupt.Level,
238 (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
239 (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
240 ResourceDescriptor->u.Interrupt.Affinity,
241 FALSE);
242
243 if (!NT_SUCCESS(Status))
244 {
245 //
246 // failed to register interrupt
247 //
248 DPRINT1("IoConnect Interrupt failed with %x\n", Status);
249 return Status;
250 }
251 break;
252 }
253 case CmResourceTypeMemory:
254 {
255 //
256 // get resource base
257 //
258 ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
259 if (!ResourceBase)
260 {
261 //
262 // failed to map registers
263 //
264 DPRINT1("MmMapIoSpace failed\n");
265 return STATUS_INSUFFICIENT_RESOURCES;
266 }
267
268 //
269 // Get controllers capabilities
270 //
271 m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase);
272 m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + 2));
273 m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 4));
274 m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 8));
275
276 DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount);
277 if (m_Capabilities.HCSParams.PortRouteRules)
278 {
279 for (Count = 0; Count < m_Capabilities.HCSParams.PortCount; Count++)
280 {
281 m_Capabilities.PortRoute[Count] = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + 12 + Count);
282 }
283 }
284
285 //
286 // Set m_Base to the address of Operational Register Space
287 //
288 m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
289 break;
290 }
291 }
292 }
293
294
295 //
296 // zero device description
297 //
298 RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
299
300 //
301 // initialize device description
302 //
303 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
304 DeviceDescription.Master = TRUE;
305 DeviceDescription.ScatterGather = TRUE;
306 DeviceDescription.Dma32BitAddresses = TRUE;
307 DeviceDescription.DmaWidth = Width32Bits;
308 DeviceDescription.InterfaceType = PCIBus;
309 DeviceDescription.MaximumLength = MAXULONG;
310
311 //
312 // get dma adapter
313 //
314 m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
315 if (!m_Adapter)
316 {
317 //
318 // failed to get dma adapter
319 //
320 DPRINT1("Failed to acquire dma adapter\n");
321 return STATUS_INSUFFICIENT_RESOURCES;
322 }
323
324 //
325 // FIXME: Create a QueueHead that will always be the address of the AsyncList
326 //
327 AsyncListQueueHead = NULL;
328
329 //
330 // Start the controller
331 //
332 DPRINT1("Starting Controller\n");
333 return StartController();
334 }
335
336 NTSTATUS
337 CUSBHardwareDevice::PnpStop(void)
338 {
339 UNIMPLEMENTED
340 return STATUS_NOT_IMPLEMENTED;
341 }
342
343 NTSTATUS
344 CUSBHardwareDevice::HandlePower(
345 PIRP Irp)
346 {
347 UNIMPLEMENTED
348 return STATUS_NOT_IMPLEMENTED;
349 }
350
351 NTSTATUS
352 CUSBHardwareDevice::GetDeviceDetails(
353 OUT OPTIONAL PULONG VendorId,
354 OUT OPTIONAL PULONG DeviceId,
355 OUT OPTIONAL PULONG NumberOfPorts,
356 OUT OPTIONAL PULONG Speed)
357 {
358 UNIMPLEMENTED
359 return STATUS_NOT_IMPLEMENTED;
360 }
361
362
363 NTSTATUS
364 CUSBHardwareDevice::GetDmaMemoryManager(
365 OUT struct IDMAMemoryManager **OutMemoryManager)
366 {
367 UNIMPLEMENTED
368 return STATUS_NOT_IMPLEMENTED;
369 }
370
371
372 NTSTATUS
373 CUSBHardwareDevice::GetUSBQueue(
374 OUT struct IUSBQueue **OutUsbQueue)
375 {
376 UNIMPLEMENTED
377 return STATUS_NOT_IMPLEMENTED;
378 }
379
380 NTSTATUS
381 CUSBHardwareDevice::StartController(void)
382 {
383 EHCI_USBCMD_CONTENT UsbCmd;
384 EHCI_USBSTS_CONTENT UsbSts;
385 LONG FailSafe;
386
387 //
388 // Stop the controller if its running
389 //
390 GetStatusRegister(&UsbSts);
391 if (UsbSts.HCHalted)
392 StopController();
393
394 //
395 // Reset the device. Bit is set to 0 on completion.
396 //
397 SetCommandRegister(&UsbCmd);
398 UsbCmd.HCReset = TRUE;
399 SetCommandRegister(&UsbCmd);
400
401 //
402 // Check that the controller reset
403 //
404 for (FailSafe = 100; FailSafe > 1; FailSafe--)
405 {
406 KeStallExecutionProcessor(10);
407 GetCommandRegister(&UsbCmd);
408 if (!UsbCmd.HCReset)
409 {
410 break;
411 }
412 }
413
414 //
415 // If the controller did not reset then fail
416 //
417 if (UsbCmd.HCReset)
418 {
419 DPRINT1("EHCI ERROR: Controller failed to reset. Hardware problem!\n");
420 return STATUS_UNSUCCESSFUL;
421 }
422
423 //
424 // Disable Interrupts and clear status
425 //
426 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, 0);
427 EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, 0x0000001f);
428
429 //
430 // FIXME: Assign the AsyncList Register
431 //
432
433 //
434 // Set Schedules to Enable and Interrupt Threshold to 1ms.
435 //
436 GetCommandRegister(&UsbCmd);
437 UsbCmd.PeriodicEnable = FALSE;
438 UsbCmd.AsyncEnable = FALSE; //FIXME: Need USB Memory Manager
439
440 UsbCmd.IntThreshold = 1;
441 // FIXME: Set framlistsize when periodic is implemented.
442 SetCommandRegister(&UsbCmd);
443
444 //
445 // Enable Interrupts and start execution
446 //
447 EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR
448 /*| EHCI_USBINTR_FLROVR*/ | EHCI_USBINTR_PC);
449
450 UsbCmd.Run = TRUE;
451 SetCommandRegister(&UsbCmd);
452
453 //
454 // Wait for execution to start
455 //
456 for (FailSafe = 100; FailSafe > 1; FailSafe--)
457 {
458 KeStallExecutionProcessor(10);
459 GetStatusRegister(&UsbSts);
460
461 if (!UsbSts.HCHalted)
462 {
463 break;
464 }
465 }
466
467 if (!UsbSts.HCHalted)
468 {
469 DPRINT1("Could not start execution on the controller\n");
470 return STATUS_UNSUCCESSFUL;
471 }
472
473 //
474 // Set port routing to EHCI controller
475 //
476 EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
477 DPRINT1("EHCI Started!\n");
478 return STATUS_SUCCESS;
479 }
480
481 NTSTATUS
482 CUSBHardwareDevice::StopController(void)
483 {
484 EHCI_USBCMD_CONTENT UsbCmd;
485 EHCI_USBSTS_CONTENT UsbSts;
486 LONG FailSafe;
487
488 //
489 // Disable Interrupts and stop execution
490 EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
491
492 GetCommandRegister(&UsbCmd);
493 UsbCmd.Run = FALSE;
494 SetCommandRegister(&UsbCmd);
495
496 for (FailSafe = 100; FailSafe > 1; FailSafe--)
497 {
498 KeStallExecutionProcessor(10);
499 GetStatusRegister(&UsbSts);
500 if (UsbSts.HCHalted)
501 {
502 break;
503 }
504 }
505
506 if (!UsbSts.HCHalted)
507 {
508 DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
509 return STATUS_UNSUCCESSFUL;
510 }
511
512 return STATUS_SUCCESS;
513 }
514
515 NTSTATUS
516 CUSBHardwareDevice::ResetController(void)
517 {
518 UNIMPLEMENTED
519 return STATUS_NOT_IMPLEMENTED;
520 }
521
522 NTSTATUS
523 CUSBHardwareDevice::ResetPort(
524 IN ULONG PortIndex)
525 {
526 UNIMPLEMENTED
527 return STATUS_NOT_IMPLEMENTED;
528 }
529
530 KIRQL
531 CUSBHardwareDevice::AcquireDeviceLock(void)
532 {
533 KIRQL OldLevel;
534
535 //
536 // acquire lock
537 //
538 KeAcquireSpinLock(&m_Lock, &OldLevel);
539
540 //
541 // return old irql
542 //
543 return OldLevel;
544 }
545
546
547 VOID
548 CUSBHardwareDevice::ReleaseDeviceLock(
549 KIRQL OldLevel)
550 {
551 KeReleaseSpinLock(&m_Lock, OldLevel);
552 }
553
554 BOOLEAN
555 NTAPI
556 InterruptServiceRoutine(
557 IN PKINTERRUPT Interrupt,
558 IN PVOID ServiceContext)
559 {
560 UNIMPLEMENTED
561 return FALSE;
562 }
563
564 NTSTATUS
565 CreateUSBHardware(
566 PUSBHARDWAREDEVICE *OutHardware)
567 {
568 PUSBHARDWAREDEVICE This;
569
570 This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
571
572 if (!This)
573 return STATUS_INSUFFICIENT_RESOURCES;
574
575 This->AddRef();
576
577 // return result
578 *OutHardware = (PUSBHARDWAREDEVICE)This;
579
580 return STATUS_SUCCESS;
581 }