xref: /reactos/drivers/hid/hidclass/hidclass.c (revision 84ccccab)
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/hidclass.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 #define NDEBUG
14 #include <debug.h>
15 
16 static LPWSTR ClientIdentificationAddress = L"HIDCLASS";
17 static ULONG HidClassDeviceNumber = 0;
18 
19 NTSTATUS
20 NTAPI
21 DllInitialize(
22     IN PUNICODE_STRING RegistryPath)
23 {
24     return STATUS_SUCCESS;
25 }
26 
27 NTSTATUS
28 NTAPI
29 DllUnload(VOID)
30 {
31     return STATUS_SUCCESS;
32 }
33 
34 NTSTATUS
35 NTAPI
36 HidClassAddDevice(
37     IN PDRIVER_OBJECT DriverObject,
38     IN PDEVICE_OBJECT PhysicalDeviceObject)
39 {
40     WCHAR CharDeviceName[64];
41     NTSTATUS Status;
42     UNICODE_STRING DeviceName;
43     PDEVICE_OBJECT NewDeviceObject;
44     PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
45     ULONG DeviceExtensionSize;
46     PHIDCLASS_DRIVER_EXTENSION DriverExtension;
47 
48     /* increment device number */
49     InterlockedIncrement((PLONG)&HidClassDeviceNumber);
50 
51     /* construct device name */
52     swprintf(CharDeviceName, L"\\Device\\_HID%08x", HidClassDeviceNumber);
53 
54     /* initialize device name */
55     RtlInitUnicodeString(&DeviceName, CharDeviceName);
56 
57     /* get driver object extension */
58     DriverExtension = IoGetDriverObjectExtension(DriverObject, ClientIdentificationAddress);
59     if (!DriverExtension)
60     {
61         /* device removed */
62         ASSERT(FALSE);
63         return STATUS_DEVICE_CONFIGURATION_ERROR;
64     }
65 
66     /* calculate device extension size */
67     DeviceExtensionSize = sizeof(HIDCLASS_FDO_EXTENSION) + DriverExtension->DeviceExtensionSize;
68 
69     /* now create the device */
70     Status = IoCreateDevice(DriverObject, DeviceExtensionSize, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &NewDeviceObject);
71     if (!NT_SUCCESS(Status))
72     {
73         /* failed to create device object */
74         ASSERT(FALSE);
75         return Status;
76     }
77 
78     /* get device extension */
79     FDODeviceExtension = NewDeviceObject->DeviceExtension;
80 
81     /* zero device extension */
82     RtlZeroMemory(FDODeviceExtension, sizeof(HIDCLASS_FDO_EXTENSION));
83 
84     /* initialize device extension */
85     FDODeviceExtension->Common.IsFDO = TRUE;
86     FDODeviceExtension->Common.DriverExtension = DriverExtension;
87     FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = PhysicalDeviceObject;
88     FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = (PVOID)((ULONG_PTR)FDODeviceExtension + sizeof(HIDCLASS_FDO_EXTENSION));
89     FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = IoAttachDeviceToDeviceStack(NewDeviceObject, PhysicalDeviceObject);
90     if (FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject == NULL)
91     {
92         /* no PDO */
93         IoDeleteDevice(NewDeviceObject);
94         DPRINT1("[HIDCLASS] failed to attach to device stack\n");
95         return STATUS_DEVICE_REMOVED;
96     }
97 
98     /* sanity check */
99     ASSERT(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject);
100 
101     /* increment stack size */
102     NewDeviceObject->StackSize++;
103 
104     /* init device object */
105     NewDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
106     NewDeviceObject->Flags  &= ~DO_DEVICE_INITIALIZING;
107 
108     /* now call driver provided add device routine */
109     ASSERT(DriverExtension->AddDevice != 0);
110     Status = DriverExtension->AddDevice(DriverObject, NewDeviceObject);
111     if (!NT_SUCCESS(Status))
112     {
113         /* failed */
114         DPRINT1("HIDCLASS: AddDevice failed with %x\n", Status);
115         IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject);
116         IoDeleteDevice(NewDeviceObject);
117         return Status;
118     }
119 
120     /* succeeded */
121     return Status;
122 }
123 
124 VOID
125 NTAPI
126 HidClassDriverUnload(
127     IN PDRIVER_OBJECT DriverObject)
128 {
129     UNIMPLEMENTED;
130 }
131 
132 NTSTATUS
133 NTAPI
134 HidClass_Create(
135     IN PDEVICE_OBJECT DeviceObject,
136     IN PIRP Irp)
137 {
138     PIO_STACK_LOCATION IoStack;
139     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
140     PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
141     PHIDCLASS_FILEOP_CONTEXT Context;
142 
143     //
144     // get device extension
145     //
146     CommonDeviceExtension = DeviceObject->DeviceExtension;
147     if (CommonDeviceExtension->IsFDO)
148     {
149          //
150          // only supported for PDO
151          //
152          Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
153          IoCompleteRequest(Irp, IO_NO_INCREMENT);
154          return STATUS_UNSUCCESSFUL;
155     }
156 
157     //
158     // must be a PDO
159     //
160     ASSERT(CommonDeviceExtension->IsFDO == FALSE);
161 
162     //
163     // get device extension
164     //
165     PDODeviceExtension = DeviceObject->DeviceExtension;
166 
167     //
168     // get stack location
169     //
170     IoStack = IoGetCurrentIrpStackLocation(Irp);
171 
172     DPRINT("ShareAccess %x\n", IoStack->Parameters.Create.ShareAccess);
173     DPRINT("Options %x\n", IoStack->Parameters.Create.Options);
174     DPRINT("DesiredAccess %x\n", IoStack->Parameters.Create.SecurityContext->DesiredAccess);
175 
176     //
177     // allocate context
178     //
179     Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(HIDCLASS_FILEOP_CONTEXT), HIDCLASS_TAG);
180     if (!Context)
181     {
182         //
183         // no memory
184         //
185         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
186         IoCompleteRequest(Irp, IO_NO_INCREMENT);
187         return STATUS_INSUFFICIENT_RESOURCES;
188     }
189 
190     //
191     // init context
192     //
193     RtlZeroMemory(Context, sizeof(HIDCLASS_FILEOP_CONTEXT));
194     Context->DeviceExtension = PDODeviceExtension;
195     KeInitializeSpinLock(&Context->Lock);
196     InitializeListHead(&Context->ReadPendingIrpListHead);
197     InitializeListHead(&Context->IrpCompletedListHead);
198     KeInitializeEvent(&Context->IrpReadComplete, NotificationEvent, FALSE);
199 
200     //
201     // store context
202     //
203     ASSERT(IoStack->FileObject);
204     IoStack->FileObject->FsContext = Context;
205 
206     //
207     // done
208     //
209     Irp->IoStatus.Status = STATUS_SUCCESS;
210     IoCompleteRequest(Irp, IO_NO_INCREMENT);
211     return STATUS_SUCCESS;
212 }
213 
214 NTSTATUS
215 NTAPI
216 HidClass_Close(
217     IN PDEVICE_OBJECT DeviceObject,
218     IN PIRP Irp)
219 {
220     PIO_STACK_LOCATION IoStack;
221     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
222     PHIDCLASS_FILEOP_CONTEXT IrpContext;
223     BOOLEAN IsRequestPending = FALSE;
224     KIRQL OldLevel;
225     PLIST_ENTRY Entry;
226     PIRP ListIrp;
227 
228     //
229     // get device extension
230     //
231     CommonDeviceExtension = DeviceObject->DeviceExtension;
232 
233     //
234     // is it a FDO request
235     //
236     if (CommonDeviceExtension->IsFDO)
237     {
238         //
239         // how did the request get there
240         //
241         Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1;
242         IoCompleteRequest(Irp, IO_NO_INCREMENT);
243         return STATUS_INVALID_PARAMETER_1;
244     }
245 
246     //
247     // get stack location
248     //
249     IoStack = IoGetCurrentIrpStackLocation(Irp);
250 
251     //
252     // sanity checks
253     //
254     ASSERT(IoStack->FileObject);
255     ASSERT(IoStack->FileObject->FsContext);
256 
257     //
258     // get irp context
259     //
260     IrpContext = IoStack->FileObject->FsContext;
261     ASSERT(IrpContext);
262 
263     //
264     // acquire lock
265     //
266     KeAcquireSpinLock(&IrpContext->Lock, &OldLevel);
267 
268     if (!IsListEmpty(&IrpContext->ReadPendingIrpListHead))
269     {
270         //
271         // FIXME cancel irp
272         //
273         IsRequestPending = TRUE;
274     }
275 
276     //
277     // signal stop
278     //
279     IrpContext->StopInProgress = TRUE;
280 
281     //
282     // release lock
283     //
284     KeReleaseSpinLock(&IrpContext->Lock, OldLevel);
285 
286     if (IsRequestPending)
287     {
288         //
289         // wait for request to complete
290         //
291         DPRINT1("[HIDCLASS] Waiting for read irp completion...\n");
292         KeWaitForSingleObject(&IrpContext->IrpReadComplete, Executive, KernelMode, FALSE, NULL);
293     }
294 
295     //
296     // acquire lock
297     //
298     KeAcquireSpinLock(&IrpContext->Lock, &OldLevel);
299 
300     //
301     // sanity check
302     //
303     ASSERT(IsListEmpty(&IrpContext->ReadPendingIrpListHead));
304 
305     //
306     // now free all irps
307     //
308     while (!IsListEmpty(&IrpContext->IrpCompletedListHead))
309     {
310         //
311         // remove head irp
312         //
313         Entry = RemoveHeadList(&IrpContext->IrpCompletedListHead);
314 
315         //
316         // get irp
317         //
318         ListIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
319 
320         //
321         // free the irp
322         //
323         IoFreeIrp(ListIrp);
324     }
325 
326     //
327     // release lock
328     //
329     KeReleaseSpinLock(&IrpContext->Lock, OldLevel);
330 
331     //
332     // remove context
333     //
334     IoStack->FileObject->FsContext = NULL;
335 
336     //
337     // free context
338     //
339     ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
340 
341     //
342     // complete request
343     //
344     Irp->IoStatus.Status = STATUS_SUCCESS;
345     IoCompleteRequest(Irp, IO_NO_INCREMENT);
346     return STATUS_SUCCESS;
347 }
348 
349 NTSTATUS
350 NTAPI
351 HidClass_ReadCompleteIrp(
352     IN PDEVICE_OBJECT DeviceObject,
353     IN PIRP Irp,
354     IN PVOID Ctx)
355 {
356     PHIDCLASS_IRP_CONTEXT IrpContext;
357     KIRQL OldLevel;
358     PUCHAR Address;
359     ULONG Offset;
360     PHIDP_COLLECTION_DESC CollectionDescription;
361     PHIDP_REPORT_IDS ReportDescription;
362     BOOLEAN IsEmpty;
363 
364     //
365     // get irp context
366     //
367     IrpContext = Ctx;
368 
369     DPRINT("HidClass_ReadCompleteIrp Irql %lu\n", KeGetCurrentIrql());
370     DPRINT("HidClass_ReadCompleteIrp Status %lx\n", Irp->IoStatus.Status);
371     DPRINT("HidClass_ReadCompleteIrp Length %lu\n", Irp->IoStatus.Information);
372     DPRINT("HidClass_ReadCompleteIrp Irp %p\n", Irp);
373     DPRINT("HidClass_ReadCompleteIrp InputReportBuffer %p\n", IrpContext->InputReportBuffer);
374     DPRINT("HidClass_ReadCompleteIrp InputReportBufferLength %li\n", IrpContext->InputReportBufferLength);
375     DPRINT("HidClass_ReadCompleteIrp OriginalIrp %p\n", IrpContext->OriginalIrp);
376 
377     //
378     // copy result
379     //
380     if (Irp->IoStatus.Information)
381     {
382         //
383         // get address
384         //
385         Address = MmGetSystemAddressForMdlSafe(IrpContext->OriginalIrp->MdlAddress, NormalPagePriority);
386         if (Address)
387         {
388             //
389             // reports may have a report id prepended
390             //
391             Offset = 0;
392 
393             //
394             // get collection description
395             //
396             CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
397                                                                          IrpContext->FileOp->DeviceExtension->CollectionNumber);
398             ASSERT(CollectionDescription);
399 
400             //
401             // get report description
402             //
403             ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
404                                                                  IrpContext->FileOp->DeviceExtension->CollectionNumber);
405             ASSERT(ReportDescription);
406 
407             if (CollectionDescription && ReportDescription)
408             {
409                 //
410                 // calculate offset
411                 //
412                 ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength);
413                 Offset = CollectionDescription->InputLength - ReportDescription->InputLength;
414             }
415 
416             //
417             // copy result
418             //
419             RtlCopyMemory(&Address[Offset], IrpContext->InputReportBuffer, IrpContext->InputReportBufferLength);
420         }
421     }
422 
423     //
424     // copy result status
425     //
426     IrpContext->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status;
427     IrpContext->OriginalIrp->IoStatus.Information = Irp->IoStatus.Information;
428 
429     //
430     // free input report buffer
431     //
432     ExFreePoolWithTag(IrpContext->InputReportBuffer, HIDCLASS_TAG);
433 
434     //
435     // remove us from pending list
436     //
437     KeAcquireSpinLock(&IrpContext->FileOp->Lock, &OldLevel);
438 
439     //
440     // remove from pending list
441     //
442     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
443 
444     //
445     // is list empty
446     //
447     IsEmpty = IsListEmpty(&IrpContext->FileOp->ReadPendingIrpListHead);
448 
449     //
450     // insert into completed list
451     //
452     InsertTailList(&IrpContext->FileOp->IrpCompletedListHead, &Irp->Tail.Overlay.ListEntry);
453 
454     //
455     // release lock
456     //
457     KeReleaseSpinLock(&IrpContext->FileOp->Lock, OldLevel);
458 
459     //
460     // complete original request
461     //
462     IoCompleteRequest(IrpContext->OriginalIrp, IO_NO_INCREMENT);
463 
464 
465     DPRINT("StopInProgress %x IsEmpty %x\n", IrpContext->FileOp->StopInProgress, IsEmpty);
466     if (IrpContext->FileOp->StopInProgress && IsEmpty)
467     {
468         //
469         // last pending irp
470         //
471         DPRINT1("[HIDCLASS] LastPendingTransfer Signalling\n");
472         KeSetEvent(&IrpContext->FileOp->IrpReadComplete, 0, FALSE);
473     }
474 
475     if (IrpContext->FileOp->StopInProgress && IsEmpty)
476     {
477         //
478         // last pending irp
479         //
480         DPRINT1("[HIDCLASS] LastPendingTransfer Signalling\n");
481         KeSetEvent(&IrpContext->FileOp->IrpReadComplete, 0, FALSE);
482     }
483 
484     //
485     // free irp context
486     //
487     ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
488 
489     //
490     // done
491     //
492     return STATUS_MORE_PROCESSING_REQUIRED;
493 }
494 
495 PIRP
496 HidClass_GetIrp(
497     IN PHIDCLASS_FILEOP_CONTEXT Context)
498 {
499    KIRQL OldLevel;
500    PIRP Irp = NULL;
501    PLIST_ENTRY ListEntry;
502 
503     //
504     // acquire lock
505     //
506     KeAcquireSpinLock(&Context->Lock, &OldLevel);
507 
508     //
509     // is list empty?
510     //
511     if (!IsListEmpty(&Context->IrpCompletedListHead))
512     {
513         //
514         // grab first entry
515         //
516         ListEntry = RemoveHeadList(&Context->IrpCompletedListHead);
517 
518         //
519         // get irp
520         //
521         Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
522     }
523 
524     //
525     // release lock
526     //
527     KeReleaseSpinLock(&Context->Lock, OldLevel);
528 
529     //
530     // done
531     //
532     return Irp;
533 }
534 
535 NTSTATUS
536 HidClass_BuildIrp(
537     IN PDEVICE_OBJECT DeviceObject,
538     IN PIRP RequestIrp,
539     IN PHIDCLASS_FILEOP_CONTEXT Context,
540     IN ULONG DeviceIoControlCode,
541     IN ULONG BufferLength,
542     OUT PIRP *OutIrp,
543     OUT PHIDCLASS_IRP_CONTEXT *OutIrpContext)
544 {
545     PIRP Irp;
546     PIO_STACK_LOCATION IoStack;
547     PHIDCLASS_IRP_CONTEXT IrpContext;
548     PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
549     PHIDP_COLLECTION_DESC CollectionDescription;
550     PHIDP_REPORT_IDS ReportDescription;
551 
552     //
553     // get an irp from fresh list
554     //
555     Irp = HidClass_GetIrp(Context);
556     if (!Irp)
557     {
558         //
559         // build new irp
560         //
561         Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
562         if (!Irp)
563         {
564             //
565             // no memory
566             //
567             return STATUS_INSUFFICIENT_RESOURCES;
568         }
569     }
570     else
571     {
572         //
573         // re-use irp
574         //
575         IoReuseIrp(Irp, STATUS_SUCCESS);
576     }
577 
578     //
579     // allocate completion context
580     //
581     IrpContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(HIDCLASS_IRP_CONTEXT), HIDCLASS_TAG);
582     if (!IrpContext)
583     {
584         //
585         // no memory
586         //
587         IoFreeIrp(Irp);
588         return STATUS_INSUFFICIENT_RESOURCES;
589     }
590 
591     //
592     // get device extension
593     //
594     PDODeviceExtension = DeviceObject->DeviceExtension;
595     ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
596 
597     //
598     // init irp context
599     //
600     RtlZeroMemory(IrpContext, sizeof(HIDCLASS_IRP_CONTEXT));
601     IrpContext->OriginalIrp = RequestIrp;
602     IrpContext->FileOp = Context;
603 
604     //
605     // get collection description
606     //
607     CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
608                                                                  IrpContext->FileOp->DeviceExtension->CollectionNumber);
609     ASSERT(CollectionDescription);
610 
611     //
612     // get report description
613     //
614     ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription,
615                                                          IrpContext->FileOp->DeviceExtension->CollectionNumber);
616     ASSERT(ReportDescription);
617 
618     //
619     // sanity check
620     //
621     ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength);
622 
623     if (Context->StopInProgress)
624     {
625          //
626          // stop in progress
627          //
628          DPRINT1("[HIDCLASS] Stop In Progress\n");
629          Irp->IoStatus.Status = STATUS_CANCELLED;
630          IoCompleteRequest(Irp, IO_NO_INCREMENT);
631          return STATUS_CANCELLED;
632 
633     }
634 
635     //
636     // store report length
637     //
638     IrpContext->InputReportBufferLength = ReportDescription->InputLength;
639 
640     //
641     // allocate buffer
642     //
643     IrpContext->InputReportBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpContext->InputReportBufferLength, HIDCLASS_TAG);
644     if (!IrpContext->InputReportBuffer)
645     {
646         //
647         // no memory
648         //
649         IoFreeIrp(Irp);
650         ExFreePoolWithTag(IrpContext, HIDCLASS_TAG);
651         return STATUS_INSUFFICIENT_RESOURCES;
652     }
653 
654     //
655     // get stack location
656     //
657     IoStack = IoGetNextIrpStackLocation(Irp);
658 
659     //
660     // init stack location
661     //
662     IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
663     IoStack->Parameters.DeviceIoControl.IoControlCode = DeviceIoControlCode;
664     IoStack->Parameters.DeviceIoControl.OutputBufferLength = IrpContext->InputReportBufferLength;
665     IoStack->Parameters.DeviceIoControl.InputBufferLength = 0;
666     IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
667     Irp->UserBuffer = IrpContext->InputReportBuffer;
668     IoStack->DeviceObject = DeviceObject;
669 
670     //
671     // store result
672     //
673     *OutIrp = Irp;
674     *OutIrpContext = IrpContext;
675 
676     //
677     // done
678     //
679     return STATUS_SUCCESS;
680 }
681 
682 NTSTATUS
683 NTAPI
684 HidClass_Read(
685     IN PDEVICE_OBJECT DeviceObject,
686     IN PIRP Irp)
687 {
688     PIO_STACK_LOCATION IoStack;
689     PHIDCLASS_FILEOP_CONTEXT Context;
690     KIRQL OldLevel;
691     NTSTATUS Status;
692     PIRP NewIrp;
693     PHIDCLASS_IRP_CONTEXT NewIrpContext;
694     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
695 
696     //
697     // get current stack location
698     //
699     IoStack = IoGetCurrentIrpStackLocation(Irp);
700 
701     //
702     // get device extension
703     //
704     CommonDeviceExtension = DeviceObject->DeviceExtension;
705     ASSERT(CommonDeviceExtension->IsFDO == FALSE);
706 
707     //
708     // sanity check
709     //
710     ASSERT(IoStack->FileObject);
711     ASSERT(IoStack->FileObject->FsContext);
712 
713     //
714     // get context
715     //
716     Context = IoStack->FileObject->FsContext;
717     ASSERT(Context);
718 
719     //
720     // FIXME support polled devices
721     //
722     ASSERT(Context->DeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE);
723 
724     if (Context->StopInProgress)
725     {
726         //
727         // stop in progress
728         //
729         DPRINT1("[HIDCLASS] Stop In Progress\n");
730         Irp->IoStatus.Status = STATUS_CANCELLED;
731         IoCompleteRequest(Irp, IO_NO_INCREMENT);
732         return STATUS_CANCELLED;
733     }
734 
735     //
736     // build irp request
737     //
738     Status = HidClass_BuildIrp(DeviceObject,
739                                Irp,
740                                Context,
741                                IOCTL_HID_READ_REPORT,
742                                IoStack->Parameters.Read.Length,
743                                &NewIrp,
744                                &NewIrpContext);
745     if (!NT_SUCCESS(Status))
746     {
747         //
748         // failed
749         //
750         DPRINT1("HidClass_BuildIrp failed with %x\n", Status);
751         Irp->IoStatus.Status = Status;
752         IoCompleteRequest(Irp, IO_NO_INCREMENT);
753         return Status;
754     }
755 
756     //
757     // acquire lock
758     //
759     KeAcquireSpinLock(&Context->Lock, &OldLevel);
760 
761     //
762     // insert irp into pending list
763     //
764     InsertTailList(&Context->ReadPendingIrpListHead, &NewIrp->Tail.Overlay.ListEntry);
765 
766     //
767     // set completion routine
768     //
769     IoSetCompletionRoutine(NewIrp, HidClass_ReadCompleteIrp, NewIrpContext, TRUE, TRUE, TRUE);
770 
771     //
772     // make next location current
773     //
774     IoSetNextIrpStackLocation(NewIrp);
775 
776     //
777     // release spin lock
778     //
779     KeReleaseSpinLock(&Context->Lock, OldLevel);
780 
781     //
782     // mark irp pending
783     //
784     IoMarkIrpPending(Irp);
785 
786     //
787     // let's dispatch the request
788     //
789     ASSERT(Context->DeviceExtension);
790     Status = Context->DeviceExtension->Common.DriverExtension->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL](Context->DeviceExtension->FDODeviceObject, NewIrp);
791 
792     //
793     // complete
794     //
795     return STATUS_PENDING;
796 }
797 
798 NTSTATUS
799 NTAPI
800 HidClass_Write(
801     IN PDEVICE_OBJECT DeviceObject,
802     IN PIRP Irp)
803 {
804     UNIMPLEMENTED;
805     ASSERT(FALSE);
806     Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
807     IoCompleteRequest(Irp, IO_NO_INCREMENT);
808     return STATUS_NOT_IMPLEMENTED;
809 }
810 
811 NTSTATUS
812 NTAPI
813 HidClass_DeviceControl(
814     IN PDEVICE_OBJECT DeviceObject,
815     IN PIRP Irp)
816 {
817     PIO_STACK_LOCATION IoStack;
818     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
819     PHID_COLLECTION_INFORMATION CollectionInformation;
820     PHIDP_COLLECTION_DESC CollectionDescription;
821     PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
822 
823     //
824     // get device extension
825     //
826     CommonDeviceExtension = DeviceObject->DeviceExtension;
827 
828     //
829     // only PDO are supported
830     //
831     if (CommonDeviceExtension->IsFDO)
832     {
833         //
834         // invalid request
835         //
836         DPRINT1("[HIDCLASS] DeviceControl Irp for FDO arrived\n");
837         Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1;
838         IoCompleteRequest(Irp, IO_NO_INCREMENT);
839         return STATUS_INVALID_PARAMETER_1;
840     }
841 
842     ASSERT(CommonDeviceExtension->IsFDO == FALSE);
843 
844     //
845     // get pdo device extension
846     //
847     PDODeviceExtension = DeviceObject->DeviceExtension;
848 
849     //
850     // get stack location
851     //
852     IoStack = IoGetCurrentIrpStackLocation(Irp);
853 
854     switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
855     {
856         case IOCTL_HID_GET_COLLECTION_INFORMATION:
857         {
858             //
859             // check if output buffer is big enough
860             //
861             if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
862             {
863                 //
864                 // invalid buffer size
865                 //
866                 Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
867                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
868                 return STATUS_INVALID_BUFFER_SIZE;
869             }
870 
871             //
872             // get output buffer
873             //
874             CollectionInformation = Irp->AssociatedIrp.SystemBuffer;
875             ASSERT(CollectionInformation);
876 
877             //
878             // get collection description
879             //
880             CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
881                                                                          PDODeviceExtension->CollectionNumber);
882             ASSERT(CollectionDescription);
883 
884             //
885             // init result buffer
886             //
887             CollectionInformation->DescriptorSize = CollectionDescription->PreparsedDataLength;
888             CollectionInformation->Polled = CommonDeviceExtension->DriverExtension->DevicesArePolled;
889             CollectionInformation->VendorID = CommonDeviceExtension->Attributes.VendorID;
890             CollectionInformation->ProductID = CommonDeviceExtension->Attributes.ProductID;
891             CollectionInformation->VersionNumber = CommonDeviceExtension->Attributes.VersionNumber;
892 
893             //
894             // complete request
895             //
896             Irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
897             Irp->IoStatus.Status = STATUS_SUCCESS;
898             IoCompleteRequest(Irp, IO_NO_INCREMENT);
899             return STATUS_SUCCESS;
900         }
901         case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
902         {
903             //
904             // get collection description
905             //
906             CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription,
907                                                                          PDODeviceExtension->CollectionNumber);
908             ASSERT(CollectionDescription);
909 
910             //
911             // check if output buffer is big enough
912             //
913             if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < CollectionDescription->PreparsedDataLength)
914             {
915                 //
916                 // invalid buffer size
917                 //
918                 Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
919                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
920                 return STATUS_INVALID_BUFFER_SIZE;
921             }
922 
923             //
924             // copy result
925             //
926             ASSERT(Irp->UserBuffer);
927             RtlCopyMemory(Irp->UserBuffer, CollectionDescription->PreparsedData, CollectionDescription->PreparsedDataLength);
928 
929             //
930             // complete request
931             //
932             Irp->IoStatus.Information = CollectionDescription->PreparsedDataLength;
933             Irp->IoStatus.Status = STATUS_SUCCESS;
934             IoCompleteRequest(Irp, IO_NO_INCREMENT);
935             return STATUS_SUCCESS;
936         }
937         default:
938         {
939             DPRINT1("[HIDCLASS] DeviceControl IoControlCode 0x%x not implemented\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
940             Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
941             IoCompleteRequest(Irp, IO_NO_INCREMENT);
942             return STATUS_NOT_IMPLEMENTED;
943         }
944     }
945 }
946 
947 NTSTATUS
948 NTAPI
949 HidClass_InternalDeviceControl(
950     IN PDEVICE_OBJECT DeviceObject,
951     IN PIRP Irp)
952 {
953     UNIMPLEMENTED;
954     ASSERT(FALSE);
955     Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
956     IoCompleteRequest(Irp, IO_NO_INCREMENT);
957     return STATUS_NOT_IMPLEMENTED;
958 }
959 
960 NTSTATUS
961 NTAPI
962 HidClass_Power(
963     IN PDEVICE_OBJECT DeviceObject,
964     IN PIRP Irp)
965 {
966     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
967     CommonDeviceExtension = DeviceObject->DeviceExtension;
968 
969     if (CommonDeviceExtension->IsFDO)
970     {
971         IoCopyCurrentIrpStackLocationToNext(Irp);
972         return HidClassFDO_DispatchRequest(DeviceObject, Irp);
973     }
974     else
975     {
976         Irp->IoStatus.Status = STATUS_SUCCESS;
977         PoStartNextPowerIrp(Irp);
978         IoCompleteRequest(Irp, IO_NO_INCREMENT);
979         return STATUS_SUCCESS;
980     }
981 }
982 
983 NTSTATUS
984 NTAPI
985 HidClass_PnP(
986     IN PDEVICE_OBJECT DeviceObject,
987     IN PIRP Irp)
988 {
989     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
990 
991     //
992     // get common device extension
993     //
994     CommonDeviceExtension = DeviceObject->DeviceExtension;
995 
996     //
997     // check type of device object
998     //
999     if (CommonDeviceExtension->IsFDO)
1000     {
1001         //
1002         // handle request
1003         //
1004         return HidClassFDO_PnP(DeviceObject, Irp);
1005     }
1006     else
1007     {
1008         //
1009         // handle request
1010         //
1011         return HidClassPDO_PnP(DeviceObject, Irp);
1012     }
1013 }
1014 
1015 NTSTATUS
1016 NTAPI
1017 HidClass_DispatchDefault(
1018     IN PDEVICE_OBJECT DeviceObject,
1019     IN PIRP Irp)
1020 {
1021     PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension;
1022 
1023     //
1024     // get common device extension
1025     //
1026     CommonDeviceExtension = DeviceObject->DeviceExtension;
1027 
1028     //
1029     // FIXME: support PDO
1030     //
1031     ASSERT(CommonDeviceExtension->IsFDO == TRUE);
1032 
1033     //
1034     // skip current irp stack location
1035     //
1036     IoSkipCurrentIrpStackLocation(Irp);
1037 
1038     //
1039     // dispatch to lower device object
1040     //
1041     return IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, Irp);
1042 }
1043 
1044 NTSTATUS
1045 NTAPI
1046 HidClassDispatch(
1047     IN PDEVICE_OBJECT DeviceObject,
1048     IN PIRP Irp)
1049 {
1050     PIO_STACK_LOCATION IoStack;
1051 
1052     //
1053     // get current stack location
1054     //
1055     IoStack = IoGetCurrentIrpStackLocation(Irp);
1056     DPRINT("[HIDCLASS] Dispatch Major %x Minor %x\n", IoStack->MajorFunction, IoStack->MinorFunction);
1057 
1058     //
1059     // dispatch request based on major function
1060     //
1061     switch (IoStack->MajorFunction)
1062     {
1063         case IRP_MJ_CREATE:
1064             return HidClass_Create(DeviceObject, Irp);
1065         case IRP_MJ_CLOSE:
1066             return HidClass_Close(DeviceObject, Irp);
1067         case IRP_MJ_READ:
1068             return HidClass_Read(DeviceObject, Irp);
1069         case IRP_MJ_WRITE:
1070             return HidClass_Write(DeviceObject, Irp);
1071         case IRP_MJ_DEVICE_CONTROL:
1072             return HidClass_DeviceControl(DeviceObject, Irp);
1073         case IRP_MJ_INTERNAL_DEVICE_CONTROL:
1074            return HidClass_InternalDeviceControl(DeviceObject, Irp);
1075         case IRP_MJ_POWER:
1076             return HidClass_Power(DeviceObject, Irp);
1077         case IRP_MJ_PNP:
1078             return HidClass_PnP(DeviceObject, Irp);
1079         default:
1080             return HidClass_DispatchDefault(DeviceObject, Irp);
1081     }
1082 }
1083 
1084 NTSTATUS
1085 NTAPI
1086 HidRegisterMinidriver(
1087     IN PHID_MINIDRIVER_REGISTRATION MinidriverRegistration)
1088 {
1089     NTSTATUS Status;
1090     PHIDCLASS_DRIVER_EXTENSION DriverExtension;
1091 
1092     /* check if the version matches */
1093     if (MinidriverRegistration->Revision > HID_REVISION)
1094     {
1095         /* revision mismatch */
1096         ASSERT(FALSE);
1097         return STATUS_REVISION_MISMATCH;
1098     }
1099 
1100     /* now allocate the driver object extension */
1101     Status = IoAllocateDriverObjectExtension(MinidriverRegistration->DriverObject,
1102                                              ClientIdentificationAddress,
1103                                              sizeof(HIDCLASS_DRIVER_EXTENSION),
1104                                              (PVOID *)&DriverExtension);
1105     if (!NT_SUCCESS(Status))
1106     {
1107         /* failed to allocate driver extension */
1108         ASSERT(FALSE);
1109         return Status;
1110     }
1111 
1112     /* zero driver extension */
1113     RtlZeroMemory(DriverExtension, sizeof(HIDCLASS_DRIVER_EXTENSION));
1114 
1115     /* init driver extension */
1116     DriverExtension->DriverObject = MinidriverRegistration->DriverObject;
1117     DriverExtension->DeviceExtensionSize = MinidriverRegistration->DeviceExtensionSize;
1118     DriverExtension->DevicesArePolled = MinidriverRegistration->DevicesArePolled;
1119     DriverExtension->AddDevice = MinidriverRegistration->DriverObject->DriverExtension->AddDevice;
1120     DriverExtension->DriverUnload = MinidriverRegistration->DriverObject->DriverUnload;
1121 
1122     /* copy driver dispatch routines */
1123     RtlCopyMemory(DriverExtension->MajorFunction,
1124                   MinidriverRegistration->DriverObject->MajorFunction,
1125                   sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1));
1126 
1127     /* initialize lock */
1128     KeInitializeSpinLock(&DriverExtension->Lock);
1129 
1130     /* now replace dispatch routines */
1131     DriverExtension->DriverObject->DriverExtension->AddDevice = HidClassAddDevice;
1132     DriverExtension->DriverObject->DriverUnload = HidClassDriverUnload;
1133     DriverExtension->DriverObject->MajorFunction[IRP_MJ_CREATE] = HidClassDispatch;
1134     DriverExtension->DriverObject->MajorFunction[IRP_MJ_CLOSE] = HidClassDispatch;
1135     DriverExtension->DriverObject->MajorFunction[IRP_MJ_READ] = HidClassDispatch;
1136     DriverExtension->DriverObject->MajorFunction[IRP_MJ_WRITE] = HidClassDispatch;
1137     DriverExtension->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HidClassDispatch;
1138     DriverExtension->DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HidClassDispatch;
1139     DriverExtension->DriverObject->MajorFunction[IRP_MJ_POWER] = HidClassDispatch;
1140     DriverExtension->DriverObject->MajorFunction[IRP_MJ_PNP] = HidClassDispatch;
1141 
1142     /* done */
1143     return STATUS_SUCCESS;
1144 }
1145