[USB]
[reactos.git] / reactos / 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 }
424
425 //
426 // copy capabilities
427 //
428 RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &PDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
429 Status = STATUS_SUCCESS;
430 break;
431 }
432 case IRP_MN_QUERY_BUS_INFORMATION:
433 {
434 //
435 //
436 //
437 BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(NonPagedPool, sizeof(PNP_BUS_INFORMATION));
438
439 //
440 // fill in result
441 //
442 RtlCopyMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_HID, sizeof(GUID));
443 BusInformation->LegacyBusType = PNPBus;
444 BusInformation->BusNumber = 0; //FIXME
445
446 //
447 // store result
448 //
449 Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
450 Status = STATUS_SUCCESS;
451 break;
452 }
453 case IRP_MN_QUERY_PNP_DEVICE_STATE:
454 {
455 //
456 // FIXME set flags when driver fails / disabled
457 //
458 Status = STATUS_SUCCESS;
459 break;
460 }
461 case IRP_MN_QUERY_DEVICE_RELATIONS:
462 {
463 //
464 // only target relations are supported
465 //
466 if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
467 {
468 //
469 // not supported
470 //
471 Status = Irp->IoStatus.Status;
472 break;
473 }
474
475 //
476 // allocate device relations
477 //
478 DeviceRelation = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS));
479 if (!DeviceRelation)
480 {
481 //
482 // no memory
483 //
484 Status = STATUS_INSUFFICIENT_RESOURCES;
485 break;
486 }
487
488 //
489 // init device relation
490 //
491 DeviceRelation->Count = 1;
492 DeviceRelation->Objects[0] = DeviceObject;
493 ObReferenceObject(DeviceRelation->Objects[0]);
494
495 //
496 // store result
497 //
498 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation;
499 Status = STATUS_SUCCESS;
500 break;
501 }
502 case IRP_MN_START_DEVICE:
503 {
504 //
505 // FIXME: support polled devices
506 //
507 ASSERT(PDODeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE);
508
509 //
510 // now register the device interface
511 //
512 Status = IoRegisterDeviceInterface(PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject, &GUID_DEVINTERFACE_HID, NULL, &PDODeviceExtension->DeviceInterface);
513 DPRINT("[HIDCLASS] IoRegisterDeviceInterfaceState Status %x\n", Status);
514 if (NT_SUCCESS(Status))
515 {
516 //
517 // enable device interface
518 //
519 Status = IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, TRUE);
520 DPRINT("[HIDCLASS] IoSetDeviceInterFaceState %x\n", Status);
521 }
522
523 //
524 // done
525 //
526 Status = STATUS_SUCCESS;
527 break;
528 }
529 case IRP_MN_REMOVE_DEVICE:
530 {
531 /* Disable the device interface */
532 if (PDODeviceExtension->DeviceInterface.Length != 0)
533 IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, FALSE);
534
535 //
536 // remove us from the fdo's pdo list
537 //
538 bFound = FALSE;
539 for(Index = 0; Index < PDODeviceExtension->FDODeviceExtension->DeviceRelations->Count; Index++)
540 {
541 if (PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] == DeviceObject)
542 {
543 //
544 // remove us
545 //
546 bFound = TRUE;
547 PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] = NULL;
548 break;
549 }
550 }
551
552 /* Complete the IRP */
553 Irp->IoStatus.Status = STATUS_SUCCESS;
554 IoCompleteRequest(Irp, IO_NO_INCREMENT);
555
556 if (bFound)
557 {
558 /* Delete our device object*/
559 IoDeleteDevice(DeviceObject);
560 }
561
562 return STATUS_SUCCESS;
563 }
564 case IRP_MN_QUERY_INTERFACE:
565 {
566 DPRINT1("[HIDCLASS] PDO IRP_MN_QUERY_INTERFACE not implemented\n");
567 ASSERT(FALSE);
568
569 //
570 // do nothing
571 //
572 Status = Irp->IoStatus.Status;
573 break;
574 }
575 case IRP_MN_QUERY_REMOVE_DEVICE:
576 case IRP_MN_CANCEL_STOP_DEVICE:
577 case IRP_MN_QUERY_STOP_DEVICE:
578 case IRP_MN_CANCEL_REMOVE_DEVICE:
579 {
580 //
581 // no/op
582 //
583 Status = STATUS_SUCCESS;
584 break;
585 }
586 default:
587 {
588 //
589 // do nothing
590 //
591 Status = Irp->IoStatus.Status;
592 break;
593 }
594 }
595
596 //
597 // complete request
598 //
599 if (Status != STATUS_PENDING)
600 {
601 //
602 // store result
603 //
604 Irp->IoStatus.Status = Status;
605
606 //
607 // complete request
608 //
609 IoCompleteRequest(Irp, IO_NO_INCREMENT);
610 }
611
612 //
613 // done processing
614 //
615 return Status;
616 }
617
618 NTSTATUS
619 HidClassPDO_CreatePDO(
620 IN PDEVICE_OBJECT DeviceObject,
621 OUT PDEVICE_RELATIONS *OutDeviceRelations)
622 {
623 PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
624 NTSTATUS Status;
625 PDEVICE_OBJECT PDODeviceObject;
626 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
627 ULONG Index;
628 PDEVICE_RELATIONS DeviceRelations;
629 ULONG Length;
630
631 //
632 // get device extension
633 //
634 FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension;
635 ASSERT(FDODeviceExtension->Common.IsFDO);
636
637 //
638 // first allocate device relations
639 //
640 Length = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * FDODeviceExtension->Common.DeviceDescription.CollectionDescLength;
641 DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, Length);
642 if (!DeviceRelations)
643 {
644 //
645 // no memory
646 //
647 return STATUS_INSUFFICIENT_RESOURCES;
648 }
649
650 //
651 // zero device relations
652 //
653 RtlZeroMemory(DeviceRelations, Length);
654
655 //
656 // lets create a PDO for top level collection
657 //
658 Index = 0;
659 do
660 {
661 //
662 // lets create the device object
663 //
664 Status = IoCreateDevice(FDODeviceExtension->Common.DriverExtension->DriverObject, sizeof(HIDCLASS_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject);
665 if (!NT_SUCCESS(Status))
666 {
667 //
668 // failed to create device
669 //
670 DPRINT1("[HIDCLASS] Failed to create PDO %x\n", Status);
671 break;
672 }
673
674 //
675 // patch stack size
676 //
677 PDODeviceObject->StackSize = DeviceObject->StackSize + 1;
678
679 //
680 // get device extension
681 //
682 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
683
684 //
685 // init device extension
686 //
687 PDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension;
688 PDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject;
689 PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject;
690 PDODeviceExtension->Common.IsFDO = FALSE;
691 PDODeviceExtension->FDODeviceExtension = FDODeviceExtension;
692 PDODeviceExtension->FDODeviceObject = DeviceObject;
693 PDODeviceExtension->Common.DriverExtension = FDODeviceExtension->Common.DriverExtension;
694 PDODeviceExtension->CollectionNumber = FDODeviceExtension->Common.DeviceDescription.CollectionDesc[Index].CollectionNumber;
695
696 //
697 // copy device data
698 //
699 RtlCopyMemory(&PDODeviceExtension->Common.Attributes, &FDODeviceExtension->Common.Attributes, sizeof(HID_DEVICE_ATTRIBUTES));
700 RtlCopyMemory(&PDODeviceExtension->Common.DeviceDescription, &FDODeviceExtension->Common.DeviceDescription, sizeof(HIDP_DEVICE_DESC));
701 RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
702
703 //
704 // set device flags
705 //
706 PDODeviceObject->Flags |= DO_MAP_IO_BUFFER;
707
708 //
709 // device is initialized
710 //
711 PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
712
713 //
714 // store device object in device relations
715 //
716 DeviceRelations->Objects[Index] = PDODeviceObject;
717 DeviceRelations->Count++;
718
719 //
720 // move to next
721 //
722 Index++;
723
724 }while(Index < FDODeviceExtension->Common.DeviceDescription.CollectionDescLength);
725
726
727 //
728 // check if creating succeeded
729 //
730 if (!NT_SUCCESS(Status))
731 {
732 //
733 // failed
734 //
735 for(Index = 0; Index < DeviceRelations->Count; Index++)
736 {
737 //
738 // delete device
739 //
740 IoDeleteDevice(DeviceRelations->Objects[Index]);
741 }
742
743 //
744 // free device relations
745 //
746 ExFreePool(DeviceRelations);
747 return Status;
748 }
749
750 //
751 // store device relations
752 //
753 *OutDeviceRelations = DeviceRelations;
754
755 //
756 // done
757 //
758 return STATUS_SUCCESS;
759 }