[USBOHCI]
[reactos.git] / drivers / usb / usbohci / hcd_controller.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
14 class CHCDController : public IHCDController,
15 public IDispatchIrp
16 {
17 public:
18 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
19
20 STDMETHODIMP_(ULONG) AddRef()
21 {
22 InterlockedIncrement(&m_Ref);
23 return m_Ref;
24 }
25 STDMETHODIMP_(ULONG) Release()
26 {
27 InterlockedDecrement(&m_Ref);
28
29 if (!m_Ref)
30 {
31 delete this;
32 return 0;
33 }
34 return m_Ref;
35 }
36
37 // IHCDController interface functions
38 NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject);
39
40 // IDispatchIrp interface functions
41 NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
42 NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
43 NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
44
45 // local functions
46 NTSTATUS CreateFDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject);
47 NTSTATUS SetSymbolicLink(BOOLEAN Enable);
48
49 // constructor / destructor
50 CHCDController(IUnknown *OuterUnknown){}
51 virtual ~CHCDController(){}
52
53 protected:
54 LONG m_Ref;
55 PROOTHDCCONTROLLER m_RootController;
56 PDRIVER_OBJECT m_DriverObject;
57 PDEVICE_OBJECT m_PhysicalDeviceObject;
58 PDEVICE_OBJECT m_FunctionalDeviceObject;
59 PDEVICE_OBJECT m_NextDeviceObject;
60 PUSBHARDWAREDEVICE m_Hardware;
61 PHUBCONTROLLER m_HubController;
62 ULONG m_FDODeviceNumber;
63 };
64
65 //=================================================================================================
66 // COM
67 //
68 NTSTATUS
69 STDMETHODCALLTYPE
70 CHCDController::QueryInterface(
71 IN REFIID refiid,
72 OUT PVOID* Output)
73 {
74 return STATUS_UNSUCCESSFUL;
75 }
76
77 //-------------------------------------------------------------------------------------------------
78 NTSTATUS
79 CHCDController::Initialize(
80 IN PROOTHDCCONTROLLER RootHCDController,
81 IN PDRIVER_OBJECT DriverObject,
82 IN PDEVICE_OBJECT PhysicalDeviceObject)
83 {
84 NTSTATUS Status;
85 PCOMMON_DEVICE_EXTENSION DeviceExtension;
86
87 //
88 // create usb hardware
89 //
90 Status = CreateUSBHardware(&m_Hardware);
91 if (!NT_SUCCESS(Status))
92 {
93 //
94 // failed to create hardware object
95 //
96 DPRINT1("Failed to create hardware object\n");
97 return STATUS_INSUFFICIENT_RESOURCES;
98 }
99
100 //
101 // initialize members
102 //
103 m_DriverObject = DriverObject;
104 m_PhysicalDeviceObject = PhysicalDeviceObject;
105 m_RootController = RootHCDController;
106
107 //
108 // create FDO
109 //
110 Status = CreateFDO(m_DriverObject, &m_FunctionalDeviceObject);
111 if (!NT_SUCCESS(Status))
112 {
113 //
114 // failed to create PDO
115 //
116 return Status;
117 }
118
119 //
120 // now attach to device stack
121 //
122 m_NextDeviceObject = IoAttachDeviceToDeviceStack(m_FunctionalDeviceObject, m_PhysicalDeviceObject);
123 if (!m_NextDeviceObject)
124 {
125 //
126 // failed to attach to device stack
127 //
128 IoDeleteDevice(m_FunctionalDeviceObject);
129 m_FunctionalDeviceObject = 0;
130
131 return STATUS_NO_SUCH_DEVICE;
132 }
133
134 //
135 // initialize hardware object
136 //
137 Status = m_Hardware->Initialize(m_DriverObject, m_FunctionalDeviceObject, m_PhysicalDeviceObject, m_NextDeviceObject);
138 if (!NT_SUCCESS(Status))
139 {
140 DPRINT1("Failed to initialize hardware object %x\n", Status);
141
142 //
143 // failed to initialize hardware object, detach from device stack
144 //
145 IoDetachDevice(m_NextDeviceObject);
146
147 //
148 // now delete the device
149 //
150 IoDeleteDevice(m_FunctionalDeviceObject);
151
152 //
153 // nullify pointers :)
154 //
155 m_FunctionalDeviceObject = 0;
156 m_NextDeviceObject = 0;
157
158 return Status;
159 }
160
161
162 //
163 // set device flags
164 //
165 m_FunctionalDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
166
167
168 //
169 // get device extension
170 //
171 DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_FunctionalDeviceObject->DeviceExtension;
172 PC_ASSERT(DeviceExtension);
173
174 //
175 // initialize device extension
176 //
177 DeviceExtension->IsFDO = TRUE;
178 DeviceExtension->IsHub = FALSE;
179 DeviceExtension->Dispatcher = PDISPATCHIRP(this);
180
181 //
182 // device is initialized
183 //
184 m_FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
185
186
187 //
188 // is there a root controller
189 //
190 if (m_RootController)
191 {
192 //
193 // add reference
194 //
195 m_RootController->AddRef();
196
197 //
198 // register with controller
199 //
200 m_RootController->RegisterHCD(this);
201 }
202
203
204 //
205 // done
206 //
207 return STATUS_SUCCESS;
208 }
209
210 //-------------------------------------------------------------------------------------------------
211 NTSTATUS
212 CHCDController::HandleDeviceControl(
213 IN PDEVICE_OBJECT DeviceObject,
214 IN PIRP Irp)
215 {
216 PIO_STACK_LOCATION IoStack;
217 PCOMMON_DEVICE_EXTENSION DeviceExtension;
218 NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
219 PUSB_HCD_DRIVERKEY_NAME DriverKey;
220 ULONG ResultLength;
221
222 //
223 // get current stack location
224 //
225 IoStack = IoGetCurrentIrpStackLocation(Irp);
226
227 //
228 // get device extension
229 //
230 DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
231
232 //
233 // sanity check
234 //
235 PC_ASSERT(DeviceExtension->IsFDO);
236
237 DPRINT1("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu\n",
238 IoStack->Parameters.DeviceIoControl.IoControlCode,
239 IoStack->Parameters.DeviceIoControl.InputBufferLength,
240 IoStack->Parameters.DeviceIoControl.OutputBufferLength);
241
242 //
243 // perform ioctl for FDO
244 //
245 if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_HCD_DRIVERKEY_NAME)
246 {
247 //
248 // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME
249 //
250 if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME))
251 {
252 //
253 // get device property size
254 //
255 Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, 0, NULL, &ResultLength);
256
257 //
258 // get input buffer
259 //
260 DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer;
261
262 //
263 // check result
264 //
265 if (Status == STATUS_BUFFER_TOO_SMALL)
266 {
267 //
268 // does the caller provide enough buffer space
269 //
270 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= ResultLength)
271 {
272 //
273 // it does
274 //
275 Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength);
276
277 if (NT_SUCCESS(Status))
278 {
279 //
280 // informal debug print
281 //
282 DPRINT1("Result %S\n", DriverKey->DriverKeyName);
283 }
284 }
285
286 //
287 // store result
288 //
289 DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR);
290 Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
291 Status = STATUS_SUCCESS;
292 }
293 }
294 else
295 {
296 //
297 // buffer is certainly too small
298 //
299 Status = STATUS_BUFFER_OVERFLOW;
300 Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME);
301 }
302 }
303 else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_ROOT_HUB_NAME)
304 {
305 //
306 // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME
307 //
308 if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME))
309 {
310 //
311 // sanity check
312 //
313 PC_ASSERT(m_HubController);
314
315 //
316 // get input buffer
317 //
318 DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer;
319
320 //
321 // get symbolic link
322 //
323 Status = m_HubController->GetHubControllerSymbolicLink(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength);
324
325
326 if (NT_SUCCESS(Status))
327 {
328 //
329 // null terminate it
330 //
331 PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG) - sizeof(WCHAR) >= ResultLength);
332
333 DriverKey->DriverKeyName[ResultLength / sizeof(WCHAR)] = L'\0';
334 DPRINT1("Result %S\n", DriverKey->DriverKeyName);
335 }
336
337 //
338 // store result
339 //
340 DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR);
341 Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
342 Status = STATUS_SUCCESS;
343 }
344 else
345 {
346 //
347 // buffer is certainly too small
348 //
349 Status = STATUS_BUFFER_OVERFLOW;
350 Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME);
351 }
352 }
353
354 //
355 // complete the request
356 //
357 Irp->IoStatus.Status = Status;
358 IoCompleteRequest(Irp, IO_NO_INCREMENT);
359
360 //
361 // done
362 //
363 return Status;
364 }
365
366 NTSTATUS
367 CHCDController::HandlePnp(
368 IN PDEVICE_OBJECT DeviceObject,
369 IN PIRP Irp)
370 {
371 PIO_STACK_LOCATION IoStack;
372 PCOMMON_DEVICE_EXTENSION DeviceExtension;
373 PCM_RESOURCE_LIST RawResourceList;
374 PCM_RESOURCE_LIST TranslatedResourceList;
375 PDEVICE_RELATIONS DeviceRelations;
376 NTSTATUS Status;
377
378 //
379 // get device extension
380 //
381 DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
382
383 //
384 // sanity check
385 //
386 PC_ASSERT(DeviceExtension->IsFDO);
387
388 //
389 // get current stack location
390 //
391 IoStack = IoGetCurrentIrpStackLocation(Irp);
392
393 switch(IoStack->MinorFunction)
394 {
395 case IRP_MN_START_DEVICE:
396 {
397 DPRINT1("CHCDController::HandlePnp IRP_MN_START FDO\n");
398
399 //
400 // first start lower device object
401 //
402 Status = SyncForwardIrp(m_NextDeviceObject, Irp);
403
404 if (NT_SUCCESS(Status))
405 {
406 //
407 // operation succeeded, lets start the device
408 //
409 RawResourceList = IoStack->Parameters.StartDevice.AllocatedResources;
410 TranslatedResourceList = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated;
411
412 if (m_Hardware)
413 {
414 //
415 // start the hardware
416 //
417 Status = m_Hardware->PnpStart(RawResourceList, TranslatedResourceList);
418 }
419
420 //
421 // enable symbolic link
422 //
423 Status = SetSymbolicLink(TRUE);
424 }
425
426 DPRINT1("CHCDController::HandlePnp IRP_MN_START FDO: Status %x\n", Status);
427 break;
428 }
429 case IRP_MN_QUERY_DEVICE_RELATIONS:
430 {
431 DPRINT1("CHCDController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %lx\n", IoStack->Parameters.QueryDeviceRelations.Type);
432
433 if (m_HubController == NULL)
434 {
435 //
436 // create hub controller
437 //
438 Status = CreateHubController(&m_HubController);
439 if (!NT_SUCCESS(Status))
440 {
441 //
442 // failed to create hub controller
443 //
444 break;
445 }
446
447 //
448 // initialize hub controller
449 //
450 Status = m_HubController->Initialize(m_DriverObject, PHCDCONTROLLER(this), m_Hardware, TRUE, 0 /* FIXME*/);
451 if (!NT_SUCCESS(Status))
452 {
453 //
454 // failed to initialize hub controller
455 //
456 break;
457 }
458
459 //
460 // add reference to prevent it from getting deleting while hub driver adds / removes references
461 //
462 m_HubController->AddRef();
463 }
464
465 if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations)
466 {
467 //
468 // allocate device relations
469 //
470 DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
471
472 if (!DeviceRelations)
473 {
474 //
475 // no memory
476 //
477 Status = STATUS_INSUFFICIENT_RESOURCES;
478 break;
479 }
480
481 //
482 // init device relations
483 //
484 DeviceRelations->Count = 1;
485 Status = m_HubController->GetHubControllerDeviceObject(&DeviceRelations->Objects [0]);
486
487 //
488 // sanity check
489 //
490 PC_ASSERT(Status == STATUS_SUCCESS);
491
492 ObReferenceObject(DeviceRelations->Objects [0]);
493
494 //
495 // store result
496 //
497 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
498 Status = STATUS_SUCCESS;
499 }
500 else
501 {
502 //
503 // not supported
504 //
505 Status = STATUS_NOT_SUPPORTED;
506 }
507 break;
508 }
509 case IRP_MN_STOP_DEVICE:
510 {
511 DPRINT1("CHCDController::HandlePnp IRP_MN_STOP_DEVICE\n");
512
513 if (m_Hardware)
514 {
515 //
516 // stop the hardware
517 //
518 Status = m_Hardware->PnpStop();
519 }
520 else
521 {
522 //
523 // fake success
524 //
525 Status = STATUS_SUCCESS;
526 }
527
528 if (NT_SUCCESS(Status))
529 {
530 //
531 // stop lower device
532 //
533 Status = SyncForwardIrp(m_NextDeviceObject, Irp);
534 }
535 break;
536 }
537 case IRP_MN_REMOVE_DEVICE:
538 {
539 DPRINT1("CHCDController::HandlePnp IRP_MN_REMOVE_DEVICE FDO\n");
540
541 //
542 // detach device from device stack
543 //
544 IoDetachDevice(m_NextDeviceObject);
545
546 //
547 // delete device
548 //
549 IoDeleteDevice(m_FunctionalDeviceObject);
550
551 Status = STATUS_SUCCESS;
552 break;
553 }
554 default:
555 {
556 //
557 // forward irp to next device object
558 //
559 IoSkipCurrentIrpStackLocation(Irp);
560 return IoCallDriver(m_NextDeviceObject, Irp);
561 }
562 }
563
564 //
565 // store result and complete request
566 //
567 Irp->IoStatus.Status = Status;
568 IoCompleteRequest(Irp, IO_NO_INCREMENT);
569
570 return Status;
571 }
572
573 NTSTATUS
574 CHCDController::HandlePower(
575 IN PDEVICE_OBJECT DeviceObject,
576 IN PIRP Irp)
577 {
578 UNIMPLEMENTED
579
580 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
581 IoCompleteRequest(Irp, IO_NO_INCREMENT);
582
583 return STATUS_NOT_IMPLEMENTED;
584 }
585
586 NTSTATUS
587 CHCDController::CreateFDO(
588 PDRIVER_OBJECT DriverObject,
589 PDEVICE_OBJECT * OutDeviceObject)
590 {
591 WCHAR CharDeviceName[64];
592 NTSTATUS Status;
593 ULONG UsbDeviceNumber = 0;
594 UNICODE_STRING DeviceName;
595
596 while (TRUE)
597 {
598 //
599 // construct device name
600 //
601 swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber);
602
603 //
604 // initialize device name
605 //
606 RtlInitUnicodeString(&DeviceName, CharDeviceName);
607
608 //
609 // create device
610 //
611 Status = IoCreateDevice(DriverObject,
612 sizeof(COMMON_DEVICE_EXTENSION),
613 &DeviceName,
614 FILE_DEVICE_CONTROLLER,
615 0,
616 FALSE,
617 OutDeviceObject);
618
619 //
620 // check for success
621 //
622 if (NT_SUCCESS(Status))
623 break;
624
625 //
626 // is there a device object with that same name
627 //
628 if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION))
629 {
630 //
631 // Try the next name
632 //
633 UsbDeviceNumber++;
634 continue;
635 }
636
637 //
638 // bail out on other errors
639 //
640 if (!NT_SUCCESS(Status))
641 {
642 DPRINT1("CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status);
643 return Status;
644 }
645 }
646
647 //
648 // store FDO number
649 //
650 m_FDODeviceNumber = UsbDeviceNumber;
651
652 DPRINT1("CreateFDO: DeviceName %wZ\n", &DeviceName);
653
654 /* done */
655 return Status;
656 }
657
658 NTSTATUS
659 CHCDController::SetSymbolicLink(
660 BOOLEAN Enable)
661 {
662 NTSTATUS Status;
663 WCHAR LinkName[32];
664 WCHAR FDOName[32];
665 UNICODE_STRING Link, FDO;
666
667 if (Enable)
668 {
669 //
670 // create legacy link
671 //
672 swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber);
673 swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber);
674 RtlInitUnicodeString(&Link, LinkName);
675 RtlInitUnicodeString(&FDO, FDOName);
676
677 //
678 // create symbolic link
679 //
680 Status = IoCreateSymbolicLink(&Link, &FDO);
681
682 if (!NT_SUCCESS(Status))
683 {
684 //
685 // FIXME: handle me
686 //
687 ASSERT(0);
688 }
689 }
690 else
691 {
692 //
693 // create legacy link
694 //
695 swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber);
696 RtlInitUnicodeString(&Link, LinkName);
697
698 //
699 // now delete the symbolic link
700 //
701 Status = IoDeleteSymbolicLink(&Link);
702
703 if (!NT_SUCCESS(Status))
704 {
705 //
706 // FIXME: handle me
707 //
708 ASSERT(0);
709 }
710 }
711
712 //
713 // done
714 //
715 return Status;
716 }
717
718 NTSTATUS
719 CreateHCDController(
720 PHCDCONTROLLER *OutHcdController)
721 {
722 PHCDCONTROLLER This;
723
724 //
725 // allocate controller
726 //
727 This = new(NonPagedPool, TAG_USBOHCI) CHCDController(0);
728 if (!This)
729 {
730 //
731 // failed to allocate
732 //
733 return STATUS_INSUFFICIENT_RESOURCES;
734 }
735
736 //
737 // add reference count
738 //
739 This->AddRef();
740
741 //
742 // return result
743 //
744 *OutHcdController = (PHCDCONTROLLER)This;
745
746 //
747 // done
748 //
749 return STATUS_SUCCESS;
750 }