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