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