Sync with trunk r58033.
[reactos.git] / drivers / hid / hidclass / pdo.c
1 /*
2 * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/hid/hidclass/fdo.c
5 * PURPOSE: HID Class Driver
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10 #include "precomp.h"
11
12 PHIDP_COLLECTION_DESC
13 HidClassPDO_GetCollectionDescription(
14 PHIDP_DEVICE_DESC DeviceDescription,
15 ULONG CollectionNumber)
16 {
17 ULONG Index;
18
19 for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++)
20 {
21 if (DeviceDescription->CollectionDesc[Index].CollectionNumber == CollectionNumber)
22 {
23 //
24 // found collection
25 //
26 return &DeviceDescription->CollectionDesc[Index];
27 }
28 }
29
30 //
31 // failed to find collection
32 //
33 DPRINT1("[HIDCLASS] GetCollectionDescription CollectionNumber %x not found\n", CollectionNumber);
34 ASSERT(FALSE);
35 return NULL;
36 }
37
38 PHIDP_REPORT_IDS
39 HidClassPDO_GetReportDescription(
40 PHIDP_DEVICE_DESC DeviceDescription,
41 ULONG CollectionNumber)
42 {
43 ULONG Index;
44
45 for(Index = 0; Index < DeviceDescription->ReportIDsLength; Index++)
46 {
47 if (DeviceDescription->ReportIDs[Index].CollectionNumber == CollectionNumber)
48 {
49 //
50 // found collection
51 //
52 return &DeviceDescription->ReportIDs[Index];
53 }
54 }
55
56 //
57 // failed to find collection
58 //
59 DPRINT1("[HIDCLASS] GetReportDescription CollectionNumber %x not found\n", CollectionNumber);
60 ASSERT(FALSE);
61 return NULL;
62 }
63
64 NTSTATUS
65 HidClassPDO_HandleQueryDeviceId(
66 IN PDEVICE_OBJECT DeviceObject,
67 IN PIRP Irp)
68 {
69 NTSTATUS Status;
70 LPWSTR Buffer;
71 LPWSTR NewBuffer, Ptr;
72 ULONG Length;
73
74 //
75 // copy current stack location
76 //
77 IoCopyCurrentIrpStackLocationToNext(Irp);
78
79 //
80 // call mini-driver
81 //
82 Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
83 if (!NT_SUCCESS(Status))
84 {
85 //
86 // failed
87 //
88 return Status;
89 }
90
91 //
92 // get buffer
93 //
94 Buffer = (LPWSTR)Irp->IoStatus.Information;
95 Length = wcslen(Buffer);
96
97 //
98 // allocate new buffer
99 //
100 NewBuffer = (LPWSTR)ExAllocatePool(NonPagedPool, (Length + 1) * sizeof(WCHAR));
101 if (!NewBuffer)
102 {
103 //
104 // failed to allocate buffer
105 //
106 return STATUS_INSUFFICIENT_RESOURCES;
107 }
108
109 //
110 // replace bus
111 //
112 wcscpy(NewBuffer, L"HID\\");
113
114 //
115 // get offset to first '\\'
116 //
117 Ptr = wcschr(Buffer, L'\\');
118 if (Ptr)
119 {
120 //
121 // append result
122 //
123 wcscat(NewBuffer, Ptr + 1);
124 }
125
126 //
127 // free old buffer
128 //
129 ExFreePool(Buffer);
130
131 //
132 // store result
133 //
134 DPRINT("NewBuffer %S\n", NewBuffer);
135 Irp->IoStatus.Information = (ULONG_PTR)NewBuffer;
136 return STATUS_SUCCESS;
137 }
138
139 NTSTATUS
140 HidClassPDO_HandleQueryHardwareId(
141 IN PDEVICE_OBJECT DeviceObject,
142 IN PIRP Irp)
143 {
144 NTSTATUS Status;
145 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
146 WCHAR Buffer[200];
147 ULONG Offset = 0;
148 LPWSTR Ptr;
149 PHIDP_COLLECTION_DESC CollectionDescription;
150
151 //
152 // get device extension
153 //
154 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
155 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
156
157 //
158 // copy current stack location
159 //
160 IoCopyCurrentIrpStackLocationToNext(Irp);
161
162 //
163 // call mini-driver
164 //
165 Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
166 if (!NT_SUCCESS(Status))
167 {
168 //
169 // failed
170 //
171 return Status;
172 }
173
174 if (PDODeviceExtension->Common.DeviceDescription.CollectionDescLength > 1)
175 {
176 //
177 // multi-tlc device
178 //
179 Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber, PDODeviceExtension->CollectionNumber) + 1;
180 Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->CollectionNumber) + 1;
181 }
182 else
183 {
184 //
185 // single tlc device
186 //
187 Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber) + 1;
188 Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID) + 1;
189 }
190
191 //
192 // get collection description
193 //
194 CollectionDescription = HidClassPDO_GetCollectionDescription(&PDODeviceExtension->Common.DeviceDescription, PDODeviceExtension->CollectionNumber);
195 ASSERT(CollectionDescription);
196
197 if (CollectionDescription->UsagePage == HID_USAGE_PAGE_GENERIC)
198 {
199 switch(CollectionDescription->Usage)
200 {
201 case HID_USAGE_GENERIC_POINTER:
202 case HID_USAGE_GENERIC_MOUSE:
203 //
204 // Pointer / Mouse
205 //
206 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_MOUSE") + 1;
207 break;
208 case HID_USAGE_GENERIC_GAMEPAD:
209 case HID_USAGE_GENERIC_JOYSTICK:
210 //
211 // Joystick / Gamepad
212 //
213 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_GAME") + 1;
214 break;
215 case HID_USAGE_GENERIC_KEYBOARD:
216 case HID_USAGE_GENERIC_KEYPAD:
217 //
218 // Keyboard / Keypad
219 //
220 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_KEYBOARD") + 1;
221 break;
222 case HID_USAGE_GENERIC_SYSTEM_CTL:
223 //
224 // System Control
225 //
226 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONTROL") + 1;
227 break;
228 }
229 }
230 else if (CollectionDescription->UsagePage == HID_USAGE_PAGE_CONSUMER && CollectionDescription->Usage == HID_USAGE_CONSUMERCTRL)
231 {
232 //
233 // Consumer Audio Control
234 //
235 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONSUMER") + 1;
236 }
237
238 //
239 // add HID_DEVICE_UP:0001_U:0002'
240 //
241 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_UP:%04x_U:%04x", CollectionDescription->UsagePage, CollectionDescription->Usage) + 1;
242
243 //
244 // add HID
245 //
246 Offset +=swprintf(&Buffer[Offset], L"HID_DEVICE") + 1;
247
248 //
249 // free old buffer
250 //
251 ExFreePool((PVOID)Irp->IoStatus.Information);
252
253 //
254 // allocate buffer
255 //
256 Ptr = (LPWSTR)ExAllocatePool(NonPagedPool, (Offset +1)* sizeof(WCHAR));
257 if (!Ptr)
258 {
259 //
260 // no memory
261 //
262 Irp->IoStatus.Information = 0;
263 return STATUS_INSUFFICIENT_RESOURCES;
264 }
265
266 //
267 // copy buffer
268 //
269 RtlCopyMemory(Ptr, Buffer, Offset * sizeof(WCHAR));
270 Ptr[Offset] = UNICODE_NULL;
271
272 //
273 // store result
274 //
275 Irp->IoStatus.Information = (ULONG_PTR)Ptr;
276 return STATUS_SUCCESS;
277 }
278
279 NTSTATUS
280 HidClassPDO_HandleQueryInstanceId(
281 IN PDEVICE_OBJECT DeviceObject,
282 IN PIRP Irp)
283 {
284 LPWSTR Buffer;
285 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
286
287 //
288 // get device extension
289 //
290 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
291 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
292
293
294 //
295 // allocate buffer
296 //
297 Buffer = ExAllocatePool(NonPagedPool, 5 * sizeof(WCHAR));
298 if (!Buffer)
299 {
300 //
301 // failed
302 //
303 return STATUS_INSUFFICIENT_RESOURCES;
304 }
305
306 //
307 // write device id
308 //
309 swprintf(Buffer, L"%04x", PDODeviceExtension->CollectionNumber);
310 Irp->IoStatus.Information = (ULONG_PTR)Buffer;
311
312 //
313 // done
314 //
315 return STATUS_SUCCESS;
316 }
317
318 NTSTATUS
319 HidClassPDO_HandleQueryCompatibleId(
320 IN PDEVICE_OBJECT DeviceObject,
321 IN PIRP Irp)
322 {
323 LPWSTR Buffer;
324
325 Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
326 if (!Buffer)
327 {
328 //
329 // no memory
330 //
331 return STATUS_INSUFFICIENT_RESOURCES;
332 }
333
334 //
335 // zero buffer
336 //
337 Buffer[0] = 0;
338 Buffer[1] = 0;
339
340 //
341 // store result
342 //
343 Irp->IoStatus.Information = (ULONG_PTR)Buffer;
344 return STATUS_SUCCESS;
345 }
346
347 NTSTATUS
348 HidClassPDO_PnP(
349 IN PDEVICE_OBJECT DeviceObject,
350 IN PIRP Irp)
351 {
352 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
353 PIO_STACK_LOCATION IoStack;
354 NTSTATUS Status;
355 PPNP_BUS_INFORMATION BusInformation;
356 PDEVICE_RELATIONS DeviceRelation;
357 ULONG Index, bFound;
358
359 //
360 // get device extension
361 //
362 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
363 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
364
365 //
366 // get current irp stack location
367 //
368 IoStack = IoGetCurrentIrpStackLocation(Irp);
369
370 //
371 // handle request
372 //
373 switch(IoStack->MinorFunction)
374 {
375 case IRP_MN_QUERY_ID:
376 {
377 if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID)
378 {
379 //
380 // handle query device id
381 //
382 Status = HidClassPDO_HandleQueryDeviceId(DeviceObject, Irp);
383 break;
384 }
385 else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs)
386 {
387 //
388 // handle instance id
389 //
390 Status = HidClassPDO_HandleQueryHardwareId(DeviceObject, Irp);
391 break;
392 }
393 else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID)
394 {
395 //
396 // handle instance id
397 //
398 Status = HidClassPDO_HandleQueryInstanceId(DeviceObject, Irp);
399 break;
400 }
401 else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
402 {
403 //
404 // handle instance id
405 //
406 Status = HidClassPDO_HandleQueryCompatibleId(DeviceObject, Irp);
407 break;
408 }
409
410 DPRINT1("[HIDCLASS]: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType);
411 Status = STATUS_NOT_SUPPORTED;
412 Irp->IoStatus.Information = 0;
413 break;
414 }
415 case IRP_MN_QUERY_CAPABILITIES:
416 {
417 if (IoStack->Parameters.DeviceCapabilities.Capabilities == NULL)
418 {
419 //
420 // invalid request
421 //
422 Status = STATUS_DEVICE_CONFIGURATION_ERROR;
423 break;
424 }
425
426 //
427 // copy capabilities
428 //
429 RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &PDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
430 Status = STATUS_SUCCESS;
431 break;
432 }
433 case IRP_MN_QUERY_BUS_INFORMATION:
434 {
435 //
436 //
437 //
438 BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(NonPagedPool, sizeof(PNP_BUS_INFORMATION));
439
440 //
441 // fill in result
442 //
443 RtlCopyMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_HID, sizeof(GUID));
444 BusInformation->LegacyBusType = PNPBus;
445 BusInformation->BusNumber = 0; //FIXME
446
447 //
448 // store result
449 //
450 Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
451 Status = STATUS_SUCCESS;
452 break;
453 }
454 case IRP_MN_QUERY_PNP_DEVICE_STATE:
455 {
456 //
457 // FIXME set flags when driver fails / disabled
458 //
459 Status = STATUS_SUCCESS;
460 break;
461 }
462 case IRP_MN_QUERY_DEVICE_RELATIONS:
463 {
464 //
465 // only target relations are supported
466 //
467 if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
468 {
469 //
470 // not supported
471 //
472 Status = Irp->IoStatus.Status;
473 break;
474 }
475
476 //
477 // allocate device relations
478 //
479 DeviceRelation = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS));
480 if (!DeviceRelation)
481 {
482 //
483 // no memory
484 //
485 Status = STATUS_INSUFFICIENT_RESOURCES;
486 break;
487 }
488
489 //
490 // init device relation
491 //
492 DeviceRelation->Count = 1;
493 DeviceRelation->Objects[0] = DeviceObject;
494 ObReferenceObject(DeviceRelation->Objects[0]);
495
496 //
497 // store result
498 //
499 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation;
500 Status = STATUS_SUCCESS;
501 break;
502 }
503 case IRP_MN_START_DEVICE:
504 {
505 //
506 // FIXME: support polled devices
507 //
508 ASSERT(PDODeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE);
509
510 //
511 // now register the device interface
512 //
513 Status = IoRegisterDeviceInterface(PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject, &GUID_DEVINTERFACE_HID, NULL, &PDODeviceExtension->DeviceInterface);
514 DPRINT("[HIDCLASS] IoRegisterDeviceInterfaceState Status %x\n", Status);
515 if (NT_SUCCESS(Status))
516 {
517 //
518 // enable device interface
519 //
520 Status = IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, TRUE);
521 DPRINT("[HIDCLASS] IoSetDeviceInterFaceState %x\n", Status);
522 }
523
524 //
525 // done
526 //
527 Status = STATUS_SUCCESS;
528 break;
529 }
530 case IRP_MN_REMOVE_DEVICE:
531 {
532 /* Disable the device interface */
533 if (PDODeviceExtension->DeviceInterface.Length != 0)
534 IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, FALSE);
535
536 //
537 // remove us from the fdo's pdo list
538 //
539 bFound = FALSE;
540 for(Index = 0; Index < PDODeviceExtension->FDODeviceExtension->DeviceRelations->Count; Index++)
541 {
542 if (PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] == DeviceObject)
543 {
544 //
545 // remove us
546 //
547 bFound = TRUE;
548 PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] = NULL;
549 break;
550 }
551 }
552
553 /* Complete the IRP */
554 Irp->IoStatus.Status = STATUS_SUCCESS;
555 IoCompleteRequest(Irp, IO_NO_INCREMENT);
556
557 if (bFound)
558 {
559 /* Delete our device object*/
560 IoDeleteDevice(DeviceObject);
561 }
562
563 return STATUS_SUCCESS;
564 }
565 case IRP_MN_QUERY_INTERFACE:
566 {
567 DPRINT1("[HIDCLASS] PDO IRP_MN_QUERY_INTERFACE not implemented\n");
568 ASSERT(FALSE);
569
570 //
571 // do nothing
572 //
573 Status = Irp->IoStatus.Status;
574 break;
575 }
576 case IRP_MN_QUERY_REMOVE_DEVICE:
577 case IRP_MN_CANCEL_STOP_DEVICE:
578 case IRP_MN_QUERY_STOP_DEVICE:
579 case IRP_MN_CANCEL_REMOVE_DEVICE:
580 {
581 //
582 // no/op
583 //
584 #if 0
585 Status = STATUS_SUCCESS;
586 #else
587 DPRINT1("Denying removal of HID device due to IRP cancellation bugs\n");
588 Status = STATUS_UNSUCCESSFUL;
589 #endif
590 break;
591 }
592 default:
593 {
594 //
595 // do nothing
596 //
597 Status = Irp->IoStatus.Status;
598 break;
599 }
600 }
601
602 //
603 // complete request
604 //
605 if (Status != STATUS_PENDING)
606 {
607 //
608 // store result
609 //
610 Irp->IoStatus.Status = Status;
611
612 //
613 // complete request
614 //
615 IoCompleteRequest(Irp, IO_NO_INCREMENT);
616 }
617
618 //
619 // done processing
620 //
621 return Status;
622 }
623
624 NTSTATUS
625 HidClassPDO_CreatePDO(
626 IN PDEVICE_OBJECT DeviceObject,
627 OUT PDEVICE_RELATIONS *OutDeviceRelations)
628 {
629 PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
630 NTSTATUS Status;
631 PDEVICE_OBJECT PDODeviceObject;
632 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
633 ULONG Index;
634 PDEVICE_RELATIONS DeviceRelations;
635 ULONG Length;
636
637 //
638 // get device extension
639 //
640 FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension;
641 ASSERT(FDODeviceExtension->Common.IsFDO);
642
643 //
644 // first allocate device relations
645 //
646 Length = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * FDODeviceExtension->Common.DeviceDescription.CollectionDescLength;
647 DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, Length);
648 if (!DeviceRelations)
649 {
650 //
651 // no memory
652 //
653 return STATUS_INSUFFICIENT_RESOURCES;
654 }
655
656 //
657 // zero device relations
658 //
659 RtlZeroMemory(DeviceRelations, Length);
660
661 //
662 // lets create a PDO for top level collection
663 //
664 Index = 0;
665 do
666 {
667 //
668 // lets create the device object
669 //
670 Status = IoCreateDevice(FDODeviceExtension->Common.DriverExtension->DriverObject, sizeof(HIDCLASS_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject);
671 if (!NT_SUCCESS(Status))
672 {
673 //
674 // failed to create device
675 //
676 DPRINT1("[HIDCLASS] Failed to create PDO %x\n", Status);
677 break;
678 }
679
680 //
681 // patch stack size
682 //
683 PDODeviceObject->StackSize = DeviceObject->StackSize + 1;
684
685 //
686 // get device extension
687 //
688 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
689
690 //
691 // init device extension
692 //
693 PDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension;
694 PDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject;
695 PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject;
696 PDODeviceExtension->Common.IsFDO = FALSE;
697 PDODeviceExtension->FDODeviceExtension = FDODeviceExtension;
698 PDODeviceExtension->FDODeviceObject = DeviceObject;
699 PDODeviceExtension->Common.DriverExtension = FDODeviceExtension->Common.DriverExtension;
700 PDODeviceExtension->CollectionNumber = FDODeviceExtension->Common.DeviceDescription.CollectionDesc[Index].CollectionNumber;
701
702 //
703 // copy device data
704 //
705 RtlCopyMemory(&PDODeviceExtension->Common.Attributes, &FDODeviceExtension->Common.Attributes, sizeof(HID_DEVICE_ATTRIBUTES));
706 RtlCopyMemory(&PDODeviceExtension->Common.DeviceDescription, &FDODeviceExtension->Common.DeviceDescription, sizeof(HIDP_DEVICE_DESC));
707 RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
708
709 //
710 // set device flags
711 //
712 PDODeviceObject->Flags |= DO_MAP_IO_BUFFER;
713
714 //
715 // device is initialized
716 //
717 PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
718
719 //
720 // store device object in device relations
721 //
722 DeviceRelations->Objects[Index] = PDODeviceObject;
723 DeviceRelations->Count++;
724
725 //
726 // move to next
727 //
728 Index++;
729
730 }while(Index < FDODeviceExtension->Common.DeviceDescription.CollectionDescLength);
731
732
733 //
734 // check if creating succeeded
735 //
736 if (!NT_SUCCESS(Status))
737 {
738 //
739 // failed
740 //
741 for(Index = 0; Index < DeviceRelations->Count; Index++)
742 {
743 //
744 // delete device
745 //
746 IoDeleteDevice(DeviceRelations->Objects[Index]);
747 }
748
749 //
750 // free device relations
751 //
752 ExFreePool(DeviceRelations);
753 return Status;
754 }
755
756 //
757 // store device relations
758 //
759 *OutDeviceRelations = DeviceRelations;
760
761 //
762 // done
763 //
764 return STATUS_SUCCESS;
765 }