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