[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_QUERY_REMOVE_DEVICE:
538 case IRP_MN_QUERY_STOP_DEVICE:
539 {
540 //
541 // sure
542 //
543 Irp->IoStatus.Status = STATUS_SUCCESS;
544
545 //
546 // forward irp to next device object
547 //
548 IoSkipCurrentIrpStackLocation(Irp);
549 return IoCallDriver(m_NextDeviceObject, Irp);
550 }
551 case IRP_MN_REMOVE_DEVICE:
552 {
553 DPRINT1("CHCDController::HandlePnp IRP_MN_REMOVE_DEVICE FDO\n");
554
555 //
556 // delete the symbolic link
557 //
558 SetSymbolicLink(FALSE);
559
560 //
561 // forward irp to next device object
562 //
563 IoSkipCurrentIrpStackLocation(Irp);
564 IoCallDriver(m_NextDeviceObject, Irp);
565
566 //
567 // detach device from device stack
568 //
569 IoDetachDevice(m_NextDeviceObject);
570
571 //
572 // delete device
573 //
574 IoDeleteDevice(m_FunctionalDeviceObject);
575
576 return STATUS_SUCCESS;
577 }
578 default:
579 {
580 //
581 // forward irp to next device object
582 //
583 IoSkipCurrentIrpStackLocation(Irp);
584 return IoCallDriver(m_NextDeviceObject, Irp);
585 }
586 }
587
588 //
589 // store result and complete request
590 //
591 Irp->IoStatus.Status = Status;
592 IoCompleteRequest(Irp, IO_NO_INCREMENT);
593
594 return Status;
595 }
596
597 NTSTATUS
598 CHCDController::HandlePower(
599 IN PDEVICE_OBJECT DeviceObject,
600 IN PIRP Irp)
601 {
602 UNIMPLEMENTED
603
604 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
605 IoCompleteRequest(Irp, IO_NO_INCREMENT);
606
607 return STATUS_NOT_IMPLEMENTED;
608 }
609
610 NTSTATUS
611 CHCDController::CreateFDO(
612 PDRIVER_OBJECT DriverObject,
613 PDEVICE_OBJECT * OutDeviceObject)
614 {
615 WCHAR CharDeviceName[64];
616 NTSTATUS Status;
617 ULONG UsbDeviceNumber = 0;
618 UNICODE_STRING DeviceName;
619
620 while (TRUE)
621 {
622 //
623 // construct device name
624 //
625 swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber);
626
627 //
628 // initialize device name
629 //
630 RtlInitUnicodeString(&DeviceName, CharDeviceName);
631
632 //
633 // create device
634 //
635 Status = IoCreateDevice(DriverObject,
636 sizeof(COMMON_DEVICE_EXTENSION),
637 &DeviceName,
638 FILE_DEVICE_CONTROLLER,
639 0,
640 FALSE,
641 OutDeviceObject);
642
643 //
644 // check for success
645 //
646 if (NT_SUCCESS(Status))
647 break;
648
649 //
650 // is there a device object with that same name
651 //
652 if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION))
653 {
654 //
655 // Try the next name
656 //
657 UsbDeviceNumber++;
658 continue;
659 }
660
661 //
662 // bail out on other errors
663 //
664 if (!NT_SUCCESS(Status))
665 {
666 DPRINT1("CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status);
667 return Status;
668 }
669 }
670
671 //
672 // store FDO number
673 //
674 m_FDODeviceNumber = UsbDeviceNumber;
675
676 DPRINT1("CreateFDO: DeviceName %wZ\n", &DeviceName);
677
678 /* done */
679 return Status;
680 }
681
682 NTSTATUS
683 CHCDController::SetSymbolicLink(
684 BOOLEAN Enable)
685 {
686 NTSTATUS Status;
687 WCHAR LinkName[32];
688 WCHAR FDOName[32];
689 UNICODE_STRING Link, FDO;
690
691 if (Enable)
692 {
693 //
694 // create legacy link
695 //
696 swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber);
697 swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber);
698 RtlInitUnicodeString(&Link, LinkName);
699 RtlInitUnicodeString(&FDO, FDOName);
700
701 //
702 // create symbolic link
703 //
704 Status = IoCreateSymbolicLink(&Link, &FDO);
705
706 if (!NT_SUCCESS(Status))
707 {
708 //
709 // FIXME: handle me
710 //
711 ASSERT(0);
712 }
713 }
714 else
715 {
716 //
717 // create legacy link
718 //
719 swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber);
720 RtlInitUnicodeString(&Link, LinkName);
721
722 //
723 // now delete the symbolic link
724 //
725 Status = IoDeleteSymbolicLink(&Link);
726
727 if (!NT_SUCCESS(Status))
728 {
729 //
730 // FIXME: handle me
731 //
732 ASSERT(0);
733 }
734 }
735
736 //
737 // done
738 //
739 return Status;
740 }
741
742 NTSTATUS
743 CreateHCDController(
744 PHCDCONTROLLER *OutHcdController)
745 {
746 PHCDCONTROLLER This;
747
748 //
749 // allocate controller
750 //
751 This = new(NonPagedPool, TAG_USBOHCI) CHCDController(0);
752 if (!This)
753 {
754 //
755 // failed to allocate
756 //
757 return STATUS_INSUFFICIENT_RESOURCES;
758 }
759
760 //
761 // add reference count
762 //
763 This->AddRef();
764
765 //
766 // return result
767 //
768 *OutHcdController = (PHCDCONTROLLER)This;
769
770 //
771 // done
772 //
773 return STATUS_SUCCESS;
774 }